Skip to content

Commit 027bd6c

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 ca5a2e9 commit 027bd6c

File tree

4 files changed

+231
-5
lines changed

4 files changed

+231
-5
lines changed

Documentation/admin-guide/kernel-parameters.txt

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

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

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

drivers/usb/core/quirks.c

Lines changed: 173 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,149 @@
66
* Copyright (c) 2007 Greg Kroah-Hartman <[email protected]>
77
*/
88

9+
#include <linux/moduleparam.h>
910
#include <linux/usb.h>
1011
#include <linux/usb/quirks.h>
1112
#include <linux/usb/hcd.h>
1213
#include "usb.h"
1314

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

324-
static u32 __usb_detect_quirks(struct usb_device *udev,
325-
const struct usb_device_id *id)
462+
static u32 usb_detect_static_quirks(struct usb_device *udev,
463+
const struct usb_device_id *id)
326464
{
327465
u32 quirks = 0;
328466

@@ -340,21 +478,43 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
340478
return quirks;
341479
}
342480

481+
static u32 usb_detect_dynamic_quirks(struct usb_device *udev)
482+
{
483+
u16 vid = le16_to_cpu(udev->descriptor.idVendor);
484+
u16 pid = le16_to_cpu(udev->descriptor.idProduct);
485+
int i, flags = 0;
486+
487+
mutex_lock(&quirk_mutex);
488+
489+
for (i = 0; i < quirk_count; i++) {
490+
if (vid == quirk_list[i].vid && pid == quirk_list[i].pid) {
491+
flags = quirk_list[i].flags;
492+
break;
493+
}
494+
}
495+
496+
mutex_unlock(&quirk_mutex);
497+
498+
return flags;
499+
}
500+
343501
/*
344502
* Detect any quirks the device has, and do any housekeeping for it if needed.
345503
*/
346504
void usb_detect_quirks(struct usb_device *udev)
347505
{
348-
udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
506+
udev->quirks = usb_detect_static_quirks(udev, usb_quirk_list);
349507

350508
/*
351509
* Pixart-based mice would trigger remote wakeup issue on AMD
352510
* Yangtze chipset, so set them as RESET_RESUME flag.
353511
*/
354512
if (usb_amd_resume_quirk(udev))
355-
udev->quirks |= __usb_detect_quirks(udev,
513+
udev->quirks |= usb_detect_static_quirks(udev,
356514
usb_amd_resume_quirk_list);
357515

516+
udev->quirks ^= usb_detect_dynamic_quirks(udev);
517+
358518
if (udev->quirks)
359519
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
360520
udev->quirks);
@@ -373,11 +533,19 @@ void usb_detect_interface_quirks(struct usb_device *udev)
373533
{
374534
u32 quirks;
375535

376-
quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
536+
quirks = usb_detect_static_quirks(udev, usb_interface_quirk_list);
377537
if (quirks == 0)
378538
return;
379539

380540
dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
381541
quirks);
382542
udev->quirks |= quirks;
383543
}
544+
545+
void usb_release_quirk_list(void)
546+
{
547+
mutex_lock(&quirk_mutex);
548+
kfree(quirk_list);
549+
quirk_list = NULL;
550+
mutex_unlock(&quirk_mutex);
551+
}

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)