aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2007-01-26 08:26:21 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-23 18:03:46 -0500
commit7ceec1f1d26f966c0816b86a1aab1e0b3b208757 (patch)
tree3b444fb3eedff17366a7aa01106ee7eed50e051a
parentbb417020ba8c559eb52f57379ba17f669f8f72cd (diff)
USB: add a blacklist for devices that can't handle some things we throw at them.
This adds a blacklist to the USB core to handle some autosuspend and string issues that devices have. Originally written by Oliver, but hacked up a lot by Greg. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/hub.c3
-rw-r--r--drivers/usb/core/message.c6
-rw-r--r--drivers/usb/core/quirks.c75
-rw-r--r--drivers/usb/core/sysfs.c11
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--include/linux/usb.h1
-rw-r--r--include/linux/usb/quirks.h11
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
5usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ 5usbcore-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
9ifeq ($(CONFIG_PCI),y) 9ifeq ($(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 */
30static 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
40static void usb_autosuspend_quirk(struct usb_device *udev)
41{
42 /* unbalanced resume to prevent autosuspends */
43 usb_autoresume_device(udev);
44}
45
46static 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 */
61void 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}
149static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); 149static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
150 150
151static ssize_t
152show_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}
159static 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) \
153static ssize_t \ 163static 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};
209static struct attribute_group dev_attr_grp = { 220static 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);
14extern void usb_release_interface_cache(struct kref *ref); 14extern void usb_release_interface_cache(struct kref *ref);
15extern void usb_disable_device (struct usb_device *dev, int skip_ep0); 15extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
16extern void usb_detect_quirks(struct usb_device *udev);
16 17
17extern int usb_get_device_descriptor(struct usb_device *dev, 18extern 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