USB: usb-storage: add "quirks=" module parameter
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 10 Nov 2008 19:07:45 +0000 (14:07 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 7 Jan 2009 17:59:53 +0000 (09:59 -0800)
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>
Documentation/kernel-parameters.txt
drivers/usb/storage/usb.c

index 0b3f671..8eb6e35 100644 (file)
@@ -91,6 +91,7 @@ parameter is applicable:
        SUSPEND System suspend states are enabled.
        FTRACE  Function tracing enabled.
        TS      Appropriate touchscreen support is enabled.
        SUSPEND System suspend states are enabled.
        FTRACE  Function tracing enabled.
        TS      Appropriate touchscreen support is enabled.
+       UMS     USB Mass Storage support is enabled.
        USB     USB support is enabled.
        USBHID  USB Human Interface Device support is enabled.
        V4L     Video For Linux support is enabled.
        USB     USB support is enabled.
        USBHID  USB Human Interface Device 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] The interval which mice are to be polled at.
 
        usbhid.mousepoll=
                        [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
                        kernel's map of available physical RAM.
 
        add_efi_memmap  [EFI; x86-32,X86-64] Include EFI memory map in
                        kernel's map of available physical RAM.
 
index 27016fd..eb1a53a 100644 (file)
@@ -113,6 +113,16 @@ static unsigned int delay_use = 5;
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 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) */
 static struct us_unusual_dev *find_unusual(const struct usb_device_id *id)
 {
 /* Find an unusual_dev descriptor (always succeeds in the current code) */
 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 :
                        unusual_dev->useTransport;
        us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
                        idesc->bInterfaceProtocol :
                        unusual_dev->useTransport;
        us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+       adjust_quirks(us);
 
        if (us->fflags & US_FL_IGNORE_DEVICE) {
                printk(KERN_INFO USB_STORAGE "device ignored\n");
 
        if (us->fflags & US_FL_IGNORE_DEVICE) {
                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)
 {
        int retval;
 static int __init usb_stor_init(void)
 {
        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 */
        retval = usb_register(&usb_storage_driver);
 
        /* register the driver, return usb_register return code if error */
        retval = usb_register(&usb_storage_driver);