diff options
author | Aurelien Jarno <aurelien@aurel32.net> | 2011-01-21 17:56:50 +0100 |
---|---|---|
committer | Aurelien Jarno <aurelien@aurel32.net> | 2011-01-21 17:56:50 +0100 |
commit | b947c12c0bb217fe09968e652873e0d22b269d68 (patch) | |
tree | 1fbb289269c4d28d310391a5bdd4fe667feda1ca /hw | |
parent | 543c4c94cf235d11315157bab25a24f7d9a48711 (diff) | |
parent | ace1318b8ea95efa332fb3e8e159924d4f928753 (diff) |
Merge branch 'usb.4' of git://anongit.freedesktop.org/spice/qemu
* 'usb.4' of git://anongit.freedesktop.org/spice/qemu: (32 commits)
usb: zap pdev from usbport
usb: rewrite fw path, fix numbering
usb: add port property.
usb: keep track of physical port address.
usb storage: handle long responses
usb storage: fix status reporting
usb storage: high speed support
usb: add device qualifier support
usb: add usb_desc_attach
usb: add attach callback
usb: add speed mask to ports
usb: hid: change serial number to "42".
usb: hid: remote wakeup support.
usb: hub: remote wakeup support.
usb: uhci: remote wakeup support.
usb: add usb_wakeup() + wakeup callback to port ops
usb: rework attach/detach workflow
usb: create USBPortOps, move attach there.
usb: move remote wakeup handling to common code
usb: move USB_REQ_{GET,SET}_CONFIGURATION handling to common code
...
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb-bt.c | 525 | ||||
-rw-r--r-- | hw/usb-bus.c | 129 | ||||
-rw-r--r-- | hw/usb-desc.c | 406 | ||||
-rw-r--r-- | hw/usb-desc.h | 92 | ||||
-rw-r--r-- | hw/usb-hid.c | 486 | ||||
-rw-r--r-- | hw/usb-hub.c | 250 | ||||
-rw-r--r-- | hw/usb-msd.c | 267 | ||||
-rw-r--r-- | hw/usb-musb.c | 44 | ||||
-rw-r--r-- | hw/usb-net.c | 528 | ||||
-rw-r--r-- | hw/usb-ohci.c | 88 | ||||
-rw-r--r-- | hw/usb-serial.c | 236 | ||||
-rw-r--r-- | hw/usb-uhci.c | 98 | ||||
-rw-r--r-- | hw/usb-wacom.c | 214 | ||||
-rw-r--r-- | hw/usb.c | 34 | ||||
-rw-r--r-- | hw/usb.h | 50 |
15 files changed, 1828 insertions, 1619 deletions
diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 56d1a6ce4a..22e6845049 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -20,6 +20,7 @@ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" #include "net.h" #include "bt.h" @@ -51,251 +52,202 @@ struct USBBtState { #define USB_ACL_EP 2 #define USB_SCO_EP 3 -static const uint8_t qemu_bt_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - USB_DT_DEVICE, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.10 */ +enum { + STR_MANUFACTURER = 1, + STR_SERIALNUMBER, +}; - 0xe0, /* u8 bDeviceClass; Wireless */ - 0x01, /* u8 bDeviceSubClass; Radio Frequency */ - 0x01, /* u8 bDeviceProtocol; Bluetooth */ - 0x40, /* u8 bMaxPacketSize0; 64 Bytes */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_SERIALNUMBER] = "1", +}; - 0x12, 0x0a, /* u16 idVendor; */ - 0x01, 0x00, /* u16 idProduct; Bluetooth Dongle (HCI mode) */ - 0x58, 0x19, /* u16 bcdDevice; (some devices have 0x48, 0x02) */ +static const USBDescIface desc_iface_bluetooth[] = { + { + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | USB_EVT_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x10, + .bInterval = 0x02, + }, + { + .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + .bInterval = 0x0a, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_ACL_EP, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + .bInterval = 0x0a, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 2, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 3, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 4, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 5, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, + }, + } +}; - 0x00, /* u8 iManufacturer; */ - 0x00, /* u8 iProduct; */ - 0x00, /* u8 iSerialNumber; */ - 0x01, /* u8 bNumConfigurations; */ +static const USBDescDevice desc_device_bluetooth = { + .bcdUSB = 0x0110, + .bDeviceClass = 0xe0, /* Wireless */ + .bDeviceSubClass = 0x01, /* Radio Frequency */ + .bDeviceProtocol = 0x01, /* Bluetooth */ + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .bmAttributes = 0xc0, + .bMaxPower = 0, + .nif = ARRAY_SIZE(desc_iface_bluetooth), + .ifs = desc_iface_bluetooth, + }, + }, }; -static const uint8_t qemu_bt_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - USB_DT_CONFIG, /* u8 bDescriptorType; */ - 0xb1, 0x00, /* u16 wTotalLength; */ - 0x02, /* u8 bNumInterfaces; (2) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* interface one */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x03, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_EVT_EP, /* u8 ep_bEndpointAddress; */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x10, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x02, /* u8 ep_bInterval; */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint three */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting one */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting two */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x01, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting three */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x02, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting four */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x03, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting five */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x04, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting six */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x05, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* If implemented, the DFU interface descriptor goes here with no - * endpoints or alternative settings. */ +static const USBDesc desc_bluetooth = { + .id = { + .idVendor = 0x0a12, + .idProduct = 0x0001, + .bcdDevice = 0x1958, + .iManufacturer = STR_MANUFACTURER, + .iProduct = 0, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_bluetooth, + .str = desc_strings, }; static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) @@ -424,85 +376,38 @@ static int usb_bt_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { struct USBBtState *s = (struct USBBtState *) dev->opaque; - int ret = 0; + int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + switch (request) { + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + s->config = 0; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + s->config = 1; + usb_bt_fifo_reset(&s->evt); + usb_bt_fifo_reset(&s->acl); + usb_bt_fifo_reset(&s->sco); + break; + } + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: case InterfaceRequest | USB_REQ_GET_STATUS: case EndpointRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[0] = 0x00; data[1] = 0x00; ret = 2; break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: + goto fail; case InterfaceOutRequest | USB_REQ_SET_FEATURE: case EndpointOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case USB_DT_DEVICE: - ret = sizeof(qemu_bt_dev_descriptor); - memcpy(data, qemu_bt_dev_descriptor, ret); - break; - case USB_DT_CONFIG: - ret = sizeof(qemu_bt_config_descriptor); - memcpy(data, qemu_bt_config_descriptor, ret); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = qemu_bt_config_descriptor[0x5]; - ret = 1; - s->config = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - if (value != qemu_bt_config_descriptor[0x5] && value != 0) { - printf("%s: Wrong SET_CONFIGURATION request (%i)\n", - __FUNCTION__, value); - goto fail; - } - s->config = 1; - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); + goto fail; break; case InterfaceRequest | USB_REQ_GET_INTERFACE: if (value != 0 || (index & ~1) || length != 1) @@ -618,8 +523,7 @@ static void usb_bt_handle_destroy(USBDevice *dev) static int usb_bt_initfn(USBDevice *dev) { - struct USBBtState *s = DO_UPCAST(struct USBBtState, dev, dev); - s->dev.speed = USB_SPEED_HIGH; + usb_desc_init(dev); return 0; } @@ -648,6 +552,7 @@ static struct USBDeviceInfo bt_info = { .product_desc = "QEMU BT dongle", .qdev.name = "usb-bt-dongle", .qdev.size = sizeof(struct USBBtState), + .usb_desc = &desc_bluetooth, .init = usb_bt_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_bt_handle_reset, diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 8b4583c1e6..6e2e5fd17a 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -5,13 +5,20 @@ #include "monitor.h" static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); -static char *usbbus_get_fw_dev_path(DeviceState *dev); + +static char *usb_get_dev_path(DeviceState *dev); +static char *usb_get_fw_dev_path(DeviceState *qdev); static struct BusInfo usb_bus_info = { .name = "USB", .size = sizeof(USBBus), .print_dev = usb_bus_dev_print, - .get_fw_dev_path = usbbus_get_fw_dev_path, + .get_dev_path = usb_get_dev_path, + .get_fw_dev_path = usb_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_STRING("port", USBDevice, port_path), + DEFINE_PROP_END_OF_LIST() + }, }; static int next_usb_bus = 0; static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses); @@ -48,6 +55,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc); dev->info = info; dev->auto_attach = 1; + QLIST_INIT(&dev->strings); rc = dev->info->init(dev); if (rc == 0 && dev->auto_attach) usb_device_attach(dev); @@ -112,16 +120,28 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) } void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBDevice *pdev, usb_attachfn attach) + USBPortOps *ops, int speedmask) { port->opaque = opaque; port->index = index; - port->attach = attach; - port->pdev = pdev; + port->opaque = opaque; + port->index = index; + port->ops = ops; + port->speedmask = speedmask; QTAILQ_INSERT_TAIL(&bus->free, port, next); bus->nfree++; } +void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) +{ + if (upstream) { + snprintf(downstream->path, sizeof(downstream->path), "%s.%d", + upstream->path, portnr); + } else { + snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); + } +} + void usb_unregister_port(USBBus *bus, USBPort *port) { if (port->dev) @@ -140,9 +160,22 @@ static void do_attach(USBDevice *dev) dev->product_desc); return; } - dev->attached++; + if (dev->port_path) { + QTAILQ_FOREACH(port, &bus->free, next) { + if (strcmp(port->path, dev->port_path) == 0) { + break; + } + } + if (port == NULL) { + fprintf(stderr, "Warning: usb port %s (bus %s) not found\n", + dev->port_path, bus->qbus.name); + return; + } + } else { + port = QTAILQ_FIRST(&bus->free); + } - port = QTAILQ_FIRST(&bus->free); + dev->attached++; QTAILQ_REMOVE(&bus->free, port, next); bus->nfree--; @@ -156,8 +189,9 @@ int usb_device_attach(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); - if (bus->nfree == 1) { - /* Create a new hub and chain it on. */ + if (bus->nfree == 1 && dev->port_path == NULL) { + /* Create a new hub and chain it on + (unless a physical port location is specified). */ usb_create_simple(bus, "usb-hub"); } do_attach(dev); @@ -231,12 +265,43 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); USBBus *bus = usb_bus_from_device(dev); - monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s%s\n", + monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n", indent, "", bus->busnr, dev->addr, + dev->port ? dev->port->path : "-", usb_speed(dev->speed), dev->product_desc, dev->attached ? ", attached" : ""); } +static char *usb_get_dev_path(DeviceState *qdev) +{ + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); + return qemu_strdup(dev->port->path); +} + +static char *usb_get_fw_dev_path(DeviceState *qdev) +{ + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); + char *fw_path, *in; + int pos = 0; + long nr; + + fw_path = qemu_malloc(32 + strlen(dev->port->path) * 6); + in = dev->port->path; + while (true) { + nr = strtol(in, &in, 10); + if (in[0] == '.') { + /* some hub between root port and device */ + pos += sprintf(fw_path + pos, "hub@%ld/", nr); + in++; + } else { + /* the device itself */ + pos += sprintf(fw_path + pos, "%s@%ld", qdev_fw_name(qdev), nr); + break; + } + } + return fw_path; +} + void usb_info(Monitor *mon) { USBBus *bus; @@ -253,8 +318,8 @@ void usb_info(Monitor *mon) dev = port->dev; if (!dev) continue; - monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n", - bus->busnr, dev->addr, usb_speed(dev->speed), + monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n", + bus->busnr, dev->addr, port->path, usb_speed(dev->speed), dev->product_desc); } } @@ -309,43 +374,3 @@ USBDevice *usbdevice_create(const char *cmdline) } return usb->usbdevice_init(params); } - -static int usbbus_get_fw_dev_path_helper(USBDevice *d, USBBus *bus, char *p, - int len) -{ - int l = 0; - USBPort *port; - - QTAILQ_FOREACH(port, &bus->used, next) { - if (port->dev == d) { - if (port->pdev) { - l = usbbus_get_fw_dev_path_helper(port->pdev, bus, p, len); - } - l += snprintf(p + l, len - l, "%s@%x/", qdev_fw_name(&d->qdev), - port->index); - break; - } - } - - return l; -} - -static char *usbbus_get_fw_dev_path(DeviceState *dev) -{ - USBDevice *d = (USBDevice*)dev; - USBBus *bus = usb_bus_from_device(d); - char path[100]; - int l; - - assert(d->attached != 0); - - l = usbbus_get_fw_dev_path_helper(d, bus, path, sizeof(path)); - - if (l == 0) { - abort(); - } - - path[l-1] = '\0'; - - return strdup(path); -} diff --git a/hw/usb-desc.c b/hw/usb-desc.c new file mode 100644 index 0000000000..62591f20aa --- /dev/null +++ b/hw/usb-desc.c @@ -0,0 +1,406 @@ +#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_device_qualifier(const USBDescDevice *dev, + uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x0a; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_DEVICE_QUALIFIER; + + 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] = dev->bNumConfigurations; + dest[0x09] = 0; /* reserved */ + + 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; +} + +/* ------------------------------------------------------------------ */ + +static void usb_desc_setdefaults(USBDevice *dev) +{ + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); + switch (dev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + dev->device = desc->full; + break; + case USB_SPEED_HIGH: + dev->device = desc->high; + break; + } + dev->config = dev->device->confs; +} + +void usb_desc_init(USBDevice *dev) +{ + dev->speed = USB_SPEED_FULL; + usb_desc_setdefaults(dev); +} + +void usb_desc_attach(USBDevice *dev) +{ + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); + if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { + dev->speed = USB_SPEED_HIGH; + } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { + dev->speed = USB_SPEED_FULL; + } else { + fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n", + dev->info->product_desc); + return; + } + usb_desc_setdefaults(dev); +} + +void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str) +{ + USBDescString *s; + + QLIST_FOREACH(s, &dev->strings, next) { + if (s->index == index) { + break; + } + } + if (s == NULL) { + s = qemu_mallocz(sizeof(*s)); + s->index = index; + QLIST_INSERT_HEAD(&dev->strings, s, next); + } + qemu_free(s->str); + s->str = qemu_strdup(str); +} + +const char *usb_desc_get_string(USBDevice *dev, uint8_t index) +{ + USBDescString *s; + + QLIST_FOREACH(s, &dev->strings, next) { + if (s->index == index) { + return s->str; + } + } + return NULL; +} + +int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len) +{ + uint8_t bLength, pos, i; + const char *str; + + 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; + } + + str = usb_desc_get_string(dev, index); + if (str == NULL) { + str = dev->info->usb_desc->str[index]; + if (str == NULL) { + return 0; + } + } + + bLength = strlen(str) * 2 + 2; + dest[0] = bLength; + dest[1] = USB_DT_STRING; + i = 0; pos = 2; + while (pos+1 < bLength && pos+1 < len) { + dest[pos++] = str[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; + const USBDescDevice *other_dev; + uint8_t buf[256]; + uint8_t type = value >> 8; + uint8_t index = value & 0xff; + int ret = -1; + + if (dev->speed == USB_SPEED_HIGH) { + other_dev = dev->info->usb_desc->full; + } else { + other_dev = dev->info->usb_desc->high; + } + + switch(type) { + case USB_DT_DEVICE: + ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf)); + trace_usb_desc_device(dev->addr, len, ret); + break; + case USB_DT_CONFIG: + if (index < dev->device->bNumConfigurations) { + ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf)); + } + trace_usb_desc_config(dev->addr, index, len, ret); + break; + case USB_DT_STRING: + ret = usb_desc_string(dev, index, buf, sizeof(buf)); + trace_usb_desc_string(dev->addr, index, len, ret); + break; + + case USB_DT_DEVICE_QUALIFIER: + if (other_dev != NULL) { + ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); + } + trace_usb_desc_device_qualifier(dev->addr, len, ret); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (other_dev != NULL && index < other_dev->bNumConfigurations) { + ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf)); + buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; + } + trace_usb_desc_other_speed_config(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 i, ret = -1; + + assert(desc != NULL); + switch(request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + trace_usb_set_addr(dev->addr); + ret = 0; + break; + + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + ret = usb_desc_get_descriptor(dev, value, data, length); + break; + + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = dev->config->bConfigurationValue; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + for (i = 0; i < dev->device->bNumConfigurations; i++) { + if (dev->device->confs[i].bConfigurationValue == value) { + dev->config = dev->device->confs + i; + ret = 0; + } + } + trace_usb_set_config(dev->addr, value, ret); + break; + + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = 0; + if (dev->config->bmAttributes & 0x40) { + data[0] |= 1 << USB_DEVICE_SELF_POWERED; + } + if (dev->remote_wakeup) { + data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + ret = 0; + } + trace_usb_clear_device_feature(dev->addr, value, ret); + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + ret = 0; + } + trace_usb_set_device_feature(dev->addr, value, ret); + break; + } + return ret; +} diff --git a/hw/usb-desc.h b/hw/usb-desc.h new file mode 100644 index 0000000000..ac734ab088 --- /dev/null +++ b/hw/usb-desc.h @@ -0,0 +1,92 @@ +#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_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_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); + +/* control message emulation helpers */ +void usb_desc_init(USBDevice *dev); +void usb_desc_attach(USBDevice *dev); +void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str); +const char *usb_desc_get_string(USBDevice *dev, uint8_t index); +int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len); +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-hid.c b/hw/usb-hid.c index 12bf46fa7e..90a2b49e1d 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -25,6 +25,7 @@ #include "hw.h" #include "console.h" #include "usb.h" +#include "usb-desc.h" #include "sysemu.h" /* HID interface requests */ @@ -73,190 +74,206 @@ typedef struct USBHIDState { void (*datain)(void *); } USBHIDState; -/* mostly the same values as the Bochs USB Mouse device */ -static const uint8_t qemu_mouse_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x01, /* u16 bcdUSB; v1.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x27, 0x06, /* u16 idVendor; */ - 0x01, 0x00, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT_MOUSE, + STR_PRODUCT_TABLET, + STR_PRODUCT_KEYBOARD, + STR_SERIALNUMBER, + STR_CONFIG_MOUSE, + STR_CONFIG_TABLET, + STR_CONFIG_KEYBOARD, }; -static const uint8_t qemu_mouse_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xe0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 52, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x04, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", + [STR_PRODUCT_TABLET] = "QEMU USB Tablet", + [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", + [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ + [STR_CONFIG_MOUSE] = "HID Mouse", + [STR_CONFIG_TABLET] = "HID Tablet", + [STR_CONFIG_KEYBOARD] = "HID Keyboard", }; -static const uint8_t qemu_tablet_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x05, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 74, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +static const USBDescIface desc_iface_mouse = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 52, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 4, + .bInterval = 0x0a, + }, + }, }; -static const uint8_t qemu_keyboard_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x06, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x32, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; HID */ - 0x01, /* u8 if_bInterfaceSubClass; Boot */ - 0x01, /* u8 if_bInterfaceProtocol; Keyboard */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - USB_DT_HID, /* u8 bDescriptorType; */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type; Report */ - 0x3f, 0x00, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */ - USB_DIR_IN | 0x01, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +static const USBDescIface desc_iface_tablet = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 74, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescIface desc_iface_keyboard = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x01, /* keyboard */ + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x11, 0x01, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 0x3f, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescDevice desc_device_mouse = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_MOUSE, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_mouse, + }, + }, +}; + +static const USBDescDevice desc_device_tablet = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_TABLET, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_tablet, + }, + }, +}; + +static const USBDescDevice desc_device_keyboard = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_KEYBOARD, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_keyboard, + }, + }, +}; + +static const USBDesc desc_mouse = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_MOUSE, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_mouse, + .str = desc_strings, +}; + +static const USBDesc desc_tablet = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_TABLET, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_tablet, + .str = desc_strings, +}; + +static const USBDesc desc_keyboard = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_KEYBOARD, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_keyboard, + .str = desc_strings, }; static const uint8_t qemu_mouse_hid_report_descriptor[] = { @@ -412,6 +429,8 @@ static void usb_hid_changed(USBHIDState *hs) if (hs->datain) hs->datain(hs->datain_opaque); + + usb_wakeup(&hs->dev); } static void usb_mouse_event(void *opaque, @@ -650,106 +669,15 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBHIDState *s = (USBHIDState *)dev; - int ret = 0; + int ret; + + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + ret = 0; switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_mouse_dev_descriptor, - sizeof(qemu_mouse_dev_descriptor)); - ret = sizeof(qemu_mouse_dev_descriptor); - break; - case USB_DT_CONFIG: - if (s->kind == USB_MOUSE) { - memcpy(data, qemu_mouse_config_descriptor, - sizeof(qemu_mouse_config_descriptor)); - ret = sizeof(qemu_mouse_config_descriptor); - } else if (s->kind == USB_TABLET) { - memcpy(data, qemu_tablet_config_descriptor, - sizeof(qemu_tablet_config_descriptor)); - ret = sizeof(qemu_tablet_config_descriptor); - } else if (s->kind == USB_KEYBOARD) { - memcpy(data, qemu_keyboard_config_descriptor, - sizeof(qemu_keyboard_config_descriptor)); - ret = sizeof(qemu_keyboard_config_descriptor); - } - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, s->dev.product_desc); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 4: - ret = set_usb_string(data, "HID Mouse"); - break; - case 5: - ret = set_usb_string(data, "HID Tablet"); - break; - case 6: - ret = set_usb_string(data, "HID Keyboard"); - break; - case 7: - ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -870,7 +798,8 @@ static void usb_hid_handle_destroy(USBDevice *dev) static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + + usb_desc_init(dev); s->kind = kind; if (s->kind == USB_MOUSE) { @@ -915,6 +844,7 @@ static struct USBDeviceInfo hid_info[] = { .qdev.name = "usb-tablet", .usbdevice_name = "tablet", .qdev.size = sizeof(USBHIDState), + .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_mouse_handle_reset, @@ -926,6 +856,7 @@ static struct USBDeviceInfo hid_info[] = { .qdev.name = "usb-mouse", .usbdevice_name = "mouse", .qdev.size = sizeof(USBHIDState), + .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_mouse_handle_reset, @@ -937,6 +868,7 @@ static struct USBDeviceInfo hid_info[] = { .qdev.name = "usb-kbd", .usbdevice_name = "keyboard", .qdev.size = sizeof(USBHIDState), + .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_keyboard_handle_reset, diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 8a3f829c96..78698caf67 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -23,10 +23,11 @@ */ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" //#define DEBUG -#define MAX_PORTS 8 +#define NUM_PORTS 8 typedef struct USBHubPort { USBPort port; @@ -36,8 +37,7 @@ typedef struct USBHubPort { typedef struct USBHubState { USBDevice dev; - int nb_ports; - USBHubPort ports[MAX_PORTS]; + USBHubPort ports[NUM_PORTS]; } USBHubState; #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) @@ -83,6 +83,60 @@ typedef struct USBHubState { /* same as Linux kernel root hubs */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "QEMU USB Hub", + [STR_SERIALNUMBER] = "314159", +}; + +static const USBDescIface desc_iface_hub = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8, + .bInterval = 0xff, + }, + } +}; + +static const USBDescDevice desc_device_hub = { + .bcdUSB = 0x0110, + .bDeviceClass = USB_CLASS_HUB, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0xe0, + .ifs = &desc_iface_hub, + }, + }, +}; + +static const USBDesc desc_hub = { + .id = { + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0x0101, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_hub, + .str = desc_strings, +}; + static const uint8_t qemu_hub_dev_descriptor[] = { 0x12, /* u8 bLength; */ 0x01, /* u8 bDescriptorType; Device */ @@ -164,37 +218,51 @@ static const uint8_t qemu_hub_hub_descriptor[] = /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; -static void usb_hub_attach(USBPort *port1, USBDevice *dev) +static void usb_hub_attach(USBPort *port1) { USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - if (dev) { - if (port->port.dev) - usb_attach(port1, NULL); - - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (dev->speed == USB_SPEED_LOW) - port->wPortStatus |= PORT_STAT_LOW_SPEED; - else - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; - port->port.dev = dev; - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); + port->wPortStatus |= PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->port.dev->speed == USB_SPEED_LOW) { + port->wPortStatus |= PORT_STAT_LOW_SPEED; } else { - dev = port->port.dev; - if (dev) { - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - port->port.dev = NULL; - } + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + } +} + +static void usb_hub_detach(USBPort *port1) +{ + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; + + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { + port->wPortStatus &= ~PORT_STAT_ENABLE; + port->wPortChange |= PORT_STAT_C_ENABLE; + } +} + +static void usb_hub_wakeup(USBDevice *dev) +{ + USBHubState *s = dev->port->opaque; + USBHubPort *port = &s->ports[dev->port->index]; + + if (port->wPortStatus & PORT_STAT_SUSPEND) { + port->wPortChange |= PORT_STAT_C_SUSPEND; + usb_wakeup(&s->dev); + } +} + +static void usb_hub_handle_attach(USBDevice *dev) +{ + USBHubState *s = DO_UPCAST(USBHubState, dev, dev); + int i; + + for (i = 0; i < NUM_PORTS; i++) { + usb_port_location(&s->ports[i].port, dev->port, i+1); } } @@ -209,93 +277,18 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, USBHubState *s = (USBHubState *)dev; int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: if (value == 0 && index != 0x81) { /* clear ep halt */ goto fail; } ret = 0; break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_hub_dev_descriptor, - sizeof(qemu_hub_dev_descriptor)); - ret = sizeof(qemu_hub_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_hub_config_descriptor, - sizeof(qemu_hub_config_descriptor)); - - /* status change endpoint size based on number - * of ports */ - data[22] = (s->nb_ports + 1 + 7) / 8; - - ret = sizeof(qemu_hub_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "314159"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB Hub"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -315,8 +308,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, { unsigned int n = index - 1; USBHubPort *port; - if (n >= s->nb_ports) + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; data[0] = port->wPortStatus; data[1] = port->wPortStatus >> 8; @@ -338,8 +332,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, unsigned int n = index - 1; USBHubPort *port; USBDevice *dev; - if (n >= s->nb_ports) + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; dev = port->port.dev; switch(value) { @@ -367,8 +362,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, unsigned int n = index - 1; USBHubPort *port; - if (n >= s->nb_ports) + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; switch(value) { case PORT_ENABLE: @@ -403,17 +399,17 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, unsigned int n, limit, var_hub_size = 0; memcpy(data, qemu_hub_hub_descriptor, sizeof(qemu_hub_hub_descriptor)); - data[2] = s->nb_ports; + data[2] = NUM_PORTS; /* fill DeviceRemovable bits */ - limit = ((s->nb_ports + 1 + 7) / 8) + 7; + limit = ((NUM_PORTS + 1 + 7) / 8) + 7; for (n = 7; n < limit; n++) { data[n] = 0x00; var_hub_size++; } /* fill PortPwrCtrlMask bits */ - limit = limit + ((s->nb_ports + 7) / 8); + limit = limit + ((NUM_PORTS + 7) / 8); for (;n < limit; n++) { data[n] = 0xff; var_hub_size++; @@ -442,14 +438,14 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) USBHubPort *port; unsigned int status; int i, n; - n = (s->nb_ports + 1 + 7) / 8; + n = (NUM_PORTS + 1 + 7) / 8; if (p->len == 1) { /* FreeBSD workaround */ n = 1; } else if (n > p->len) { return USB_RET_BABBLE; } status = 0; - for(i = 0; i < s->nb_ports; i++) { + for(i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; if (port->wPortChange) status |= (1 << (i + 1)); @@ -481,7 +477,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p) USBDevice *dev; int i, ret; - for(i = 0; i < s->nb_ports; i++) { + for(i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; dev = port->port.dev; if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { @@ -518,24 +514,30 @@ static void usb_hub_handle_destroy(USBDevice *dev) USBHubState *s = (USBHubState *)dev; int i; - for (i = 0; i < s->nb_ports; i++) { + for (i = 0; i < NUM_PORTS; i++) { usb_unregister_port(usb_bus_from_device(dev), &s->ports[i].port); } } +static USBPortOps usb_hub_port_ops = { + .attach = usb_hub_attach, + .detach = usb_hub_detach, + .wakeup = usb_hub_wakeup, +}; + static int usb_hub_initfn(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); USBHubPort *port; int i; - s->dev.speed = USB_SPEED_FULL, - s->nb_ports = MAX_PORTS; /* FIXME: make configurable */ - for (i = 0; i < s->nb_ports; i++) { + usb_desc_init(dev); + for (i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; usb_register_port(usb_bus_from_device(dev), - &port->port, s, i, &s->dev, usb_hub_attach); + &port->port, s, i, &usb_hub_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); port->wPortStatus = PORT_STAT_POWER; port->wPortChange = 0; } @@ -547,8 +549,10 @@ static struct USBDeviceInfo hub_info = { .qdev.name = "usb-hub", .qdev.fw_name = "hub", .qdev.size = sizeof(USBHubState), + .usb_desc = &desc_hub, .init = usb_hub_initfn, .handle_packet = usb_hub_handle_packet, + .handle_attach = usb_hub_handle_attach, .handle_reset = usb_hub_handle_reset, .handle_control = usb_hub_handle_control, .handle_data = usb_hub_handle_data, diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 0a95d8d506..729d96ccc3 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -11,6 +11,7 @@ #include "qemu-option.h" #include "qemu-config.h" #include "usb.h" +#include "usb-desc.h" #include "scsi.h" #include "console.h" #include "monitor.h" @@ -72,69 +73,102 @@ struct usb_msd_csw { uint8_t status; }; -static const uint8_t qemu_msd_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x01, /* u16 bcdUSB; v1.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - /* Vendor and product id are arbitrary. */ - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x03, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, }; -static const uint8_t qemu_msd_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x20, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */ - 0x06, /* u8 if_bInterfaceSubClass; SCSI */ - 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */ - 0x00, /* u8 if_iInterface; */ - - /* Bulk-In endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00, /* u8 ep_bInterval; */ - - /* Bulk-Out endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00 /* u8 ep_bInterval; */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "QEMU USB HARDDRIVE", + [STR_SERIALNUMBER] = "1", + [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", +}; + +static const USBDescIface desc_iface_full = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x50, /* Bulk */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + }, + } +}; + +static const USBDescDevice desc_device_full = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_FULL, + .bmAttributes = 0xc0, + .ifs = &desc_iface_full, + }, + }, +}; + +static const USBDescIface desc_iface_high = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x50, /* Bulk */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + }, + } +}; + +static const USBDescDevice desc_device_high = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_HIGH, + .bmAttributes = 0xc0, + .ifs = &desc_iface_high, + }, + }, +}; + +static const USBDesc desc = { + .id = { + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_full, + .high = &desc_device_high, + .str = desc_strings, }; static void usb_msd_copy_data(MSDState *s) @@ -153,7 +187,7 @@ static void usb_msd_copy_data(MSDState *s) s->usb_buf += len; s->scsi_buf += len; s->data_len -= len; - if (s->scsi_len == 0) { + if (s->scsi_len == 0 || s->data_len == 0) { if (s->mode == USB_MSDM_DATAIN) { s->scsi_dev->info->read_data(s->scsi_dev, s->tag); } else if (s->mode == USB_MSDM_DATAOUT) { @@ -162,15 +196,18 @@ static void usb_msd_copy_data(MSDState *s) } } -static void usb_msd_send_status(MSDState *s) +static void usb_msd_send_status(MSDState *s, USBPacket *p) { struct usb_msd_csw csw; + int len; csw.sig = cpu_to_le32(0x53425355); csw.tag = cpu_to_le32(s->tag); csw.residue = s->residue; csw.status = s->result; - memcpy(s->usb_buf, &csw, 13); + + len = MIN(sizeof(csw), p->len); + memcpy(p->data, &csw, len); } static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, @@ -190,7 +227,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { /* A deferred packet with no write data remaining must be the status read packet. */ - usb_msd_send_status(s); + usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; } else { if (s->data_len) { @@ -236,84 +273,15 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { MSDState *s = (MSDState *)dev; - int ret = 0; + int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_msd_dev_descriptor, - sizeof(qemu_msd_dev_descriptor)); - ret = sizeof(qemu_msd_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_msd_config_descriptor, - sizeof(qemu_msd_config_descriptor)); - ret = sizeof(qemu_msd_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB HARDDRIVE"); - break; - case 3: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -338,7 +306,6 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value, ret = 1; break; default: - fail: ret = USB_RET_STALL; break; } @@ -461,15 +428,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (len < 13) goto fail; - s->usb_len = len; - s->usb_buf = data; - usb_msd_send_status(s); + usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; ret = 13; break; case USB_MSDM_DATAIN: - DPRINTF("Data in %d/%d\n", len, s->data_len); + DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len); if (len > s->data_len) len = s->data_len; s->usb_buf = data; @@ -524,6 +489,7 @@ static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); BlockDriverState *bs = s->conf.bs; + DriveInfo *dinfo; if (!bs) { error_report("usb-msd: drive property not set"); @@ -542,7 +508,12 @@ static int usb_msd_initfn(USBDevice *dev) bdrv_detach(bs, &s->dev.qdev); s->conf.bs = NULL; - s->dev.speed = USB_SPEED_FULL; + dinfo = drive_get_by_blockdev(bs); + if (dinfo && dinfo->serial) { + usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->serial); + } + + usb_desc_init(dev); scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0); if (!s->scsi_dev) { @@ -625,8 +596,10 @@ static struct USBDeviceInfo msd_info = { .product_desc = "QEMU USB MSD", .qdev.name = "usb-storage", .qdev.size = sizeof(MSDState), + .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, + .handle_attach = usb_desc_attach, .handle_reset = usb_msd_handle_reset, .handle_control = usb_msd_handle_control, .handle_data = usb_msd_handle_data, diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 9efe7a6344..782cfa2282 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -259,7 +259,13 @@ #endif -static void musb_attach(USBPort *port, USBDevice *dev); +static void musb_attach(USBPort *port); +static void musb_detach(USBPort *port); + +static USBPortOps musb_port_ops = { + .attach = musb_attach, + .detach = musb_detach, +}; typedef struct { uint16_t faddr[2]; @@ -343,7 +349,9 @@ struct MUSBState { } usb_bus_new(&s->bus, NULL /* FIXME */); - usb_register_port(&s->bus, &s->port, s, 0, NULL, musb_attach); + usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&s->port, NULL, 1); return s; } @@ -460,34 +468,20 @@ static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess) } /* Attach or detach a device on our only port. */ -static void musb_attach(USBPort *port, USBDevice *dev) +static void musb_attach(USBPort *port) { MUSBState *s = (MUSBState *) port->opaque; - USBDevice *curr; - - port = &s->port; - curr = port->dev; - - if (dev) { - if (curr) { - usb_attach(port, NULL); - /* TODO: signal some interrupts */ - } - - musb_intr_set(s, musb_irq_vbus_request, 1); - /* Send the attach message to device */ - usb_send_msg(dev, USB_MSG_ATTACH); - } else if (curr) { - /* Send the detach message */ - usb_send_msg(curr, USB_MSG_DETACH); - - musb_intr_set(s, musb_irq_disconnect, 1); - } + musb_intr_set(s, musb_irq_vbus_request, 1); + musb_session_update(s, 0, s->session); +} - port->dev = dev; +static void musb_detach(USBPort *port) +{ + MUSBState *s = (MUSBState *) port->opaque; - musb_session_update(s, !!curr, s->session); + musb_intr_set(s, musb_irq_disconnect, 1); + musb_session_update(s, 1, s->session); } static inline void musb_cb_tick0(void *opaque) diff --git a/hw/usb-net.c b/hw/usb-net.c index 84924550fd..bf51bb3890 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -25,6 +25,7 @@ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" #include "net.h" #include "qemu-queue.h" #include "sysemu.h" @@ -89,182 +90,209 @@ enum usbstring_idx { #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ -/* - * mostly the same descriptor as the linux gadget rndis driver - */ -static const uint8_t qemu_net_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - USB_DT_DEVICE, /* u8 bDescriptorType; Device */ - 0x00, 0x02, /* u16 bcdUSB; v2.0 */ - USB_CLASS_COMM, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full only ] */ - 0x40, /* u8 bMaxPacketSize0 */ - RNDIS_VENDOR_NUM & 0xff, RNDIS_VENDOR_NUM >> 8, /* u16 idVendor; */ - RNDIS_PRODUCT_NUM & 0xff, RNDIS_PRODUCT_NUM >> 8, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - STRING_MANUFACTURER, /* u8 iManufacturer; */ - STRING_PRODUCT, /* u8 iProduct; */ - STRING_SERIALNUMBER, /* u8 iSerialNumber; */ - 0x02, /* u8 bNumConfigurations; */ +static const USBDescStrings usb_net_stringtable = { + [STRING_MANUFACTURER] = "QEMU", + [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", + [STRING_ETHADDR] = "400102030405", + [STRING_DATA] = "QEMU USB Net Data Interface", + [STRING_CONTROL] = "QEMU USB Net Control Interface", + [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", + [STRING_CDC] = "QEMU USB Net CDC", + [STRING_SUBSET] = "QEMU USB Net Subset", + [STRING_RNDIS] = "QEMU USB Net RNDIS", + [STRING_SERIALNUMBER] = "1", +}; + +static const USBDescIface desc_iface_rndis[] = { + { + /* RNDIS Control Interface */ + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + .iInterface = STRING_RNDIS_CONTROL, + .ndesc = 4, + .descs = (USBDescOther[]) { + { + /* Header Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + }, + },{ + /* Call Management Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + 0x01, /* u8 bDataInterface */ + }, + },{ + /* ACM Descriptor */ + .data = (uint8_t[]) { + 0x04, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + }, + },{ + /* Union Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = STATUS_BYTECOUNT, + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, + }, + } + },{ + /* RNDIS Data Interface */ + .bInterfaceNumber = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = STRING_DATA, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + } + } + } +}; + +static const USBDescIface desc_iface_cdc[] = { + { + /* CDC Control Interface */ + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + .iInterface = STRING_CONTROL, + .ndesc = 3, + .descs = (USBDescOther[]) { + { + /* Header Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + }, + },{ + /* Union Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + }, + },{ + /* Ethernet Descriptor */ + .data = (uint8_t[]) { + 0x0d, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ + STRING_ETHADDR, /* u8 iMACAddress */ + 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ + ETH_FRAME_LEN & 0xff, + ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ + 0x00, 0x00, /* le16 wNumberMCFilters */ + 0x00, /* u8 bNumberPowerFilters */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = STATUS_BYTECOUNT, + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, + }, + } + },{ + /* CDC Data Interface (off) */ + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + },{ + /* CDC Data Interface */ + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = STRING_DATA, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + } + } + } }; -static const uint8_t qemu_net_rndis_config_descriptor[] = { - /* Configuration Descriptor */ - 0x09, /* u8 bLength */ - USB_DT_CONFIG, /* u8 bDescriptorType */ - 0x43, 0x00, /* le16 wTotalLength */ - 0x02, /* u8 bNumInterfaces */ - DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */ - STRING_RNDIS, /* u8 iConfiguration */ - 0xc0, /* u8 bmAttributes */ - 0x32, /* u8 bMaxPower */ - /* RNDIS Control Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x00, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x01, /* u8 bNumEndpoints */ - USB_CLASS_COMM, /* u8 bInterfaceClass */ - USB_CDC_SUBCLASS_ACM, /* u8 bInterfaceSubClass */ - USB_CDC_ACM_PROTO_VENDOR, /* u8 bInterfaceProtocol */ - STRING_RNDIS_CONTROL, /* u8 iInterface */ - /* Header Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - /* Call Management Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - 0x01, /* u8 bDataInterface */ - /* ACM Descriptor */ - 0x04, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - /* Union Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - /* Status Descriptor */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 1, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ - STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ - 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ - /* RNDIS Data Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x02, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - STRING_DATA, /* u8 iInterface */ - /* Source Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00, /* u8 bInterval */ - /* Sink Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_OUT | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00 /* u8 bInterval */ +static const USBDescDevice desc_device_net = { + .bcdUSB = 0x0200, + .bDeviceClass = USB_CLASS_COMM, + .bMaxPacketSize0 = 0x40, + .bNumConfigurations = 2, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 2, + .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, + .iConfiguration = STRING_RNDIS, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, + .nif = ARRAY_SIZE(desc_iface_rndis), + .ifs = desc_iface_rndis, + },{ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_CONFIG_VALUE, + .iConfiguration = STRING_CDC, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, + .nif = ARRAY_SIZE(desc_iface_cdc), + .ifs = desc_iface_cdc, + } + }, }; -static const uint8_t qemu_net_cdc_config_descriptor[] = { - /* Configuration Descriptor */ - 0x09, /* u8 bLength */ - USB_DT_CONFIG, /* u8 bDescriptorType */ - 0x50, 0x00, /* le16 wTotalLength */ - 0x02, /* u8 bNumInterfaces */ - DEV_CONFIG_VALUE, /* u8 bConfigurationValue */ - STRING_CDC, /* u8 iConfiguration */ - 0xc0, /* u8 bmAttributes */ - 0x32, /* u8 bMaxPower */ - /* CDC Control Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x00, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x01, /* u8 bNumEndpoints */ - USB_CLASS_COMM, /* u8 bInterfaceClass */ - USB_CDC_SUBCLASS_ETHERNET, /* u8 bInterfaceSubClass */ - USB_CDC_PROTO_NONE, /* u8 bInterfaceProtocol */ - STRING_CONTROL, /* u8 iInterface */ - /* Header Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - /* Union Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - /* Ethernet Descriptor */ - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ - STRING_ETHADDR, /* u8 iMACAddress */ - 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ - ETH_FRAME_LEN & 0xff, ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ - 0x00, 0x00, /* le16 wNumberMCFilters */ - 0x00, /* u8 bNumberPowerFilters */ - /* Status Descriptor */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 1, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ - STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ - 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ - /* CDC Data (nop) Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x00, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - 0x00, /* u8 iInterface */ - /* CDC Data Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x01, /* u8 bAlternateSetting */ - 0x02, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - STRING_DATA, /* u8 iInterface */ - /* Source Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00, /* u8 bInterval */ - /* Sink Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_OUT | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00 /* u8 bInterval */ +static const USBDesc desc_net = { + .id = { + .idVendor = RNDIS_VENDOR_NUM, + .idProduct = RNDIS_PRODUCT_NUM, + .bcdDevice = 0, + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIALNUMBER, + }, + .full = &desc_device_net, + .str = usb_net_stringtable, }; /* @@ -599,7 +627,6 @@ struct rndis_response { typedef struct USBNetState { USBDevice dev; - unsigned int rndis; enum rndis_state rndis_state; uint32_t medium; uint32_t speed; @@ -620,6 +647,11 @@ typedef struct USBNetState { QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; } USBNetState; +static int is_rndis(USBNetState *s) +{ + return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE; +} + static int ndis_query(USBNetState *s, uint32_t oid, uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf, size_t outlen) @@ -1010,59 +1042,23 @@ static void usb_net_handle_reset(USBDevice *dev) { } -static const char * const usb_net_stringtable[] = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", - [STRING_ETHADDR] = "400102030405", - [STRING_DATA] = "QEMU USB Net Data Interface", - [STRING_CONTROL] = "QEMU USB Net Control Interface", - [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", - [STRING_CDC] = "QEMU USB Net CDC", - [STRING_SUBSET] = "QEMU USB Net Subset", - [STRING_RNDIS] = "QEMU USB Net RNDIS", - [STRING_SERIALNUMBER] = "1", -}; - static int usb_net_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBNetState *s = (USBNetState *) dev; - int ret = 0; - - switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; + int ret; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + ret = 0; + switch(request) { case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (!s->rndis || value || index != 0) + if (!is_rndis(s) || value || index != 0) { goto fail; + } #ifdef TRAFFIC_DEBUG { unsigned int i; @@ -1079,8 +1075,9 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value, break; case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: - if (!s->rndis || value || index != 0) + if (!is_rndis(s) || value || index != 0) { goto fail; + } ret = rndis_get_response(s, data); if (!ret) { data[0] = 0; @@ -1100,85 +1097,6 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value, #endif break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - ret = sizeof(qemu_net_dev_descriptor); - memcpy(data, qemu_net_dev_descriptor, ret); - break; - - case USB_DT_CONFIG: - switch (value & 0xff) { - case 0: - ret = sizeof(qemu_net_rndis_config_descriptor); - memcpy(data, qemu_net_rndis_config_descriptor, ret); - break; - - case 1: - ret = sizeof(qemu_net_cdc_config_descriptor); - memcpy(data, qemu_net_cdc_config_descriptor, ret); - break; - - default: - goto fail; - } - - data[2] = ret & 0xff; - data[3] = ret >> 8; - break; - - case USB_DT_STRING: - switch (value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - - case STRING_ETHADDR: - ret = set_usb_string(data, s->usbstring_mac); - break; - - default: - if (ARRAY_SIZE(usb_net_stringtable) > (value & 0xff)) { - ret = set_usb_string(data, - usb_net_stringtable[value & 0xff]); - break; - } - - goto fail; - } - break; - - default: - goto fail; - } - break; - - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = s->rndis ? DEV_RNDIS_CONFIG_VALUE : DEV_CONFIG_VALUE; - ret = 1; - break; - - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - switch (value & 0xff) { - case DEV_CONFIG_VALUE: - s->rndis = 0; - break; - - case DEV_RNDIS_CONFIG_VALUE: - s->rndis = 1; - break; - - default: - goto fail; - } - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_INTERFACE: case InterfaceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; @@ -1249,7 +1167,7 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) memcpy(p->data, &s->in_buf[s->in_ptr], ret); s->in_ptr += ret; if (s->in_ptr >= s->in_len && - (s->rndis || (s->in_len & (64 - 1)) || !ret)) { + (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { /* no short packet necessary */ s->in_ptr = s->in_len = 0; } @@ -1298,7 +1216,7 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) memcpy(&s->out_buf[s->out_ptr], p->data, sz); s->out_ptr += sz; - if (!s->rndis) { + if (!is_rndis(s)) { if (ret < 64) { qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); s->out_ptr = 0; @@ -1369,7 +1287,7 @@ static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t si USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; struct rndis_packet_msg_type *msg; - if (s->rndis) { + if (is_rndis(s)) { msg = (struct rndis_packet_msg_type *) s->in_buf; if (!s->rndis_state == RNDIS_DATA_INITIALIZED) return -1; @@ -1405,8 +1323,9 @@ static int usbnet_can_receive(VLANClientState *nc) { USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED) + if (is_rndis(s) && !s->rndis_state == RNDIS_DATA_INITIALIZED) { return 1; + } return !s->in_len; } @@ -1439,9 +1358,8 @@ static int usb_net_initfn(USBDevice *dev) { USBNetState *s = DO_UPCAST(USBNetState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + usb_desc_init(dev); - s->rndis = 1; s->rndis_state = RNDIS_UNINITIALIZED; QTAILQ_INIT(&s->rndis_resp); @@ -1463,6 +1381,7 @@ static int usb_net_initfn(USBDevice *dev) s->conf.macaddr.a[3], s->conf.macaddr.a[4], s->conf.macaddr.a[5]); + usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac); add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0"); return 0; @@ -1500,6 +1419,7 @@ static struct USBDeviceInfo net_info = { .qdev.name = "usb-net", .qdev.fw_name = "network", .qdev.size = sizeof(USBNetState), + .usb_desc = &desc_net, .init = usb_net_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_net_handle_reset, diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 240e8409af..09ea0b6260 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -322,52 +322,46 @@ static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr) } /* Attach or detach a device on a root hub port. */ -static void ohci_attach(USBPort *port1, USBDevice *dev) +static void ohci_attach(USBPort *port1) { OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - if (dev) { - if (port->port.dev) { - usb_attach(port1, NULL); - } - /* set connect status */ - port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; - - /* update speed */ - if (dev->speed == USB_SPEED_LOW) - port->ctrl |= OHCI_PORT_LSDA; - else - port->ctrl &= ~OHCI_PORT_LSDA; - port->port.dev = dev; - - /* notify of remote-wakeup */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) - ohci_set_interrupt(s, OHCI_INTR_RD); - - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); - DPRINTF("usb-ohci: Attached port %d\n", port1->index); + /* set connect status */ + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; + + /* update speed */ + if (port->port.dev->speed == USB_SPEED_LOW) { + port->ctrl |= OHCI_PORT_LSDA; } else { - /* set connect status */ - if (port->ctrl & OHCI_PORT_CCS) { - port->ctrl &= ~OHCI_PORT_CCS; - port->ctrl |= OHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & OHCI_PORT_PES) { - port->ctrl &= ~OHCI_PORT_PES; - port->ctrl |= OHCI_PORT_PESC; - } - dev = port->port.dev; - if (dev) { - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - } - port->port.dev = NULL; - DPRINTF("usb-ohci: Detached port %d\n", port1->index); + port->ctrl &= ~OHCI_PORT_LSDA; + } + + /* notify of remote-wakeup */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + ohci_set_interrupt(s, OHCI_INTR_RD); + } + + DPRINTF("usb-ohci: Attached port %d\n", port1->index); +} + +static void ohci_detach(USBPort *port1) +{ + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + + /* set connect status */ + if (port->ctrl & OHCI_PORT_CCS) { + port->ctrl &= ~OHCI_PORT_CCS; + port->ctrl |= OHCI_PORT_CSC; } + /* disable port */ + if (port->ctrl & OHCI_PORT_PES) { + port->ctrl &= ~OHCI_PORT_PES; + port->ctrl |= OHCI_PORT_PESC; + } + DPRINTF("usb-ohci: Detached port %d\n", port1->index); if (old_state != port->ctrl) ohci_set_interrupt(s, OHCI_INTR_RHSC); @@ -413,8 +407,9 @@ static void ohci_reset(void *opaque) { port = &ohci->rhport[i]; port->ctrl = 0; - if (port->port.dev) - ohci_attach(&port->port, port->port.dev); + if (port->port.dev) { + usb_attach(&port->port, port->port.dev); + } } if (ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); @@ -1669,6 +1664,11 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={ ohci_mem_write }; +static USBPortOps ohci_port_ops = { + .attach = ohci_attach, + .detach = ohci_detach, +}; + static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, int num_ports, uint32_t localmem_base) { @@ -1699,7 +1699,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_bus_new(&ohci->bus, dev); ohci->num_ports = num_ports; for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, NULL, ohci_attach); + usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&ohci->rhport[i].port, NULL, i+1); } ohci->async_td = 0; diff --git a/hw/usb-serial.c b/hw/usb-serial.c index c19580f305..6763d52040 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -11,6 +11,7 @@ #include "qemu-common.h" #include "qemu-error.h" #include "usb.h" +#include "usb-desc.h" #include "qemu-char.h" //#define DEBUG_Serial @@ -91,8 +92,6 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) typedef struct { USBDevice dev; - uint32_t vendorid; - uint32_t productid; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; uint16_t recv_used; @@ -104,69 +103,78 @@ typedef struct { CharDriverState *cs; } USBSerialState; -static const uint8_t qemu_serial_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x02, /* u16 bcdUSB; v2.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - /* Vendor and product id are arbitrary. */ - 0x03, 0x04, /* u16 idVendor; */ - 0x00, 0xFF, /* u16 idProduct; */ - 0x00, 0x04, /* u16 bcdDevice */ - - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x03, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT_SERIAL, + STR_PRODUCT_BRAILLE, + STR_SERIALNUMBER, }; -static const uint8_t qemu_serial_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x20, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0x80, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 100/2, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xff, /* u8 if_bInterfaceClass; Vendor Specific */ - 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */ - 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */ - 0x02, /* u8 if_iInterface; */ - - /* Bulk-In endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00, /* u8 ep_bInterval; */ - - /* Bulk-Out endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00 /* u8 ep_bInterval; */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", + [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE", + [STR_SERIALNUMBER] = "1", +}; + +static const USBDescIface desc_iface0 = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + }, + } +}; + +static const USBDescDevice desc_device = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0x80, + .bMaxPower = 50, + .ifs = &desc_iface0, + }, + }, +}; + +static const USBDesc desc_serial = { + .id = { + .idVendor = 0x0403, + .idProduct = 0x6001, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_SERIAL, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, +}; + +static const USBDesc desc_braille = { + .id = { + .idVendor = 0x0403, + .idProduct = 0xfe72, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_BRAILLE, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, }; static void usb_serial_reset(USBSerialState *s) @@ -214,89 +222,16 @@ static int usb_serial_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBSerialState *s = (USBSerialState *)dev; - int ret = 0; + int ret; - //DPRINTF("got control %x, value %x\n",request, value); + DPRINTF("got control %x, value %x\n",request, value); + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (0 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_serial_dev_descriptor, - sizeof(qemu_serial_dev_descriptor)); - data[8] = s->vendorid & 0xff; - data[9] = ((s->vendorid) >> 8) & 0xff; - data[10] = s->productid & 0xff; - data[11] = ((s->productid) >> 8) & 0xff; - ret = sizeof(qemu_serial_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_serial_config_descriptor, - sizeof(qemu_serial_config_descriptor)); - ret = sizeof(qemu_serial_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB SERIAL"); - break; - case 3: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -543,7 +478,8 @@ static void usb_serial_event(void *opaque, int event) static int usb_serial_initfn(USBDevice *dev) { USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + + usb_desc_init(dev); if (!s->cs) { error_report("Property chardev is required"); @@ -633,6 +569,7 @@ static struct USBDeviceInfo serial_info = { .product_desc = "QEMU USB Serial", .qdev.name = "usb-serial", .qdev.size = sizeof(USBSerialState), + .usb_desc = &desc_serial, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_serial_handle_reset, @@ -642,9 +579,7 @@ static struct USBDeviceInfo serial_info = { .usbdevice_name = "serial", .usbdevice_init = usb_serial_init, .qdev.props = (Property[]) { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403), - DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0x6001), + DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_END_OF_LIST(), }, }; @@ -653,6 +588,7 @@ static struct USBDeviceInfo braille_info = { .product_desc = "QEMU USB Braille", .qdev.name = "usb-braille", .qdev.size = sizeof(USBSerialState), + .usb_desc = &desc_braille, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_serial_handle_reset, @@ -662,9 +598,7 @@ static struct USBDeviceInfo braille_info = { .usbdevice_name = "braille", .usbdevice_init = usb_braille_init, .qdev.props = (Property[]) { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403), - DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0xfe72), + DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index b9b822fcb1..b384e1ddec 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -57,13 +57,18 @@ #define TD_CTRL_NAK (1 << 19) #define TD_CTRL_TIMEOUT (1 << 18) +#define UHCI_PORT_SUSPEND (1 << 12) #define UHCI_PORT_RESET (1 << 9) #define UHCI_PORT_LSDA (1 << 8) +#define UHCI_PORT_RD (1 << 6) #define UHCI_PORT_ENC (1 << 3) #define UHCI_PORT_EN (1 << 2) #define UHCI_PORT_CSC (1 << 1) #define UHCI_PORT_CCS (1 << 0) +#define UHCI_PORT_READ_ONLY (0x1bb) +#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC) + #define FRAME_TIMER_FREQ 1000 #define FRAME_MAX_LOOPS 100 @@ -307,8 +312,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token return match; } -static void uhci_attach(USBPort *port1, USBDevice *dev); - static void uhci_update_irq(UHCIState *s) { int level; @@ -348,8 +351,9 @@ static void uhci_reset(void *opaque) for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; port->ctrl = 0x0080; - if (port->port.dev) - uhci_attach(&port->port, port->port.dev); + if (port->port.dev) { + usb_attach(&port->port, port->port.dev); + } } uhci_async_cancel_all(s); @@ -498,9 +502,10 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) usb_send_msg(dev, USB_MSG_RESET); } } - port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); + port->ctrl &= UHCI_PORT_READ_ONLY; + port->ctrl |= (val & ~UHCI_PORT_READ_ONLY); /* some bits are reset when a '1' is written to them */ - port->ctrl &= ~(val & 0x000a); + port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR); } break; } @@ -593,49 +598,52 @@ static void uhci_resume (void *opaque) } } -static void uhci_attach(USBPort *port1, USBDevice *dev) +static void uhci_attach(USBPort *port1) { UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; - if (dev) { - if (port->port.dev) { - usb_attach(port1, NULL); - } - /* set connect status */ - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + /* set connect status */ + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; - /* update speed */ - if (dev->speed == USB_SPEED_LOW) - port->ctrl |= UHCI_PORT_LSDA; - else - port->ctrl &= ~UHCI_PORT_LSDA; + /* update speed */ + if (port->port.dev->speed == USB_SPEED_LOW) { + port->ctrl |= UHCI_PORT_LSDA; + } else { + port->ctrl &= ~UHCI_PORT_LSDA; + } - uhci_resume(s); + uhci_resume(s); +} - port->port.dev = dev; - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); - } else { - /* set connect status */ - if (port->ctrl & UHCI_PORT_CCS) { - port->ctrl &= ~UHCI_PORT_CCS; - port->ctrl |= UHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & UHCI_PORT_EN) { - port->ctrl &= ~UHCI_PORT_EN; - port->ctrl |= UHCI_PORT_ENC; - } +static void uhci_detach(USBPort *port1) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; - uhci_resume(s); + /* set connect status */ + if (port->ctrl & UHCI_PORT_CCS) { + port->ctrl &= ~UHCI_PORT_CCS; + port->ctrl |= UHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & UHCI_PORT_EN) { + port->ctrl &= ~UHCI_PORT_EN; + port->ctrl |= UHCI_PORT_ENC; + } - dev = port->port.dev; - if (dev) { - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - } - port->port.dev = NULL; + uhci_resume(s); +} + +static void uhci_wakeup(USBDevice *dev) +{ + USBBus *bus = usb_bus_from_device(dev); + UHCIState *s = container_of(bus, UHCIState, bus); + UHCIPort *port = s->ports + dev->port->index; + + if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { + port->ctrl |= UHCI_PORT_RD; + uhci_resume(s); } } @@ -1101,6 +1109,12 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } +static USBPortOps uhci_port_ops = { + .attach = uhci_attach, + .detach = uhci_detach, + .wakeup = uhci_wakeup, +}; + static int usb_uhci_common_initfn(UHCIState *s) { uint8_t *pci_conf = s->dev.config; @@ -1115,7 +1129,9 @@ static int usb_uhci_common_initfn(UHCIState *s) usb_bus_new(&s->bus, &s->dev.qdev); for(i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, NULL, uhci_attach); + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&s->ports[i].port, NULL, i+1); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); s->expire_time = qemu_get_clock(vm_clock) + diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 47f26cd0a3..16be7a20cf 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -28,6 +28,7 @@ #include "hw.h" #include "console.h" #include "usb.h" +#include "usb-desc.h" /* Interface requests */ #define WACOM_GET_REPORT 0x2101 @@ -54,68 +55,75 @@ typedef struct USBWacomState { int changed; } USBWacomState; -static const uint8_t qemu_wacom_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x10, /* u16 bcdUSB; v1.10 */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, +}; - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "Wacom PenPartner", + [STR_SERIALNUMBER] = "1", +}; - 0x6a, 0x05, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x10, 0x42, /* u16 bcdDevice */ +static const USBDescIface desc_iface_wacom = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + 0x21, /* u8 bDescriptorType */ + 0x01, 0x10, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type: Report */ + 0x6e, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x00, /* u8 iSerialNumber; */ - 0x01, /* u8 bNumConfigurations; */ +static const USBDescDevice desc_device_wacom = { + .bcdUSB = 0x0110, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0x80, + .bMaxPower = 40, + .ifs = &desc_iface_wacom, + }, + }, }; -static const uint8_t qemu_wacom_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0x80, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 40, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; HID */ - 0x01, /* u8 if_bInterfaceSubClass; Boot */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x10, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 0x6e, 0x00, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; */ +static const USBDesc desc_wacom = { + .id = { + .idVendor = 0x056a, + .idProduct = 0x0000, + .bcdDevice = 0x4210, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_wacom, + .str = desc_strings, }; static void usb_mouse_event(void *opaque, @@ -245,89 +253,15 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBWacomState *s = (USBWacomState *) dev; - int ret = 0; + int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_wacom_dev_descriptor, - sizeof(qemu_wacom_dev_descriptor)); - ret = sizeof(qemu_wacom_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_wacom_config_descriptor, - sizeof(qemu_wacom_config_descriptor)); - ret = sizeof(qemu_wacom_config_descriptor); - break; - case USB_DT_STRING: - switch (value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - case 2: - ret = set_usb_string(data, "Wacom PenPartner"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 4: - ret = set_usb_string(data, "Wacom Tablet"); - break; - case 5: - ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -364,7 +298,6 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value, ret = 0; break; default: - fail: ret = USB_RET_STALL; break; } @@ -410,7 +343,7 @@ static void usb_wacom_handle_destroy(USBDevice *dev) static int usb_wacom_initfn(USBDevice *dev) { USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + usb_desc_init(dev); s->changed = 1; return 0; } @@ -420,6 +353,7 @@ static struct USBDeviceInfo wacom_info = { .qdev.name = "usb-wacom-tablet", .qdev.desc = "QEMU PenPartner Tablet", .usbdevice_name = "wacom-tablet", + .usb_desc = &desc_wacom, .qdev.size = sizeof(USBWacomState), .init = usb_wacom_initfn, .handle_packet = usb_generic_handle_packet, @@ -28,7 +28,32 @@ void usb_attach(USBPort *port, USBDevice *dev) { - port->attach(port, dev); + if (dev != NULL) { + /* attach */ + if (port->dev) { + usb_attach(port, NULL); + } + dev->port = port; + port->dev = dev; + port->ops->attach(port); + usb_send_msg(dev, USB_MSG_ATTACH); + } else { + /* detach */ + dev = port->dev; + port->ops->detach(port); + if (dev) { + usb_send_msg(dev, USB_MSG_DETACH); + dev->port = NULL; + port->dev = NULL; + } + } +} + +void usb_wakeup(USBDevice *dev) +{ + if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { + dev->port->ops->wakeup(dev); + } } /**********************/ @@ -169,6 +194,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) switch(p->pid) { case USB_MSG_ATTACH: s->state = USB_STATE_ATTACHED; + if (s->info->handle_attach) { + s->info->handle_attach(s); + } return 0; case USB_MSG_DETACH: @@ -179,7 +207,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) s->remote_wakeup = 0; s->addr = 0; s->state = USB_STATE_DEFAULT; - s->info->handle_reset(s); + if (s->info->handle_reset) { + s->info->handle_reset(s); + } return 0; } @@ -44,6 +44,12 @@ #define USB_SPEED_LOW 0 #define USB_SPEED_FULL 1 #define USB_SPEED_HIGH 2 +#define USB_SPEED_SUPER 3 + +#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW) +#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL) +#define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH) +#define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER) #define USB_STATE_NOTATTACHED 0 #define USB_STATE_ATTACHED 1 @@ -116,6 +122,8 @@ #define USB_DT_STRING 0x03 #define USB_DT_INTERFACE 0x04 #define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 @@ -128,10 +136,27 @@ 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; +typedef struct USBDescString USBDescString; + +struct USBDescString { + uint8_t index; + char *str; + QLIST_ENTRY(USBDescString) next; +}; + /* definition of a USB device */ struct USBDevice { DeviceState qdev; USBDeviceInfo *info; + USBPort *port; + char *port_path; void *opaque; int speed; @@ -147,6 +172,10 @@ struct USBDevice { int setup_state; int setup_len; int setup_index; + + QLIST_HEAD(, USBDescString) strings; + const USBDescDevice *device; + const USBDescConfig *config; }; struct USBDeviceInfo { @@ -168,6 +197,11 @@ struct USBDeviceInfo { void (*handle_destroy)(USBDevice *dev); /* + * Attach the device + */ + void (*handle_attach)(USBDevice *dev); + + /* * Reset the device */ void (*handle_reset)(USBDevice *dev); @@ -190,20 +224,26 @@ 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; USBDevice *(*usbdevice_init)(const char *params); }; -typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); +typedef struct USBPortOps { + void (*attach)(USBPort *port); + void (*detach)(USBPort *port); + void (*wakeup)(USBDevice *dev); +} USBPortOps; /* USB port on which a device can be connected */ struct USBPort { USBDevice *dev; - usb_attachfn attach; + int speedmask; + char path[16]; + USBPortOps *ops; void *opaque; - USBDevice *pdev; int index; /* internal port index, may be used with the opaque */ QTAILQ_ENTRY(USBPort) next; }; @@ -251,6 +291,7 @@ static inline void usb_cancel_packet(USBPacket * p) } void usb_attach(USBPort *port, USBDevice *dev); +void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); void usb_send_msg(USBDevice *dev, int msg); @@ -313,7 +354,8 @@ USBDevice *usb_create(USBBus *bus, const char *name); USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBDevice *pdev, usb_attachfn attach); + USBPortOps *ops, int speedmask); +void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_unregister_port(USBBus *bus, USBPort *port); int usb_device_attach(USBDevice *dev); int usb_device_detach(USBDevice *dev); |