I've put together a patch demonstrating how Interface Association
Descriptors could be supported with a minimal amount of changes the
current USB stack. When a USB device is enumerated the descriptors are
parsed by the code in drivers/usb/core/config.c. A list of IADs for a
given configuration would be stored in the usb_host_config struct for
that configuration. If an interface is referenced by an IAD then a
pointer to the IAD will be stored in the usb_host_interface struct for
that interface.
The probe entry point of a class driver could check if the interface
it's probing is referenced by an IAD. If it is then configure the driver
to handle the other associated interfaces as well. The probe function
could also check the "extra" and "extralen" fields in the
usb_host_interface struct for each interface to get the class specific
descriptors.
The code that displays descriptor information in /proc/bus/usb/devices
will show IADs in the following format:
A: 1stIf= 0 IfCount= 2 Cls=ff(vend.) Sub=03 Prot=00
With the patch the descriptors for the Logitech QuickCam Pro 5000 are
shown in /proc/bus/usb/devices as:
T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 3 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=046d ProdID=08c5 Rev= 0.05
C:* #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA
A: 1stIf= 0 IfCount= 2 Cls=ff(vend.) Sub=03 Prot=00
A: 1stIf= 2 IfCount= 2 Cls=01(audio) Sub=02 Prot=00
I: If#= 0 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=01 Prot=00 Driver=(none)
E: Ad=87(I) Atr=03(Int.) MxPS= 16 Ivl=16ms
I: If#= 1 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
I: If#= 1 Alt= 1 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 192 Ivl=125us
I: If#= 1 Alt= 2 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 384 Ivl=125us
I: If#= 1 Alt= 3 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 512 Ivl=125us
I: If#= 1 Alt= 4 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 640 Ivl=125us
I: If#= 1 Alt= 5 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 800 Ivl=125us
I: If#= 1 Alt= 6 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS= 944 Ivl=125us
I: If#= 1 Alt= 7 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS=1280 Ivl=125us
I: If#= 1 Alt= 8 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS=1600 Ivl=125us
I: If#= 1 Alt= 9 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS=1984 Ivl=125us
I: If#= 1 Alt=10 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS=2688 Ivl=125us
I: If#= 1 Alt=11 #EPs= 1 Cls=ff(vend.) Sub=02 Prot=00 Driver=(none)
E: Ad=81(I) Atr=01(Isoc) MxPS=3060 Ivl=125us
I: If#= 2 Alt= 0 #EPs= 0 Cls=01(audio) Sub=01 Prot=00 Driver=(none)
I: If#= 3 Alt= 0 #EPs= 0 Cls=01(audio) Sub=02 Prot=00 Driver=(none)
I: If#= 3 Alt= 1 #EPs= 1 Cls=01(audio) Sub=02 Prot=00 Driver=(none)
E: Ad=86(I) Atr=05(Isoc) MxPS= 16 Ivl=1ms
I: If#= 3 Alt= 2 #EPs= 1 Cls=01(audio) Sub=02 Prot=00 Driver=(none)
E: Ad=86(I) Atr=05(Isoc) MxPS= 32 Ivl=1ms
I: If#= 3 Alt= 3 #EPs= 1 Cls=01(audio) Sub=02 Prot=00 Driver=(none)
E: Ad=86(I) Atr=05(Isoc) MxPS= 48 Ivl=1ms
Best Regards,
Craig Nadler
--- a/include/linux/usb.h 2006-03-04 21:04:03.000000000 -0500
+++ b/include/linux/usb.h 2006-03-04 20:34:46.000000000 -0500
@@ -138,6 +138,10 @@
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
+ /* If there is an interface association descriptor then it will list
+ * the associated interfaces */
+ struct usb_interface_assoc_descriptor *intf_assoc;
+
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
@@ -163,6 +167,7 @@
/* this maximum is arbitrary */
#define USB_MAXINTERFACES 32
+#define USB_MAXIADS USB_MAXINTERFACES/2
/**
* struct usb_interface_cache - long-term representation of a device interface
@@ -233,6 +238,11 @@
struct usb_config_descriptor desc;
char *string; /* iConfiguration string, if present */
+
+ /* List of any Interface Association Descriptors in this
+ * configuration. */
+ struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
+
/* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES];
--- a/drivers/usb/core/config.c 2006-01-02 22:21:10.000000000 -0500
+++ b/drivers/usb/core/config.c 2006-03-05 01:19:33.000000000 -0500
@@ -233,6 +233,7 @@
struct usb_descriptor_header *header;
int len, retval;
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
+ u8 iad_num;
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -258,6 +259,7 @@
/* Go through the descriptors, checking their length and counting the
* number of altsettings for each interface */
n = 0;
+ iad_num = 0;
for ((buffer2 = buffer, size2 = size);
size2 > 0;
(buffer2 += header->bLength, size2 -= header->bLength)) {
@@ -310,6 +312,22 @@
++n;
}
+ } else if (header->bDescriptorType ==
+ USB_DT_INTERFACE_ASSOCIATION) {
+ if (iad_num == USB_MAXIADS) {
+ dev_err(ddev, "found more Interface "
+ "Association Descriptors "
+ "than allocated for\n");
+ } else {
+ config->intf_assoc[iad_num] = kzalloc(
+ header->bLength, GFP_KERNEL);
+ if (!config->intf_assoc[iad_num])
+ return -ENOMEM;
+ memcpy (config->intf_assoc[iad_num], header,
+ header->bLength);
+ iad_num++;
+ }
+
} else if (header->bDescriptorType == USB_DT_DEVICE ||
header->bDescriptorType == USB_DT_CONFIG)
dev_warn(ddev, "config %d contains an unexpected "
@@ -419,6 +437,10 @@
struct usb_host_config *cf = &dev->config[c];
kfree(cf->string);
+ for (i = 0; i < USB_MAXIADS; i++) {
+ if (cf->intf_assoc[i])
+ kfree(cf->intf_assoc[i]);
+ }
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
if (cf->intf_cache[i])
kref_put(&cf->intf_cache[i]->ref,
--- a/drivers/usb/core/devices.c 2006-03-04 21:04:00.000000000 -0500
+++ b/drivers/usb/core/devices.c 2006-03-04 17:16:38.000000000 -0500
@@ -101,6 +101,10 @@
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
+static const char *format_iad =
+/* A: 1stIf=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
+ "A: 1stIf=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
+
static const char *format_iface =
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
"I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
@@ -145,6 +149,7 @@
{USB_CLASS_STILL_IMAGE, "still"},
{USB_CLASS_CSCID, "scard"},
{USB_CLASS_CONTENT_SEC, "c-sec"},
+ {USB_CLASS_VIDEO, "video"},
{-1, "unk."} /* leave as last */
};
@@ -282,6 +287,21 @@
return start;
}
+static char *usb_dump_iad_descriptor(char *start, char *end,
+ const struct usb_interface_assoc_descriptor *iad)
+{
+ if (start > end)
+ return start;
+ start += sprintf(start, format_iad,
+ iad->bFirstInterface,
+ iad->bInterfaceCount,
+ iad->bFunctionClass,
+ class_decode(iad->bFunctionClass),
+ iad->bFunctionSubClass,
+ iad->bFunctionProtocol);
+ return start;
+}
+
/* TBD:
* 0. TBDs
* 1. marking active interface altsettings (code lists all, but should mark
@@ -318,6 +338,11 @@
if (!config) /* getting these some in 2.3.7; none in 2.3.6 */
return start + sprintf(start, "(null Cfg. desc.)\n");
start = usb_dump_config_descriptor(start, end, &config->desc, active);
+ for (i = 0; i < USB_MAXIADS; i++) {
+ if (config->intf_assoc[i] == NULL) break;
+ start = usb_dump_iad_descriptor(start, end,
+ config->intf_assoc[i]);
+ }
for (i = 0; i < config->desc.bNumInterfaces; i++) {
intfc = config->intf_cache[i];
interface = config->interface[i];