diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2012-07-19 12:39:13 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-07-19 15:44:58 -0700 |
commit | 80da2e0df5af700518611b7d1cc4fc9945bcaf95 (patch) | |
tree | c914e555d34b2339c49bd3f29ef5268b642664f5 /drivers/usb/core/quirks.c | |
parent | 66177cc10295ffdfc613c06f59b07a577d91ee82 (diff) | |
download | blackbird-op-linux-80da2e0df5af700518611b7d1cc4fc9945bcaf95.tar.gz blackbird-op-linux-80da2e0df5af700518611b7d1cc4fc9945bcaf95.zip |
usb: Add quirk detection based on interface information
When a whole class of devices (possibly from a specific vendor, or
across multiple vendors) require a quirk, explictly listing all devices
in the class make the quirks table unnecessarily large. Fix this by
allowing matching devices based on interface information.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core/quirks.c')
-rw-r--r-- | drivers/usb/core/quirks.c | 93 |
1 files changed, 72 insertions, 21 deletions
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 32d3adc315f5..cbd15d1d25d3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -15,17 +15,22 @@ #include <linux/usb/quirks.h> #include "usb.h" -/* List of quirky USB devices. Please keep this list ordered by: +/* Lists of quirky USB devices, split in device quirks and interface quirks. + * Device quirks are applied at the very beginning of the enumeration process, + * right after reading the device descriptor. They can thus only match on device + * information. + * + * Interface quirks are applied after reading all the configuration descriptors. + * They can match on both device and interface information. + * + * Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as + * interface quirks, as they only influence the enumeration process which is run + * before processing the interface quirks. + * + * Please keep the lists 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[] = { /* CBM - Flash disk */ @@ -156,16 +161,53 @@ static const struct usb_device_id usb_quirk_list[] = { { } /* terminating entry must be last */ }; -static const struct usb_device_id *find_id(struct usb_device *udev) +static const struct usb_device_id usb_interface_quirk_list[] = { + { } /* terminating entry must be last */ +}; + +static bool usb_match_any_interface(struct usb_device *udev, + const struct usb_device_id *id) +{ + unsigned int i; + + for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) { + struct usb_host_config *cfg = &udev->config[i]; + unsigned int j; + + for (j = 0; j < cfg->desc.bNumInterfaces; ++j) { + struct usb_interface_cache *cache; + struct usb_host_interface *intf; + + cache = cfg->intf_cache[j]; + if (cache->num_altsetting == 0) + continue; + + intf = &cache->altsetting[0]; + if (usb_match_one_id_intf(udev, intf, id)) + return true; + } + } + + return false; +} + +static u32 __usb_detect_quirks(struct usb_device *udev, + const struct usb_device_id *id) { - const struct usb_device_id *id = usb_quirk_list; + u32 quirks = 0; - for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || - id->driver_info; id++) { - if (usb_match_device(udev, id)) - return id; + for (; id->match_flags; id++) { + if (!usb_match_device(udev, id)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) && + !usb_match_any_interface(udev, id)) + continue; + + quirks |= (u32)(id->driver_info); } - return NULL; + + return quirks; } /* @@ -173,14 +215,10 @@ static const struct usb_device_id *find_id(struct usb_device *udev) */ 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); + udev->quirks = __usb_detect_quirks(udev, usb_quirk_list); if (udev->quirks) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", - udev->quirks); + udev->quirks); /* For the present, all devices default to USB-PERSIST enabled */ #if 0 /* was: #ifdef CONFIG_PM */ @@ -197,3 +235,16 @@ void usb_detect_quirks(struct usb_device *udev) udev->persist_enabled = 1; #endif /* CONFIG_PM */ } + +void usb_detect_interface_quirks(struct usb_device *udev) +{ + u32 quirks; + + quirks = __usb_detect_quirks(udev, usb_interface_quirk_list); + if (quirks == 0) + return; + + dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n", + quirks); + udev->quirks |= quirks; +} |