usb: add support for "grouped" interfaces and the Interface Association Descriptor
This is used for some devices that have multiple interfaces that form a logic device. An example is Video Class, which has a Control interface and a Streaming interface. There can be additional interfaces on the same (physical) devices (e.g. a microphone), and Interface Association Descriptor handles this case. Signed-off-by: Brad Hards <bradh@frogmouth.net> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
fef13fa8e4
commit
6e625fc704
@ -91,6 +91,18 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
|||||||
dest[0x08] = conf->bMaxPower;
|
dest[0x08] = conf->bMaxPower;
|
||||||
wTotalLength += bLength;
|
wTotalLength += bLength;
|
||||||
|
|
||||||
|
/* handle grouped interfaces if any*/
|
||||||
|
for (i = 0; i < conf->nif_groups; i++) {
|
||||||
|
rc = usb_desc_iface_group(&(conf->if_groups[i]),
|
||||||
|
dest + wTotalLength,
|
||||||
|
len - wTotalLength);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
wTotalLength += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handle normal (ungrouped / no IAD) interfaces if any */
|
||||||
for (i = 0; i < conf->nif; i++) {
|
for (i = 0; i < conf->nif; i++) {
|
||||||
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
|
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@ -104,6 +116,41 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
|||||||
return wTotalLength;
|
return wTotalLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
/* handle interface association descriptor */
|
||||||
|
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;
|
||||||
|
pos += bLength;
|
||||||
|
|
||||||
|
/* handle associated interfaces in this group */
|
||||||
|
for (i = 0; i < iad->nif; i++) {
|
||||||
|
int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
pos += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
|
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
|
||||||
{
|
{
|
||||||
uint8_t bLength = 0x09;
|
uint8_t bLength = 0x09;
|
||||||
|
@ -30,6 +30,24 @@ struct USBDescConfig {
|
|||||||
uint8_t bmAttributes;
|
uint8_t bmAttributes;
|
||||||
uint8_t bMaxPower;
|
uint8_t bMaxPower;
|
||||||
|
|
||||||
|
/* grouped interfaces */
|
||||||
|
uint8_t nif_groups;
|
||||||
|
const USBDescIfaceAssoc *if_groups;
|
||||||
|
|
||||||
|
/* "normal" interfaces */
|
||||||
|
uint8_t nif;
|
||||||
|
const USBDescIface *ifs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* conceptually an Interface Association Descriptor, and releated interfaces */
|
||||||
|
struct USBDescIfaceAssoc {
|
||||||
|
uint8_t bFirstInterface;
|
||||||
|
uint8_t bInterfaceCount;
|
||||||
|
uint8_t bFunctionClass;
|
||||||
|
uint8_t bFunctionSubClass;
|
||||||
|
uint8_t bFunctionProtocol;
|
||||||
|
uint8_t iFunction;
|
||||||
|
|
||||||
uint8_t nif;
|
uint8_t nif;
|
||||||
const USBDescIface *ifs;
|
const USBDescIface *ifs;
|
||||||
};
|
};
|
||||||
@ -75,6 +93,8 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
|||||||
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
||||||
uint8_t *dest, size_t len);
|
uint8_t *dest, size_t len);
|
||||||
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
|
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_iface_group(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_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_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
|
||||||
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
|
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
|
||||||
|
1
hw/usb.h
1
hw/usb.h
@ -141,6 +141,7 @@ typedef struct USBDesc USBDesc;
|
|||||||
typedef struct USBDescID USBDescID;
|
typedef struct USBDescID USBDescID;
|
||||||
typedef struct USBDescDevice USBDescDevice;
|
typedef struct USBDescDevice USBDescDevice;
|
||||||
typedef struct USBDescConfig USBDescConfig;
|
typedef struct USBDescConfig USBDescConfig;
|
||||||
|
typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
|
||||||
typedef struct USBDescIface USBDescIface;
|
typedef struct USBDescIface USBDescIface;
|
||||||
typedef struct USBDescEndpoint USBDescEndpoint;
|
typedef struct USBDescEndpoint USBDescEndpoint;
|
||||||
typedef struct USBDescOther USBDescOther;
|
typedef struct USBDescOther USBDescOther;
|
||||||
|
Loading…
Reference in New Issue
Block a user