Skip to content
  • Greg Kroah-Hartman's avatar
    Revert "usb: core: Add "quirks" parameter for usbcore" · 95713fb8
    Greg Kroah-Hartman authored
    This reverts commit b27560e4
    
     as it
    breaks the build for some arches :(
    
    Reported-by: default avatarkbuild test robot <fengguang.wu@intel.com>
    Cc: Kai-Heng Feng <kai.heng.feng@canonical.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    
    diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
    index 1d1d53f85ddd..70a7398c20e2 100644
    --- a/Documentation/admin-guide/kernel-parameters.txt
    +++ b/Documentation/admin-guide/kernel-parameters.txt
    @@ -4368,6 +4368,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 f4a548471f0f..42faaeead81b 100644
    --- a/drivers/usb/core/quirks.c
    +++ b/drivers/usb/core/quirks.c
    @@ -11,6 +11,143 @@
     #include <linux/usb/hcd.h>
     #include "usb.h"
    
    +struct quirk_entry {
    +	u16 vid;
    +	u16 pid;
    +	u32 flags;
    +};
    +
    +static DEFINE_MUTEX(quirk_mutex);
    +
    +static struct quirk_entry *quirk_list;
    +static unsigned int quirk_count;
    +
    +static char quirks_param[128];
    +
    +static int quirks_param_set(const char *val, const struct kernel_param *kp)
    +{
    +	char *p, *field;
    +	u16 vid, pid;
    +	u32 flags;
    +	size_t i;
    +
    +	mutex_lock(&quirk_mutex);
    +
    +	if (!val || !*val) {
    +		quirk_count = 0;
    +		kfree(quirk_list);
    +		quirk_list = NULL;
    +		goto unlock;
    +	}
    +
    +	for (quirk_count = 1, i = 0; val[i]; i++)
    +		if (val[i] == ',')
    +			quirk_count++;
    +
    +	if (quirk_list) {
    +		kfree(quirk_list);
    +		quirk_list = NULL;
    +	}
    +
    +	quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
    +			     GFP_KERNEL);
    +	if (!quirk_list) {
    +		mutex_unlock(&quirk_mutex);
    +		return -ENOMEM;
    +	}
    +
    +	for (i = 0, p = (char *)val; p && *p;) {
    +		/* Each entry consists of VID:PID:flags */
    +		field = strsep(&p, ":");
    +		if (!field)
    +			break;
    +
    +		if (kstrtou16(field, 16, &vid))
    +			break;
    +
    +		field = strsep(&p, ":");
    +		if (!field)
    +			break;
    +
    +		if (kstrtou16(field, 16, &pid))
    +			break;
    +
    +		field = strsep(&p, ",");
    +		if (!field || !*field)
    +			break;
    +
    +		/* Collect the flags */
    +		for (flags = 0; *field; field++) {
    +			switch (*field) {
    +			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 */
    +			}
    +		}
    +
    +		quirk_list[i++] = (struct quirk_entry)
    +			{ .vid = vid, .pid = pid, .flags = flags };
    +	}
    +
    +	if (i < quirk_count)
    +		quirk_count = i;
    +
    +unlock:
    +	mutex_unlock(&quirk_mutex);
    +
    +	return param_set_copystring(val, kp);
    +}
    +
    +static const struct kernel_param_ops quirks_param_ops = {
    +	.set = quirks_param_set,
    +	.get = param_get_string,
    +};
    +
    +static struct kparam_string quirks_param_string = {
    +	.maxlen = sizeof(quirks_param),
    +	.string = quirks_param,
    +};
    +
    +module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644);
    +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
    @@ -320,8 +457,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;
    
    @@ -339,21 +476,43 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
     	return quirks;
     }
    
    +static u32 usb_detect_dynamic_quirks(struct usb_device *udev)
    +{
    +	u16 vid = le16_to_cpu(udev->descriptor.idVendor);
    +	u16 pid = le16_to_cpu(udev->descriptor.idProduct);
    +	int i, flags = 0;
    +
    +	mutex_lock(&quirk_mutex);
    +
    +	for (i = 0; i < quirk_count; i++) {
    +		if (vid == quirk_list[i].vid && pid == quirk_list[i].pid) {
    +			flags = quirk_list[i].flags;
    +			break;
    +		}
    +	}
    +
    +	mutex_unlock(&quirk_mutex);
    +
    +	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_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);
    
    +	udev->quirks ^= usb_detect_dynamic_quirks(udev);
    +
     	if (udev->quirks)
     		dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
     			udev->quirks);
    @@ -372,7 +531,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;
    
    @@ -380,3 +539,11 @@ void usb_detect_interface_quirks(struct usb_device *udev)
     		quirks);
     	udev->quirks |= quirks;
     }
    +
    +void usb_release_quirk_list(void)
    +{
    +	mutex_lock(&quirk_mutex);
    +	kfree(quirk_list);
    +	quirk_list = NULL;
    +	mutex_unlock(&quirk_mutex);
    +}
    diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
    index 2f5fbc56a9dd..0adb6345ff2e 100644
    --- a/drivers/usb/core/usb.c
    +++ b/drivers/usb/core/usb.c
    @@ -1259,6 +1259,7 @@ static void __exit usb_exit(void)
     	if (usb_disabled())
     		return;
    
    +	usb_release_quirk_list();
     	usb_deregister_device_driver(&usb_generic_driver);
     	usb_major_cleanup();
     	usb_deregister(&usbfs_driver);
    diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
    index 149cc7480971..546a2219454b 100644
    --- a/drivers/usb/core/usb.h
    +++ b/drivers/usb/core/usb.h
    @@ -36,6 +36,7 @@ extern void usb_deauthorize_interface(struct usb_interface *);
     extern void usb_authorize_interface(struct usb_interface *);
     extern void usb_detect_quirks(struct usb_device *udev);
     extern void usb_detect_interface_quirks(struct usb_device *udev);
    +extern void usb_release_quirk_list(void);
     extern int usb_remove_device(struct usb_device *udev);
    
     extern int usb_get_device_descriptor(struct usb_device *dev,
    95713fb8