Patchwork Request for suggestions: USB Interface Association Descriptors

login
register
mail settings
Submitter Brad Hards
Date March 25, 2011, 8:43 a.m.
Message ID <201103251943.52198.bradh@frogmouth.net>
Download mbox | patch
Permalink /patch/88348/
State New
Headers show

Comments

Brad Hards - March 25, 2011, 8:43 a.m.
Hi,

I've just started the learning process on QEMU, and am trying to create a USB 
webcam virtual device (targetting the Linux UVC driver). So far, I'm able to 
create the device, plug it in (thanks to those involved in qdev), and inspect 
the descriptor.

The problem is building the descriptor, and particularly how to do an 
Interface Association Descriptor. This is covered in Section 9.6.4 of the USB 
3.0 spec, but there is a better description in the Intel IAD whitepaper 
(www.usb.org/developers/whitepapers/iadclasscode_r10.pdf).

The short version is that IAD is an extra descriptor type that appears before 
a group (two or more) interface descriptors, that explains which interface 
descriptors make up a virtual device.  So it could look like:
Config Desc
IAD#0
Iface#0
Iface#1
Iface#2
IAD#1
Iface#3
Iface#4

[Check the diagram in the Intel IAD whitepaper if that makes no sense]

I've managed to make the USB descriptor code produce my descriptor, but the 
change has (at least) two problems I'd appreciate suggestions on how to fix:
1. I can only do the simple case where there is 0 or 1 IADs.
2. The change impacts on every USB configuration descriptor, even though IAD 
isn't very common.

Here is what I'm currently doing in any case (not complete - I haven't 
converted over all the existing device to have the .niad = 0 bit, since I got 
a bad feeling about it.  Not sure what the problem is - it just feels a bit 
wrong).

Suggestions?

Brad

Patch

--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -91,6 +91,15 @@  int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, 
size_t len)
     dest[0x08] = conf->bMaxPower;
     wTotalLength += bLength;
 
+    /* handle Interface Association Descriptor, if present */
+    if (conf->niad != 0) {
+        rc = usb_desc_iad(conf->iad, dest + wTotalLength, len - 
wTotalLength);
+        if (rc < 0) {
+            return rc;
+        }
+        wTotalLength += rc;
+    }
+    
     count = conf->nif ? conf->nif : conf->bNumInterfaces;
     for (i = 0; i < count; i++) {
         rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - 
wTotalLength);
@@ -105,6 +114,26 @@  int usb_desc_config(const USBDescConfig *conf, uint8_t 
*dest, size_t len)
     return wTotalLength;
 }
 
+int usb_desc_iad(const USBDescIfaceAssoc *iad, uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x08;
+    
+    if (len < bLength) {
+        return -1;
+    }
+    
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_INTERFACE_ASSOC;
+    dest[0x02] = iad->bFirstInterface;
+    dest[0x03] = iad->bInterfaceCount;
+    dest[0x04] = iad->bFunctionClass;
+    dest[0x05] = iad->bFunctionSubClass;
+    dest[0x06] = iad->bFunctionProtocol;
+    dest[0x07] = iad->iFunction;
+    
+    return bLength;
+}
+
 int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x09;
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index ac734ab..cf5f794 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -30,10 +30,22 @@  struct USBDescConfig {
     uint8_t                   bmAttributes;
     uint8_t                   bMaxPower;
 
+    uint8_t                   niad; /* 0 or 1 for now */
+    const USBDescIfaceAssoc   *iad;
+
     uint8_t                   nif;
     const USBDescIface        *ifs;
 };
 
+struct USBDescIfaceAssoc {
+    uint8_t                   bFirstInterface;
+    uint8_t                   bInterfaceCount;
+    uint8_t                   bFunctionClass;
+    uint8_t                   bFunctionSubClass;
+    uint8_t                   bFunctionProtocol;
+    uint8_t                   iFunction;
+};
+
 struct USBDescIface {
     uint8_t                   bInterfaceNumber;
     uint8_t                   bAlternateSetting;
@@ -75,6 +87,7 @@  int usb_desc_device(const USBDescID *id, const USBDescDevice 
*dev,
 int usb_desc_device_qualifier(const USBDescDevice *dev,
                               uint8_t *dest, size_t len);
 int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
+int usb_desc_iad(const USBDescIfaceAssoc *iad, uint8_t *dest, size_t len);
 int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
 int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
 int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index c25362c..acbcddc 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -211,6 +211,7 @@  static const USBDescDevice desc_device_mouse = {
             .iConfiguration        = STR_CONFIG_MOUSE,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .niad                  = 0,
             .ifs = &desc_iface_mouse,
         },
     },
@@ -227,6 +228,7 @@  static const USBDescDevice desc_device_tablet = {
             .iConfiguration        = STR_CONFIG_TABLET,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .niad                  = 0,
             .ifs = &desc_iface_tablet,
         },
     },
@@ -243,6 +245,7 @@  static const USBDescDevice desc_device_keyboard = {
             .iConfiguration        = STR_CONFIG_KEYBOARD,
             .bmAttributes          = 0xa0,
             .bMaxPower             = 50,
+            .niad                  = 0,
             .ifs = &desc_iface_keyboard,
         },
     },
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 3dd31ba..bc06a01 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -119,6 +119,7 @@  static const USBDescDevice desc_device_hub = {
             .bNumInterfaces        = 1,
             .bConfigurationValue   = 1,
             .bmAttributes          = 0xe0,
+            .niad                  = 0,
             .ifs = &desc_iface_hub,
         },
     },
diff --git a/hw/usb.h b/hw/usb.h
index d3d755d..b1e8879 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -124,6 +124,7 @@ 
 #define USB_DT_ENDPOINT                        0x05
 #define USB_DT_DEVICE_QUALIFIER         0x06
 #define USB_DT_OTHER_SPEED_CONFIG       0x07
+#define USB_DT_INTERFACE_ASSOC  0x0B
 
 #define USB_ENDPOINT_XFER_CONTROL      0
 #define USB_ENDPOINT_XFER_ISOC         1
@@ -140,6 +141,7 @@  typedef struct USBDesc USBDesc;
 typedef struct USBDescID USBDescID;
 typedef struct USBDescDevice USBDescDevice;
 typedef struct USBDescConfig USBDescConfig;
+typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
 typedef struct USBDescIface USBDescIface;
 typedef struct USBDescEndpoint USBDescEndpoint;
 typedef struct USBDescOther USBDescOther;