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>
This commit is contained in:
		
							parent
							
								
									bb417020ba
								
							
						
					
					
						commit
						7ceec1f1d2
					
				| @ -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 | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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 */ | ||||
|  | ||||
							
								
								
									
										75
									
								
								drivers/usb/core/quirks.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								drivers/usb/core/quirks.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| } | ||||
| @ -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 = { | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 */ | ||||
|  | ||||
							
								
								
									
										11
									
								
								include/linux/usb/quirks.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								include/linux/usb/quirks.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user