Index: trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp
===================================================================
--- trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp	(revision 93944)
+++ trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp	(revision 93955)
@@ -294,867 +294,9 @@
 
 /**
- * Attaches a device to a specific hub.
- *
- * This function is called by the vusb_add_device() and vusbRhAttachDevice().
- *
- * @returns VBox status code.
- * @param   pHub        The hub to attach it to.
- * @param   pDev        The device to attach.
- * @thread  EMT
- */
-static int vusbHubAttach(PVUSBHUB pHub, PVUSBDEV pDev)
-{
-    LogFlow(("vusbHubAttach: pHub=%p[%s] pDev=%p[%s]\n", pHub, pHub->pszName, pDev, pDev->pUsbIns->pszName));
-    return vusbDevAttach(pDev, pHub);
-}
-
-
-/* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
-
-/** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
-static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
-{
-    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
-
-    /*
-     * Allocate a new VUSB device and initialize it.
-     */
-    PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
-    AssertReturn(pDev, VERR_NO_MEMORY);
-    int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
-    if (RT_SUCCESS(rc))
-    {
-        pUsbIns->pvVUsbDev2 = pDev;
-        rc = vusbHubAttach(&pThis->Hub, pDev);
-        if (RT_SUCCESS(rc))
-        {
-            *piPort = UINT32_MAX; /// @todo implement piPort
-            return rc;
-        }
-
-        RTMemFree(pDev->paIfStates);
-        pUsbIns->pvVUsbDev2 = NULL;
-    }
-    vusbDevRelease(pDev);
-    return rc;
-}
-
-
-/** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
-static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
-{
-    RT_NOREF(pDrvIns, iPort);
-    PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
-    Assert(pDev);
-
-    /*
-     * Deal with pending async reset.
-     * (anything but reset)
-     */
-    vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
-
-    /*
-     * Detach and free resources.
-     */
-    if (pDev->pHub)
-        vusbDevDetach(pDev);
-
-    vusbDevRelease(pDev);
-    return VINF_SUCCESS;
-}
-
-/**
- * The hub registration structure.
- */
-static const PDMUSBHUBREG g_vusbHubReg =
-{
-    PDM_USBHUBREG_VERSION,
-    vusbPDMHubAttachDevice,
-    vusbPDMHubDetachDevice,
-    PDM_USBHUBREG_VERSION
-};
-
-
-/* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
-
-
-/**
- * Callback for freeing an URB.
- * @param   pUrb    The URB to free.
- */
-static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
-{
-    /*
-     * Assert sanity.
-     */
-    vusbUrbAssert(pUrb);
-    PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
-    Assert(pRh);
-
-    Assert(pUrb->enmState != VUSBURBSTATE_FREE);
-
-    /*
-     * Free the URB description (logging builds only).
-     */
-    if (pUrb->pszDesc)
-    {
-        RTStrFree(pUrb->pszDesc);
-        pUrb->pszDesc = NULL;
-    }
-
-    /* The URB comes from the roothub if there is no device (invalid address). */
-    if (pUrb->pVUsb->pDev)
-    {
-        PVUSBDEV pDev = pUrb->pVUsb->pDev;
-
-        vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
-        vusbDevRelease(pDev);
-    }
-    else
-        vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb);
-}
-
-
-/**
- * Worker routine for vusbRhConnNewUrb().
- */
-static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
-                             VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
-{
-    RT_NOREF(pszTag);
-    PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool;
-
-    if (RT_UNLIKELY(cbData > (32 * _1M)))
-    {
-        LogFunc(("Bad URB size (%u)!\n", cbData));
-        return NULL;
-    }
-
-    PVUSBDEV pDev;
-    if (uPort == VUSB_DEVICE_PORT_INVALID)
-        pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress);
-    else
-        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
-
-    if (pDev)
-        pUrbPool = &pDev->UrbPool;
-
-    PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
-                                     pRh->cbHci, pRh->cbHciTd, cTds);
-    if (RT_LIKELY(pUrb))
-    {
-        pUrb->pVUsb->pvFreeCtx = pRh;
-        pUrb->pVUsb->pfnFree   = vusbRhFreeUrb;
-        pUrb->DstAddress       = DstAddress;
-        pUrb->pVUsb->pDev      = pDev;
-
-#ifdef LOG_ENABLED
-        const char *pszType = NULL;
-
-        switch(pUrb->enmType)
-        {
-            case VUSBXFERTYPE_CTRL:
-                pszType = "ctrl";
-                break;
-            case VUSBXFERTYPE_INTR:
-                pszType = "intr";
-                break;
-            case VUSBXFERTYPE_BULK:
-                pszType = "bulk";
-                break;
-            case VUSBXFERTYPE_ISOC:
-                pszType = "isoc";
-                break;
-            default:
-                pszType = "invld";
-                break;
-        }
-
-        pRh->iSerial = (pRh->iSerial + 1) % 10000;
-        RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
-                     (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
-                     pRh->iSerial, pszTag ? pszTag : "<none>");
-#endif
-    }
-
-    return pUrb;
-}
-
-
-/**
- * Calculate frame timer variables given a frame rate.
- */
-static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
-{
-    pThis->nsWait     = RT_NS_1SEC / u32FrameRate;
-    pThis->uFrameRate = u32FrameRate;
-    /* Inform the HCD about the new frame rate. */
-    pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
-}
-
-
-/**
- * Calculates the new frame rate based on the idle detection and number of idle
- * cycles.
- *
- * @returns nothing.
- * @param   pThis    The roothub instance data.
- * @param   fIdle    Flag whether the last frame didn't produce any activity.
- */
-static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
-{
-    uint32_t uNewFrameRate = pThis->uFrameRate;
-
-    /*
-     * Adjust the frame timer interval based on idle detection.
-     */
-    if (fIdle)
-    {
-        pThis->cIdleCycles++;
-        /* Set the new frame rate based on how long we've been idle. Tunable. */
-        switch (pThis->cIdleCycles)
-        {
-            case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
-            case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
-            case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
-            default:    break;
-        }
-        /* Avoid overflow. */
-        if (pThis->cIdleCycles > 60000)
-            pThis->cIdleCycles = 20000;
-    }
-    else
-    {
-        if (pThis->cIdleCycles)
-        {
-            pThis->cIdleCycles = 0;
-            uNewFrameRate      = pThis->uFrameRateDefault;
-        }
-    }
-
-    if (   uNewFrameRate != pThis->uFrameRate
-        && uNewFrameRate)
-    {
-        LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
-        vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
-    }
-}
-
-
-/**
- * The core frame processing routine keeping track of the elapsed time and calling into
- * the device emulation above us to do the work.
- *
- * @returns Relative timespan when to process the next frame.
- * @param   pThis     The roothub instance data.
- * @param   fCallback Flag whether this method is called from the URB completion callback or
- *                    from the worker thread (only used for statistics).
- */
-DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
-{
-    uint64_t tsNext = 0;
-    uint64_t tsNanoStart = RTTimeNanoTS();
-
-    /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
-    if (!pThis->uFrameRateDefault)
-        return 0;
-
-    if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
-        return pThis->nsWait;
-
-    if (   tsNanoStart > pThis->tsFrameProcessed
-        && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
-    {
-        LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
-
-        bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
-        vusbRhR3FrameRateCalcNew(pThis, fIdle);
-
-        uint64_t tsNow = RTTimeNanoTS();
-        tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
-        pThis->tsFrameProcessed = tsNanoStart;
-        LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
-        if (fCallback)
-            STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
-        else
-            STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
-    }
-    else
-    {
-        tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
-        LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
-                     tsNanoStart, pThis->tsFrameProcessed));
-    }
-
-    ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
-    LogFlowFunc(("returns %llu\n", tsNext));
-    return tsNext;
-}
-
-
-/**
- * Worker for processing frames periodically.
- *
- * @returns VBox status code.
- * @param   pDrvIns     The driver instance.
- * @param   pThread     The PDM thread structure for the thread this worker runs on.
- */
-static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
-{
-    RT_NOREF(pDrvIns);
-    int rc = VINF_SUCCESS;
-    PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
-
-    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
-        return VINF_SUCCESS;
-
-    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
-    {
-        while (   !ASMAtomicReadU32(&pThis->uFrameRateDefault)
-               && pThread->enmState == PDMTHREADSTATE_RUNNING)
-        {
-            /* Signal the waiter that we are stopped now. */
-            rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
-            AssertRC(rc);
-
-            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
-            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
-
-            /*
-             * Notify the device above about the frame rate changed if we are supposed to
-             * process frames.
-             */
-            uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
-            if (uFrameRate)
-                vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
-        }
-
-        AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
-        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
-            break;
-
-        uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
-
-        if (tsNext >= 250 * RT_NS_1US)
-        {
-            rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
-                                       tsNext);
-            AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
-            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Unblock the periodic frame thread so it can respond to a state change.
- *
- * @returns VBox status code.
- * @param   pDrvIns     The driver instance.
- * @param   pThread     The send thread.
- */
-static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
-{
-    RT_NOREF(pThread);
-    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
-    return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
-static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-
-    pRh->cbHci   = cbHci;
-    pRh->cbHciTd = cbHciTd;
-
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
-static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
-static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
-
-    Assert(     pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
-           &&   pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
-
-    if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED)
-        pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED;
-
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
-static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
-
-    Assert(     pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
-           &&   pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
-
-    /*
-     * Cancel all URBs and reap them.
-     */
-    VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
-    for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
-        VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
-
-    pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
-static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
-                                               VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
-static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
-{
-    RT_NOREF(pInterface);
-    pUrb->pVUsb->pfnFree(pUrb);
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
-static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
-
-#ifdef VBOX_WITH_STATISTICS
-    /*
-     * Total and per-type submit statistics.
-     */
-    Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
-    STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
-    STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
-
-    STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
-    STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
-    if (pUrb->enmDir == VUSBDIRECTION_IN)
-    {
-        STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
-        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
-    }
-    else
-    {
-        STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
-        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
-    }
-
-    if (pUrb->enmType == VUSBXFERTYPE_ISOC)
-    {
-        STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
-        if (pUrb->enmDir == VUSBDIRECTION_IN)
-            STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
-        else
-            STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
-    }
-#endif
-
-    /* If there is a sniffer on the roothub record the URB there. */
-    if (pRh->hSniffer != VUSBSNIFFER_NIL)
-    {
-        int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
-        if (RT_FAILURE(rc))
-            LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
-    }
-
-    /*
-     * The device was resolved when we allocated the URB.
-     * Submit it to the device if we found it, if not fail with device-not-ready.
-     */
-    int rc;
-    if (   pUrb->pVUsb->pDev
-        && pUrb->pVUsb->pDev->pUsbIns)
-    {
-        switch (pUrb->enmDir)
-        {
-            case VUSBDIRECTION_IN:
-                pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
-                rc = vusbUrbSubmit(pUrb);
-                pLed->Actual.s.fReading = 0;
-                break;
-            case VUSBDIRECTION_OUT:
-                pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
-                rc = vusbUrbSubmit(pUrb);
-                pLed->Actual.s.fWriting = 0;
-                break;
-            default:
-                rc = vusbUrbSubmit(pUrb);
-                break;
-        }
-
-        if (RT_FAILURE(rc))
-        {
-            LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
-            pUrb->pVUsb->pfnFree(pUrb);
-        }
-    }
-    else
-    {
-        vusbDevRetain(&pRh->Hub.Dev);
-        pUrb->pVUsb->pDev = &pRh->Hub.Dev;
-        Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
-
-        pUrb->enmState = VUSBURBSTATE_REAPED;
-        pUrb->enmStatus = VUSBSTATUS_DNR;
-        vusbUrbCompletionRh(pUrb);
-        rc = VINF_SUCCESS;
-    }
-
-    STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
-    return rc;
-}
-
-
-static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
-{
-    if (!cMillies)
-        vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
-    else
-    {
-        uint64_t u64Start = RTTimeMilliTS();
-        do
-        {
-            vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
-        } while (   !RTListIsEmpty(&pDev->LstAsyncUrbs)
-                 && RTTimeMilliTS() - u64Start < cMillies);
-    }
-
-    return VINF_SUCCESS;
-}
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
-static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
-    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
-
-    if (  !pDev
-        || RTListIsEmpty(&pDev->LstAsyncUrbs))
-        return;
-
-    STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
-    int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
-    AssertRC(rc);
-    STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
-
-    vusbDevRelease(pDev);
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
-static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    AssertReturn(pRh, VERR_INVALID_PARAMETER);
-    AssertReturn(pUrb, VERR_INVALID_PARAMETER);
-
-    /// @todo This method of URB canceling may not work on non-Linux hosts.
-    /*
-     * Cancel and reap the URB(s) on an endpoint.
-     */
-    LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
-
-    vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
-
-    /* The reaper thread will take care of completing the URB. */
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
- *
- * @returns VBox status code.
- * @param   pDev    USB device instance data.
- */
-static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
-{
-    /*
-     * Cancel the URBS.
-     *
-     * Not using th CritAsyncUrbs critical section here is safe
-     * as the I/O thread is the only thread accessing this struture at the
-     * moment.
-     */
-    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
-    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
-    {
-        PVUSBURB pUrb = pVUsbUrb->pUrb;
-        /* Call the worker directly. */
-        vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
-    }
-
-    return VINF_SUCCESS;
-}
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
-static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-
-    RTCritSectEnter(&pThis->CritSectDevices);
-    for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
-    {
-        PVUSBDEV pDev = pThis->apDevByPort[i];
-        if (pDev)
-            vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
-    }
-    RTCritSectLeave(&pThis->CritSectDevices);
-}
-
-/**
- * Worker doing the actual cancelling of all outstanding per-EP URBs on the
- * device I/O thread.
- *
- * @returns VBox status code.
- * @param   pDev    USB device instance data.
- * @param   EndPt   Endpoint number.
- * @param   enmDir  Endpoint direction.
- */
-static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
-{
-    /*
-     * Iterate the URBs, find ones corresponding to given EP, and cancel them.
-     */
-    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
-    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
-    {
-        PVUSBURB pUrb = pVUsbUrb->pUrb;
-
-        Assert(pUrb->pVUsb->pDev == pDev);
-
-        /* For the default control EP, direction does not matter. */
-        if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
-        {
-            LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
-            int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
-            AssertRC(rc);
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
-static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
-{
-    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
-
-    if (&pRh->Hub != pDev->pHub)
-        AssertFailedReturn(VERR_INVALID_PARAMETER);
-
-    vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
-    vusbDevRelease(pDev);
-
-    /* The reaper thread will take care of completing the URB. */
-
-    return VINF_SUCCESS;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
-static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
-{
-    int rc = VINF_SUCCESS;
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-
-    /* Create the frame thread lazily. */
-    if (   !pThis->hThreadPeriodFrame
-        && uFrameRate)
-    {
-        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
-        pThis->uFrameRate = uFrameRate;
-        vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
-
-        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
-        AssertRCReturn(rc, rc);
-
-        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
-        AssertRCReturn(rc, rc);
-
-        rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
-                                   vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
-        AssertRCReturn(rc, rc);
-
-        VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
-        if (   enmState == VMSTATE_RUNNING
-            || enmState == VMSTATE_RUNNING_LS)
-        {
-            rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
-            AssertRCReturn(rc, rc);
-        }
-    }
-    else if (   pThis->hThreadPeriodFrame
-             && !uFrameRate)
-    {
-        /* Stop processing. */
-        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
-        if (uFrameRateOld)
-        {
-            rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
-            AssertRC(rc);
-
-            /* Signal the frame thread to stop. */
-            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
-
-            /* Wait for signal from the thread that it stopped. */
-            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
-            AssertRC(rc);
-        }
-    }
-    else if (   pThis->hThreadPeriodFrame
-             && uFrameRate)
-    {
-        /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
-        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
-
-        /* Signal the frame thread to continue if it was stopped. */
-        if (!uFrameRateOld)
-            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
-    }
-
-    return rc;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
-static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-
-    return pThis->uFrameRate;
-}
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
-static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
-                                                         int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
-{
-    PVUSBROOTHUB    pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    AssertReturn(pRh, 0);
-    PVUSBDEV        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev);
-    PVUSBPIPE       pPipe = &pDev->aPipes[EndPt];
-    uint32_t        *puLastFrame;
-    int32_t         uFrameDelta;
-    uint32_t        uMaxVal = 1 << uBits;
-
-    puLastFrame  = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
-    uFrameDelta  = uNewFrameID - *puLastFrame;
-    *puLastFrame = uNewFrameID;
-    /* Take care of wrap-around. */
-    if (uFrameDelta < 0)
-        uFrameDelta += uMaxVal;
-
-    vusbDevRelease(pDev);
-    return (uint16_t)uFrameDelta;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
-static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
-                                          PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
-    vusbDevRelease(pDev);
-    return rc;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
-static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    int rc = VUSBIDevPowerOn(&pDev->IDevice);
-    vusbDevRelease(pDev);
-    return rc;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
-static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    int rc = VUSBIDevPowerOff(&pDev->IDevice);
-    vusbDevRelease(pDev);
-    return rc;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
-static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
-    vusbDevRelease(pDev);
-    return enmState;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
-static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
-    vusbDevRelease(pDev);
-    return fSavedStateSupported;
-}
-
-
-/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
-static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
-{
-    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
-    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
-    AssertPtr(pDev);
-
-    VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
-    vusbDevRelease(pDev);
-    return enmSpeed;
-}
-
-
+ * Returns a human readable string fromthe given USB speed enum.
+ *
+ * @returns Human readable string.
+ * @param   enmSpeed            The speed to stringify.
+ */
 static const char *vusbGetSpeedString(VUSBSPEED enmSpeed)
 {
@@ -1190,4 +332,968 @@
 
 /**
+ * Attaches a device to a specific hub.
+ *
+ * This function is called by the vusb_add_device() and vusbRhAttachDevice().
+ *
+ * @returns VBox status code.
+ * @param   pThis       The roothub to attach it to.
+ * @param   pDev        The device to attach.
+ * @thread  EMT
+ */
+static int vusbHubAttach(PVUSBROOTHUB pThis, PVUSBDEV pDev)
+{
+    LogFlow(("vusbHubAttach: pThis=%p[%s] pDev=%p[%s]\n", pThis, pThis->Hub.pszName, pDev, pDev->pUsbIns->pszName));
+
+    PVUSBHUB pHub = &pThis->Hub;
+
+    /*
+     * Assign a port.
+     */
+    int iPort = ASMBitFirstSet(&pThis->Bitmap, sizeof(pThis->Bitmap) * 8);
+    if (iPort < 0)
+    {
+        LogRel(("VUSB: No ports available!\n"));
+        return VERR_VUSB_NO_PORTS;
+    }
+    ASMBitClear(&pThis->Bitmap, iPort);
+    pHub->cDevices++;
+    pDev->i16Port = iPort;
+
+    /* Call the device attach helper, so it can initialize its state. */
+    int rc = vusbDevAttach(pDev, pHub);
+    if (RT_SUCCESS(rc))
+    {
+        RTCritSectEnter(&pThis->CritSectDevices);
+        Assert(!pThis->apDevByPort[iPort]);
+        pThis->apDevByPort[iPort] = pDev;
+        RTCritSectLeave(&pThis->CritSectDevices);
+
+        /*
+         * Call the HCI attach routine and let it have its say before the device is
+         * linked into the device list of this hub.
+         */
+        VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
+        rc = pThis->pIRhPort->pfnAttach(pThis->pIRhPort, iPort, enmSpeed);
+        if (RT_SUCCESS(rc))
+        {
+            LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,
+                    iPort, pHub->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));
+            return VINF_SUCCESS;
+        }
+
+        /* Remove from the port in case of failure. */
+        RTCritSectEnter(&pThis->CritSectDevices);
+        Assert(!pThis->apDevByPort[iPort]);
+        pThis->apDevByPort[iPort] = NULL;
+        RTCritSectLeave(&pThis->CritSectDevices);
+
+        vusbDevDetach(pDev);
+    }
+
+    ASMBitSet(&pThis->Bitmap, iPort);
+    pHub->cDevices--;
+    pDev->i16Port = -1;
+    LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));
+
+    return rc;
+}
+
+
+/* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
+
+/** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
+static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
+{
+    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
+
+    /*
+     * Allocate a new VUSB device and initialize it.
+     */
+    PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
+    AssertReturn(pDev, VERR_NO_MEMORY);
+    int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
+    if (RT_SUCCESS(rc))
+    {
+        pUsbIns->pvVUsbDev2 = pDev;
+        rc = vusbHubAttach(pThis, pDev);
+        if (RT_SUCCESS(rc))
+        {
+            *piPort = UINT32_MAX; /// @todo implement piPort
+            return rc;
+        }
+
+        RTMemFree(pDev->paIfStates);
+        pUsbIns->pvVUsbDev2 = NULL;
+    }
+    vusbDevRelease(pDev);
+    return rc;
+}
+
+
+/** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
+static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
+{
+    RT_NOREF(iPort);
+    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
+    PVUSBHUB pHub = &pThis->Hub;
+    PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
+    Assert(pDev);
+
+    /*
+     * Deal with pending async reset.
+     * (anything but reset)
+     */
+    vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
+    Assert(pDev->i16Port != -1);
+
+    /* Cancel all in-flight URBs from this device. */
+    vusbDevCancelAllUrbs(pDev, true);
+
+    /*
+     * Detach the device and mark the port as available.
+     */
+    unsigned uPort = pDev->i16Port;
+    pDev->i16Port = -1;
+    pThis->pIRhPort->pfnDetach(pThis->pIRhPort, uPort);
+    ASMBitSet(&pThis->Bitmap, uPort);
+    pHub->cDevices--;
+
+    /* Check that it's attached and remove it. */
+    RTCritSectEnter(&pThis->CritSectDevices);
+    Assert(pThis->apDevByPort[uPort] == pDev);
+    pThis->apDevByPort[uPort]  = NULL;
+
+    if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)
+    {
+        AssertPtr(pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS]);
+
+        if (pDev == pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS])
+            pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr;
+        else
+        {
+            /* Search the list for the device and remove it. */
+            PVUSBDEV pDevPrev = pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS];
+
+            while (   pDevPrev
+                   && pDevPrev->pNextDefAddr != pDev)
+                pDevPrev = pDevPrev->pNextDefAddr;
+
+            AssertPtr(pDevPrev);
+            pDevPrev->pNextDefAddr = pDev->pNextDefAddr;
+        }
+
+        pDev->pNextDefAddr = NULL;
+    }
+    else
+    {
+        Assert(pThis->apDevByAddr[pDev->u8Address] == pDev);
+        pThis->apDevByAddr[pDev->u8Address] = NULL;
+    }
+    RTCritSectLeave(&pThis->CritSectDevices);
+
+    /*
+     * Detach and free resources.
+     */
+    vusbDevDetach(pDev);
+
+    LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName));
+    vusbDevRelease(pDev);
+    return VINF_SUCCESS;
+}
+
+/**
+ * The hub registration structure.
+ */
+static const PDMUSBHUBREG g_vusbHubReg =
+{
+    PDM_USBHUBREG_VERSION,
+    vusbPDMHubAttachDevice,
+    vusbPDMHubDetachDevice,
+    PDM_USBHUBREG_VERSION
+};
+
+
+/* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
+
+
+/**
+ * Callback for freeing an URB.
+ * @param   pUrb    The URB to free.
+ */
+static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
+{
+    /*
+     * Assert sanity.
+     */
+    vusbUrbAssert(pUrb);
+    PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
+    Assert(pRh);
+
+    Assert(pUrb->enmState != VUSBURBSTATE_FREE);
+
+    /*
+     * Free the URB description (logging builds only).
+     */
+    if (pUrb->pszDesc)
+    {
+        RTStrFree(pUrb->pszDesc);
+        pUrb->pszDesc = NULL;
+    }
+
+    /* The URB comes from the roothub if there is no device (invalid address). */
+    if (pUrb->pVUsb->pDev)
+    {
+        PVUSBDEV pDev = pUrb->pVUsb->pDev;
+
+        vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
+        vusbDevRelease(pDev);
+    }
+    else
+        vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb);
+}
+
+
+/**
+ * Worker routine for vusbRhConnNewUrb().
+ */
+static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
+                             VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
+{
+    RT_NOREF(pszTag);
+    PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool;
+
+    if (RT_UNLIKELY(cbData > (32 * _1M)))
+    {
+        LogFunc(("Bad URB size (%u)!\n", cbData));
+        return NULL;
+    }
+
+    PVUSBDEV pDev;
+    if (uPort == VUSB_DEVICE_PORT_INVALID)
+        pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress);
+    else
+        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
+
+    if (pDev)
+        pUrbPool = &pDev->UrbPool;
+
+    PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
+                                     pRh->cbHci, pRh->cbHciTd, cTds);
+    if (RT_LIKELY(pUrb))
+    {
+        pUrb->pVUsb->pvFreeCtx = pRh;
+        pUrb->pVUsb->pfnFree   = vusbRhFreeUrb;
+        pUrb->DstAddress       = DstAddress;
+        pUrb->pVUsb->pDev      = pDev;
+
+#ifdef LOG_ENABLED
+        const char *pszType = NULL;
+
+        switch(pUrb->enmType)
+        {
+            case VUSBXFERTYPE_CTRL:
+                pszType = "ctrl";
+                break;
+            case VUSBXFERTYPE_INTR:
+                pszType = "intr";
+                break;
+            case VUSBXFERTYPE_BULK:
+                pszType = "bulk";
+                break;
+            case VUSBXFERTYPE_ISOC:
+                pszType = "isoc";
+                break;
+            default:
+                pszType = "invld";
+                break;
+        }
+
+        pRh->iSerial = (pRh->iSerial + 1) % 10000;
+        RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
+                     (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
+                     pRh->iSerial, pszTag ? pszTag : "<none>");
+#endif
+    }
+
+    return pUrb;
+}
+
+
+/**
+ * Calculate frame timer variables given a frame rate.
+ */
+static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
+{
+    pThis->nsWait     = RT_NS_1SEC / u32FrameRate;
+    pThis->uFrameRate = u32FrameRate;
+    /* Inform the HCD about the new frame rate. */
+    pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
+}
+
+
+/**
+ * Calculates the new frame rate based on the idle detection and number of idle
+ * cycles.
+ *
+ * @returns nothing.
+ * @param   pThis    The roothub instance data.
+ * @param   fIdle    Flag whether the last frame didn't produce any activity.
+ */
+static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
+{
+    uint32_t uNewFrameRate = pThis->uFrameRate;
+
+    /*
+     * Adjust the frame timer interval based on idle detection.
+     */
+    if (fIdle)
+    {
+        pThis->cIdleCycles++;
+        /* Set the new frame rate based on how long we've been idle. Tunable. */
+        switch (pThis->cIdleCycles)
+        {
+            case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
+            case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
+            case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
+            default:    break;
+        }
+        /* Avoid overflow. */
+        if (pThis->cIdleCycles > 60000)
+            pThis->cIdleCycles = 20000;
+    }
+    else
+    {
+        if (pThis->cIdleCycles)
+        {
+            pThis->cIdleCycles = 0;
+            uNewFrameRate      = pThis->uFrameRateDefault;
+        }
+    }
+
+    if (   uNewFrameRate != pThis->uFrameRate
+        && uNewFrameRate)
+    {
+        LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
+        vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
+    }
+}
+
+
+/**
+ * The core frame processing routine keeping track of the elapsed time and calling into
+ * the device emulation above us to do the work.
+ *
+ * @returns Relative timespan when to process the next frame.
+ * @param   pThis     The roothub instance data.
+ * @param   fCallback Flag whether this method is called from the URB completion callback or
+ *                    from the worker thread (only used for statistics).
+ */
+DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
+{
+    uint64_t tsNext = 0;
+    uint64_t tsNanoStart = RTTimeNanoTS();
+
+    /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
+    if (!pThis->uFrameRateDefault)
+        return 0;
+
+    if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
+        return pThis->nsWait;
+
+    if (   tsNanoStart > pThis->tsFrameProcessed
+        && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
+    {
+        LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
+
+        bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
+        vusbRhR3FrameRateCalcNew(pThis, fIdle);
+
+        uint64_t tsNow = RTTimeNanoTS();
+        tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
+        pThis->tsFrameProcessed = tsNanoStart;
+        LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
+        if (fCallback)
+            STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
+        else
+            STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
+    }
+    else
+    {
+        tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
+        LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
+                     tsNanoStart, pThis->tsFrameProcessed));
+    }
+
+    ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
+    LogFlowFunc(("returns %llu\n", tsNext));
+    return tsNext;
+}
+
+
+/**
+ * Worker for processing frames periodically.
+ *
+ * @returns VBox status code.
+ * @param   pDrvIns     The driver instance.
+ * @param   pThread     The PDM thread structure for the thread this worker runs on.
+ */
+static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+    RT_NOREF(pDrvIns);
+    int rc = VINF_SUCCESS;
+    PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
+
+    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+        return VINF_SUCCESS;
+
+    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+    {
+        while (   !ASMAtomicReadU32(&pThis->uFrameRateDefault)
+               && pThread->enmState == PDMTHREADSTATE_RUNNING)
+        {
+            /* Signal the waiter that we are stopped now. */
+            rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
+            AssertRC(rc);
+
+            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
+            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
+
+            /*
+             * Notify the device above about the frame rate changed if we are supposed to
+             * process frames.
+             */
+            uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
+            if (uFrameRate)
+                vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
+        }
+
+        AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
+        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+            break;
+
+        uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
+
+        if (tsNext >= 250 * RT_NS_1US)
+        {
+            rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
+                                       tsNext);
+            AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
+            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the periodic frame thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param   pDrvIns     The driver instance.
+ * @param   pThread     The send thread.
+ */
+static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+    RT_NOREF(pThread);
+    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
+    return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
+static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+
+    pRh->cbHci   = cbHci;
+    pRh->cbHciTd = cbHciTd;
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
+static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
+static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
+
+    Assert(     pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
+           &&   pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
+
+    if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED)
+        pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED;
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
+static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
+
+    Assert(     pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
+           &&   pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
+
+    /*
+     * Cancel all URBs and reap them.
+     */
+    VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
+    for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
+        VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
+
+    pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
+static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
+                                               VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
+static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
+{
+    RT_NOREF(pInterface);
+    pUrb->pVUsb->pfnFree(pUrb);
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
+static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
+
+#ifdef VBOX_WITH_STATISTICS
+    /*
+     * Total and per-type submit statistics.
+     */
+    Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
+    STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
+    STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
+
+    STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
+    STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
+    if (pUrb->enmDir == VUSBDIRECTION_IN)
+    {
+        STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
+        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
+    }
+    else
+    {
+        STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
+        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
+    }
+
+    if (pUrb->enmType == VUSBXFERTYPE_ISOC)
+    {
+        STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
+        if (pUrb->enmDir == VUSBDIRECTION_IN)
+            STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
+        else
+            STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
+    }
+#endif
+
+    /* If there is a sniffer on the roothub record the URB there. */
+    if (pRh->hSniffer != VUSBSNIFFER_NIL)
+    {
+        int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
+        if (RT_FAILURE(rc))
+            LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
+    }
+
+    /*
+     * The device was resolved when we allocated the URB.
+     * Submit it to the device if we found it, if not fail with device-not-ready.
+     */
+    int rc;
+    if (   pUrb->pVUsb->pDev
+        && pUrb->pVUsb->pDev->pUsbIns)
+    {
+        switch (pUrb->enmDir)
+        {
+            case VUSBDIRECTION_IN:
+                pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
+                rc = vusbUrbSubmit(pUrb);
+                pLed->Actual.s.fReading = 0;
+                break;
+            case VUSBDIRECTION_OUT:
+                pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
+                rc = vusbUrbSubmit(pUrb);
+                pLed->Actual.s.fWriting = 0;
+                break;
+            default:
+                rc = vusbUrbSubmit(pUrb);
+                break;
+        }
+
+        if (RT_FAILURE(rc))
+        {
+            LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
+            pUrb->pVUsb->pfnFree(pUrb);
+        }
+    }
+    else
+    {
+        vusbDevRetain(&pRh->Hub.Dev);
+        pUrb->pVUsb->pDev = &pRh->Hub.Dev;
+        Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
+
+        pUrb->enmState = VUSBURBSTATE_REAPED;
+        pUrb->enmStatus = VUSBSTATUS_DNR;
+        vusbUrbCompletionRh(pUrb);
+        rc = VINF_SUCCESS;
+    }
+
+    STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
+    return rc;
+}
+
+
+static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
+{
+    if (!cMillies)
+        vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
+    else
+    {
+        uint64_t u64Start = RTTimeMilliTS();
+        do
+        {
+            vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
+        } while (   !RTListIsEmpty(&pDev->LstAsyncUrbs)
+                 && RTTimeMilliTS() - u64Start < cMillies);
+    }
+
+    return VINF_SUCCESS;
+}
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
+static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
+    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
+
+    if (  !pDev
+        || RTListIsEmpty(&pDev->LstAsyncUrbs))
+        return;
+
+    STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
+    int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
+    AssertRC(rc);
+    STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
+
+    vusbDevRelease(pDev);
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
+static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    AssertReturn(pRh, VERR_INVALID_PARAMETER);
+    AssertReturn(pUrb, VERR_INVALID_PARAMETER);
+
+    /// @todo This method of URB canceling may not work on non-Linux hosts.
+    /*
+     * Cancel and reap the URB(s) on an endpoint.
+     */
+    LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
+
+    vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
+
+    /* The reaper thread will take care of completing the URB. */
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
+ *
+ * @returns VBox status code.
+ * @param   pDev    USB device instance data.
+ */
+static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
+{
+    /*
+     * Cancel the URBS.
+     *
+     * Not using th CritAsyncUrbs critical section here is safe
+     * as the I/O thread is the only thread accessing this struture at the
+     * moment.
+     */
+    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
+    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
+    {
+        PVUSBURB pUrb = pVUsbUrb->pUrb;
+        /* Call the worker directly. */
+        vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
+    }
+
+    return VINF_SUCCESS;
+}
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
+static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+
+    RTCritSectEnter(&pThis->CritSectDevices);
+    for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
+    {
+        PVUSBDEV pDev = pThis->apDevByPort[i];
+        if (pDev)
+            vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
+    }
+    RTCritSectLeave(&pThis->CritSectDevices);
+}
+
+/**
+ * Worker doing the actual cancelling of all outstanding per-EP URBs on the
+ * device I/O thread.
+ *
+ * @returns VBox status code.
+ * @param   pDev    USB device instance data.
+ * @param   EndPt   Endpoint number.
+ * @param   enmDir  Endpoint direction.
+ */
+static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
+{
+    /*
+     * Iterate the URBs, find ones corresponding to given EP, and cancel them.
+     */
+    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
+    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
+    {
+        PVUSBURB pUrb = pVUsbUrb->pUrb;
+
+        Assert(pUrb->pVUsb->pDev == pDev);
+
+        /* For the default control EP, direction does not matter. */
+        if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
+        {
+            LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
+            int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
+            AssertRC(rc);
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
+static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
+{
+    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
+
+    if (&pRh->Hub != pDev->pHub)
+        AssertFailedReturn(VERR_INVALID_PARAMETER);
+
+    vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
+    vusbDevRelease(pDev);
+
+    /* The reaper thread will take care of completing the URB. */
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
+static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
+{
+    int rc = VINF_SUCCESS;
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+
+    /* Create the frame thread lazily. */
+    if (   !pThis->hThreadPeriodFrame
+        && uFrameRate)
+    {
+        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
+        pThis->uFrameRate = uFrameRate;
+        vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
+
+        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
+        AssertRCReturn(rc, rc);
+
+        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
+        AssertRCReturn(rc, rc);
+
+        rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
+                                   vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
+        AssertRCReturn(rc, rc);
+
+        VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
+        if (   enmState == VMSTATE_RUNNING
+            || enmState == VMSTATE_RUNNING_LS)
+        {
+            rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
+            AssertRCReturn(rc, rc);
+        }
+    }
+    else if (   pThis->hThreadPeriodFrame
+             && !uFrameRate)
+    {
+        /* Stop processing. */
+        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
+        if (uFrameRateOld)
+        {
+            rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
+            AssertRC(rc);
+
+            /* Signal the frame thread to stop. */
+            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
+
+            /* Wait for signal from the thread that it stopped. */
+            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
+            AssertRC(rc);
+        }
+    }
+    else if (   pThis->hThreadPeriodFrame
+             && uFrameRate)
+    {
+        /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
+        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
+
+        /* Signal the frame thread to continue if it was stopped. */
+        if (!uFrameRateOld)
+            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
+    }
+
+    return rc;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
+static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+
+    return pThis->uFrameRate;
+}
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
+static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
+                                                         int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
+{
+    PVUSBROOTHUB    pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    AssertReturn(pRh, 0);
+    PVUSBDEV        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev);
+    PVUSBPIPE       pPipe = &pDev->aPipes[EndPt];
+    uint32_t        *puLastFrame;
+    int32_t         uFrameDelta;
+    uint32_t        uMaxVal = 1 << uBits;
+
+    puLastFrame  = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
+    uFrameDelta  = uNewFrameID - *puLastFrame;
+    *puLastFrame = uNewFrameID;
+    /* Take care of wrap-around. */
+    if (uFrameDelta < 0)
+        uFrameDelta += uMaxVal;
+
+    vusbDevRelease(pDev);
+    return (uint16_t)uFrameDelta;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
+static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
+                                          PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
+    vusbDevRelease(pDev);
+    return rc;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
+static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    int rc = VUSBIDevPowerOn(&pDev->IDevice);
+    vusbDevRelease(pDev);
+    return rc;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
+static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    int rc = VUSBIDevPowerOff(&pDev->IDevice);
+    vusbDevRelease(pDev);
+    return rc;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
+static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
+    vusbDevRelease(pDev);
+    return enmState;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
+static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
+    vusbDevRelease(pDev);
+    return fSavedStateSupported;
+}
+
+
+/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
+static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
+{
+    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
+    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
+    AssertPtr(pDev);
+
+    VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
+    vusbDevRelease(pDev);
+    return enmSpeed;
+}
+
+
+/**
  * @callback_method_impl{FNSSMDRVSAVEPREP, All URBs needs to be canceled.}
  */
@@ -1270,5 +1376,5 @@
         PVUSBDEV pDev = aPortsOld[i];
         if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
-            vusbHubAttach(&pThis->Hub, pDev);
+            vusbHubAttach(pThis, pDev);
     }
 
@@ -1343,5 +1449,5 @@
      */
     for (unsigned i = 0; i < pLoad->cDevs; i++)
-        vusbHubAttach(&pThis->Hub, pLoad->apDevs[i]);
+        vusbHubAttach(pThis, pLoad->apDevs[i]);
 
     /*
@@ -1381,126 +1487,4 @@
 
 
-/* -=-=-=-=-=- VUSB Hub methods -=-=-=-=-=- */
-
-
-/**
- * Attach the device to the hub.
- * Port assignments and all such stuff is up to this routine.
- *
- * @returns VBox status code.
- * @param   pHub        Pointer to the hub.
- * @param   pDev        Pointer to the device.
- */
-static int vusbRhHubOpAttach(PVUSBHUB pHub, PVUSBDEV pDev)
-{
-    PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;
-
-    /*
-     * Assign a port.
-     */
-    int iPort = ASMBitFirstSet(&pRh->Bitmap, sizeof(pRh->Bitmap) * 8);
-    if (iPort < 0)
-    {
-        LogRel(("VUSB: No ports available!\n"));
-        return VERR_VUSB_NO_PORTS;
-    }
-    ASMBitClear(&pRh->Bitmap, iPort);
-    pHub->cDevices++;
-    pDev->i16Port = iPort;
-
-    /*
-     * Call the HCI attach routine and let it have its say before the device is
-     * linked into the device list of this hub.
-     */
-    VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
-    int rc = pRh->pIRhPort->pfnAttach(pRh->pIRhPort, iPort, enmSpeed);
-    if (RT_SUCCESS(rc))
-    {
-        RTCritSectEnter(&pRh->CritSectDevices);
-        Assert(!pRh->apDevByPort[iPort]);
-        pRh->apDevByPort[iPort] = pDev;
-
-        RTCritSectLeave(&pRh->CritSectDevices);
-        LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,
-                iPort, pHub->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));
-    }
-    else
-    {
-        ASMBitSet(&pRh->Bitmap, iPort);
-        pHub->cDevices--;
-        pDev->i16Port = -1;
-        LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));
-    }
-    return rc;
-}
-
-
-/**
- * Detach the device from the hub.
- *
- * @returns VBox status code.
- * @param   pHub        Pointer to the hub.
- * @param   pDev        Pointer to the device.
- */
-static void vusbRhHubOpDetach(PVUSBHUB pHub, PVUSBDEV pDev)
-{
-    PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;
-    Assert(pDev->i16Port != -1);
-
-    /* Check that it's attached and remvoe it. */
-    RTCritSectEnter(&pRh->CritSectDevices);
-    Assert(pRh->apDevByPort[pDev->i16Port] == pDev);
-    pRh->apDevByPort[pDev->i16Port]   = NULL;
-
-    if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)
-    {
-        AssertPtr(pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS]);
-
-        if (pDev == pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS])
-            pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr;
-        else
-        {
-            /* Search the list for the device and remove it. */
-            PVUSBDEV pDevPrev = pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS];
-
-            while (   pDevPrev
-                   && pDevPrev->pNextDefAddr != pDev)
-                pDevPrev = pDevPrev->pNextDefAddr;
-
-            AssertPtr(pDevPrev);
-            pDevPrev->pNextDefAddr = pDev->pNextDefAddr;
-        }
-
-        pDev->pNextDefAddr = NULL;
-    }
-    else
-    {
-        Assert(pRh->apDevByAddr[pDev->u8Address] == pDev);
-        pRh->apDevByAddr[pDev->u8Address] = NULL;
-    }
-    RTCritSectLeave(&pRh->CritSectDevices);
-
-    /*
-     * Detach the device and mark the port as available.
-     */
-    unsigned uPort = pDev->i16Port;
-    pRh->pIRhPort->pfnDetach(pRh->pIRhPort, uPort);
-    LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName));
-    ASMBitSet(&pRh->Bitmap, uPort);
-    pHub->cDevices--;
-}
-
-
-/**
- * The Hub methods implemented by the root hub.
- */
-static const VUSBHUBOPS s_VUsbRhHubOps =
-{
-    vusbRhHubOpAttach,
-    vusbRhHubOpDetach
-};
-
-
-
 /* -=-=-=-=-=- PDM Base interface methods -=-=-=-=-=- */
 
@@ -1604,5 +1588,4 @@
     pThis->Hub.Dev.cRefs                = 1;
     /* the hub */
-    pThis->Hub.pOps                     = &s_VUsbRhHubOps;
     pThis->Hub.pRootHub                 = pThis;
     //pThis->hub.cPorts                - later
Index: trunk/src/VBox/Devices/USB/VUSBDevice.cpp
===================================================================
--- trunk/src/VBox/Devices/USB/VUSBDevice.cpp	(revision 93944)
+++ trunk/src/VBox/Devices/USB/VUSBDevice.cpp	(revision 93955)
@@ -1279,7 +1279,4 @@
     /* Create I/O thread and attach to the hub. */
     int rc = vusbDevUrbIoThreadCreate(pDev);
-    if (RT_SUCCESS(rc))
-        rc = pHub->pOps->pfnAttach(pHub, pDev);
-
     if (RT_FAILURE(rc))
     {
@@ -1305,14 +1302,4 @@
     VUSBDEV_ASSERT_VALID_STATE(pDev->enmState);
     Assert(pDev->enmState != VUSB_DEVICE_STATE_RESET);
-
-    vusbDevCancelAllUrbs(pDev, true);
-
-    PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
-    if (!pRh)
-        AssertMsgFailedReturn(("Not attached!\n"), VERR_VUSB_DEVICE_NOT_ATTACHED);
-
-    /* The roothub will remove the device from the address to device array. */
-    pDev->pHub->pOps->pfnDetach(pDev->pHub, pDev);
-    pDev->i16Port = -1;
 
     /*
Index: trunk/src/VBox/Devices/USB/VUSBInternal.h
===================================================================
--- trunk/src/VBox/Devices/USB/VUSBInternal.h	(revision 93944)
+++ trunk/src/VBox/Devices/USB/VUSBInternal.h	(revision 93955)
@@ -320,16 +320,4 @@
 
 
-/** Virtual method table for USB hub devices.
- * Hub and roothub drivers need to implement these functions in addition to the
- * vusb_dev_ops.
- */
-typedef struct VUSBHUBOPS
-{
-    int     (*pfnAttach)(PVUSBHUB pHub, PVUSBDEV pDev);
-    void    (*pfnDetach)(PVUSBHUB pHub, PVUSBDEV pDev);
-} VUSBHUBOPS;
-/** Pointer to a const HUB method table. */
-typedef const VUSBHUBOPS *PCVUSBHUBOPS;
-
 /** A VUSB Hub Device - Hub and roothub drivers need to use this struct
  * @todo eliminate this (PDM  / roothubs only).
@@ -338,5 +326,4 @@
 {
     VUSBDEV             Dev;
-    PCVUSBHUBOPS        pOps;
     PVUSBROOTHUB        pRootHub;
     uint16_t            cPorts;
@@ -345,5 +332,4 @@
     char               *pszName;
 } VUSBHUB;
-AssertCompileMemberAlignment(VUSBHUB, pOps, 8);
 AssertCompileSizeAlignment(VUSBHUB, 8);
 
