Просмотр исходного кода

USB: usb-storage: add "quirks=" module parameter

This patch (as1163b) adds a "quirks=" module parameter to usb-storage.
This will allow people to make short-term changes to their
unusual_devs list without rebuilding the entire driver.  Testing will
become much easier, and less-sophisticated users will be able to
access their buggy devices after a simple config-file change instead
of having to wait for a new kernel release.

The patch also adds a documentation entry for usb-storage's
"delay_use" parameter, which has been around for years but but was
never listed among the kernel parameters.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Alan Stern 16 лет назад
Родитель
Сommit
d4f373e57d
2 измененных файлов с 142 добавлено и 0 удалено
  1. 29 0
      Documentation/kernel-parameters.txt
  2. 113 0
      drivers/usb/storage/usb.c

+ 29 - 0
Documentation/kernel-parameters.txt

@@ -91,6 +91,7 @@ parameter is applicable:
 	SUSPEND	System suspend states are enabled.
 	SUSPEND	System suspend states are enabled.
 	FTRACE	Function tracing enabled.
 	FTRACE	Function tracing enabled.
 	TS	Appropriate touchscreen support is enabled.
 	TS	Appropriate touchscreen support is enabled.
+	UMS	USB Mass Storage support is enabled.
 	USB	USB support is enabled.
 	USB	USB support is enabled.
 	USBHID	USB Human Interface Device support is enabled.
 	USBHID	USB Human Interface Device support is enabled.
 	V4L	Video For Linux support is enabled.
 	V4L	Video For Linux support is enabled.
@@ -2383,6 +2384,34 @@ and is between 256 and 4096 characters. It is defined in the file
 	usbhid.mousepoll=
 	usbhid.mousepoll=
 			[USBHID] The interval which mice are to be polled at.
 			[USBHID] The interval which mice are to be polled at.
 
 
+	usb-storage.delay_use=
+			[UMS] The delay in seconds before a new device is
+			scanned for Logical Units (default 5).
+
+	usb-storage.quirks=
+			[UMS] A list of quirks entries to supplement or
+			override the built-in unusual_devs list.  List
+			entries are separated by commas.  Each entry has
+			the form VID:PID:Flags where VID and PID are Vendor
+			and Product ID values (4-digit hex numbers) and
+			Flags is a set of characters, each corresponding
+			to a common usb-storage quirk flag as follows:
+				c = FIX_CAPACITY (decrease the reported
+					device capacity by one sector);
+				i = IGNORE_DEVICE (don't bind to this
+					device);
+				l = NOT_LOCKABLE (don't try to lock and
+					unlock ejectable media);
+				m = MAX_SECTORS_64 (don't transfer more
+					than 64 sectors = 32 KB at a time);
+				r = IGNORE_RESIDUE (the device reports
+					bogus residue values);
+				s = SINGLE_LUN (the device has only one
+					Logical Unit);
+				w = NO_WP_DETECT (don't test whether the
+					medium is write-protected).
+			Example: quirks=0419:aaf5:rl,0421:0433:rc
+
 	add_efi_memmap	[EFI; x86-32,X86-64] Include EFI memory map in
 	add_efi_memmap	[EFI; x86-32,X86-64] Include EFI memory map in
 			kernel's map of available physical RAM.
 			kernel's map of available physical RAM.
 
 

+ 113 - 0
drivers/usb/storage/usb.c

@@ -113,6 +113,16 @@ static unsigned int delay_use = 5;
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 
 
+static char *quirks;
+module_param(quirks, charp, S_IRUGO);
+MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
+
+struct quirks_entry {
+	u16	vid, pid;
+	u32	fflags;
+};
+static struct quirks_entry *quirks_list, *quirks_end;
+
 
 
 /*
 /*
  * The entries in this table correspond, line for line,
  * The entries in this table correspond, line for line,
@@ -473,6 +483,30 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Adjust device flags based on the "quirks=" module parameter */
+static void adjust_quirks(struct us_data *us)
+{
+	u16 vid, pid;
+	struct quirks_entry *q;
+	unsigned int mask = (US_FL_FIX_CAPACITY | US_FL_IGNORE_DEVICE |
+			US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 |
+			US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN |
+			US_FL_NO_WP_DETECT);
+
+	vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor);
+	pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct);
+
+	for (q = quirks_list; q != quirks_end; ++q) {
+		if (q->vid == vid && q->pid == pid) {
+			us->fflags = (us->fflags & ~mask) | q->fflags;
+			dev_info(&us->pusb_intf->dev, "Quirks match for "
+					"vid %04x pid %04x: %x\n",
+					vid, pid, q->fflags);
+			break;
+		}
+	}
+}
+
 /* Find an unusual_dev descriptor (always succeeds in the current code) */
 /* Find an unusual_dev descriptor (always succeeds in the current code) */
 static struct us_unusual_dev *find_unusual(const struct usb_device_id *id)
 static struct us_unusual_dev *find_unusual(const struct usb_device_id *id)
 {
 {
@@ -497,6 +531,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
 			idesc->bInterfaceProtocol :
 			idesc->bInterfaceProtocol :
 			unusual_dev->useTransport;
 			unusual_dev->useTransport;
 	us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
 	us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+	adjust_quirks(us);
 
 
 	if (us->fflags & US_FL_IGNORE_DEVICE) {
 	if (us->fflags & US_FL_IGNORE_DEVICE) {
 		printk(KERN_INFO USB_STORAGE "device ignored\n");
 		printk(KERN_INFO USB_STORAGE "device ignored\n");
@@ -1061,10 +1096,88 @@ static struct usb_driver usb_storage_driver = {
 	.soft_unbind =	1,
 	.soft_unbind =	1,
 };
 };
 
 
+/* Works only for digits and letters, but small and fast */
+#define TOLOWER(x) ((x) | 0x20)
+
+static void __init parse_quirks(void)
+{
+	int n, i;
+	char *p;
+
+	if (!quirks)
+		return;
+
+	/* Count the ':' characters to get 2 * the number of entries */
+	n = 0;
+	for (p = quirks; *p; ++p) {
+		if (*p == ':')
+			++n;
+	}
+	n /= 2;
+	if (n == 0)
+		return;		/* Don't allocate 0 bytes */
+
+	quirks_list = kmalloc(n * sizeof(*quirks_list), GFP_KERNEL);
+	if (!quirks_list)
+		return;
+
+	p = quirks;
+	quirks_end = quirks_list;
+	for (i = 0; i < n && *p; ++i) {
+		unsigned f = 0;
+
+		/* Each entry consists of VID:PID:flags */
+		quirks_end->vid = simple_strtoul(p, &p, 16);
+		if (*p != ':')
+			goto skip_to_next;
+		quirks_end->pid = simple_strtoul(p+1, &p, 16);
+		if (*p != ':')
+			goto skip_to_next;
+
+		while (*++p && *p != ',') {
+			switch (TOLOWER(*p)) {
+			case 'c':
+				f |= US_FL_FIX_CAPACITY;
+				break;
+			case 'i':
+				f |= US_FL_IGNORE_DEVICE;
+				break;
+			case 'l':
+				f |= US_FL_NOT_LOCKABLE;
+				break;
+			case 'm':
+				f |= US_FL_MAX_SECTORS_64;
+				break;
+			case 'r':
+				f |= US_FL_IGNORE_RESIDUE;
+				break;
+			case 's':
+				f |= US_FL_SINGLE_LUN;
+				break;
+			case 'w':
+				f |= US_FL_NO_WP_DETECT;
+				break;
+			/* Ignore unrecognized flag characters */
+			}
+		}
+		quirks_end->fflags = f;
+		++quirks_end;
+
+ skip_to_next:
+		/* Entries are separated by commas */
+		while (*p) {
+			if (*p++ == ',')
+				break;
+		}
+	} /* for (i = 0; ...) */
+}
+
 static int __init usb_stor_init(void)
 static int __init usb_stor_init(void)
 {
 {
 	int retval;
 	int retval;
+
 	printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
 	printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
+	parse_quirks();
 
 
 	/* register the driver, return usb_register return code if error */
 	/* register the driver, return usb_register return code if error */
 	retval = usb_register(&usb_storage_driver);
 	retval = usb_register(&usb_storage_driver);