Index: /trunk/include/VBox/vusb.h
===================================================================
--- /trunk/include/VBox/vusb.h	(revision 59874)
+++ /trunk/include/VBox/vusb.h	(revision 59875)
@@ -29,4 +29,5 @@
 #include <VBox/cdefs.h>
 #include <VBox/types.h>
+#include <iprt/assert.h>
 
 struct PDMLED;
@@ -645,4 +646,23 @@
     DECLR3CALLBACKMEMBER(bool, pfnXferError,(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb));
 
+    /**
+     * Processes a new frame if periodic frame processing is enabled.
+     *
+     * @returns Flag whether there was activity which influences the frame rate.
+     * @param   pInterface      Pointer to this structure.
+     * @param   u32FrameNo      The frame number.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnStartFrame, (PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo));
+
+    /**
+     * Informs the callee about a change in the frame rate due to too many idle cycles or
+     * when seeing activity after some idle time.
+     *
+     * @returns nothing.
+     * @param   pInterface      Pointer to this structure.
+     * @param   u32Framerate    The new frame rate.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnFrameRateChanged, (PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate));
+
     /** Alignment dummy. */
     RTR3PTR Alignment;
@@ -650,5 +670,5 @@
 } VUSBIROOTHUBPORT;
 /** VUSBIROOTHUBPORT interface ID. */
-#define VUSBIROOTHUBPORT_IID                    "79a31188-043d-432c-82ac-9485c9ab9a49"
+#define VUSBIROOTHUBPORT_IID                    "6571aece-6c33-4714-a8ac-9508a3b8b429"
 
 /** Pointer to a VUSB RootHub connector interface. */
@@ -789,10 +809,28 @@
     DECLR3CALLBACKMEMBER(int, pfnDetachDevice,(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBIDEVICE pDevice));
 
-    /** Alignment dummy. */
-    RTR3PTR Alignment;
+    /**
+     * Sets periodic frame processing.
+     *
+     * @returns VBox status code.
+     * @param   pInterface  Pointer to this struct.
+     * @param   uFrameRate  The target frame rate in Hertz, 0 disables periodic frame processing.
+     *                      The real frame rate might be lower if there is no activity for a certain period or
+     *                      higher if there is a need for catching up with where the guest expects the device to be.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetPeriodicFrameProcessing, (PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate));
+
+    /**
+     * Returns the current frame rate for the periodic frame processing.
+     *
+     * @returns Frame rate for periodic frame processing.
+     * @retval  0 if disabled.
+     * @param   pInterface  Pointer to this struct.
+     */
+    DECLR3CALLBACKMEMBER(uint32_t, pfnGetPeriodicFrameRate, (PVUSBIROOTHUBCONNECTOR pInterface));
 
 } VUSBIROOTHUBCONNECTOR;
+AssertCompileSizeAlignment(VUSBIROOTHUBCONNECTOR, 8);
 /** VUSBIROOTHUBCONNECTOR interface ID. */
-#define VUSBIROOTHUBCONNECTOR_IID               "a593cc64-a821-4e57-af2d-f86b2a052ea4"
+#define VUSBIROOTHUBCONNECTOR_IID               "662d7822-b9c6-43b5-88b6-5d59f0106e46"
 
 
@@ -845,4 +883,16 @@
 {
     return pInterface->pfnDetachDevice(pInterface, pDevice);
+}
+
+/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetPeriodicFrameProcessing */
+DECLINLINE(int) VUSBIRhSetPeriodicFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
+{
+    return pInterface->pfnSetPeriodicFrameProcessing(pInterface, uFrameRate);
+}
+
+/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetPeriodicFrameProcessing */
+DECLINLINE(uint32_t) VUSBIRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
+{
+    return pInterface->pfnGetPeriodicFrameRate(pInterface);
 }
 #endif /* IN_RING3 */
Index: /trunk/src/VBox/Devices/USB/DevOHCI.cpp
===================================================================
--- /trunk/src/VBox/Devices/USB/DevOHCI.cpp	(revision 59874)
+++ /trunk/src/VBox/Devices/USB/DevOHCI.cpp	(revision 59875)
@@ -223,7 +223,4 @@
     R3PTRTYPE(POHCI)                    pOhci;
 } OHCIROOTHUB;
-#if HC_ARCH_BITS == 64
-AssertCompile(sizeof(OHCIROOTHUB) == 392); /* saved state */
-#endif
 /** Pointer to the OHCI root hub. */
 typedef OHCIROOTHUB *POHCIROOTHUB;
@@ -387,8 +384,4 @@
     /** VM timer frequency used for frame timer calculations. */
     uint64_t            u64TimerHz;
-    /** Number of USB work cycles with no transfers. */
-    uint32_t            cIdleCycles;
-    /** Current frame timer rate (default 1000). */
-    uint32_t            uFrameRate;
     /** Idle detection flag; must be cleared at start of frame */
     bool                fIdle;
@@ -403,17 +396,4 @@
     /** Critical section synchronising interrupt handling. */
     PDMCRITSECT         CsIrq;
-
-    /** The framer thread. */
-    R3PTRTYPE(PPDMTHREAD)      hThreadFrame;
-    /** Event semaphore to interact with the framer thread. */
-    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrame;
-    /** Event semaphore to release the thread waiting for the framer thread to stop. */
-    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrameStopped;
-    /** Flag whether the framer thread should processing frames. */
-    volatile bool              fBusStarted;
-    /** Alignment. */
-    uint32_t                   Alignment5;
-    /** How long to wait until the next frame. */
-    uint64_t                   nsWait;
     /** Critical section to synchronize the framer and URB completion handler. */
     RTCRITSECT                 CritSect;
@@ -2193,67 +2173,4 @@
 
 /**
- * Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
- */
-static void ohciCalcTimerIntervals(POHCI pThis, uint32_t u32FrameRate)
-{
-    Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
-
-    pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
-    if (!pThis->cTicksPerFrame)
-        pThis->cTicksPerFrame = 1;
-    pThis->cTicksPerUsbTick   = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
-    pThis->nsWait             = RT_NS_1SEC / u32FrameRate;
-    pThis->uFrameRate         = u32FrameRate;
-}
-
-
-/**
- * Calculates the new frame rate based on the idle detection and number of idle
- * cycles.
- *
- * @returns nothing.
- * @param   pThis    The OHCI device data.
- */
-static bool ohciFramerateCalcNew(POHCI pThis)
-{
-    uint32_t uNewFrameRate = pThis->uFrameRate;
-
-    /*
-     * Adjust the frame timer interval based on idle detection.
-     */
-    if (pThis->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      = OHCI_DEFAULT_TIMER_FREQ;
-        }
-    }
-    if (uNewFrameRate != pThis->uFrameRate)
-    {
-        LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
-        ohciCalcTimerIntervals(pThis, uNewFrameRate);
-        return true;
-    }
-    return false;
-}
-
-
-/**
  * Returns the OHCI_CC_* corresponding to the VUSB status code.
  *
@@ -2640,8 +2557,4 @@
     /* finally write back the endpoint descriptor. */
     ohciWriteEd(pThis, pUrb->pHci->EdAddr, &Ed);
-
-    /* Calculate new frame rate and wakeup the framer thread if the rate was chnaged. */
-    if (ohciFramerateCalcNew(pThis))
-        RTSemEventMultiSignal(pThis->hSemEventFrame);
 
     RTCritSectLeave(&pThis->CritSect);
@@ -3874,9 +3787,4 @@
     }
 #endif
-
-    /*
-     * Adjust the frame timer interval based on idle detection.
-     */
-    ohciFramerateCalcNew(pThis);
 }
 
@@ -3891,121 +3799,45 @@
 }
 
-static DECLCALLBACK(int) ohciR3ThreadFrame(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    POHCI pThis = (POHCI)pThread->pvUser;
-    uint64_t tsBeginServicing = 0;
-    uint64_t cFramesProcessed = 0;
-
-    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
-        return VINF_SUCCESS;
-
-    tsBeginServicing = RTTimeNanoTS();
-    cFramesProcessed = 0;
-
-    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
-    {
-        while (   !ASMAtomicReadBool(&pThis->fBusStarted)
-               && pThread->enmState == PDMTHREADSTATE_RUNNING)
-        {
-            /* Signal the waiter that we are stopped now. */
-            int rc = RTSemEventMultiSignal(pThis->hSemEventFrameStopped);
-            AssertRC(rc);
-            rc = RTSemEventMultiReset(pThis->hSemEventFrame);
-            AssertRC(rc);
-
-            /*
-             * We have to check that the Bus was not started and the thread state
-             * did not change or otherwise we risk hanging here indefinitely
-             * if the signaller set the event semaphore before we reset it.
-             */
-            if (ASMAtomicReadBool(&pThis->fBusStarted) || pThread->enmState != PDMTHREADSTATE_RUNNING)
-                break;
-
-            rc = RTSemEventMultiWait(pThis->hSemEventFrame, RT_INDEFINITE_WAIT);
-            AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
-            tsBeginServicing = RTTimeNanoTS();
-            cFramesProcessed = 0;
-        }
-
-        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
-            break;
-
-        RTCritSectEnter(&pThis->CritSect);
-
-        /* Reset idle detection flag */
-        pThis->fIdle = true;
-
-        /*
-         * Process new frames until we reached the required amount of
-         * frames for this service period. We might need to catch up
-         * here and process multiple frames at once due to scheduling
-         * preempting us. This is required because isochronous transfers
-         * have a tight timing requirement.
-         */
-        uint64_t tsNow = RTTimeNanoTS();
-        uint64_t nsWait = 0;
-        while (tsBeginServicing + (cFramesProcessed * RT_NS_1MS) < tsNow)
-        {
-            uint64_t tsNanoStart = RTTimeNanoTS();
-            LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
-
-            /* Frame boundary, so do EOF stuff here. */
-            bump_frame_number(pThis);
-            if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
-                pThis->dqic--;
-
-            /* Clean up any URBs that have been removed. */
-            ohciCancelOrphanedURBs(pThis);
-
-            /* Start the next frame. */
-            ohciStartOfFrame(pThis);
-            cFramesProcessed++;
-
-            tsNow = RTTimeNanoTS();
-            uint64_t tsFrameNext = tsNanoStart + pThis->nsWait;
-
-            if (tsFrameNext > tsNow)
-            {
-                nsWait = tsFrameNext - tsNow;
-                LogFlowFunc(("Current frame took %llu nano seconds to finish, we can wait %llu ns for the next frame\n", tsNow - tsNanoStart, nsWait));
-                break;
-            }
-            else if (tsBeginServicing + (cFramesProcessed + 100) * RT_NS_1MS < tsNow)
-            {
-                /* If we lag to far behind stop trying to catch up. */
-                LogRelMax(10, ("OHCI#%u: Lagging too far behind, not trying to catch up anymore. Expect glitches with USB devices\n",
-                               pThis->pDevInsR3->iInstance));
-                tsBeginServicing = tsNow;
-                cFramesProcessed = 0;
-            }
-        }
-
-        RTCritSectLeave(&pThis->CritSect);
-
-        /* Wait for the next round. */
-        if (nsWait >= 500 * RT_NS_1US)
-        {
-            LogFlowFunc(("Going to sleep for at least %llu ns\n", nsWait));
-            int rc = RTSemEventMultiWaitEx(pThis->hSemEventFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
-                                           nsWait);
-            AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
-            RTSemEventMultiReset(pThis->hSemEventFrame);
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Unblock the framer thread so it can respond to a state change.
- *
- * @returns VBox status code.
- * @param   pDevIns     The device instance.
- * @param   pThread     The send thread.
- */
-static DECLCALLBACK(int) ohciR3ThreadFrameWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
-    POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
-    return RTSemEventMultiSignal(pThis->hSemEventFrame);
+/**
+ * Callback for periodic frame processing.
+ */
+static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
+{
+    POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
+
+    RTCritSectEnter(&pThis->CritSect);
+
+    /* Reset idle detection flag */
+    pThis->fIdle = true;
+
+    /* Frame boundary, so do EOF stuff here. */
+    bump_frame_number(pThis);
+    if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
+        pThis->dqic--;
+
+    /* Clean up any URBs that have been removed. */
+    ohciCancelOrphanedURBs(pThis);
+
+    /* Start the next frame. */
+    ohciStartOfFrame(pThis);
+
+    RTCritSectLeave(&pThis->CritSect);
+
+    return pThis->fIdle;
+}
+
+/**
+ * @copydoc VUSBIROOTHUBPORT::pfnFramerateChanged.
+ */
+static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
+{
+    POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
+
+    Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
+
+    pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
+    if (!pThis->cTicksPerFrame)
+        pThis->cTicksPerFrame = 1;
+    pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
 }
 
@@ -4029,7 +3861,6 @@
 
     pThis->SofTime = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns));
-    bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, true);
-    if (!fBusActive)
-        RTSemEventMultiSignal(pThis->hSemEventFrame);
+    int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
+    AssertRC(rc);
 }
 
@@ -4039,17 +3870,6 @@
 static void ohciBusStop(POHCI pThis)
 {
-    bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, false);
-    if (fBusActive)
-    {
-        int rc = RTSemEventMultiReset(pThis->hSemEventFrameStopped);
-        AssertRC(rc);
-
-        /* Signal the frame thread to stop. */
-        RTSemEventMultiSignal(pThis->hSemEventFrame);
-
-        /* Wait for signal from the thrad that it stopped. */
-        rc = RTSemEventMultiWait(pThis->hSemEventFrameStopped, RT_INDEFINITE_WAIT);
-        AssertRC(rc);
-    }
+    int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, 0);
+    AssertRC(rc);
     VUSBIDevPowerOff(pThis->RootHub.pIDev);
 }
@@ -5268,5 +5088,5 @@
      */
     /** @todo: Do it properly for 4.4 by changing the saved state. */
-    if (pThis->fBusStarted)
+    if (VUSBIRhGetPeriodicFrameRate(pRh->pIRhConn) != 0)
     {
         /* Calculate a new timer expiration so this saved state works with older releases. */
@@ -5680,7 +5500,7 @@
         AssertRC(rc);
 
-        LogFlowFunc(("Bus was active, restart frame thread\n"));
-        ASMAtomicXchgBool(&pThis->fBusStarted, true);
-        RTSemEventMultiSignal(pThis->hSemEventFrame);
+        LogFlowFunc(("Bus was active, enable periodic frame processing\n"));
+        rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
+        AssertRC(rc);
     }
 }
@@ -5772,11 +5592,4 @@
     PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
 
-    /*
-     * Destroy event sempahores.
-     */
-    if (pThis->hSemEventFrame)
-        RTSemEventMultiDestroy(pThis->hSemEventFrame);
-    if (pThis->hSemEventFrameStopped)
-        RTSemEventMultiDestroy(pThis->hSemEventFrameStopped);
     if (RTCritSectIsInitialized(&pThis->CritSect))
         RTCritSectDelete(&pThis->CritSect);
@@ -5827,4 +5640,6 @@
     pThis->RootHub.IRhPort.pfnXferCompletion     = ohciRhXferCompletion;
     pThis->RootHub.IRhPort.pfnXferError          = ohciRhXferError;
+    pThis->RootHub.IRhPort.pfnStartFrame         = ohciR3StartFrame;
+    pThis->RootHub.IRhPort.pfnFrameRateChanged   = ohciR3FrameRateChanged;
 
     /* USB LED */
@@ -5942,9 +5757,4 @@
      */
     pThis->u64TimerHz = TMTimerGetFreq(pThis->CTX_SUFF(pEndOfFrameTimer));
-    ohciCalcTimerIntervals(pThis, OHCI_DEFAULT_TIMER_FREQ);
-    Log(("ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
-         pThis->cTicksPerFrame, pThis->cTicksPerUsbTick));
-
-    pThis->fBusStarted = false;
 
     rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
@@ -5953,20 +5763,8 @@
                                    N_("OHCI: Failed to create critical section"));
 
-    rc = RTSemEventMultiCreate(&pThis->hSemEventFrame);
-    AssertRCReturn(rc, rc);
-
-    rc = RTSemEventMultiCreate(&pThis->hSemEventFrameStopped);
-    AssertRCReturn(rc, rc);
-
     rc = RTCritSectInit(&pThis->CritSect);
     if (RT_FAILURE(rc))
         return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                    N_("OHCI: Failed to create critical section"));
-
-    rc = PDMDevHlpThreadCreate(pDevIns, &pThis->hThreadFrame, pThis, ohciR3ThreadFrame,
-                               ohciR3ThreadFrameWakeup, 0, RTTHREADTYPE_TIMER, "OhciFramer");
-    if (RT_FAILURE(rc))
-        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
-                                   N_("OHCI: Failed to create worker thread"));
 
     /*
Index: /trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp
===================================================================
--- /trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp	(revision 59874)
+++ /trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp	(revision 59875)
@@ -415,4 +415,175 @@
 
 
+/**
+ * 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)
+    {
+        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)
+{
+    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);
+        }
+
+        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)
+{
+    PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
+    return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
+}
+
+
 /** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetUrbParams */
 static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
@@ -703,4 +874,60 @@
 
 
+/** @copydoc VUSBIROOTHUBCONNECTOR::pfnSetFrameProcessing */
+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);
+
+        rc = PDMR3ThreadResume(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. */
+        ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
+    }
+
+    return rc;
+}
+
+
 /* -=-=-=-=-=- VUSB Device methods (for the root hub) -=-=-=-=-=- */
 
@@ -920,4 +1147,11 @@
     if (pRh->hSniffer != VUSBSNIFFER_NIL)
         VUSBSnifferDestroy(pRh->hSniffer);
+
+    if (pRh->hSemEventPeriodFrame)
+        RTSemEventMultiDestroy(pRh->hSemEventPeriodFrame);
+
+    if (pRh->hSemEventPeriodFrameStopped)
+        RTSemEventMultiDestroy(pRh->hSemEventPeriodFrameStopped);
+
     RTCritSectDelete(&pRh->CritSectDevices);
 }
@@ -985,19 +1219,21 @@
     pThis->pDrvIns                      = pDrvIns;
     /* the connector */
-    pThis->IRhConnector.pfnSetUrbParams = vusbRhSetUrbParams;
-    pThis->IRhConnector.pfnNewUrb       = vusbRhConnNewUrb;
-    pThis->IRhConnector.pfnFreeUrb      = vusbRhConnFreeUrb;
-    pThis->IRhConnector.pfnSubmitUrb    = vusbRhSubmitUrb;
-    pThis->IRhConnector.pfnReapAsyncUrbs= vusbRhReapAsyncUrbs;
-    pThis->IRhConnector.pfnCancelUrbsEp = vusbRhCancelUrbsEp;
-    pThis->IRhConnector.pfnCancelAllUrbs= vusbRhCancelAllUrbs;
-    pThis->IRhConnector.pfnAbortEp      = vusbRhAbortEp;
-    pThis->IRhConnector.pfnAttachDevice = vusbRhAttachDevice;
-    pThis->IRhConnector.pfnDetachDevice = vusbRhDetachDevice;
-    pThis->hSniffer                     = VUSBSNIFFER_NIL;
-    pThis->cbHci                        = 0;
-    pThis->cbHciTd                      = 0;
+    pThis->IRhConnector.pfnSetUrbParams               = vusbRhSetUrbParams;
+    pThis->IRhConnector.pfnNewUrb                     = vusbRhConnNewUrb;
+    pThis->IRhConnector.pfnFreeUrb                    = vusbRhConnFreeUrb;
+    pThis->IRhConnector.pfnSubmitUrb                  = vusbRhSubmitUrb;
+    pThis->IRhConnector.pfnReapAsyncUrbs              = vusbRhReapAsyncUrbs;
+    pThis->IRhConnector.pfnCancelUrbsEp               = vusbRhCancelUrbsEp;
+    pThis->IRhConnector.pfnCancelAllUrbs              = vusbRhCancelAllUrbs;
+    pThis->IRhConnector.pfnAbortEp                    = vusbRhAbortEp;
+    pThis->IRhConnector.pfnAttachDevice               = vusbRhAttachDevice;
+    pThis->IRhConnector.pfnDetachDevice               = vusbRhDetachDevice;
+    pThis->IRhConnector.pfnSetPeriodicFrameProcessing = vusbRhSetFrameProcessing;
+    pThis->hSniffer                                   = VUSBSNIFFER_NIL;
+    pThis->cbHci                                      = 0;
+    pThis->cbHciTd                                    = 0;
+    pThis->fFrameProcessing                           = false;
 #ifdef LOG_ENABLED
-    pThis->iSerial                      = 0;
+    pThis->iSerial                                    = 0;
 #endif
     /*
@@ -1168,4 +1404,6 @@
     PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReapAsyncUrbs, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Profiling the vusbRhReapAsyncUrbs body (omitting calls when nothing is in-flight).",  "/VUSB/%d/ReapAsyncUrbs", pDrvIns->iInstance);
     PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSubmitUrb,     STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Profiling the vusbRhSubmitUrb body.",                                 "/VUSB/%d/SubmitUrb",                 pDrvIns->iInstance);
+    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedThread, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the dedicated thread", "/VUSB/%d/FramesProcessedThread",       pDrvIns->iInstance);
+    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedClbk,   STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the URB completion callback", "/VUSB/%d/FramesProcessedClbk",  pDrvIns->iInstance);
 #endif
     PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->Hub.Dev.UrbPool.cUrbsInPool, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs in the pool.",                                 "/VUSB/%d/cUrbsInPool",               pDrvIns->iInstance);
Index: /trunk/src/VBox/Devices/USB/VUSBInternal.h
===================================================================
--- /trunk/src/VBox/Devices/USB/VUSBInternal.h	(revision 59874)
+++ /trunk/src/VBox/Devices/USB/VUSBInternal.h	(revision 59875)
@@ -386,47 +386,68 @@
     /** The HUB.
      * @todo remove this? */
-    VUSBHUB                 Hub;
+    VUSBHUB                    Hub;
     /** Address hash table. */
-    PVUSBDEV                apAddrHash[VUSB_ADDR_HASHSZ];
+    PVUSBDEV                   apAddrHash[VUSB_ADDR_HASHSZ];
     /** The default address. */
-    PVUSBDEV                pDefaultAddress;
+    PVUSBDEV                   pDefaultAddress;
 
     /** Pointer to the driver instance. */
-    PPDMDRVINS              pDrvIns;
+    PPDMDRVINS                 pDrvIns;
     /** Pointer to the root hub port interface we're attached to. */
-    PVUSBIROOTHUBPORT       pIRhPort;
+    PVUSBIROOTHUBPORT          pIRhPort;
     /** Connector interface exposed upwards. */
-    VUSBIROOTHUBCONNECTOR   IRhConnector;
+    VUSBIROOTHUBCONNECTOR      IRhConnector;
+
+    /** Critical section protecting the device list. */
+    RTCRITSECT                 CritSectDevices;
+    /** Chain of devices attached to this hub. */
+    PVUSBDEV                   pDevices;
 
 #if HC_ARCH_BITS == 32
-    uint32_t                Alignment0;
-#endif
-
-    /** Critical section protecting the device list. */
-    RTCRITSECT              CritSectDevices;
-    /** Chain of devices attached to this hub. */
-    PVUSBDEV                pDevices;
+    uint32_t                   Alignment0;
+#endif
+
+    /** Availability Bitmap. */
+    VUSBPORTBITMAP             Bitmap;
+
+    /** Sniffer instance for the root hub. */
+    VUSBSNIFFER                hSniffer;
+    /** Version of the attached Host Controller. */
+    uint32_t                   fHcVersions;
+    /** Size of the HCI specific data for each URB. */
+    size_t                     cbHci;
+    /** Size of the HCI specific TD. */
+    size_t                     cbHciTd;
+
+    /** The periodic frame processing thread. */
+    R3PTRTYPE(PPDMTHREAD)      hThreadPeriodFrame;
+    /** Event semaphore to interact with the periodic frame processing thread. */
+    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventPeriodFrame;
+    /** Event semaphore to release the thread waiting for the periodic frame processing thread to stop. */
+    R3PTRTYPE(RTSEMEVENTMULTI) hSemEventPeriodFrameStopped;
+    /** Current default frame rate for periodic frame processing thread. */
+    volatile uint32_t          uFrameRateDefault;
+    /** Current frame rate (can be lower than the default frame rate if there is no activity). */
+    uint32_t                   uFrameRate;
+    /** How long to wait until the next frame. */
+    uint64_t                   nsWait;
+    /** Timestamp when the last frame was processed. */
+    uint64_t                   tsFrameProcessed;
+    /** Number of USB work cycles with no transfers. */
+    uint32_t                   cIdleCycles;
+
+    /** Flag whether a frame is currently being processed. */
+    volatile bool              fFrameProcessing;
 
 #if HC_ARCH_BITS == 32
-    uint32_t                Alignment1;
-#endif
-
-    /** Availability Bitmap. */
-    VUSBPORTBITMAP          Bitmap;
-
-    /** Sniffer instance for the root hub. */
-    VUSBSNIFFER             hSniffer;
-    /** Version of the attached Host Controller. */
-    uint32_t                fHcVersions;
-    /** Size of the HCI specific data for each URB. */
-    size_t                  cbHci;
-    /** Size of the HCI specific TD. */
-    size_t                  cbHciTd;
+    uint32_t                   Alignment1;
+#endif
+
 #ifdef LOG_ENABLED
     /** A serial number for URBs submitted on the roothub instance.
      * Only logging builds. */
-    uint32_t                iSerial;
+    uint32_t                   iSerial;
     /** Alignment */
-    uint32_t                Alignment2;
+    uint32_t                   Alignment2;
 #endif
 #ifdef VBOX_WITH_STATISTICS
@@ -454,4 +475,6 @@
     STAMPROFILE             StatReapAsyncUrbs;
     STAMPROFILE             StatSubmitUrb;
+    STAMCOUNTER             StatFramesProcessedClbk;
+    STAMCOUNTER             StatFramesProcessedThread;
 #endif
 } VUSBROOTHUB;
@@ -501,4 +524,6 @@
 DECLHIDDEN(int) vusbUrbCancelWorker(PVUSBURB pUrb, CANCELMODE enmMode);
 
+DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback);
+
 int  vusbUrbQueueAsyncRh(PVUSBURB pUrb);
 
Index: /trunk/src/VBox/Devices/USB/VUSBUrb.cpp
===================================================================
--- /trunk/src/VBox/Devices/USB/VUSBUrb.cpp	(revision 59874)
+++ /trunk/src/VBox/Devices/USB/VUSBUrb.cpp	(revision 59875)
@@ -376,4 +376,6 @@
         pUrb->pVUsb->pfnFree(pUrb);
     }
+
+    vusbRhR3ProcessFrame(pRh, true /* fCallback */);
 }
 
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 59874)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 59875)
@@ -1051,9 +1051,5 @@
     GEN_CHECK_OFF(OHCI, StatTimer);
 # endif
-    GEN_CHECK_OFF(OHCI, hThreadFrame);
-    GEN_CHECK_OFF(OHCI, hSemEventFrame);
-    GEN_CHECK_OFF(OHCI, fBusStarted);
     GEN_CHECK_OFF(OHCI, CsIrq);
-    GEN_CHECK_OFF(OHCI, nsWait);
     GEN_CHECK_OFF(OHCI, CritSect);
 
