diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-11-10 14:07:45 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-07 12:59:53 -0500 |
commit | d4f373e57d3916814110968c5ea1155a8d972b5a (patch) | |
tree | eb9e79187ab45942044e48c22c1b62c7c0c29ce1 /drivers/usb/storage | |
parent | 74c71ebd8d7c7a513022851a02bb52b9fa7e0dcb (diff) |
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>
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 27016fd2cad1..eb1a53a3e5ca 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c | |||
@@ -113,6 +113,16 @@ static unsigned int delay_use = 5; | |||
113 | module_param(delay_use, uint, S_IRUGO | S_IWUSR); | 113 | module_param(delay_use, uint, S_IRUGO | S_IWUSR); |
114 | MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); | 114 | MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); |
115 | 115 | ||
116 | static char *quirks; | ||
117 | module_param(quirks, charp, S_IRUGO); | ||
118 | MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); | ||
119 | |||
120 | struct quirks_entry { | ||
121 | u16 vid, pid; | ||
122 | u32 fflags; | ||
123 | }; | ||
124 | static struct quirks_entry *quirks_list, *quirks_end; | ||
125 | |||
116 | 126 | ||
117 | /* | 127 | /* |
118 | * The entries in this table correspond, line for line, | 128 | * 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) | |||
473 | return 0; | 483 | return 0; |
474 | } | 484 | } |
475 | 485 | ||
486 | /* Adjust device flags based on the "quirks=" module parameter */ | ||
487 | static void adjust_quirks(struct us_data *us) | ||
488 | { | ||
489 | u16 vid, pid; | ||
490 | struct quirks_entry *q; | ||
491 | unsigned int mask = (US_FL_FIX_CAPACITY | US_FL_IGNORE_DEVICE | | ||
492 | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | | ||
493 | US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | | ||
494 | US_FL_NO_WP_DETECT); | ||
495 | |||
496 | vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor); | ||
497 | pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct); | ||
498 | |||
499 | for (q = quirks_list; q != quirks_end; ++q) { | ||
500 | if (q->vid == vid && q->pid == pid) { | ||
501 | us->fflags = (us->fflags & ~mask) | q->fflags; | ||
502 | dev_info(&us->pusb_intf->dev, "Quirks match for " | ||
503 | "vid %04x pid %04x: %x\n", | ||
504 | vid, pid, q->fflags); | ||
505 | break; | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
476 | /* Find an unusual_dev descriptor (always succeeds in the current code) */ | 510 | /* Find an unusual_dev descriptor (always succeeds in the current code) */ |
477 | static struct us_unusual_dev *find_unusual(const struct usb_device_id *id) | 511 | static struct us_unusual_dev *find_unusual(const struct usb_device_id *id) |
478 | { | 512 | { |
@@ -497,6 +531,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) | |||
497 | idesc->bInterfaceProtocol : | 531 | idesc->bInterfaceProtocol : |
498 | unusual_dev->useTransport; | 532 | unusual_dev->useTransport; |
499 | us->fflags = USB_US_ORIG_FLAGS(id->driver_info); | 533 | us->fflags = USB_US_ORIG_FLAGS(id->driver_info); |
534 | adjust_quirks(us); | ||
500 | 535 | ||
501 | if (us->fflags & US_FL_IGNORE_DEVICE) { | 536 | if (us->fflags & US_FL_IGNORE_DEVICE) { |
502 | printk(KERN_INFO USB_STORAGE "device ignored\n"); | 537 | printk(KERN_INFO USB_STORAGE "device ignored\n"); |
@@ -1061,10 +1096,88 @@ static struct usb_driver usb_storage_driver = { | |||
1061 | .soft_unbind = 1, | 1096 | .soft_unbind = 1, |
1062 | }; | 1097 | }; |
1063 | 1098 | ||
1099 | /* Works only for digits and letters, but small and fast */ | ||
1100 | #define TOLOWER(x) ((x) | 0x20) | ||
1101 | |||
1102 | static void __init parse_quirks(void) | ||
1103 | { | ||
1104 | int n, i; | ||
1105 | char *p; | ||
1106 | |||
1107 | if (!quirks) | ||
1108 | return; | ||
1109 | |||
1110 | /* Count the ':' characters to get 2 * the number of entries */ | ||
1111 | n = 0; | ||
1112 | for (p = quirks; *p; ++p) { | ||
1113 | if (*p == ':') | ||
1114 | ++n; | ||
1115 | } | ||
1116 | n /= 2; | ||
1117 | if (n == 0) | ||
1118 | return; /* Don't allocate 0 bytes */ | ||
1119 | |||
1120 | quirks_list = kmalloc(n * sizeof(*quirks_list), GFP_KERNEL); | ||
1121 | if (!quirks_list) | ||
1122 | return; | ||
1123 | |||
1124 | p = quirks; | ||
1125 | quirks_end = quirks_list; | ||
1126 | for (i = 0; i < n && *p; ++i) { | ||
1127 | unsigned f = 0; | ||
1128 | |||
1129 | /* Each entry consists of VID:PID:flags */ | ||
1130 | quirks_end->vid = simple_strtoul(p, &p, 16); | ||
1131 | if (*p != ':') | ||
1132 | goto skip_to_next; | ||
1133 | quirks_end->pid = simple_strtoul(p+1, &p, 16); | ||
1134 | if (*p != ':') | ||
1135 | goto skip_to_next; | ||
1136 | |||
1137 | while (*++p && *p != ',') { | ||
1138 | switch (TOLOWER(*p)) { | ||
1139 | case 'c': | ||
1140 | f |= US_FL_FIX_CAPACITY; | ||
1141 | break; | ||
1142 | case 'i': | ||
1143 | f |= US_FL_IGNORE_DEVICE; | ||
1144 | break; | ||
1145 | case 'l': | ||
1146 | f |= US_FL_NOT_LOCKABLE; | ||
1147 | break; | ||
1148 | case 'm': | ||
1149 | f |= US_FL_MAX_SECTORS_64; | ||
1150 | break; | ||
1151 | case 'r': | ||
1152 | f |= US_FL_IGNORE_RESIDUE; | ||
1153 | break; | ||
1154 | case 's': | ||
1155 | f |= US_FL_SINGLE_LUN; | ||
1156 | break; | ||
1157 | case 'w': | ||
1158 | f |= US_FL_NO_WP_DETECT; | ||
1159 | break; | ||
1160 | /* Ignore unrecognized flag characters */ | ||
1161 | } | ||
1162 | } | ||
1163 | quirks_end->fflags = f; | ||
1164 | ++quirks_end; | ||
1165 | |||
1166 | skip_to_next: | ||
1167 | /* Entries are separated by commas */ | ||
1168 | while (*p) { | ||
1169 | if (*p++ == ',') | ||
1170 | break; | ||
1171 | } | ||
1172 | } /* for (i = 0; ...) */ | ||
1173 | } | ||
1174 | |||
1064 | static int __init usb_stor_init(void) | 1175 | static int __init usb_stor_init(void) |
1065 | { | 1176 | { |
1066 | int retval; | 1177 | int retval; |
1178 | |||
1067 | printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); | 1179 | printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); |
1180 | parse_quirks(); | ||
1068 | 1181 | ||
1069 | /* register the driver, return usb_register return code if error */ | 1182 | /* register the driver, return usb_register return code if error */ |
1070 | retval = usb_register(&usb_storage_driver); | 1183 | retval = usb_register(&usb_storage_driver); |