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); +MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying" + "quirks=vendorID:productID:quirks"); + /* 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]); + + 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); /* * Pixart-based mice would trigger remote wakeup issue on AMD * Yangtze chipset, so set them as RESET_RESUME flag. */ if (usb_amd_resume_quirk(udev)) - udev->quirks |= __usb_detect_quirks(udev, + udev->quirks |= usb_detect_static_quirks(udev, usb_amd_resume_quirk_list); if (udev->quirks) - dev_dbg(&udev->dev, "USB quirks for this device: %x\n", + dev_dbg(&udev->dev, "Static USB quirks for this device: %x\n", udev->quirks); #ifdef CONFIG_USB_DEFAULT_PERSIST @@ -362,7 +450,7 @@ void usb_detect_interface_quirks(struct usb_device *udev) { u32 quirks; - quirks = __usb_detect_quirks(udev, usb_interface_quirk_list); + quirks = usb_detect_static_quirks(udev, usb_interface_quirk_list); if (quirks == 0) return; diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index f1fcec2fd5f8..40254f2fafb2 100644 --- 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 + /* string descriptors must not be fetched using a 255-byte read */ #define USB_QUIRK_STRING_FETCH_255 BIT(0) -- 2.14.1 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html