Re: [PATCH v2] usb: core: Add "quirks" parameter for usbcore

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

 



> On 6 Dec 2017, at 10:10 PM, Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx> wrote:
> 
> On Wed, Dec 06, 2017 at 06:26:21PM +0800, Kai-Heng Feng wrote:
>> Trying quirks in usbcore needs to rebuild the driver or the entire
>> kernel if it's builtin. It can save a lot of time if usbcore has similar
>> ability like "usbhid.quirks=" and "usb-storage.quirks=".
>> 
>> Rename the original quirk detection function to "static" as we introduce
>> this new "dynamic" function.
>> 
>> Now users can use "usbcore.quirks=" as short term workaround before the
>> next kernel release.
>> 
>> This is inspired by usbhid and usb-storage.
>> 
>> Signed-off-by: Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx>
>> ---
>> v2: use in-kernel tolower() function.
>> 
>> Documentation/admin-guide/kernel-parameters.txt |  55 +++++++++++++
>> drivers/usb/core/quirks.c                       | 100 ++++++++++++++++++++++--
>> include/linux/usb/quirks.h                      |   2 +
>> 3 files changed, 151 insertions(+), 6 deletions(-)
>> 
>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>> index 6571fbfdb2a1..d42fb278320b 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -4302,6 +4302,61 @@
>> 
>> 	usbcore.nousb	[USB] Disable the USB subsystem
>> 
>> +	usbcore.quirks=
>> +			[USB] A list of quirks entries to supplement or
>> +			override the built-in usb core quirk 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 core quirk flag as follows:
>> +				a = USB_QUIRK_STRING_FETCH_255 (string
>> +					descriptors must not be fetched using
>> +					a 255-byte read);
>> +				b = USB_QUIRK_RESET_RESUME (device can't resume
>> +					correctly so reset it instead);
>> +				c = USB_QUIRK_NO_SET_INTF (device can't handle
>> +					Set-Interface requests);
>> +				d = USB_QUIRK_CONFIG_INTF_STRINGS (device can't
>> +					handle its Configuration or Interface
>> +					strings);
>> +				e = USB_QUIRK_RESET (device can't be reset
>> +					(e.g morph devices), don't use reset);
>> +				f = USB_QUIRK_HONOR_BNUMINTERFACES (device has
>> +					more interface descriptions than the
>> +					bNumInterfaces count, and can't handle
>> +					talking to these interfaces);
>> +				g = USB_QUIRK_DELAY_INIT (device needs a pause
>> +					during initialization, after we read
>> +					the device descriptor);
>> +				h = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL (For
>> +					high speed and super speed interrupt
>> +					endpoints, the USB 2.0 and USB 3.0 spec
>> +					require the interval in microframes (1
>> +					microframe = 125 microseconds) to be
>> +					calculated as interval = 2 ^
>> +					(bInterval-1).
>> +					Devices with this quirk report their
>> +					bInterval as the result of this
>> +					calculation instead of the exponent
>> +					variable used in the calculation);
>> +				i = USB_QUIRK_DEVICE_QUALIFIER (device can't
>> +					handle device_qualifier descriptor
>> +					requests);
>> +				j = USB_QUIRK_IGNORE_REMOTE_WAKEUP (device
>> +					generates spurious wakeup, ignore
>> +					remote wakeup capability);
>> +				k = USB_QUIRK_NO_LPM (device can't handle Link
>> +					Power Management);
>> +				l = USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL
>> +					(Device reports its bInterval as linear
>> +					frames instead of the USB 2.0
>> +					calculation);
>> +				m = USB_QUIRK_DISCONNECT_SUSPEND (Device needs
>> +					to be disconnected before suspend to
>> +					prevent spurious wakeup)
>> +			Example: quirks=0781:5580:bk,0a5c:5834:gij
>> +
>> 	usbhid.mousepoll=
>> 			[USBHID] The interval which mice are to be polled at.
>> 
>> diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
>> index f1dbab6f798f..5471765765be 100644
>> --- a/drivers/usb/core/quirks.c
>> +++ b/drivers/usb/core/quirks.c
>> @@ -11,6 +11,12 @@
>> #include <linux/usb/hcd.h>
>> #include "usb.h"
>> 
>> +/* Quirks specified at module load time */
>> +static char *quirks_param[MAX_USB_BOOT_QUIRKS];
>> +module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
> 
> So you can't modify this at runtime?  Why not?

Sure, I think make it runtime tunable is a good idea.

> 
>> +MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying"
>> +			 "quirks=vendorID:productID:quirks");
> 
> Don't break strings over multiple lines.

Will fix in next version.

> 
>> +
>> /* Lists of quirky USB devices, split in device quirks and interface quirks.
>>  * Device quirks are applied at the very beginning of the enumeration process,
>>  * right after reading the device descriptor. They can thus only match on device
>> @@ -310,8 +316,8 @@ static int usb_amd_resume_quirk(struct usb_device *udev)
>> 	return 0;
>> }
>> 
>> -static u32 __usb_detect_quirks(struct usb_device *udev,
>> -			       const struct usb_device_id *id)
>> +static u32 usb_detect_static_quirks(struct usb_device *udev,
>> +				    const struct usb_device_id *id)
>> {
>> 	u32 quirks = 0;
>> 
>> @@ -329,23 +335,105 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
>> 	return quirks;
>> }
>> 
>> +static u32 usb_detect_dynamic_quirks(struct usb_device *udev)
>> +{
>> +	u16 dev_vid = le16_to_cpu(udev->descriptor.idVendor);
>> +	u16 dev_pid = le16_to_cpu(udev->descriptor.idProduct);
>> +	u16 quirk_vid, quirk_pid;
>> +	char quirks[32];
>> +	char *p;
>> +	u32 flags = 0;
>> +	int n, m;
>> +
>> +	for (n = 0; n < MAX_USB_BOOT_QUIRKS && quirks_param[n]; n++) {
>> +		m = sscanf(quirks_param[n], "%hx:%hx:%s",
>> +			   &quirk_vid, &quirk_pid, quirks);
>> +		if (m != 3)
>> +			pr_warn("Could not parse USB quirk module param %s\n",
>> +				quirks_param[n]);
> 
> dev_warn().
> 
> Also, you are going to complain about the inability to parse the quirks
> for _EVERY_ USB device in the system?  That feels really wrong.  Parse
> it once, and then use the parsed table when matching things up.  Don't
> parse the string each and every time a device is added, that's a
> horrible waste of time (not like USB enumeration is fast, but really,
> let's not make it worse for no reason.)

Makes sense. Will improve this in next version.

> 
>> +
>> +		if (quirk_vid == dev_vid && quirk_pid == dev_pid)
>> +			break;
>> +	}
>> +	if (n == MAX_USB_BOOT_QUIRKS || !quirks_param[n])
>> +		return 0;
>> +
>> +	p = quirks;
>> +
>> +	/* Collect the flags */
>> +	for (; *p; p++) {
>> +		switch (tolower(*p)) {
>> +		case 'a':
>> +			flags |= USB_QUIRK_STRING_FETCH_255;
>> +			break;
>> +		case 'b':
>> +			flags |= USB_QUIRK_RESET_RESUME;
>> +			break;
>> +		case 'c':
>> +			flags |= USB_QUIRK_NO_SET_INTF;
>> +			break;
>> +		case 'd':
>> +			flags |= USB_QUIRK_CONFIG_INTF_STRINGS;
>> +			break;
>> +		case 'e':
>> +			flags |= USB_QUIRK_RESET;
>> +			break;
>> +		case 'f':
>> +			flags |= USB_QUIRK_HONOR_BNUMINTERFACES;
>> +			break;
>> +		case 'g':
>> +			flags |= USB_QUIRK_DELAY_INIT;
>> +			break;
>> +		case 'h':
>> +			flags |= USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL;
>> +			break;
>> +		case 'i':
>> +			flags |= USB_QUIRK_DEVICE_QUALIFIER;
>> +			break;
>> +		case 'j':
>> +			flags |= USB_QUIRK_IGNORE_REMOTE_WAKEUP;
>> +			break;
>> +		case 'k':
>> +			flags |= USB_QUIRK_NO_LPM;
>> +			break;
>> +		case 'l':
>> +			flags |= USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL;
>> +			break;
>> +		case 'm':
>> +			flags |= USB_QUIRK_DISCONNECT_SUSPEND;
>> +			break;
>> +		/* Ignore unrecognized flag characters */
>> +		}
>> +	}
>> +
>> +	return flags;
>> +}
>> +
>> /*
>>  * Detect any quirks the device has, and do any housekeeping for it if needed.
>>  */
>> void usb_detect_quirks(struct usb_device *udev)
>> {
>> -	udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
>> +	udev->quirks = usb_detect_dynamic_quirks(udev);
>> +
>> +	if (udev->quirks) {
>> +		dev_dbg(&udev->dev, "Dynamic USB quirks for this device: %x\n",
>> +			udev->quirks);
>> +		return;
>> +	}
>> +
>> +	udev->quirks = usb_detect_static_quirks(udev, usb_quirk_list);
> 
> Did you just overwrite the dynamic with the static ones?  It feels like
> you just did :(

That’s my original intention. I want to let users can override quirk, in 
case the quirk somehow regressed their device.

> 
>> --- a/include/linux/usb/quirks.h
>> +++ b/include/linux/usb/quirks.h
>> @@ -8,6 +8,8 @@
>> #ifndef __LINUX_USB_QUIRKS_H
>> #define __LINUX_USB_QUIRKS_H
>> 
>> +#define MAX_USB_BOOT_QUIRKS 4
> 
> Why 4?  And why in this .h file?

It’s from usbhid. I think I’ll make it 16 next version.
I’ll put it in the .c file.

> 
> Also, Oliver's request is good, xor is a nice way to do things if at all
> possible.

Yea, I think Oliver’s suggestion is great. Will do in next version.

> 
> thanks,
> 
> greg k-h

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