USB: Add a new quirk: USB_QUIRK_HONOR_BNUMINTERFACES
authorHans de Goede <hdegoede@redhat.com>
Mon, 29 Mar 2010 10:03:17 +0000 (12:03 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 20 May 2010 20:21:32 +0000 (13:21 -0700)
Add a new quirk USB_QUIRK_HONOR_BNUMINTERFACES, when this quirk is
set and a device has more interface descriptors in a configuration
then it claims to have in config->bNumInterfaces, ignore all additional
interfaces.

This is needed for devices which try to hide unused interfaces by only
lowering config->bNumInterfaces, and which can't handle if you try to talk
to the "hidden" interfaces.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/config.c
drivers/usb/core/quirks.c
include/linux/usb/quirks.h

index 77e0dda..16c1157 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/usb.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -479,9 +480,10 @@ skip_to_next_interface_descriptor:
        return buffer - buffer0 + i;
 }
 
-static int usb_parse_configuration(struct device *ddev, int cfgidx,
+static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
     struct usb_host_config *config, unsigned char *buffer, int size)
 {
+       struct device *ddev = &dev->dev;
        unsigned char *buffer0 = buffer;
        int cfgno;
        int nintf, nintf_orig;
@@ -550,6 +552,16 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
                        }
 
                        inum = d->bInterfaceNumber;
+
+                       if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
+                           n >= nintf_orig) {
+                               dev_warn(ddev, "config %d has more interface "
+                                   "descriptors, than it declares in "
+                                   "bNumInterfaces, ignoring interface "
+                                   "number: %d\n", cfgno, inum);
+                               continue;
+                       }
+
                        if (inum >= nintf_orig)
                                dev_warn(ddev, "config %d has an invalid "
                                    "interface number: %d but max is %d\n",
@@ -801,7 +813,7 @@ int usb_get_configuration(struct usb_device *dev)
 
                dev->rawdescriptors[cfgno] = bigbuffer;
 
-               result = usb_parse_configuration(&dev->dev, cfgno,
+               result = usb_parse_configuration(dev, cfgno,
                    &dev->config[cfgno], bigbuffer, length);
                if (result < 0) {
                        ++cfgno;
index f073c5c..f22d03d 100644 (file)
@@ -71,6 +71,10 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* SKYMEDI USB_DRIVE */
        { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* BUILDWIN Photo Frame */
+       { USB_DEVICE(0x1908, 0x1315), .driver_info =
+                       USB_QUIRK_HONOR_BNUMINTERFACES },
+
        /* INTEL VALUE SSD */
        { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
 
index 0a555dd..16b7f33 100644 (file)
@@ -22,4 +22,8 @@
 /*device will morph if reset, don't use reset for handling errors */
 #define USB_QUIRK_RESET_MORPHS         0x00000010
 
+/* device has more interface descriptions than the bNumInterfaces count,
+   and can't handle talking to these interfaces */
+#define USB_QUIRK_HONOR_BNUMINTERFACES 0x00000020
+
 #endif /* __LINUX_USB_QUIRKS_H */