From 7ceec1f1d26f966c0816b86a1aab1e0b3b208757 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 26 Jan 2007 14:26:21 +0100 Subject: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Makefile | 2 +- drivers/usb/core/hub.c | 3 ++ drivers/usb/core/message.c | 6 +++- drivers/usb/core/quirks.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/sysfs.c | 11 +++++++ drivers/usb/core/usb.h | 1 + include/linux/usb.h | 1 + include/linux/usb/quirks.h | 11 +++++++ 8 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/core/quirks.c create mode 100644 include/linux/usb/quirks.h 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 @@ usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ config.o file.o buffer.o sysfs.o endpoint.o \ - devio.o notify.o generic.o + devio.o notify.o generic.o quirks.o ifeq ($(CONFIG_PCI),y) 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) if (!try_module_get(THIS_MODULE)) return -EINVAL; + /* Determine quirks */ + usb_detect_quirks(udev); + err = usb_get_configuration(udev); if (err < 0) { 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 @@ #include #include #include +#include #include #include @@ -685,7 +686,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, /* Try to read the string descriptor by asking for the maximum * possible number of bytes */ - rc = usb_get_string(dev, langid, index, buf, 255); + if (dev->quirks & USB_QUIRK_STRING_FETCH_255) + rc = -EIO; + else + rc = usb_get_string(dev, langid, index, buf, 255); /* If that failed try to read the descriptor length, then * 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 @@ +/* + * USB device quirk handling logic and table + * + * Copyright (c) 2007 Oliver Neukum + * Copyright (c) 2007 Greg Kroah-Hartman + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * + */ + +#include +#include +#include "usb.h" + +/* List of quirky USB devices. Please keep this list ordered by: + * 1) Vendor ID + * 2) Product ID + * 3) Class ID + * + * as we want specific devices to be overridden first, and only after that, any + * class specific quirks. + * + * Right now the logic aborts if it finds a valid device in the table, we might + * want to change that in the future if it turns out that a whole class of + * devices is broken... + */ +static const struct usb_device_id usb_quirk_list[] = { + /* HP 5300/5370C scanner */ + { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + + /* Elsa MicroLink 56k (V.250) */ + { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + + { } /* terminating entry must be last */ +}; + +static void usb_autosuspend_quirk(struct usb_device *udev) +{ + /* unbalanced resume to prevent autosuspends */ + usb_autoresume_device(udev); +} + +static const struct usb_device_id *find_id(struct usb_device *udev) +{ + const struct usb_device_id *id = usb_quirk_list; + + for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || + id->driver_info; id++) { + if (usb_match_device(udev, id)) + return id; + } + return NULL; +} + +/* + * Detect any quirks the device has, and do any housekeeping for it if needed. + */ +void usb_detect_quirks(struct usb_device *udev) +{ + const struct usb_device_id *id = usb_quirk_list; + + id = find_id(udev); + if (id) + udev->quirks = (u32)(id->driver_info); + if (udev->quirks) + dev_dbg(&udev->dev, "USB quirks for this device: %x\n", + udev->quirks); + + /* do any special quirk handling here if needed */ + if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND) + usb_autosuspend_quirk(udev); +} 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) } static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); +static ssize_t +show_quirks(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "0x%x\n", udev->quirks); +} +static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); + /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ @@ -204,6 +214,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_devnum.attr, &dev_attr_version.attr, &dev_attr_maxchild.attr, + &dev_attr_quirks.attr, NULL, }; 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, struct usb_interface *intf); extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device (struct usb_device *dev, int skip_ep0); +extern void usb_detect_quirks(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, 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 { struct usb_device *children[USB_MAXCHILDREN]; int pm_usage_cnt; /* usage counter for autosuspend */ + u32 quirks; /* quirks of the whole device */ #ifdef CONFIG_PM struct delayed_work autosuspend; /* for delayed autosuspends */ 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 @@ +/* + * This file holds the definitions of quirks found in USB devices. + * Only quirks that affect the whole device, not an interface, + * belong here. + */ + +/* device must not be autosuspended */ +#define USB_QUIRK_NO_AUTOSUSPEND 0x00000001 + +/* string descriptors must not be fetched using a 255-byte read */ +#define USB_QUIRK_STRING_FETCH_255 0x00000002 -- cgit v1.2.3