usb-passthru: add usb passthrough feature
authorChulHo Song <ch81.song@samsung.com>
Fri, 17 Jul 2015 07:22:32 +0000 (16:22 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Mon, 20 Jul 2015 08:26:17 +0000 (17:26 +0900)
default disable

Change-Id: I5637b7b757c2187e2c5a189e22701cb9777111f9
Signed-off-by: ChulHo Song <ch81.song@samsung.com>
configure
hw/usb/host-tizenusb.c [new file with mode: 0644]

index 06b886e..0c98bac 100755 (executable)
--- a/configure
+++ b/configure
@@ -309,6 +309,7 @@ spice=""
 rbd=""
 smartcard_nss=""
 libusb=""
+libtizenusb="no"
 usb_redir=""
 opengl=""
 efence="no"
@@ -1095,6 +1096,10 @@ for opt do
   ;;
   --enable-libusb) libusb="yes"
   ;;
+  --disable-libtizenusb) libtizenusb="no"
+  ;;
+  --enable-libtizenusb) libtizenusb="yes"
+  ;;
   --disable-usb-redir) usb_redir="no"
   ;;
   --enable-usb-redir) usb_redir="yes"
@@ -3785,6 +3790,26 @@ EOF
     fi
 fi
 
+# check for libtizenusb
+if test "$libtizenusb" != "no" ; then
+        libtizenusb="yes"
+        libusb="no"
+        usb="libtizenusb"
+        echo $PKG_CONFIG_PATH
+        if $pkg_config --modversion libtizenusb >/dev/null 2>&1 ; then
+            libtizenusb_cflags=$($pkg_config --cflags libtizenusb 2>/dev/null)
+            libtizenusb_libs=$($pkg_config --libs libtizenusb 2>/dev/null)
+
+            echo "libtizenusb found"
+            echo "cflags = $libtizenusb_cflags"
+            echo "libs = $libtizenusb_libs"
+        else
+            error_exit "libtizenusb not found (you might not be set PKG_CONFIG_PATH"
+        fi
+        QEMU_CFLAGS="$QEMU_CFLAGS $libtizenusb_cflags"
+        libs_softmmu="$libs_softmmu $libtizenusb_libs"
+fi
+
 # check for libusb
 if test "$libusb" != "no" ; then
     if $pkg_config --atleast-version=1.0.13 libusb-1.0; then
@@ -4672,6 +4697,7 @@ echo "TIZEN-maru libav support $libav"
 echo "TIZEN-maru libpng support $libpng"
 echo "TIZEN-maru DXVA2 support $dxva2"
 echo "TIZEN-maru vaapi support $vaapi"
+echo "TIZEN-maru libtizenusb support $libtizenusb"
 echo "TIZEN-maru extension path $extension_path"
 #
 
@@ -5168,6 +5194,8 @@ fi
 # USB host support
 if test "$libusb" = "yes"; then
   echo "HOST_USB=libusb legacy" >> $config_host_mak
+elif test "$libtizenusb" = "yes"; then
+  echo "HOST_USB=tizenusb legacy" >> $config_host_mak
 else
   echo "HOST_USB=stub" >> $config_host_mak
 fi
@@ -5207,6 +5235,9 @@ if test "$vaapi" = "yes" ; then
   echo "CONFIG_VAAPI=y" >> $config_host_mak
   echo "LIBVA_CFLAGS=$libva_cflags $libva_x11_cflags" >> $config_host_mak
 fi
+if test "$libtizenusb" = "yes" ; then
+  echo "CONFIG_TIZENUSB=y" >> $config_host_mak
+fi
 if [ ! -z "$extension_path" ] ; then
   echo "CONFIG_EXTENSION_PATH=$extension_path" >> $config_host_mak
 fi
diff --git a/hw/usb/host-tizenusb.c b/hw/usb/host-tizenusb.c
new file mode 100644 (file)
index 0000000..6efaa2e
--- /dev/null
@@ -0,0 +1,2100 @@
+/*
+ * Emulator USB Pass-through
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ *  Chulho Song     <ch81.song@samsung.com>
+ *  HakHyun Kim     <haken.kim@samsung.com>
+ *  Jinhyung Choi   <jinhyung2.choi@samsung.com>
+ *  Byeongki Shin   <bk0121.shin@samsung.com>
+ *  Sangho Park     <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+#ifdef CONFIG_LINUX
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <linux/version.h>
+#include <linux/usbdevice_fs.h>
+#endif
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/usb/host.h"
+#include "libtizenusb.h"
+#include "tizen/src/debug_ch.h"
+#include "trace.h"
+
+#define ISOC                0
+
+#ifdef CONFIG_LINUX
+#undef RT_FAILURE
+#define RT_FAILURE(rc)      ((rc) != 0)
+#endif
+
+#define VBOX_PROXY_DEVNAME  "usb-host"
+#define MAX_PORTLEN         16
+#define VBOX_MAX_PATH       4096
+#define MAX_EXPIRE_TIME     1000
+
+MULTI_DEBUG_CHANNEL(usb, vboxusb);
+
+enum USBHostDeviceOptions {
+    USB_HOST_OPT_PIPELINE,
+};
+
+#if ISOC
+typedef struct USBHostRequest USBHostRequest;
+
+struct endp_data {
+    uint8_t type;
+    uint8_t halted;
+    uint8_t iso_started;
+    USBHostRequest *iso_urb;
+    int iso_urb_idx;
+    int iso_buffer_used;
+    int max_packet_size;
+    int inflight;
+};
+#endif
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int fd;
+    int speed;
+    int speedmask;
+    struct USBAutoFilter match;
+    int32_t                          bootindex;
+    uint32_t                         iso_urb_count;
+    uint32_t                         iso_urb_frames;
+    uint32_t                         options;
+    QEMUBH                           *bh_nodev; //for nodev error
+    Notifier                         exit;
+    int seen, errcount;
+    int bus_num;
+    int addr;
+    USBPROXYDEV vboxdev;
+#ifdef CONFIG_DARWIN
+    QEMUTimer *callback_timer;
+    unsigned int expire_time;
+#endif
+#if ISOC
+    struct endp_data ep_in[USB_MAX_ENDPOINTS];
+    struct endp_data ep_out[USB_MAX_ENDPOINTS];
+#endif
+    QTAILQ_ENTRY(USBHostDevice) next;
+    QTAILQ_HEAD(, USBHostRequest)    requests;
+} USBHostDevice;
+
+typedef struct USBHostRequest{
+    VUSBURB                          vbox_urb;
+    USBHostDevice                    *host;
+    USBPacket                        *p;
+    bool                             in;
+    unsigned char                    *cbuf;
+    unsigned int                     clen;
+#if ISOC
+    int iso_frame_idx; /* -1 means in flight */
+#endif
+#ifdef CONFIG_WIN32
+    HANDLE event_handle;
+#endif
+    QTAILQ_ENTRY(USBHostRequest)     next;
+}USBHostRequest;
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+
+static void usb_host_auto_check(void *unused);
+static int usb_host_open_device(USBHostDevice* s, int bus, int addr, int product_id, int vendor_id,
+                                const char* port, const char* prod_name, int speed);
+static USBHostRequest* usb_host_req_find(USBHostDevice *s, USBPacket* p);
+static void usb_host_req_free(USBHostRequest* req);
+static USBHostRequest* usb_host_req_alloc(USBHostDevice* s, USBPacket* p, bool in);
+static void usb_fill_data_urb(USBHostRequest* req, USBPacket *p, uint8_t type, size_t size);
+static void usb_host_nodev(USBHostDevice *s);
+static void async_complete(void *opaque);
+
+struct desc_counts
+{
+    size_t num_ed, num_id, num_if;
+    /** bitmap (128 bits) */
+    uint32_t idmap[4];
+};
+
+#if ISOC
+static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
+    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
+    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
+    return eps + ep - 1;
+}
+
+static int is_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_started;
+}
+
+static void clear_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    get_endp(s, pid, ep)->iso_started = 0;
+}
+
+static void set_iso_started(USBHostDevice *s, int pid, int ep)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+    if (!e->iso_started) {
+        e->iso_started = 1;
+        e->inflight = 0;
+    }
+}
+
+static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
+{
+    struct endp_data *e = get_endp(s, pid, ep);
+
+    e->inflight += value;
+    return e->inflight;
+}
+
+static void set_iso_urb(USBHostDevice *s, int pid, int ep, USBHostRequest *iso_urb)
+{
+    get_endp(s, pid, ep)->iso_urb = iso_urb;
+}
+
+static USBHostRequest *get_iso_urb(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb;
+}
+
+static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_urb_idx = i;
+}
+
+static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_urb_idx;
+}
+
+static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
+{
+    get_endp(s, pid, ep)->iso_buffer_used = i;
+}
+
+static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
+{
+    return get_endp(s, pid, ep)->iso_buffer_used;
+}
+#endif
+
+static void usb_host_reset_callback_time(USBHostDevice *s)
+{
+#ifdef CONFIG_DARWIN
+    s->expire_time = 1;
+    timer_mod(s->callback_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + s->expire_time);
+#endif
+}
+
+static void usb_host_increase_callback_time(USBHostDevice *s)
+{
+#ifdef CONFIG_DARWIN
+    if (s->expire_time < MAX_EXPIRE_TIME)
+        s->expire_time++;
+    timer_mod(s->callback_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + s->expire_time);
+#endif
+}
+
+#ifdef CONFIG_WIN32
+/*
+ * After queuing a urb to proxy,
+ * the event handle is inserted to global main wait object
+ * to process it on single thread.
+ * This function should called after pfnUrbQueue()
+ *
+ * XXX : Data of usb library is safe?
+ */
+static int usb_host_add_event(USBHostRequest *req)
+{
+    USBHostDevice *s = req->host;
+    USBPROXYDEV *pThis = &s->vboxdev;
+    PVUSBURB vurb = &req->vbox_urb;
+    HANDLE hevent = (HANDLE)vurb->handle;
+
+    /* checking handle validation
+     *
+     * if usb backend failed to something or already reaped,
+     * the handle is already freed.
+     */
+    if (hevent == INVALID_HANDLE_VALUE) {
+        ERR("(%s) handle is no more valid\n", __func__);
+        return -1;
+    }
+
+    if (qemu_add_wait_object (hevent, async_complete, pThis)) {
+        ERR("(%s) failed add wait object\n", __func__);
+        return -1;
+    }
+
+    req->event_handle = hevent;
+
+    TRACE("(%s) req->vbox_urb=%p, req->event_handle=%d\n",
+           __func__, &req->vbox_urb, (int)req->event_handle);
+    return 0;
+}
+
+static void usb_host_del_event(USBHostRequest *req)
+{
+    USBHostDevice *s = req->host;
+    USBPROXYDEV *pThis = &s->vboxdev;
+    //PVUSBURB vurb = &req->vbox_urb;
+    HANDLE hevent = req->event_handle;
+
+    TRACE("(%s) req->vbox_urb=%p, req->event_handle=%d\n",
+           __func__, &req->vbox_urb, (int)req->event_handle);
+
+    qemu_del_wait_object(hevent, async_complete, pThis);
+}
+#endif
+
+
+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 const void *collect_stray_bits(uint8_t *this_desc, uint8_t *end, uint16_t *cbExtra)
+{
+    uint8_t *tmp, *buf;
+    uint8_t type;
+
+    Assert(*(this_desc + 1) == VUSB_DT_INTERFACE || *(this_desc + 1) == VUSB_DT_ENDPOINT);
+    buf = this_desc;
+
+    /* Skip the current interface/endpoint descriptor. */
+    buf += *(uint8_t *)buf;
+
+    /* Loop until we find another descriptor we understand. */
+    for (tmp = buf; ((tmp + 1) < end) && *tmp; tmp += *tmp)
+    {
+        type = *(tmp + 1);
+        if (type == VUSB_DT_INTERFACE || type == VUSB_DT_ENDPOINT)
+            break;
+    }
+    *cbExtra = tmp - buf;
+    if (*cbExtra)
+        return buf;
+    else
+        return NULL;
+}
+
+
+static int copy_interface(PVUSBINTERFACE pIf, uint8_t ifnum,
+                          PVUSBDESCINTERFACEEX *id, PVUSBDESCENDPOINTEX *ed,
+                          uint8_t *buf, size_t len)
+{
+    PVUSBDESCINTERFACEEX cur_if = NULL;
+    uint32_t altmap[4] = {0,};
+    uint8_t *tmp, *end = buf + len;
+    uint8_t alt;
+    int state;
+    size_t num_ep = 0;
+
+    buf += *(uint8_t *)buf;
+
+    pIf->cSettings = 0;
+    pIf->paSettings = NULL;
+
+    for (tmp = buf, state = 0; ((tmp + 1) < end) && *tmp; tmp += *tmp)
+    {
+        uint8_t type;
+        PVUSBDESCINTERFACE ifd;
+        PVUSBDESCENDPOINT epd;
+        PVUSBDESCENDPOINTEX cur_ep;
+
+        type = tmp[1];
+
+        switch ( type ) {
+        case VUSB_DT_INTERFACE:
+            state = 0;
+            ifd = (PVUSBDESCINTERFACE)tmp;
+
+            /* Ignoring this interface */
+            if ( ifd->bInterfaceNumber != ifnum )
+                break;
+
+            /* Check we didn't see this alternate setting already
+             * because that will break stuff
+             */
+            alt = ifd->bAlternateSetting;
+            if ( altmap[alt >> 6] & (1 << (alt & 0x1f)) )
+                return 0;
+            altmap[alt >> 6] |= (1 << (alt & 0x1f));
+
+            cur_if = *id;
+            (*id)++;
+            if ( pIf->cSettings == 0 )
+                pIf->paSettings = cur_if;
+
+            memcpy(cur_if, ifd, sizeof(cur_if->Core));
+
+            /* Point to additional interface descriptor bytes, if any. */
+            if (cur_if->Core.bLength - VUSB_DT_INTERFACE_MIN_LEN > 0)
+                cur_if->pvMore = tmp + VUSB_DT_INTERFACE_MIN_LEN;
+            else
+                cur_if->pvMore = NULL;
+
+            cur_if->pvClass = collect_stray_bits(tmp, end, &cur_if->cbClass);
+
+            pIf->cSettings++;
+
+            state = 1;
+            num_ep = 0;
+            break;
+        case VUSB_DT_ENDPOINT:
+            if ( state == 0 )
+                break;
+
+            epd = (PVUSBDESCENDPOINT)tmp;
+
+            cur_ep = *ed;
+            (*ed)++;
+
+            if ( num_ep == 0 )
+                cur_if->paEndpoints = cur_ep;
+
+            if ( num_ep > cur_if->Core.bNumEndpoints )
+                return 0;
+
+            memcpy(cur_ep, epd, sizeof(cur_ep->Core));
+
+            /* Point to additional endpoint descriptor bytes, if any. */
+            if (cur_ep->Core.bLength - VUSB_DT_ENDPOINT_MIN_LEN > 0)
+                cur_ep->pvMore = tmp + VUSB_DT_ENDPOINT_MIN_LEN;
+            else
+                cur_ep->pvMore = NULL;
+
+            cur_ep->pvClass = collect_stray_bits(tmp, end, &cur_ep->cbClass);
+
+            cur_ep->Core.wMaxPacketSize = RT_LE2H_U16(cur_ep->Core.wMaxPacketSize);
+
+            num_ep++;
+            break;
+        default:
+            /* Skip unknown descriptors. */
+            WARN("unknown type:%d\n", type);
+            break;
+        }
+    }
+
+    return 1;
+}
+
+
+static int count_descriptors(struct desc_counts *cnt, uint8_t *buf, size_t len)
+{
+    PVUSBDESCCONFIG cfg;
+    uint8_t *tmp, *end;
+    uint32_t i, x;
+
+    memset(cnt, 0, sizeof(*cnt));
+
+    end = buf + len;
+
+    cfg = (PVUSBDESCCONFIG)buf;
+    if ( cfg->bLength < VUSB_DT_CONFIG_MIN_LEN )
+        return 0;
+    if ( cfg->bLength > len )
+        return 0;
+
+    for (tmp = buf + cfg->bLength; ((tmp + 1) < end) && *tmp; tmp += *tmp)
+    {
+        uint8_t type;
+        uint32_t ifnum;
+        PVUSBDESCINTERFACE id;
+        PVUSBDESCENDPOINT ed;
+
+        type = *(tmp + 1);
+
+        switch ( type ) {
+        case VUSB_DT_INTERFACE:
+            id = (PVUSBDESCINTERFACE)tmp;
+            if ( id->bLength < VUSB_DT_INTERFACE_MIN_LEN )
+                return 0;
+            cnt->num_id++;
+            ifnum = id->bInterfaceNumber;
+            cnt->idmap[ifnum >> 6] |= (1 << (ifnum & 0x1f));
+            break;
+        case VUSB_DT_ENDPOINT:
+            ed = (PVUSBDESCENDPOINT)tmp;
+            if ( ed->bLength < VUSB_DT_ENDPOINT_MIN_LEN )
+                return 0;
+            cnt->num_ed++;
+            break;
+        default:
+            break;
+        }
+    }
+
+    /* count interfaces */
+    for(i=0; i < RT_ELEMENTS(cnt->idmap); i++)
+        for(x=1; x; x<<=1)
+            if ( cnt->idmap[i] & x )
+                cnt->num_if++;
+
+    return 1;
+}
+
+
+
+
+/* Synchronously obtain a standard USB descriptor for a device, used in order
+ * to grab configuration descriptors when we first add the device
+ */
+static void *GetStdDescSync(PUSBPROXYDEV pProxyDev, uint8_t iDescType, uint8_t iIdx, uint16_t LangId, uint16_t cbHint)
+{
+    for (;;)
+    {
+        /*
+         * Setup a MSG URB, queue and reap it.
+         */
+        VUSBURB Urb;
+        Urb.u32Magic = VUSBURB_MAGIC;
+        Urb.enmState = VUSBURBSTATE_IN_FLIGHT;
+        Urb.pszDesc = (char*)"URB sync";
+        memset(&Urb.VUsb, 0, sizeof(Urb.VUsb));
+        memset(&Urb.Hci, 0, sizeof(Urb.Hci));
+        Urb.pDev = (struct VUSBDEV*) pProxyDev;
+        Urb.Dev.pvPrivate = NULL;
+        Urb.Dev.pNext = NULL;
+#ifndef RT_OS_LINUX
+        Urb.pUsbIns = (PPDMUSBINS)pProxyDev;
+#endif
+        Urb.DstAddress = 0;
+        Urb.EndPt = 0;
+        Urb.enmType = VUSBXFERTYPE_MSG;
+        Urb.enmDir = VUSBDIRECTION_IN;
+        Urb.fShortNotOk = false;
+        Urb.enmStatus = VUSBSTATUS_INVALID;
+        cbHint = RT_MIN(cbHint, sizeof(Urb.abData) - sizeof(VUSBSETUP));
+        Urb.cbData = cbHint + sizeof(VUSBSETUP);
+
+        PVUSBSETUP pSetup = (PVUSBSETUP)Urb.abData;
+        pSetup->bmRequestType = VUSB_DIR_TO_HOST | VUSB_REQ_STANDARD | VUSB_TO_DEVICE;
+        pSetup->bRequest = VUSB_REQ_GET_DESCRIPTOR;
+        pSetup->wValue = (iDescType << 8) | iIdx;
+        pSetup->wIndex = LangId;
+        pSetup->wLength = cbHint;
+
+        if (RT_FAILURE(pProxyDev->pOps->pfnUrbQueue(&Urb)))
+        {
+            ERR("urb queue failed\n");
+            break;
+        }
+
+
+        /* Don't wait forever, it's just a simple request that should
+           return immediately. Since we're executing in the EMT thread
+           it's important not to get stuck here. (Some of the builtin
+           iMac devices may not refuse respond for instance.) */
+        PVUSBURB pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, 10000 /* ms */);
+        if (!pUrbReaped)
+        {
+            pProxyDev->pOps->pfnUrbCancel(&Urb);
+            pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, RT_INDEFINITE_WAIT);
+        }
+        if (pUrbReaped != &Urb)
+        {
+            ERR("GetStdDescSync: pfnUrbReap failed, pUrbReaped=%p\n", pUrbReaped);
+            break;
+        }
+
+        if (Urb.enmStatus != VUSBSTATUS_OK)
+        {
+            ERR("GetStdDescSync: Urb.enmStatus=%d\n", Urb.enmStatus);
+            break;
+        }
+
+        /*
+         * Check the length, config descriptors have total_length field
+         */
+        uint8_t *pbDesc = (uint8_t *)(pSetup + 1);
+        uint32_t cbDesc;
+        if (iDescType == VUSB_DT_CONFIG)
+        {
+            if (Urb.cbData < sizeof(VUSBSETUP) + 4)
+            {
+               TRACE("GetStdDescSync: Urb.cbData=%#x (min 4)\n", Urb.cbData);
+                break;
+            }
+            cbDesc = RT_LE2H_U16(((uint16_t *)pbDesc)[1]);
+        }
+        else
+        {
+            if (Urb.cbData < sizeof(VUSBSETUP) + 1)
+            {
+                TRACE("GetStdDescSync: Urb.cbData=%#x (min 1)\n", Urb.cbData);
+                break;
+            }
+            cbDesc = ((uint8_t *)pbDesc)[0];
+        }
+
+        TRACE("GetStdDescSync: got Urb.cbData=%u, cbDesc=%u cbHint=%u\n", Urb.cbData, cbDesc, cbHint);
+
+        if (    Urb.cbData == cbHint + sizeof(VUSBSETUP)
+            &&  cbDesc > Urb.cbData - sizeof(VUSBSETUP))
+        {
+            cbHint = cbDesc;
+            if (cbHint > sizeof(Urb.abData))
+            {
+                AssertMsgFailed(("cbHint=%u\n", cbHint));
+                break;
+            }
+            continue;
+        }
+        Assert(cbDesc <= Urb.cbData - sizeof(VUSBSETUP));
+#if 0
+        vusbUrbTrace(&Urb, "GetStdDescSync", true);
+#endif
+
+        /*
+         * Fine, we got everything return a heap duplicate of the descriptor.
+         */
+
+    char* ret = (char*) malloc(cbDesc);
+    if( ret )
+        memcpy((void*)ret, (void*)pbDesc, cbDesc);
+        return ret;
+
+    }
+    return NULL;
+}
+static bool copy_config(PUSBPROXYDEV pProxyDev, uint8_t idx, PVUSBDESCCONFIGEX out)
+{
+    PVUSBDESCCONFIG cfg;
+    PVUSBINTERFACE pIf;
+    PVUSBDESCINTERFACEEX ifd;
+    PVUSBDESCENDPOINTEX epd;
+    struct desc_counts cnt;
+    void *descs;
+    size_t tot_len;
+    size_t cbIface;
+    uint32_t i, x;
+
+    descs = GetStdDescSync(pProxyDev, VUSB_DT_CONFIG, idx, 0, VUSB_DT_CONFIG_MIN_LEN);
+    if ( descs == NULL ) {
+        ERR("copy_config: GetStdDescSync failed\n");
+        return false;
+    }
+
+    cfg = (PVUSBDESCCONFIG)descs;
+    tot_len = RT_LE2H_U16(cfg->wTotalLength);
+
+    if ( !count_descriptors(&cnt, (uint8_t *)descs, tot_len) ) {
+        ERR("copy_config: count_descriptors failed\n");
+        goto err;
+    }
+
+    if ( cfg->bNumInterfaces != cnt.num_if )
+        ERR("usb-proxy: config%u: bNumInterfaces %u != %u\n",
+            idx, cfg->bNumInterfaces, cnt.num_if);
+
+    TRACE("usb-proxy: config%u: %u bytes id=%u ed=%u if=%u\n",
+        idx, tot_len, cnt.num_id, cnt.num_ed, cnt.num_if);
+
+    cbIface = cnt.num_if * sizeof(VUSBINTERFACE)
+           + cnt.num_id * sizeof(VUSBDESCINTERFACEEX)
+           + cnt.num_ed * sizeof(VUSBDESCENDPOINTEX);
+    out->paIfs = (PCVUSBINTERFACE)malloc(cbIface);
+    if ( out->paIfs == NULL ) {
+        free(descs);
+        return false;
+    }
+
+    /* Stash a pointer to the raw config descriptor; we may need bits of it later.  */
+    out->pvOriginal = descs;
+
+    pIf = (PVUSBINTERFACE)out->paIfs;
+    ifd = (PVUSBDESCINTERFACEEX)&pIf[cnt.num_if];
+    epd = (PVUSBDESCENDPOINTEX)&ifd[cnt.num_id];
+
+    out->Core.bLength = cfg->bLength;
+    out->Core.bDescriptorType = cfg->bDescriptorType;
+    out->Core.wTotalLength = 0; /* Auto Calculated */
+    out->Core.bNumInterfaces = (uint8_t)cnt.num_if;
+    out->Core.bConfigurationValue = cfg->bConfigurationValue;
+    out->Core.iConfiguration = cfg->iConfiguration;
+    out->Core.bmAttributes = cfg->bmAttributes;
+    out->Core.MaxPower = cfg->MaxPower;
+
+    for(i=0; i < 4; i++)
+        for(x=0; x < 32; x++)
+            if ( cnt.idmap[i] & (1 << x) )
+                if ( !copy_interface(pIf++, (i << 6) | x, &ifd, &epd, (uint8_t *)out->pvOriginal, tot_len) ) {
+                    ERR("copy_interface(%d,,) failed\n", pIf - 1);
+                    goto err;
+                }
+
+    return true;
+err:
+    ERR("usb-proxy: config%u: Corrupted configuration descriptor\n", idx);
+    free(descs);
+    return false;
+}
+
+
+static bool usbProxyGetDeviceDesc(PUSBPROXYDEV pProxyDev, PVUSBDESCDEVICE pOut)
+{
+    /*
+     * Get the descriptor from the device.
+     */
+    PVUSBDESCDEVICE pIn = (PVUSBDESCDEVICE)GetStdDescSync(pProxyDev, VUSB_DT_DEVICE, 0, 0, VUSB_DT_DEVICE_MIN_LEN);
+    if (!pIn)
+    {
+        ERR("usbProxyGetDeviceDesc: pProxyDev=: GetStdDescSync failed\n");
+        return false;
+    }
+    if (pIn->bLength < VUSB_DT_DEVICE_MIN_LEN)
+        return false;
+
+    /*
+     * Convert it.
+     */
+    pOut->bLength            = VUSB_DT_DEVICE_MIN_LEN;
+    pOut->bDescriptorType    = VUSB_DT_DEVICE;
+    pOut->bcdUSB             = RT_LE2H_U16(pIn->bcdUSB);
+    pOut->bDeviceClass       = pIn->bDeviceClass;
+    pOut->bDeviceSubClass    = pIn->bDeviceSubClass;
+    pOut->bDeviceProtocol    = pIn->bDeviceProtocol;
+    pOut->bMaxPacketSize0    = pIn->bMaxPacketSize0;
+    pOut->idVendor           = RT_LE2H_U16(pIn->idVendor);
+    pOut->idProduct          = RT_LE2H_U16(pIn->idProduct);
+    pOut->bcdDevice          = RT_LE2H_U16(pIn->bcdDevice);
+    pOut->iManufacturer      = pIn->iManufacturer;
+    pOut->iProduct           = pIn->iProduct;
+    pOut->iSerialNumber      = pIn->iSerialNumber;
+    pOut->bNumConfigurations = pIn->bNumConfigurations;
+
+    free(pIn);
+    return true;
+}
+
+
+
+
+
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
+                        int class_id, int vendor_id, int product_id,
+                        const char *product_name, int speed);
+
+
+static QEMUTimer *usb_auto_timer;
+static VMChangeStateEntry *usb_vmstate;
+
+
+
+static void async_complete(void *opaque)
+{
+#ifdef CONFIG_LINUX
+    USBHostDevice *s = opaque;
+    USBPROXYDEV* pThis=&s->vboxdev;
+#else
+    USBPROXYDEV* pThis= (PUSBPROXYDEV)opaque;
+    USBHostDevice *s = container_of(pThis, USBHostDevice, vboxdev);
+#endif
+    PVUSBURB vUrb = NULL;
+    TRACE("callback called async_complete Hostdevice: 0x%p, PROXYDEV:0x%p\n", s, pThis);
+
+    if (QTAILQ_EMPTY(&s->requests)) {
+        /* TODO : need to handle unplugged event
+         *
+         * Qemu can't know unplugged event directly so that should poll host device.
+         * But, the usb lib doesn't supply that interface.
+         * Besides, it is difficult to use existing interface,
+         * becasue of difference from implemetation for each host.
+         * (Usb lib sets 'fDetached' only when it fails to io operations which is
+         *  invoked by exported interface, such like open, queue, reap, and so on.)
+         * Therefore, we have other idea that is to support by command of host.
+         */
+        return;
+    }
+
+    while (1) {
+        USBHostRequest* r;
+        USBPacket* p;
+        vUrb = pThis->pOps->pfnUrbReap(pThis, 0);
+        if (!vUrb) {
+            TRACE("(%s) urb reap failed(errno=%d)\n", __func__, errno);
+            usb_host_increase_callback_time(s);
+            return;
+        }
+        r = (USBHostRequest*)vUrb;
+        TRACE("complete req:0x%p\n", r);
+#ifdef CONFIG_WIN32
+        usb_host_del_event(r);
+#endif
+
+#if ISOC
+        if (r->iso_frame_idx == -1) {
+            int inflight;
+            int pid = (r->vbox_urb.EndPt & USB_DIR_IN) ?
+                USB_TOKEN_IN : USB_TOKEN_OUT;
+            int ep = r->vbox_urb.EndPt & 0xf;
+            if (vUrb->enmStatus == VUSBSTATUS_STALL) {
+                INFO("async_complete set status halt\n");
+                return;
+            }
+            r->iso_frame_idx = 0;
+            inflight = change_iso_inflight(s, pid, ep, -1);
+            if (inflight == 0 && is_iso_started(s, pid, ep)) {
+                ERR("husb: out of buffers for iso stream\n");
+            }
+            continue;
+        }
+#endif
+
+        p = r->p;
+        //fill packet structure
+        if (p) {
+            switch (vUrb->enmStatus){
+                /** Transer was ok. */
+            case VUSBSTATUS_OK:
+                {
+                    p->actual_length = vUrb->cbData;
+                    p->status = USB_RET_SUCCESS;
+               }
+                break;
+                /** Transfer stalled, endpoint halted. */
+            case VUSBSTATUS_STALL:
+                //set_halt(s, p->pid, p->ep->nr);
+                p->status = USB_RET_STALL;
+                ERR("VUSBSTATUS_STALL\n");
+                break;
+                /** Device not responding. */
+            case VUSBSTATUS_DNR:
+                ERR("VUSBSTATUS_DNR\n");
+                /** CRC error. */
+            case VUSBSTATUS_CRC:
+                ERR("VUSBSTATUS_CRC\n");
+                /** Data overrun error. */
+            case VUSBSTATUS_DATA_UNDERRUN:
+                ERR("VUSBSTATUS_DATA_UNDERRUN\n");
+                /** The isochronous buffer hasn't been touched. */
+            case VUSBSTATUS_NOT_ACCESSED:
+                ERR("VUSBSTATUS_NOT_ACCESSED\n");
+                /** Canceled/undone URB (VUSB internal). */
+            case VUSBSTATUS_UNDO:
+                ERR("VUSBSTATUS_UNDO\n");
+                /** Invalid status. */
+                p->status = USB_RET_IOERROR;
+                break;
+            default:
+                assert(0);
+                break;
+            }
+            /* status OK */
+            if ( p->state == USB_PACKET_CANCELED )
+            {
+                usb_host_req_free(r);
+                continue;
+            }
+
+            if ( vUrb->enmType == VUSBXFERTYPE_MSG )
+            {
+                trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                            p->status, vUrb->cbData);
+                if( r->in && p->actual_length )
+                    memcpy(r->cbuf, (void*)(((unsigned char*)vUrb->abData) + 8), p->actual_length);
+                usb_generic_async_ctrl_complete(&s->dev, p);
+            } else {
+
+                if (r->in && vUrb->cbData) {
+                    p->actual_length = 0;
+                    usb_packet_copy(r->p, vUrb->abData, vUrb->cbData);
+                    p->actual_length = vUrb->cbData;
+                }
+                usb_packet_complete(&s->dev, p);
+            }
+            usb_host_req_free(r);
+        }
+    }
+    usb_host_reset_callback_time(s);
+}
+
+static int usb_host_auto_scan(void *opaque, int bus_num,
+                              int addr, const char *port,
+                              int class_id, int vendor_id, int product_id,
+                              const char *product_name, int speed)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    /* Ignore hubs */
+    if (class_id == 9)
+        return 0;
+
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        f = &s->match;
+
+        if (f->bus_num > 0 && f->bus_num != bus_num) {
+            continue;
+        }
+        if (f->addr > 0 && f->addr != addr) {
+            continue;
+        }
+        if (f->port != NULL && strcmp(f->port, port) != 0) {
+            continue;
+        }
+
+        if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
+            continue;
+        }
+
+        if (f->product_id > 0 && f->product_id != product_id) {
+            continue;
+        }
+        /* We got a match */
+        s->seen++;
+
+        /* Already attached ? */
+        if (s->dev.attached) {
+            return 0;
+        }
+        //DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); 
+
+        if (usb_host_open_device(s, bus_num, addr, product_id, vendor_id, port, product_name, speed) < 0) {
+            s->errcount++;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+
+
+/*
+ * modify to use libvboxusb to enumerate
+ */
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    libvbox_usb *iter, *intern = NULL;
+    int ret = 0;
+    int rc, i;
+
+    rc = VBOXUSB_enum_devices(&intern);
+    if(!rc)
+        goto end;
+
+
+    iter = intern;
+    for( i = 0; i < rc; i++)
+    {
+        ret = func(opaque, iter->bus_num, iter->addr, iter->port, iter->class_id, iter->vendor_id, iter->product_id, iter->product_name, iter->speed);
+        if( ret )
+            goto end;
+        iter ++;
+    }
+
+end:
+    if(intern != NULL)
+        VBOXUSB_free(intern);
+    return ret;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID, "Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+    const struct usb_class_info *p;
+    for(p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class) {
+            break;
+        }
+    }
+    return p->class_name;
+}
+
+static void usb_host_ep_update(USBHostDevice *s)
+{
+    int i, j, k;
+    uint8_t devep, type;
+    int pid, ep;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    USBDevice *udev = USB_DEVICE(s);
+
+#if ISOC
+    int m;
+
+    usb_ep_reset(udev);
+    TRACE("usb_host_ep_update conf_num %d\n", pThis->DevDesc.bNumConfigurations);
+
+    for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++){
+        PVUSBDESCCONFIGEX ifconfig = (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i] ;
+        const VUSBINTERFACE* paIfs = ifconfig->paIfs;
+        TRACE("usb_host_ep_update interface %d\n", ifconfig->Core.bNumInterfaces);
+        for( m = 0; m < ifconfig->Core.bNumInterfaces; m++ )
+        {
+            TRACE("usb_host_ep_update cSettings %d\n", paIfs[m].cSettings);
+            for( j = 0; j < paIfs[m].cSettings; j++ )
+            {
+                if (udev->altsetting[m] != j) {
+                    continue;
+                }
+                const PVUSBDESCINTERFACEEX endp = (PVUSBDESCINTERFACEEX)&paIfs[m].paSettings[j];
+                TRACE("usb_host_ep_update endpoint %d\n", endp->Core.bNumEndpoints);
+                for( k = 0; k < endp->Core.bNumEndpoints; k++ )
+                {
+                    const PVUSBDESCENDPOINTEX end = (PVUSBDESCENDPOINTEX)&endp->paEndpoints[k];
+                    devep = end->Core.bEndpointAddress;
+                    pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+                    ep = devep & 0xf;
+                    TRACE("usb_host_ep_update bmAttributes: %d, max: %d\n", end->Core.bmAttributes, end->Core.wMaxPacketSize);
+                    type = end->Core.bmAttributes & 0x3;
+                    TRACE("usb_host_ep_update ep: %d\n", ep);
+                    if (ep == 0) {
+                        trace_usb_host_parse_error(s->bus_num, s->addr,
+                                                   "invalid endpoint address");
+                        ERR("INVALID endpoint address\n");
+                        return;
+                    }
+                    TRACE("usb_host_ep_update get_type: %d:%d\n", usb_ep_get_type(udev, pid, ep), USB_ENDPOINT_XFER_INVALID);
+                    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");
+                        ERR("duplicate endpoint address\n");
+                        continue;
+                    }
+                    INFO("usb_host_ep_update pid: %d, ep: %d, type : %d, interface: %d, max: %d\n", pid, ep, type, m, end->Core.wMaxPacketSize);
+                    usb_ep_set_max_packet_size(udev, pid, ep,
+                                               end->Core.wMaxPacketSize);
+                    usb_ep_set_type(udev, pid, ep, type);
+                    usb_ep_set_ifnum(udev, pid, ep, m);
+
+                    if (type == USB_ENDPOINT_XFER_ISOC) {
+                        usb_ep_set_pipeline(udev, pid, ep, true);
+                        usb_ep_set_max_streams(udev, pid, ep, end->Core.wMaxPacketSize);
+                    }
+
+                    usb_ep_set_halted(udev, pid, ep, 0);
+                }
+            }
+        }
+    }
+#else
+    for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++){
+        PVUSBDESCCONFIGEX ifconfig = (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i] ;
+        const VUSBINTERFACE* paIfs = ifconfig->paIfs;
+        for( j = 0; j < paIfs->cSettings; j++ )
+        {
+            const PVUSBDESCINTERFACEEX endp = (PVUSBDESCINTERFACEEX)&paIfs->paSettings[j];
+            for( k = 0; k < endp->Core.bNumEndpoints; k++ )
+            {
+                const PVUSBDESCENDPOINTEX end = (PVUSBDESCENDPOINTEX)&endp->paEndpoints[k];
+                devep = end->Core.bEndpointAddress;
+                pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+                ep = devep & 0xf;
+                type = end->Core.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;
+                }
+                usb_ep_set_max_packet_size(udev, pid, ep,
+                                           end->Core.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);
+            }
+        }
+    }
+#endif
+}
+/*
+ * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
+ * this function assumes this is safe, if:
+ * 1) There are no isoc endpoints
+ * 2) There are no interrupt endpoints with a max_packet_size > 64
+ * Note bulk endpoints with a max_packet_size > 64 in theory also are not
+ * usb1 compatible, but in practice this seems to work fine.
+ */
+static int usb_host_full_speed_compat(USBHostDevice *s)
+{
+    int i, j, k;
+    USBPROXYDEV* pThis=&s->vboxdev;
+
+    for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++){
+        PVUSBDESCCONFIGEX ifconfig = (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i] ;
+        const VUSBINTERFACE* paIfs = ifconfig->paIfs;
+        for( j = 0; j < paIfs->cSettings; j++ )
+        {
+            const PVUSBDESCINTERFACEEX endp = (PVUSBDESCINTERFACEEX)&paIfs->paSettings[j];
+            for( k = 0; k < endp->Core.bNumEndpoints; k++ )
+            {
+                const PVUSBDESCENDPOINTEX end = (PVUSBDESCENDPOINTEX)&endp->paEndpoints[k];
+                switch ( end->Core.bmAttributes & 0x3 ) {
+                case 0x00: /* CONTROL */
+                    break;
+                case 0x01: /* ISO */
+                    return 0;
+                case 0x02: /* BULK */
+                    break;
+                case 0x03: /* INTERRUPT */
+                    if (end->Core.wMaxPacketSize > 64)
+                        return 0;
+                    break;
+                }
+            }
+        }
+    }
+    return 1;
+}
+static int usb_host_open_device(USBHostDevice* s, int nBus, int nAddr, int nPid, int nVid,
+                                const char* port, const char* prod_name, int speed)
+{
+    char filename[VBOX_MAX_PATH] = {0,};
+    int rc;
+    USBDevice *udev = USB_DEVICE(s);
+    USBPROXYDEV* pThis=&s->vboxdev;
+    void* Backend = NULL;
+    unsigned i;
+    Error* open_error = NULL;
+
+    rc = VBOXUSB_preparedev(nBus, nAddr, nPid, nVid);
+    if( rc )
+    {
+        ERR("VBOXUSB_preparedev failed:%d\n", rc);
+        goto fail;
+    }
+    TRACE("prepare device :%d\n", rc);
+    rc = VBOXUSB_getdevname(filename, nBus, nAddr, nPid, nVid);
+    if( rc )
+    {
+        ERR("VBOXUSB_getdevname failed:%d\n", rc);
+        goto fail;
+        //todo _cleanup
+    }
+    TRACE("sizeof(path):%d, nBuf:%d, nAddr:%d\n", sizeof(filename), nBus, nAddr);
+    TRACE("open:%s \n", filename);
+    rc = pThis->pOps->pfnOpen(pThis, filename, Backend);
+    if( RT_FAILURE(rc))
+    {
+        ERR("usbProxyOpen failed with %d\n", rc);
+        goto fail;
+    }
+    s->bus_num = nBus;
+    s->addr = nAddr;
+
+    TRACE("open device:%d\n", rc);
+
+    /* 4. get(clear) all interface */
+    if (!usbProxyGetDeviceDesc(pThis, &pThis->DevDesc))
+    {
+        ERR("usbProxyConstruct: usbProxyGetDeviceDesc failed\n");
+        rc = VERR_READ_ERROR;
+        goto desc_fail;
+    }
+
+    size_t cbConfigs = pThis->DevDesc.bNumConfigurations * sizeof(pThis->paCfgDescs[0]);
+    pThis->paCfgDescs = (PVUSBDESCCONFIGEX)malloc(cbConfigs);
+
+    for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++){
+        if (!copy_config(pThis, i, (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i]))
+            break;
+    }
+
+    TRACE("%d/%d\n", i, pThis->DevDesc.bNumConfigurations);
+    if (i < pThis->DevDesc.bNumConfigurations)
+    {
+        ERR("usbProxyConstruct: copy_config failed, i=%d\n", i);
+        rc = VERR_READ_ERROR;
+        goto desc_fail;
+    }
+
+    if( pThis->pOps->pfnInit){
+        rc = pThis->pOps->pfnInit(pThis);
+        if (RT_FAILURE(rc)){
+            ERR("init failed:%d\n", rc);
+            goto init_fail;
+        }
+        pThis->fInited = true;
+    }
+
+/*
+ * Register callbacks of its own OS
+ *
+ * NOTE : Windows makes changes of event handlers on runtime
+ *        Therefore, for windows, registered to qemu main polling loop
+ *        for each urb request.
+ */
+#if defined(CONFIG_DARWIN)
+    s->callback_timer = timer_new_ms(QEMU_CLOCK_REALTIME, async_complete, pThis);
+    usb_host_reset_callback_time(s);
+#elif defined(CONFIG_LINUX)
+    s->fd  = (int)*((int*)pThis->Backend.pv);
+    qemu_set_fd_handler(s->fd, NULL, async_complete, s);
+#endif
+
+    if (!prod_name || prod_name[0] == '\0') {
+        snprintf(udev->product_desc, sizeof(udev->product_desc),
+                 "host:%d.%d", nBus, nAddr);
+    } else {
+        pstrcpy(udev->product_desc, sizeof(udev->product_desc),
+                prod_name);
+    }
+
+    udev->speed = speed;
+    udev->speedmask = (1 << udev->speed);
+    if (udev->speed == USB_SPEED_HIGH && usb_host_full_speed_compat(s)) {
+        udev->speedmask |= USB_SPEED_MASK_FULL;
+    }
+
+    usb_ep_init(udev);
+    usb_host_ep_update(s);
+
+    usb_device_attach(udev, &open_error);
+    if (open_error) {
+        error_report("%s", error_get_pretty(open_error));
+        error_free(open_error);
+        rc = VERR_UNRESOLVED_ERROR;
+        goto fail;
+
+    }
+    INFO("usb device attached\n");
+
+    return 0;
+init_fail:
+desc_fail:
+    pThis->pOps->pfnClose(&s->vboxdev);
+fail:
+    return rc;
+}
+
+static void usb_host_cancel_packet(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    USBPROXYDEV* pThis=&s->vboxdev;
+    USBHostRequest *r;
+
+    if (p->combined) {
+        WARN("combined:%d\n", __LINE__);
+        //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) {
+        pThis->pOps->pfnUrbCancel(&r->vbox_urb);
+        r->p = NULL; /* mark as dead */
+    }
+}
+static void usb_host_handle_reset(USBDevice *dev)
+{
+    int rc;
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    USBPROXYDEV* pThis = &s->vboxdev;
+#ifndef CONFIG_LINUX
+    if (dev->configuration == 0) {
+        return;
+    }
+#endif
+    rc = pThis->pOps->pfnReset(pThis, 1);
+    if (RT_FAILURE(rc)){
+        ERR("proxy reset failed :%d\n", rc);
+    }
+}
+#if 0
+static void usb_host_abort_pendings(USBHostDevice* s)
+{
+    USBHostRequest *r;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    QTAILQ_FOREACH(r, &s->requests, next) {
+        if(r && r->p){
+            r->p->status = USB_RET_NODEV;
+            pThis->pOps->pfnUrbCancel(&r->vbox_urb);
+            r->p = NULL;
+            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);
+            }
+            usb_host_req_free(r);
+        }
+    }
+
+
+}
+#endif
+static int usb_host_close(USBHostDevice *s)
+{
+    int rc = 0;
+    USBDevice *udev = USB_DEVICE(s);
+    USBPROXYDEV* pThis=&s->vboxdev;
+
+    TRACE("host_close\n");
+    if (!s->vboxdev.Backend.pv)
+    {
+        INFO("USB device object does not exist\n");
+        return -1;
+    }
+#if 0
+    usb_host_iso_free_all(s);
+#endif
+    if (udev->attached) {
+        usb_device_detach(udev);
+    }
+
+    rc = pThis->pOps->pfnReset(pThis, 1);
+    if (RT_FAILURE(rc))
+    {
+        ERR("proxy dev reset failed in host_close:%d\n", rc);
+    }
+
+    if( pThis->pOps->pfnReleaseInterface )
+    {
+        int i, j;
+        for( j = 0; j < pThis->DevDesc.bNumConfigurations; j++){
+            PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)&pThis->paCfgDescs[j];
+            for (i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
+                pThis->pOps->pfnReleaseInterface(pThis, i);
+            free((void*)pThis->paCfgDescs[j].paIfs);
+        }
+    }
+    free(pThis->paCfgDescs);
+    pThis->pOps->pfnClose(&s->vboxdev);
+
+#if defined(CONFIG_LINUX)
+    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+#elif defined(CONFIG_DARWIN)
+    timer_del(s->callback_timer);
+    timer_free(s->callback_timer);
+#endif
+    //TODO WIndows
+
+    s->fd = -1;
+    usb_host_auto_check(NULL);
+    return 0;
+}
+
+static void usb_host_nodev_bh(void *opaque)
+{
+    TRACE("(%s)\n", __func__);
+    USBHostDevice *s = opaque;
+    usb_host_close(s);
+}
+
+static void usb_host_nodev(USBHostDevice *s)
+{
+    if (!s->bh_nodev) {
+        s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s);
+    }
+    TRACE("(%s) invoking bh_nodev\n", __func__);
+    qemu_bh_schedule(s->bh_nodev);
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+    TRACE("(%s)\n", __func__);
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    qemu_remove_exit_notifier(&s->exit);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    usb_host_close(s);
+}
+
+#if ISOC
+#define ISO_FRAME_DESC_PER_URB  8
+static USBHostRequest *usb_host_alloc_iso(USBHostDevice *s, USBPacket *p, int pid, uint8_t ep)
+{
+    USBHostRequest *req;
+    int i, j, len = p->ep->max_packet_size;
+
+    req = g_malloc0(s->iso_urb_count * sizeof(USBHostRequest));
+    for (i = 0; i < s->iso_urb_count; i++) {
+        req[i].host = s;
+        req[i].p = p;
+        req[i].in = (p->pid == USB_TOKEN_IN);
+        req[i].vbox_urb.pDev = (PVUSBDEV)&req->host->vboxdev;
+        req[i].vbox_urb.EndPt = p->ep->nr;
+        req[i].vbox_urb.enmDir = (p->pid == USB_TOKEN_IN) ? VUSBDIRECTION_IN : VUSBDIRECTION_OUT;
+        req[i].vbox_urb.fShortNotOk = false;
+        req[i].vbox_urb.enmType = VUSBXFERTYPE_ISOC;
+        req[i].vbox_urb.cbData = ISO_FRAME_DESC_PER_URB * len;
+        req[i].vbox_urb.cIsocPkts = ISO_FRAME_DESC_PER_URB;
+        for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
+            req[i].vbox_urb.aIsocPkts[j].cb = len;
+        if (pid == USB_TOKEN_IN) {
+            req[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
+        }
+    }
+    set_iso_urb(s, pid, ep, req);
+
+    return req;
+}
+
+static void urb_status_to_usb_ret(USBPacket *p, int status)
+{
+    switch (status) {
+    case VUSBSTATUS_STALL:
+        p->status = USB_RET_STALL;
+        break;
+    default:
+        p->status = USB_RET_NAK;
+    }
+}
+
+static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
+{
+    int i, j, rc, max_packet_size, offset, len = 0;
+    uint8_t *buf;
+    USBHostRequest *req;
+    USBPROXYDEV* pThis=&s->vboxdev;
+
+    max_packet_size = p->ep->max_packet_size;
+    if (max_packet_size == 0)
+        return;
+
+    req = get_iso_urb(s, p->pid, p->ep->nr);
+    if (!req) {
+        req = usb_host_alloc_iso(s, p, p->pid, p->ep->nr);
+    }
+
+    i = get_iso_urb_idx(s, p->pid, p->ep->nr);
+    j = req[i].iso_frame_idx;
+
+    INFO("usb_host_handle_iso_data urb idx: %d, iso_frame_index: %d\n", i, j);
+
+    if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
+        if (in) {
+            if (req[i].vbox_urb.enmStatus) {
+                urb_status_to_usb_ret(p, req[i].vbox_urb.enmStatus);
+                /* Move to the next urb */
+                req[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
+            /* Check frame status */
+            } else if (req[i].vbox_urb.aIsocPkts[j].enmStatus) {
+                urb_status_to_usb_ret(p, req[i].vbox_urb.aIsocPkts[j].enmStatus);
+            /* Check the frame fits */
+            } else if (req[i].vbox_urb.aIsocPkts[j].cb
+                       > p->iov.size) {
+                ERR("received iso data is larger then packet\n");
+                p->status = USB_RET_NAK;
+            /* All good copy data over */
+            } else {
+                len = req[i].vbox_urb.aIsocPkts[j].cb;
+                buf  = req[i].vbox_urb.abData +
+                    j * req[i].vbox_urb.aIsocPkts[0].cb;
+                usb_packet_copy(p, buf, len);
+            }
+        } else {
+            len = p->iov.size;
+            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
+            INFO("packet size: %d, offset : %d\n", len, offset);
+
+            /* Check the frame fits */
+            if (len > max_packet_size) {
+                ERR("husb: send iso data is larger then max packet size\n");
+                return;
+            }
+
+            /* All good copy data over */
+            usb_packet_copy(p, req[i].vbox_urb.abData + offset, len);
+            req[i].vbox_urb.aIsocPkts[j].cb = len;
+            req[i].vbox_urb.aIsocPkts[j].off = offset;
+            offset += len;
+            set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
+
+            /* Start the stream once we have buffered enough data */
+            if (!is_iso_started(s, p->pid, p->ep->nr) && i == (s->iso_urb_count / 2)
+                    && j == (ISO_FRAME_DESC_PER_URB - 1)) {
+                set_iso_started(s, p->pid, p->ep->nr);
+            }
+        }
+        req[i].iso_frame_idx++;
+        if (req[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+            i = (i + 1) % s->iso_urb_count;
+            set_iso_urb_idx(s, p->pid, p->ep->nr, i);
+        }
+    } else {
+        if (in) {
+            set_iso_started(s, p->pid, p->ep->nr);
+        } else {
+            ERR("hubs: iso out error no free buffer, dropping packet\n");
+        }
+    }
+
+    if (is_iso_started(s, p->pid, p->ep->nr)) {
+        for (i = 0; i < s->iso_urb_count; i++) {
+            if (req[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
+                TRACE("Queue urb address: %p\n", &req[i].vbox_urb);
+                rc = pThis->pOps->pfnUrbQueue(&req[i].vbox_urb);
+                if (rc != 0) {
+                    ERR("usb_host_handle_iso_data pfnUrbQueue RC: %d\n", rc);
+#ifdef CONFIG_LINUX
+                    if( rc == ETIMEDOUT )
+                        p->status = USB_RET_NAK;
+                    else
+#endif
+                        p->status = USB_RET_STALL;
+                    return;
+                }
+
+                req[i].iso_frame_idx = -1;
+                change_iso_inflight(s, p->pid, p->ep->nr, 1);
+            }
+        }
+    }
+}
+#endif
+
+static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    USBPROXYDEV* pThis=&s->vboxdev;
+    USBHostRequest* req;
+    int rc;
+    size_t size;
+    uint8_t type;
+
+    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 (p->ep->halted) {
+        p->status = USB_RET_STALL;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+
+    type = usb_ep_get_type(dev, p->pid, p->ep->nr);
+    switch (type){
+    case USB_ENDPOINT_XFER_INT:
+        size = p->iov.size;
+        req = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN);
+        assert(req);
+        break;
+    case USB_ENDPOINT_XFER_BULK:
+        size = usb_packet_size(p);
+        req = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN);
+        assert(req);
+        break;
+    case USB_ENDPOINT_XFER_ISOC:
+#if ISOC
+        usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+        return;
+#endif
+    default:
+        WARN("not implemented\n");
+        p->status = USB_RET_STALL;
+        return;
+    }
+    usb_fill_data_urb(req, p, type, size);
+    rc = pThis->pOps->pfnUrbQueue(&req->vbox_urb);
+    if (RT_FAILURE(rc)) {
+        p->status = USB_RET_NODEV;
+        ERR("(%s) urb queueing failed(err=%d, detached=%d)\n",
+                    __func__, rc, pThis->fDetached? 1:0);
+        if (pThis->fDetached)
+            usb_host_nodev(s);
+        return;
+    }
+
+#ifdef CONFIG_WIN32
+    usb_host_add_event(req);
+#endif
+    usb_host_reset_callback_time(s);
+    p->status = USB_RET_ASYNC;
+}
+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)
+{
+#ifndef CONFIG_LINUX
+    USBPROXYDEV* pThis=&s->vboxdev;
+    int ret = 0;
+
+    if( pThis->pOps->pfnReleaseInterface )
+    {
+        int i;
+        PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)&pThis->paCfgDescs[config-1];
+        for (i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
+            pThis->pOps->pfnReleaseInterface(pThis, i);
+    }
+
+    if(pThis->pOps->pfnSetConfig)
+    {
+        ret = pThis->pOps->pfnSetConfig(pThis, config);
+        if( RT_FAILURE(ret) ){
+            ERR("set config failed then what???:%d\n", ret);
+            return ;
+        }
+    }
+    p->status = USB_RET_SUCCESS;
+    usb_host_ep_update(s);
+    TRACE("set config success\n");
+    ret = pThis->pOps->pfnClaimInterface(pThis, config) ;
+
+    if( RT_FAILURE(ret))
+        ERR("claim falied: %d\n", ret);
+#endif
+}
+
+#if ISOC
+static void usb_host_stop_n_free_iso(USBHostDevice *s, USBPacket *p)
+{
+    USBHostRequest *req;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    int i, killed = 0;
+
+    req = get_iso_urb(s, p->pid, p->ep->nr);
+    if (!req) {
+        return;
+    }
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        /* in flight? */
+        if (req[i].iso_frame_idx == -1) {
+            pThis->pOps->pfnUrbCancel(&req[i].vbox_urb);
+            killed++;
+        }
+    }
+
+    if (killed)
+        async_complete(s);
+
+    g_free(req);
+
+    set_iso_urb(s, p->pid, p->ep->nr, NULL);
+    set_iso_urb_idx(s, p->pid, p->ep->nr, 0);
+    clear_iso_started(s, p->pid, p->ep->nr);
+}
+#endif
+
+static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
+                                   USBPacket *p)
+{
+#if ISOC
+    INFO("usb_host_set_interface %d, %d\n", iface, alt);
+    USBDevice *udev = USB_DEVICE(s);
+    USBPROXYDEV* pThis=&s->vboxdev;
+    int rc;
+    int type;
+
+    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
+
+    type = usb_ep_get_type(udev, p->pid, p->ep->nr);
+    if (type == USB_ENDPOINT_XFER_ISOC) {
+        usb_host_stop_n_free_iso(s, p);
+    }
+
+    if (iface >= USB_MAX_INTERFACES) {
+        p->status = USB_RET_STALL;
+        return;
+    }
+
+    rc = pThis->pOps->pfnSetInterface(pThis, iface, alt);
+    if (RT_FAILURE(rc))
+    {
+        p->status = USB_RET_STALL;
+        ERR("proxy dev reset failed in host_set_interface:%d\n", rc);
+        return;
+    }
+
+    udev->altsetting[iface] = alt;
+    usb_host_ep_update(s);
+#else
+    assert(0);
+#endif
+}
+
+static USBHostRequest* usb_host_req_alloc(USBHostDevice* s, USBPacket* p, bool in)
+{
+    USBHostRequest *r = g_new0(USBHostRequest, 1);
+    r->host = s;
+    r->p = p;
+    r->in = in;
+    QTAILQ_INSERT_TAIL(&s->requests, r, next);
+    return r;
+
+}
+static void usb_host_req_free(USBHostRequest* req)
+{
+    if( req->host )
+    {
+        QTAILQ_REMOVE(&req->host->requests, req, next);
+    }
+    g_free(req);
+}
+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 printVUrb(PVUSBURB vurb)
+{
+    /*
+    INFO("===print VUrb====\n");
+    INFO("pdev:%p, endpt:0x%x, enmDir:0x%x, cbdata:%d, enmType:%d\n",
+         vurb->pDev, vurb->EndPt, vurb->enmDir, vurb->cbData, vurb->enmType);
+    INFO("####end print VUrb ######\n");
+    */
+}
+static void usb_fill_data_urb(USBHostRequest* req, USBPacket *p, uint8_t type, size_t size)
+{
+    PVUSBURB vurb = &req->vbox_urb;
+
+#ifndef RT_OS_LINUX
+    USBHostDevice *s = req->host;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    vurb->pUsbIns = (PPDMUSBINS)pThis ;
+#endif
+    vurb->pDev = (PVUSBDEV)&req->host->vboxdev;
+    vurb->EndPt = p->ep->nr;
+    vurb->enmDir = (req->in) ? VUSBDIRECTION_IN : VUSBDIRECTION_OUT;
+    vurb->fShortNotOk = false;
+
+    if(!req->in)
+    {
+        usb_packet_copy(p, (void*)vurb->abData, size);
+    }
+    vurb->cbData = size;
+    switch(type) {
+    case USB_ENDPOINT_XFER_INT:
+        vurb->enmType = VUSBXFERTYPE_INTR;
+        break;
+    case USB_ENDPOINT_XFER_BULK:
+        vurb->enmType = VUSBXFERTYPE_BULK;
+        break;
+    case USB_ENDPOINT_XFER_ISOC:
+        vurb->enmType = VUSBXFERTYPE_ISOC;
+        break;
+    default:
+        ERR("Unknown data urb type \n");
+        assert(0);
+    }
+
+    TRACE("QUEUE: data: req:0x%p\n", req);
+    printVUrb(vurb);
+}
+
+static void usb_fill_msg_urb(USBDevice *dev, USBHostRequest* req, USBPacket *p, int length, uint8_t *data)
+{
+    PVUSBURB vurb = &req->vbox_urb;
+#ifndef RT_OS_LINUX
+    USBHostDevice *s = req->host;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    vurb->pUsbIns = (PPDMUSBINS)pThis ;
+#endif
+    vurb->pDev = (PVUSBDEV)&req->host->vboxdev;
+    vurb->EndPt = p->ep->nr;
+    vurb->enmType = VUSBXFERTYPE_MSG;
+    vurb->enmDir = (req->in) ? VUSBDIRECTION_IN : VUSBDIRECTION_OUT;
+    memcpy((void*)vurb->abData, (void*)dev->setup_buf, 8);
+    if( !req->in)
+        memcpy((void*)((unsigned char*)vurb->abData + 8), data, length);
+    vurb->cbData = length + 8;
+    vurb->fShortNotOk = false;
+    TRACE("QUEUE: msg: req:0x%p\n", req);
+    printVUrb(vurb);
+}
+/**
+  @brief whether device is running or not
+  @param state : device state
+  @param running : running state
+  @return 0 on success, and a different value on error
+  @remark input parameter running is not null
+ */
+
+static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+    /* Note request is (bRequestType << 8) | bRequest */
+    int rc = 0;
+    USBPROXYDEV* pThis=&s->vboxdev;
+    USBHostRequest* req;
+
+
+    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;
+            rc = pThis->pOps->pfnClearHaltedEndpoint(&s->vboxdev, index);
+            if( RT_FAILURE(rc))
+            { //TODO don't care about errors?
+                ERR("failed on clear halted ep:%d\n", rc);
+            }
+            usb_ep_set_halted(dev, pid, index & 0x0f, 0);
+            return;
+        }
+    }
+
+    /* The rest are asynchronous */
+    if (length > sizeof(dev->data_buf)) {
+        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+                length, sizeof(dev->data_buf));
+        p->status = USB_RET_STALL;
+        return;
+    }
+
+    req = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN);
+    assert(req);
+    req->cbuf = data;
+    req->clen = length;
+    usb_fill_msg_urb(dev, req, p, length, data);
+    rc = pThis->pOps->pfnUrbQueue(&req->vbox_urb);
+    if( RT_FAILURE(rc) )
+    {
+        ERR("fail on handle_control due to queuing failed:%d\n", rc);
+        //TODO fix error code
+        usb_host_req_free(req);
+#ifdef CONFIG_LINUX
+        if( rc == ETIMEDOUT )
+            p->status = USB_RET_NAK;
+        else
+#endif
+            p->status = USB_RET_STALL;
+
+        return;
+    }
+#ifdef CONFIG_WIN32
+    usb_host_add_event(req);
+#endif
+    usb_host_reset_callback_time(s);
+    p->status = USB_RET_ASYNC;
+}
+
+static int usb_host_post_load(void *opaque, int version_id)
+{
+    USBHostDevice *dev = opaque;
+    NOREF(dev);
+    return 0;
+}
+
+static const VMStateDescription vmstate_usb_host = {
+    .name = VBOX_PROXY_DEVNAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = usb_host_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_USB_DEVICE(dev, USBHostDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+static Property usb_host_dev_properties[] = {
+    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_UINT32("vendorid",  USBHostDevice, match.vendor_id,  0),
+    DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0),
+    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
+    DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
+                    USB_HOST_OPT_PIPELINE, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
+{
+    USBHostDevice *s = container_of(n, USBHostDevice, exit);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    usb_host_close(s);
+}
+
+
+static void usb_host_realize(USBDevice *dev, Error **errp)
+{
+    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+    if (s->match.vendor_id > 0xffff) {
+        error_setg(errp, "vendorid out of range");
+        return;
+    }
+    if (s->match.product_id > 0xffff) {
+        error_setg(errp, "productid out of range");
+        return;
+    }
+    if (s->match.addr > 127) {
+        error_setg(errp, "hostaddr out of range");
+        return;
+    }
+
+    //init host device members
+    dev->auto_attach = 0;
+    dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
+
+    s->fd = -1;
+
+    s->exit.notify = usb_host_exit_notifier;
+    s->vboxdev.pOps = VBOXUSB_getbackend();
+    qemu_add_exit_notifier(&s->exit);
+
+    QTAILQ_INIT(&s->requests);
+    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+    usb_host_auto_check(NULL);
+}
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+    uc->realize           = usb_host_realize;
+    uc->product_desc   = "VBOX USB Proxy 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;
+    dc->vmsd = &vmstate_usb_host;
+    dc->props = usb_host_dev_properties;
+}
+
+static const TypeInfo usb_host_dev_info = {
+    .name          = VBOX_PROXY_DEVNAME,
+    .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 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;
+    int unconnected = 0;
+
+    if (runstate_is_running()) {
+        usb_host_scan(NULL, usb_host_auto_scan);
+
+        QTAILQ_FOREACH(s, &hostdevs, next) {
+            if (s->fd == -1) {
+                unconnected++;
+            }
+            if (s->seen == 0) {
+                s->errcount = 0;
+            }
+            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 = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL);
+        if (!usb_auto_timer) {
+            return;
+        }
+        trace_usb_host_auto_scan_enabled();
+    }
+    timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
+}
+
+static void usb_info_device(Monitor *mon, int bus_num,
+                            int addr, const char *port,
+                            int class_id, int vendor_id, int product_id,
+                            const char *product_name,
+                            int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW:
+        speed_str = "1.5";
+        break;
+    case USB_SPEED_FULL:
+        speed_str = "12";
+        break;
+    case USB_SPEED_HIGH:
+        speed_str = "480";
+        break;
+    case USB_SPEED_SUPER:
+        speed_str = "5000";
+        break;
+    default:
+        speed_str = "?";
+        break;
+    }
+
+    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                   bus_num, addr, port, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str) {
+        monitor_printf(mon, "    %s:", class_str);
+    } else {
+        monitor_printf(mon, "    Class %02x:", class_id);
+    }
+    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0') {
+        monitor_printf(mon, ", %s", product_name);
+    }
+    monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+                                const char *path, int class_id,
+                                int vendor_id, int product_id,
+                                const char *product_name,
+                                int speed)
+{
+    Monitor *mon = opaque;
+
+    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+static void dec2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%d", val);
+    }
+}
+
+static void hex2str(int val, char *str, size_t size)
+{
+    if (val == 0) {
+        snprintf(str, size, "*");
+    } else {
+        snprintf(str, size, "%04x", val);
+    }
+}
+
+void usb_host_info(Monitor *mon, const QDict *qdict)
+{
+    struct USBAutoFilter *f;
+    struct USBHostDevice *s;
+
+    usb_host_scan(mon, usb_host_info_device);
+
+    if (QTAILQ_EMPTY(&hostdevs)) {
+        return;
+    }
+    monitor_printf(mon, "  Auto filters:\n");
+    QTAILQ_FOREACH(s, &hostdevs, next) {
+        char bus[10], addr[10], vid[10], pid[10];
+        f = &s->match;
+        dec2str(f->bus_num, bus, sizeof(bus));
+        dec2str(f->addr, addr, sizeof(addr));
+        hex2str(f->vendor_id, vid, sizeof(vid));
+        hex2str(f->product_id, pid, sizeof(pid));
+        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
+                       bus, addr, f->port ? f->port : "*", vid, pid);
+    }
+}
+
+