Patchwork [02/30] usb: data structs and helpers for usb descriptors.

login
register
mail settings
Submitter Gerd Hoffmann
Date Dec. 17, 2010, 11:26 a.m.
Message ID <1292585206-24862-3-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/75864/
State New
Headers show

Comments

Gerd Hoffmann - Dec. 17, 2010, 11:26 a.m.
This patch adds hw/usb-desc.[ch] files.  They carry data structures
for various usb descriptors and helper functions to generate usb
packets from the structures.

The intention is to have a internal representation of the device
desription which is more usable than the current char array blobs,
so we can have common code handle common usb device emulation using
the device description.

The usage of this infrastructure is optional for usb drivers as there
are cases such as pass-through where it probably isn't very useful.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.objs |    2 +-
 hw/usb-desc.c |  238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-desc.h |   86 +++++++++++++++++++++
 hw/usb.h      |    9 ++
 trace-events  |    5 +
 5 files changed, 339 insertions(+), 1 deletions(-)
 create mode 100644 hw/usb-desc.c
 create mode 100644 hw/usb-desc.h

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 04625eb..39b1aea 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -85,7 +85,7 @@  common-obj-y += eeprom93xx.o
 common-obj-y += scsi-disk.o cdrom.o
 common-obj-y += scsi-generic.o scsi-bus.o
 common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
-common-obj-y += usb-serial.o usb-net.o usb-bus.o
+common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 common-obj-$(CONFIG_SD) += sd.o
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
new file mode 100644
index 0000000..559ced7
--- /dev/null
+++ b/hw/usb-desc.c
@@ -0,0 +1,238 @@ 
+#include "usb.h"
+#include "usb-desc.h"
+#include "trace.h"
+
+/* ------------------------------------------------------------------ */
+
+static uint8_t usb_lo(uint16_t val)
+{
+    return val & 0xff;
+}
+
+static uint8_t usb_hi(uint16_t val)
+{
+    return (val >> 8) & 0xff;
+}
+
+int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+                    uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x12;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_DEVICE;
+
+    dest[0x02] = usb_lo(dev->bcdUSB);
+    dest[0x03] = usb_hi(dev->bcdUSB);
+    dest[0x04] = dev->bDeviceClass;
+    dest[0x05] = dev->bDeviceSubClass;
+    dest[0x06] = dev->bDeviceProtocol;
+    dest[0x07] = dev->bMaxPacketSize0;
+
+    dest[0x08] = usb_lo(id->idVendor);
+    dest[0x09] = usb_hi(id->idVendor);
+    dest[0x0a] = usb_lo(id->idProduct);
+    dest[0x0b] = usb_hi(id->idProduct);
+    dest[0x0c] = usb_lo(id->bcdDevice);
+    dest[0x0d] = usb_hi(id->bcdDevice);
+    dest[0x0e] = id->iManufacturer;
+    dest[0x0f] = id->iProduct;
+    dest[0x10] = id->iSerialNumber;
+
+    dest[0x11] = dev->bNumConfigurations;
+
+    return bLength;
+}
+
+int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+{
+    uint8_t  bLength = 0x09;
+    uint16_t wTotalLength = 0;
+    int i, rc, count;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_CONFIG;
+    dest[0x04] = conf->bNumInterfaces;
+    dest[0x05] = conf->bConfigurationValue;
+    dest[0x06] = conf->iConfiguration;
+    dest[0x07] = conf->bmAttributes;
+    dest[0x08] = conf->bMaxPower;
+    wTotalLength += bLength;
+
+    count = conf->nif ? conf->nif : conf->bNumInterfaces;
+    for (i = 0; i < count; i++) {
+        rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
+        if (rc < 0) {
+            return rc;
+        }
+        wTotalLength += rc;
+    }
+
+    dest[0x02] = usb_lo(wTotalLength);
+    dest[0x03] = usb_hi(wTotalLength);
+    return wTotalLength;
+}
+
+int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x09;
+    int i, rc, pos = 0;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_INTERFACE;
+    dest[0x02] = iface->bInterfaceNumber;
+    dest[0x03] = iface->bAlternateSetting;
+    dest[0x04] = iface->bNumEndpoints;
+    dest[0x05] = iface->bInterfaceClass;
+    dest[0x06] = iface->bInterfaceSubClass;
+    dest[0x07] = iface->bInterfaceProtocol;
+    dest[0x08] = iface->iInterface;
+    pos += bLength;
+
+    for (i = 0; i < iface->ndesc; i++) {
+        rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    for (i = 0; i < iface->bNumEndpoints; i++) {
+        rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
+        if (rc < 0) {
+            return rc;
+        }
+        pos += rc;
+    }
+
+    return pos;
+}
+
+int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
+{
+    uint8_t bLength = 0x07;
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    dest[0x00] = bLength;
+    dest[0x01] = USB_DT_ENDPOINT;
+    dest[0x02] = ep->bEndpointAddress;
+    dest[0x03] = ep->bmAttributes;
+    dest[0x04] = usb_lo(ep->wMaxPacketSize);
+    dest[0x05] = usb_hi(ep->wMaxPacketSize);
+    dest[0x06] = ep->bInterval;
+
+    return bLength;
+}
+
+int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
+{
+    int bLength = desc->length ? desc->length : desc->data[0];
+
+    if (len < bLength) {
+        return -1;
+    }
+
+    memcpy(dest, desc->data, bLength);
+    return bLength;
+}
+
+int usb_desc_string(const char* const *str, int index, uint8_t *dest, size_t len)
+{
+    uint8_t bLength, pos, i;
+
+    if (len < 4) {
+        return -1;
+    }
+
+    if (index == 0) {
+        /* language ids */
+        dest[0] = 4;
+        dest[1] = USB_DT_STRING;
+        dest[2] = 0x09;
+        dest[3] = 0x04;
+        return 4;
+    }
+
+    if (str[index] == NULL) {
+        return 0;
+    }
+    bLength = strlen(str[index]) * 2 + 2;
+    dest[0] = bLength;
+    dest[1] = USB_DT_STRING;
+    i = 0; pos = 2;
+    while (pos+1 < bLength && pos+1 < len) {
+        dest[pos++] = str[index][i++];
+        dest[pos++] = 0;
+    }
+    return pos;
+}
+
+/* ------------------------------------------------------------------ */
+
+int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
+{
+    const USBDesc *desc = dev->info->usb_desc;
+    uint8_t buf[256];
+    uint8_t type = value >> 8;
+    uint8_t index = value & 0xff;
+    int ret = -1;
+
+    switch(type) {
+    case USB_DT_DEVICE:
+        ret = usb_desc_device(&desc->id, desc->full, buf, sizeof(buf));
+        trace_usb_desc_device(dev->addr, len, ret);
+        break;
+    case USB_DT_CONFIG:
+        if (index < desc->full->bNumConfigurations) {
+            ret = usb_desc_config(desc->full->confs + index, buf, sizeof(buf));
+        }
+        trace_usb_desc_config(dev->addr, index, len, ret);
+        break;
+    case USB_DT_STRING:
+        ret = usb_desc_string(desc->str, index, buf, sizeof(buf));
+        trace_usb_desc_string(dev->addr, index, len, ret);
+        break;
+    default:
+        fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
+                dev->addr, type, len);
+        break;
+    }
+
+    if (ret > 0) {
+        if (ret > len) {
+            ret = len;
+        }
+        memcpy(dest, buf, ret);
+    }
+    return ret;
+}
+
+int usb_desc_handle_control(USBDevice *dev, int request, int value,
+                            int index, int length, uint8_t *data)
+{
+    const USBDesc *desc = dev->info->usb_desc;
+    int ret = -1;
+
+    assert(desc != NULL);
+    switch(request) {
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        ret = usb_desc_get_descriptor(dev, value, data, length);
+        break;
+    }
+    return ret;
+}
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
new file mode 100644
index 0000000..d80efdb
--- /dev/null
+++ b/hw/usb-desc.h
@@ -0,0 +1,86 @@ 
+#ifndef QEMU_HW_USB_DESC_H
+#define QEMU_HW_USB_DESC_H
+
+#include <inttypes.h>
+
+struct USBDescID {
+    uint16_t                  idVendor;
+    uint16_t                  idProduct;
+    uint16_t                  bcdDevice;
+    uint8_t                   iManufacturer;
+    uint8_t                   iProduct;
+    uint8_t                   iSerialNumber;
+};
+
+struct USBDescDevice {
+    uint16_t                  bcdUSB;
+    uint8_t                   bDeviceClass;
+    uint8_t                   bDeviceSubClass;
+    uint8_t                   bDeviceProtocol;
+    uint8_t                   bMaxPacketSize0;
+    uint8_t                   bNumConfigurations;
+
+    const USBDescConfig       *confs;
+};
+
+struct USBDescConfig {
+    uint8_t                   bNumInterfaces;
+    uint8_t                   bConfigurationValue;
+    uint8_t                   iConfiguration;
+    uint8_t                   bmAttributes;
+    uint8_t                   bMaxPower;
+
+    uint8_t                   nif;
+    const USBDescIface        *ifs;
+};
+
+struct USBDescIface {
+    uint8_t                   bInterfaceNumber;
+    uint8_t                   bAlternateSetting;
+    uint8_t                   bNumEndpoints;
+    uint8_t                   bInterfaceClass;
+    uint8_t                   bInterfaceSubClass;
+    uint8_t                   bInterfaceProtocol;
+    uint8_t                   iInterface;
+
+    uint8_t                   ndesc;
+    USBDescOther              *descs;
+    USBDescEndpoint           *eps;
+};
+
+struct USBDescEndpoint {
+    uint8_t                   bEndpointAddress;
+    uint8_t                   bmAttributes;
+    uint16_t                  wMaxPacketSize;
+    uint8_t                   bInterval;
+};
+
+struct USBDescOther {
+    uint8_t                   length;
+    uint8_t                   *data;
+};
+
+typedef const char *USBDescStrings[256];
+
+struct USBDesc {
+    USBDescID                 id;
+    const USBDescDevice       *full;
+    const USBDescDevice       *high;
+    const char* const         *str;
+};
+
+/* generate usb packages from structs */
+int usb_desc_device(const USBDescID *id, 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_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);
+int usb_desc_string(const char* const *str, int index, uint8_t *dest, size_t len);
+
+/* control message emulation helpers */
+int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
+int usb_desc_handle_control(USBDevice *dev, int request, int value,
+                            int index, int length, uint8_t *data);
+
+#endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb.h b/hw/usb.h
index 00d2802..3aeb975 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -128,6 +128,14 @@  typedef struct USBDevice USBDevice;
 typedef struct USBDeviceInfo USBDeviceInfo;
 typedef struct USBPacket USBPacket;
 
+typedef struct USBDesc USBDesc;
+typedef struct USBDescID USBDescID;
+typedef struct USBDescDevice USBDescDevice;
+typedef struct USBDescConfig USBDescConfig;
+typedef struct USBDescIface USBDescIface;
+typedef struct USBDescEndpoint USBDescEndpoint;
+typedef struct USBDescOther USBDescOther;
+
 /* definition of a USB device */
 struct USBDevice {
     DeviceState qdev;
@@ -190,6 +198,7 @@  struct USBDeviceInfo {
     int (*handle_data)(USBDevice *dev, USBPacket *p);
 
     const char *product_desc;
+    const USBDesc *usb_desc;
 
     /* handle legacy -usbdevice command line options */
     const char *usbdevice_name;
diff --git a/trace-events b/trace-events
index da03d4b..23303c4 100644
--- a/trace-events
+++ b/trace-events
@@ -190,5 +190,10 @@  disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "g
 disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
 disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
 
+# hw/usb-desc.c
+disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
+disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
+
 # vl.c
 disable vm_state_notify(int running, int reason) "running %d reason %d"