VirtualBox

Changeset 93955 in vbox for trunk/src/VBox/Devices/USB


Ignore:
Timestamp:
Feb 25, 2022 4:13:44 PM (3 years ago)
Author:
vboxsync
Message:

Devices/USB: Eliminiate some callback ping-pong and streamline the device attach/detach logic, bugref:10196

Location:
trunk/src/VBox/Devices/USB
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp

    r93939 r93955  
    294294
    295295/**
    296  * Attaches a device to a specific hub.
    297  *
    298  * This function is called by the vusb_add_device() and vusbRhAttachDevice().
    299  *
    300  * @returns VBox status code.
    301  * @param   pHub        The hub to attach it to.
    302  * @param   pDev        The device to attach.
    303  * @thread  EMT
    304  */
    305 static int vusbHubAttach(PVUSBHUB pHub, PVUSBDEV pDev)
    306 {
    307     LogFlow(("vusbHubAttach: pHub=%p[%s] pDev=%p[%s]\n", pHub, pHub->pszName, pDev, pDev->pUsbIns->pszName));
    308     return vusbDevAttach(pDev, pHub);
    309 }
    310 
    311 
    312 /* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
    313 
    314 /** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
    315 static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
    316 {
    317     PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
    318 
    319     /*
    320      * Allocate a new VUSB device and initialize it.
    321      */
    322     PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
    323     AssertReturn(pDev, VERR_NO_MEMORY);
    324     int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
    325     if (RT_SUCCESS(rc))
    326     {
    327         pUsbIns->pvVUsbDev2 = pDev;
    328         rc = vusbHubAttach(&pThis->Hub, pDev);
    329         if (RT_SUCCESS(rc))
    330         {
    331             *piPort = UINT32_MAX; /// @todo implement piPort
    332             return rc;
    333         }
    334 
    335         RTMemFree(pDev->paIfStates);
    336         pUsbIns->pvVUsbDev2 = NULL;
    337     }
    338     vusbDevRelease(pDev);
    339     return rc;
    340 }
    341 
    342 
    343 /** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
    344 static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
    345 {
    346     RT_NOREF(pDrvIns, iPort);
    347     PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
    348     Assert(pDev);
    349 
    350     /*
    351      * Deal with pending async reset.
    352      * (anything but reset)
    353      */
    354     vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
    355 
    356     /*
    357      * Detach and free resources.
    358      */
    359     if (pDev->pHub)
    360         vusbDevDetach(pDev);
    361 
    362     vusbDevRelease(pDev);
    363     return VINF_SUCCESS;
    364 }
    365 
    366 /**
    367  * The hub registration structure.
    368  */
    369 static const PDMUSBHUBREG g_vusbHubReg =
    370 {
    371     PDM_USBHUBREG_VERSION,
    372     vusbPDMHubAttachDevice,
    373     vusbPDMHubDetachDevice,
    374     PDM_USBHUBREG_VERSION
    375 };
    376 
    377 
    378 /* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
    379 
    380 
    381 /**
    382  * Callback for freeing an URB.
    383  * @param   pUrb    The URB to free.
    384  */
    385 static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
    386 {
    387     /*
    388      * Assert sanity.
    389      */
    390     vusbUrbAssert(pUrb);
    391     PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
    392     Assert(pRh);
    393 
    394     Assert(pUrb->enmState != VUSBURBSTATE_FREE);
    395 
    396     /*
    397      * Free the URB description (logging builds only).
    398      */
    399     if (pUrb->pszDesc)
    400     {
    401         RTStrFree(pUrb->pszDesc);
    402         pUrb->pszDesc = NULL;
    403     }
    404 
    405     /* The URB comes from the roothub if there is no device (invalid address). */
    406     if (pUrb->pVUsb->pDev)
    407     {
    408         PVUSBDEV pDev = pUrb->pVUsb->pDev;
    409 
    410         vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
    411         vusbDevRelease(pDev);
    412     }
    413     else
    414         vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb);
    415 }
    416 
    417 
    418 /**
    419  * Worker routine for vusbRhConnNewUrb().
    420  */
    421 static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
    422                              VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
    423 {
    424     RT_NOREF(pszTag);
    425     PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool;
    426 
    427     if (RT_UNLIKELY(cbData > (32 * _1M)))
    428     {
    429         LogFunc(("Bad URB size (%u)!\n", cbData));
    430         return NULL;
    431     }
    432 
    433     PVUSBDEV pDev;
    434     if (uPort == VUSB_DEVICE_PORT_INVALID)
    435         pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress);
    436     else
    437         pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
    438 
    439     if (pDev)
    440         pUrbPool = &pDev->UrbPool;
    441 
    442     PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
    443                                      pRh->cbHci, pRh->cbHciTd, cTds);
    444     if (RT_LIKELY(pUrb))
    445     {
    446         pUrb->pVUsb->pvFreeCtx = pRh;
    447         pUrb->pVUsb->pfnFree   = vusbRhFreeUrb;
    448         pUrb->DstAddress       = DstAddress;
    449         pUrb->pVUsb->pDev      = pDev;
    450 
    451 #ifdef LOG_ENABLED
    452         const char *pszType = NULL;
    453 
    454         switch(pUrb->enmType)
    455         {
    456             case VUSBXFERTYPE_CTRL:
    457                 pszType = "ctrl";
    458                 break;
    459             case VUSBXFERTYPE_INTR:
    460                 pszType = "intr";
    461                 break;
    462             case VUSBXFERTYPE_BULK:
    463                 pszType = "bulk";
    464                 break;
    465             case VUSBXFERTYPE_ISOC:
    466                 pszType = "isoc";
    467                 break;
    468             default:
    469                 pszType = "invld";
    470                 break;
    471         }
    472 
    473         pRh->iSerial = (pRh->iSerial + 1) % 10000;
    474         RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
    475                      (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
    476                      pRh->iSerial, pszTag ? pszTag : "<none>");
    477 #endif
    478     }
    479 
    480     return pUrb;
    481 }
    482 
    483 
    484 /**
    485  * Calculate frame timer variables given a frame rate.
    486  */
    487 static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
    488 {
    489     pThis->nsWait     = RT_NS_1SEC / u32FrameRate;
    490     pThis->uFrameRate = u32FrameRate;
    491     /* Inform the HCD about the new frame rate. */
    492     pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
    493 }
    494 
    495 
    496 /**
    497  * Calculates the new frame rate based on the idle detection and number of idle
    498  * cycles.
    499  *
    500  * @returns nothing.
    501  * @param   pThis    The roothub instance data.
    502  * @param   fIdle    Flag whether the last frame didn't produce any activity.
    503  */
    504 static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
    505 {
    506     uint32_t uNewFrameRate = pThis->uFrameRate;
    507 
    508     /*
    509      * Adjust the frame timer interval based on idle detection.
    510      */
    511     if (fIdle)
    512     {
    513         pThis->cIdleCycles++;
    514         /* Set the new frame rate based on how long we've been idle. Tunable. */
    515         switch (pThis->cIdleCycles)
    516         {
    517             case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
    518             case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
    519             case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
    520             default:    break;
    521         }
    522         /* Avoid overflow. */
    523         if (pThis->cIdleCycles > 60000)
    524             pThis->cIdleCycles = 20000;
    525     }
    526     else
    527     {
    528         if (pThis->cIdleCycles)
    529         {
    530             pThis->cIdleCycles = 0;
    531             uNewFrameRate      = pThis->uFrameRateDefault;
    532         }
    533     }
    534 
    535     if (   uNewFrameRate != pThis->uFrameRate
    536         && uNewFrameRate)
    537     {
    538         LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
    539         vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
    540     }
    541 }
    542 
    543 
    544 /**
    545  * The core frame processing routine keeping track of the elapsed time and calling into
    546  * the device emulation above us to do the work.
    547  *
    548  * @returns Relative timespan when to process the next frame.
    549  * @param   pThis     The roothub instance data.
    550  * @param   fCallback Flag whether this method is called from the URB completion callback or
    551  *                    from the worker thread (only used for statistics).
    552  */
    553 DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
    554 {
    555     uint64_t tsNext = 0;
    556     uint64_t tsNanoStart = RTTimeNanoTS();
    557 
    558     /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
    559     if (!pThis->uFrameRateDefault)
    560         return 0;
    561 
    562     if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
    563         return pThis->nsWait;
    564 
    565     if (   tsNanoStart > pThis->tsFrameProcessed
    566         && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
    567     {
    568         LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
    569 
    570         bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
    571         vusbRhR3FrameRateCalcNew(pThis, fIdle);
    572 
    573         uint64_t tsNow = RTTimeNanoTS();
    574         tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
    575         pThis->tsFrameProcessed = tsNanoStart;
    576         LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
    577         if (fCallback)
    578             STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
    579         else
    580             STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
    581     }
    582     else
    583     {
    584         tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
    585         LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
    586                      tsNanoStart, pThis->tsFrameProcessed));
    587     }
    588 
    589     ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
    590     LogFlowFunc(("returns %llu\n", tsNext));
    591     return tsNext;
    592 }
    593 
    594 
    595 /**
    596  * Worker for processing frames periodically.
    597  *
    598  * @returns VBox status code.
    599  * @param   pDrvIns     The driver instance.
    600  * @param   pThread     The PDM thread structure for the thread this worker runs on.
    601  */
    602 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
    603 {
    604     RT_NOREF(pDrvIns);
    605     int rc = VINF_SUCCESS;
    606     PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
    607 
    608     if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
    609         return VINF_SUCCESS;
    610 
    611     while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    612     {
    613         while (   !ASMAtomicReadU32(&pThis->uFrameRateDefault)
    614                && pThread->enmState == PDMTHREADSTATE_RUNNING)
    615         {
    616             /* Signal the waiter that we are stopped now. */
    617             rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
    618             AssertRC(rc);
    619 
    620             rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
    621             RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
    622 
    623             /*
    624              * Notify the device above about the frame rate changed if we are supposed to
    625              * process frames.
    626              */
    627             uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
    628             if (uFrameRate)
    629                 vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
    630         }
    631 
    632         AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
    633         if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
    634             break;
    635 
    636         uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
    637 
    638         if (tsNext >= 250 * RT_NS_1US)
    639         {
    640             rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
    641                                        tsNext);
    642             AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
    643             RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
    644         }
    645     }
    646 
    647     return VINF_SUCCESS;
    648 }
    649 
    650 
    651 /**
    652  * Unblock the periodic frame thread so it can respond to a state change.
    653  *
    654  * @returns VBox status code.
    655  * @param   pDrvIns     The driver instance.
    656  * @param   pThread     The send thread.
    657  */
    658 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
    659 {
    660     RT_NOREF(pThread);
    661     PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
    662     return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
    663 }
    664 
    665 
    666 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
    667 static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
    668 {
    669     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    670 
    671     pRh->cbHci   = cbHci;
    672     pRh->cbHciTd = cbHciTd;
    673 
    674     return VINF_SUCCESS;
    675 }
    676 
    677 
    678 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
    679 static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
    680 {
    681     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    682     return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
    683 }
    684 
    685 
    686 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
    687 static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
    688 {
    689     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    690     LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
    691 
    692     Assert(     pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
    693            &&   pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
    694 
    695     if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED)
    696         pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED;
    697 
    698     return VINF_SUCCESS;
    699 }
    700 
    701 
    702 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
    703 static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
    704 {
    705     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    706     LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
    707 
    708     Assert(     pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
    709            &&   pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
    710 
    711     /*
    712      * Cancel all URBs and reap them.
    713      */
    714     VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
    715     for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
    716         VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
    717 
    718     pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
    719     return VINF_SUCCESS;
    720 }
    721 
    722 
    723 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
    724 static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
    725                                                VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
    726 {
    727     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    728     return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
    729 }
    730 
    731 
    732 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
    733 static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
    734 {
    735     RT_NOREF(pInterface);
    736     pUrb->pVUsb->pfnFree(pUrb);
    737     return VINF_SUCCESS;
    738 }
    739 
    740 
    741 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
    742 static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
    743 {
    744     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    745     STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
    746 
    747 #ifdef VBOX_WITH_STATISTICS
    748     /*
    749      * Total and per-type submit statistics.
    750      */
    751     Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
    752     STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
    753     STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
    754 
    755     STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
    756     STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
    757     if (pUrb->enmDir == VUSBDIRECTION_IN)
    758     {
    759         STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
    760         STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
    761     }
    762     else
    763     {
    764         STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
    765         STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
    766     }
    767 
    768     if (pUrb->enmType == VUSBXFERTYPE_ISOC)
    769     {
    770         STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
    771         if (pUrb->enmDir == VUSBDIRECTION_IN)
    772             STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
    773         else
    774             STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
    775     }
    776 #endif
    777 
    778     /* If there is a sniffer on the roothub record the URB there. */
    779     if (pRh->hSniffer != VUSBSNIFFER_NIL)
    780     {
    781         int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
    782         if (RT_FAILURE(rc))
    783             LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
    784     }
    785 
    786     /*
    787      * The device was resolved when we allocated the URB.
    788      * Submit it to the device if we found it, if not fail with device-not-ready.
    789      */
    790     int rc;
    791     if (   pUrb->pVUsb->pDev
    792         && pUrb->pVUsb->pDev->pUsbIns)
    793     {
    794         switch (pUrb->enmDir)
    795         {
    796             case VUSBDIRECTION_IN:
    797                 pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
    798                 rc = vusbUrbSubmit(pUrb);
    799                 pLed->Actual.s.fReading = 0;
    800                 break;
    801             case VUSBDIRECTION_OUT:
    802                 pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
    803                 rc = vusbUrbSubmit(pUrb);
    804                 pLed->Actual.s.fWriting = 0;
    805                 break;
    806             default:
    807                 rc = vusbUrbSubmit(pUrb);
    808                 break;
    809         }
    810 
    811         if (RT_FAILURE(rc))
    812         {
    813             LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
    814             pUrb->pVUsb->pfnFree(pUrb);
    815         }
    816     }
    817     else
    818     {
    819         vusbDevRetain(&pRh->Hub.Dev);
    820         pUrb->pVUsb->pDev = &pRh->Hub.Dev;
    821         Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
    822 
    823         pUrb->enmState = VUSBURBSTATE_REAPED;
    824         pUrb->enmStatus = VUSBSTATUS_DNR;
    825         vusbUrbCompletionRh(pUrb);
    826         rc = VINF_SUCCESS;
    827     }
    828 
    829     STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
    830     return rc;
    831 }
    832 
    833 
    834 static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
    835 {
    836     if (!cMillies)
    837         vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
    838     else
    839     {
    840         uint64_t u64Start = RTTimeMilliTS();
    841         do
    842         {
    843             vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
    844         } while (   !RTListIsEmpty(&pDev->LstAsyncUrbs)
    845                  && RTTimeMilliTS() - u64Start < cMillies);
    846     }
    847 
    848     return VINF_SUCCESS;
    849 }
    850 
    851 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
    852 static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
    853 {
    854     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
    855     PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
    856 
    857     if (  !pDev
    858         || RTListIsEmpty(&pDev->LstAsyncUrbs))
    859         return;
    860 
    861     STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
    862     int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
    863     AssertRC(rc);
    864     STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
    865 
    866     vusbDevRelease(pDev);
    867 }
    868 
    869 
    870 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
    871 static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
    872 {
    873     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    874     AssertReturn(pRh, VERR_INVALID_PARAMETER);
    875     AssertReturn(pUrb, VERR_INVALID_PARAMETER);
    876 
    877     /// @todo This method of URB canceling may not work on non-Linux hosts.
    878     /*
    879      * Cancel and reap the URB(s) on an endpoint.
    880      */
    881     LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
    882 
    883     vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
    884 
    885     /* The reaper thread will take care of completing the URB. */
    886 
    887     return VINF_SUCCESS;
    888 }
    889 
    890 /**
    891  * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
    892  *
    893  * @returns VBox status code.
    894  * @param   pDev    USB device instance data.
    895  */
    896 static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
    897 {
    898     /*
    899      * Cancel the URBS.
    900      *
    901      * Not using th CritAsyncUrbs critical section here is safe
    902      * as the I/O thread is the only thread accessing this struture at the
    903      * moment.
    904      */
    905     PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
    906     RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
    907     {
    908         PVUSBURB pUrb = pVUsbUrb->pUrb;
    909         /* Call the worker directly. */
    910         vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
    911     }
    912 
    913     return VINF_SUCCESS;
    914 }
    915 
    916 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
    917 static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
    918 {
    919     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    920 
    921     RTCritSectEnter(&pThis->CritSectDevices);
    922     for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
    923     {
    924         PVUSBDEV pDev = pThis->apDevByPort[i];
    925         if (pDev)
    926             vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
    927     }
    928     RTCritSectLeave(&pThis->CritSectDevices);
    929 }
    930 
    931 /**
    932  * Worker doing the actual cancelling of all outstanding per-EP URBs on the
    933  * device I/O thread.
    934  *
    935  * @returns VBox status code.
    936  * @param   pDev    USB device instance data.
    937  * @param   EndPt   Endpoint number.
    938  * @param   enmDir  Endpoint direction.
    939  */
    940 static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
    941 {
    942     /*
    943      * Iterate the URBs, find ones corresponding to given EP, and cancel them.
    944      */
    945     PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
    946     RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
    947     {
    948         PVUSBURB pUrb = pVUsbUrb->pUrb;
    949 
    950         Assert(pUrb->pVUsb->pDev == pDev);
    951 
    952         /* For the default control EP, direction does not matter. */
    953         if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
    954         {
    955             LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
    956             int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
    957             AssertRC(rc);
    958         }
    959     }
    960 
    961     return VINF_SUCCESS;
    962 }
    963 
    964 
    965 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
    966 static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
    967 {
    968     PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    969     PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
    970 
    971     if (&pRh->Hub != pDev->pHub)
    972         AssertFailedReturn(VERR_INVALID_PARAMETER);
    973 
    974     vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
    975     vusbDevRelease(pDev);
    976 
    977     /* The reaper thread will take care of completing the URB. */
    978 
    979     return VINF_SUCCESS;
    980 }
    981 
    982 
    983 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
    984 static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
    985 {
    986     int rc = VINF_SUCCESS;
    987     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    988 
    989     /* Create the frame thread lazily. */
    990     if (   !pThis->hThreadPeriodFrame
    991         && uFrameRate)
    992     {
    993         ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
    994         pThis->uFrameRate = uFrameRate;
    995         vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
    996 
    997         rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
    998         AssertRCReturn(rc, rc);
    999 
    1000         rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
    1001         AssertRCReturn(rc, rc);
    1002 
    1003         rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
    1004                                    vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
    1005         AssertRCReturn(rc, rc);
    1006 
    1007         VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
    1008         if (   enmState == VMSTATE_RUNNING
    1009             || enmState == VMSTATE_RUNNING_LS)
    1010         {
    1011             rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
    1012             AssertRCReturn(rc, rc);
    1013         }
    1014     }
    1015     else if (   pThis->hThreadPeriodFrame
    1016              && !uFrameRate)
    1017     {
    1018         /* Stop processing. */
    1019         uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
    1020         if (uFrameRateOld)
    1021         {
    1022             rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
    1023             AssertRC(rc);
    1024 
    1025             /* Signal the frame thread to stop. */
    1026             RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
    1027 
    1028             /* Wait for signal from the thread that it stopped. */
    1029             rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
    1030             AssertRC(rc);
    1031         }
    1032     }
    1033     else if (   pThis->hThreadPeriodFrame
    1034              && uFrameRate)
    1035     {
    1036         /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
    1037         uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
    1038 
    1039         /* Signal the frame thread to continue if it was stopped. */
    1040         if (!uFrameRateOld)
    1041             RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
    1042     }
    1043 
    1044     return rc;
    1045 }
    1046 
    1047 
    1048 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
    1049 static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
    1050 {
    1051     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1052 
    1053     return pThis->uFrameRate;
    1054 }
    1055 
    1056 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
    1057 static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
    1058                                                          int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
    1059 {
    1060     PVUSBROOTHUB    pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1061     AssertReturn(pRh, 0);
    1062     PVUSBDEV        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev);
    1063     PVUSBPIPE       pPipe = &pDev->aPipes[EndPt];
    1064     uint32_t        *puLastFrame;
    1065     int32_t         uFrameDelta;
    1066     uint32_t        uMaxVal = 1 << uBits;
    1067 
    1068     puLastFrame  = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
    1069     uFrameDelta  = uNewFrameID - *puLastFrame;
    1070     *puLastFrame = uNewFrameID;
    1071     /* Take care of wrap-around. */
    1072     if (uFrameDelta < 0)
    1073         uFrameDelta += uMaxVal;
    1074 
    1075     vusbDevRelease(pDev);
    1076     return (uint16_t)uFrameDelta;
    1077 }
    1078 
    1079 
    1080 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
    1081 static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
    1082                                           PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
    1083 {
    1084     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1085     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1086     AssertPtr(pDev);
    1087 
    1088     int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
    1089     vusbDevRelease(pDev);
    1090     return rc;
    1091 }
    1092 
    1093 
    1094 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
    1095 static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
    1096 {
    1097     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1098     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1099     AssertPtr(pDev);
    1100 
    1101     int rc = VUSBIDevPowerOn(&pDev->IDevice);
    1102     vusbDevRelease(pDev);
    1103     return rc;
    1104 }
    1105 
    1106 
    1107 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
    1108 static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
    1109 {
    1110     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1111     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1112     AssertPtr(pDev);
    1113 
    1114     int rc = VUSBIDevPowerOff(&pDev->IDevice);
    1115     vusbDevRelease(pDev);
    1116     return rc;
    1117 }
    1118 
    1119 
    1120 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
    1121 static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
    1122 {
    1123     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1124     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1125     AssertPtr(pDev);
    1126 
    1127     VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
    1128     vusbDevRelease(pDev);
    1129     return enmState;
    1130 }
    1131 
    1132 
    1133 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
    1134 static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
    1135 {
    1136     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1137     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1138     AssertPtr(pDev);
    1139 
    1140     bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
    1141     vusbDevRelease(pDev);
    1142     return fSavedStateSupported;
    1143 }
    1144 
    1145 
    1146 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
    1147 static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
    1148 {
    1149     PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
    1150     PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
    1151     AssertPtr(pDev);
    1152 
    1153     VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
    1154     vusbDevRelease(pDev);
    1155     return enmSpeed;
    1156 }
    1157 
    1158 
     296 * Returns a human readable string fromthe given USB speed enum.
     297 *
     298 * @returns Human readable string.
     299 * @param   enmSpeed            The speed to stringify.
     300 */
    1159301static const char *vusbGetSpeedString(VUSBSPEED enmSpeed)
    1160302{
     
    1190332
    1191333/**
     334 * Attaches a device to a specific hub.
     335 *
     336 * This function is called by the vusb_add_device() and vusbRhAttachDevice().
     337 *
     338 * @returns VBox status code.
     339 * @param   pThis       The roothub to attach it to.
     340 * @param   pDev        The device to attach.
     341 * @thread  EMT
     342 */
     343static int vusbHubAttach(PVUSBROOTHUB pThis, PVUSBDEV pDev)
     344{
     345    LogFlow(("vusbHubAttach: pThis=%p[%s] pDev=%p[%s]\n", pThis, pThis->Hub.pszName, pDev, pDev->pUsbIns->pszName));
     346
     347    PVUSBHUB pHub = &pThis->Hub;
     348
     349    /*
     350     * Assign a port.
     351     */
     352    int iPort = ASMBitFirstSet(&pThis->Bitmap, sizeof(pThis->Bitmap) * 8);
     353    if (iPort < 0)
     354    {
     355        LogRel(("VUSB: No ports available!\n"));
     356        return VERR_VUSB_NO_PORTS;
     357    }
     358    ASMBitClear(&pThis->Bitmap, iPort);
     359    pHub->cDevices++;
     360    pDev->i16Port = iPort;
     361
     362    /* Call the device attach helper, so it can initialize its state. */
     363    int rc = vusbDevAttach(pDev, pHub);
     364    if (RT_SUCCESS(rc))
     365    {
     366        RTCritSectEnter(&pThis->CritSectDevices);
     367        Assert(!pThis->apDevByPort[iPort]);
     368        pThis->apDevByPort[iPort] = pDev;
     369        RTCritSectLeave(&pThis->CritSectDevices);
     370
     371        /*
     372         * Call the HCI attach routine and let it have its say before the device is
     373         * linked into the device list of this hub.
     374         */
     375        VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
     376        rc = pThis->pIRhPort->pfnAttach(pThis->pIRhPort, iPort, enmSpeed);
     377        if (RT_SUCCESS(rc))
     378        {
     379            LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,
     380                    iPort, pHub->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));
     381            return VINF_SUCCESS;
     382        }
     383
     384        /* Remove from the port in case of failure. */
     385        RTCritSectEnter(&pThis->CritSectDevices);
     386        Assert(!pThis->apDevByPort[iPort]);
     387        pThis->apDevByPort[iPort] = NULL;
     388        RTCritSectLeave(&pThis->CritSectDevices);
     389
     390        vusbDevDetach(pDev);
     391    }
     392
     393    ASMBitSet(&pThis->Bitmap, iPort);
     394    pHub->cDevices--;
     395    pDev->i16Port = -1;
     396    LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));
     397
     398    return rc;
     399}
     400
     401
     402/* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
     403
     404/** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
     405static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
     406{
     407    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
     408
     409    /*
     410     * Allocate a new VUSB device and initialize it.
     411     */
     412    PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
     413    AssertReturn(pDev, VERR_NO_MEMORY);
     414    int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
     415    if (RT_SUCCESS(rc))
     416    {
     417        pUsbIns->pvVUsbDev2 = pDev;
     418        rc = vusbHubAttach(pThis, pDev);
     419        if (RT_SUCCESS(rc))
     420        {
     421            *piPort = UINT32_MAX; /// @todo implement piPort
     422            return rc;
     423        }
     424
     425        RTMemFree(pDev->paIfStates);
     426        pUsbIns->pvVUsbDev2 = NULL;
     427    }
     428    vusbDevRelease(pDev);
     429    return rc;
     430}
     431
     432
     433/** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
     434static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
     435{
     436    RT_NOREF(iPort);
     437    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
     438    PVUSBHUB pHub = &pThis->Hub;
     439    PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
     440    Assert(pDev);
     441
     442    /*
     443     * Deal with pending async reset.
     444     * (anything but reset)
     445     */
     446    vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
     447    Assert(pDev->i16Port != -1);
     448
     449    /* Cancel all in-flight URBs from this device. */
     450    vusbDevCancelAllUrbs(pDev, true);
     451
     452    /*
     453     * Detach the device and mark the port as available.
     454     */
     455    unsigned uPort = pDev->i16Port;
     456    pDev->i16Port = -1;
     457    pThis->pIRhPort->pfnDetach(pThis->pIRhPort, uPort);
     458    ASMBitSet(&pThis->Bitmap, uPort);
     459    pHub->cDevices--;
     460
     461    /* Check that it's attached and remove it. */
     462    RTCritSectEnter(&pThis->CritSectDevices);
     463    Assert(pThis->apDevByPort[uPort] == pDev);
     464    pThis->apDevByPort[uPort]  = NULL;
     465
     466    if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)
     467    {
     468        AssertPtr(pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS]);
     469
     470        if (pDev == pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS])
     471            pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr;
     472        else
     473        {
     474            /* Search the list for the device and remove it. */
     475            PVUSBDEV pDevPrev = pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS];
     476
     477            while (   pDevPrev
     478                   && pDevPrev->pNextDefAddr != pDev)
     479                pDevPrev = pDevPrev->pNextDefAddr;
     480
     481            AssertPtr(pDevPrev);
     482            pDevPrev->pNextDefAddr = pDev->pNextDefAddr;
     483        }
     484
     485        pDev->pNextDefAddr = NULL;
     486    }
     487    else
     488    {
     489        Assert(pThis->apDevByAddr[pDev->u8Address] == pDev);
     490        pThis->apDevByAddr[pDev->u8Address] = NULL;
     491    }
     492    RTCritSectLeave(&pThis->CritSectDevices);
     493
     494    /*
     495     * Detach and free resources.
     496     */
     497    vusbDevDetach(pDev);
     498
     499    LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName));
     500    vusbDevRelease(pDev);
     501    return VINF_SUCCESS;
     502}
     503
     504/**
     505 * The hub registration structure.
     506 */
     507static const PDMUSBHUBREG g_vusbHubReg =
     508{
     509    PDM_USBHUBREG_VERSION,
     510    vusbPDMHubAttachDevice,
     511    vusbPDMHubDetachDevice,
     512    PDM_USBHUBREG_VERSION
     513};
     514
     515
     516/* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
     517
     518
     519/**
     520 * Callback for freeing an URB.
     521 * @param   pUrb    The URB to free.
     522 */
     523static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
     524{
     525    /*
     526     * Assert sanity.
     527     */
     528    vusbUrbAssert(pUrb);
     529    PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
     530    Assert(pRh);
     531
     532    Assert(pUrb->enmState != VUSBURBSTATE_FREE);
     533
     534    /*
     535     * Free the URB description (logging builds only).
     536     */
     537    if (pUrb->pszDesc)
     538    {
     539        RTStrFree(pUrb->pszDesc);
     540        pUrb->pszDesc = NULL;
     541    }
     542
     543    /* The URB comes from the roothub if there is no device (invalid address). */
     544    if (pUrb->pVUsb->pDev)
     545    {
     546        PVUSBDEV pDev = pUrb->pVUsb->pDev;
     547
     548        vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
     549        vusbDevRelease(pDev);
     550    }
     551    else
     552        vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb);
     553}
     554
     555
     556/**
     557 * Worker routine for vusbRhConnNewUrb().
     558 */
     559static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
     560                             VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
     561{
     562    RT_NOREF(pszTag);
     563    PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool;
     564
     565    if (RT_UNLIKELY(cbData > (32 * _1M)))
     566    {
     567        LogFunc(("Bad URB size (%u)!\n", cbData));
     568        return NULL;
     569    }
     570
     571    PVUSBDEV pDev;
     572    if (uPort == VUSB_DEVICE_PORT_INVALID)
     573        pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress);
     574    else
     575        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
     576
     577    if (pDev)
     578        pUrbPool = &pDev->UrbPool;
     579
     580    PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
     581                                     pRh->cbHci, pRh->cbHciTd, cTds);
     582    if (RT_LIKELY(pUrb))
     583    {
     584        pUrb->pVUsb->pvFreeCtx = pRh;
     585        pUrb->pVUsb->pfnFree   = vusbRhFreeUrb;
     586        pUrb->DstAddress       = DstAddress;
     587        pUrb->pVUsb->pDev      = pDev;
     588
     589#ifdef LOG_ENABLED
     590        const char *pszType = NULL;
     591
     592        switch(pUrb->enmType)
     593        {
     594            case VUSBXFERTYPE_CTRL:
     595                pszType = "ctrl";
     596                break;
     597            case VUSBXFERTYPE_INTR:
     598                pszType = "intr";
     599                break;
     600            case VUSBXFERTYPE_BULK:
     601                pszType = "bulk";
     602                break;
     603            case VUSBXFERTYPE_ISOC:
     604                pszType = "isoc";
     605                break;
     606            default:
     607                pszType = "invld";
     608                break;
     609        }
     610
     611        pRh->iSerial = (pRh->iSerial + 1) % 10000;
     612        RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
     613                     (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
     614                     pRh->iSerial, pszTag ? pszTag : "<none>");
     615#endif
     616    }
     617
     618    return pUrb;
     619}
     620
     621
     622/**
     623 * Calculate frame timer variables given a frame rate.
     624 */
     625static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
     626{
     627    pThis->nsWait     = RT_NS_1SEC / u32FrameRate;
     628    pThis->uFrameRate = u32FrameRate;
     629    /* Inform the HCD about the new frame rate. */
     630    pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
     631}
     632
     633
     634/**
     635 * Calculates the new frame rate based on the idle detection and number of idle
     636 * cycles.
     637 *
     638 * @returns nothing.
     639 * @param   pThis    The roothub instance data.
     640 * @param   fIdle    Flag whether the last frame didn't produce any activity.
     641 */
     642static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
     643{
     644    uint32_t uNewFrameRate = pThis->uFrameRate;
     645
     646    /*
     647     * Adjust the frame timer interval based on idle detection.
     648     */
     649    if (fIdle)
     650    {
     651        pThis->cIdleCycles++;
     652        /* Set the new frame rate based on how long we've been idle. Tunable. */
     653        switch (pThis->cIdleCycles)
     654        {
     655            case 4: uNewFrameRate = 500;    break;  /*  2ms interval */
     656            case 16:uNewFrameRate = 125;    break;  /*  8ms interval */
     657            case 24:uNewFrameRate = 50;     break;  /* 20ms interval */
     658            default:    break;
     659        }
     660        /* Avoid overflow. */
     661        if (pThis->cIdleCycles > 60000)
     662            pThis->cIdleCycles = 20000;
     663    }
     664    else
     665    {
     666        if (pThis->cIdleCycles)
     667        {
     668            pThis->cIdleCycles = 0;
     669            uNewFrameRate      = pThis->uFrameRateDefault;
     670        }
     671    }
     672
     673    if (   uNewFrameRate != pThis->uFrameRate
     674        && uNewFrameRate)
     675    {
     676        LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
     677        vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
     678    }
     679}
     680
     681
     682/**
     683 * The core frame processing routine keeping track of the elapsed time and calling into
     684 * the device emulation above us to do the work.
     685 *
     686 * @returns Relative timespan when to process the next frame.
     687 * @param   pThis     The roothub instance data.
     688 * @param   fCallback Flag whether this method is called from the URB completion callback or
     689 *                    from the worker thread (only used for statistics).
     690 */
     691DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
     692{
     693    uint64_t tsNext = 0;
     694    uint64_t tsNanoStart = RTTimeNanoTS();
     695
     696    /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
     697    if (!pThis->uFrameRateDefault)
     698        return 0;
     699
     700    if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
     701        return pThis->nsWait;
     702
     703    if (   tsNanoStart > pThis->tsFrameProcessed
     704        && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
     705    {
     706        LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
     707
     708        bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
     709        vusbRhR3FrameRateCalcNew(pThis, fIdle);
     710
     711        uint64_t tsNow = RTTimeNanoTS();
     712        tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
     713        pThis->tsFrameProcessed = tsNanoStart;
     714        LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
     715        if (fCallback)
     716            STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
     717        else
     718            STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
     719    }
     720    else
     721    {
     722        tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
     723        LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
     724                     tsNanoStart, pThis->tsFrameProcessed));
     725    }
     726
     727    ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
     728    LogFlowFunc(("returns %llu\n", tsNext));
     729    return tsNext;
     730}
     731
     732
     733/**
     734 * Worker for processing frames periodically.
     735 *
     736 * @returns VBox status code.
     737 * @param   pDrvIns     The driver instance.
     738 * @param   pThread     The PDM thread structure for the thread this worker runs on.
     739 */
     740static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     741{
     742    RT_NOREF(pDrvIns);
     743    int rc = VINF_SUCCESS;
     744    PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
     745
     746    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     747        return VINF_SUCCESS;
     748
     749    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     750    {
     751        while (   !ASMAtomicReadU32(&pThis->uFrameRateDefault)
     752               && pThread->enmState == PDMTHREADSTATE_RUNNING)
     753        {
     754            /* Signal the waiter that we are stopped now. */
     755            rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
     756            AssertRC(rc);
     757
     758            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
     759            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
     760
     761            /*
     762             * Notify the device above about the frame rate changed if we are supposed to
     763             * process frames.
     764             */
     765            uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
     766            if (uFrameRate)
     767                vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
     768        }
     769
     770        AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
     771        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
     772            break;
     773
     774        uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
     775
     776        if (tsNext >= 250 * RT_NS_1US)
     777        {
     778            rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
     779                                       tsNext);
     780            AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
     781            RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
     782        }
     783    }
     784
     785    return VINF_SUCCESS;
     786}
     787
     788
     789/**
     790 * Unblock the periodic frame thread so it can respond to a state change.
     791 *
     792 * @returns VBox status code.
     793 * @param   pDrvIns     The driver instance.
     794 * @param   pThread     The send thread.
     795 */
     796static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     797{
     798    RT_NOREF(pThread);
     799    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
     800    return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
     801}
     802
     803
     804/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
     805static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
     806{
     807    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     808
     809    pRh->cbHci   = cbHci;
     810    pRh->cbHciTd = cbHciTd;
     811
     812    return VINF_SUCCESS;
     813}
     814
     815
     816/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
     817static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
     818{
     819    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     820    return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
     821}
     822
     823
     824/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
     825static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
     826{
     827    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     828    LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
     829
     830    Assert(     pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
     831           &&   pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
     832
     833    if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED)
     834        pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED;
     835
     836    return VINF_SUCCESS;
     837}
     838
     839
     840/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
     841static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
     842{
     843    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     844    LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
     845
     846    Assert(     pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
     847           &&   pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
     848
     849    /*
     850     * Cancel all URBs and reap them.
     851     */
     852    VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
     853    for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
     854        VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
     855
     856    pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
     857    return VINF_SUCCESS;
     858}
     859
     860
     861/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
     862static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
     863                                               VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
     864{
     865    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     866    return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
     867}
     868
     869
     870/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
     871static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
     872{
     873    RT_NOREF(pInterface);
     874    pUrb->pVUsb->pfnFree(pUrb);
     875    return VINF_SUCCESS;
     876}
     877
     878
     879/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
     880static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
     881{
     882    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     883    STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
     884
     885#ifdef VBOX_WITH_STATISTICS
     886    /*
     887     * Total and per-type submit statistics.
     888     */
     889    Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
     890    STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
     891    STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
     892
     893    STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
     894    STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
     895    if (pUrb->enmDir == VUSBDIRECTION_IN)
     896    {
     897        STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
     898        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
     899    }
     900    else
     901    {
     902        STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
     903        STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
     904    }
     905
     906    if (pUrb->enmType == VUSBXFERTYPE_ISOC)
     907    {
     908        STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
     909        if (pUrb->enmDir == VUSBDIRECTION_IN)
     910            STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
     911        else
     912            STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
     913    }
     914#endif
     915
     916    /* If there is a sniffer on the roothub record the URB there. */
     917    if (pRh->hSniffer != VUSBSNIFFER_NIL)
     918    {
     919        int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
     920        if (RT_FAILURE(rc))
     921            LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
     922    }
     923
     924    /*
     925     * The device was resolved when we allocated the URB.
     926     * Submit it to the device if we found it, if not fail with device-not-ready.
     927     */
     928    int rc;
     929    if (   pUrb->pVUsb->pDev
     930        && pUrb->pVUsb->pDev->pUsbIns)
     931    {
     932        switch (pUrb->enmDir)
     933        {
     934            case VUSBDIRECTION_IN:
     935                pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
     936                rc = vusbUrbSubmit(pUrb);
     937                pLed->Actual.s.fReading = 0;
     938                break;
     939            case VUSBDIRECTION_OUT:
     940                pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
     941                rc = vusbUrbSubmit(pUrb);
     942                pLed->Actual.s.fWriting = 0;
     943                break;
     944            default:
     945                rc = vusbUrbSubmit(pUrb);
     946                break;
     947        }
     948
     949        if (RT_FAILURE(rc))
     950        {
     951            LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
     952            pUrb->pVUsb->pfnFree(pUrb);
     953        }
     954    }
     955    else
     956    {
     957        vusbDevRetain(&pRh->Hub.Dev);
     958        pUrb->pVUsb->pDev = &pRh->Hub.Dev;
     959        Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
     960
     961        pUrb->enmState = VUSBURBSTATE_REAPED;
     962        pUrb->enmStatus = VUSBSTATUS_DNR;
     963        vusbUrbCompletionRh(pUrb);
     964        rc = VINF_SUCCESS;
     965    }
     966
     967    STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
     968    return rc;
     969}
     970
     971
     972static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
     973{
     974    if (!cMillies)
     975        vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
     976    else
     977    {
     978        uint64_t u64Start = RTTimeMilliTS();
     979        do
     980        {
     981            vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
     982        } while (   !RTListIsEmpty(&pDev->LstAsyncUrbs)
     983                 && RTTimeMilliTS() - u64Start < cMillies);
     984    }
     985
     986    return VINF_SUCCESS;
     987}
     988
     989/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
     990static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
     991{
     992    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
     993    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
     994
     995    if (  !pDev
     996        || RTListIsEmpty(&pDev->LstAsyncUrbs))
     997        return;
     998
     999    STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
     1000    int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
     1001    AssertRC(rc);
     1002    STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
     1003
     1004    vusbDevRelease(pDev);
     1005}
     1006
     1007
     1008/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
     1009static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
     1010{
     1011    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1012    AssertReturn(pRh, VERR_INVALID_PARAMETER);
     1013    AssertReturn(pUrb, VERR_INVALID_PARAMETER);
     1014
     1015    /// @todo This method of URB canceling may not work on non-Linux hosts.
     1016    /*
     1017     * Cancel and reap the URB(s) on an endpoint.
     1018     */
     1019    LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
     1020
     1021    vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
     1022
     1023    /* The reaper thread will take care of completing the URB. */
     1024
     1025    return VINF_SUCCESS;
     1026}
     1027
     1028/**
     1029 * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
     1030 *
     1031 * @returns VBox status code.
     1032 * @param   pDev    USB device instance data.
     1033 */
     1034static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
     1035{
     1036    /*
     1037     * Cancel the URBS.
     1038     *
     1039     * Not using th CritAsyncUrbs critical section here is safe
     1040     * as the I/O thread is the only thread accessing this struture at the
     1041     * moment.
     1042     */
     1043    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
     1044    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
     1045    {
     1046        PVUSBURB pUrb = pVUsbUrb->pUrb;
     1047        /* Call the worker directly. */
     1048        vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
     1049    }
     1050
     1051    return VINF_SUCCESS;
     1052}
     1053
     1054/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
     1055static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
     1056{
     1057    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1058
     1059    RTCritSectEnter(&pThis->CritSectDevices);
     1060    for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
     1061    {
     1062        PVUSBDEV pDev = pThis->apDevByPort[i];
     1063        if (pDev)
     1064            vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
     1065    }
     1066    RTCritSectLeave(&pThis->CritSectDevices);
     1067}
     1068
     1069/**
     1070 * Worker doing the actual cancelling of all outstanding per-EP URBs on the
     1071 * device I/O thread.
     1072 *
     1073 * @returns VBox status code.
     1074 * @param   pDev    USB device instance data.
     1075 * @param   EndPt   Endpoint number.
     1076 * @param   enmDir  Endpoint direction.
     1077 */
     1078static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
     1079{
     1080    /*
     1081     * Iterate the URBs, find ones corresponding to given EP, and cancel them.
     1082     */
     1083    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
     1084    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
     1085    {
     1086        PVUSBURB pUrb = pVUsbUrb->pUrb;
     1087
     1088        Assert(pUrb->pVUsb->pDev == pDev);
     1089
     1090        /* For the default control EP, direction does not matter. */
     1091        if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
     1092        {
     1093            LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
     1094            int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
     1095            AssertRC(rc);
     1096        }
     1097    }
     1098
     1099    return VINF_SUCCESS;
     1100}
     1101
     1102
     1103/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
     1104static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
     1105{
     1106    PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1107    PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
     1108
     1109    if (&pRh->Hub != pDev->pHub)
     1110        AssertFailedReturn(VERR_INVALID_PARAMETER);
     1111
     1112    vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
     1113    vusbDevRelease(pDev);
     1114
     1115    /* The reaper thread will take care of completing the URB. */
     1116
     1117    return VINF_SUCCESS;
     1118}
     1119
     1120
     1121/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
     1122static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
     1123{
     1124    int rc = VINF_SUCCESS;
     1125    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1126
     1127    /* Create the frame thread lazily. */
     1128    if (   !pThis->hThreadPeriodFrame
     1129        && uFrameRate)
     1130    {
     1131        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     1132        pThis->uFrameRate = uFrameRate;
     1133        vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
     1134
     1135        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
     1136        AssertRCReturn(rc, rc);
     1137
     1138        rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
     1139        AssertRCReturn(rc, rc);
     1140
     1141        rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
     1142                                   vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
     1143        AssertRCReturn(rc, rc);
     1144
     1145        VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
     1146        if (   enmState == VMSTATE_RUNNING
     1147            || enmState == VMSTATE_RUNNING_LS)
     1148        {
     1149            rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
     1150            AssertRCReturn(rc, rc);
     1151        }
     1152    }
     1153    else if (   pThis->hThreadPeriodFrame
     1154             && !uFrameRate)
     1155    {
     1156        /* Stop processing. */
     1157        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     1158        if (uFrameRateOld)
     1159        {
     1160            rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
     1161            AssertRC(rc);
     1162
     1163            /* Signal the frame thread to stop. */
     1164            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
     1165
     1166            /* Wait for signal from the thread that it stopped. */
     1167            rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
     1168            AssertRC(rc);
     1169        }
     1170    }
     1171    else if (   pThis->hThreadPeriodFrame
     1172             && uFrameRate)
     1173    {
     1174        /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
     1175        uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
     1176
     1177        /* Signal the frame thread to continue if it was stopped. */
     1178        if (!uFrameRateOld)
     1179            RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
     1180    }
     1181
     1182    return rc;
     1183}
     1184
     1185
     1186/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
     1187static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
     1188{
     1189    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1190
     1191    return pThis->uFrameRate;
     1192}
     1193
     1194/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
     1195static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
     1196                                                         int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
     1197{
     1198    PVUSBROOTHUB    pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1199    AssertReturn(pRh, 0);
     1200    PVUSBDEV        pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev);
     1201    PVUSBPIPE       pPipe = &pDev->aPipes[EndPt];
     1202    uint32_t        *puLastFrame;
     1203    int32_t         uFrameDelta;
     1204    uint32_t        uMaxVal = 1 << uBits;
     1205
     1206    puLastFrame  = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
     1207    uFrameDelta  = uNewFrameID - *puLastFrame;
     1208    *puLastFrame = uNewFrameID;
     1209    /* Take care of wrap-around. */
     1210    if (uFrameDelta < 0)
     1211        uFrameDelta += uMaxVal;
     1212
     1213    vusbDevRelease(pDev);
     1214    return (uint16_t)uFrameDelta;
     1215}
     1216
     1217
     1218/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
     1219static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
     1220                                          PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
     1221{
     1222    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1223    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1224    AssertPtr(pDev);
     1225
     1226    int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
     1227    vusbDevRelease(pDev);
     1228    return rc;
     1229}
     1230
     1231
     1232/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
     1233static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
     1234{
     1235    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1236    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1237    AssertPtr(pDev);
     1238
     1239    int rc = VUSBIDevPowerOn(&pDev->IDevice);
     1240    vusbDevRelease(pDev);
     1241    return rc;
     1242}
     1243
     1244
     1245/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
     1246static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
     1247{
     1248    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1249    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1250    AssertPtr(pDev);
     1251
     1252    int rc = VUSBIDevPowerOff(&pDev->IDevice);
     1253    vusbDevRelease(pDev);
     1254    return rc;
     1255}
     1256
     1257
     1258/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
     1259static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
     1260{
     1261    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1262    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1263    AssertPtr(pDev);
     1264
     1265    VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
     1266    vusbDevRelease(pDev);
     1267    return enmState;
     1268}
     1269
     1270
     1271/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
     1272static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
     1273{
     1274    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1275    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1276    AssertPtr(pDev);
     1277
     1278    bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
     1279    vusbDevRelease(pDev);
     1280    return fSavedStateSupported;
     1281}
     1282
     1283
     1284/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
     1285static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
     1286{
     1287    PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
     1288    PVUSBDEV     pDev  = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
     1289    AssertPtr(pDev);
     1290
     1291    VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
     1292    vusbDevRelease(pDev);
     1293    return enmSpeed;
     1294}
     1295
     1296
     1297/**
    11921298 * @callback_method_impl{FNSSMDRVSAVEPREP, All URBs needs to be canceled.}
    11931299 */
     
    12701376        PVUSBDEV pDev = aPortsOld[i];
    12711377        if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
    1272             vusbHubAttach(&pThis->Hub, pDev);
     1378            vusbHubAttach(pThis, pDev);
    12731379    }
    12741380
     
    13431449     */
    13441450    for (unsigned i = 0; i < pLoad->cDevs; i++)
    1345         vusbHubAttach(&pThis->Hub, pLoad->apDevs[i]);
     1451        vusbHubAttach(pThis, pLoad->apDevs[i]);
    13461452
    13471453    /*
     
    13811487
    13821488
    1383 /* -=-=-=-=-=- VUSB Hub methods -=-=-=-=-=- */
    1384 
    1385 
    1386 /**
    1387  * Attach the device to the hub.
    1388  * Port assignments and all such stuff is up to this routine.
    1389  *
    1390  * @returns VBox status code.
    1391  * @param   pHub        Pointer to the hub.
    1392  * @param   pDev        Pointer to the device.
    1393  */
    1394 static int vusbRhHubOpAttach(PVUSBHUB pHub, PVUSBDEV pDev)
    1395 {
    1396     PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;
    1397 
    1398     /*
    1399      * Assign a port.
    1400      */
    1401     int iPort = ASMBitFirstSet(&pRh->Bitmap, sizeof(pRh->Bitmap) * 8);
    1402     if (iPort < 0)
    1403     {
    1404         LogRel(("VUSB: No ports available!\n"));
    1405         return VERR_VUSB_NO_PORTS;
    1406     }
    1407     ASMBitClear(&pRh->Bitmap, iPort);
    1408     pHub->cDevices++;
    1409     pDev->i16Port = iPort;
    1410 
    1411     /*
    1412      * Call the HCI attach routine and let it have its say before the device is
    1413      * linked into the device list of this hub.
    1414      */
    1415     VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
    1416     int rc = pRh->pIRhPort->pfnAttach(pRh->pIRhPort, iPort, enmSpeed);
    1417     if (RT_SUCCESS(rc))
    1418     {
    1419         RTCritSectEnter(&pRh->CritSectDevices);
    1420         Assert(!pRh->apDevByPort[iPort]);
    1421         pRh->apDevByPort[iPort] = pDev;
    1422 
    1423         RTCritSectLeave(&pRh->CritSectDevices);
    1424         LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,
    1425                 iPort, pHub->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));
    1426     }
    1427     else
    1428     {
    1429         ASMBitSet(&pRh->Bitmap, iPort);
    1430         pHub->cDevices--;
    1431         pDev->i16Port = -1;
    1432         LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));
    1433     }
    1434     return rc;
    1435 }
    1436 
    1437 
    1438 /**
    1439  * Detach the device from the hub.
    1440  *
    1441  * @returns VBox status code.
    1442  * @param   pHub        Pointer to the hub.
    1443  * @param   pDev        Pointer to the device.
    1444  */
    1445 static void vusbRhHubOpDetach(PVUSBHUB pHub, PVUSBDEV pDev)
    1446 {
    1447     PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;
    1448     Assert(pDev->i16Port != -1);
    1449 
    1450     /* Check that it's attached and remvoe it. */
    1451     RTCritSectEnter(&pRh->CritSectDevices);
    1452     Assert(pRh->apDevByPort[pDev->i16Port] == pDev);
    1453     pRh->apDevByPort[pDev->i16Port]   = NULL;
    1454 
    1455     if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)
    1456     {
    1457         AssertPtr(pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS]);
    1458 
    1459         if (pDev == pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS])
    1460             pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr;
    1461         else
    1462         {
    1463             /* Search the list for the device and remove it. */
    1464             PVUSBDEV pDevPrev = pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS];
    1465 
    1466             while (   pDevPrev
    1467                    && pDevPrev->pNextDefAddr != pDev)
    1468                 pDevPrev = pDevPrev->pNextDefAddr;
    1469 
    1470             AssertPtr(pDevPrev);
    1471             pDevPrev->pNextDefAddr = pDev->pNextDefAddr;
    1472         }
    1473 
    1474         pDev->pNextDefAddr = NULL;
    1475     }
    1476     else
    1477     {
    1478         Assert(pRh->apDevByAddr[pDev->u8Address] == pDev);
    1479         pRh->apDevByAddr[pDev->u8Address] = NULL;
    1480     }
    1481     RTCritSectLeave(&pRh->CritSectDevices);
    1482 
    1483     /*
    1484      * Detach the device and mark the port as available.
    1485      */
    1486     unsigned uPort = pDev->i16Port;
    1487     pRh->pIRhPort->pfnDetach(pRh->pIRhPort, uPort);
    1488     LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName));
    1489     ASMBitSet(&pRh->Bitmap, uPort);
    1490     pHub->cDevices--;
    1491 }
    1492 
    1493 
    1494 /**
    1495  * The Hub methods implemented by the root hub.
    1496  */
    1497 static const VUSBHUBOPS s_VUsbRhHubOps =
    1498 {
    1499     vusbRhHubOpAttach,
    1500     vusbRhHubOpDetach
    1501 };
    1502 
    1503 
    1504 
    15051489/* -=-=-=-=-=- PDM Base interface methods -=-=-=-=-=- */
    15061490
     
    16041588    pThis->Hub.Dev.cRefs                = 1;
    16051589    /* the hub */
    1606     pThis->Hub.pOps                     = &s_VUsbRhHubOps;
    16071590    pThis->Hub.pRootHub                 = pThis;
    16081591    //pThis->hub.cPorts                - later
  • trunk/src/VBox/Devices/USB/VUSBDevice.cpp

    r93940 r93955  
    12791279    /* Create I/O thread and attach to the hub. */
    12801280    int rc = vusbDevUrbIoThreadCreate(pDev);
    1281     if (RT_SUCCESS(rc))
    1282         rc = pHub->pOps->pfnAttach(pHub, pDev);
    1283 
    12841281    if (RT_FAILURE(rc))
    12851282    {
     
    13051302    VUSBDEV_ASSERT_VALID_STATE(pDev->enmState);
    13061303    Assert(pDev->enmState != VUSB_DEVICE_STATE_RESET);
    1307 
    1308     vusbDevCancelAllUrbs(pDev, true);
    1309 
    1310     PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
    1311     if (!pRh)
    1312         AssertMsgFailedReturn(("Not attached!\n"), VERR_VUSB_DEVICE_NOT_ATTACHED);
    1313 
    1314     /* The roothub will remove the device from the address to device array. */
    1315     pDev->pHub->pOps->pfnDetach(pDev->pHub, pDev);
    1316     pDev->i16Port = -1;
    13171304
    13181305    /*
  • trunk/src/VBox/Devices/USB/VUSBInternal.h

    r93939 r93955  
    320320
    321321
    322 /** Virtual method table for USB hub devices.
    323  * Hub and roothub drivers need to implement these functions in addition to the
    324  * vusb_dev_ops.
    325  */
    326 typedef struct VUSBHUBOPS
    327 {
    328     int     (*pfnAttach)(PVUSBHUB pHub, PVUSBDEV pDev);
    329     void    (*pfnDetach)(PVUSBHUB pHub, PVUSBDEV pDev);
    330 } VUSBHUBOPS;
    331 /** Pointer to a const HUB method table. */
    332 typedef const VUSBHUBOPS *PCVUSBHUBOPS;
    333 
    334322/** A VUSB Hub Device - Hub and roothub drivers need to use this struct
    335323 * @todo eliminate this (PDM  / roothubs only).
     
    338326{
    339327    VUSBDEV             Dev;
    340     PCVUSBHUBOPS        pOps;
    341328    PVUSBROOTHUB        pRootHub;
    342329    uint16_t            cPorts;
     
    345332    char               *pszName;
    346333} VUSBHUB;
    347 AssertCompileMemberAlignment(VUSBHUB, pOps, 8);
    348334AssertCompileSizeAlignment(VUSBHUB, 8);
    349335
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette