Skip to content

Commit b27560e

Browse files
khfenggregkh
authored andcommitted
usb: core: Add "quirks" parameter for usbcore
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. Also, the quirk parameter can XOR the builtin quirks for debugging purpose. This is inspired by usbhid and usb-storage. Signed-off-by: Kai-Heng Feng <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent cf6e06c commit b27560e

File tree

4 files changed

+229
-5
lines changed

4 files changed

+229
-5
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4368,6 +4368,61 @@
43684368

43694369
usbcore.nousb [USB] Disable the USB subsystem
43704370

4371+
usbcore.quirks=
4372+
[USB] A list of quirks entries to supplement or
4373+
override the built-in usb core quirk list. List
4374+
entries are separated by commas. Each entry has
4375+
the form VID:PID:Flags where VID and PID are Vendor
4376+
and Product ID values (4-digit hex numbers) and
4377+
Flags is a set of characters, each corresponding
4378+
to a common usb core quirk flag as follows:
4379+
a = USB_QUIRK_STRING_FETCH_255 (string
4380+
descriptors must not be fetched using
4381+
a 255-byte read);
4382+
b = USB_QUIRK_RESET_RESUME (device can't resume
4383+
correctly so reset it instead);
4384+
c = USB_QUIRK_NO_SET_INTF (device can't handle
4385+
Set-Interface requests);
4386+
d = USB_QUIRK_CONFIG_INTF_STRINGS (device can't
4387+
handle its Configuration or Interface
4388+
strings);
4389+
e = USB_QUIRK_RESET (device can't be reset
4390+
(e.g morph devices), don't use reset);
4391+
f = USB_QUIRK_HONOR_BNUMINTERFACES (device has
4392+
more interface descriptions than the
4393+
bNumInterfaces count, and can't handle
4394+
talking to these interfaces);
4395+
g = USB_QUIRK_DELAY_INIT (device needs a pause
4396+
during initialization, after we read
4397+
the device descriptor);
4398+
h = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL (For
4399+
high speed and super speed interrupt
4400+
endpoints, the USB 2.0 and USB 3.0 spec
4401+
require the interval in microframes (1
4402+
microframe = 125 microseconds) to be
4403+
calculated as interval = 2 ^
4404+
(bInterval-1).
4405+
Devices with this quirk report their
4406+
bInterval as the result of this
4407+
calculation instead of the exponent
4408+
variable used in the calculation);
4409+
i = USB_QUIRK_DEVICE_QUALIFIER (device can't
4410+
handle device_qualifier descriptor
4411+
requests);
4412+
j = USB_QUIRK_IGNORE_REMOTE_WAKEUP (device
4413+
generates spurious wakeup, ignore
4414+
remote wakeup capability);
4415+
k = USB_QUIRK_NO_LPM (device can't handle Link
4416+
Power Management);
4417+
l = USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL
4418+
(Device reports its bInterval as linear
4419+
frames instead of the USB 2.0
4420+
calculation);
4421+
m = USB_QUIRK_DISCONNECT_SUSPEND (Device needs
4422+
to be disconnected before suspend to
4423+
prevent spurious wakeup)
4424+
Example: quirks=0781:5580:bk,0a5c:5834:gij
4425+
43714426
usbhid.mousepoll=
43724427
[USBHID] The interval which mice are to be polled at.
43734428

drivers/usb/core/quirks.c

Lines changed: 172 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,143 @@
1111
#include <linux/usb/hcd.h>
1212
#include "usb.h"
1313

14+
struct quirk_entry {
15+
u16 vid;
16+
u16 pid;
17+
u32 flags;
18+
};
19+
20+
static DEFINE_MUTEX(quirk_mutex);
21+
22+
static struct quirk_entry *quirk_list;
23+
static unsigned int quirk_count;
24+
25+
static char quirks_param[128];
26+
27+
static int quirks_param_set(const char *val, const struct kernel_param *kp)
28+
{
29+
char *p, *field;
30+
u16 vid, pid;
31+
u32 flags;
32+
size_t i;
33+
34+
mutex_lock(&quirk_mutex);
35+
36+
if (!val || !*val) {
37+
quirk_count = 0;
38+
kfree(quirk_list);
39+
quirk_list = NULL;
40+
goto unlock;
41+
}
42+
43+
for (quirk_count = 1, i = 0; val[i]; i++)
44+
if (val[i] == ',')
45+
quirk_count++;
46+
47+
if (quirk_list) {
48+
kfree(quirk_list);
49+
quirk_list = NULL;
50+
}
51+
52+
quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
53+
GFP_KERNEL);
54+
if (!quirk_list) {
55+
mutex_unlock(&quirk_mutex);
56+
return -ENOMEM;
57+
}
58+
59+
for (i = 0, p = (char *)val; p && *p;) {
60+
/* Each entry consists of VID:PID:flags */
61+
field = strsep(&p, ":");
62+
if (!field)
63+
break;
64+
65+
if (kstrtou16(field, 16, &vid))
66+
break;
67+
68+
field = strsep(&p, ":");
69+
if (!field)
70+
break;
71+
72+
if (kstrtou16(field, 16, &pid))
73+
break;
74+
75+
field = strsep(&p, ",");
76+
if (!field || !*field)
77+
break;
78+
79+
/* Collect the flags */
80+
for (flags = 0; *field; field++) {
81+
switch (*field) {
82+
case 'a':
83+
flags |= USB_QUIRK_STRING_FETCH_255;
84+
break;
85+
case 'b':
86+
flags |= USB_QUIRK_RESET_RESUME;
87+
break;
88+
case 'c':
89+
flags |= USB_QUIRK_NO_SET_INTF;
90+
break;
91+
case 'd':
92+
flags |= USB_QUIRK_CONFIG_INTF_STRINGS;
93+
break;
94+
case 'e':
95+
flags |= USB_QUIRK_RESET;
96+
break;
97+
case 'f':
98+
flags |= USB_QUIRK_HONOR_BNUMINTERFACES;
99+
break;
100+
case 'g':
101+
flags |= USB_QUIRK_DELAY_INIT;
102+
break;
103+
case 'h':
104+
flags |= USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL;
105+
break;
106+
case 'i':
107+
flags |= USB_QUIRK_DEVICE_QUALIFIER;
108+
break;
109+
case 'j':
110+
flags |= USB_QUIRK_IGNORE_REMOTE_WAKEUP;
111+
break;
112+
case 'k':
113+
flags |= USB_QUIRK_NO_LPM;
114+
break;
115+
case 'l':
116+
flags |= USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL;
117+
break;
118+
case 'm':
119+
flags |= USB_QUIRK_DISCONNECT_SUSPEND;
120+
break;
121+
/* Ignore unrecognized flag characters */
122+
}
123+
}
124+
125+
quirk_list[i++] = (struct quirk_entry)
126+
{ .vid = vid, .pid = pid, .flags = flags };
127+
}
128+
129+
if (i < quirk_count)
130+
quirk_count = i;
131+
132+
unlock:
133+
mutex_unlock(&quirk_mutex);
134+
135+
return param_set_copystring(val, kp);
136+
}
137+
138+
static const struct kernel_param_ops quirks_param_ops = {
139+
.set = quirks_param_set,
140+
.get = param_get_string,
141+
};
142+
143+
static struct kparam_string quirks_param_string = {
144+
.maxlen = sizeof(quirks_param),
145+
.string = quirks_param,
146+
};
147+
148+
module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644);
149+
MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying quirks=vendorID:productID:quirks");
150+
14151
/* Lists of quirky USB devices, split in device quirks and interface quirks.
15152
* Device quirks are applied at the very beginning of the enumeration process,
16153
* 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)
320457
return 0;
321458
}
322459

323-
static u32 __usb_detect_quirks(struct usb_device *udev,
324-
const struct usb_device_id *id)
460+
static u32 usb_detect_static_quirks(struct usb_device *udev,
461+
const struct usb_device_id *id)
325462
{
326463
u32 quirks = 0;
327464

@@ -339,21 +476,43 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
339476
return quirks;
340477
}
341478

479+
static u32 usb_detect_dynamic_quirks(struct usb_device *udev)
480+
{
481+
u16 vid = le16_to_cpu(udev->descriptor.idVendor);
482+
u16 pid = le16_to_cpu(udev->descriptor.idProduct);
483+
int i, flags = 0;
484+
485+
mutex_lock(&quirk_mutex);
486+
487+
for (i = 0; i < quirk_count; i++) {
488+
if (vid == quirk_list[i].vid && pid == quirk_list[i].pid) {
489+
flags = quirk_list[i].flags;
490+
break;
491+
}
492+
}
493+
494+
mutex_unlock(&quirk_mutex);
495+
496+
return flags;
497+
}
498+
342499
/*
343500
* Detect any quirks the device has, and do any housekeeping for it if needed.
344501
*/
345502
void usb_detect_quirks(struct usb_device *udev)
346503
{
347-
udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
504+
udev->quirks = usb_detect_static_quirks(udev, usb_quirk_list);
348505

349506
/*
350507
* Pixart-based mice would trigger remote wakeup issue on AMD
351508
* Yangtze chipset, so set them as RESET_RESUME flag.
352509
*/
353510
if (usb_amd_resume_quirk(udev))
354-
udev->quirks |= __usb_detect_quirks(udev,
511+
udev->quirks |= usb_detect_static_quirks(udev,
355512
usb_amd_resume_quirk_list);
356513

514+
udev->quirks ^= usb_detect_dynamic_quirks(udev);
515+
357516
if (udev->quirks)
358517
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
359518
udev->quirks);
@@ -372,11 +531,19 @@ void usb_detect_interface_quirks(struct usb_device *udev)
372531
{
373532
u32 quirks;
374533

375-
quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
534+
quirks = usb_detect_static_quirks(udev, usb_interface_quirk_list);
376535
if (quirks == 0)
377536
return;
378537

379538
dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
380539
quirks);
381540
udev->quirks |= quirks;
382541
}
542+
543+
void usb_release_quirk_list(void)
544+
{
545+
mutex_lock(&quirk_mutex);
546+
kfree(quirk_list);
547+
quirk_list = NULL;
548+
mutex_unlock(&quirk_mutex);
549+
}

drivers/usb/core/usb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,7 @@ static void __exit usb_exit(void)
12591259
if (usb_disabled())
12601260
return;
12611261

1262+
usb_release_quirk_list();
12621263
usb_deregister_device_driver(&usb_generic_driver);
12631264
usb_major_cleanup();
12641265
usb_deregister(&usbfs_driver);

drivers/usb/core/usb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern void usb_deauthorize_interface(struct usb_interface *);
3636
extern void usb_authorize_interface(struct usb_interface *);
3737
extern void usb_detect_quirks(struct usb_device *udev);
3838
extern void usb_detect_interface_quirks(struct usb_device *udev);
39+
extern void usb_release_quirk_list(void);
3940
extern int usb_remove_device(struct usb_device *udev);
4041

4142
extern int usb_get_device_descriptor(struct usb_device *dev,

0 commit comments

Comments
 (0)