diff options
-rw-r--r-- | drivers/usb/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 75 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 1 | ||||
-rw-r--r-- | include/linux/usb.h | 1 | ||||
-rw-r--r-- | include/linux/usb/quirks.h | 11 |
8 files changed, 108 insertions, 2 deletions
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 34e9bac319b4..b6078706fb93 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ | 5 | usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ |
6 | config.o file.o buffer.o sysfs.o endpoint.o \ | 6 | config.o file.o buffer.o sysfs.o endpoint.o \ |
7 | devio.o notify.o generic.o | 7 | devio.o notify.o generic.o quirks.o |
8 | 8 | ||
9 | ifeq ($(CONFIG_PCI),y) | 9 | ifeq ($(CONFIG_PCI),y) |
10 | usbcore-objs += hcd-pci.o | 10 | usbcore-objs += hcd-pci.o |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 50c0db15304a..41400743ce2c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1287,6 +1287,9 @@ int usb_new_device(struct usb_device *udev) | |||
1287 | if (!try_module_get(THIS_MODULE)) | 1287 | if (!try_module_get(THIS_MODULE)) |
1288 | return -EINVAL; | 1288 | return -EINVAL; |
1289 | 1289 | ||
1290 | /* Determine quirks */ | ||
1291 | usb_detect_quirks(udev); | ||
1292 | |||
1290 | err = usb_get_configuration(udev); | 1293 | err = usb_get_configuration(udev); |
1291 | if (err < 0) { | 1294 | if (err < 0) { |
1292 | dev_err(&udev->dev, "can't read configurations, error %d\n", | 1295 | dev_err(&udev->dev, "can't read configurations, error %d\n", |
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 74edaea5665d..2f17468b5c1e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/timer.h> | 11 | #include <linux/timer.h> |
12 | #include <linux/ctype.h> | 12 | #include <linux/ctype.h> |
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/usb/quirks.h> | ||
14 | #include <asm/byteorder.h> | 15 | #include <asm/byteorder.h> |
15 | #include <asm/scatterlist.h> | 16 | #include <asm/scatterlist.h> |
16 | 17 | ||
@@ -685,7 +686,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, | |||
685 | 686 | ||
686 | /* Try to read the string descriptor by asking for the maximum | 687 | /* Try to read the string descriptor by asking for the maximum |
687 | * possible number of bytes */ | 688 | * possible number of bytes */ |
688 | rc = usb_get_string(dev, langid, index, buf, 255); | 689 | if (dev->quirks & USB_QUIRK_STRING_FETCH_255) |
690 | rc = -EIO; | ||
691 | else | ||
692 | rc = usb_get_string(dev, langid, index, buf, 255); | ||
689 | 693 | ||
690 | /* If that failed try to read the descriptor length, then | 694 | /* If that failed try to read the descriptor length, then |
691 | * ask for just that many bytes */ | 695 | * ask for just that many bytes */ |
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c new file mode 100644 index 000000000000..ea0e48e9f611 --- /dev/null +++ b/drivers/usb/core/quirks.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * USB device quirk handling logic and table | ||
3 | * | ||
4 | * Copyright (c) 2007 Oliver Neukum | ||
5 | * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the Free | ||
9 | * Software Foundation, version 2. | ||
10 | * | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/usb.h> | ||
15 | #include <linux/usb/quirks.h> | ||
16 | #include "usb.h" | ||
17 | |||
18 | /* List of quirky USB devices. Please keep this list ordered by: | ||
19 | * 1) Vendor ID | ||
20 | * 2) Product ID | ||
21 | * 3) Class ID | ||
22 | * | ||
23 | * as we want specific devices to be overridden first, and only after that, any | ||
24 | * class specific quirks. | ||
25 | * | ||
26 | * Right now the logic aborts if it finds a valid device in the table, we might | ||
27 | * want to change that in the future if it turns out that a whole class of | ||
28 | * devices is broken... | ||
29 | */ | ||
30 | static const struct usb_device_id usb_quirk_list[] = { | ||
31 | /* HP 5300/5370C scanner */ | ||
32 | { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, | ||
33 | |||
34 | /* Elsa MicroLink 56k (V.250) */ | ||
35 | { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, | ||
36 | |||
37 | { } /* terminating entry must be last */ | ||
38 | }; | ||
39 | |||
40 | static void usb_autosuspend_quirk(struct usb_device *udev) | ||
41 | { | ||
42 | /* unbalanced resume to prevent autosuspends */ | ||
43 | usb_autoresume_device(udev); | ||
44 | } | ||
45 | |||
46 | static const struct usb_device_id *find_id(struct usb_device *udev) | ||
47 | { | ||
48 | const struct usb_device_id *id = usb_quirk_list; | ||
49 | |||
50 | for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || | ||
51 | id->driver_info; id++) { | ||
52 | if (usb_match_device(udev, id)) | ||
53 | return id; | ||
54 | } | ||
55 | return NULL; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Detect any quirks the device has, and do any housekeeping for it if needed. | ||
60 | */ | ||
61 | void usb_detect_quirks(struct usb_device *udev) | ||
62 | { | ||
63 | const struct usb_device_id *id = usb_quirk_list; | ||
64 | |||
65 | id = find_id(udev); | ||
66 | if (id) | ||
67 | udev->quirks = (u32)(id->driver_info); | ||
68 | if (udev->quirks) | ||
69 | dev_dbg(&udev->dev, "USB quirks for this device: %x\n", | ||
70 | udev->quirks); | ||
71 | |||
72 | /* do any special quirk handling here if needed */ | ||
73 | if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND) | ||
74 | usb_autosuspend_quirk(udev); | ||
75 | } | ||
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 0edfbafd702c..8f5a764057cd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -148,6 +148,16 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf) | |||
148 | } | 148 | } |
149 | static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); | 149 | static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); |
150 | 150 | ||
151 | static ssize_t | ||
152 | show_quirks(struct device *dev, struct device_attribute *attr, char *buf) | ||
153 | { | ||
154 | struct usb_device *udev; | ||
155 | |||
156 | udev = to_usb_device(dev); | ||
157 | return sprintf(buf, "0x%x\n", udev->quirks); | ||
158 | } | ||
159 | static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); | ||
160 | |||
151 | /* Descriptor fields */ | 161 | /* Descriptor fields */ |
152 | #define usb_descriptor_attr_le16(field, format_string) \ | 162 | #define usb_descriptor_attr_le16(field, format_string) \ |
153 | static ssize_t \ | 163 | static ssize_t \ |
@@ -204,6 +214,7 @@ static struct attribute *dev_attrs[] = { | |||
204 | &dev_attr_devnum.attr, | 214 | &dev_attr_devnum.attr, |
205 | &dev_attr_version.attr, | 215 | &dev_attr_version.attr, |
206 | &dev_attr_maxchild.attr, | 216 | &dev_attr_maxchild.attr, |
217 | &dev_attr_quirks.attr, | ||
207 | NULL, | 218 | NULL, |
208 | }; | 219 | }; |
209 | static struct attribute_group dev_attr_grp = { | 220 | static struct attribute_group dev_attr_grp = { |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 86692a23573f..045cbd111887 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev, | |||
13 | struct usb_interface *intf); | 13 | struct usb_interface *intf); |
14 | extern void usb_release_interface_cache(struct kref *ref); | 14 | extern void usb_release_interface_cache(struct kref *ref); |
15 | extern void usb_disable_device (struct usb_device *dev, int skip_ep0); | 15 | extern void usb_disable_device (struct usb_device *dev, int skip_ep0); |
16 | extern void usb_detect_quirks(struct usb_device *udev); | ||
16 | 17 | ||
17 | extern int usb_get_device_descriptor(struct usb_device *dev, | 18 | extern int usb_get_device_descriptor(struct usb_device *dev, |
18 | unsigned int size); | 19 | unsigned int size); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index a8e8d1ecebb1..37e522eba47f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -388,6 +388,7 @@ struct usb_device { | |||
388 | struct usb_device *children[USB_MAXCHILDREN]; | 388 | struct usb_device *children[USB_MAXCHILDREN]; |
389 | 389 | ||
390 | int pm_usage_cnt; /* usage counter for autosuspend */ | 390 | int pm_usage_cnt; /* usage counter for autosuspend */ |
391 | u32 quirks; /* quirks of the whole device */ | ||
391 | #ifdef CONFIG_PM | 392 | #ifdef CONFIG_PM |
392 | struct delayed_work autosuspend; /* for delayed autosuspends */ | 393 | struct delayed_work autosuspend; /* for delayed autosuspends */ |
393 | struct mutex pm_mutex; /* protects PM operations */ | 394 | struct mutex pm_mutex; /* protects PM operations */ |
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h new file mode 100644 index 000000000000..6bac8faacbc6 --- /dev/null +++ b/include/linux/usb/quirks.h | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * This file holds the definitions of quirks found in USB devices. | ||
3 | * Only quirks that affect the whole device, not an interface, | ||
4 | * belong here. | ||
5 | */ | ||
6 | |||
7 | /* device must not be autosuspended */ | ||
8 | #define USB_QUIRK_NO_AUTOSUSPEND 0x00000001 | ||
9 | |||
10 | /* string descriptors must not be fetched using a 255-byte read */ | ||
11 | #define USB_QUIRK_STRING_FETCH_255 0x00000002 | ||