diff options
-rw-r--r-- | Makefile.objs | 2 | ||||
-rw-r--r-- | hw/usb-desc.c | 238 | ||||
-rw-r--r-- | hw/usb-desc.h | 86 | ||||
-rw-r--r-- | hw/usb.h | 9 | ||||
-rw-r--r-- | trace-events | 5 |
5 files changed, 339 insertions, 1 deletions
diff --git a/Makefile.objs b/Makefile.objs index c3e52c5674..fda366d11c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -88,7 +88,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 0000000000..559ced78b9 --- /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 0000000000..d80efdb24f --- /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 */ @@ -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 e8fed0f424..8264ff0e77 100644 --- a/trace-events +++ b/trace-events @@ -190,6 +190,11 @@ 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" |