summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS4
-rw-r--r--Makefile.objs2
-rw-r--r--hw/usb-bt.c525
-rw-r--r--hw/usb-bus.c129
-rw-r--r--hw/usb-desc.c406
-rw-r--r--hw/usb-desc.h92
-rw-r--r--hw/usb-hid.c486
-rw-r--r--hw/usb-hub.c250
-rw-r--r--hw/usb-msd.c267
-rw-r--r--hw/usb-musb.c44
-rw-r--r--hw/usb-net.c528
-rw-r--r--hw/usb-ohci.c88
-rw-r--r--hw/usb-serial.c236
-rw-r--r--hw/usb-uhci.c98
-rw-r--r--hw/usb-wacom.c214
-rw-r--r--hw/usb.c34
-rw-r--r--hw/usb.h50
-rw-r--r--trace-events11
18 files changed, 1842 insertions, 1622 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 98db32216f..f20d390d0c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -329,8 +329,8 @@ F: hw/lsi53c895a.c
 F: hw/scsi*
 
 USB
-M: qemu-devel@nongnu.org
-S: Odd Fixes
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Maintained
 F: hw/usb*
 
 vhost
diff --git a/Makefile.objs b/Makefile.objs
index c3e52c5674..fda366d11c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -88,7 +88,7 @@ common-obj-y += eeprom93xx.o
 common-obj-y += scsi-disk.o cdrom.o
 common-obj-y += scsi-generic.o scsi-bus.o
 common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
-common-obj-y += usb-serial.o usb-net.o usb-bus.o
+common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
 common-obj-$(CONFIG_SD) += sd.o
diff --git a/hw/usb-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,
diff --git a/hw/usb.c b/hw/usb.c
index a326bcfffe..82a6217a0b 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -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;
     }
 
diff --git a/hw/usb.h b/hw/usb.h
index 0b32d77e6f..5c1da3ed25 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -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);
diff --git a/trace-events b/trace-events
index e8fed0f424..19cee6a1d8 100644
--- a/trace-events
+++ b/trace-events
@@ -190,6 +190,17 @@ disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "g
 disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
 disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
 
+# hw/usb-desc.c
+disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
+disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
+disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+disable usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
+disable usb_set_addr(int addr) "dev %d"
+disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
+disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
+disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
+
 # vl.c
 disable vm_state_notify(int running, int reason) "running %d reason %d"