/** @interface_method_impl{PDMHPETHLPR3,pfnSetLegacyMode} */
static DECLCALLBACK(int) pdmR3HpetHlp_SetLegacyMode(PPDMDEVINS pDevIns, bool fActivated)
{
    PDMDEV_ASSERT_DEVINS(pDevIns);
    LogFlow(("pdmR3HpetHlp_SetLegacyMode: caller='%s'/%d: fActivated=%RTbool\n", pDevIns->pReg->szName, pDevIns->iInstance, fActivated));

    size_t                      i;
    int                         rc = VINF_SUCCESS;
    static const char * const   s_apszDevsToNotify[] =
    {
        "i8254",
        "mc146818"
    };
    for (i = 0; i < RT_ELEMENTS(s_apszDevsToNotify); i++)
    {
        PPDMIBASE pBase;
        rc = PDMR3QueryDevice(pDevIns->Internal.s.pVMR3->pUVM, "i8254", 0, &pBase);
        if (RT_SUCCESS(rc))
        {
            PPDMIHPETLEGACYNOTIFY pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIHPETLEGACYNOTIFY);
            AssertLogRelMsgBreakStmt(pPort, ("%s\n", s_apszDevsToNotify[i]), rc = VERR_PDM_HPET_LEGACY_NOTIFY_MISSING);
            pPort->pfnModeChanged(pPort, fActivated);
        }
        else if (   rc == VERR_PDM_DEVICE_NOT_FOUND
                 || rc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
            rc = VINF_SUCCESS; /* the device isn't configured, ignore. */
        else
            AssertLogRelMsgFailedBreak(("%s -> %Rrc\n", s_apszDevsToNotify[i], rc));
    }

    /* Don't bother cleaning up, any failure here will cause a guru meditation. */

    LogFlow(("pdmR3HpetHlp_SetLegacyMode: caller='%s'/%d: returns %Rrc\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
    return rc;
}
static DECLCALLBACK(int) drvHostALSAAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
                                                   uint32_t *pcSamplesCaptured)
{
    NOREF(pInterface);
    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);

    PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;

    snd_pcm_sframes_t cAvail;
    int rc = drvHostALSAAudioGetAvail(pThisStrmIn->phPCM, &cAvail);
    if (RT_FAILURE(rc))
    {
        LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
        return rc;
    }

    if (!cAvail) /* No data yet? */
    {
        snd_pcm_state_t state = snd_pcm_state(pThisStrmIn->phPCM);
        switch (state)
        {
            case SND_PCM_STATE_PREPARED:
                cAvail = AudioMixBufFree(&pHstStrmIn->MixBuf);
                break;

            case SND_PCM_STATE_SUSPENDED:
            {
                rc = drvHostALSAAudioResume(pThisStrmIn->phPCM);
                if (RT_FAILURE(rc))
                    break;

                LogFlow(("Resuming suspended input stream\n"));
                break;
            }

            default:
                LogFlow(("No frames available, state=%d\n", state));
                break;
        }

        if (!cAvail)
        {
            if (pcSamplesCaptured)
                *pcSamplesCaptured = 0;
            return VINF_SUCCESS;
        }
    }

    /*
     * Check how much we can read from the capture device without overflowing
     * the mixer buffer.
     */
    Assert(cAvail);
    size_t cbMixFree = AudioMixBufFreeBytes(&pHstStrmIn->MixBuf);
    size_t cbToRead = RT_MIN((size_t)AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cAvail), cbMixFree);

    LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));

    uint32_t cWrittenTotal = 0;
    snd_pcm_uframes_t cToRead;
    snd_pcm_sframes_t cRead;

    while (   cbToRead
           && RT_SUCCESS(rc))
    {
        cToRead = RT_MIN(AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbToRead),
                         AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, pThisStrmIn->cbBuf));
        AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
        cRead = snd_pcm_readi(pThisStrmIn->phPCM, pThisStrmIn->pvBuf, cToRead);
        if (cRead <= 0)
        {
            switch (cRead)
            {
                case 0:
                {
                    LogFunc(("No input frames available\n"));
                    rc = VERR_ACCESS_DENIED;
                    break;
                }

                case -EAGAIN:
                    /*
                     * Don't set error here because EAGAIN means there are no further frames
                     * available at the moment, try later. As we might have read some frames
                     * already these need to be processed instead.
                     */
                    cbToRead = 0;
                    break;

                case -EPIPE:
                {
                    rc = drvHostALSAAudioRecover(pThisStrmIn->phPCM);
                    if (RT_FAILURE(rc))
                        break;

                    LogFlowFunc(("Recovered from capturing\n"));
                    continue;
                }

                default:
                    LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
                    break;
            }
        }
        else
        {
            uint32_t cWritten;
            rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
                                      pThisStrmIn->pvBuf, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cRead),
                                      &cWritten);
            if (RT_FAILURE(rc))
                break;

            /*
             * We should not run into a full mixer buffer or we loose samples and
             * run into an endless loop if ALSA keeps producing samples ("null"
             * capture device for example).
             */
            AssertLogRelMsgBreakStmt(cWritten > 0, ("Mixer buffer shouldn't be full at this point!\n"),
                                     rc = VERR_INTERNAL_ERROR);
            uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);

            Assert(cbToRead >= cbWritten);
            cbToRead -= cbWritten;
            cWrittenTotal += cWritten;
        }
    }

    if (RT_SUCCESS(rc))
    {
        uint32_t cProcessed = 0;
        if (cWrittenTotal)
            rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
                                        &cProcessed);

        if (pcSamplesCaptured)
            *pcSamplesCaptured = cWrittenTotal;

        LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
                     cWrittenTotal, cProcessed, rc));
    }

    LogFlowFuncLeaveRC(rc);
    return rc;
}
/**
 * Watcher thread.
 */
/*static*/ DECLCALLBACK(int) VirtualBoxSDS::i_watcherThreadProc(RTTHREAD hSelf, void *pvUser)
{
    VBoxSDSWatcher *pThis    = (VBoxSDSWatcher *)pvUser;
    VirtualBoxSDS  *pVBoxSDS = pThis->pVBoxSDS;
    RT_NOREF(hSelf);

    /*
     * This thread may release references to IVBoxSVCRegistration objects.
     */
    CoInitializeEx(NULL, COINIT_MULTITHREADED);

    /*
     * The loop.
     */
    RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
    while (!pThis->fShutdown)
    {
        /*
         * Deal with the todo list.
         */
        uint32_t cHandles = pThis->cHandles;
        uint32_t cTodos   = pThis->cTodos;

        for (uint32_t i = 0; i < cTodos; i++)
        {
            VBoxSDSPerUserData *pUserData = pThis->aTodos[i].Data.pUserData;
            AssertContinue(pUserData);
            if (pThis->aTodos[i].hProcess != NULL)
            {
                /* Add: */
                AssertLogRelMsgBreakStmt(cHandles < RT_ELEMENTS(pThis->aHandles),
                                         ("cHandles=%u cTodos=%u i=%u iWatcher=%u\n", cHandles, cTodos, i, pThis->iWatcher),
                                         pThis->fShutdown = true);
                pThis->aHandles[cHandles] = pThis->aTodos[i].hProcess;
                pThis->aData[cHandles]    = pThis->aTodos[i].Data;
                cHandles++;
            }
            else
            {
                /* Remove: */
                uint32_t cRemoved = 0;
                uint32_t j        = cHandles;
                while (j-- > 1)
                    if (pThis->aData[j].pUserData == pUserData)
                    {
                        cHandles = pThis->removeHandle(j, cHandles);
                        pUserData->i_release();
                        cRemoved++;
                    }
                if (cRemoved != 1)
                    LogRel(("i_watcherThreadProc/#%u: Warning! cRemoved=%u pUserData=%p\n", pThis->iWatcher, cRemoved, pUserData));
            }
            /* Zap the entry in case we assert and leave further up. */
            pThis->aTodos[i].Data.setNull();
            pThis->aTodos[i].hProcess = NULL;
        }

        Assert(cHandles > 0 && cHandles <= RT_ELEMENTS(pThis->aHandles));
        pThis->cHandles          = cHandles;
        pThis->cHandlesEffective = cHandles;
        pThis->cTodos            = 0;

        if (pThis->fShutdown)
            break;

        /*
         * Wait.
         */
        RTCritSectLeave(&pVBoxSDS->m_WatcherCritSect);

        LogRel(("i_watcherThreadProc/#%u: Waiting on %u handles...\n", pThis->iWatcher, cHandles));
        DWORD const dwWait = WaitForMultipleObjects(cHandles, pThis->aHandles, FALSE /*fWaitAll*/, INFINITE);
        LogRel(("i_watcherThreadProc/#%u: ... wait returned: %#x (%d)\n", pThis->iWatcher, dwWait, dwWait));

        uint32_t const iHandle = dwWait - WAIT_OBJECT_0;
        if (iHandle < cHandles && iHandle > 0)
        {
            /*
             * A VBoxSVC process has terminated.
             *
             * Note! We need to take the user data lock before the watcher one here.
             */
            VBoxSDSPerUserData * const pUserData = pThis->aData[iHandle].pUserData;
            uint32_t const             iRevision = pThis->aData[iHandle].iRevision;
            RTPROCESS const            pid       = pThis->aData[iHandle].pid;

            pUserData->i_lock();
            RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);

            DWORD dwExit = 0;
            GetExitCodeProcess(pThis->aHandles[iHandle], &dwExit);
            LogRel(("i_watcherThreadProc/#%u: %p/%s: PID %u/%#x termination detected: %d (%#x)  [iRev=%u, cur %u]\n",
                    pThis->iWatcher, pUserData, pUserData->m_strUsername.c_str(), pid, pid, dwExit, dwExit,
                    iRevision, pUserData->m_iTheChosenOneRevision));

            /* Remove it from the handle array. */
            CloseHandle(pThis->aHandles[iHandle]);
            pThis->cHandles = cHandles = pThis->removeHandle(iHandle, cHandles);
            pThis->cHandlesEffective -= 1;

            /* If the process we were watching is still the current chosen one,
               unchoose it and decrement the client count.  Otherwise we were subject
               to a deregistration/termination race (unlikely). */
            if (pUserData->m_iTheChosenOneRevision == iRevision)
            {
                pUserData->i_unchooseTheOne(true /*fIrregular*/);
                pUserData->i_unlock();
                pVBoxSDS->i_decrementClientCount();
            }
            else
                pUserData->i_unlock();
            pUserData->i_release();
        }
        else
        {
            RTCritSectEnter(&pThis->pVBoxSDS->m_WatcherCritSect);
            AssertLogRelMsgBreak(iHandle == 0 || dwWait == WAIT_TIMEOUT,
                                 ("dwWait=%u (%#x) cHandles=%u\n", dwWait, dwWait, cHandles));
        }
    }

    RTCritSectLeave(&pThis->pVBoxSDS->m_WatcherCritSect);

    /*
     * In case we quit w/o being told, signal i_watchIt that we're out of action.
     */
    pThis->fShutdown = true;

    /*
     * Release all our data on the way out.
     */
    uint32_t i = pThis->cHandles;
    while (i-- > 1)
    {
        if (pThis->aData[i].pUserData)
        {
            pThis->aData[i].pUserData->i_release();
            pThis->aData[i].pUserData = NULL;
        }
        if (pThis->aHandles[i])
        {
            CloseHandle(pThis->aHandles[i]);
            pThis->aHandles[i] = NULL;
        }
    }
    if (pThis->aHandles[0])
    {
        CloseHandle(pThis->aHandles[0]);
        pThis->aHandles[0] = NULL;
    }

    i = pThis->cTodos;
    pThis->cTodos = 0;
    while (i-- > 0)
    {
        if (pThis->aTodos[i].Data.pUserData)
        {
            pThis->aTodos[i].Data.pUserData->i_release();
            pThis->aTodos[i].Data.pUserData = NULL;
        }
        if (pThis->aTodos[i].hProcess)
        {
            CloseHandle(pThis->aTodos[i].hProcess);
            pThis->aTodos[i].hProcess = NULL;
        }
    }

    if (ASMAtomicDecU32(&pThis->cRefs) == 0)
        RTMemFree(pThis);

    return VINF_SUCCESS;
}