From patchwork Fri Mar 25 08:43:51 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brad Hards X-Patchwork-Id: 88348 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 0EBCAB6F91 for ; Fri, 25 Mar 2011 19:44:55 +1100 (EST) Received: from localhost ([127.0.0.1]:41165 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q32dX-0005Cs-0Z for incoming@patchwork.ozlabs.org; Fri, 25 Mar 2011 04:44:51 -0400 Received: from [140.186.70.92] (port=56324 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q32co-000561-OI for qemu-devel@nongnu.org; Fri, 25 Mar 2011 04:44:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Q32cm-0005Iy-5C for qemu-devel@nongnu.org; Fri, 25 Mar 2011 04:44:05 -0400 Received: from ipmail04.adl6.internode.on.net ([150.101.137.141]:49990) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Q32cl-0005GF-9J for qemu-devel@nongnu.org; Fri, 25 Mar 2011 04:44:04 -0400 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: ApsEAFxSjE2WZXPN/2dsb2JhbACmR8RPhWkE Received: from ppp115-205.static.internode.on.net (HELO incana.localnet) ([150.101.115.205]) by ipmail04.adl6.internode.on.net with ESMTP; 25 Mar 2011 19:13:53 +1030 From: Brad Hards To: qemu-devel@nongnu.org Date: Fri, 25 Mar 2011 19:43:51 +1100 User-Agent: KMail/1.13.5 (Linux/2.6.35-28-generic; KDE/4.5.1; x86_64; ; ) MIME-Version: 1.0 Message-Id: <201103251943.52198.bradh@frogmouth.net> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 150.101.137.141 Subject: [Qemu-devel] Request for suggestions: USB Interface Association Descriptors X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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;