summary refs log tree commit diff stats
path: root/hw/usb
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/Makefile.objs13
-rw-r--r--hw/usb/bus.c58
-rw-r--r--hw/usb/core.c17
-rw-r--r--hw/usb/desc.c5
-rw-r--r--hw/usb/dev-bluetooth.c2
-rw-r--r--hw/usb/dev-hid.c2
-rw-r--r--hw/usb/dev-hub.c2
-rw-r--r--hw/usb/dev-serial.c2
-rw-r--r--hw/usb/dev-smartcard-reader.c29
-rw-r--r--hw/usb/dev-storage.c71
-rw-r--r--hw/usb/dev-wacom.c2
-rw-r--r--hw/usb/hcd-ehci.c723
-rw-r--r--hw/usb/hcd-ohci.c93
-rw-r--r--hw/usb/hcd-uhci.c73
-rw-r--r--hw/usb/hcd-xhci.c251
-rw-r--r--hw/usb/host-linux.c94
-rw-r--r--hw/usb/libhw.c21
-rw-r--r--hw/usb/redirect.c10
18 files changed, 970 insertions, 498 deletions
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
new file mode 100644
index 0000000000..9c7ddf5cb2
--- /dev/null
+++ b/hw/usb/Makefile.objs
@@ -0,0 +1,13 @@
+hw-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
+hw-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
+hw-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o
+hw-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
+hw-obj-y += libhw.o
+
+hw-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o
+hw-obj-$(CONFIG_USB_REDIR) += redirect.o
+
+common-obj-y += core.o bus.o desc.o dev-hub.o
+common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
+common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
+common-obj-y += dev-serial.o dev-network.o dev-audio.o
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 2068640a58..b649360dd3 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -11,26 +11,49 @@ static char *usb_get_dev_path(DeviceState *dev);
 static char *usb_get_fw_dev_path(DeviceState *qdev);
 static int usb_qdev_exit(DeviceState *qdev);
 
-static struct BusInfo usb_bus_info = {
-    .name      = "USB",
-    .size      = sizeof(USBBus),
-    .print_dev = usb_bus_dev_print,
-    .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_BIT("full-path", USBDevice, flags,
-                        USB_DEV_FLAG_FULL_PATH, true),
-        DEFINE_PROP_END_OF_LIST()
-    },
+static Property usb_props[] = {
+    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()
 };
+
+static void usb_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->print_dev = usb_bus_dev_print;
+    k->get_dev_path = usb_get_dev_path;
+    k->get_fw_dev_path = usb_get_fw_dev_path;
+}
+
+static const TypeInfo usb_bus_info = {
+    .name = TYPE_USB_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(USBBus),
+    .class_init = usb_bus_class_init,
+};
+
 static int next_usb_bus = 0;
 static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
 
+static int usb_device_post_load(void *opaque, int version_id)
+{
+    USBDevice *dev = opaque;
+
+    if (dev->state == USB_STATE_NOTATTACHED) {
+        dev->attached = 0;
+    } else {
+        dev->attached = 1;
+    }
+    return 0;
+}
+
 const VMStateDescription vmstate_usb_device = {
     .name = "USBDevice",
     .version_id = 1,
     .minimum_version_id = 1,
+    .post_load = usb_device_post_load,
     .fields = (VMStateField []) {
         VMSTATE_UINT8(addr, USBDevice),
         VMSTATE_INT32(state, USBDevice),
@@ -45,7 +68,7 @@ const VMStateDescription vmstate_usb_device = {
 
 void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
 {
-    qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
+    qbus_create_inplace(&bus->qbus, TYPE_USB_BUS, host, NULL);
     bus->ops = ops;
     bus->busnr = next_usb_bus++;
     bus->qbus.allow_hotplug = 1; /* Yes, we can */
@@ -465,9 +488,8 @@ static char *usb_get_dev_path(DeviceState *qdev)
     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 (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) {
+        id = qdev_get_dev_path(hcd);
     }
     if (id) {
         char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
@@ -576,10 +598,11 @@ USBDevice *usbdevice_create(const char *cmdline)
 static void usb_device_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_info = &usb_bus_info;
+    k->bus_type = TYPE_USB_BUS;
     k->init     = usb_qdev_init;
     k->unplug   = qdev_simple_unplug_cb;
     k->exit     = usb_qdev_exit;
+    k->props    = usb_props;
 }
 
 static TypeInfo usb_device_type_info = {
@@ -593,6 +616,7 @@ static TypeInfo usb_device_type_info = {
 
 static void usb_register_types(void)
 {
+    type_register_static(&usb_bus_info);
     type_register_static(&usb_device_type_info);
 }
 
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 2641685a32..01a7622837 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -550,7 +550,7 @@ void usb_packet_cleanup(USBPacket *p)
     qemu_iovec_destroy(&p->iov);
 }
 
-void usb_ep_init(USBDevice *dev)
+void usb_ep_reset(USBDevice *dev)
 {
     int ep;
 
@@ -559,7 +559,6 @@ void usb_ep_init(USBDevice *dev)
     dev->ep_ctl.ifnum = 0;
     dev->ep_ctl.dev = dev;
     dev->ep_ctl.pipeline = false;
-    QTAILQ_INIT(&dev->ep_ctl.queue);
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
         dev->ep_in[ep].nr = ep + 1;
         dev->ep_out[ep].nr = ep + 1;
@@ -567,12 +566,22 @@ void usb_ep_init(USBDevice *dev)
         dev->ep_out[ep].pid = USB_TOKEN_OUT;
         dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
         dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
-        dev->ep_in[ep].ifnum = 0;
-        dev->ep_out[ep].ifnum = 0;
+        dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID;
+        dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID;
         dev->ep_in[ep].dev = dev;
         dev->ep_out[ep].dev = dev;
         dev->ep_in[ep].pipeline = false;
         dev->ep_out[ep].pipeline = false;
+    }
+}
+
+void usb_ep_init(USBDevice *dev)
+{
+    int ep;
+
+    usb_ep_reset(dev);
+    QTAILQ_INIT(&dev->ep_ctl.queue);
+    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
         QTAILQ_INIT(&dev->ep_in[ep].queue);
         QTAILQ_INIT(&dev->ep_out[ep].queue);
     }
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index e8a3c6af3d..0a9d3c9f60 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -432,12 +432,13 @@ void usb_desc_create_serial(USBDevice *dev)
     const USBDesc *desc = usb_device_get_usb_desc(dev);
     int index = desc->id.iSerialNumber;
     char serial[64];
+    char *path;
     int dst;
 
     assert(index != 0 && desc->str[index] != NULL);
     dst = snprintf(serial, sizeof(serial), "%s", desc->str[index]);
-    if (hcd && hcd->parent_bus && hcd->parent_bus->info->get_dev_path) {
-        char *path = hcd->parent_bus->info->get_dev_path(hcd);
+    path = qdev_get_dev_path(hcd);
+    if (path) {
         dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", path);
     }
     dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", dev->port->path);
diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c
index 6b74eff4ad..55bc19184b 100644
--- a/hw/usb/dev-bluetooth.c
+++ b/hw/usb/dev-bluetooth.c
@@ -57,7 +57,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER]     = "QEMU",
     [STR_SERIALNUMBER]     = "1",
 };
 
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index f29544d954..b3dcd23109 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -60,7 +60,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER]     = "QEMU",
     [STR_PRODUCT_MOUSE]    = "QEMU USB Mouse",
     [STR_PRODUCT_TABLET]   = "QEMU USB Tablet",
     [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index b5962da72a..8fd30df0e6 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -90,7 +90,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER] = "QEMU",
     [STR_PRODUCT]      = "QEMU USB Hub",
     [STR_SERIALNUMBER] = "314159",
 };
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 56743ee020..8aa655286b 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -111,7 +111,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]    = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER]    = "QEMU",
     [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
     [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
     [STR_SERIALNUMBER]    = "1",
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 3b7604e8b1..1ea079176a 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -81,7 +81,7 @@ do { \
 #define CCID_CONTROL_GET_DATA_RATES         0x3
 
 #define CCID_PRODUCT_DESCRIPTION        "QEMU USB CCID"
-#define CCID_VENDOR_DESCRIPTION         "QEMU " QEMU_VERSION
+#define CCID_VENDOR_DESCRIPTION         "QEMU"
 #define CCID_INTERFACE_NAME             "CCID Interface"
 #define CCID_SERIAL_NUMBER_STRING       "1"
 /*
@@ -401,7 +401,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]  = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER]  = "QEMU",
     [STR_PRODUCT]       = "QEMU USB CCID",
     [STR_SERIALNUMBER]  = "1",
     [STR_INTERFACE]     = "CCID Interface",
@@ -1055,13 +1055,18 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s)
         : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM];
 }
 
-static struct BusInfo ccid_bus_info = {
-    .name = "ccid-bus",
-    .size = sizeof(CCIDBus),
-    .props = (Property[]) {
-        DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
-        DEFINE_PROP_END_OF_LIST(),
-    }
+static Property ccid_props[] = {
+    DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+#define TYPE_CCID_BUS "ccid-bus"
+#define CCID_BUS(obj) OBJECT_CHECK(CCIDBus, (obj), TYPE_CCID_BUS)
+
+static const TypeInfo ccid_bus_info = {
+    .name = TYPE_CCID_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(CCIDBus),
 };
 
 void ccid_card_send_apdu_to_guest(CCIDCardState *card,
@@ -1191,7 +1196,7 @@ static int ccid_initfn(USBDevice *dev)
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
-    qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
+    qbus_create_inplace(&s->bus.qbus, TYPE_CCID_BUS, &dev->qdev, NULL);
     s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
     s->bus.qbus.allow_hotplug = 1;
     s->card = NULL;
@@ -1342,9 +1347,10 @@ static TypeInfo ccid_info = {
 static void ccid_card_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_info = &ccid_bus_info;
+    k->bus_type = TYPE_CCID_BUS;
     k->init = ccid_card_init;
     k->exit = ccid_card_exit;
+    k->props = ccid_props;
 }
 
 static TypeInfo ccid_card_type_info = {
@@ -1358,6 +1364,7 @@ static TypeInfo ccid_card_type_info = {
 
 static void ccid_register_types(void)
 {
+    type_register_static(&ccid_bus_info);
     type_register_static(&ccid_card_type_info);
     type_register_static(&ccid_info);
     usb_legacy_register(CCID_DEV_NAME, "ccid", NULL);
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index a96c0b9e5e..251e7de1cd 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -48,10 +48,9 @@ struct usb_msd_csw {
 typedef struct {
     USBDevice dev;
     enum USBMSDMode mode;
+    uint32_t scsi_off;
     uint32_t scsi_len;
-    uint8_t *scsi_buf;
     uint32_t data_len;
-    uint32_t residue;
     struct usb_msd_csw csw;
     SCSIRequest *req;
     SCSIBus bus;
@@ -82,7 +81,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER] = "QEMU",
     [STR_PRODUCT]      = "QEMU USB HARDDRIVE",
     [STR_SERIALNUMBER] = "1",
     [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
@@ -179,9 +178,9 @@ static void usb_msd_copy_data(MSDState *s, USBPacket *p)
     len = p->iov.size - p->result;
     if (len > s->scsi_len)
         len = s->scsi_len;
-    usb_packet_copy(p, s->scsi_buf, len);
+    usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len);
     s->scsi_len -= len;
-    s->scsi_buf += len;
+    s->scsi_off += len;
     s->data_len -= len;
     if (s->scsi_len == 0 || s->data_len == 0) {
         scsi_req_continue(s->req);
@@ -201,6 +200,18 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
     memset(&s->csw, 0, sizeof(s->csw));
 }
 
+static void usb_msd_packet_complete(MSDState *s)
+{
+    USBPacket *p = s->packet;
+
+    /* Set s->packet to NULL before calling usb_packet_complete
+       because another request may be issued before
+       usb_packet_complete returns.  */
+    DPRINTF("Packet complete %p\n", p);
+    s->packet = NULL;
+    usb_packet_complete(&s->dev, p);
+}
+
 static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
 {
     MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -208,17 +219,12 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
 
     assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
     s->scsi_len = len;
-    s->scsi_buf = scsi_req_get_buf(req);
+    s->scsi_off = 0;
     if (p) {
         usb_msd_copy_data(s, p);
         p = s->packet;
         if (p && p->result == p->iov.size) {
-            /* Set s->packet to NULL before calling usb_packet_complete
-               because another request may be issued before
-               usb_packet_complete returns.  */
-            DPRINTF("Packet complete %p\n", p);
-            s->packet = NULL;
-            usb_packet_complete(&s->dev, p);
+            usb_msd_packet_complete(s);
         }
     }
 }
@@ -229,11 +235,10 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
     USBPacket *p = s->packet;
 
     DPRINTF("Command complete %d tag 0x%x\n", status, req->tag);
-    s->residue = s->data_len;
 
     s->csw.sig = cpu_to_le32(0x53425355);
     s->csw.tag = cpu_to_le32(req->tag);
-    s->csw.residue = cpu_to_le32(s->residue);
+    s->csw.residue = cpu_to_le32(s->data_len);
     s->csw.status = status != 0;
 
     if (s->packet) {
@@ -252,8 +257,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
                 s->mode = USB_MSDM_CSW;
             }
         }
-        s->packet = NULL;
-        usb_packet_complete(&s->dev, p);
+        usb_msd_packet_complete(s);
     } else if (s->data_len == 0) {
         s->mode = USB_MSDM_CSW;
     }
@@ -283,10 +287,8 @@ static void usb_msd_handle_reset(USBDevice *dev)
     assert(s->req == NULL);
 
     if (s->packet) {
-        USBPacket *p = s->packet;
-        s->packet = NULL;
-        p->result = USB_RET_STALL;
-        usb_packet_complete(dev, p);
+        s->packet->result = USB_RET_STALL;
+        usb_msd_packet_complete(s);
     }
 
     s->mode = USB_MSDM_CBW;
@@ -378,7 +380,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             }
             DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
                     tag, cbw.flags, cbw.cmd_len, s->data_len);
-            s->residue = 0;
+            assert(le32_to_cpu(s->csw.residue) == 0);
             s->scsi_len = 0;
             s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
             scsi_req_enqueue(s->req);
@@ -397,7 +399,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             if (s->scsi_len) {
                 usb_msd_copy_data(s, p);
             }
-            if (s->residue) {
+            if (le32_to_cpu(s->csw.residue)) {
                 int len = p->iov.size - p->result;
                 if (len) {
                     usb_packet_skip(p, len);
@@ -458,7 +460,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             if (s->scsi_len) {
                 usb_msd_copy_data(s, p);
             }
-            if (s->residue) {
+            if (le32_to_cpu(s->csw.residue)) {
                 int len = p->iov.size - p->result;
                 if (len) {
                     usb_packet_skip(p, len);
@@ -504,6 +506,17 @@ static void usb_msd_password_cb(void *opaque, int err)
         qdev_unplug(&s->dev.qdev, NULL);
 }
 
+static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
+{
+    MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+
+    /* nothing to load, just store req in our state struct */
+    assert(s->req == NULL);
+    scsi_req_ref(req);
+    s->req = req;
+    return NULL;
+}
+
 static const struct SCSIBusInfo usb_msd_scsi_info = {
     .tcq = false,
     .max_target = 0,
@@ -511,7 +524,8 @@ static const struct SCSIBusInfo usb_msd_scsi_info = {
 
     .transfer_data = usb_msd_transfer_data,
     .complete = usb_msd_command_complete,
-    .cancel = usb_msd_request_cancelled
+    .cancel = usb_msd_request_cancelled,
+    .load_request = usb_msd_load_request,
 };
 
 static int usb_msd_initfn(USBDevice *dev)
@@ -631,11 +645,18 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
 
 static const VMStateDescription vmstate_usb_msd = {
     .name = "usb-storage",
-    .unmigratable = 1, /* FIXME: handle transactions which are in flight */
     .version_id = 1,
     .minimum_version_id = 1,
     .fields = (VMStateField []) {
         VMSTATE_USB_DEVICE(dev, MSDState),
+        VMSTATE_UINT32(mode, MSDState),
+        VMSTATE_UINT32(scsi_len, MSDState),
+        VMSTATE_UINT32(scsi_off, MSDState),
+        VMSTATE_UINT32(data_len, MSDState),
+        VMSTATE_UINT32(csw.sig, MSDState),
+        VMSTATE_UINT32(csw.tag, MSDState),
+        VMSTATE_UINT32(csw.residue, MSDState),
+        VMSTATE_UINT8(csw.status, MSDState),
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c
index 3b51d458f4..ed9a5ee358 100644
--- a/hw/usb/dev-wacom.c
+++ b/hw/usb/dev-wacom.c
@@ -62,7 +62,7 @@ enum {
 };
 
 static const USBDescStrings desc_strings = {
-    [STR_MANUFACTURER]     = "QEMU " QEMU_VERSION,
+    [STR_MANUFACTURER]     = "QEMU",
     [STR_PRODUCT]          = "Wacom PenPartner",
     [STR_SERIALNUMBER]     = "1",
 };
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index e759c996ce..080f62c00d 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -334,6 +334,7 @@ typedef struct EHCIfstn {
     uint32_t backptr;                 // Standard next link pointer
 } EHCIfstn;
 
+typedef struct EHCIPacket EHCIPacket;
 typedef struct EHCIQueue EHCIQueue;
 typedef struct EHCIState EHCIState;
 
@@ -343,26 +344,37 @@ enum async_state {
     EHCI_ASYNC_FINISHED,
 };
 
+struct EHCIPacket {
+    EHCIQueue *queue;
+    QTAILQ_ENTRY(EHCIPacket) next;
+
+    EHCIqtd qtd;           /* copy of current QTD (being worked on) */
+    uint32_t qtdaddr;      /* address QTD read from                 */
+
+    USBPacket packet;
+    QEMUSGList sgl;
+    int pid;
+    uint32_t tbytes;
+    enum async_state async;
+    int usb_status;
+};
+
 struct EHCIQueue {
     EHCIState *ehci;
     QTAILQ_ENTRY(EHCIQueue) next;
     uint32_t seen;
     uint64_t ts;
+    int async;
+    int revalidate;
 
     /* cached data from guest - needs to be flushed
      * when guest removes an entry (doorbell, handshake sequence)
      */
-    EHCIqh qh;             // copy of current QH (being worked on)
-    uint32_t qhaddr;       // address QH read from
-    EHCIqtd qtd;           // copy of current QTD (being worked on)
-    uint32_t qtdaddr;      // address QTD read from
-
-    USBPacket packet;
-    QEMUSGList sgl;
-    int pid;
-    uint32_t tbytes;
-    enum async_state async;
-    int usb_status;
+    EHCIqh qh;             /* copy of current QH (being worked on) */
+    uint32_t qhaddr;       /* address QH read from                 */
+    uint32_t qtdaddr;      /* address QTD read from                */
+    USBDevice *dev;
+    QTAILQ_HEAD(, EHCIPacket) packets;
 };
 
 typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
@@ -375,7 +387,6 @@ struct EHCIState {
     int companion_count;
 
     /* properties */
-    uint32_t freq;
     uint32_t maxframes;
 
     /*
@@ -403,22 +414,24 @@ struct EHCIState {
      *  Internal states, shadow registers, etc
      */
     QEMUTimer *frame_timer;
-    int attach_poll_counter;
-    int astate;                        // Current state in asynchronous schedule
-    int pstate;                        // Current state in periodic schedule
+    QEMUBH *async_bh;
+    uint32_t astate;         /* Current state in asynchronous schedule */
+    uint32_t pstate;         /* Current state in periodic schedule     */
     USBPort ports[NB_PORTS];
     USBPort *companion_ports[NB_PORTS];
     uint32_t usbsts_pending;
     EHCIQueueHead aqueues;
     EHCIQueueHead pqueues;
 
-    uint32_t a_fetch_addr;   // which address to look at next
-    uint32_t p_fetch_addr;   // which address to look at next
+    /* which address to look at next */
+    uint32_t a_fetch_addr;
+    uint32_t p_fetch_addr;
 
     USBPacket ipacket;
     QEMUSGList isgl;
 
     uint64_t last_run_ns;
+    uint32_t async_stepdown;
 };
 
 #define SET_LAST_RUN_CLOCK(s) \
@@ -557,6 +570,7 @@ static inline void ehci_set_interrupt(EHCIState *s, int intr)
         level = 1;
     }
 
+    trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr);
     qemu_set_irq(s->irq, level);
 }
 
@@ -574,14 +588,37 @@ static inline void ehci_commit_interrupt(EHCIState *s)
     s->usbsts_pending = 0;
 }
 
+static void ehci_update_halt(EHCIState *s)
+{
+    if (s->usbcmd & USBCMD_RUNSTOP) {
+        ehci_clear_usbsts(s, USBSTS_HALT);
+    } else {
+        if (s->astate == EST_INACTIVE && s->pstate == EST_INACTIVE) {
+            ehci_set_usbsts(s, USBSTS_HALT);
+        }
+    }
+}
+
 static void ehci_set_state(EHCIState *s, int async, int state)
 {
     if (async) {
         trace_usb_ehci_state("async", state2str(state));
         s->astate = state;
+        if (s->astate == EST_INACTIVE) {
+            ehci_clear_usbsts(s, USBSTS_ASS);
+            ehci_update_halt(s);
+        } else {
+            ehci_set_usbsts(s, USBSTS_ASS);
+        }
     } else {
         trace_usb_ehci_state("periodic", state2str(state));
         s->pstate = state;
+        if (s->pstate == EST_INACTIVE) {
+            ehci_clear_usbsts(s, USBSTS_PSS);
+            ehci_update_halt(s);
+        } else {
+            ehci_set_usbsts(s, USBSTS_PSS);
+        }
     }
 }
 
@@ -655,27 +692,71 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
                         (bool)(sitd->results & SITD_RESULTS_ACTIVE));
 }
 
+static inline bool ehci_enabled(EHCIState *s)
+{
+    return s->usbcmd & USBCMD_RUNSTOP;
+}
+
+static inline bool ehci_async_enabled(EHCIState *s)
+{
+    return ehci_enabled(s) && (s->usbcmd & USBCMD_ASE);
+}
+
+static inline bool ehci_periodic_enabled(EHCIState *s)
+{
+    return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE);
+}
+
+/* packet management */
+
+static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
+{
+    EHCIPacket *p;
+
+    p = g_new0(EHCIPacket, 1);
+    p->queue = q;
+    usb_packet_init(&p->packet);
+    QTAILQ_INSERT_TAIL(&q->packets, p, next);
+    trace_usb_ehci_packet_action(p->queue, p, "alloc");
+    return p;
+}
+
+static void ehci_free_packet(EHCIPacket *p)
+{
+    trace_usb_ehci_packet_action(p->queue, p, "free");
+    if (p->async == EHCI_ASYNC_INFLIGHT) {
+        usb_cancel_packet(&p->packet);
+    }
+    QTAILQ_REMOVE(&p->queue->packets, p, next);
+    usb_packet_cleanup(&p->packet);
+    g_free(p);
+}
+
 /* queue management */
 
-static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
+static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
 {
     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
     EHCIQueue *q;
 
     q = g_malloc0(sizeof(*q));
     q->ehci = ehci;
-    usb_packet_init(&q->packet);
+    q->qhaddr = addr;
+    q->async = async;
+    QTAILQ_INIT(&q->packets);
     QTAILQ_INSERT_HEAD(head, q, next);
     trace_usb_ehci_queue_action(q, "alloc");
     return q;
 }
 
-static void ehci_free_queue(EHCIQueue *q, int async)
+static void ehci_free_queue(EHCIQueue *q)
 {
-    EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues;
+    EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
+    EHCIPacket *p;
+
     trace_usb_ehci_queue_action(q, "free");
-    if (q->async == EHCI_ASYNC_INFLIGHT) {
-        usb_cancel_packet(&q->packet);
+    while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
+        ehci_free_packet(p);
     }
     QTAILQ_REMOVE(head, q, next);
     g_free(q);
@@ -695,9 +776,21 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
     return NULL;
 }
 
-static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+static void ehci_queues_tag_unused_async(EHCIState *ehci)
+{
+    EHCIQueue *q;
+
+    QTAILQ_FOREACH(q, &ehci->aqueues, next) {
+        if (!q->seen) {
+            q->revalidate = 1;
+        }
+    }
+}
+
+static void ehci_queues_rip_unused(EHCIState *ehci, int async)
 {
     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+    uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
     EHCIQueue *q, *tmp;
 
     QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
@@ -706,11 +799,10 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
             q->ts = ehci->last_run_ns;
             continue;
         }
-        if (!flush && ehci->last_run_ns < q->ts + 250000000) {
-            /* allow 0.25 sec idle */
+        if (ehci->last_run_ns < q->ts + maxage) {
             continue;
         }
-        ehci_free_queue(q, async);
+        ehci_free_queue(q);
     }
 }
 
@@ -720,11 +812,10 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
     EHCIQueue *q, *tmp;
 
     QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
-        if (!usb_packet_is_inflight(&q->packet) ||
-            q->packet.ep->dev != dev) {
+        if (q->dev != dev) {
             continue;
         }
-        ehci_free_queue(q, async);
+        ehci_free_queue(q);
     }
 }
 
@@ -734,7 +825,7 @@ static void ehci_queues_rip_all(EHCIState *ehci, int async)
     EHCIQueue *q, *tmp;
 
     QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
-        ehci_free_queue(q, async);
+        ehci_free_queue(q);
     }
 }
 
@@ -744,8 +835,9 @@ static void ehci_attach(USBPort *port)
 {
     EHCIState *s = port->opaque;
     uint32_t *portsc = &s->portsc[port->index];
+    const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci";
 
-    trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
+    trace_usb_ehci_port_attach(port->index, owner, port->dev->product_desc);
 
     if (*portsc & PORTSC_POWNER) {
         USBPort *companion = s->companion_ports[port->index];
@@ -764,8 +856,9 @@ static void ehci_detach(USBPort *port)
 {
     EHCIState *s = port->opaque;
     uint32_t *portsc = &s->portsc[port->index];
+    const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci";
 
-    trace_usb_ehci_port_detach(port->index);
+    trace_usb_ehci_port_detach(port->index, owner);
 
     if (*portsc & PORTSC_POWNER) {
         USBPort *companion = s->companion_ports[port->index];
@@ -813,7 +906,10 @@ static void ehci_wakeup(USBPort *port)
         if (companion->ops->wakeup) {
             companion->ops->wakeup(companion);
         }
+        return;
     }
+
+    qemu_bh_schedule(s->async_bh);
 }
 
 static int ehci_register_companion(USBBus *bus, USBPort *ports[],
@@ -904,7 +1000,6 @@ static void ehci_reset(void *opaque)
 
     s->astate = EST_INACTIVE;
     s->pstate = EST_INACTIVE;
-    s->attach_poll_counter = 0;
 
     for(i = 0; i < NB_PORTS; i++) {
         if (s->companion_ports[i]) {
@@ -920,6 +1015,7 @@ static void ehci_reset(void *opaque)
     ehci_queues_rip_all(s, 0);
     ehci_queues_rip_all(s, 1);
     qemu_del_timer(s->frame_timer);
+    qemu_bh_cancel(s->async_bh);
 }
 
 static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
@@ -1064,22 +1160,20 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
     /* Do any register specific pre-write processing here.  */
     switch(addr) {
     case USBCMD:
-        if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
-            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
-            SET_LAST_RUN_CLOCK(s);
-            ehci_clear_usbsts(s, USBSTS_HALT);
-        }
-
-        if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
-            qemu_del_timer(s->frame_timer);
-            ehci_queues_rip_all(s, 0);
-            ehci_queues_rip_all(s, 1);
-            ehci_set_usbsts(s, USBSTS_HALT);
-        }
-
         if (val & USBCMD_HCRESET) {
             ehci_reset(s);
             val = s->usbcmd;
+            break;
+        }
+
+        if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
+            ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
+            if (s->pstate == EST_INACTIVE) {
+                SET_LAST_RUN_CLOCK(s);
+            }
+            ehci_update_halt(s);
+            s->async_stepdown = 0;
+            qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
         }
 
         /* not supporting dynamic frame list size at the moment */
@@ -1114,7 +1208,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
         break;
 
     case PERIODICLISTBASE:
-        if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+        if (ehci_periodic_enabled(s)) {
             fprintf(stderr,
               "ehci: PERIODIC list base register set while periodic schedule\n"
               "      is enabled and HC is enabled\n");
@@ -1122,7 +1216,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
         break;
 
     case ASYNCLISTADDR:
-        if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+        if (ehci_async_enabled(s)) {
             fprintf(stderr,
               "ehci: ASYNC list address register set while async schedule\n"
               "      is enabled and HC is enabled\n");
@@ -1165,25 +1259,46 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr,
     return 1;
 }
 
+/*
+ *  Write the qh back to guest physical memory.  This step isn't
+ *  in the EHCI spec but we need to do it since we don't share
+ *  physical memory with our guest VM.
+ *
+ *  The first three dwords are read-only for the EHCI, so skip them
+ *  when writing back the qh.
+ */
+static void ehci_flush_qh(EHCIQueue *q)
+{
+    uint32_t *qh = (uint32_t *) &q->qh;
+    uint32_t dwords = sizeof(EHCIqh) >> 2;
+    uint32_t addr = NLPTR_GET(q->qhaddr);
+
+    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
+}
+
 // 4.10.2
 
 static int ehci_qh_do_overlay(EHCIQueue *q)
 {
+    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
     int i;
     int dtoggle;
     int ping;
     int eps;
     int reload;
 
+    assert(p != NULL);
+    assert(p->qtdaddr == q->qtdaddr);
+
     // remember values in fields to preserve in qh after overlay
 
     dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE;
     ping    = q->qh.token & QTD_TOKEN_PING;
 
-    q->qh.current_qtd = q->qtdaddr;
-    q->qh.next_qtd    = q->qtd.next;
-    q->qh.altnext_qtd = q->qtd.altnext;
-    q->qh.token       = q->qtd.token;
+    q->qh.current_qtd = p->qtdaddr;
+    q->qh.next_qtd    = p->qtd.next;
+    q->qh.altnext_qtd = p->qtd.altnext;
+    q->qh.token       = p->qtd.token;
 
 
     eps = get_field(q->qh.epchar, QH_EPCHAR_EPS);
@@ -1196,7 +1311,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
     set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
 
     for (i = 0; i < 5; i++) {
-        q->qh.bufptr[i] = q->qtd.bufptr[i];
+        q->qh.bufptr[i] = p->qtd.bufptr[i];
     }
 
     if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
@@ -1208,21 +1323,20 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
     q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
     q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
 
-    put_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh,
-               sizeof(EHCIqh) >> 2);
+    ehci_flush_qh(q);
 
     return 0;
 }
 
-static int ehci_init_transfer(EHCIQueue *q)
+static int ehci_init_transfer(EHCIPacket *p)
 {
     uint32_t cpage, offset, bytes, plen;
     dma_addr_t page;
 
-    cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
-    bytes  = get_field(q->qh.token, QTD_TOKEN_TBYTES);
-    offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
-    pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5);
+    cpage  = get_field(p->qtd.token, QTD_TOKEN_CPAGE);
+    bytes  = get_field(p->qtd.token, QTD_TOKEN_TBYTES);
+    offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK;
+    pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5);
 
     while (bytes > 0) {
         if (cpage > 4) {
@@ -1230,7 +1344,7 @@ static int ehci_init_transfer(EHCIQueue *q)
             return USB_RET_PROCERR;
         }
 
-        page  = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
+        page  = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
         page += offset;
         plen  = bytes;
         if (plen > 4096 - offset) {
@@ -1239,7 +1353,7 @@ static int ehci_init_transfer(EHCIQueue *q)
             cpage++;
         }
 
-        qemu_sglist_add(&q->sgl, page, plen);
+        qemu_sglist_add(&p->sgl, page, plen);
         bytes -= plen;
     }
     return 0;
@@ -1249,8 +1363,6 @@ static void ehci_finish_transfer(EHCIQueue *q, int status)
 {
     uint32_t cpage, offset;
 
-    qemu_sglist_destroy(&q->sgl);
-
     if (status > 0) {
         /* update cpage & offset */
         cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
@@ -1268,7 +1380,7 @@ static void ehci_finish_transfer(EHCIQueue *q, int status)
 
 static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 {
-    EHCIQueue *q;
+    EHCIPacket *p;
     EHCIState *s = port->opaque;
     uint32_t portsc = s->portsc[port->index];
 
@@ -1278,23 +1390,31 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
         return;
     }
 
-    q = container_of(packet, EHCIQueue, packet);
-    trace_usb_ehci_queue_action(q, "wakeup");
-    assert(q->async == EHCI_ASYNC_INFLIGHT);
-    q->async = EHCI_ASYNC_FINISHED;
-    q->usb_status = packet->result;
+    p = container_of(packet, EHCIPacket, packet);
+    trace_usb_ehci_packet_action(p->queue, p, "wakeup");
+    assert(p->async == EHCI_ASYNC_INFLIGHT);
+    p->async = EHCI_ASYNC_FINISHED;
+    p->usb_status = packet->result;
+
+    if (p->queue->async) {
+        qemu_bh_schedule(p->queue->ehci->async_bh);
+    }
 }
 
 static void ehci_execute_complete(EHCIQueue *q)
 {
-    assert(q->async != EHCI_ASYNC_INFLIGHT);
-    q->async = EHCI_ASYNC_NONE;
+    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
+
+    assert(p != NULL);
+    assert(p->qtdaddr == q->qtdaddr);
+    assert(p->async != EHCI_ASYNC_INFLIGHT);
+    p->async = EHCI_ASYNC_NONE;
 
     DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
             q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
 
-    if (q->usb_status < 0) {
-        switch(q->usb_status) {
+    if (p->usb_status < 0) {
+        switch (p->usb_status) {
         case USB_RET_IOERROR:
         case USB_RET_NODEV:
             q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
@@ -1314,28 +1434,29 @@ static void ehci_execute_complete(EHCIQueue *q)
             break;
         default:
             /* should not be triggerable */
-            fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status);
+            fprintf(stderr, "USB invalid response %d\n", p->usb_status);
             assert(0);
             break;
         }
-    } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
-        q->usb_status = USB_RET_BABBLE;
+    } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
+        p->usb_status = USB_RET_BABBLE;
         q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
         ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
     } else {
         // TODO check 4.12 for splits
 
-        if (q->tbytes && q->pid == USB_TOKEN_IN) {
-            q->tbytes -= q->usb_status;
+        if (p->tbytes && p->pid == USB_TOKEN_IN) {
+            p->tbytes -= p->usb_status;
         } else {
-            q->tbytes = 0;
+            p->tbytes = 0;
         }
 
-        DPRINTF("updating tbytes to %d\n", q->tbytes);
-        set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
+        DPRINTF("updating tbytes to %d\n", p->tbytes);
+        set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES);
     }
-    ehci_finish_transfer(q, q->usb_status);
-    usb_packet_unmap(&q->packet);
+    ehci_finish_transfer(q, p->usb_status);
+    usb_packet_unmap(&p->packet, &p->sgl);
+    qemu_sglist_destroy(&p->sgl);
 
     q->qh.token ^= QTD_TOKEN_DTOGGLE;
     q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1347,48 +1468,51 @@ static void ehci_execute_complete(EHCIQueue *q)
 
 // 4.10.3
 
-static int ehci_execute(EHCIQueue *q)
+static int ehci_execute(EHCIPacket *p, const char *action)
 {
-    USBDevice *dev;
     USBEndpoint *ep;
     int ret;
     int endp;
-    int devadr;
 
-    if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) {
-        fprintf(stderr, "Attempting to execute inactive QH\n");
+    if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
+        fprintf(stderr, "Attempting to execute inactive qtd\n");
         return USB_RET_PROCERR;
     }
 
-    q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
-    if (q->tbytes > BUFF_SIZE) {
+    p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+    if (p->tbytes > BUFF_SIZE) {
         fprintf(stderr, "Request for more bytes than allowed\n");
         return USB_RET_PROCERR;
     }
 
-    q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
-    switch(q->pid) {
-        case 0: q->pid = USB_TOKEN_OUT; break;
-        case 1: q->pid = USB_TOKEN_IN; break;
-        case 2: q->pid = USB_TOKEN_SETUP; break;
-        default: fprintf(stderr, "bad token\n"); break;
+    p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+    switch (p->pid) {
+    case 0:
+        p->pid = USB_TOKEN_OUT;
+        break;
+    case 1:
+        p->pid = USB_TOKEN_IN;
+        break;
+    case 2:
+        p->pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        fprintf(stderr, "bad token\n");
+        break;
     }
 
-    if (ehci_init_transfer(q) != 0) {
+    if (ehci_init_transfer(p) != 0) {
         return USB_RET_PROCERR;
     }
 
-    endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
-    devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
-
-    /* TODO: associating device with ehci port */
-    dev = ehci_find_device(q->ehci, devadr);
-    ep = usb_ep_get(dev, q->pid, endp);
+    endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
+    ep = usb_ep_get(p->queue->dev, p->pid, endp);
 
-    usb_packet_setup(&q->packet, q->pid, ep);
-    usb_packet_map(&q->packet, &q->sgl);
+    usb_packet_setup(&p->packet, p->pid, ep);
+    usb_packet_map(&p->packet, &p->sgl);
 
-    ret = usb_handle_packet(dev, &q->packet);
+    trace_usb_ehci_packet_action(p->queue, p, action);
+    ret = usb_handle_packet(p->queue->dev, &p->packet);
     DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
             "(total %d) endp %x ret %d\n",
             q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
@@ -1456,7 +1580,7 @@ static int ehci_process_itd(EHCIState *ehci,
                 usb_packet_map(&ehci->ipacket, &ehci->isgl);
                 ret = usb_handle_packet(dev, &ehci->ipacket);
                 assert(ret != USB_RET_ASYNC);
-                usb_packet_unmap(&ehci->ipacket);
+                usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
             } else {
                 DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
                 ret = USB_RET_NAK;
@@ -1504,6 +1628,7 @@ static int ehci_process_itd(EHCIState *ehci,
     return 0;
 }
 
+
 /*  This state is the entry point for asynchronous schedule
  *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
  */
@@ -1519,7 +1644,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
         ehci_set_usbsts(ehci, USBSTS_REC);
     }
 
-    ehci_queues_rip_unused(ehci, async, 0);
+    ehci_queues_rip_unused(ehci, async);
 
     /*  Find the head of the list (4.9.1.1) */
     for(i = 0; i < MAX_QH; i++) {
@@ -1601,17 +1726,19 @@ out:
 
 static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
 {
-    uint32_t entry;
+    EHCIPacket *p;
+    uint32_t entry, devaddr;
     EHCIQueue *q;
+    EHCIqh qh;
 
     entry = ehci_get_fetch_addr(ehci, async);
     q = ehci_find_queue_by_qh(ehci, entry, async);
     if (NULL == q) {
-        q = ehci_alloc_queue(ehci, async);
+        q = ehci_alloc_queue(ehci, entry, async);
     }
-    q->qhaddr = entry;
-    q->seen++;
+    p = QTAILQ_FIRST(&q->packets);
 
+    q->seen++;
     if (q->seen > 1) {
         /* we are going in circles -- stop processing */
         ehci_set_state(ehci, async, EST_ACTIVE);
@@ -1620,17 +1747,41 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
     }
 
     get_dwords(ehci, NLPTR_GET(q->qhaddr),
-               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
+               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
+    if (q->revalidate && (q->qh.epchar      != qh.epchar ||
+                          q->qh.epcap       != qh.epcap  ||
+                          q->qh.current_qtd != qh.current_qtd)) {
+        ehci_free_queue(q);
+        q = ehci_alloc_queue(ehci, entry, async);
+        q->seen++;
+        p = NULL;
+    }
+    q->qh = qh;
+    q->revalidate = 0;
     ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
 
-    if (q->async == EHCI_ASYNC_INFLIGHT) {
+    devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+    if (q->dev != NULL && q->dev->addr != devaddr) {
+        if (!QTAILQ_EMPTY(&q->packets)) {
+            /* should not happen (guest bug) */
+            while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
+                ehci_free_packet(p);
+            }
+        }
+        q->dev = NULL;
+    }
+    if (q->dev == NULL) {
+        q->dev = ehci_find_device(q->ehci, devaddr);
+    }
+
+    if (p && p->async == EHCI_ASYNC_INFLIGHT) {
         /* I/O still in progress -- skip queue */
         ehci_set_state(ehci, async, EST_HORIZONTALQH);
         goto out;
     }
-    if (q->async == EHCI_ASYNC_FINISHED) {
+    if (p && p->async == EHCI_ASYNC_FINISHED) {
         /* I/O finished -- continue processing queue */
-        trace_usb_ehci_queue_action(q, "resume");
+        trace_usb_ehci_packet_action(p->queue, p, "complete");
         ehci_set_state(ehci, async, EST_EXECUTING);
         goto out;
     }
@@ -1726,7 +1877,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async)
 }
 
 /* Section 4.10.2 - paragraph 3 */
-static int ehci_state_advqueue(EHCIQueue *q, int async)
+static int ehci_state_advqueue(EHCIQueue *q)
 {
 #if 0
     /* TO-DO: 4.10.2 - paragraph 2
@@ -1745,81 +1896,117 @@ static int ehci_state_advqueue(EHCIQueue *q, int async)
     if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
         (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
         q->qtdaddr = q->qh.altnext_qtd;
-        ehci_set_state(q->ehci, async, EST_FETCHQTD);
+        ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
 
     /*
      *  next qTD is valid
      */
     } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
         q->qtdaddr = q->qh.next_qtd;
-        ehci_set_state(q->ehci, async, EST_FETCHQTD);
+        ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
 
     /*
      *  no valid qTD, try next QH
      */
     } else {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
     }
 
     return 1;
 }
 
 /* Section 4.10.2 - paragraph 4 */
-static int ehci_state_fetchqtd(EHCIQueue *q, int async)
+static int ehci_state_fetchqtd(EHCIQueue *q)
 {
+    EHCIqtd qtd;
+    EHCIPacket *p;
     int again = 0;
 
-    get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd,
+    get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
                sizeof(EHCIqtd) >> 2);
-    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd);
+    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
 
-    if (q->qtd.token & QTD_TOKEN_ACTIVE) {
-        ehci_set_state(q->ehci, async, EST_EXECUTE);
+    p = QTAILQ_FIRST(&q->packets);
+    while (p != NULL && p->qtdaddr != q->qtdaddr) {
+        /* should not happen (guest bug) */
+        ehci_free_packet(p);
+        p = QTAILQ_FIRST(&q->packets);
+    }
+    if (p != NULL) {
+        ehci_qh_do_overlay(q);
+        ehci_flush_qh(q);
+        if (p->async == EHCI_ASYNC_INFLIGHT) {
+            ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+        } else {
+            ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+        }
+        again = 1;
+    } else if (qtd.token & QTD_TOKEN_ACTIVE) {
+        p = ehci_alloc_packet(q);
+        p->qtdaddr = q->qtdaddr;
+        p->qtd = qtd;
+        ehci_set_state(q->ehci, q->async, EST_EXECUTE);
         again = 1;
     } else {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
         again = 1;
     }
 
     return again;
 }
 
-static int ehci_state_horizqh(EHCIQueue *q, int async)
+static int ehci_state_horizqh(EHCIQueue *q)
 {
     int again = 0;
 
-    if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) {
-        ehci_set_fetch_addr(q->ehci, async, q->qh.next);
-        ehci_set_state(q->ehci, async, EST_FETCHENTRY);
+    if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) {
+        ehci_set_fetch_addr(q->ehci, q->async, q->qh.next);
+        ehci_set_state(q->ehci, q->async, EST_FETCHENTRY);
         again = 1;
     } else {
-        ehci_set_state(q->ehci, async, EST_ACTIVE);
+        ehci_set_state(q->ehci, q->async, EST_ACTIVE);
     }
 
     return again;
 }
 
-/*
- *  Write the qh back to guest physical memory.  This step isn't
- *  in the EHCI spec but we need to do it since we don't share
- *  physical memory with our guest VM.
- *
- *  The first three dwords are read-only for the EHCI, so skip them
- *  when writing back the qh.
- */
-static void ehci_flush_qh(EHCIQueue *q)
+static void ehci_fill_queue(EHCIPacket *p)
 {
-    uint32_t *qh = (uint32_t *) &q->qh;
-    uint32_t dwords = sizeof(EHCIqh) >> 2;
-    uint32_t addr = NLPTR_GET(q->qhaddr);
+    EHCIQueue *q = p->queue;
+    EHCIqtd qtd = p->qtd;
+    uint32_t qtdaddr;
 
-    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
+    for (;;) {
+        if (NLPTR_TBIT(qtd.altnext) == 0) {
+            break;
+        }
+        if (NLPTR_TBIT(qtd.next) != 0) {
+            break;
+        }
+        qtdaddr = qtd.next;
+        get_dwords(q->ehci, NLPTR_GET(qtdaddr),
+                   (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
+        ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
+        if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
+            break;
+        }
+        p = ehci_alloc_packet(q);
+        p->qtdaddr = qtdaddr;
+        p->qtd = qtd;
+        p->usb_status = ehci_execute(p, "queue");
+        assert(p->usb_status = USB_RET_ASYNC);
+        p->async = EHCI_ASYNC_INFLIGHT;
+    }
 }
 
-static int ehci_state_execute(EHCIQueue *q, int async)
+static int ehci_state_execute(EHCIQueue *q)
 {
+    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
     int again = 0;
 
+    assert(p != NULL);
+    assert(p->qtdaddr == q->qtdaddr);
+
     if (ehci_qh_do_overlay(q) != 0) {
         return -1;
     }
@@ -1828,55 +2015,60 @@ static int ehci_state_execute(EHCIQueue *q, int async)
     // TODO write back ptr to async list when done or out of time
     // TODO Windows does not seem to ever set the MULT field
 
-    if (!async) {
+    if (!q->async) {
         int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
         if (!transactCtr) {
-            ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+            ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
             again = 1;
             goto out;
         }
     }
 
-    if (async) {
+    if (q->async) {
         ehci_set_usbsts(q->ehci, USBSTS_REC);
     }
 
-    q->usb_status = ehci_execute(q);
-    if (q->usb_status == USB_RET_PROCERR) {
+    p->usb_status = ehci_execute(p, "process");
+    if (p->usb_status == USB_RET_PROCERR) {
         again = -1;
         goto out;
     }
-    if (q->usb_status == USB_RET_ASYNC) {
+    if (p->usb_status == USB_RET_ASYNC) {
         ehci_flush_qh(q);
-        trace_usb_ehci_queue_action(q, "suspend");
-        q->async = EHCI_ASYNC_INFLIGHT;
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        trace_usb_ehci_packet_action(p->queue, p, "async");
+        p->async = EHCI_ASYNC_INFLIGHT;
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
         again = 1;
+        ehci_fill_queue(p);
         goto out;
     }
 
-    ehci_set_state(q->ehci, async, EST_EXECUTING);
+    ehci_set_state(q->ehci, q->async, EST_EXECUTING);
     again = 1;
 
 out:
     return again;
 }
 
-static int ehci_state_executing(EHCIQueue *q, int async)
+static int ehci_state_executing(EHCIQueue *q)
 {
+    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
     int again = 0;
 
+    assert(p != NULL);
+    assert(p->qtdaddr == q->qtdaddr);
+
     ehci_execute_complete(q);
-    if (q->usb_status == USB_RET_ASYNC) {
+    if (p->usb_status == USB_RET_ASYNC) {
         goto out;
     }
-    if (q->usb_status == USB_RET_PROCERR) {
+    if (p->usb_status == USB_RET_PROCERR) {
         again = -1;
         goto out;
     }
 
     // 4.10.3
-    if (!async) {
+    if (!q->async) {
         int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
         transactCtr--;
         set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
@@ -1885,10 +2077,10 @@ static int ehci_state_executing(EHCIQueue *q, int async)
     }
 
     /* 4.10.5 */
-    if (q->usb_status == USB_RET_NAK) {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+    if (p->usb_status == USB_RET_NAK) {
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
     } else {
-        ehci_set_state(q->ehci, async, EST_WRITEBACK);
+        ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
     }
 
     again = 1;
@@ -1899,14 +2091,21 @@ out:
 }
 
 
-static int ehci_state_writeback(EHCIQueue *q, int async)
+static int ehci_state_writeback(EHCIQueue *q)
 {
+    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
+    uint32_t *qtd, addr;
     int again = 0;
 
     /*  Write back the QTD from the QH area */
-    ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd);
-    put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd,
-               sizeof(EHCIqtd) >> 2);
+    assert(p != NULL);
+    assert(p->qtdaddr == q->qtdaddr);
+
+    ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd);
+    qtd = (uint32_t *) &q->qh.next_qtd;
+    addr = NLPTR_GET(p->qtdaddr);
+    put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2);
+    ehci_free_packet(p);
 
     /*
      * EHCI specs say go horizontal here.
@@ -1917,10 +2116,10 @@ static int ehci_state_writeback(EHCIQueue *q, int async)
      * bit is clear.
      */
     if (q->qh.token & QTD_TOKEN_HALT) {
-        ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
         again = 1;
     } else {
-        ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE);
+        ehci_set_state(q->ehci, q->async, EST_ADVANCEQUEUE);
         again = 1;
     }
     return again;
@@ -1930,8 +2129,7 @@ static int ehci_state_writeback(EHCIQueue *q, int async)
  * This is the state machine that is common to both async and periodic
  */
 
-static void ehci_advance_state(EHCIState *ehci,
-                               int async)
+static void ehci_advance_state(EHCIState *ehci, int async)
 {
     EHCIQueue *q = NULL;
     int again;
@@ -1948,7 +2146,12 @@ static void ehci_advance_state(EHCIState *ehci,
 
         case EST_FETCHQH:
             q = ehci_state_fetchqh(ehci, async);
-            again = q ? 1 : 0;
+            if (q != NULL) {
+                assert(q->async == async);
+                again = 1;
+            } else {
+                again = 0;
+            }
             break;
 
         case EST_FETCHITD:
@@ -1960,29 +2163,35 @@ static void ehci_advance_state(EHCIState *ehci,
             break;
 
         case EST_ADVANCEQUEUE:
-            again = ehci_state_advqueue(q, async);
+            again = ehci_state_advqueue(q);
             break;
 
         case EST_FETCHQTD:
-            again = ehci_state_fetchqtd(q, async);
+            again = ehci_state_fetchqtd(q);
             break;
 
         case EST_HORIZONTALQH:
-            again = ehci_state_horizqh(q, async);
+            again = ehci_state_horizqh(q);
             break;
 
         case EST_EXECUTE:
-            again = ehci_state_execute(q, async);
+            again = ehci_state_execute(q);
+            if (async) {
+                ehci->async_stepdown = 0;
+            }
             break;
 
         case EST_EXECUTING:
             assert(q != NULL);
-            again = ehci_state_executing(q, async);
+            if (async) {
+                ehci->async_stepdown = 0;
+            }
+            again = ehci_state_executing(q);
             break;
 
         case EST_WRITEBACK:
             assert(q != NULL);
-            again = ehci_state_writeback(q, async);
+            again = ehci_state_writeback(q);
             break;
 
         default:
@@ -2009,17 +2218,15 @@ static void ehci_advance_async_state(EHCIState *ehci)
 
     switch(ehci_get_state(ehci, async)) {
     case EST_INACTIVE:
-        if (!(ehci->usbcmd & USBCMD_ASE)) {
+        if (!ehci_async_enabled(ehci)) {
             break;
         }
-        ehci_set_usbsts(ehci, USBSTS_ASS);
         ehci_set_state(ehci, async, EST_ACTIVE);
         // No break, fall through to ACTIVE
 
     case EST_ACTIVE:
-        if ( !(ehci->usbcmd & USBCMD_ASE)) {
+        if (!ehci_async_enabled(ehci)) {
             ehci_queues_rip_all(ehci, async);
-            ehci_clear_usbsts(ehci, USBSTS_ASS);
             ehci_set_state(ehci, async, EST_INACTIVE);
             break;
         }
@@ -2045,7 +2252,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
          */
         if (ehci->usbcmd & USBCMD_IAAD) {
             /* Remove all unseen qhs from the async qhs queue */
-            ehci_queues_rip_unused(ehci, async, 1);
+            ehci_queues_tag_unused_async(ehci);
             DPRINTF("ASYNC: doorbell request acknowledged\n");
             ehci->usbcmd &= ~USBCMD_IAAD;
             ehci_set_interrupt(ehci, USBSTS_IAA);
@@ -2070,17 +2277,15 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
 
     switch(ehci_get_state(ehci, async)) {
     case EST_INACTIVE:
-        if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
-            ehci_set_usbsts(ehci, USBSTS_PSS);
+        if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) {
             ehci_set_state(ehci, async, EST_ACTIVE);
             // No break, fall through to ACTIVE
         } else
             break;
 
     case EST_ACTIVE:
-        if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+        if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) {
             ehci_queues_rip_all(ehci, async);
-            ehci_clear_usbsts(ehci, USBSTS_PSS);
             ehci_set_state(ehci, async, EST_INACTIVE);
             break;
         }
@@ -2100,7 +2305,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
         ehci_set_fetch_addr(ehci, async,entry);
         ehci_set_state(ehci, async, EST_FETCHENTRY);
         ehci_advance_state(ehci, async);
-        ehci_queues_rip_unused(ehci, async, 0);
+        ehci_queues_rip_unused(ehci, async);
         break;
 
     default:
@@ -2111,58 +2316,86 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
     }
 }
 
+static void ehci_update_frindex(EHCIState *ehci, int frames)
+{
+    int i;
+
+    if (!ehci_enabled(ehci)) {
+        return;
+    }
+
+    for (i = 0; i < frames; i++) {
+        ehci->frindex += 8;
+
+        if (ehci->frindex == 0x00002000) {
+            ehci_set_interrupt(ehci, USBSTS_FLR);
+        }
+
+        if (ehci->frindex == 0x00004000) {
+            ehci_set_interrupt(ehci, USBSTS_FLR);
+            ehci->frindex = 0;
+        }
+    }
+}
+
 static void ehci_frame_timer(void *opaque)
 {
     EHCIState *ehci = opaque;
+    int schedules = 0;
     int64_t expire_time, t_now;
     uint64_t ns_elapsed;
-    int frames;
+    int frames, skipped_frames;
     int i;
-    int skipped_frames = 0;
 
     t_now = qemu_get_clock_ns(vm_clock);
-    expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
-
     ns_elapsed = t_now - ehci->last_run_ns;
     frames = ns_elapsed / FRAME_TIMER_NS;
 
-    for (i = 0; i < frames; i++) {
-        if ( !(ehci->usbsts & USBSTS_HALT)) {
-            ehci->frindex += 8;
-
-            if (ehci->frindex == 0x00002000) {
-                ehci_set_interrupt(ehci, USBSTS_FLR);
-            }
+    if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
+        schedules++;
+        expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
 
-            if (ehci->frindex == 0x00004000) {
-                ehci_set_interrupt(ehci, USBSTS_FLR);
-                ehci->frindex = 0;
-            }
+        if (frames > ehci->maxframes) {
+            skipped_frames = frames - ehci->maxframes;
+            ehci_update_frindex(ehci, skipped_frames);
+            ehci->last_run_ns += FRAME_TIMER_NS * skipped_frames;
+            frames -= skipped_frames;
+            DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
         }
 
-        if (frames - i > ehci->maxframes) {
-            skipped_frames++;
-        } else {
+        for (i = 0; i < frames; i++) {
+            ehci_update_frindex(ehci, 1);
             ehci_advance_periodic_state(ehci);
+            ehci->last_run_ns += FRAME_TIMER_NS;
         }
-
-        ehci->last_run_ns += FRAME_TIMER_NS;
-    }
-
-#if 0
-    if (skipped_frames) {
-        DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+    } else {
+        if (ehci->async_stepdown < ehci->maxframes / 2) {
+            ehci->async_stepdown++;
+        }
+        expire_time = t_now + (get_ticks_per_sec()
+                               * ehci->async_stepdown / FRAME_TIMER_FREQ);
+        ehci_update_frindex(ehci, frames);
+        ehci->last_run_ns += FRAME_TIMER_NS * frames;
     }
-#endif
 
     /*  Async is not inside loop since it executes everything it can once
      *  called
      */
-    ehci_advance_async_state(ehci);
+    if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
+        schedules++;
+        qemu_bh_schedule(ehci->async_bh);
+    }
 
-    qemu_mod_timer(ehci->frame_timer, expire_time);
+    if (schedules) {
+        qemu_mod_timer(ehci->frame_timer, expire_time);
+    }
 }
 
+static void ehci_async_bh(void *opaque)
+{
+    EHCIState *ehci = opaque;
+    ehci_advance_async_state(ehci);
+}
 
 static const MemoryRegionOps ehci_mem_ops = {
     .old_mmio = {
@@ -2186,13 +2419,61 @@ static USBBusOps ehci_bus_ops = {
     .register_companion = ehci_register_companion,
 };
 
+static int usb_ehci_post_load(void *opaque, int version_id)
+{
+    EHCIState *s = opaque;
+    int i;
+
+    for (i = 0; i < NB_PORTS; i++) {
+        USBPort *companion = s->companion_ports[i];
+        if (companion == NULL) {
+            continue;
+        }
+        if (s->portsc[i] & PORTSC_POWNER) {
+            companion->dev = s->ports[i].dev;
+        } else {
+            companion->dev = NULL;
+        }
+    }
+
+    return 0;
+}
+
 static const VMStateDescription vmstate_ehci = {
-    .name = "ehci",
-    .unmigratable = 1,
+    .name        = "ehci",
+    .version_id  = 1,
+    .post_load   = usb_ehci_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, EHCIState),
+        /* mmio registers */
+        VMSTATE_UINT32(usbcmd, EHCIState),
+        VMSTATE_UINT32(usbsts, EHCIState),
+        VMSTATE_UINT32(usbintr, EHCIState),
+        VMSTATE_UINT32(frindex, EHCIState),
+        VMSTATE_UINT32(ctrldssegment, EHCIState),
+        VMSTATE_UINT32(periodiclistbase, EHCIState),
+        VMSTATE_UINT32(asynclistaddr, EHCIState),
+        VMSTATE_UINT32(configflag, EHCIState),
+        VMSTATE_UINT32(portsc[0], EHCIState),
+        VMSTATE_UINT32(portsc[1], EHCIState),
+        VMSTATE_UINT32(portsc[2], EHCIState),
+        VMSTATE_UINT32(portsc[3], EHCIState),
+        VMSTATE_UINT32(portsc[4], EHCIState),
+        VMSTATE_UINT32(portsc[5], EHCIState),
+        /* frame timer */
+        VMSTATE_TIMER(frame_timer, EHCIState),
+        VMSTATE_UINT64(last_run_ns, EHCIState),
+        VMSTATE_UINT32(async_stepdown, EHCIState),
+        /* schedule state */
+        VMSTATE_UINT32(astate, EHCIState),
+        VMSTATE_UINT32(pstate, EHCIState),
+        VMSTATE_UINT32(a_fetch_addr, EHCIState),
+        VMSTATE_UINT32(p_fetch_addr, EHCIState),
+        VMSTATE_END_OF_LIST()
+    }
 };
 
 static Property ehci_properties[] = {
-    DEFINE_PROP_UINT32("freq",      EHCIState, freq, FRAME_TIMER_FREQ),
     DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
     DEFINE_PROP_END_OF_LIST(),
 };
@@ -2298,8 +2579,10 @@ static int usb_ehci_initfn(PCIDevice *dev)
     }
 
     s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
+    s->async_bh = qemu_bh_new(ehci_async_bh, s);
     QTAILQ_INIT(&s->aqueues);
     QTAILQ_INIT(&s->pqueues);
+    usb_packet_init(&s->ipacket);
 
     qemu_register_reset(ehci_reset, s);
 
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 1a1cc88b1f..844e7ed166 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -31,7 +31,7 @@
 #include "hw/usb.h"
 #include "hw/pci.h"
 #include "hw/sysbus.h"
-#include "hw/qdev-addr.h"
+#include "hw/qdev-dma.h"
 
 //#define DEBUG_OHCI
 /* Dump packet contents.  */
@@ -62,6 +62,7 @@ typedef struct {
     USBBus bus;
     qemu_irq irq;
     MemoryRegion mem;
+    DMAContext *dma;
     int num_ports;
     const char *name;
 
@@ -104,7 +105,7 @@ typedef struct {
     uint32_t htest;
 
     /* SM501 local memory offset */
-    target_phys_addr_t localmem_base;
+    dma_addr_t localmem_base;
 
     /* Active packets.  */
     uint32_t old_ctl;
@@ -482,14 +483,14 @@ static void ohci_reset(void *opaque)
 
 /* Get an array of dwords from main memory */
 static inline int get_dwords(OHCIState *ohci,
-                             uint32_t addr, uint32_t *buf, int num)
+                             dma_addr_t addr, uint32_t *buf, int num)
 {
     int i;
 
     addr += ohci->localmem_base;
 
     for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        cpu_physical_memory_read(addr, buf, sizeof(*buf));
+        dma_memory_read(ohci->dma, addr, buf, sizeof(*buf));
         *buf = le32_to_cpu(*buf);
     }
 
@@ -498,7 +499,7 @@ static inline int get_dwords(OHCIState *ohci,
 
 /* Put an array of dwords in to main memory */
 static inline int put_dwords(OHCIState *ohci,
-                             uint32_t addr, uint32_t *buf, int num)
+                             dma_addr_t addr, uint32_t *buf, int num)
 {
     int i;
 
@@ -506,7 +507,7 @@ static inline int put_dwords(OHCIState *ohci,
 
     for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
         uint32_t tmp = cpu_to_le32(*buf);
-        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
+        dma_memory_write(ohci->dma, addr, &tmp, sizeof(tmp));
     }
 
     return 1;
@@ -514,14 +515,14 @@ static inline int put_dwords(OHCIState *ohci,
 
 /* Get an array of words from main memory */
 static inline int get_words(OHCIState *ohci,
-                            uint32_t addr, uint16_t *buf, int num)
+                            dma_addr_t addr, uint16_t *buf, int num)
 {
     int i;
 
     addr += ohci->localmem_base;
 
     for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
-        cpu_physical_memory_read(addr, buf, sizeof(*buf));
+        dma_memory_read(ohci->dma, addr, buf, sizeof(*buf));
         *buf = le16_to_cpu(*buf);
     }
 
@@ -530,7 +531,7 @@ static inline int get_words(OHCIState *ohci,
 
 /* Put an array of words in to main memory */
 static inline int put_words(OHCIState *ohci,
-                            uint32_t addr, uint16_t *buf, int num)
+                            dma_addr_t addr, uint16_t *buf, int num)
 {
     int i;
 
@@ -538,40 +539,40 @@ static inline int put_words(OHCIState *ohci,
 
     for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
         uint16_t tmp = cpu_to_le16(*buf);
-        cpu_physical_memory_write(addr, &tmp, sizeof(tmp));
+        dma_memory_write(ohci->dma, addr, &tmp, sizeof(tmp));
     }
 
     return 1;
 }
 
 static inline int ohci_read_ed(OHCIState *ohci,
-                               uint32_t addr, struct ohci_ed *ed)
+                               dma_addr_t addr, struct ohci_ed *ed)
 {
     return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
 }
 
 static inline int ohci_read_td(OHCIState *ohci,
-                               uint32_t addr, struct ohci_td *td)
+                               dma_addr_t addr, struct ohci_td *td)
 {
     return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
 static inline int ohci_read_iso_td(OHCIState *ohci,
-                                   uint32_t addr, struct ohci_iso_td *td)
+                                   dma_addr_t addr, struct ohci_iso_td *td)
 {
     return (get_dwords(ohci, addr, (uint32_t *)td, 4) &&
             get_words(ohci, addr + 16, td->offset, 8));
 }
 
 static inline int ohci_read_hcca(OHCIState *ohci,
-                                 uint32_t addr, struct ohci_hcca *hcca)
+                                 dma_addr_t addr, struct ohci_hcca *hcca)
 {
-    cpu_physical_memory_read(addr + ohci->localmem_base, hcca, sizeof(*hcca));
+    dma_memory_read(ohci->dma, addr + ohci->localmem_base, hcca, sizeof(*hcca));
     return 1;
 }
 
 static inline int ohci_put_ed(OHCIState *ohci,
-                              uint32_t addr, struct ohci_ed *ed)
+                              dma_addr_t addr, struct ohci_ed *ed)
 {
     /* ed->tail is under control of the HCD.
      * Since just ed->head is changed by HC, just write back this
@@ -583,64 +584,63 @@ static inline int ohci_put_ed(OHCIState *ohci,
 }
 
 static inline int ohci_put_td(OHCIState *ohci,
-                              uint32_t addr, struct ohci_td *td)
+                              dma_addr_t addr, struct ohci_td *td)
 {
     return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
 static inline int ohci_put_iso_td(OHCIState *ohci,
-                                  uint32_t addr, struct ohci_iso_td *td)
+                                  dma_addr_t addr, struct ohci_iso_td *td)
 {
     return (put_dwords(ohci, addr, (uint32_t *)td, 4) &&
             put_words(ohci, addr + 16, td->offset, 8));
 }
 
 static inline int ohci_put_hcca(OHCIState *ohci,
-                                uint32_t addr, struct ohci_hcca *hcca)
+                                dma_addr_t addr, struct ohci_hcca *hcca)
 {
-    cpu_physical_memory_write(addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET,
-                              (char *)hcca + HCCA_WRITEBACK_OFFSET,
-                              HCCA_WRITEBACK_SIZE);
+    dma_memory_write(ohci->dma,
+                     addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET,
+                     (char *)hcca + HCCA_WRITEBACK_OFFSET,
+                     HCCA_WRITEBACK_SIZE);
     return 1;
 }
 
 /* Read/Write the contents of a TD from/to main memory.  */
 static void ohci_copy_td(OHCIState *ohci, struct ohci_td *td,
-                         uint8_t *buf, int len, int write)
+                         uint8_t *buf, int len, DMADirection dir)
 {
-    uint32_t ptr;
-    uint32_t n;
+    dma_addr_t ptr, n;
 
     ptr = td->cbp;
     n = 0x1000 - (ptr & 0xfff);
     if (n > len)
         n = len;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+    dma_memory_rw(ohci->dma, ptr + ohci->localmem_base, buf, n, dir);
     if (n == len)
         return;
     ptr = td->be & ~0xfffu;
     buf += n;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+    dma_memory_rw(ohci->dma, ptr + ohci->localmem_base, buf, len - n, dir);
 }
 
 /* Read/Write the contents of an ISO TD from/to main memory.  */
 static void ohci_copy_iso_td(OHCIState *ohci,
                              uint32_t start_addr, uint32_t end_addr,
-                             uint8_t *buf, int len, int write)
+                             uint8_t *buf, int len, DMADirection dir)
 {
-    uint32_t ptr;
-    uint32_t n;
+    dma_addr_t ptr, n;
 
     ptr = start_addr;
     n = 0x1000 - (ptr & 0xfff);
     if (n > len)
         n = len;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+    dma_memory_rw(ohci->dma, ptr + ohci->localmem_base, buf, n, dir);
     if (n == len)
         return;
     ptr = end_addr & ~0xfffu;
     buf += n;
-    cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+    dma_memory_rw(ohci->dma, ptr + ohci->localmem_base, buf, len - n, dir);
 }
 
 static void ohci_process_lists(OHCIState *ohci, int completion);
@@ -803,7 +803,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     }
 
     if (len && dir != OHCI_TD_DIR_IN) {
-        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, 0);
+        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len,
+                         DMA_DIRECTION_TO_DEVICE);
     }
 
     if (completion) {
@@ -827,7 +828,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     /* Writeback */
     if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
         /* IN transfer succeeded */
-        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, 1);
+        ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret,
+                         DMA_DIRECTION_FROM_DEVICE);
         OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
                     OHCI_CC_NOERROR);
         OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
@@ -971,7 +973,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
                 pktlen = len;
             }
             if (!completion) {
-                ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, 0);
+                ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen,
+                             DMA_DIRECTION_TO_DEVICE);
             }
         }
     }
@@ -1021,7 +1024,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
     }
     if (ret >= 0) {
         if (dir == OHCI_TD_DIR_IN) {
-            ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1);
+            ohci_copy_td(ohci, &td, ohci->usb_buf, ret,
+                         DMA_DIRECTION_FROM_DEVICE);
 #ifdef DEBUG_PACKET
             DPRINTF("  data:");
             for (i = 0; i < ret; i++)
@@ -1748,11 +1752,14 @@ static USBBusOps ohci_bus_ops = {
 };
 
 static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
-                         int num_ports, uint32_t localmem_base,
-                         char *masterbus, uint32_t firstport)
+                         int num_ports, dma_addr_t localmem_base,
+                         char *masterbus, uint32_t firstport,
+                         DMAContext *dma)
 {
     int i;
 
+    ohci->dma = dma;
+
     if (usb_frame_time == 0) {
 #ifdef OHCI_TIME_WARP
         usb_frame_time = get_ticks_per_sec();
@@ -1817,7 +1824,8 @@ static int usb_ohci_initfn_pci(struct PCIDevice *dev)
     ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
 
     if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0,
-                      ohci->masterbus, ohci->firstport) != 0) {
+                      ohci->masterbus, ohci->firstport,
+                      pci_dma_context(dev)) != 0) {
         return -1;
     }
     ohci->state.irq = ohci->pci_dev.irq[0];
@@ -1831,7 +1839,7 @@ typedef struct {
     SysBusDevice busdev;
     OHCIState ohci;
     uint32_t num_ports;
-    target_phys_addr_t dma_offset;
+    dma_addr_t dma_offset;
 } OHCISysBusState;
 
 static int ohci_init_pxa(SysBusDevice *dev)
@@ -1839,7 +1847,8 @@ static int ohci_init_pxa(SysBusDevice *dev)
     OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
 
     /* Cannot fail as we pass NULL for masterbus */
-    usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0);
+    usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0,
+                  NULL);
     sysbus_init_irq(dev, &s->ohci.irq);
     sysbus_init_mmio(dev, &s->ohci.mem);
 
@@ -1875,7 +1884,7 @@ static TypeInfo ohci_pci_info = {
 
 static Property ohci_sysbus_properties[] = {
     DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3),
-    DEFINE_PROP_TADDR("dma-offset", OHCISysBusState, dma_offset, 3),
+    DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 3),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 9e211a0bb4..8f652d2f4a 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -131,10 +131,14 @@ struct UHCIState {
     uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
     int64_t expire_time;
     QEMUTimer *frame_timer;
+    QEMUBH *bh;
+    uint32_t frame_bytes;
+    uint32_t frame_bandwidth;
     UHCIPort ports[NB_PORTS];
 
     /* Interrupts that should be raised at the end of the current frame.  */
     uint32_t pending_int_mask;
+    int irq_pin;
 
     /* Active packets */
     QTAILQ_HEAD(, UHCIQueue) queues;
@@ -288,10 +292,10 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
 
 static void uhci_async_cancel_all(UHCIState *s)
 {
-    UHCIQueue *queue;
+    UHCIQueue *queue, *nq;
     UHCIAsync *curr, *n;
 
-    QTAILQ_FOREACH(queue, &s->queues, next) {
+    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) {
         QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
             uhci_async_unlink(curr);
             uhci_async_cancel(curr);
@@ -337,7 +341,7 @@ static void uhci_update_irq(UHCIState *s)
     } else {
         level = 0;
     }
-    qemu_set_irq(s->dev.irq[3], level);
+    qemu_set_irq(s->dev.irq[s->irq_pin], level);
 }
 
 static void uhci_reset(void *opaque)
@@ -369,16 +373,10 @@ static void uhci_reset(void *opaque)
     }
 
     uhci_async_cancel_all(s);
+    qemu_bh_cancel(s->bh);
     uhci_update_irq(s);
 }
 
-static void uhci_pre_save(void *opaque)
-{
-    UHCIState *s = opaque;
-
-    uhci_async_cancel_all(s);
-}
-
 static const VMStateDescription vmstate_uhci_port = {
     .name = "uhci port",
     .version_id = 1,
@@ -395,7 +393,6 @@ static const VMStateDescription vmstate_uhci = {
     .version_id = 2,
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
-    .pre_save = uhci_pre_save,
     .fields      = (VMStateField []) {
         VMSTATE_PCI_DEVICE(dev, UHCIState),
         VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
@@ -874,7 +871,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
 
 done:
     len = uhci_complete_td(s, td, async, int_mask);
-    usb_packet_unmap(&async->packet);
+    usb_packet_unmap(&async->packet, &async->sgl);
     uhci_async_free(async);
     return len;
 }
@@ -905,7 +902,9 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
         uhci_async_free(async);
     } else {
         async->done = 1;
-        uhci_process_frame(s);
+        if (s->frame_bytes < s->frame_bandwidth) {
+            qemu_bh_schedule(s->bh);
+        }
     }
 }
 
@@ -985,7 +984,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
 static void uhci_process_frame(UHCIState *s)
 {
     uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
-    uint32_t curr_qh, td_count = 0, bytes_count = 0;
+    uint32_t curr_qh, td_count = 0;
     int cnt, ret;
     UHCI_TD td;
     UHCI_QH qh;
@@ -1002,6 +1001,12 @@ static void uhci_process_frame(UHCIState *s)
     qhdb_reset(&qhdb);
 
     for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
+        if (s->frame_bytes >= s->frame_bandwidth) {
+            /* We've reached the usb 1.1 bandwidth, which is
+               1280 bytes/frame, stop processing */
+            trace_usb_uhci_frame_stop_bandwidth();
+            break;
+        }
         if (is_qh(link)) {
             /* QH */
             trace_usb_uhci_qh_load(link & ~0xf);
@@ -1011,18 +1016,12 @@ static void uhci_process_frame(UHCIState *s)
                  * We're going in circles. Which is not a bug because
                  * HCD is allowed to do that as part of the BW management.
                  *
-                 * Stop processing here if
-                 *  (a) no transaction has been done since we've been
-                 *      here last time, or
-                 *  (b) we've reached the usb 1.1 bandwidth, which is
-                 *      1280 bytes/frame.
+                 * Stop processing here if no transaction has been done
+                 * since we've been here last time.
                  */
                 if (td_count == 0) {
                     trace_usb_uhci_frame_loop_stop_idle();
                     break;
-                } else if (bytes_count >= 1280) {
-                    trace_usb_uhci_frame_loop_stop_bandwidth();
-                    break;
                 } else {
                     trace_usb_uhci_frame_loop_continue();
                     td_count = 0;
@@ -1085,7 +1084,7 @@ static void uhci_process_frame(UHCIState *s)
             trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf);
             link = td.link;
             td_count++;
-            bytes_count += (td.ctrl & 0x7ff) + 1;
+            s->frame_bytes += (td.ctrl & 0x7ff) + 1;
 
             if (curr_qh) {
                 /* update QH element link */
@@ -1112,12 +1111,20 @@ out:
     s->pending_int_mask |= int_mask;
 }
 
+static void uhci_bh(void *opaque)
+{
+    UHCIState *s = opaque;
+    uhci_process_frame(s);
+}
+
 static void uhci_frame_timer(void *opaque)
 {
     UHCIState *s = opaque;
 
     /* prepare the timer for the next frame */
     s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+    s->frame_bytes = 0;
+    qemu_bh_cancel(s->bh);
 
     if (!(s->cmd & UHCI_CMD_RS)) {
         /* Full stop */
@@ -1178,15 +1185,31 @@ static USBBusOps uhci_bus_ops = {
 
 static int usb_uhci_common_initfn(PCIDevice *dev)
 {
+    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
     UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
     uint8_t *pci_conf = s->dev.config;
     int i;
 
     pci_conf[PCI_CLASS_PROG] = 0x00;
     /* TODO: reset value should be 0. */
-    pci_conf[PCI_INTERRUPT_PIN] = 4; /* interrupt pin D */
     pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
 
+    switch (pc->device_id) {
+    case PCI_DEVICE_ID_INTEL_82801I_UHCI1:
+        s->irq_pin = 0;  /* A */
+        break;
+    case PCI_DEVICE_ID_INTEL_82801I_UHCI2:
+        s->irq_pin = 1;  /* B */
+        break;
+    case PCI_DEVICE_ID_INTEL_82801I_UHCI3:
+        s->irq_pin = 2;  /* C */
+        break;
+    default:
+        s->irq_pin = 3;  /* D */
+        break;
+    }
+    pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
+
     if (s->masterbus) {
         USBPort *ports[NB_PORTS];
         for(i = 0; i < NB_PORTS; i++) {
@@ -1204,6 +1227,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
                               USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
         }
     }
+    s->bh = qemu_bh_new(uhci_bh, s);
     s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
     s->num_ports_vmstate = NB_PORTS;
     QTAILQ_INIT(&s->queues);
@@ -1244,6 +1268,7 @@ static int usb_uhci_exit(PCIDevice *dev)
 static Property uhci_properties[] = {
     DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
     DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
+    DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 5cf1a64699..6c2ff024e0 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -23,6 +23,7 @@
 #include "hw/usb.h"
 #include "hw/pci.h"
 #include "hw/msi.h"
+#include "trace.h"
 
 //#define DEBUG_XHCI
 //#define DEBUG_DATA
@@ -421,7 +422,6 @@ typedef struct XHCIEvRingSeg {
     uint32_t rsvd;
 } XHCIEvRingSeg;
 
-#ifdef DEBUG_XHCI
 static const char *TRBType_names[] = {
     [TRB_RESERVED]                     = "TRB_RESERVED",
     [TR_NORMAL]                        = "TR_NORMAL",
@@ -473,7 +473,6 @@ static const char *trb_name(XHCITRB *trb)
     return lookup_name(TRB_TYPE(*trb), TRBType_names,
                        ARRAY_SIZE(TRBType_names));
 }
-#endif
 
 static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
                          unsigned int epid);
@@ -505,14 +504,13 @@ static void xhci_irq_update(XHCIState *xhci)
         level = 1;
     }
 
-    DPRINTF("xhci_irq_update(): %d\n", level);
-
     if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
         if (level) {
-            DPRINTF("xhci_irq_update(): MSI signal\n");
+            trace_usb_xhci_irq_msi(0);
             msi_notify(&xhci->pci_dev, 0);
         }
     } else {
+        trace_usb_xhci_irq_intx(level);
         qemu_set_irq(xhci->irq, level);
     }
 }
@@ -542,9 +540,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
     }
     ev_trb.control = cpu_to_le32(ev_trb.control);
 
-    DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n",
-            xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control,
-            trb_name(&ev_trb));
+    trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
+                               ev_trb.parameter, ev_trb.status, ev_trb.control);
 
     addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
     pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
@@ -704,10 +701,8 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
         le32_to_cpus(&trb->status);
         le32_to_cpus(&trb->control);
 
-        DPRINTF("xhci: TRB fetched [" DMA_ADDR_FMT "]: "
-                "%016" PRIx64 " %08x %08x %s\n",
-                ring->dequeue, trb->parameter, trb->status, trb->control,
-                trb_name(trb));
+        trace_usb_xhci_fetch_trb(ring->dequeue, trb_name(trb),
+                                 trb->parameter, trb->status, trb->control);
 
         if ((trb->control & TRB_C) != ring->ccs) {
             return 0;
@@ -746,10 +741,6 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
         le32_to_cpus(&trb.status);
         le32_to_cpus(&trb.control);
 
-        DPRINTF("xhci: TRB peeked [" DMA_ADDR_FMT "]: "
-                "%016" PRIx64 " %08x %08x\n",
-                dequeue, trb.parameter, trb.status, trb.control);
-
         if ((trb.control & TRB_C) != ccs) {
             return -length;
         }
@@ -812,14 +803,13 @@ static void xhci_er_reset(XHCIState *xhci)
 
 static void xhci_run(XHCIState *xhci)
 {
-    DPRINTF("xhci_run()\n");
-
+    trace_usb_xhci_run();
     xhci->usbsts &= ~USBSTS_HCH;
 }
 
 static void xhci_stop(XHCIState *xhci)
 {
-    DPRINTF("xhci_stop()\n");
+    trace_usb_xhci_stop();
     xhci->usbsts |= USBSTS_HCH;
     xhci->crcr_low &= ~CRCR_CRR;
 }
@@ -852,11 +842,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
     dma_addr_t dequeue;
     int i;
 
+    trace_usb_xhci_ep_enable(slotid, epid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
     assert(epid >= 1 && epid <= 31);
 
-    DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid);
-
     slot = &xhci->slots[slotid-1];
     if (slot->eps[epid-1]) {
         fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
@@ -971,11 +960,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
     XHCISlot *slot;
     XHCIEPContext *epctx;
 
+    trace_usb_xhci_ep_disable(slotid, epid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
     assert(epid >= 1 && epid <= 31);
 
-    DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid);
-
     slot = &xhci->slots[slotid-1];
 
     if (!slot->eps[epid-1]) {
@@ -1001,8 +989,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
     XHCISlot *slot;
     XHCIEPContext *epctx;
 
-    DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid);
-
+    trace_usb_xhci_ep_stop(slotid, epid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
 
     if (epid < 1 || epid > 31) {
@@ -1036,10 +1023,9 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
     XHCIEPContext *epctx;
     USBDevice *dev;
 
+    trace_usb_xhci_ep_reset(slotid, epid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
 
-    DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid);
-
     if (epid < 1 || epid > 31) {
         fprintf(stderr, "xhci: bad ep %d\n", epid);
         return CC_TRB_ERROR;
@@ -1416,12 +1402,14 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
 static int xhci_complete_packet(XHCITransfer *xfer, int ret)
 {
     if (ret == USB_RET_ASYNC) {
+        trace_usb_xhci_xfer_async(xfer);
         xfer->running_async = 1;
         xfer->running_retry = 0;
         xfer->complete = 0;
         xfer->cancelled = 0;
         return 0;
     } else if (ret == USB_RET_NAK) {
+        trace_usb_xhci_xfer_nak(xfer);
         xfer->running_async = 0;
         xfer->running_retry = 1;
         xfer->complete = 0;
@@ -1436,10 +1424,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
     if (ret >= 0) {
         xfer->status = CC_SUCCESS;
         xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
+        trace_usb_xhci_xfer_success(xfer, ret);
         return 0;
     }
 
     /* error */
+    trace_usb_xhci_xfer_error(xfer, ret);
     switch (ret) {
     case USB_RET_NODEV:
         xfer->status = CC_USB_TRANSACTION_ERROR;
@@ -1475,11 +1465,12 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
     USBDevice *dev;
     int ret;
 
-    DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid);
-
     trb_setup = &xfer->trbs[0];
     trb_status = &xfer->trbs[xfer->trb_count-1];
 
+    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
+                              trb_setup->parameter >> 48);
+
     /* at most one Event Data TRB allowed after STATUS */
     if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
         trb_status--;
@@ -1620,15 +1611,14 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
     unsigned int length = 0;
     XHCITRB *trb;
 
-    DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
     for (i = 0; i < xfer->trb_count; i++) {
         trb = &xfer->trbs[i];
         if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
             length += trb->status & 0x1ffff;
         }
     }
-    DPRINTF("xhci: total TD length=%d\n", length);
+
+    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
 
     if (!epctx->has_bg) {
         xfer->data_length = length;
@@ -1664,9 +1654,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
     int length;
     int i;
 
+    trace_usb_xhci_ep_kick(slotid, epid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
     assert(epid >= 1 && epid <= 31);
-    DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid);
 
     if (!xhci->slots[slotid-1].enabled) {
         fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid);
@@ -1684,15 +1674,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
         XHCITransfer *xfer = epctx->retry;
         int result;
 
-        DPRINTF("xhci: retry nack'ed transfer ...\n");
+        trace_usb_xhci_xfer_retry(xfer);
         assert(xfer->running_retry);
         xhci_setup_packet(xfer, xfer->packet.ep->dev);
         result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
         if (result == USB_RET_NAK) {
-            DPRINTF("xhci: ... xfer still nacked\n");
             return;
         }
-        DPRINTF("xhci: ... result %d\n", result);
         xhci_complete_packet(xfer, result);
         assert(!xfer->running_retry);
         epctx->retry = NULL;
@@ -1708,21 +1696,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
     while (1) {
         XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
         if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
-            DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n",
-                    epctx->next_xfer, xfer->running_async,
-                    xfer->running_retry, xfer->backgrounded);
             break;
-        } else {
-            DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer);
         }
         length = xhci_ring_chain_length(xhci, &epctx->ring);
         if (length < 0) {
-            DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length);
             break;
         } else if (length == 0) {
             break;
         }
-        DPRINTF("xhci: fetching %d-TRB TD\n", length);
         if (xfer->trbs && xfer->trb_alloced < length) {
             xfer->trb_count = 0;
             xfer->trb_alloced = 0;
@@ -1757,7 +1738,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
         }
 
         if (epctx->state == EP_HALTED) {
-            DPRINTF("xhci: ep halted, stopping schedule\n");
             break;
         }
         if (xfer->running_retry) {
@@ -1770,8 +1750,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
 
 static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
 {
+    trace_usb_xhci_slot_enable(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_enable_slot(%d)\n", slotid);
     xhci->slots[slotid-1].enabled = 1;
     xhci->slots[slotid-1].port = 0;
     memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
@@ -1783,8 +1763,8 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
 {
     int i;
 
+    trace_usb_xhci_slot_disable(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_disable_slot(%d)\n", slotid);
 
     for (i = 1; i <= 31; i++) {
         if (xhci->slots[slotid-1].eps[i-1]) {
@@ -1810,8 +1790,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
     int i;
     TRBCCode res;
 
+    trace_usb_xhci_slot_address(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_address_slot(%d)\n", slotid);
 
     dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
     pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx));
@@ -1897,8 +1877,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
     int i;
     TRBCCode res;
 
+    trace_usb_xhci_slot_configure(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_configure_slot(%d)\n", slotid);
 
     ictx = xhci_mask64(pictx);
     octx = xhci->slots[slotid-1].ctx;
@@ -1985,8 +1965,8 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
     uint32_t islot_ctx[4];
     uint32_t slot_ctx[4];
 
+    trace_usb_xhci_slot_evaluate(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_evaluate_slot(%d)\n", slotid);
 
     ictx = xhci_mask64(pictx);
     octx = xhci->slots[slotid-1].ctx;
@@ -2048,8 +2028,8 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
     dma_addr_t octx;
     int i;
 
+    trace_usb_xhci_slot_reset(slotid);
     assert(slotid >= 1 && slotid <= MAXSLOTS);
-    DPRINTF("xhci_reset_slot(%d)\n", slotid);
 
     octx = xhci->slots[slotid-1].ctx;
 
@@ -2296,12 +2276,12 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
     }
 }
 
-static void xhci_reset(void *opaque)
+static void xhci_reset(DeviceState *dev)
 {
-    XHCIState *xhci = opaque;
+    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev);
     int i;
 
-    DPRINTF("xhci: full reset\n");
+    trace_usb_xhci_reset();
     if (!(xhci->usbsts & USBSTS_HCH)) {
         fprintf(stderr, "xhci: reset while running!\n");
     }
@@ -2342,77 +2322,98 @@ static void xhci_reset(void *opaque)
 
 static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
 {
-    DPRINTF("xhci_cap_read(0x%x)\n", reg);
+    uint32_t ret;
 
     switch (reg) {
     case 0x00: /* HCIVERSION, CAPLENGTH */
-        return 0x01000000 | LEN_CAP;
+        ret = 0x01000000 | LEN_CAP;
+        break;
     case 0x04: /* HCSPARAMS 1 */
-        return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+        ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+        break;
     case 0x08: /* HCSPARAMS 2 */
-        return 0x0000000f;
+        ret = 0x0000000f;
+        break;
     case 0x0c: /* HCSPARAMS 3 */
-        return 0x00000000;
+        ret = 0x00000000;
+        break;
     case 0x10: /* HCCPARAMS */
-#if TARGET_PHYS_ADDR_BITS > 32
-        return 0x00081001;
-#else
-        return 0x00081000;
-#endif
+        if (sizeof(dma_addr_t) == 4) {
+            ret = 0x00081000;
+        } else {
+            ret = 0x00081001;
+        }
+        break;
     case 0x14: /* DBOFF */
-        return OFF_DOORBELL;
+        ret = OFF_DOORBELL;
+        break;
     case 0x18: /* RTSOFF */
-        return OFF_RUNTIME;
+        ret = OFF_RUNTIME;
+        break;
 
     /* extended capabilities */
     case 0x20: /* Supported Protocol:00 */
-#if USB3_PORTS > 0
-        return 0x02000402; /* USB 2.0 */
-#else
-        return 0x02000002; /* USB 2.0 */
-#endif
+        ret = 0x02000402; /* USB 2.0 */
+        break;
     case 0x24: /* Supported Protocol:04 */
-        return 0x20425455; /* "USB " */
+        ret = 0x20425455; /* "USB " */
+        break;
     case 0x28: /* Supported Protocol:08 */
-        return 0x00000001 | (USB2_PORTS<<8);
+        ret = 0x00000001 | (USB2_PORTS<<8);
+        break;
     case 0x2c: /* Supported Protocol:0c */
-        return 0x00000000; /* reserved */
-#if USB3_PORTS > 0
+        ret = 0x00000000; /* reserved */
+        break;
     case 0x30: /* Supported Protocol:00 */
-        return 0x03000002; /* USB 3.0 */
+        ret = 0x03000002; /* USB 3.0 */
+        break;
     case 0x34: /* Supported Protocol:04 */
-        return 0x20425455; /* "USB " */
+        ret = 0x20425455; /* "USB " */
+        break;
     case 0x38: /* Supported Protocol:08 */
-        return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+        ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+        break;
     case 0x3c: /* Supported Protocol:0c */
-        return 0x00000000; /* reserved */
-#endif
+        ret = 0x00000000; /* reserved */
+        break;
     default:
         fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
+        ret = 0;
     }
-    return 0;
+
+    trace_usb_xhci_cap_read(reg, ret);
+    return ret;
 }
 
 static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
 {
     uint32_t port = reg >> 4;
+    uint32_t ret;
+
     if (port >= MAXPORTS) {
         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
-        return 0;
+        ret = 0;
+        goto out;
     }
 
     switch (reg & 0xf) {
     case 0x00: /* PORTSC */
-        return xhci->ports[port].portsc;
+        ret = xhci->ports[port].portsc;
+        break;
     case 0x04: /* PORTPMSC */
     case 0x08: /* PORTLI */
-        return 0;
+        ret = 0;
+        break;
     case 0x0c: /* reserved */
     default:
         fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
                 port, reg);
-        return 0;
+        ret = 0;
     }
+
+out:
+    trace_usb_xhci_port_read(port, reg & 0x0f, ret);
+    return ret;
 }
 
 static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
@@ -2420,6 +2421,8 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
     uint32_t port = reg >> 4;
     uint32_t portsc;
 
+    trace_usb_xhci_port_write(port, reg & 0x0f, val);
+
     if (port >= MAXPORTS) {
         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
         return;
@@ -2457,7 +2460,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 
 static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
 {
-    DPRINTF("xhci_oper_read(0x%x)\n", reg);
+    uint32_t ret;
 
     if (reg >= 0x400) {
         return xhci_port_read(xhci, reg - 0x400);
@@ -2465,38 +2468,50 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
 
     switch (reg) {
     case 0x00: /* USBCMD */
-        return xhci->usbcmd;
+        ret = xhci->usbcmd;
+        break;
     case 0x04: /* USBSTS */
-        return xhci->usbsts;
+        ret = xhci->usbsts;
+        break;
     case 0x08: /* PAGESIZE */
-        return 1; /* 4KiB */
+        ret = 1; /* 4KiB */
+        break;
     case 0x14: /* DNCTRL */
-        return xhci->dnctrl;
+        ret = xhci->dnctrl;
+        break;
     case 0x18: /* CRCR low */
-        return xhci->crcr_low & ~0xe;
+        ret = xhci->crcr_low & ~0xe;
+        break;
     case 0x1c: /* CRCR high */
-        return xhci->crcr_high;
+        ret = xhci->crcr_high;
+        break;
     case 0x30: /* DCBAAP low */
-        return xhci->dcbaap_low;
+        ret = xhci->dcbaap_low;
+        break;
     case 0x34: /* DCBAAP high */
-        return xhci->dcbaap_high;
+        ret = xhci->dcbaap_high;
+        break;
     case 0x38: /* CONFIG */
-        return xhci->config;
+        ret = xhci->config;
+        break;
     default:
         fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
+        ret = 0;
     }
-    return 0;
+
+    trace_usb_xhci_oper_read(reg, ret);
+    return ret;
 }
 
 static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 {
-    DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val);
-
     if (reg >= 0x400) {
         xhci_port_write(xhci, reg - 0x400, val);
         return;
     }
 
+    trace_usb_xhci_oper_write(reg, val);
+
     switch (reg) {
     case 0x00: /* USBCMD */
         if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
@@ -2506,7 +2521,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
         }
         xhci->usbcmd = val & 0xc0f;
         if (val & USBCMD_HCRST) {
-            xhci_reset(xhci);
+            xhci_reset(&xhci->pci_dev.qdev);
         }
         xhci_irq_update(xhci);
         break;
@@ -2552,35 +2567,46 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 
 static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
 {
-    DPRINTF("xhci_runtime_read(0x%x)\n", reg);
+    uint32_t ret;
 
     switch (reg) {
     case 0x00: /* MFINDEX */
         fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
-        return xhci->mfindex;
+        ret = xhci->mfindex;
+        break;
     case 0x20: /* IMAN */
-        return xhci->iman;
+        ret = xhci->iman;
+        break;
     case 0x24: /* IMOD */
-        return xhci->imod;
+        ret = xhci->imod;
+        break;
     case 0x28: /* ERSTSZ */
-        return xhci->erstsz;
+        ret = xhci->erstsz;
+        break;
     case 0x30: /* ERSTBA low */
-        return xhci->erstba_low;
+        ret = xhci->erstba_low;
+        break;
     case 0x34: /* ERSTBA high */
-        return xhci->erstba_high;
+        ret = xhci->erstba_high;
+        break;
     case 0x38: /* ERDP low */
-        return xhci->erdp_low;
+        ret = xhci->erdp_low;
+        break;
     case 0x3c: /* ERDP high */
-        return xhci->erdp_high;
+        ret = xhci->erdp_high;
+        break;
     default:
         fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+        ret = 0;
     }
-    return 0;
+
+    trace_usb_xhci_runtime_read(reg, ret);
+    return ret;
 }
 
 static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 {
-    DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val);
+    trace_usb_xhci_runtime_read(reg, val);
 
     switch (reg) {
     case 0x20: /* IMAN */
@@ -2623,14 +2649,14 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 
 static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
 {
-    DPRINTF("xhci_doorbell_read(0x%x)\n", reg);
     /* doorbells always read as 0 */
+    trace_usb_xhci_doorbell_read(reg, 0);
     return 0;
 }
 
 static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
 {
-    DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val);
+    trace_usb_xhci_doorbell_write(reg, val);
 
     if (!xhci_running(xhci)) {
         fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n");
@@ -2831,8 +2857,6 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
     for (i = 0; i < MAXSLOTS; i++) {
         xhci->slots[i].enabled = 0;
     }
-
-    qemu_register_reset(xhci_reset, xhci);
 }
 
 static int usb_xhci_initfn(struct PCIDevice *dev)
@@ -2895,6 +2919,7 @@ static void xhci_class_init(ObjectClass *klass, void *data)
 
     dc->vmsd    = &vmstate_xhci;
     dc->props   = xhci_properties;
+    dc->reset   = xhci_reset;
     k->init         = usb_xhci_initfn;
     k->vendor_id    = PCI_VENDOR_ID_NEC;
     k->device_id    = PCI_DEVICE_ID_NEC_UPD720200;
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index a95b0eda55..d55be878ad 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -111,6 +111,7 @@ typedef struct USBHostDevice {
     uint32_t  iso_urb_count;
     uint32_t  options;
     Notifier  exit;
+    QEMUBH    *bh;
 
     struct endp_data ep_in[USB_MAX_ENDPOINTS];
     struct endp_data ep_out[USB_MAX_ENDPOINTS];
@@ -212,7 +213,7 @@ static int is_iso_started(USBHostDevice *s, int pid, int ep)
 
 static void clear_iso_started(USBHostDevice *s, int pid, int ep)
 {
-    trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
+    trace_usb_host_iso_stop(s->bus_num, s->addr, ep);
     get_endp(s, pid, ep)->iso_started = 0;
 }
 
@@ -220,7 +221,7 @@ static void set_iso_started(USBHostDevice *s, int pid, int ep)
 {
     struct endp_data *e = get_endp(s, pid, ep);
 
-    trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
+    trace_usb_host_iso_start(s->bus_num, s->addr, ep);
     if (!e->iso_started) {
         e->iso_started = 1;
         e->inflight = 0;
@@ -318,7 +319,8 @@ static void async_complete(void *opaque)
         if (r < 0) {
             if (errno == EAGAIN) {
                 if (urbs > 2) {
-                    fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
+                    /* indicates possible latency issues */
+                    trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs);
                 }
                 return;
             }
@@ -351,7 +353,8 @@ static void async_complete(void *opaque)
             urbs++;
             inflight = change_iso_inflight(s, pid, ep, -1);
             if (inflight == 0 && is_iso_started(s, pid, ep)) {
-                fprintf(stderr, "husb: out of buffers for iso stream\n");
+                /* can be latency issues, or simply end of stream */
+                trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep);
             }
             continue;
         }
@@ -1135,7 +1138,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
     USBDescriptor *d;
     bool active = false;
 
-    usb_ep_init(&s->dev);
+    usb_ep_reset(&s->dev);
 
     for (i = 0;; i += d->bLength) {
         if (i+2 >= s->descr_len) {
@@ -1238,7 +1241,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
     return 0;
 
 error:
-    usb_ep_init(&s->dev);
+    usb_ep_reset(&s->dev);
     return 1;
 }
 
@@ -1325,6 +1328,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
         goto fail;
     }
 
+    usb_ep_init(&dev->dev);
     ret = usb_linux_update_endp_table(dev);
     if (ret) {
         goto fail;
@@ -1421,6 +1425,43 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data)
     }
 }
 
+/*
+ * This is *NOT* about restoring state.  We have absolutely no idea
+ * what state the host device is in at the moment and whenever it is
+ * still present in the first place.  Attemping to contine where we
+ * left off is impossible.
+ *
+ * What we are going to to to here is emulate a surprise removal of
+ * the usb device passed through, then kick host scan so the device
+ * will get re-attached (and re-initialized by the guest) in case it
+ * is still present.
+ *
+ * As the device removal will change the state of other devices (usb
+ * host controller, most likely interrupt controller too) we have to
+ * wait with it until *all* vmstate is loaded.  Thus post_load just
+ * kicks a bottom half which then does the actual work.
+ */
+static void usb_host_post_load_bh(void *opaque)
+{
+    USBHostDevice *dev = opaque;
+
+    if (dev->fd != -1) {
+        usb_host_close(dev);
+    }
+    if (dev->dev.attached) {
+        usb_device_detach(&dev->dev);
+    }
+    usb_host_auto_check(NULL);
+}
+
+static int usb_host_post_load(void *opaque, int version_id)
+{
+    USBHostDevice *dev = opaque;
+
+    qemu_bh_schedule(dev->bh);
+    return 0;
+}
+
 static int usb_host_initfn(USBDevice *dev)
 {
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
@@ -1432,6 +1473,7 @@ static int usb_host_initfn(USBDevice *dev)
     QTAILQ_INSERT_TAIL(&hostdevs, s, next);
     s->exit.notify = usb_host_exit_notifier;
     qemu_add_exit_notifier(&s->exit);
+    s->bh = qemu_bh_new(usb_host_post_load_bh, s);
     usb_host_auto_check(NULL);
 
     if (s->match.bus_num != 0 && s->match.port != NULL) {
@@ -1443,7 +1485,13 @@ static int usb_host_initfn(USBDevice *dev)
 
 static const VMStateDescription vmstate_usb_host = {
     .name = "usb-host",
-    .unmigratable = 1,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = usb_host_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_USB_DEVICE(dev, USBHostDevice),
+        VMSTATE_END_OF_LIST()
+    }
 };
 
 static Property usb_host_dev_properties[] = {
@@ -1737,25 +1785,27 @@ static void usb_host_auto_check(void *unused)
     struct USBHostDevice *s;
     int unconnected = 0;
 
-    usb_host_scan(NULL, usb_host_auto_scan);
+    if (runstate_is_running()) {
+        usb_host_scan(NULL, usb_host_auto_scan);
 
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        if (s->fd == -1) {
-            unconnected++;
-        }
-        if (s->seen == 0) {
-            s->errcount = 0;
+        QTAILQ_FOREACH(s, &hostdevs, next) {
+            if (s->fd == -1) {
+                unconnected++;
+            }
+            if (s->seen == 0) {
+                s->errcount = 0;
+            }
+            s->seen = 0;
         }
-        s->seen = 0;
-    }
 
-    if (unconnected == 0) {
-        /* nothing to watch */
-        if (usb_auto_timer) {
-            qemu_del_timer(usb_auto_timer);
-            trace_usb_host_auto_scan_disabled();
+        if (unconnected == 0) {
+            /* nothing to watch */
+            if (usb_auto_timer) {
+                qemu_del_timer(usb_auto_timer);
+                trace_usb_host_auto_scan_disabled();
+            }
+            return;
         }
-        return;
     }
 
     if (!usb_auto_timer) {
diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c
index 2462351389..c0de30ea88 100644
--- a/hw/usb/libhw.c
+++ b/hw/usb/libhw.c
@@ -26,15 +26,15 @@
 
 int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
 {
-    int is_write = (p->pid == USB_TOKEN_IN);
-    target_phys_addr_t len;
+    DMADirection dir = (p->pid == USB_TOKEN_IN) ?
+        DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE;
+    dma_addr_t len;
     void *mem;
     int i;
 
     for (i = 0; i < sgl->nsg; i++) {
         len = sgl->sg[i].len;
-        mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
-                                      is_write);
+        mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &len, dir);
         if (!mem) {
             goto err;
         }
@@ -46,18 +46,19 @@ int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
     return 0;
 
 err:
-    usb_packet_unmap(p);
+    usb_packet_unmap(p, sgl);
     return -1;
 }
 
-void usb_packet_unmap(USBPacket *p)
+void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl)
 {
-    int is_write = (p->pid == USB_TOKEN_IN);
+    DMADirection dir = (p->pid == USB_TOKEN_IN) ?
+        DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE;
     int i;
 
     for (i = 0; i < p->iov.niov; i++) {
-        cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
-                                  p->iov.iov[i].iov_len, is_write,
-                                  p->iov.iov[i].iov_len);
+        dma_memory_unmap(sgl->dma, p->iov.iov[i].iov_base,
+                         p->iov.iov[i].iov_len, dir,
+                         p->iov.iov[i].iov_len);
     }
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 51c27b4051..10b4fbb3a7 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -143,8 +143,6 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
 static int usbredir_handle_status(USBRedirDevice *dev,
                                        int status, int actual_len);
 
-#define VERSION "qemu usb-redir guest " QEMU_VERSION
-
 /*
  * Logging stuff
  */
@@ -794,6 +792,10 @@ static void usbredir_open_close_bh(void *opaque)
 {
     USBRedirDevice *dev = opaque;
     uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+    char version[32];
+
+    strcpy(version, "qemu usb-redir guest ");
+    pstrcat(version, sizeof(version), qemu_get_version());
 
     usbredir_device_disconnect(dev);
 
@@ -828,7 +830,7 @@ static void usbredir_open_close_bh(void *opaque)
 
         usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
         usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
-        usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0);
+        usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
         usbredirparser_do_write(dev->parser);
     }
 }
@@ -1031,6 +1033,8 @@ static int usbredir_handle_status(USBRedirDevice *dev,
     case usb_redir_inval:
         WARNING("got invalid param error from usb-host?\n");
         return USB_RET_NAK;
+    case usb_redir_babble:
+        return USB_RET_BABBLE;
     case usb_redir_ioerror:
     case usb_redir_timeout:
     default: