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 34e9bac319b..b6078706fb9 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 50c0db15304..41400743ce2 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 74edaea5665..2f17468b5c1 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -11,6 +11,7 @@  #include <linux/timer.h>  #include <linux/ctype.h>  #include <linux/device.h> +#include <linux/usb/quirks.h>  #include <asm/byteorder.h>  #include <asm/scatterlist.h> @@ -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 00000000000..ea0e48e9f61 --- /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 <gregkh@suse.de> + * + * 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 <linux/usb.h> +#include <linux/usb/quirks.h> +#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 0edfbafd702..8f5a764057c 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 86692a23573..045cbd11188 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 a8e8d1ecebb..37e522eba47 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 00000000000..6bac8faacbc --- /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  |