diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 907d723728de6209aa8d0048d2cf1f223c1bf620..6a75718fbb2a572de2e41c3b863a8b67d22dec5a 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -377,6 +377,10 @@ static QEMUMachine pc_machine_v1_1 = {
             .driver   = "apic",\
             .property = "vapic",\
             .value    = "off",\
+        },{\
+            .driver   = "USB",\
+            .property = "full-path",\
+            .value    = "no",\
         }
 
 static QEMUMachine pc_machine_v1_0 = {
diff --git a/hw/usb.h b/hw/usb.h
index e95085f0b3b73b864ddfdd9dc85071697fad134c..ae7ccda18cc08378ce89985280c8ebd82e47c413 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -182,12 +182,17 @@ struct USBEndpoint {
     QTAILQ_HEAD(, USBPacket) queue;
 };
 
+enum USBDeviceFlags {
+    USB_DEV_FLAG_FULL_PATH,
+};
+
 /* definition of a USB device */
 struct USBDevice {
     DeviceState qdev;
     USBPort *port;
     char *port_path;
     void *opaque;
+    uint32_t flags;
 
     /* Actual connected speed */
     int speed;
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index d3f835895ded90c01202a613c831a922528c8e9c..2068640a58bcdf57d9d0f47f411d720c06863fad 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -19,6 +19,8 @@ static struct BusInfo usb_bus_info = {
     .get_fw_dev_path = usb_get_fw_dev_path,
     .props      = (Property[]) {
         DEFINE_PROP_STRING("port", USBDevice, port_path),
+        DEFINE_PROP_BIT("full-path", USBDevice, flags,
+                        USB_DEV_FLAG_FULL_PATH, true),
         DEFINE_PROP_END_OF_LIST()
     },
 };
@@ -460,7 +462,20 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
 static char *usb_get_dev_path(DeviceState *qdev)
 {
     USBDevice *dev = USB_DEVICE(qdev);
-    return g_strdup(dev->port->path);
+    DeviceState *hcd = qdev->parent_bus->parent;
+    char *id = NULL;
+
+    if ((dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) &&
+        hcd && hcd->parent_bus && hcd->parent_bus->info->get_dev_path) {
+        id = hcd->parent_bus->info->get_dev_path(hcd);
+    }
+    if (id) {
+        char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
+        g_free(id);
+        return ret;
+    } else {
+        return g_strdup(dev->port->path);
+    }
 }
 
 static char *usb_get_fw_dev_path(DeviceState *qdev)
diff --git a/hw/usb/core.c b/hw/usb/core.c
index a4048fe3e0bf87086115354037d211561be2c4db..9a14a5385227035b549e754f59a8bfc458a0227c 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -484,12 +484,17 @@ void usb_packet_check_state(USBPacket *p, USBPacketState expected)
 
 void usb_packet_set_state(USBPacket *p, USBPacketState state)
 {
-    USBDevice *dev = p->ep->dev;
-    USBBus *bus = usb_bus_from_device(dev);
-
-    trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
-                                  usb_packet_state_name(p->state),
-                                  usb_packet_state_name(state));
+    if (p->ep) {
+        USBDevice *dev = p->ep->dev;
+        USBBus *bus = usb_bus_from_device(dev);
+        trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
+                                      usb_packet_state_name(p->state),
+                                      usb_packet_state_name(state));
+    } else {
+        trace_usb_packet_state_change(-1, "", -1, p,
+                                      usb_packet_state_name(p->state),
+                                      usb_packet_state_name(state));
+    }
     p->state = state;
 }
 
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index 9847a75b83b225e7034762328dfa53c92c89acf3..3c77368cb056df4fe316e347dcb68be68a607310 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -18,32 +18,33 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
                     uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x12;
+    USBDescriptor *d = (void *)dest;
 
     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;
+    d->bLength                     = bLength;
+    d->bDescriptorType             = USB_DT_DEVICE;
+
+    d->u.device.bcdUSB_lo          = usb_lo(dev->bcdUSB);
+    d->u.device.bcdUSB_hi          = usb_hi(dev->bcdUSB);
+    d->u.device.bDeviceClass       = dev->bDeviceClass;
+    d->u.device.bDeviceSubClass    = dev->bDeviceSubClass;
+    d->u.device.bDeviceProtocol    = dev->bDeviceProtocol;
+    d->u.device.bMaxPacketSize0    = dev->bMaxPacketSize0;
+
+    d->u.device.idVendor_lo        = usb_lo(id->idVendor);
+    d->u.device.idVendor_hi        = usb_hi(id->idVendor);
+    d->u.device.idProduct_lo       = usb_lo(id->idProduct);
+    d->u.device.idProduct_hi       = usb_hi(id->idProduct);
+    d->u.device.bcdDevice_lo       = usb_lo(id->bcdDevice);
+    d->u.device.bcdDevice_hi       = usb_hi(id->bcdDevice);
+    d->u.device.iManufacturer      = id->iManufacturer;
+    d->u.device.iProduct           = id->iProduct;
+    d->u.device.iSerialNumber      = id->iSerialNumber;
+
+    d->u.device.bNumConfigurations = dev->bNumConfigurations;
 
     return bLength;
 }
@@ -52,22 +53,23 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
                               uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x0a;
+    USBDescriptor *d = (void *)dest;
 
     if (len < bLength) {
         return -1;
     }
 
-    dest[0x00] = bLength;
-    dest[0x01] = USB_DT_DEVICE_QUALIFIER;
+    d->bLength                               = bLength;
+    d->bDescriptorType                       = 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 */
+    d->u.device_qualifier.bcdUSB_lo          = usb_lo(dev->bcdUSB);
+    d->u.device_qualifier.bcdUSB_hi          = usb_hi(dev->bcdUSB);
+    d->u.device_qualifier.bDeviceClass       = dev->bDeviceClass;
+    d->u.device_qualifier.bDeviceSubClass    = dev->bDeviceSubClass;
+    d->u.device_qualifier.bDeviceProtocol    = dev->bDeviceProtocol;
+    d->u.device_qualifier.bMaxPacketSize0    = dev->bMaxPacketSize0;
+    d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
+    d->u.device_qualifier.bReserved          = 0;
 
     return bLength;
 }
@@ -76,22 +78,24 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
 {
     uint8_t  bLength = 0x09;
     uint16_t wTotalLength = 0;
+    USBDescriptor *d = (void *)dest;
     int i, rc;
 
     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;
+    d->bLength                      = bLength;
+    d->bDescriptorType              = USB_DT_CONFIG;
+
+    d->u.config.bNumInterfaces      = conf->bNumInterfaces;
+    d->u.config.bConfigurationValue = conf->bConfigurationValue;
+    d->u.config.iConfiguration      = conf->iConfiguration;
+    d->u.config.bmAttributes        = conf->bmAttributes;
+    d->u.config.bMaxPower           = conf->bMaxPower;
     wTotalLength += bLength;
 
-    /* handle grouped interfaces if any*/
+    /* handle grouped interfaces if any */
     for (i = 0; i < conf->nif_groups; i++) {
         rc = usb_desc_iface_group(&(conf->if_groups[i]),
                                   dest + wTotalLength,
@@ -111,8 +115,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
         wTotalLength += rc;
     }
 
-    dest[0x02] = usb_lo(wTotalLength);
-    dest[0x03] = usb_hi(wTotalLength);
+    d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
+    d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
     return wTotalLength;
 }
 
@@ -155,20 +159,22 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
 {
     uint8_t bLength = 0x09;
     int i, rc, pos = 0;
+    USBDescriptor *d = (void *)dest;
 
     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;
+    d->bLength                        = bLength;
+    d->bDescriptorType                = USB_DT_INTERFACE;
+
+    d->u.interface.bInterfaceNumber   = iface->bInterfaceNumber;
+    d->u.interface.bAlternateSetting  = iface->bAlternateSetting;
+    d->u.interface.bNumEndpoints      = iface->bNumEndpoints;
+    d->u.interface.bInterfaceClass    = iface->bInterfaceClass;
+    d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass;
+    d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol;
+    d->u.interface.iInterface         = iface->iInterface;
     pos += bLength;
 
     for (i = 0; i < iface->ndesc; i++) {
@@ -194,21 +200,23 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
 {
     uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
     uint8_t extralen = ep->extra ? ep->extra[0] : 0;
+    USBDescriptor *d = (void *)dest;
 
     if (len < bLength + extralen) {
         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;
+    d->bLength                      = bLength;
+    d->bDescriptorType              = USB_DT_ENDPOINT;
+
+    d->u.endpoint.bEndpointAddress  = ep->bEndpointAddress;
+    d->u.endpoint.bmAttributes      = ep->bmAttributes;
+    d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize);
+    d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize);
+    d->u.endpoint.bInterval         = ep->bInterval;
     if (ep->is_audio) {
-        dest[0x07] = ep->bRefresh;
-        dest[0x08] = ep->bSynchAddress;
+        d->u.endpoint.bRefresh      = ep->bRefresh;
+        d->u.endpoint.bSynchAddress = ep->bSynchAddress;
     }
     if (ep->extra) {
         memcpy(dest + bLength, ep->extra, extralen);
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index d6e07ea5d24da097c145b98a85521760206754a3..d164e8f891799356535f8d8fa2a2e280cedfcb63 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -3,6 +3,69 @@
 
 #include <inttypes.h>
 
+/* binary representation */
+typedef struct USBDescriptor {
+    uint8_t                   bLength;
+    uint8_t                   bDescriptorType;
+    union {
+        struct {
+            uint8_t           bcdUSB_lo;
+            uint8_t           bcdUSB_hi;
+            uint8_t           bDeviceClass;
+            uint8_t           bDeviceSubClass;
+            uint8_t           bDeviceProtocol;
+            uint8_t           bMaxPacketSize0;
+            uint8_t           idVendor_lo;
+            uint8_t           idVendor_hi;
+            uint8_t           idProduct_lo;
+            uint8_t           idProduct_hi;
+            uint8_t           bcdDevice_lo;
+            uint8_t           bcdDevice_hi;
+            uint8_t           iManufacturer;
+            uint8_t           iProduct;
+            uint8_t           iSerialNumber;
+            uint8_t           bNumConfigurations;
+        } device;
+        struct {
+            uint8_t           bcdUSB_lo;
+            uint8_t           bcdUSB_hi;
+            uint8_t           bDeviceClass;
+            uint8_t           bDeviceSubClass;
+            uint8_t           bDeviceProtocol;
+            uint8_t           bMaxPacketSize0;
+            uint8_t           bNumConfigurations;
+            uint8_t           bReserved;
+        } device_qualifier;
+        struct {
+            uint8_t           wTotalLength_lo;
+            uint8_t           wTotalLength_hi;
+            uint8_t           bNumInterfaces;
+            uint8_t           bConfigurationValue;
+            uint8_t           iConfiguration;
+            uint8_t           bmAttributes;
+            uint8_t           bMaxPower;
+        } config;
+        struct {
+            uint8_t           bInterfaceNumber;
+            uint8_t           bAlternateSetting;
+            uint8_t           bNumEndpoints;
+            uint8_t           bInterfaceClass;
+            uint8_t           bInterfaceSubClass;
+            uint8_t           bInterfaceProtocol;
+            uint8_t           iInterface;
+        } interface;
+        struct {
+            uint8_t           bEndpointAddress;
+            uint8_t           bmAttributes;
+            uint8_t           wMaxPacketSize_lo;
+            uint8_t           wMaxPacketSize_hi;
+            uint8_t           bInterval;
+            uint8_t           bRefresh;        /* only audio ep */
+            uint8_t           bSynchAddress;   /* only audio ep */
+        } endpoint;
+    } u;
+} QEMU_PACKED USBDescriptor;
+
 struct USBDescID {
     uint16_t                  idVendor;
     uint16_t                  idProduct;
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index eb4e71120741e84eeced764ac1c29814e90e359c..9c9166551e501eb3816370794e8f6a36615ba9d4 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -22,11 +22,10 @@
  * THE SOFTWARE.
  */
 #include "qemu-common.h"
+#include "trace.h"
 #include "hw/usb.h"
 #include "hw/usb/desc.h"
 
-//#define DEBUG
-
 #define NUM_PORTS 8
 
 typedef struct USBHubPort {
@@ -157,6 +156,7 @@ static void usb_hub_attach(USBPort *port1)
     USBHubState *s = port1->opaque;
     USBHubPort *port = &s->ports[port1->index];
 
+    trace_usb_hub_attach(s->dev.addr, port1->index + 1);
     port->wPortStatus |= PORT_STAT_CONNECTION;
     port->wPortChange |= PORT_STAT_C_CONNECTION;
     if (port->port.dev->speed == USB_SPEED_LOW) {
@@ -172,6 +172,7 @@ static void usb_hub_detach(USBPort *port1)
     USBHubState *s = port1->opaque;
     USBHubPort *port = &s->ports[port1->index];
 
+    trace_usb_hub_detach(s->dev.addr, port1->index + 1);
     usb_wakeup(s->intr);
 
     /* Let upstream know the device on this port is gone */
@@ -247,6 +248,7 @@ static void usb_hub_handle_reset(USBDevice *dev)
     USBHubPort *port;
     int i;
 
+    trace_usb_hub_reset(s->dev.addr);
     for (i = 0; i < NUM_PORTS; i++) {
         port = s->ports + i;
         port->wPortStatus = PORT_STAT_POWER;
@@ -261,12 +263,39 @@ static void usb_hub_handle_reset(USBDevice *dev)
     }
 }
 
+static const char *feature_name(int feature)
+{
+    static const char *name[] = {
+        [PORT_CONNECTION]    = "connection",
+        [PORT_ENABLE]        = "enable",
+        [PORT_SUSPEND]       = "suspend",
+        [PORT_OVERCURRENT]   = "overcurrent",
+        [PORT_RESET]         = "reset",
+        [PORT_POWER]         = "power",
+        [PORT_LOWSPEED]      = "lowspeed",
+        [PORT_HIGHSPEED]     = "highspeed",
+        [PORT_C_CONNECTION]  = "change connection",
+        [PORT_C_ENABLE]      = "change enable",
+        [PORT_C_SUSPEND]     = "change suspend",
+        [PORT_C_OVERCURRENT] = "change overcurrent",
+        [PORT_C_RESET]       = "change reset",
+        [PORT_TEST]          = "test",
+        [PORT_INDICATOR]     = "indicator",
+    };
+    if (feature < 0 || feature >= ARRAY_SIZE(name)) {
+        return "?";
+    }
+    return name[feature] ?: "?";
+}
+
 static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBHubState *s = (USBHubState *)dev;
     int ret;
 
+    trace_usb_hub_control(s->dev.addr, request, value, index, length);
+
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
         return ret;
@@ -295,6 +324,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
                 goto fail;
             }
             port = &s->ports[n];
+            trace_usb_hub_get_port_status(s->dev.addr, index,
+                                          port->wPortStatus,
+                                          port->wPortChange);
             data[0] = port->wPortStatus;
             data[1] = port->wPortStatus >> 8;
             data[2] = port->wPortChange;
@@ -315,6 +347,10 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
             unsigned int n = index - 1;
             USBHubPort *port;
             USBDevice *dev;
+
+            trace_usb_hub_set_port_feature(s->dev.addr, index,
+                                           feature_name(value));
+
             if (n >= NUM_PORTS) {
                 goto fail;
             }
@@ -345,6 +381,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
             unsigned int n = index - 1;
             USBHubPort *port;
 
+            trace_usb_hub_clear_port_feature(s->dev.addr, index,
+                                             feature_name(value));
+
             if (n >= NUM_PORTS) {
                 goto fail;
             }
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 60f9f5bdb59e31e2c0eb4d357d40438c3d64bfd6..23631a47c9e727b89bbd941212553531a464c375 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -403,7 +403,6 @@ struct EHCIState {
     /*
      *  Internal states, shadow registers, etc
      */
-    uint32_t sofv;
     QEMUTimer *frame_timer;
     int attach_poll_counter;
     int astate;                        // Current state in asynchronous schedule
@@ -797,7 +796,6 @@ static void ehci_child_detach(USBPort *port, USBDevice *child)
     if (portsc & PORTSC_POWNER) {
         USBPort *companion = s->companion_ports[port->index];
         companion->ops->child_detach(companion, child);
-        companion->dev = NULL;
         return;
     }
 
@@ -1103,10 +1101,6 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
         val &= USBINTR_MASK;
         break;
 
-    case FRINDEX:
-        s->sofv = val >> 3;
-        break;
-
     case CONFIGFLAG:
         val &= 0x1;
         if (val) {
@@ -2015,7 +2009,6 @@ static void ehci_advance_state(EHCIState *ehci,
             fprintf(stderr, "processing error - resetting ehci HC\n");
             ehci_reset(ehci);
             again = 0;
-            assert(0);
         }
     }
     while (again);
@@ -2150,13 +2143,14 @@ static void ehci_frame_timer(void *opaque)
         if ( !(ehci->usbsts & USBSTS_HALT)) {
             ehci->frindex += 8;
 
-            if (ehci->frindex > 0x00001fff) {
-                ehci->frindex = 0;
+            if (ehci->frindex == 0x00002000) {
                 ehci_set_interrupt(ehci, USBSTS_FLR);
             }
 
-            ehci->sofv = (ehci->frindex - 1) >> 3;
-            ehci->sofv &= 0x000003ff;
+            if (ehci->frindex == 0x00004000) {
+                ehci_set_interrupt(ehci, USBSTS_FLR);
+                ehci->frindex = 0;
+            }
         }
 
         if (frames - i > ehci->maxframes) {
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index e55dad914c322eaecd04acdaf020a16f866277ed..266d550b9ce3543ddf4f4759271a158c06aa594b 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -795,7 +795,8 @@ out:
     return TD_RESULT_NEXT_QH;
 }
 
-static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
+                          uint32_t *int_mask, bool queuing)
 {
     UHCIAsync *async;
     int len = 0, max_len;
@@ -814,6 +815,12 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 
         if (!async->done)
             return TD_RESULT_ASYNC_CONT;
+        if (queuing) {
+            /* we are busy filling the queue, we are not prepared
+               to consume completed packages then, just leave them
+               in async state */
+            return TD_RESULT_ASYNC_CONT;
+        }
 
         uhci_async_unlink(async);
         goto done;
@@ -964,7 +971,10 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
             break;
         }
         trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
-        ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+        ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
+        if (ret == TD_RESULT_ASYNC_CONT) {
+            break;
+        }
         assert(ret == TD_RESULT_ASYNC_START);
         assert(int_mask == 0);
         plink = ptd.link;
@@ -1045,7 +1055,7 @@ static void uhci_process_frame(UHCIState *s)
         trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
 
         old_td_ctrl = td.ctrl;
-        ret = uhci_handle_td(s, link, &td, &int_mask);
+        ret = uhci_handle_td(s, link, &td, &int_mask, false);
         if (old_td_ctrl != td.ctrl) {
             /* update the status bits of the TD */
             val = cpu_to_le32(td.ctrl);
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 90919c242aef6e8636157ffdabc4b9c0218a8361..061a1b78252ccd95473c2faf2faecb2012577eae 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -42,6 +42,7 @@
 #include <linux/usbdevice_fs.h>
 #include <linux/version.h>
 #include "hw/usb.h"
+#include "hw/usb/desc.h"
 
 /* We redefine it to avoid version problems */
 struct usb_ctrltransfer {
@@ -94,6 +95,10 @@ struct USBAutoFilter {
     uint32_t product_id;
 };
 
+enum USBHostDeviceOptions {
+    USB_HOST_OPT_PIPELINE,
+};
+
 typedef struct USBHostDevice {
     USBDevice dev;
     int       fd;
@@ -104,6 +109,7 @@ typedef struct USBHostDevice {
     int       descr_len;
     int       closing;
     uint32_t  iso_urb_count;
+    uint32_t  options;
     Notifier  exit;
 
     struct endp_data ep_in[USB_MAX_ENDPOINTS];
@@ -115,6 +121,7 @@ typedef struct USBHostDevice {
     int addr;
     char port[MAX_PORTLEN];
     struct USBAutoFilter match;
+    int32_t bootindex;
     int seen, errcount;
 
     QTAILQ_ENTRY(USBHostDevice) next;
@@ -374,10 +381,10 @@ static void async_complete(void *opaque)
             }
 
             if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
                 usb_generic_async_ctrl_complete(&s->dev, p);
             } else if (!aurb->more) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
                 usb_packet_complete(&s->dev, p);
             }
         }
@@ -391,12 +398,14 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     AsyncURB *aurb;
 
+    trace_usb_host_req_canceled(s->bus_num, s->addr, p);
+
     QLIST_FOREACH(aurb, &s->aurbs, next) {
         if (p != aurb->packet) {
             continue;
         }
 
-        DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
+        trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
 
         /* Mark it as dead (see async_complete above) */
         aurb->packet = NULL;
@@ -844,12 +853,12 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
     uint8_t *pbuf;
     uint8_t ep;
 
-    trace_usb_host_req_data(s->bus_num, s->addr,
+    trace_usb_host_req_data(s->bus_num, s->addr, p,
                             p->pid == USB_TOKEN_IN,
                             p->ep->nr, p->iov.size);
 
     if (!is_valid(s, p->pid, p->ep->nr)) {
-        trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+        trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
         return USB_RET_NAK;
     }
 
@@ -864,7 +873,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
         if (ret < 0) {
             perror("USBDEVFS_CLEAR_HALT");
-            trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+            trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
             return USB_RET_NAK;
         }
         clear_halt(s, p->pid, p->ep->nr);
@@ -919,11 +928,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
 
             switch(errno) {
             case ETIMEDOUT:
-                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                            USB_RET_NAK);
                 return USB_RET_NAK;
             case EPIPE:
             default:
-                trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                            USB_RET_STALL);
                 return USB_RET_STALL;
             }
         }
@@ -1030,17 +1041,23 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
      */
 
     /* Note request is (bRequestType << 8) | bRequest */
-    trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
+    trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
 
     switch (request) {
     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        return usb_host_set_address(s, value);
+        ret = usb_host_set_address(s, value);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+        return ret;
 
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        return usb_host_set_config(s, value & 0xff);
+        ret = usb_host_set_config(s, value & 0xff);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+        return ret;
 
     case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        return usb_host_set_interface(s, index, value);
+        ret = usb_host_set_interface(s, index, value);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+        return ret;
     }
 
     /* The rest are asynchronous */
@@ -1092,120 +1109,128 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
     return USB_RET_ASYNC;
 }
 
-static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
-    uint8_t configuration, uint8_t interface)
-{
-    char device_name[64], line[1024];
-    int alt_setting;
-
-    sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
-            (int)configuration, (int)interface);
-
-    if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
-                            device_name)) {
-        /* Assume alt 0 on error */
-        return 0;
-    }
-    if (sscanf(line, "%d", &alt_setting) != 1) {
-        /* Assume alt 0 on error */
-        return 0;
-    }
-    return alt_setting;
-}
-
 /* returns 1 on problem encountered or 0 for success */
 static int usb_linux_update_endp_table(USBHostDevice *s)
 {
-    uint8_t *descriptors;
-    uint8_t devep, type, alt_interface;
-    uint16_t raw;
-    int interface, length, i, ep, pid;
+    static const char *tname[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = "control",
+        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
+        [USB_ENDPOINT_XFER_BULK]    = "bulk",
+        [USB_ENDPOINT_XFER_INT]     = "int",
+    };
+    uint8_t devep, type;
+    uint16_t mps, v, p;
+    int ep, pid;
+    unsigned int i, configuration = -1, interface = -1, altsetting = -1;
     struct endp_data *epd;
+    USBDescriptor *d;
+    bool active = false;
 
     usb_ep_init(&s->dev);
 
-    if (s->dev.configuration == 0) {
-        /* not configured yet -- leave all endpoints disabled */
-        return 0;
-    }
-
-    /* get the desired configuration, interface, and endpoint descriptors
-     * from device description */
-    descriptors = &s->descr[18];
-    length = s->descr_len - 18;
-    i = 0;
-
-    while (i < length) {
-        if (descriptors[i + 1] != USB_DT_CONFIG) {
-            fprintf(stderr, "invalid descriptor data\n");
-            return 1;
-        } else if (descriptors[i + 5] != s->dev.configuration) {
-            DPRINTF("not requested configuration %d\n", s->dev.configuration);
-            i += (descriptors[i + 3] << 8) + descriptors[i + 2];
-            continue;
+    for (i = 0;; i += d->bLength) {
+        if (i+2 >= s->descr_len) {
+            break;
         }
-        i += descriptors[i];
-
-        if (descriptors[i + 1] != USB_DT_INTERFACE ||
-            (descriptors[i + 1] == USB_DT_INTERFACE &&
-             descriptors[i + 4] == 0)) {
-            i += descriptors[i];
-            continue;
+        d = (void *)(s->descr + i);
+        if (d->bLength < 2) {
+            trace_usb_host_parse_error(s->bus_num, s->addr,
+                                       "descriptor too short");
+            goto error;
         }
-
-        interface = descriptors[i + 2];
-        alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
-                                                  interface);
-
-        /* the current interface descriptor is the active interface
-         * and has endpoints */
-        if (descriptors[i + 3] != alt_interface) {
-            i += descriptors[i];
-            continue;
-        }
-
-        /* advance to the endpoints */
-        while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
-            i += descriptors[i];
+        if (i + d->bLength > s->descr_len) {
+            trace_usb_host_parse_error(s->bus_num, s->addr,
+                                       "descriptor too long");
+            goto error;
         }
-
-        if (i >= length)
+        switch (d->bDescriptorType) {
+        case 0:
+            trace_usb_host_parse_error(s->bus_num, s->addr,
+                                       "invalid descriptor type");
+            goto error;
+        case USB_DT_DEVICE:
+            if (d->bLength < 0x12) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "device descriptor too short");
+                goto error;
+            }
+            v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
+            p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
+            trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
             break;
-
-        while (i < length) {
-            if (descriptors[i + 1] != USB_DT_ENDPOINT) {
-                break;
+        case USB_DT_CONFIG:
+            if (d->bLength < 0x09) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "config descriptor too short");
+                goto error;
             }
-
-            devep = descriptors[i + 2];
+            configuration = d->u.config.bConfigurationValue;
+            active = (configuration == s->dev.configuration);
+            trace_usb_host_parse_config(s->bus_num, s->addr,
+                                        configuration, active);
+            break;
+        case USB_DT_INTERFACE:
+            if (d->bLength < 0x09) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "interface descriptor too short");
+                goto error;
+            }
+            interface = d->u.interface.bInterfaceNumber;
+            altsetting = d->u.interface.bAlternateSetting;
+            active = (configuration == s->dev.configuration) &&
+                (altsetting == s->dev.altsetting[interface]);
+            trace_usb_host_parse_interface(s->bus_num, s->addr,
+                                           interface, altsetting, active);
+            break;
+        case USB_DT_ENDPOINT:
+            if (d->bLength < 0x07) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "endpoint descriptor too short");
+                goto error;
+            }
+            devep = d->u.endpoint.bEndpointAddress;
             pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
             ep = devep & 0xf;
             if (ep == 0) {
-                fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
-                return 1;
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "invalid endpoint address");
+                goto error;
             }
 
-            type = descriptors[i + 3] & 0x3;
-            raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
-            usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
-            assert(usb_ep_get_type(&s->dev, pid, ep) ==
-                   USB_ENDPOINT_XFER_INVALID);
-            usb_ep_set_type(&s->dev, pid, ep, type);
-            usb_ep_set_ifnum(&s->dev, pid, ep, interface);
-            if (type == USB_ENDPOINT_XFER_BULK) {
-                usb_ep_set_pipeline(&s->dev, pid, ep, true);
-            }
+            type = d->u.endpoint.bmAttributes & 0x3;
+            mps = d->u.endpoint.wMaxPacketSize_lo |
+                (d->u.endpoint.wMaxPacketSize_hi << 8);
+            trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
+                                          (devep & USB_DIR_IN) ? "in" : "out",
+                                          tname[type], active);
+
+            if (active) {
+                usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
+                assert(usb_ep_get_type(&s->dev, pid, ep) ==
+                       USB_ENDPOINT_XFER_INVALID);
+                usb_ep_set_type(&s->dev, pid, ep, type);
+                usb_ep_set_ifnum(&s->dev, pid, ep, interface);
+                if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
+                    (type == USB_ENDPOINT_XFER_BULK)) {
+                    usb_ep_set_pipeline(&s->dev, pid, ep, true);
+                }
 
-            epd = get_endp(s, pid, ep);
-            epd->halted = 0;
+                epd = get_endp(s, pid, ep);
+                epd->halted = 0;
+            }
 
-            i += descriptors[i];
+            break;
+        default:
+            trace_usb_host_parse_unknown(s->bus_num, s->addr,
+                                         d->bLength, d->bDescriptorType);
+            break;
         }
     }
-#ifdef DEBUG
-    usb_ep_dump(&s->dev);
-#endif
     return 0;
+
+error:
+    usb_ep_init(&s->dev);
+    return 1;
 }
 
 /*
@@ -1403,6 +1428,7 @@ static int usb_host_initfn(USBDevice *dev)
     if (s->match.bus_num != 0 && s->match.port != NULL) {
         usb_host_claim_port(s);
     }
+    add_boot_device_path(s->bootindex, &dev->qdev, NULL);
     return 0;
 }
 
@@ -1418,6 +1444,9 @@ static Property usb_host_dev_properties[] = {
     DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
     DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
     DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
+    DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
+                    USB_HOST_OPT_PIPELINE, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8e9f175dbb94eba7f7ad830d89c65ec293fccb09..94ab4632caa5a921a440656a20f130bcc1282235 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -74,6 +74,7 @@ struct USBRedirDevice {
     CharDriverState *cs;
     uint8_t debug;
     char *filter_str;
+    int32_t bootindex;
     /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
     const uint8_t *read_buf;
     int read_buf_size;
@@ -835,7 +836,13 @@ static void usbredir_do_attach(void *opaque)
 {
     USBRedirDevice *dev = opaque;
 
-    usb_device_attach(&dev->dev);
+    if (usb_device_attach(&dev->dev) != 0) {
+        usbredir_device_disconnect(dev);
+        if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+            usbredirparser_send_filter_reject(dev->parser);
+            usbredirparser_do_write(dev->parser);
+        }
+    }
 }
 
 /*
@@ -923,6 +930,7 @@ static int usbredir_initfn(USBDevice *udev)
     qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
                           usbredir_chardev_read, usbredir_chardev_event, dev);
 
+    add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
     return 0;
 }
 
@@ -1452,6 +1460,7 @@ static Property usbredir_properties[] = {
     DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
     DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
     DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+    DEFINE_PROP_INT32("bootindex", USBRedirDevice, bootindex, -1),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/trace-events b/trace-events
index a5f276d0202688794256c69874252b52e7ca37d8..d2e787962fb47eaead1910dff098133652fde9df 100644
--- a/trace-events
+++ b/trace-events
@@ -289,7 +289,7 @@ usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
 usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
 usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
 
-# hw/usb-desc.c
+# hw/usb/desc.c
 usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
 usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
 usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
@@ -301,6 +301,15 @@ usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d,
 usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
 
+# hw/usb/dev-hub.c
+usb_hub_reset(int addr) "dev %d"
+usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, langth %d"
+usb_hub_get_port_status(int addr, int nr, int status, int changed) "dev %d, port %d, status 0x%x, changed 0x%x"
+usb_hub_set_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s"
+usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s"
+usb_hub_attach(int addr, int nr) "dev %d, port %d"
+usb_hub_detach(int addr, int nr) "dev %d, port %d"
+
 # hw/usb/host-linux.c
 usb_host_open_started(int bus, int addr) "dev %d:%d"
 usb_host_open_success(int bus, int addr) "dev %d:%d"
@@ -312,11 +321,14 @@ usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d"
 usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d"
 usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d"
 usb_host_release_interfaces(int bus, int addr) "dev %d:%d"
-usb_host_req_control(int bus, int addr, int req, int value, int index) "dev %d:%d, req 0x%x, value %d, index %d"
-usb_host_req_data(int bus, int addr, int in, int ep, int size) "dev %d:%d, in %d, ep %d, size %d"
-usb_host_req_complete(int bus, int addr, int status) "dev %d:%d, status %d"
+usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d"
+usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d"
+usb_host_req_complete(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d"
+usb_host_req_emulated(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d"
+usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p"
 usb_host_urb_submit(int bus, int addr, void *aurb, int length, int more) "dev %d:%d, aurb %p, length %d, more %d"
 usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int more) "dev %d:%d, aurb %p, status %d, length %d, more %d"
+usb_host_urb_canceled(int bus, int addr, void *aurb) "dev %d:%d, aurb %p"
 usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 usb_host_ep_start_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"
@@ -325,6 +337,12 @@ usb_host_reset(int bus, int addr) "dev %d:%d"
 usb_host_auto_scan_enabled(void)
 usb_host_auto_scan_disabled(void)
 usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d"
+usb_host_parse_device(int bus, int addr, int vendor, int product) "dev %d:%d, id %04x:%04x"
+usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d"
+usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d"
+usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d"
+usb_host_parse_unknown(int bus, int addr, int len, int type) "dev %d:%d, len %d, type %d"
+usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s"
 
 # hw/scsi-bus.c
 scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"