USB: allow match on bInterfaceNumber
Some composite USB devices provide multiple interfaces
with different functions, all using "vendor-specific"
for class/subclass/protocol. Another OS use interface
numbers to match the driver and interface. It seems
these devices are designed with that in mind - using
static interface numbers for the different functions.
This adds support for matching against the
bInterfaceNumber, allowing such devices to be supported
without having to resort to testing against interface
number whitelists and/or blacklists in the probe.
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f536aeb..23d7bbd 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -622,14 +622,15 @@
if (!usb_match_device(dev, id))
return 0;
- /* The interface class, subclass, and protocol should never be
+ /* The interface class, subclass, protocol and number should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
- USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL |
+ USB_DEVICE_ID_MATCH_INT_NUMBER)))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
@@ -644,6 +645,10 @@
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
return 0;
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
+ (id->bInterfaceNumber != intf->desc.bInterfaceNumber))
+ return 0;
+
return 1;
}
EXPORT_SYMBOL_GPL(usb_match_one_id);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index b548cf1..ca7fc39 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1559,7 +1559,7 @@
if (add_uevent_var(env,
"MODALIAS=usb:"
- "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+ "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
@@ -1568,7 +1568,8 @@
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
+ alt->desc.bInterfaceProtocol,
+ alt->desc.bInterfaceNumber))
return -ENOMEM;
return 0;
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 9a56e3a..777f03c 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -840,7 +840,7 @@
alt = intf->cur_altsetting;
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
- "ic%02Xisc%02Xip%02X\n",
+ "ic%02Xisc%02Xip%02Xin%02X\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
le16_to_cpu(udev->descriptor.bcdDevice),
@@ -849,7 +849,8 @@
udev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol);
+ alt->desc.bInterfaceProtocol,
+ alt->desc.bInterfaceNumber);
}
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 5db9382..7771d45 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -78,6 +78,9 @@
* of a given interface; other interfaces may support other classes.
* @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass.
* @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass.
+ * @bInterfaceNumber: Number of interface; composite devices may use
+ * fixed interface numbers to differentiate between vendor-specific
+ * interfaces.
* @driver_info: Holds information used by the driver. Usually it holds
* a pointer to a descriptor understood by the driver, or perhaps
* device flags.
@@ -115,6 +118,9 @@
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
+ /* Used for vendor-specific interface matches */
+ __u8 bInterfaceNumber;
+
/* not matched against */
kernel_ulong_t driver_info;
};
@@ -130,6 +136,7 @@
#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
+#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400
#define HID_ANY_ID (~0)
#define HID_BUS_ANY 0xffff
diff --git a/include/linux/usb.h b/include/linux/usb.h
index dea39dc..f717fbd 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -777,6 +777,22 @@
.bInterfaceProtocol = (pr)
/**
+ * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @num: bInterfaceNumber value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific interface number of devices.
+ */
+#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_NUMBER, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceNumber = (num)
+
+/**
* USB_DEVICE_INFO - macro used to describe a class of usb devices
* @cl: bDeviceClass value
* @sc: bDeviceSubClass value
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 5759751..7ed6864 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -156,7 +156,7 @@
}
/* USB is special because the bcdDevice can be matched against a numeric range */
-/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */
+/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */
static void do_usb_entry(struct usb_device_id *id,
unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
unsigned char range_lo, unsigned char range_hi,
@@ -210,6 +210,9 @@
ADD(alias, "ip",
id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
id->bInterfaceProtocol);
+ ADD(alias, "in",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER,
+ id->bInterfaceNumber);
add_wildcard(alias);
buf_printf(&mod->dev_table_buf,