qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Brad Hards <bradh@frogmouth.net>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] Request for suggestions: USB Interface Association Descriptors
Date: Fri, 25 Mar 2011 19:43:51 +1100	[thread overview]
Message-ID: <201103251943.52198.bradh@frogmouth.net> (raw)

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

--- 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;

                 reply	other threads:[~2011-03-25  8:44 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=201103251943.52198.bradh@frogmouth.net \
    --to=bradh@frogmouth.net \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).