qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 0/7] usb patch queue
@ 2013-04-16 10:13 Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 1/7] usb-serial: Remove double call to qemu_chr_add_handlers( NULL ) Gerd Hoffmann
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

Here comes the usb patch queue.  Big new feature is the libusb-based
usb-host driver.  There also is a usb-serial fix and a small collection
of xhci bugfixes.

please pull,
  Gerd

The following changes since commit 24a6e7f4d91e9ed5f8117ecb083431a23f8609a0:

  virtio-balloon: fix dynamic properties. (2013-04-15 17:06:58 -0500)

are available in the git repository at:

  git://git.kraxel.org/qemu usb.80

for you to fetch changes up to 2b2325ff6491224a42e1fec99b1c39fbc521c95c:

  use libusb for usb-host (2013-04-16 12:04:09 +0200)

----------------------------------------------------------------
Gerd Hoffmann (6):
      xhci: remove leftover debug printf
      xhci: add xhci_cap_write
      xhci: fix portsc writes
      xhci: use slotid as device address
      xhci: fix address device
      use libusb for usb-host

Hans de Goede (1):
      usb-serial: Remove double call to qemu_chr_add_handlers( NULL )

 configure            |   36 ++
 hw/usb/dev-serial.c  |    9 -
 hw/usb/hcd-xhci.c    |   79 +--
 hw/usb/host-libusb.c | 1449 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/host-linux.c  |   14 +-
 trace-events         |    5 +
 6 files changed, 1551 insertions(+), 41 deletions(-)
 create mode 100644 hw/usb/host-libusb.c

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 1/7] usb-serial: Remove double call to qemu_chr_add_handlers( NULL )
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 2/7] xhci: remove leftover debug printf Gerd Hoffmann
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hans de Goede, Gerd Hoffmann

From: Hans de Goede <hdegoede@redhat.com>

usb-serial has a qdev chardev property, and hw/qdev-properties-system.c
already contains:

static void release_chr(Object *obj, const char *name, void *opaque)
{
    DeviceState *dev = DEVICE(obj);
    Property *prop = opaque;
    CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
    CharDriverState *chr = *ptr;

    if (chr) {
        qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
        qemu_chr_fe_release(chr);
    }
}

So doing the qemu_chr_add_handlers(s->cs, NULL, NULL, NULL, NULL); from
the usb handle_destroy function too will lead to it being done twice.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/dev-serial.c |    9 ---------
 1 file changed, 9 deletions(-)

diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index dd0a608..2fc8a3b 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -410,13 +410,6 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
     }
 }
 
-static void usb_serial_handle_destroy(USBDevice *dev)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-
-    qemu_chr_add_handlers(s->cs, NULL, NULL, NULL, NULL);
-}
-
 static int usb_serial_can_read(void *opaque)
 {
     USBSerialState *s = opaque;
@@ -595,7 +588,6 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data)
     uc->handle_reset   = usb_serial_handle_reset;
     uc->handle_control = usb_serial_handle_control;
     uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
     dc->vmsd = &vmstate_usb_serial;
     dc->props = serial_properties;
 }
@@ -623,7 +615,6 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data)
     uc->handle_reset   = usb_serial_handle_reset;
     uc->handle_control = usb_serial_handle_control;
     uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
     dc->vmsd = &vmstate_usb_serial;
     dc->props = braille_properties;
 }
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 2/7] xhci: remove leftover debug printf
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 1/7] usb-serial: Remove double call to qemu_chr_add_handlers( NULL ) Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 3/7] xhci: add xhci_cap_write Gerd Hoffmann
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-xhci.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index efd4b0d..c0dbc54 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2526,7 +2526,6 @@ static void xhci_process_commands(XHCIState *xhci)
             }
             break;
         case CR_SET_TR_DEQUEUE:
-            fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__);
             slotid = xhci_get_slot(xhci, &event, &trb);
             if (slotid) {
                 unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 3/7] xhci: add xhci_cap_write
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 1/7] usb-serial: Remove double call to qemu_chr_add_handlers( NULL ) Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 2/7] xhci: remove leftover debug printf Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 4/7] xhci: fix portsc writes Gerd Hoffmann
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-xhci.c |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index c0dbc54..7f740d9 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -3079,8 +3079,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
     }
 }
 
+static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val,
+                           unsigned width)
+{
+    /* nothing */
+}
+
 static const MemoryRegionOps xhci_cap_ops = {
     .read = xhci_cap_read,
+    .write = xhci_cap_write,
     .valid.min_access_size = 1,
     .valid.max_access_size = 4,
     .impl.min_access_size = 4,
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 4/7] xhci: fix portsc writes
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 3/7] xhci: add xhci_cap_write Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 5/7] xhci: use slotid as device address Gerd Hoffmann
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Check for port reset first and skip everything else then.
Add sanity checks for PLS updates.
Add PLC notification when entering PLS_U0 state.

This gets host-initiated port resume going on win8.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-xhci.c |   42 +++++++++++++++++++++++++++++++++++-------
 trace-events      |    1 +
 2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 7f740d9..bb0cf1e 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2592,6 +2592,7 @@ static void xhci_port_notify(XHCIPort *port, uint32_t bits)
     if ((port->portsc & bits) == bits) {
         return;
     }
+    trace_usb_xhci_port_notify(port->portnr, bits);
     port->portsc |= bits;
     if (!xhci_running(port->xhci)) {
         return;
@@ -2798,29 +2799,56 @@ static void xhci_port_write(void *ptr, hwaddr reg,
                             uint64_t val, unsigned size)
 {
     XHCIPort *port = ptr;
-    uint32_t portsc;
+    uint32_t portsc, notify;
 
     trace_usb_xhci_port_write(port->portnr, reg, val);
 
     switch (reg) {
     case 0x00: /* PORTSC */
+        /* write-1-to-start bits */
+        if (val & PORTSC_PR) {
+            xhci_port_reset(port);
+            break;
+        }
+
         portsc = port->portsc;
+        notify = 0;
         /* write-1-to-clear bits*/
         portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
                            PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
         if (val & PORTSC_LWS) {
             /* overwrite PLS only when LWS=1 */
-            uint32_t pls = get_field(val, PORTSC_PLS);
-            set_field(&portsc, pls, PORTSC_PLS);
-            trace_usb_xhci_port_link(port->portnr, pls);
+            uint32_t old_pls = get_field(port->portsc, PORTSC_PLS);
+            uint32_t new_pls = get_field(val, PORTSC_PLS);
+            switch (new_pls) {
+            case PLS_U0:
+                if (old_pls != PLS_U0) {
+                    set_field(&portsc, new_pls, PORTSC_PLS);
+                    trace_usb_xhci_port_link(port->portnr, new_pls);
+                    notify = PORTSC_PLC;
+                }
+                break;
+            case PLS_U3:
+                if (old_pls < PLS_U3) {
+                    set_field(&portsc, new_pls, PORTSC_PLS);
+                    trace_usb_xhci_port_link(port->portnr, new_pls);
+                }
+                break;
+            case PLS_RESUME:
+                /* windows does this for some reason, don't spam stderr */
+                break;
+            default:
+                fprintf(stderr, "%s: ignore pls write (old %d, new %d)\n",
+                        __func__, old_pls, new_pls);
+                break;
+            }
         }
         /* read/write bits */
         portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
         portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
         port->portsc = portsc;
-        /* write-1-to-start bits */
-        if (val & PORTSC_PR) {
-            xhci_port_reset(port);
+        if (notify) {
+            xhci_port_notify(port, notify);
         }
         break;
     case 0x04: /* PORTPMSC */
diff --git a/trace-events b/trace-events
index 412f7e4..54b7d90 100644
--- a/trace-events
+++ b/trace-events
@@ -362,6 +362,7 @@ usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char
 usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
 usb_xhci_port_reset(uint32_t port) "port %d"
 usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
+usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x"
 usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
 usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
 usb_xhci_slot_address(uint32_t slotid) "slotid %d"
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 5/7] xhci: use slotid as device address
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 4/7] xhci: fix portsc writes Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 6/7] xhci: fix address device Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 7/7] use libusb for usb-host Gerd Hoffmann
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Is good enougth for unique device addresses and avoids the need for any
state for device addressing.  Makes live migration support easier.  Also
makes device->slot lookups trivial.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-xhci.c |   25 +++----------------------
 1 file changed, 3 insertions(+), 22 deletions(-)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index bb0cf1e..e489059 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -408,7 +408,6 @@ typedef struct XHCISlot {
     bool enabled;
     dma_addr_t ctx;
     USBPort *uport;
-    unsigned int devaddr;
     XHCIEPContext * eps[31];
 } XHCISlot;
 
@@ -452,7 +451,6 @@ struct XHCIState {
     MemoryRegion mem_oper;
     MemoryRegion mem_runtime;
     MemoryRegion mem_doorbell;
-    unsigned int devaddr;
 
     /* properties */
     uint32_t numports_2;
@@ -2141,16 +2139,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
         slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
     } else {
         USBPacket p;
-        slot->devaddr = xhci->devaddr++;
-        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
-        DPRINTF("xhci: device address is %d\n", slot->devaddr);
+        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
         usb_device_reset(dev);
         usb_packet_setup(&p, USB_TOKEN_OUT,
                          usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
                          0, false, false);
         usb_device_handle_control(dev, &p,
                                   DeviceOutRequest | USB_REQ_SET_ADDRESS,
-                                  slot->devaddr, 0, 0, NULL);
+                                  slotid, 0, 0, NULL);
         assert(p.status != USB_RET_ASYNC);
     }
 
@@ -2674,7 +2670,6 @@ static void xhci_reset(DeviceState *dev)
     xhci->dcbaap_low = 0;
     xhci->dcbaap_high = 0;
     xhci->config = 0;
-    xhci->devaddr = 2;
 
     for (i = 0; i < xhci->numslots; i++) {
         xhci_disable_slot(xhci, i+1);
@@ -3212,20 +3207,6 @@ static USBPortOps xhci_uport_ops = {
     .child_detach = xhci_child_detach,
 };
 
-static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
-{
-    XHCISlot *slot;
-    int slotid;
-
-    for (slotid = 1; slotid <= xhci->numslots; slotid++) {
-        slot = &xhci->slots[slotid-1];
-        if (slot->devaddr == dev->addr) {
-            return slotid;
-        }
-    }
-    return 0;
-}
-
 static int xhci_find_epid(USBEndpoint *ep)
 {
     if (ep->nr == 0) {
@@ -3245,7 +3226,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
     int slotid;
 
     DPRINTF("%s\n", __func__);
-    slotid = xhci_find_slotid(xhci, ep->dev);
+    slotid = ep->dev->addr;
     if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
         DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
         return;
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 6/7] xhci: fix address device
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
                   ` (4 preceding siblings ...)
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 5/7] xhci: use slotid as device address Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 7/7] use libusb for usb-host Gerd Hoffmann
  6 siblings, 0 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Zero-initialize the set-address dummy USBPacket,
also add buffer to avoid sanity checks triggering.

https://bugzilla.redhat.com/show_bug.cgi?id=929019

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-xhci.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index e489059..a26b78e 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2139,8 +2139,12 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
         slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
     } else {
         USBPacket p;
+        uint8_t buf[1];
+
         slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
         usb_device_reset(dev);
+        memset(&p, 0, sizeof(p));
+        usb_packet_addbuf(&p, buf, sizeof(buf));
         usb_packet_setup(&p, USB_TOKEN_OUT,
                          usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
                          0, false, false);
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 7/7] use libusb for usb-host
  2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
                   ` (5 preceding siblings ...)
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 6/7] xhci: fix address device Gerd Hoffmann
@ 2013-04-16 10:13 ` Gerd Hoffmann
  2013-04-18 13:07   ` Markus Armbruster
  6 siblings, 1 reply; 10+ messages in thread
From: Gerd Hoffmann @ 2013-04-16 10:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Reimplement usb-host on top of libusb.
Reasons to do this:

 (1) Largely rewritten from scratch, nice opportunity to kill historical
     cruft.
 (2) Offload usbfs handling to libusb.
 (3) Have a single portable code base instead of bsd + linux variants.
 (4) Bring usb-host support to any platform supported by libusbx.

For now this goes side-by-side to the existing code.  That is only to
simplify regression testing though, at the end of the day I want remove
the old code and support libusb exclusively.  Merge early in 1.5 cycle,
remove the old code after 1.5 release or something like this.

Thanks to qdev the old and new code can coexist nicely on linux.  Just
use "-device usb-host-linux" to use the old linux driver instead of the
libusb one (which takes over the "usb-host" name).

The bsd driver isn't qdev'ified so it isn't that easy for bsd.
I didn't bother making it runtime switchable, so you have to rebuild
qemu with --disable-libusb to get back the old code.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 configure            |   36 ++
 hw/usb/host-libusb.c | 1449 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/host-linux.c  |   14 +-
 trace-events         |    4 +
 4 files changed, 1501 insertions(+), 2 deletions(-)
 create mode 100644 hw/usb/host-libusb.c

diff --git a/configure b/configure
index 0788e27..4c4f6f6 100755
--- a/configure
+++ b/configure
@@ -226,6 +226,7 @@ trace_file="trace"
 spice=""
 rbd=""
 smartcard_nss=""
+libusb=""
 usb_redir=""
 glx=""
 zlib="yes"
@@ -890,6 +891,10 @@ for opt do
   ;;
   --enable-smartcard-nss) smartcard_nss="yes"
   ;;
+  --disable-libusb) libusb="no"
+  ;;
+  --enable-libusb) libusb="yes"
+  ;;
   --disable-usb-redir) usb_redir="no"
   ;;
   --enable-usb-redir) usb_redir="yes"
@@ -1175,6 +1180,8 @@ echo "  --disable-libiscsi       disable iscsi support"
 echo "  --enable-libiscsi        enable iscsi support"
 echo "  --disable-smartcard-nss  disable smartcard nss support"
 echo "  --enable-smartcard-nss   enable smartcard nss support"
+echo "  --disable-libusb         disable libusb (for usb passthrough)"
+echo "  --enable-libusb          enable libusb (for usb passthrough)"
 echo "  --disable-usb-redir      disable usb network redirection support"
 echo "  --enable-usb-redir       enable usb network redirection support"
 echo "  --disable-guest-agent    disable building of the QEMU Guest Agent"
@@ -3005,6 +3012,23 @@ EOF
     fi
 fi
 
+# check for libusb
+if test "$libusb" != "no" ; then
+    if $pkg_config libusb-1.0 >/dev/null 2>&1 ; then
+        libusb="yes"
+	usb="libusb"
+        libusb_cflags=$($pkg_config --cflags libusb-1.0 2>/dev/null)
+        libusb_libs=$($pkg_config --libs libusb-1.0 2>/dev/null)
+        QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags"
+        libs_softmmu="$libs_softmmu $libusb_libs"
+    else
+        if test "$libusb" = "yes"; then
+            feature_not_found "libusb"
+        fi
+        libusb="no"
+    fi
+fi
+
 # check for usbredirparser for usb network redirection support
 if test "$usb_redir" != "no" ; then
     if $pkg_config --atleast-version=0.6 libusbredirparser-0.5 >/dev/null 2>&1 ; then
@@ -3516,6 +3540,7 @@ echo "spice support     $spice ($spice_protocol_version/$spice_server_version)"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
 echo "nss used          $smartcard_nss"
+echo "libusb            $libusb"
 echo "usb net redir     $usb_redir"
 echo "GLX support       $glx"
 echo "libiscsi support  $libiscsi"
@@ -3823,6 +3848,10 @@ if test "$smartcard_nss" = "yes" ; then
   echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
 fi
 
+if test "$libusb" = "yes" ; then
+  echo "CONFIG_USB_LIBUSB=y" >> $config_host_mak
+fi
+
 if test "$usb_redir" = "yes" ; then
   echo "CONFIG_USB_REDIR=y" >> $config_host_mak
 fi
@@ -3907,6 +3936,13 @@ linux)
 bsd)
   echo "HOST_USB=bsd" >> $config_host_mak
 ;;
+libusb)
+  if test "$linux" = "yes"; then
+    echo "HOST_USB=libusb linux legacy" >> $config_host_mak
+  else
+    echo "HOST_USB=libusb legacy" >> $config_host_mak
+  fi
+;;
 *)
   echo "HOST_USB=stub" >> $config_host_mak
 ;;
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
new file mode 100644
index 0000000..29f35b3
--- /dev/null
+++ b/hw/usb/host-libusb.c
@@ -0,0 +1,1449 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * (c) 2012 Gerd Hoffmann <kraxel@redhat.com>
+ *      Completely rewritten to use libusb instead of usbfs ioctls.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <poll.h>
+#include <libusb.h>
+
+#include "qemu-common.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+#include "hw/usb.h"
+
+/* ------------------------------------------------------------------------ */
+
+#define TYPE_USB_HOST_DEVICE "usb-host"
+#define USB_HOST_DEVICE(obj) \
+     OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE)
+
+typedef struct USBHostDevice USBHostDevice;
+typedef struct USBHostRequest USBHostRequest;
+typedef struct USBHostIsoXfer USBHostIsoXfer;
+typedef struct USBHostIsoRing USBHostIsoRing;
+
+struct USBAutoFilter {
+    uint32_t bus_num;
+    uint32_t addr;
+    char     *port;
+    uint32_t vendor_id;
+    uint32_t product_id;
+};
+
+enum USBHostDeviceOptions {
+    USB_HOST_OPT_PIPELINE,
+};
+
+struct USBHostDevice {
+    USBDevice parent_obj;
+
+    /* properties */
+    struct USBAutoFilter             match;
+    int32_t                          bootindex;
+    uint32_t                         iso_urb_count;
+    uint32_t                         iso_urb_frames;
+    uint32_t                         options;
+    uint32_t                         loglevel;
+
+    /* state */
+    QTAILQ_ENTRY(USBHostDevice)      next;
+    int                              seen, errcount;
+    int                              bus_num;
+    int                              addr;
+    char                             port[16];
+
+    libusb_device                    *dev;
+    libusb_device_handle             *dh;
+    struct libusb_device_descriptor  ddesc;
+
+    struct {
+        bool                         detached;
+        bool                         claimed;
+    } ifs[USB_MAX_INTERFACES];
+
+    /* callbacks & friends */
+    QEMUBH                           *bh;
+    Notifier                         exit;
+
+    /* request queues */
+    QTAILQ_HEAD(, USBHostRequest)    requests;
+    QTAILQ_HEAD(, USBHostIsoRing)    isorings;
+};
+
+struct USBHostRequest {
+    USBHostDevice                    *host;
+    USBPacket                        *p;
+    bool                             in;
+    struct libusb_transfer           *xfer;
+    unsigned char                    *buffer;
+    unsigned char                    *cbuf;
+    unsigned int                     clen;
+    QTAILQ_ENTRY(USBHostRequest)     next;
+};
+
+struct USBHostIsoXfer {
+    USBHostIsoRing                   *ring;
+    struct libusb_transfer           *xfer;
+    bool                             copy_complete;
+    unsigned int                     packet;
+    QTAILQ_ENTRY(USBHostIsoXfer)     next;
+};
+
+struct USBHostIsoRing {
+    USBHostDevice                    *host;
+    USBEndpoint                      *ep;
+    QTAILQ_HEAD(, USBHostIsoXfer)    unused;
+    QTAILQ_HEAD(, USBHostIsoXfer)    inflight;
+    QTAILQ_HEAD(, USBHostIsoXfer)    copy;
+    QTAILQ_ENTRY(USBHostIsoRing)     next;
+};
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs =
+    QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+static void usb_host_auto_check(void *unused);
+static void usb_host_release_interfaces(USBHostDevice *s);
+static void usb_host_nodev(USBHostDevice *s);
+static void usb_host_attach_kernel(USBHostDevice *s);
+
+/* ------------------------------------------------------------------------ */
+
+#define CONTROL_TIMEOUT  10000        /* 10 sec    */
+#define BULK_TIMEOUT         0        /* unlimited */
+#define INTR_TIMEOUT         0        /* unlimited */
+
+static const char *speed_name[] = {
+    [LIBUSB_SPEED_UNKNOWN] = "?",
+    [LIBUSB_SPEED_LOW]     = "1.5",
+    [LIBUSB_SPEED_FULL]    = "12",
+    [LIBUSB_SPEED_HIGH]    = "480",
+    [LIBUSB_SPEED_SUPER]   = "5000",
+};
+
+static const unsigned int speed_map[] = {
+    [LIBUSB_SPEED_LOW]     = USB_SPEED_LOW,
+    [LIBUSB_SPEED_FULL]    = USB_SPEED_FULL,
+    [LIBUSB_SPEED_HIGH]    = USB_SPEED_HIGH,
+    [LIBUSB_SPEED_SUPER]   = USB_SPEED_SUPER,
+};
+
+static const unsigned int status_map[] = {
+    [LIBUSB_TRANSFER_COMPLETED] = USB_RET_SUCCESS,
+    [LIBUSB_TRANSFER_ERROR]     = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_TIMED_OUT] = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_CANCELLED] = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_STALL]     = USB_RET_STALL,
+    [LIBUSB_TRANSFER_NO_DEVICE] = USB_RET_NODEV,
+    [LIBUSB_TRANSFER_OVERFLOW]  = USB_RET_BABBLE,
+};
+
+static const char *err_names[] = {
+    [-LIBUSB_ERROR_IO]               = "IO",
+    [-LIBUSB_ERROR_INVALID_PARAM]    = "INVALID_PARAM",
+    [-LIBUSB_ERROR_ACCESS]           = "ACCESS",
+    [-LIBUSB_ERROR_NO_DEVICE]        = "NO_DEVICE",
+    [-LIBUSB_ERROR_NOT_FOUND]        = "NOT_FOUND",
+    [-LIBUSB_ERROR_BUSY]             = "BUSY",
+    [-LIBUSB_ERROR_TIMEOUT]          = "TIMEOUT",
+    [-LIBUSB_ERROR_OVERFLOW]         = "OVERFLOW",
+    [-LIBUSB_ERROR_PIPE]             = "PIPE",
+    [-LIBUSB_ERROR_INTERRUPTED]      = "INTERRUPTED",
+    [-LIBUSB_ERROR_NO_MEM]           = "NO_MEM",
+    [-LIBUSB_ERROR_NOT_SUPPORTED]    = "NOT_SUPPORTED",
+    [-LIBUSB_ERROR_OTHER]            = "OTHER",
+};
+
+static libusb_context *ctx;
+static uint32_t loglevel;
+
+static void usb_host_handle_fd(void *opaque)
+{
+    struct timeval tv = { 0, 0 };
+    libusb_handle_events_timeout(ctx, &tv);
+}
+
+static void usb_host_add_fd(int fd, short events, void *user_data)
+{
+    qemu_set_fd_handler(fd,
+                        (events & POLLIN)  ? usb_host_handle_fd : NULL,
+                        (events & POLLOUT) ? usb_host_handle_fd : NULL,
+                        ctx);
+}
+
+static void usb_host_del_fd(int fd, void *user_data)
+{
+    qemu_set_fd_handler(fd, NULL, NULL, NULL);
+}
+
+static int usb_host_init(void)
+{
+    const struct libusb_pollfd **poll;
+    int i, rc;
+
+    if (ctx) {
+        return 0;
+    }
+    rc = libusb_init(&ctx);
+    if (rc != 0) {
+        return -1;
+    }
+    libusb_set_debug(ctx, loglevel);
+
+    libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
+                                usb_host_del_fd,
+                                ctx);
+    poll = libusb_get_pollfds(ctx);
+    if (poll) {
+        for (i = 0; poll[i] != NULL; i++) {
+            usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
+        }
+    }
+    free(poll);
+    return 0;
+}
+
+static int usb_host_get_port(libusb_device *dev, char *port, size_t len)
+{
+#if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x010000ff)
+    /* have libusb_get_port_path() */
+    uint8_t path[7];
+    size_t off;
+    int rc, i;
+
+    rc = libusb_get_port_path(ctx, dev, path, 7);
+    if (rc < 0) {
+        return 0;
+    }
+    off = snprintf(port, len, "%d", path[0]);
+    for (i = 1; i < rc; i++) {
+        off += snprintf(port+off, len-off, ".%d", path[i]);
+    }
+    return off;
+#else
+    return snprintf(port, len, "FIXME");
+#endif
+}
+
+static void usb_host_libusb_error(const char *func, int rc)
+{
+    const char *errname;
+
+    if (rc >= 0) {
+        return;
+    }
+
+    if (-rc < ARRAY_SIZE(err_names) && err_names[-rc]) {
+        errname = err_names[-rc];
+    } else {
+        errname = "?";
+    }
+    fprintf(stderr, "%s: %d [%s]\n", func, rc, errname);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static bool usb_host_use_combining(USBEndpoint *ep)
+{
+    int type;
+
+    if (!ep->pipeline) {
+        return false;
+    }
+    if (ep->pid != USB_TOKEN_IN) {
+        return false;
+    }
+    type = usb_ep_get_type(ep->dev, ep->pid, ep->nr);
+    if (type != USB_ENDPOINT_XFER_BULK) {
+        return false;
+    }
+    return true;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p,
+                                          bool in, size_t bufsize)
+{
+    USBHostRequest *r = g_new0(USBHostRequest, 1);
+
+    r->host = s;
+    r->p = p;
+    r->in = in;
+    r->xfer = libusb_alloc_transfer(0);
+    if (bufsize) {
+        r->buffer = g_malloc(bufsize);
+    }
+    QTAILQ_INSERT_TAIL(&s->requests, r, next);
+    return r;
+}
+
+static void usb_host_req_free(USBHostRequest *r)
+{
+    if (r->host) {
+        QTAILQ_REMOVE(&r->host->requests, r, next);
+    }
+    libusb_free_transfer(r->xfer);
+    g_free(r->buffer);
+    g_free(r);
+}
+
+static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p)
+{
+    USBHostRequest *r;
+
+    QTAILQ_FOREACH(r, &s->requests, next) {
+        if (r->p == p) {
+            return r;
+        }
+    }
+    return NULL;
+}
+
+static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice  *s = r->host;
+    bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+
+    if (r->p == NULL) {
+        goto out; /* request was canceled */
+    }
+
+    r->p->status = status_map[xfer->status];
+    r->p->actual_length = xfer->actual_length;
+    if (r->in && xfer->actual_length) {
+        memcpy(r->cbuf, r->buffer + 8, xfer->actual_length);
+    }
+    trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                r->p->status, r->p->actual_length);
+    usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
+
+out:
+    usb_host_req_free(r);
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_req_complete_data(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice  *s = r->host;
+    bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+
+    if (r->p == NULL) {
+        goto out; /* request was canceled */
+    }
+
+    r->p->status = status_map[xfer->status];
+    if (r->in && xfer->actual_length) {
+        usb_packet_copy(r->p, r->buffer, xfer->actual_length);
+    }
+    trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                r->p->status, r->p->actual_length);
+    if (usb_host_use_combining(r->p->ep)) {
+        usb_combined_input_packet_complete(USB_DEVICE(s), r->p);
+    } else {
+        usb_packet_complete(USB_DEVICE(s), r->p);
+    }
+
+out:
+    usb_host_req_free(r);
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_req_abort(USBHostRequest *r)
+{
+    USBHostDevice  *s = r->host;
+    bool inflight = (r->p && r->p->state == USB_RET_ASYNC);
+
+    if (inflight) {
+        r->p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                    r->p->status, r->p->actual_length);
+        if (r->p->ep->nr == 0) {
+            usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
+        } else {
+            usb_packet_complete(USB_DEVICE(s), r->p);
+        }
+        r->p = NULL;
+    }
+
+    QTAILQ_REMOVE(&r->host->requests, r, next);
+    r->host = NULL;
+
+    if (inflight) {
+        libusb_cancel_transfer(r->xfer);
+    }
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
+{
+    USBHostIsoXfer *xfer = transfer->user_data;
+
+    if (!xfer) {
+        /* USBHostIsoXfer released while inflight */
+        g_free(transfer->buffer);
+        libusb_free_transfer(transfer);
+        return;
+    }
+
+    QTAILQ_REMOVE(&xfer->ring->inflight, xfer, next);
+    if (QTAILQ_EMPTY(&xfer->ring->inflight)) {
+        USBHostDevice *s = xfer->ring->host;
+        trace_usb_host_iso_stop(s->bus_num, s->addr, xfer->ring->ep->nr);
+    }
+    if (xfer->ring->ep->pid == USB_TOKEN_IN) {
+        QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next);
+    } else {
+        QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next);
+    }
+}
+
+static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep)
+{
+    USBHostIsoRing *ring = g_new0(USBHostIsoRing, 1);
+    USBHostIsoXfer *xfer;
+    /* FIXME: check interval (for now assume one xfer per frame) */
+    int packets = s->iso_urb_frames;
+    int i;
+
+    ring->host = s;
+    ring->ep = ep;
+    QTAILQ_INIT(&ring->unused);
+    QTAILQ_INIT(&ring->inflight);
+    QTAILQ_INIT(&ring->copy);
+    QTAILQ_INSERT_TAIL(&s->isorings, ring, next);
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        xfer = g_new0(USBHostIsoXfer, 1);
+        xfer->ring = ring;
+        xfer->xfer = libusb_alloc_transfer(packets);
+        xfer->xfer->dev_handle = s->dh;
+        xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
+
+        xfer->xfer->endpoint = ring->ep->nr;
+        if (ring->ep->pid == USB_TOKEN_IN) {
+            xfer->xfer->endpoint |= USB_DIR_IN;
+        }
+        xfer->xfer->callback = usb_host_req_complete_iso;
+        xfer->xfer->user_data = xfer;
+
+        xfer->xfer->num_iso_packets = packets;
+        xfer->xfer->length = ring->ep->max_packet_size * packets;
+        xfer->xfer->buffer = g_malloc0(xfer->xfer->length);
+
+        QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+    }
+
+    return ring;
+}
+
+static USBHostIsoRing *usb_host_iso_find(USBHostDevice *s, USBEndpoint *ep)
+{
+    USBHostIsoRing *ring;
+
+    QTAILQ_FOREACH(ring, &s->isorings, next) {
+        if (ring->ep == ep) {
+            return ring;
+        }
+    }
+    return NULL;
+}
+
+static void usb_host_iso_reset_xfer(USBHostIsoXfer *xfer)
+{
+    libusb_set_iso_packet_lengths(xfer->xfer,
+                                  xfer->ring->ep->max_packet_size);
+    xfer->packet = 0;
+    xfer->copy_complete = false;
+}
+
+static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight)
+{
+    if (inflight) {
+        xfer->xfer->user_data = NULL;
+    } else {
+        g_free(xfer->xfer->buffer);
+        libusb_free_transfer(xfer->xfer);
+    }
+    g_free(xfer);
+}
+
+static void usb_host_iso_free(USBHostIsoRing *ring)
+{
+    USBHostIsoXfer *xfer;
+
+    while ((xfer = QTAILQ_FIRST(&ring->inflight)) != NULL) {
+        QTAILQ_REMOVE(&ring->inflight, xfer, next);
+        usb_host_iso_free_xfer(xfer, true);
+    }
+    while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_free_xfer(xfer, false);
+    }
+    while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL) {
+        QTAILQ_REMOVE(&ring->copy, xfer, next);
+        usb_host_iso_free_xfer(xfer, false);
+    }
+
+    QTAILQ_REMOVE(&ring->host->isorings, ring, next);
+    g_free(ring);
+}
+
+static void usb_host_iso_free_all(USBHostDevice *s)
+{
+    USBHostIsoRing *ring;
+
+    while ((ring = QTAILQ_FIRST(&s->isorings)) != NULL) {
+        usb_host_iso_free(ring);
+    }
+}
+
+static bool usb_host_iso_data_copy(USBHostIsoXfer *xfer, USBPacket *p)
+{
+    unsigned int psize;
+    unsigned char *buf;
+
+    buf = libusb_get_iso_packet_buffer_simple(xfer->xfer, xfer->packet);
+    if (p->pid == USB_TOKEN_OUT) {
+        psize = p->iov.size;
+        if (psize > xfer->ring->ep->max_packet_size) {
+            /* should not happen (guest bug) */
+            psize = xfer->ring->ep->max_packet_size;
+        }
+        xfer->xfer->iso_packet_desc[xfer->packet].length = psize;
+    } else {
+        psize = xfer->xfer->iso_packet_desc[xfer->packet].actual_length;
+        if (psize > p->iov.size) {
+            /* should not happen (guest bug) */
+            psize = p->iov.size;
+        }
+    }
+    usb_packet_copy(p, buf, psize);
+    xfer->packet++;
+    xfer->copy_complete = (xfer->packet == xfer->xfer->num_iso_packets);
+    return xfer->copy_complete;
+}
+
+static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p)
+{
+    USBHostIsoRing *ring;
+    USBHostIsoXfer *xfer;
+    bool disconnect = false;
+    int rc;
+
+    ring = usb_host_iso_find(s, p->ep);
+    if (ring == NULL) {
+        ring = usb_host_iso_alloc(s, p->ep);
+    }
+
+    /* copy data to guest */
+    xfer = QTAILQ_FIRST(&ring->copy);
+    if (xfer != NULL) {
+        if (usb_host_iso_data_copy(xfer, p)) {
+            QTAILQ_REMOVE(&ring->copy, xfer, next);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+        }
+    }
+
+    /* submit empty bufs to host */
+    while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_reset_xfer(xfer);
+        rc = libusb_submit_transfer(xfer->xfer);
+        if (rc != 0) {
+            usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+            if (rc == LIBUSB_ERROR_NO_DEVICE) {
+                disconnect = true;
+            }
+            break;
+        }
+        if (QTAILQ_EMPTY(&ring->inflight)) {
+            trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
+        }
+        QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
+    }
+
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
+{
+    USBHostIsoRing *ring;
+    USBHostIsoXfer *xfer;
+    bool disconnect = false;
+    int rc, filled = 0;
+
+    ring = usb_host_iso_find(s, p->ep);
+    if (ring == NULL) {
+        ring = usb_host_iso_alloc(s, p->ep);
+    }
+
+    /* copy data from guest */
+    xfer = QTAILQ_FIRST(&ring->copy);
+    while (xfer != NULL && xfer->copy_complete) {
+        filled++;
+        xfer = QTAILQ_NEXT(xfer, next);
+    }
+    if (xfer == NULL) {
+        xfer = QTAILQ_FIRST(&ring->unused);
+        if (xfer == NULL) {
+            trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, p->ep->nr);
+            return;
+        }
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_reset_xfer(xfer);
+        QTAILQ_INSERT_TAIL(&ring->copy, xfer, next);
+    }
+    usb_host_iso_data_copy(xfer, p);
+
+    if (QTAILQ_EMPTY(&ring->inflight)) {
+        /* wait until half of our buffers are filled
+           before kicking the iso out stream */
+        if (filled*2 < s->iso_urb_count) {
+            return;
+        }
+    }
+
+    /* submit filled bufs to host */
+    while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL &&
+           xfer->copy_complete) {
+        QTAILQ_REMOVE(&ring->copy, xfer, next);
+        rc = libusb_submit_transfer(xfer->xfer);
+        if (rc != 0) {
+            usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+            if (rc == LIBUSB_ERROR_NO_DEVICE) {
+                disconnect = true;
+            }
+            break;
+        }
+        if (QTAILQ_EMPTY(&ring->inflight)) {
+            trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
+        }
+        QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
+    }
+
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void usb_host_ep_update(USBHostDevice *s)
+{
+    static const char *tname[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = "control",
+        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
+        [USB_ENDPOINT_XFER_BULK]    = "bulk",
+        [USB_ENDPOINT_XFER_INT]     = "int",
+    };
+    USBDevice *udev = USB_DEVICE(s);
+    struct libusb_config_descriptor *conf;
+    const struct libusb_interface_descriptor *intf;
+    const struct libusb_endpoint_descriptor *endp;
+    uint8_t devep, type;
+    int pid, ep;
+    int rc, i, e;
+
+    usb_ep_reset(udev);
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    trace_usb_host_parse_config(s->bus_num, s->addr,
+                                conf->bConfigurationValue, true);
+
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        assert(udev->altsetting[i] < conf->interface[i].num_altsetting);
+        intf = &conf->interface[i].altsetting[udev->altsetting[i]];
+        trace_usb_host_parse_interface(s->bus_num, s->addr,
+                                       intf->bInterfaceNumber,
+                                       intf->bAlternateSetting, true);
+        for (e = 0; e < intf->bNumEndpoints; e++) {
+            endp = &intf->endpoint[e];
+
+            devep = endp->bEndpointAddress;
+            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = devep & 0xf;
+            type = endp->bmAttributes & 0x3;
+
+            if (ep == 0) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "invalid endpoint address");
+                return;
+            }
+            if (usb_ep_get_type(udev, pid, ep) != USB_ENDPOINT_XFER_INVALID) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "duplicate endpoint address");
+                return;
+            }
+
+            trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
+                                          (devep & USB_DIR_IN) ? "in" : "out",
+                                          tname[type], true);
+            usb_ep_set_max_packet_size(udev, pid, ep,
+                                       endp->wMaxPacketSize);
+            usb_ep_set_type(udev, pid, ep, type);
+            usb_ep_set_ifnum(udev, pid, ep, i);
+            usb_ep_set_halted(udev, pid, ep, 0);
+        }
+    }
+
+    libusb_free_config_descriptor(conf);
+}
+
+static int usb_host_open(USBHostDevice *s, libusb_device *dev)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int bus_num = libusb_get_bus_number(dev);
+    int addr    = libusb_get_device_address(dev);
+    int rc;
+
+    trace_usb_host_open_started(bus_num, addr);
+
+    if (s->dh != NULL) {
+        goto fail;
+    }
+    rc = libusb_open(dev, &s->dh);
+    if (rc != 0) {
+        goto fail;
+    }
+
+    libusb_get_device_descriptor(dev, &s->ddesc);
+    s->dev     = dev;
+    s->bus_num = bus_num;
+    s->addr    = addr;
+    usb_host_get_port(s->dev, s->port, sizeof(s->port));
+
+    usb_ep_init(udev);
+    usb_host_ep_update(s);
+
+    udev->speed     = speed_map[libusb_get_device_speed(dev)];
+    udev->speedmask = (1 << udev->speed);
+#if 0
+    if (udev->speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
+        udev->speedmask |= USB_SPEED_MASK_FULL;
+    }
+#endif
+
+    if (s->ddesc.iProduct) {
+        libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
+                                           (unsigned char *)udev->product_desc,
+                                           sizeof(udev->product_desc));
+    } else {
+        snprintf(udev->product_desc, sizeof(udev->product_desc),
+                 "host:%d.%d", bus_num, addr);
+    }
+
+    rc = usb_device_attach(udev);
+    if (rc) {
+        goto fail;
+    }
+
+    trace_usb_host_open_success(bus_num, addr);
+    return 0;
+
+fail:
+    trace_usb_host_open_failure(bus_num, addr);
+    if (s->dh != NULL) {
+        libusb_close(s->dh);
+        s->dh = NULL;
+        s->dev = NULL;
+    }
+    return -1;
+}
+
+static void usb_host_abort_xfers(USBHostDevice *s)
+{
+    USBHostRequest *r, *rtmp;
+
+    QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) {
+        usb_host_req_abort(r);
+    }
+}
+
+static int usb_host_close(USBHostDevice *s)
+{
+    USBDevice *udev = USB_DEVICE(s);
+
+    if (s->dh == NULL) {
+        return -1;
+    }
+
+    trace_usb_host_close(s->bus_num, s->addr);
+
+    usb_host_abort_xfers(s);
+    usb_host_iso_free_all(s);
+
+    if (udev->attached) {
+        usb_device_detach(udev);
+    }
+
+    usb_host_release_interfaces(s);
+    libusb_reset_device(s->dh);
+    usb_host_attach_kernel(s);
+    libusb_close(s->dh);
+    s->dh = NULL;
+    s->dev = NULL;
+
+    usb_host_auto_check(NULL);
+    return 0;
+}
+
+static void usb_host_nodev_bh(void *opaque)
+{
+    USBHostDevice *s = opaque;
+    usb_host_close(s);
+}
+
+static void usb_host_nodev(USBHostDevice *s)
+{
+    if (!s->bh) {
+        s->bh = qemu_bh_new(usb_host_nodev_bh, s);
+    }
+    qemu_bh_schedule(s->bh);
+}
+
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
+{
+    USBHostDevice *s = container_of(n, USBHostDevice, exit);
+
+    if (s->dh) {
+        usb_host_release_interfaces(s);
+        usb_host_attach_kernel(s);
+    }
+}
+
+static int usb_host_initfn(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    loglevel = s->loglevel;
+    udev->auto_attach = 0;
+    QTAILQ_INIT(&s->requests);
+    QTAILQ_INIT(&s->isorings);
+
+    s->exit.notify = usb_host_exit_notifier;
+    qemu_add_exit_notifier(&s->exit);
+
+    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+    add_boot_device_path(s->bootindex, &udev->qdev, NULL);
+    usb_host_auto_check(NULL);
+    return 0;
+}
+
+static void usb_host_handle_destroy(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    qemu_remove_exit_notifier(&s->exit);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    usb_host_close(s);
+}
+
+static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+
+    if (p->combined) {
+        usb_combined_packet_cancel(udev, p);
+        return;
+    }
+
+    trace_usb_host_req_canceled(s->bus_num, s->addr, p);
+
+    r = usb_host_req_find(s, p);
+    if (r && r->p) {
+        r->p = NULL; /* mark as dead */
+        libusb_cancel_transfer(r->xfer);
+    }
+}
+
+static void usb_host_detach_kernel(USBHostDevice *s)
+{
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        rc = libusb_kernel_driver_active(s->dh, i);
+        usb_host_libusb_error("libusb_kernel_driver_active", rc);
+        if (rc != 1) {
+            continue;
+        }
+        trace_usb_host_detach_kernel(s->bus_num, s->addr, i);
+        rc = libusb_detach_kernel_driver(s->dh, i);
+        usb_host_libusb_error("libusb_detach_kernel_driver", rc);
+        s->ifs[i].detached = true;
+    }
+    libusb_free_config_descriptor(conf);
+}
+
+static void usb_host_attach_kernel(USBHostDevice *s)
+{
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        if (!s->ifs[i].detached) {
+            continue;
+        }
+        trace_usb_host_attach_kernel(s->bus_num, s->addr, i);
+        libusb_attach_kernel_driver(s->dh, i);
+        s->ifs[i].detached = false;
+    }
+    libusb_free_config_descriptor(conf);
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    for (i = 0; i < USB_MAX_INTERFACES; i++) {
+        udev->altsetting[i] = 0;
+    }
+    udev->ninterfaces   = 0;
+    udev->configuration = 0;
+
+    if (configuration == 0) {
+        /* address state - ignore */
+        return USB_RET_SUCCESS;
+    }
+
+    usb_host_detach_kernel(s);
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return USB_RET_STALL;
+    }
+
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i);
+        rc = libusb_claim_interface(s->dh, i);
+        usb_host_libusb_error("libusb_claim_interface", rc);
+        if (rc != 0) {
+            return USB_RET_STALL;
+        }
+        s->ifs[i].claimed = true;
+    }
+
+    udev->ninterfaces   = conf->bNumInterfaces;
+    udev->configuration = configuration;
+
+    libusb_free_config_descriptor(conf);
+    return USB_RET_SUCCESS;
+}
+
+static void usb_host_release_interfaces(USBHostDevice *s)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int i, rc;
+
+    for (i = 0; i < udev->ninterfaces; i++) {
+        if (!s->ifs[i].claimed) {
+            continue;
+        }
+        trace_usb_host_release_interface(s->bus_num, s->addr, i);
+        rc = libusb_release_interface(s->dh, i);
+        usb_host_libusb_error("libusb_release_interface", rc);
+        s->ifs[i].claimed = false;
+    }
+}
+
+static void usb_host_set_address(USBHostDevice *s, int addr)
+{
+    USBDevice *udev = USB_DEVICE(s);
+
+    trace_usb_host_set_address(s->bus_num, s->addr, addr);
+    udev->addr = addr;
+}
+
+static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
+{
+    int rc;
+
+    trace_usb_host_set_config(s->bus_num, s->addr, config);
+
+    usb_host_release_interfaces(s);
+    usb_host_detach_kernel(s);
+    rc = libusb_set_configuration(s->dh, config);
+    if (rc != 0) {
+        usb_host_libusb_error("libusb_set_configuration", rc);
+        p->status = USB_RET_STALL;
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+    p->status = usb_host_claim_interfaces(s, config);
+    if (p->status != USB_RET_SUCCESS) {
+        return;
+    }
+    usb_host_ep_update(s);
+}
+
+static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
+                                   USBPacket *p)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int rc;
+
+    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
+
+    usb_host_iso_free_all(s);
+
+    if (iface >= USB_MAX_INTERFACES) {
+        p->status = USB_RET_STALL;
+        return;
+    }
+
+    rc = libusb_set_interface_alt_setting(s->dh, iface, alt);
+    if (rc != 0) {
+        usb_host_libusb_error("libusb_set_interface_alt_setting", rc);
+        p->status = USB_RET_STALL;
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    udev->altsetting[iface] = alt;
+    usb_host_ep_update(s);
+}
+
+static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
+                                    int request, int value, int index,
+                                    int length, uint8_t *data)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+    int rc;
+
+    trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
+
+    if (s->dh == NULL) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        usb_host_set_address(s, value);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        usb_host_set_config(s, value & 0xff, p);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        usb_host_set_interface(s, index, value, p);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == 0) { /* clear halt */
+            int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            libusb_clear_halt(s->dh, index);
+            usb_ep_set_halted(udev, pid, index & 0x0f, 0);
+            trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+            return;
+        }
+    }
+
+    r = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN, length + 8);
+    r->cbuf = data;
+    r->clen = length;
+    memcpy(r->buffer, udev->setup_buf, 8);
+    if (!r->in) {
+        memcpy(r->buffer + 8, r->cbuf, r->clen);
+    }
+
+    libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
+                                 usb_host_req_complete_ctrl, r,
+                                 CONTROL_TIMEOUT);
+    rc = libusb_submit_transfer(r->xfer);
+    if (rc != 0) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    p->status = USB_RET_ASYNC;
+}
+
+static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+    size_t size;
+    int ep, rc;
+
+    if (usb_host_use_combining(p->ep) && p->state == USB_PACKET_SETUP) {
+        p->status = USB_RET_ADD_TO_QUEUE;
+        return;
+    }
+
+    trace_usb_host_req_data(s->bus_num, s->addr, p,
+                            p->pid == USB_TOKEN_IN,
+                            p->ep->nr, p->iov.size);
+
+    if (s->dh == NULL) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+    if (p->ep->halted) {
+        p->status = USB_RET_STALL;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+
+    switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) {
+    case USB_ENDPOINT_XFER_BULK:
+        size = usb_packet_size(p);
+        r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, size);
+        if (!r->in) {
+            usb_packet_copy(p, r->buffer, size);
+        }
+        ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
+        libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
+                                  r->buffer, size,
+                                  usb_host_req_complete_data, r,
+                                  BULK_TIMEOUT);
+        break;
+    case USB_ENDPOINT_XFER_INT:
+        r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
+        if (!r->in) {
+            usb_packet_copy(p, r->buffer, p->iov.size);
+        }
+        ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
+        libusb_fill_interrupt_transfer(r->xfer, s->dh, ep,
+                                       r->buffer, p->iov.size,
+                                       usb_host_req_complete_data, r,
+                                       INTR_TIMEOUT);
+        break;
+    case USB_ENDPOINT_XFER_ISOC:
+        if (p->pid == USB_TOKEN_IN) {
+            usb_host_iso_data_in(s, p);
+        } else {
+            usb_host_iso_data_out(s, p);
+        }
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        return;
+    default:
+        p->status = USB_RET_STALL;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        return;
+    }
+
+    rc = libusb_submit_transfer(r->xfer);
+    if (rc != 0) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    p->status = USB_RET_ASYNC;
+}
+
+static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
+{
+    if (usb_host_use_combining(ep)) {
+        usb_ep_combine_input_packets(ep);
+    }
+}
+
+static void usb_host_handle_reset(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    trace_usb_host_reset(s->bus_num, s->addr);
+
+    if (udev->configuration == 0) {
+        return;
+    }
+    usb_host_release_interfaces(s);
+    libusb_reset_device(s->dh);
+    usb_host_claim_interfaces(s, 0);
+    usb_host_ep_update(s);
+}
+
+static const VMStateDescription vmstate_usb_host = {
+    .name = "usb-host",
+    .unmigratable = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_USB_DEVICE(parent_obj, USBHostDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property usb_host_dev_properties[] = {
+    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
+    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
+    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
+    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
+    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
+    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames,   32),
+    DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
+    DEFINE_PROP_UINT32("loglevel",  USBHostDevice, loglevel,
+                       LIBUSB_LOG_LEVEL_WARNING),
+    DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
+                    USB_HOST_OPT_PIPELINE, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_host_initfn;
+    uc->product_desc   = "USB Host Device";
+    uc->cancel_packet  = usb_host_cancel_packet;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_destroy = usb_host_handle_destroy;
+    uc->flush_ep_queue = usb_host_flush_ep_queue;
+    dc->vmsd = &vmstate_usb_host;
+    dc->props = usb_host_dev_properties;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = TYPE_USB_HOST_DEVICE,
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+}
+
+type_init(usb_host_register_types)
+
+/* ------------------------------------------------------------------------ */
+
+static QEMUTimer *usb_auto_timer;
+static VMChangeStateEntry *usb_vmstate;
+
+static void usb_host_vm_state(void *unused, int running, RunState state)
+{
+    if (running) {
+        usb_host_auto_check(unused);
+    }
+}
+
+static void usb_host_auto_check(void *unused)
+{
+    struct USBHostDevice *s;
+    struct USBAutoFilter *f;
+    libusb_device **devs;
+    struct libusb_device_descriptor ddesc;
+    int unconnected = 0;
+    int i, n;
+
+    if (usb_host_init() != 0) {
+        return;
+    }
+
+    if (runstate_is_running()) {
+        n = libusb_get_device_list(ctx, &devs);
+        for (i = 0; i < n; i++) {
+            if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) {
+                continue;
+            }
+            if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) {
+                continue;
+            }
+            QTAILQ_FOREACH(s, &hostdevs, next) {
+                f = &s->match;
+                if (f->bus_num > 0 &&
+                    f->bus_num != libusb_get_bus_number(devs[i])) {
+                    continue;
+                }
+                if (f->addr > 0 &&
+                    f->addr != libusb_get_device_address(devs[i])) {
+                    continue;
+                }
+                if (f->port != NULL) {
+                    char port[16] = "-";
+                    usb_host_get_port(devs[i], port, sizeof(port));
+                    if (strcmp(f->port, port) != 0) {
+                        continue;
+                    }
+                }
+                if (f->vendor_id > 0 &&
+                    f->vendor_id != ddesc.idVendor) {
+                    continue;
+                }
+                if (f->product_id > 0 &&
+                    f->product_id != ddesc.idProduct) {
+                    continue;
+                }
+
+                /* We got a match */
+                s->seen++;
+                if (s->errcount >= 3) {
+                    continue;
+                }
+                if (s->dh != NULL) {
+                    continue;
+                }
+                if (usb_host_open(s, devs[i]) < 0) {
+                    s->errcount++;
+                    continue;
+                }
+                break;
+            }
+        }
+        libusb_free_device_list(devs, 1);
+
+        QTAILQ_FOREACH(s, &hostdevs, next) {
+            if (s->dh == NULL) {
+                unconnected++;
+            }
+            if (s->seen == 0) {
+                if (s->dh) {
+                    usb_host_close(s);
+                }
+                s->errcount = 0;
+            }
+            s->seen = 0;
+        }
+
+#if 0
+        if (unconnected == 0) {
+            /* nothing to watch */
+            if (usb_auto_timer) {
+                qemu_del_timer(usb_auto_timer);
+                trace_usb_host_auto_scan_disabled();
+            }
+            return;
+        }
+#endif
+    }
+
+    if (!usb_vmstate) {
+        usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
+    }
+    if (!usb_auto_timer) {
+        usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
+        if (!usb_auto_timer) {
+            return;
+        }
+        trace_usb_host_auto_scan_enabled();
+    }
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
+}
+
+void usb_host_info(Monitor *mon, const QDict *qdict)
+{
+    libusb_device **devs;
+    struct libusb_device_descriptor ddesc;
+    char port[16];
+    int i, n;
+
+    if (usb_host_init() != 0) {
+        return;
+    }
+
+    n = libusb_get_device_list(ctx, &devs);
+    for (i = 0; i < n; i++) {
+        if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) {
+            continue;
+        }
+        if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) {
+            continue;
+        }
+        usb_host_get_port(devs[i], port, sizeof(port));
+        monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                       libusb_get_bus_number(devs[i]),
+                       libusb_get_device_address(devs[i]),
+                       port,
+                       speed_name[libusb_get_device_speed(devs[i])]);
+        monitor_printf(mon, "    Class %02x:", ddesc.bDeviceClass);
+        monitor_printf(mon, " USB device %04x:%04x",
+                       ddesc.idVendor, ddesc.idProduct);
+        if (ddesc.iProduct) {
+            libusb_device_handle *handle;
+            if (libusb_open(devs[i], &handle) == 0) {
+                unsigned char name[64] = "";
+                libusb_get_string_descriptor_ascii(handle,
+                                                   ddesc.iProduct,
+                                                   name, sizeof(name));
+                libusb_close(handle);
+                monitor_printf(mon, ", %s", name);
+            }
+        }
+        monitor_printf(mon, "\n");
+    }
+    libusb_free_device_list(devs, 1);
+}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index b67aeba..8994668 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -45,6 +45,12 @@
 #include "hw/usb/desc.h"
 #include "hw/usb/host.h"
 
+#ifdef CONFIG_USB_LIBUSB
+# define DEVNAME "usb-host-linux"
+#else
+# define DEVNAME "usb-host"
+#endif
+
 /* We redefine it to avoid version problems */
 struct usb_ctrltransfer {
     uint8_t  bRequestType;
@@ -1487,7 +1493,7 @@ static int usb_host_initfn(USBDevice *dev)
 }
 
 static const VMStateDescription vmstate_usb_host = {
-    .name = "usb-host",
+    .name = DEVNAME,
     .version_id = 1,
     .minimum_version_id = 1,
     .post_load = usb_host_post_load,
@@ -1527,7 +1533,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo usb_host_dev_info = {
-    .name          = "usb-host",
+    .name          = DEVNAME,
     .parent        = TYPE_USB_DEVICE,
     .instance_size = sizeof(USBHostDevice),
     .class_init    = usb_host_class_initfn,
@@ -1767,6 +1773,8 @@ static void usb_host_auto_check(void *unused)
     qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
 }
 
+#ifndef CONFIG_USB_LIBUSB
+
 /**********************/
 /* USB host device info */
 
@@ -1898,3 +1906,5 @@ void usb_host_info(Monitor *mon, const QDict *qdict)
                        bus, addr, f->port ? f->port : "*", vid, pid);
     }
 }
+
+#endif
diff --git a/trace-events b/trace-events
index 54b7d90..bf6bd85 100644
--- a/trace-events
+++ b/trace-events
@@ -425,11 +425,15 @@ usb_host_open_success(int bus, int addr) "dev %d:%d"
 usb_host_open_failure(int bus, int addr) "dev %d:%d"
 usb_host_disconnect(int bus, int addr) "dev %d:%d"
 usb_host_close(int bus, int addr) "dev %d:%d"
+usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
+usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
 usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d"
 usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d"
 usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d"
 usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d"
+usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d"
 usb_host_release_interfaces(int bus, int addr) "dev %d:%d"
+usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d"
 usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d"
 usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d"
 usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d"
-- 
1.7.9.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 7/7] use libusb for usb-host
  2013-04-16 10:13 ` [Qemu-devel] [PATCH 7/7] use libusb for usb-host Gerd Hoffmann
@ 2013-04-18 13:07   ` Markus Armbruster
  2013-04-22 15:57     ` Andreas Färber
  0 siblings, 1 reply; 10+ messages in thread
From: Markus Armbruster @ 2013-04-18 13:07 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Gerd Hoffmann <kraxel@redhat.com> writes:

> Reimplement usb-host on top of libusb.
> Reasons to do this:
>
>  (1) Largely rewritten from scratch, nice opportunity to kill historical
>      cruft.
>  (2) Offload usbfs handling to libusb.
>  (3) Have a single portable code base instead of bsd + linux variants.
>  (4) Bring usb-host support to any platform supported by libusbx.
>
> For now this goes side-by-side to the existing code.  That is only to
> simplify regression testing though, at the end of the day I want remove
> the old code and support libusb exclusively.  Merge early in 1.5 cycle,
> remove the old code after 1.5 release or something like this.
>
> Thanks to qdev the old and new code can coexist nicely on linux.  Just
> use "-device usb-host-linux" to use the old linux driver instead of the
> libusb one (which takes over the "usb-host" name).
>
> The bsd driver isn't qdev'ified so it isn't that easy for bsd.
> I didn't bother making it runtime switchable, so you have to rebuild
> qemu with --disable-libusb to get back the old code.

Breaks the build for me (Fedora 17's libusb1-devel-1.0.9-0.6.rc1):

  CC    hw/usb/host-libusb.o
/home/armbru/work/qemu/hw/usb/host-libusb.c:1254:217: error: ‘LIBUSB_LOG_LEVEL_WARNING’ undeclared here (not in a function)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 7/7] use libusb for usb-host
  2013-04-18 13:07   ` Markus Armbruster
@ 2013-04-22 15:57     ` Andreas Färber
  0 siblings, 0 replies; 10+ messages in thread
From: Andreas Färber @ 2013-04-22 15:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Markus Armbruster, Anthony Liguori, qemu-devel

Am 18.04.2013 15:07, schrieb Markus Armbruster:
> Gerd Hoffmann <kraxel@redhat.com> writes:
> 
>> Reimplement usb-host on top of libusb.
>> Reasons to do this:
>>
>>  (1) Largely rewritten from scratch, nice opportunity to kill historical
>>      cruft.
>>  (2) Offload usbfs handling to libusb.
>>  (3) Have a single portable code base instead of bsd + linux variants.
>>  (4) Bring usb-host support to any platform supported by libusbx.
>>
>> For now this goes side-by-side to the existing code.  That is only to
>> simplify regression testing though, at the end of the day I want remove
>> the old code and support libusb exclusively.  Merge early in 1.5 cycle,
>> remove the old code after 1.5 release or something like this.
>>
>> Thanks to qdev the old and new code can coexist nicely on linux.  Just
>> use "-device usb-host-linux" to use the old linux driver instead of the
>> libusb one (which takes over the "usb-host" name).
>>
>> The bsd driver isn't qdev'ified so it isn't that easy for bsd.
>> I didn't bother making it runtime switchable, so you have to rebuild
>> qemu with --disable-libusb to get back the old code.
> 
> Breaks the build for me (Fedora 17's libusb1-devel-1.0.9-0.6.rc1):
> 
>   CC    hw/usb/host-libusb.o
> /home/armbru/work/qemu/hw/usb/host-libusb.c:1254:217: error: ‘LIBUSB_LOG_LEVEL_WARNING’ undeclared here (not in a function)

Same on openSUSE 12.3 (1.0.9-5.1.1) - any fix cooking for this, Gerd?

Andreas

-- 
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-04-22 15:57 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-16 10:13 [Qemu-devel] [PULL 0/7] usb patch queue Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 1/7] usb-serial: Remove double call to qemu_chr_add_handlers( NULL ) Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 2/7] xhci: remove leftover debug printf Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 3/7] xhci: add xhci_cap_write Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 4/7] xhci: fix portsc writes Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 5/7] xhci: use slotid as device address Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 6/7] xhci: fix address device Gerd Hoffmann
2013-04-16 10:13 ` [Qemu-devel] [PATCH 7/7] use libusb for usb-host Gerd Hoffmann
2013-04-18 13:07   ` Markus Armbruster
2013-04-22 15:57     ` Andreas Färber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).