Greg KH wrote:
>On Mon, Mar 13, 2006 at 09:11:11PM -0500, Craig W. Nadler wrote:
>
>
>> 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
>>
>>
>
>Why "1stIf"?
>
>And what about sysfs support? That's more important these days than
>usbfs files :)
>
>thanks,
>
>greg k-h
>
>
>
>
Interface Association Descriptors must refer to consecutively numbered
interfaces. "1stIf" was meant to be the first interface number
(bFirstInterface) that the Interface Association Descriptor referred to.
"IfCount" is the number of interfaces that the Interface Association
Descriptor refers to. I've changed "1stIf" to "FirstIf#" so that it is
hopefully more readable.
Thanks for pointing out my oversight with sysfs. A new patch file is
attached with a number for updates.
The USBFS output now is in the following format:
A: FirstIf#= 0 IfCount= 2 Cls=ff(vend.) Sub=03 Prot=00
A: FirstIf#= 2 IfCount= 2 Cls=01(audio) Sub=02 Prot=00
In SYSFS Interfaces that are referenced by an Interface Association
Descriptor have the following files:
iad_bFirstInterface
iad_bInterfaceCount
iad_bFunctionClass
iad_bFunctionSubClass
iad_bFunctionProtocol
Best Regards,
Craig Nadler
--- a/include/linux/usb.h 2006-03-14 23:54:54.000000000 -0500
+++ b/include/linux/usb.h 2006-03-15 00:04:57.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-15 00:09:45.000000000 -0500
@@ -233,6 +233,7 @@
struct usb_descriptor_header *header;
int len, retval;
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
+ u8 iad_num = 0;
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -310,6 +311,17 @@
++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] = header;
+ iad_num++;
+ }
+
} else if (header->bDescriptorType == USB_DT_DEVICE ||
header->bDescriptorType == USB_DT_CONFIG)
dev_warn(ddev, "config %d contains an unexpected "
--- a/drivers/usb/core/devices.c 2006-03-14 23:54:52.000000000 -0500
+++ b/drivers/usb/core/devices.c 2006-03-15 00:47:30.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: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
+ "A: FirstIf#=%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];
--- a/drivers/usb/core/message.c 2006-03-14 23:54:52.000000000 -0500
+++ b/drivers/usb/core/message.c 2006-03-15 02:50:21.000000000 -0500
@@ -1294,6 +1294,29 @@
kfree(intf);
}
+
+
+static struct usb_interface_assoc_descriptor *find_iad(
+ struct usb_host_config *config, u8 inum)
+{
+ u8 i;
+ for (i=0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) {
+ struct usb_interface_assoc_descriptor *intf_assoc;
+ u8 first_intf, last_intf;
+
+ intf_assoc = config->intf_assoc[i];
+ if (intf_assoc->bInterfaceCount == 0) continue;
+
+ first_intf = intf_assoc->bFirstInterface;
+ last_intf = first_intf + (intf_assoc->bInterfaceCount - 1);
+ if (inum >= first_intf && inum <= last_intf)
+ return intf_assoc;
+ }
+
+ return NULL;
+}
+
+
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
@@ -1417,6 +1440,7 @@
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
+ intf->intf_assoc = find_iad(cp, i);
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
--- a/drivers/usb/core/sysfs.c 2006-01-02 22:21:10.000000000 -0500
+++ b/drivers/usb/core/sysfs.c 2006-03-15 02:02:25.000000000 -0500
@@ -439,6 +439,25 @@
device_remove_file (dev, &dev_attr_configuration);
}
+/* Interface Accociation Descriptor fields */
+#define usb_intf_assoc_attr(field, format_string) \
+static ssize_t \
+show_iad_##field (struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface (dev); \
+ \
+ return sprintf (buf, format_string, \
+ intf->intf_assoc->field); \
+} \
+static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);
+
+usb_intf_assoc_attr (bFirstInterface, "%02x\n")
+usb_intf_assoc_attr (bInterfaceCount, "%02d\n")
+usb_intf_assoc_attr (bFunctionClass, "%02x\n")
+usb_intf_assoc_attr (bFunctionSubClass, "%02x\n")
+usb_intf_assoc_attr (bFunctionProtocol, "%02x\n")
+
/* Interface fields */
#define usb_intf_attr(field, format_string) \
static ssize_t \
@@ -502,6 +521,18 @@
}
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+static struct attribute *intf_assoc_attrs[] = {
+ &dev_attr_iad_bFirstInterface.attr,
+ &dev_attr_iad_bInterfaceCount.attr,
+ &dev_attr_iad_bFunctionClass.attr,
+ &dev_attr_iad_bFunctionSubClass.attr,
+ &dev_attr_iad_bFunctionProtocol.attr,
+ NULL,
+};
+static struct attribute_group intf_assoc_attr_grp = {
+ .attrs = intf_assoc_attrs,
+};
+
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
&dev_attr_bAlternateSetting.attr,
@@ -549,6 +580,8 @@
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
device_create_file(&intf->dev, &dev_attr_interface);
+ if (intf->intf_assoc)
+ sysfs_create_group(&intf->dev.kobj, &intf_assoc_attr_grp);
usb_create_intf_ep_files(intf, udev);
}
@@ -559,4 +592,6 @@
if (intf->cur_altsetting->string)
device_remove_file(&intf->dev, &dev_attr_interface);
+ if (intf->intf_assoc)
+ sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp);
}