[PATCH 023/123] USB: usb-storage: add "quirks=" module parameter

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

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@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>
---
 Documentation/kernel-parameters.txt |   29 +++++++++
 drivers/usb/storage/usb.c           |  113 +++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+), 0 deletions(-)

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

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux