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>
This commit is contained in:
Alan Stern 2008-11-10 14:07:45 -05:00 committed by Greg Kroah-Hartman
parent 74c71ebd8d
commit d4f373e57d
2 changed files with 142 additions and 0 deletions

View file

@ -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.

View 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");
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);