Add support for creating BOS descriptor and device cappability descriptors.
Signed-off-by: Gerd Hoffmann <kra...@redhat.com> --- hw/usb.h | 6 +++ hw/usb/desc.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb/desc.h | 25 +++++++++++++ trace-events | 1 + 4 files changed, 141 insertions(+), 0 deletions(-) diff --git a/hw/usb.h b/hw/usb.h index 78ffdf4..48c8926 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -135,10 +135,16 @@ #define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_DT_DEBUG 0x0A #define USB_DT_INTERFACE_ASSOC 0x0B +#define USB_DT_BOS 0x0F +#define USB_DT_DEVICE_CAPABILITY 0x10 #define USB_DT_CS_INTERFACE 0x24 #define USB_DT_CS_ENDPOINT 0x25 #define USB_DT_ENDPOINT_COMPANION 0x30 +#define USB_DEV_CAP_WIRELESS 0x01 +#define USB_DEV_CAP_USB2_EXT 0x02 +#define USB_DEV_CAP_SUPERSPEED 0x03 + #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 #define USB_ENDPOINT_XFER_BULK 2 diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 8f5a8e5..1f12eae 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -258,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) return bLength; } +static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x07; + USBDescriptor *d = (void *)dest; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT; + + d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */ + d->u.cap.u.usb2_ext.bmAttributes_2 = 0; + d->u.cap.u.usb2_ext.bmAttributes_3 = 0; + d->u.cap.u.usb2_ext.bmAttributes_4 = 0; + + return bLength; +} + +static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x0a; + USBDescriptor *d = (void *)dest; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED; + + d->u.cap.u.super.bmAttributes = 0; + d->u.cap.u.super.wSpeedsSupported_lo = 0; + d->u.cap.u.super.wSpeedsSupported_hi = 0; + d->u.cap.u.super.bFunctionalitySupport = 0; + d->u.cap.u.super.bU1DevExitLat = 0x0a; + d->u.cap.u.super.wU2DevExitLat_lo = 0x20; + d->u.cap.u.super.wU2DevExitLat_hi = 0; + + if (desc->full) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1); + d->u.cap.u.super.bFunctionalitySupport = 1; + } + if (desc->high) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2); + if (!d->u.cap.u.super.bFunctionalitySupport) { + d->u.cap.u.super.bFunctionalitySupport = 2; + } + } + if (desc->super) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3); + if (!d->u.cap.u.super.bFunctionalitySupport) { + d->u.cap.u.super.bFunctionalitySupport = 3; + } + } + + return bLength; +} + +static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x05; + uint16_t wTotalLength = 0; + uint8_t bNumDeviceCaps = 0; + USBDescriptor *d = (void *)dest; + int rc; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_BOS; + + wTotalLength += bLength; + + if (desc->high != NULL) { + rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + bNumDeviceCaps++; + } + + if (desc->super != NULL) { + rc = usb_desc_cap_super(desc, dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + bNumDeviceCaps++; + } + + d->u.bos.wTotalLength_lo = usb_lo(wTotalLength); + d->u.bos.wTotalLength_hi = usb_hi(wTotalLength); + d->u.bos.bNumDeviceCaps = bNumDeviceCaps; + return wTotalLength; +} + /* ------------------------------------------------------------------ */ static void usb_desc_ep_init(USBDevice *dev) @@ -571,6 +676,10 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len } trace_usb_desc_other_speed_config(dev->addr, index, len, ret); break; + case USB_DT_BOS: + ret = usb_desc_bos(desc, buf, sizeof(buf)); + trace_usb_desc_bos(dev->addr, len, ret); + break; case USB_DT_DEBUG: /* ignore silently */ diff --git a/hw/usb/desc.h b/hw/usb/desc.h index 4b5e88d..68bb570 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -69,6 +69,31 @@ typedef struct USBDescriptor { uint8_t wBytesPerInterval_lo; uint8_t wBytesPerInterval_hi; } super_endpoint; + struct { + uint8_t wTotalLength_lo; + uint8_t wTotalLength_hi; + uint8_t bNumDeviceCaps; + } bos; + struct { + uint8_t bDevCapabilityType; + union { + struct { + uint8_t bmAttributes_1; + uint8_t bmAttributes_2; + uint8_t bmAttributes_3; + uint8_t bmAttributes_4; + } usb2_ext; + struct { + uint8_t bmAttributes; + uint8_t wSpeedsSupported_lo; + uint8_t wSpeedsSupported_hi; + uint8_t bFunctionalitySupport; + uint8_t bU1DevExitLat; + uint8_t wU2DevExitLat_lo; + uint8_t wU2DevExitLat_hi; + } super; + } u; + } cap; } u; } QEMU_PACKED USBDescriptor; diff --git a/trace-events b/trace-events index 8fa683d..09972ad 100644 --- a/trace-events +++ b/trace-events @@ -340,6 +340,7 @@ usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device quali usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" +usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d" usb_set_addr(int addr) "dev %d" usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d" -- 1.7.1