diff options
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/usb.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 27016fd2cad..eb1a53a3e5c 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); |