From d4f373e57d3916814110968c5ea1155a8d972b5a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 10 Nov 2008 14:07:45 -0500 Subject: [PATCH] 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 Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 29 +++++++++ drivers/usb/storage/usb.c | 113 ++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0b3f671..8eb6e35 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -91,6 +91,7 @@ parameter is applicable: 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. @@ -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. + 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. diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 27016fd..eb1a53a 100644 --- a/drivers/usb/storage/usb.c +++ b/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_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, @@ -473,6 +483,30 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) 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) { @@ -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); + adjust_quirks(us); 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, }; +/* 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; + 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); -- 1.8.2.3