summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure6
-rw-r--r--hw/usb-bt.c4
-rw-r--r--hw/usb-bus.c18
-rw-r--r--hw/usb-ccid.c2
-rw-r--r--hw/usb-desc.c20
-rw-r--r--hw/usb-ehci.c71
-rw-r--r--hw/usb-hid.c3
-rw-r--r--hw/usb-msd.c4
-rw-r--r--hw/usb-net.c4
-rw-r--r--hw/usb-serial.c8
-rw-r--r--hw/usb-uhci.c314
-rw-r--r--hw/usb-xhci.c6
-rw-r--r--hw/usb.c27
-rw-r--r--hw/usb.h7
-rw-r--r--libcacard/vcardt.h4
-rw-r--r--trace-events3
-rw-r--r--usb-bsd.c4
-rw-r--r--usb-linux.c4
-rw-r--r--usb-redir.c46
-rw-r--r--vl.c7
20 files changed, 317 insertions, 245 deletions
diff --git a/configure b/configure
index d9dde969d6..52ec952b63 100755
--- a/configure
+++ b/configure
@@ -2578,8 +2578,8 @@ if test "$smartcard" != "no" ; then
 int main(void) { PK11_FreeSlot(0); return 0; }
 EOF
         smartcard_cflags="-I\$(SRC_PATH)/libcacard"
-        libcacard_libs=$($pkg_config --libs nss 2>/dev/null)
-        libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null)
+        libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
+        libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
         if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \
           compile_prog "$smartcard_cflags $libcacard_cflags" "$libcacard_libs"; then
             smartcard_nss="yes"
@@ -2599,7 +2599,7 @@ fi
 
 # check for usbredirparser for usb network redirection support
 if test "$usb_redir" != "no" ; then
-    if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then
+    if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then
         usb_redir="yes"
         usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
         usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index 649bdcf2d7..23c39ecc23 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev)
     return 0;
 }
 
-USBDevice *usb_bt_init(HCIInfo *hci)
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
 {
     USBDevice *dev;
     struct USBBtState *s;
 
     if (!hci)
         return NULL;
-    dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
+    dev = usb_create_simple(bus, "usb-bt-dongle");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index ae79a4527b..70b7ebc086 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory
 {
     const char *name;
     const char *usbdevice_name;
-    USBDevice *(*usbdevice_init)(const char *params);
+    USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
 } LegacyUSBFactory;
 
 static GSList *legacy_usb_factory;
 
 void usb_legacy_register(const char *typename, const char *usbdevice_name,
-                         USBDevice *(*usbdevice_init)(const char *params))
+                         USBDevice *(*usbdevice_init)(USBBus *bus,
+                                                      const char *params))
 {
     if (usbdevice_name) {
         LegacyUSBFactory *f = g_malloc0(sizeof(*f));
@@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name)
 {
     DeviceState *dev;
 
-#if 1
-    /* temporary stopgap until all usb is properly qdev-ified */
-    if (!bus) {
-        bus = usb_bus_find(-1);
-        if (!bus)
-            return NULL;
-        error_report("%s: no bus specified, using \"%s\" for \"%s\"",
-                __FUNCTION__, bus->qbus.name, name);
-    }
-#endif
-
     dev = qdev_create(&bus->qbus, name);
     return USB_DEVICE(dev);
 }
@@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline)
         }
         return usb_create_simple(bus, f->name);
     }
-    return f->usbdevice_init(params);
+    return f->usbdevice_init(bus, params);
 }
 
 static void usb_device_class_init(ObjectClass *klass, void *data)
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 0b2ac8037a..ce01e343c6 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -447,7 +447,7 @@ static const USBDescDevice desc_device = {
         {
             .bNumInterfaces        = 1,
             .bConfigurationValue   = 1,
-            .bmAttributes          = 0xa0,
+            .bmAttributes          = 0xe0,
             .bMaxPower             = 50,
             .nif = 1,
             .ifs = &desc_iface0,
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 3c3ed6a802..ccf85ade9e 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         break;
 
     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-        data[0] = dev->config->bConfigurationValue;
+        /*
+         * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
+         * the non zero value of bConfigurationValue.
+         */
+        data[0] = dev->config ? dev->config->bConfigurationValue : 0;
         ret = 1;
         break;
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         trace_usb_set_config(dev->addr, value, ret);
         break;
 
-    case DeviceRequest | USB_REQ_GET_STATUS:
+    case DeviceRequest | USB_REQ_GET_STATUS: {
+        const USBDescConfig *config = dev->config ?
+            dev->config : &dev->device->confs[0];
+
         data[0] = 0;
-        if (dev->config->bmAttributes & 0x40) {
+        /*
+         * Default state: Device behavior when this request is received while
+         *                the device is in the Default state is not specified.
+         * We return the same value that a configured device would return if
+         * it used the first configuration.
+         */
+        if (config->bmAttributes & 0x40) {
             data[0] |= 1 << USB_DEVICE_SELF_POWERED;
         }
         if (dev->remote_wakeup) {
@@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         data[1] = 0x00;
         ret = 2;
         break;
+    }
     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
         if (value == USB_DEVICE_REMOTE_WAKEUP) {
             dev->remote_wakeup = 0;
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index e699814305..afc8ccf458 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -912,6 +912,7 @@ static void ehci_reset(void *opaque)
         }
     }
     ehci_queues_rip_all(s);
+    qemu_del_timer(s->frame_timer);
 }
 
 static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
@@ -1070,7 +1071,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
 
         if (val & USBCMD_HCRESET) {
             ehci_reset(s);
-            val &= ~USBCMD_HCRESET;
+            val = s->usbcmd;
         }
 
         /* not supporting dynamic frame list size at the moment */
@@ -1458,44 +1459,22 @@ static int ehci_process_itd(EHCIState *ehci,
 
             dev = ehci_find_device(ehci, devaddr);
             ep = usb_ep_get(dev, pid, endp);
-            usb_packet_setup(&ehci->ipacket, pid, ep);
-            usb_packet_map(&ehci->ipacket, &ehci->isgl);
-
-            ret = usb_handle_packet(dev, &ehci->ipacket);
-
-            usb_packet_unmap(&ehci->ipacket);
-            qemu_sglist_destroy(&ehci->isgl);
-
-#if 0
-            /*  In isoch, there is no facility to indicate a NAK so let's
-             *  instead just complete a zero-byte transaction.  Setting
-             *  DBERR seems too draconian.
-             */
-
-            if (ret == USB_RET_NAK) {
-                if (ehci->isoch_pause > 0) {
-                    DPRINTF("ISOCH: received a NAK but paused so returning\n");
-                    ehci->isoch_pause--;
-                    return 0;
-                } else if (ehci->isoch_pause == -1) {
-                    DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
-                    // Pause frindex for up to 50 msec waiting for data from
-                    // remote
-                    ehci->isoch_pause = 50;
-                    return 0;
-                } else {
-                    DPRINTF("ISOCH: isoch pause timeout! return 0\n");
-                    ret = 0;
-                }
+            if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+                usb_packet_setup(&ehci->ipacket, pid, ep);
+                usb_packet_map(&ehci->ipacket, &ehci->isgl);
+                ret = usb_handle_packet(dev, &ehci->ipacket);
+                assert(ret != USB_RET_ASYNC);
+                usb_packet_unmap(&ehci->ipacket);
             } else {
-                DPRINTF("ISOCH: received ACK, clearing pause\n");
-                ehci->isoch_pause = -1;
+                DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
+                ret = USB_RET_NAK;
             }
-#else
+            qemu_sglist_destroy(&ehci->isgl);
+
             if (ret == USB_RET_NAK) {
+                /* no data for us, so do a zero-length transfer */
                 ret = 0;
             }
-#endif
 
             if (ret >= 0) {
                 if (!dir) {
@@ -1505,11 +1484,27 @@ static int ehci_process_itd(EHCIState *ehci,
                     /* IN */
                     set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
                 }
-
-                if (itd->transact[i] & ITD_XACT_IOC) {
-                    ehci_record_interrupt(ehci, USBSTS_INT);
+            } else {
+                switch (ret) {
+                default:
+                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
+                    /* Fall through */
+                case USB_RET_NODEV:
+                    /* 3.3.2: XACTERR is only allowed on IN transactions */
+                    if (dir) {
+                        itd->transact[i] |= ITD_XACT_XACTERR;
+                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    }
+                    break;
+                case USB_RET_BABBLE:
+                    itd->transact[i] |= ITD_XACT_BABBLE;
+                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    break;
                 }
             }
+            if (itd->transact[i] & ITD_XACT_IOC) {
+                ehci_record_interrupt(ehci, USBSTS_INT);
+            }
             itd->transact[i] &= ~ITD_XACT_ACTIVE;
         }
     }
@@ -2368,8 +2363,6 @@ static int usb_ehci_initfn(PCIDevice *dev)
     memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
 
-    fprintf(stderr, "*** EHCI support is under development ***\n");
-
     return 0;
 }
 
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 7fc0bd81aa..37bca78eca 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -466,6 +466,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_IN:
         if (p->ep->nr == 1) {
             int64_t curtime = qemu_get_clock_ns(vm_clock);
+            if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+                hid_pointer_activate(hs);
+            }
             if (!hid_has_events(hs) &&
                 (!hs->idle || hs->next_idle_clock - curtime > 0)) {
                 return USB_RET_NAK;
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 5fbd2d021b..c6f08a0313 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_msd_init(const char *filename)
+static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
 {
     static int nr=0;
     char id[8];
@@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename)
     }
 
     /* create guest device */
-    dev = usb_create(NULL /* FIXME */, "usb-storage");
+    dev = usb_create(bus, "usb-storage");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 49d5d4db65..22b82017e3 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_net_init(const char *cmdline)
+static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
 {
     USBDevice *dev;
     QemuOpts *opts;
@@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline)
         return NULL;
     }
 
-    dev = usb_create(NULL /* FIXME */, "usb-net");
+    dev = usb_create(bus, "usb-net");
     if (!dev) {
         return NULL;
     }
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 52676e8f7b..0aae379b20 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev)
     return 0;
 }
 
-static USBDevice *usb_serial_init(const char *filename)
+static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 {
     USBDevice *dev;
     CharDriverState *cdrv;
@@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename)
     if (!cdrv)
         return NULL;
 
-    dev = usb_create(NULL /* FIXME */, "usb-serial");
+    dev = usb_create(bus, "usb-serial");
     if (!dev) {
         return NULL;
     }
@@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename)
     return dev;
 }
 
-static USBDevice *usb_braille_init(const char *unused)
+static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
 {
     USBDevice *dev;
     CharDriverState *cdrv;
@@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused)
     if (!cdrv)
         return NULL;
 
-    dev = usb_create(NULL /* FIXME */, "usb-braille");
+    dev = usb_create(bus, "usb-braille");
     qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
     qdev_init_nofail(&dev->qdev);
 
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 2280dc792d..70e3881321 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -95,23 +95,32 @@ static const char *pid2str(int pid)
 #endif
 
 typedef struct UHCIState UHCIState;
+typedef struct UHCIAsync UHCIAsync;
+typedef struct UHCIQueue UHCIQueue;
 
 /* 
  * Pending async transaction.
  * 'packet' must be the first field because completion
  * handler does "(UHCIAsync *) pkt" cast.
  */
-typedef struct UHCIAsync {
+
+struct UHCIAsync {
     USBPacket packet;
     QEMUSGList sgl;
-    UHCIState *uhci;
+    UHCIQueue *queue;
     QTAILQ_ENTRY(UHCIAsync) next;
     uint32_t  td;
-    uint32_t  token;
-    int8_t    valid;
     uint8_t   isoc;
     uint8_t   done;
-} UHCIAsync;
+};
+
+struct UHCIQueue {
+    uint32_t  token;
+    UHCIState *uhci;
+    QTAILQ_ENTRY(UHCIQueue) next;
+    QTAILQ_HEAD(, UHCIAsync) asyncs;
+    int8_t    valid;
+};
 
 typedef struct UHCIPort {
     USBPort port;
@@ -137,7 +146,7 @@ struct UHCIState {
     uint32_t pending_int_mask;
 
     /* Active packets */
-    QTAILQ_HEAD(,UHCIAsync) async_pending;
+    QTAILQ_HEAD(, UHCIQueue) queues;
     uint8_t num_ports_vmstate;
 
     /* Properties */
@@ -157,62 +166,90 @@ typedef struct UHCI_QH {
     uint32_t el_link;
 } UHCI_QH;
 
-static UHCIAsync *uhci_async_alloc(UHCIState *s)
+static inline int32_t uhci_queue_token(UHCI_TD *td)
+{
+    /* covers ep, dev, pid -> identifies the endpoint */
+    return td->token & 0x7ffff;
+}
+
+static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
+
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            return queue;
+        }
+    }
+
+    queue = g_new0(UHCIQueue, 1);
+    queue->uhci = s;
+    queue->token = token;
+    QTAILQ_INIT(&queue->asyncs);
+    QTAILQ_INSERT_HEAD(&s->queues, queue, next);
+    return queue;
+}
+
+static void uhci_queue_free(UHCIQueue *queue)
 {
-    UHCIAsync *async = g_malloc(sizeof(UHCIAsync));
-
-    memset(&async->packet, 0, sizeof(async->packet));
-    async->uhci  = s;
-    async->valid = 0;
-    async->td    = 0;
-    async->token = 0;
-    async->done  = 0;
-    async->isoc  = 0;
+    UHCIState *s = queue->uhci;
+
+    QTAILQ_REMOVE(&s->queues, queue, next);
+    g_free(queue);
+}
+
+static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
+{
+    UHCIAsync *async = g_new0(UHCIAsync, 1);
+
+    async->queue = queue;
     usb_packet_init(&async->packet);
-    pci_dma_sglist_init(&async->sgl, &s->dev, 1);
+    pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
 
     return async;
 }
 
-static void uhci_async_free(UHCIState *s, UHCIAsync *async)
+static void uhci_async_free(UHCIAsync *async)
 {
     usb_packet_cleanup(&async->packet);
     qemu_sglist_destroy(&async->sgl);
     g_free(async);
 }
 
-static void uhci_async_link(UHCIState *s, UHCIAsync *async)
+static void uhci_async_link(UHCIAsync *async)
 {
-    QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
+    UHCIQueue *queue = async->queue;
+    QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
 }
 
-static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
+static void uhci_async_unlink(UHCIAsync *async)
 {
-    QTAILQ_REMOVE(&s->async_pending, async, next);
+    UHCIQueue *queue = async->queue;
+    QTAILQ_REMOVE(&queue->asyncs, async, next);
 }
 
-static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
+static void uhci_async_cancel(UHCIAsync *async)
 {
     DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
            async->td, async->token, async->done);
 
     if (!async->done)
         usb_cancel_packet(&async->packet);
-    uhci_async_free(s, async);
+    uhci_async_free(async);
 }
 
 /*
  * Mark all outstanding async packets as invalid.
  * This is used for canceling them when TDs are removed by the HCD.
  */
-static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
+static void uhci_async_validate_begin(UHCIState *s)
 {
-    UHCIAsync *async;
+    UHCIQueue *queue;
 
-    QTAILQ_FOREACH(async, &s->async_pending, next) {
-        async->valid--;
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        queue->valid--;
     }
-    return NULL;
 }
 
 /*
@@ -220,77 +257,74 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
  */
 static void uhci_async_validate_end(UHCIState *s)
 {
-    UHCIAsync *curr, *n;
+    UHCIQueue *queue, *n;
+    UHCIAsync *async;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        if (curr->valid > 0) {
+    QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
+        if (queue->valid > 0) {
             continue;
         }
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
+        while (!QTAILQ_EMPTY(&queue->asyncs)) {
+            async = QTAILQ_FIRST(&queue->asyncs);
+            uhci_async_unlink(async);
+            uhci_async_cancel(async);
+        }
+        uhci_queue_free(queue);
     }
 }
 
 static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
 {
+    UHCIQueue *queue;
     UHCIAsync *curr, *n;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        if (!usb_packet_is_inflight(&curr->packet) ||
-            curr->packet.ep->dev != dev) {
-            continue;
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            if (!usb_packet_is_inflight(&curr->packet) ||
+                curr->packet.ep->dev != dev) {
+                continue;
+            }
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
         }
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
     }
 }
 
 static void uhci_async_cancel_all(UHCIState *s)
 {
+    UHCIQueue *queue;
     UHCIAsync *curr, *n;
 
-    QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
-        uhci_async_unlink(s, curr);
-        uhci_async_cancel(s, curr);
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+            uhci_async_unlink(curr);
+            uhci_async_cancel(curr);
+        }
     }
 }
 
-static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
 {
+    uint32_t token = uhci_queue_token(td);
+    UHCIQueue *queue;
     UHCIAsync *async;
-    UHCIAsync *match = NULL;
-    int count = 0;
-
-    /*
-     * We're looking for the best match here. ie both td addr and token.
-     * Otherwise we return last good match. ie just token.
-     * It's ok to match just token because it identifies the transaction
-     * rather well, token includes: device addr, endpoint, size, etc.
-     *
-     * Also since we queue async transactions in reverse order by returning
-     * last good match we restores the order.
-     *
-     * It's expected that we wont have a ton of outstanding transactions.
-     * If we ever do we'd want to optimize this algorithm.
-     */
 
-    QTAILQ_FOREACH(async, &s->async_pending, next) {
-        if (async->token == token) {
-            /* Good match */
-            match = async;
-
-            if (async->td == addr) {
-                /* Best match */
-                break;
-            }
+    QTAILQ_FOREACH(queue, &s->queues, next) {
+        if (queue->token == token) {
+            break;
         }
-        count++;
+    }
+    if (queue == NULL) {
+        return NULL;
     }
 
-    if (count > 64)
-	fprintf(stderr, "uhci: warning lots of async transactions\n");
+    QTAILQ_FOREACH(async, &queue->asyncs, next) {
+        if (async->td == addr) {
+            return async;
+        }
+    }
 
-    return match;
+    return NULL;
 }
 
 static void uhci_update_irq(UHCIState *s)
@@ -759,8 +793,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 {
     UHCIAsync *async;
     int len = 0, max_len;
-    uint8_t pid, isoc;
-    uint32_t token;
+    uint8_t pid;
     USBDevice *dev;
     USBEndpoint *ep;
 
@@ -768,41 +801,29 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
     if (!(td->ctrl & TD_CTRL_ACTIVE))
         return 1;
 
-    /* token field is not unique for isochronous requests,
-     * so use the destination buffer 
-     */
-    if (td->ctrl & TD_CTRL_IOS) {
-        token = td->buffer;
-        isoc = 1;
-    } else {
-        token = td->token;
-        isoc = 0;
-    }
-
-    async = uhci_async_find_td(s, addr, token);
+    async = uhci_async_find_td(s, addr, td);
     if (async) {
         /* Already submitted */
-        async->valid = 32;
+        async->queue->valid = 32;
 
         if (!async->done)
             return 1;
 
-        uhci_async_unlink(s, async);
+        uhci_async_unlink(async);
         goto done;
     }
 
     /* Allocate new packet */
-    async = uhci_async_alloc(s);
+    async = uhci_async_alloc(uhci_queue_get(s, td));
     if (!async)
         return 1;
 
     /* valid needs to be large enough to handle 10 frame delay
      * for initial isochronous requests
      */
-    async->valid = 32;
+    async->queue->valid = 32;
     async->td    = addr;
-    async->token = token;
-    async->isoc  = isoc;
+    async->isoc  = td->ctrl & TD_CTRL_IOS;
 
     max_len = ((td->token >> 21) + 1) & 0x7ff;
     pid = td->token & 0xff;
@@ -827,14 +848,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 
     default:
         /* invalid pid : frame interrupted */
-        uhci_async_free(s, async);
+        uhci_async_free(async);
         s->status |= UHCI_STS_HCPERR;
         uhci_update_irq(s);
         return -1;
     }
  
     if (len == USB_RET_ASYNC) {
-        uhci_async_link(s, async);
+        uhci_async_link(async);
         return 2;
     }
 
@@ -843,14 +864,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 done:
     len = uhci_complete_td(s, td, async, int_mask);
     usb_packet_unmap(&async->packet);
-    uhci_async_free(s, async);
+    uhci_async_free(async);
     return len;
 }
 
 static void uhci_async_complete(USBPort *port, USBPacket *packet)
 {
     UHCIAsync *async = container_of(packet, UHCIAsync, packet);
-    UHCIState *s = async->uhci;
+    UHCIState *s = async->queue->uhci;
 
     DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
 
@@ -865,14 +886,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
         le32_to_cpus(&td.token);
         le32_to_cpus(&td.buffer);
 
-        uhci_async_unlink(s, async);
+        uhci_async_unlink(async);
         uhci_complete_td(s, &td, async, &int_mask);
         s->pending_int_mask |= int_mask;
 
         /* update the status bits of the TD */
         val = cpu_to_le32(td.ctrl);
         pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
-        uhci_async_free(s, async);
+        uhci_async_free(async);
     } else {
         async->done = 1;
         uhci_process_frame(s);
@@ -921,6 +942,34 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
     return 0;
 }
 
+static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
+{
+    uint32_t int_mask = 0;
+    uint32_t plink = td->link;
+    uint32_t token = uhci_queue_token(td);
+    UHCI_TD ptd;
+    int ret;
+
+    fprintf(stderr, "%s: -- %x\n", __func__, token);
+    while (is_valid(plink)) {
+        pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
+        le32_to_cpus(&ptd.link);
+        le32_to_cpus(&ptd.ctrl);
+        le32_to_cpus(&ptd.token);
+        le32_to_cpus(&ptd.buffer);
+        if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
+            break;
+        }
+        if (uhci_queue_token(&ptd) != token) {
+            break;
+        }
+        ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+        assert(ret == 2); /* got USB_RET_ASYNC */
+        assert(int_mask == 0);
+        plink = ptd.link;
+    }
+}
+
 static void uhci_process_frame(UHCIState *s)
 {
     uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
@@ -1008,49 +1057,62 @@ static void uhci_process_frame(UHCIState *s)
             pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
         }
 
-        if (ret < 0) {
-            /* interrupted frame */
-            break;
-        }
-
-        if (ret == 2 || ret == 1) {
-            DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
-                    link, ret == 2 ? "pend" : "skip",
-                    td.link, td.ctrl, td.token, curr_qh);
+        switch (ret) {
+        case -1: /* interrupted frame */
+            goto out;
 
+        case 1: /* goto next queue */
+            DPRINTF("uhci: TD 0x%x skip. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
             link = curr_qh ? qh.link : td.link;
             continue;
-        }
 
-        /* completed TD */
+        case 2: /* got USB_RET_ASYNC */
+            DPRINTF("uhci: TD 0x%x async. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
+            if (is_valid(td.link)) {
+                uhci_fill_queue(s, &td);
+            }
+            link = curr_qh ? qh.link : td.link;
+            continue;
 
-        DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
-                link, td.link, td.ctrl, td.token, curr_qh);
+        case 0: /* completed TD */
+            DPRINTF("uhci: TD 0x%x done. "
+                    "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+                    link, td.link, td.ctrl, td.token, curr_qh);
 
-        link = td.link;
-        td_count++;
-        bytes_count += (td.ctrl & 0x7ff) + 1;
+            link = td.link;
+            td_count++;
+            bytes_count += (td.ctrl & 0x7ff) + 1;
 
-        if (curr_qh) {
-	    /* update QH element link */
-            qh.el_link = link;
-            val = cpu_to_le32(qh.el_link);
-            pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
+            if (curr_qh) {
+                /* update QH element link */
+                qh.el_link = link;
+                val = cpu_to_le32(qh.el_link);
+                pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
 
-            if (!depth_first(link)) {
-               /* done with this QH */
+                if (!depth_first(link)) {
+                    /* done with this QH */
 
-               DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
-                       curr_qh, qh.link, qh.el_link);
+                    DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
+                            curr_qh, qh.link, qh.el_link);
 
-               curr_qh = 0;
-               link    = qh.link;
+                    curr_qh = 0;
+                    link    = qh.link;
+                }
             }
+            break;
+
+        default:
+            assert(!"unknown return code");
         }
 
         /* go to the next entry */
     }
 
+out:
     s->pending_int_mask |= int_mask;
 }
 
@@ -1148,7 +1210,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
     }
     s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
     s->num_ports_vmstate = NB_PORTS;
-    QTAILQ_INIT(&s->async_pending);
+    QTAILQ_INIT(&s->queues);
 
     qemu_register_reset(uhci_reset, s);
 
diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c
index 008b0b5718..fc5b542d99 100644
--- a/hw/usb-xhci.c
+++ b/hw/usb-xhci.c
@@ -1769,12 +1769,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
             epctx->retry = xfer;
             break;
         }
-
-        /*
-         * Qemu usb can't handle multiple in-flight xfers.
-         * Stop here for now.
-         */
-        break;
     }
 }
 
diff --git a/hw/usb.c b/hw/usb.c
index e5b8f335dc..57fc5e3cfd 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -26,6 +26,7 @@
 #include "qemu-common.h"
 #include "usb.h"
 #include "iov.h"
+#include "trace.h"
 
 void usb_attach(USBPort *port)
 {
@@ -390,7 +391,6 @@ void usb_packet_init(USBPacket *p)
 
 void usb_packet_set_state(USBPacket *p, USBPacketState state)
 {
-#ifdef DEBUG
     static const char *name[] = {
         [USB_PACKET_UNDEFINED] = "undef",
         [USB_PACKET_SETUP]     = "setup",
@@ -399,28 +399,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
         [USB_PACKET_COMPLETE]  = "complete",
         [USB_PACKET_CANCELED]  = "canceled",
     };
-    static const char *rets[] = {
-        [-USB_RET_NODEV]  = "NODEV",
-        [-USB_RET_NAK]    = "NAK",
-        [-USB_RET_STALL]  = "STALL",
-        [-USB_RET_BABBLE] = "BABBLE",
-        [-USB_RET_ASYNC]  = "ASYNC",
-    };
-    char add[16] = "";
+    USBDevice *dev = p->ep->dev;
+    USBBus *bus = usb_bus_from_device(dev);
 
-    if (state == USB_PACKET_COMPLETE) {
-        if (p->result < 0) {
-            snprintf(add, sizeof(add), " - %s", rets[-p->result]);
-        } else {
-            snprintf(add, sizeof(add), " - %d", p->result);
-        }
-    }
-    fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n",
-            p->ep->dev->qdev.parent_bus->name,
-            p->ep->dev->port->path,
-            p->ep->dev->addr, p->ep->nr,
-            p, name[p->state], name[state], add);
-#endif
+    trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr,
+                                  p, name[p->state], name[state]);
     p->state = state;
 }
 
diff --git a/hw/usb.h b/hw/usb.h
index 4470ea890a..8e83697fb7 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -373,12 +373,12 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
 int set_usb_string(uint8_t *buf, const char *str);
 
 /* usb-linux.c */
-USBDevice *usb_host_device_open(const char *devname);
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
 int usb_host_device_close(const char *devname);
 void usb_host_info(Monitor *mon);
 
 /* usb-bt.c */
-USBDevice *usb_bt_init(HCIInfo *hci);
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci);
 
 /* usb ports of the VM */
 
@@ -431,7 +431,8 @@ struct USBBusOps {
 void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
 USBBus *usb_bus_find(int busnr);
 void usb_legacy_register(const char *typename, const char *usbdevice_name,
-                         USBDevice *(*usbdevice_init)(const char *params));
+                         USBDevice *(*usbdevice_init)(USBBus *bus,
+                                                      const char *params));
 USBDevice *usb_create(USBBus *bus, const char *name);
 USBDevice *usb_create_simple(USBBus *bus, const char *name);
 USBDevice *usbdevice_create(const char *cmdline);
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
index 538bdde3df..d4d8e2ed18 100644
--- a/libcacard/vcardt.h
+++ b/libcacard/vcardt.h
@@ -26,8 +26,8 @@ typedef struct VCardEmulStruct VCardEmul;
 #define MAX_CHANNEL 4
 
 /* create an ATR with appropriate historical bytes */
-#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
-                               'V', 'C', 'A', 'R', 'D', '_'
+#define VCARD_ATR_PREFIX(size) (0x3b, 0x68+(size), 0x00, 0xff, \
+                               'V', 'C', 'A', 'R', 'D', '_')
 
 
 typedef enum {
diff --git a/trace-events b/trace-events
index e918ff61fd..c5d0f0f547 100644
--- a/trace-events
+++ b/trace-events
@@ -227,6 +227,9 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags
 sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
 sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64
 
+# hw/usb.c
+usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s"
+
 # hw/usb-bus.c
 usb_port_claim(int bus, const char *port) "bus %d, port %s"
 usb_port_attach(int bus, const char *port) "bus %d, port %s"
diff --git a/usb-bsd.c b/usb-bsd.c
index 4fa4b42158..ec26266620 100644
--- a/usb-bsd.c
+++ b/usb-bsd.c
@@ -298,7 +298,7 @@ static int usb_host_initfn(USBDevice *dev)
     return 0;
 }
 
-USBDevice *usb_host_device_open(const char *devname)
+USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
 {
     struct usb_device_info bus_info, dev_info;
     USBDevice *d = NULL, *ret = NULL;
@@ -358,7 +358,7 @@ USBDevice *usb_host_device_open(const char *devname)
         goto fail_dfd;
     }
 
-    d = usb_create(NULL /* FIXME */, "usb-host");
+    d = usb_create(guest_bus, "usb-host");
     dev = DO_UPCAST(USBHostDevice, dev, d);
 
     if (dev_info.udi_speed == 1) {
diff --git a/usb-linux.c b/usb-linux.c
index 24700fc1f7..47994f3ceb 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -1443,13 +1443,13 @@ static void usb_host_register_types(void)
 
 type_init(usb_host_register_types)
 
-USBDevice *usb_host_device_open(const char *devname)
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
 {
     struct USBAutoFilter filter;
     USBDevice *dev;
     char *p;
 
-    dev = usb_create(NULL /* FIXME */, "usb-host");
+    dev = usb_create(bus, "usb-host");
 
     if (strstr(devname, "auto:")) {
         if (parse_filter(devname, &filter) < 0) {
diff --git a/usb-redir.c b/usb-redir.c
index a59b347f01..755492f379 100644
--- a/usb-redir.c
+++ b/usb-redir.c
@@ -106,6 +106,7 @@ struct AsyncURB {
     QTAILQ_ENTRY(AsyncURB)next;
 };
 
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
 static void usbredir_device_connect(void *priv,
     struct usb_redir_device_connect_header *device_connect);
 static void usbredir_device_disconnect(void *priv);
@@ -430,7 +431,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
             /* Check iso_error for stream errors, otherwise its an underrun */
             status = dev->endpoint[EP2I(ep)].iso_error;
             dev->endpoint[EP2I(ep)].iso_error = 0;
-            return usbredir_handle_status(dev, status, 0);
+            return status ? USB_RET_NAK : 0;
         }
         DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
                  isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
@@ -438,7 +439,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         status = isop->status;
         if (status != usb_redir_success) {
             bufp_free(dev, isop, ep);
-            return usbredir_handle_status(dev, status, 0);
+            return USB_RET_NAK;
         }
 
         len = isop->len;
@@ -547,7 +548,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
             /* Check interrupt_error for stream errors */
             status = dev->endpoint[EP2I(ep)].interrupt_error;
             dev->endpoint[EP2I(ep)].interrupt_error = 0;
-            return usbredir_handle_status(dev, status, 0);
+            if (status) {
+                return usbredir_handle_status(dev, status, 0);
+            }
+            return USB_RET_NAK;
         }
         DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
                 intp->status, intp->len);
@@ -802,6 +806,7 @@ static void usbredir_open_close_bh(void *opaque)
         dev->parser->log_func = usbredir_log;
         dev->parser->read_func = usbredir_read;
         dev->parser->write_func = usbredir_write;
+        dev->parser->hello_func = usbredir_hello;
         dev->parser->device_connect_func = usbredir_device_connect;
         dev->parser->device_disconnect_func = usbredir_device_disconnect;
         dev->parser->interface_info_func = usbredir_interface_info;
@@ -820,6 +825,7 @@ static void usbredir_open_close_bh(void *opaque)
         dev->read_buf_size = 0;
 
         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_do_write(dev->parser);
     }
@@ -958,7 +964,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
 {
     if (dev->interface_info.interface_count == 0) {
         ERROR("No interface info for device\n");
-        return -1;
+        goto error;
     }
 
     if (dev->filter_rules) {
@@ -966,7 +972,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
                                     usb_redir_cap_connect_device_version)) {
             ERROR("Device filter specified and peer does not have the "
                   "connect_device_version capability\n");
-            return -1;
+            goto error;
         }
 
         if (usbredirfilter_check(
@@ -983,11 +989,19 @@ static int usbredir_check_filter(USBRedirDevice *dev)
                 dev->device_info.product_id,
                 dev->device_info.device_version_bcd,
                 0) != 0) {
-            return -1;
+            goto error;
         }
     }
 
     return 0;
+
+error:
+    usbredir_device_disconnect(dev);
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+        usbredirparser_send_filter_reject(dev->parser);
+        usbredirparser_do_write(dev->parser);
+    }
+    return -1;
 }
 
 /*
@@ -1012,6 +1026,19 @@ static int usbredir_handle_status(USBRedirDevice *dev,
     }
 }
 
+static void usbredir_hello(void *priv, struct usb_redir_hello_header *h)
+{
+    USBRedirDevice *dev = priv;
+
+    /* Try to send the filter info now that we've the usb-host's caps */
+    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) &&
+            dev->filter_rules) {
+        usbredirparser_send_filter_filter(dev->parser, dev->filter_rules,
+                                          dev->filter_rules_count);
+        usbredirparser_do_write(dev->parser);
+    }
+}
+
 static void usbredir_device_connect(void *priv,
     struct usb_redir_device_connect_header *device_connect)
 {
@@ -1049,8 +1076,10 @@ static void usbredir_device_connect(void *priv,
                                     usb_redir_cap_connect_device_version)) {
         INFO("attaching %s device %04x:%04x version %d.%d class %02x\n",
              speed, device_connect->vendor_id, device_connect->product_id,
-             device_connect->device_version_bcd >> 8,
-             device_connect->device_version_bcd & 0xff,
+             ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 +
+             ((device_connect->device_version_bcd & 0x0f00) >>  8),
+             ((device_connect->device_version_bcd & 0x00f0) >>  4) * 10 +
+             ((device_connect->device_version_bcd & 0x000f) >>  0),
              device_connect->device_class);
     } else {
         INFO("attaching %s device %04x:%04x class %02x\n", speed,
@@ -1111,7 +1140,6 @@ static void usbredir_interface_info(void *priv,
         if (usbredir_check_filter(dev)) {
             ERROR("Device no longer matches filter after interface info "
                   "change, disconnecting!\n");
-            usbredir_device_disconnect(dev);
         }
     }
 }
diff --git a/vl.c b/vl.c
index 1d4c3500a9..4a77696374 100644
--- a/vl.c
+++ b/vl.c
@@ -1052,12 +1052,13 @@ static int usb_device_add(const char *devname)
 #ifndef CONFIG_LINUX
     /* only the linux version is qdev-ified, usb-bsd still needs this */
     if (strstart(devname, "host:", &p)) {
-        dev = usb_host_device_open(p);
+        dev = usb_host_device_open(usb_bus_find(-1), p);
     } else
 #endif
     if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
-        dev = usb_bt_init(devname[2] ? hci_init(p) :
-                        bt_new_hci(qemu_find_bt_vlan(0)));
+        dev = usb_bt_init(usb_bus_find(-1),
+                          devname[2] ? hci_init(p)
+                                     : bt_new_hci(qemu_find_bt_vlan(0)));
     } else {
         return -1;
     }