VirtualBox

Changeset 88137 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
Mar 16, 2021 12:22:05 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
143285
Message:

DevHDA: Made the DMA timer scheduling heuristics more flexible and ditched the alternative approaches. Changed the DMA engine to load the whole BDL and not reload BDLEs as we work thru them. Ditched the HDABDLE structure and some other DMA related stuff (FIFO buffer is on the todo list). bugref:9890

Location:
trunk/src/VBox/Devices/Audio
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DevHDA.cpp

    r88112 r88137  
    250250#endif
    251251} HDADRIVER;
     252
     253
     254/** Internal state of this BDLE.
     255 *  Not part of the actual BDLE registers.
     256 * @note Only for saved state.  */
     257typedef struct HDABDLESTATELEGACY
     258{
     259    /** Own index within the BDL (Buffer Descriptor List). */
     260    uint32_t     u32BDLIndex;
     261    /** Number of bytes below the stream's FIFO watermark (SDFIFOW).
     262     *  Used to check if we need fill up the FIFO again. */
     263    uint32_t     cbBelowFIFOW;
     264    /** Current offset in DMA buffer (in bytes).*/
     265    uint32_t     u32BufOff;
     266    uint32_t     Padding;
     267} HDABDLESTATELEGACY;
     268
     269/**
     270 * BDLE and state.
     271 * @note Only for saved state.
     272 */
     273typedef struct HDABDLELEGACY
     274{
     275    /** The actual BDL description. */
     276    HDABDLEDESC         Desc;
     277    HDABDLESTATELEGACY  State;
     278} HDABDLELEGACY;
     279AssertCompileSize(HDABDLELEGACY, 32);
    252280
    253281
     
    469497static SSMFIELD const g_aSSMBDLEStateFields6[] =
    470498{
    471     SSMFIELD_ENTRY(HDABDLESTATE, u32BDLIndex),
    472     SSMFIELD_ENTRY(HDABDLESTATE, cbBelowFIFOW),
    473     SSMFIELD_ENTRY_OLD(FIFO,     HDA_FIFO_MAX), /* Deprecated; now is handled in the stream's circular buffer. */
    474     SSMFIELD_ENTRY(HDABDLESTATE, u32BufOff),
     499    SSMFIELD_ENTRY(HDABDLESTATELEGACY, u32BDLIndex),
     500    SSMFIELD_ENTRY(HDABDLESTATELEGACY, cbBelowFIFOW),
     501    SSMFIELD_ENTRY_OLD(FIFO,           HDA_FIFO_MAX), /* Deprecated; now is handled in the stream's circular buffer. */
     502    SSMFIELD_ENTRY(HDABDLESTATELEGACY, u32BufOff),
    475503    SSMFIELD_ENTRY_TERM()
    476504};
     
    479507static SSMFIELD const g_aSSMBDLEStateFields7[] =
    480508{
    481     SSMFIELD_ENTRY(HDABDLESTATE, u32BDLIndex),
    482     SSMFIELD_ENTRY(HDABDLESTATE, cbBelowFIFOW),
    483     SSMFIELD_ENTRY(HDABDLESTATE, u32BufOff),
     509    SSMFIELD_ENTRY(HDABDLESTATELEGACY, u32BDLIndex),
     510    SSMFIELD_ENTRY(HDABDLESTATELEGACY, cbBelowFIFOW),
     511    SSMFIELD_ENTRY(HDABDLESTATELEGACY, u32BufOff),
    484512    SSMFIELD_ENTRY_TERM()
    485513};
     
    489517{
    490518    SSMFIELD_ENTRY_OLD(cBDLE,      sizeof(uint16_t)), /* Deprecated. */
    491     SSMFIELD_ENTRY(HDASTREAMSTATE, uCurBDLE),
     519    SSMFIELD_ENTRY_OLD(uCurBDLE,   sizeof(uint16_t)), /* We figure it out from LPID */
    492520    SSMFIELD_ENTRY_OLD(fStop,      1),                /* Deprecated; see SSMR3PutBool(). */
    493521    SSMFIELD_ENTRY_OLD(fRunning,   1),                /* Deprecated; using the HDA_SDCTL_RUN bit is sufficient. */
     
    499527static SSMFIELD const g_aSSMStreamStateFields7[] =
    500528{
    501     SSMFIELD_ENTRY(HDASTREAMSTATE, uCurBDLE),
     529    SSMFIELD_ENTRY(HDASTREAMSTATE, idxCurBdle),         /* For backward compatibility we save this. We use LPIB on restore. */
     530    SSMFIELD_ENTRY_OLD(uCurBDLEHi, sizeof(uint8_t)),    /* uCurBDLE was 16-bit for some reason, so store/ignore the zero top byte. */
    502531    SSMFIELD_ENTRY(HDASTREAMSTATE, fInReset),
    503532    SSMFIELD_ENTRY(HDASTREAMSTATE, tsTransferNext),
     
    518547static SSMFIELD const g_aSSMStreamBdleFields1234[] =
    519548{
    520     SSMFIELD_ENTRY(HDABDLE           Desc.u64BufAddr),       /* u64BdleCviAddr */
    521     SSMFIELD_ENTRY_OLD(u32BdleMaxCvi,   sizeof(uint32_t)),      /* u32BdleMaxCvi */
    522     SSMFIELD_ENTRY(HDABDLE           State.u32BDLIndex),     /* u32BdleCvi */
    523     SSMFIELD_ENTRY(HDABDLE           Desc.u32BufSize),       /* u32BdleCviLen */
    524     SSMFIELD_ENTRY(HDABDLE           State.u32BufOff),       /* u32BdleCviPos */
    525     SSMFIELD_ENTRY_CALLBACK(HDABDLE  Desc.fFlags, hdaR3GetPutTrans_HDABDLE_Desc_fFlags_1thru4), /* fBdleCviIoc */
    526     SSMFIELD_ENTRY(HDABDLE           State.cbBelowFIFOW),    /* cbUnderFifoW */
    527     SSMFIELD_ENTRY_OLD(au8FIFO,         256),                   /* au8FIFO */
     549    SSMFIELD_ENTRY(HDABDLELEGACY,           Desc.u64BufAddr),       /* u64BdleCviAddr */
     550    SSMFIELD_ENTRY_OLD(u32BdleMaxCvi,       sizeof(uint32_t)),      /* u32BdleMaxCvi */
     551    SSMFIELD_ENTRY(HDABDLELEGACY,           State.u32BDLIndex),     /* u32BdleCvi */
     552    SSMFIELD_ENTRY(HDABDLELEGACY,           Desc.u32BufSize),       /* u32BdleCviLen */
     553    SSMFIELD_ENTRY(HDABDLELEGACY,           State.u32BufOff),       /* u32BdleCviPos */
     554    SSMFIELD_ENTRY_CALLBACK(HDABDLELEGACY,  Desc.fFlags, hdaR3GetPutTrans_HDABDLE_Desc_fFlags_1thru4), /* fBdleCviIoc */
     555    SSMFIELD_ENTRY(HDABDLELEGACY,           State.cbBelowFIFOW),    /* cbUnderFifoW */
     556    SSMFIELD_ENTRY_OLD(au8FIFO,             256),                   /* au8FIFO */
    528557    SSMFIELD_ENTRY_TERM()
    529558};
     
    33543383    if (RT_SUCCESS(rc))
    33553384    {
    3356         PHDABDLE pState = (PHDABDLE)pvStruct;
     3385        HDABDLELEGACY *pState = (HDABDLELEGACY *)pvStruct;
    33573386        pState->Desc.fFlags = fIoc ? HDA_BDLE_F_IOC : 0;
    33583387    }
     
    33793408    AssertRCReturn(rc, rc);
    33803409
    3381     rc = pHlp->pfnSSMPutStructEx(pSSM, &pStreamShared->State.BDLE.Desc, sizeof(pStreamShared->State.BDLE.Desc),
    3382                                  0 /*fFlags*/, g_aSSMBDLEDescFields7, NULL);
     3410    HDABDLEDESC TmpDesc = *(HDABDLEDESC *)&pStreamShared->State.aBdl[RT_MIN(pStreamShared->State.idxCurBdle,
     3411                                                                            RT_ELEMENTS(pStreamShared->State.aBdl))];
     3412    rc = pHlp->pfnSSMPutStructEx(pSSM, &TmpDesc, sizeof(TmpDesc), 0 /*fFlags*/, g_aSSMBDLEDescFields7, NULL);
    33833413    AssertRCReturn(rc, rc);
    33843414
    3385     rc = pHlp->pfnSSMPutStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(pStreamShared->State.BDLE.State),
    3386                                 0 /*fFlags*/,  g_aSSMBDLEStateFields7, NULL);
     3415    HDABDLESTATELEGACY TmpState = { pStreamShared->State.idxCurBdle, 0, pStreamShared->State.offCurBdle, 0 };
     3416    rc = pHlp->pfnSSMPutStructEx(pSSM, &TmpState, sizeof(TmpState), 0 /*fFlags*/,  g_aSSMBDLEStateFields7, NULL);
    33873417    AssertRCReturn(rc, rc);
    33883418
     
    34773507
    34783508/**
    3479  * Does required post processing when loading a saved state.
     3509 * Does tail processing after having loaded our saved state.
    34803510 *
    3481  * @param   pDevIns             The device instance.
    3482  * @param   pThis               Pointer to the shared HDA state.
    3483  * @param   pThisCC             Pointer to the ring-3 HDA state.
    3484  */
    3485 static int hdaR3LoadExecPost(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC)
     3511 * @param   pDevIns     The device instance.
     3512 * @param   pThis       Pointer to the shared HDA state.
     3513 * @param   pThisCC     Pointer to the ring-3 HDA state.
     3514 * @param   pSSM        The saved state handle.
     3515 *
     3516 * @todo    r=bird: Replace by a post load callback.
     3517 */
     3518static int hdaR3LoadExecTail(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PSSMHANDLE pSSM)
    34863519{
    34873520    int rc = VINF_SUCCESS; /** @todo r=bird: Really, what's the point of this? */
     
    35233556            hdaR3StreamRegisterDMAHandlers(pThis, pStreamShared);
    35243557#endif
    3525             /** @todo r=bird: This can go wrong if the state saving happened exactly when
    3526              *        the timer expired.  See comparison in
    3527              *        hdaR3StreamTransferIsScheduled.   I've decided to just do this
    3528              *        unconditionally as that fixes the issue and simplifies the
    3529              *        hdaR3StreamMarkStarted.  Also, I don't really get why this check is
    3530              *        necessary at all. */
    3531             /*if (hdaR3StreamTransferIsScheduled(pStreamShared, PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer))) */
     3558
     3559            /* Use the LPIB to find the current scheduling position.  If this isn't
     3560               exactly on a scheduling item adjust LPIB down to the start of the
     3561               current.  This isn't entirely ideal, but it avoid the IRQ counting
     3562               issue if we round it upwards. (it is also a lot simpler) */
     3563            uint32_t uLpib = HDA_STREAM_REG(pThis, LPIB, i);
     3564            AssertLogRelMsgStmt(uLpib < pStreamShared->u32CBL, ("LPIB=%#RX32 CBL=%#RX32\n", uLpib, pStreamShared->u32CBL),
     3565                                HDA_STREAM_REG(pThis, LPIB, i) = uLpib = 0);
     3566
     3567            uint32_t off = 0;
     3568            for (uint32_t j = 0; j < pStreamShared->State.cSchedule; j++)
    35323569            {
    3533                 /* Avoid going through the timer here by calling the stream's timer function directly.
    3534                  * Should speed up starting the stream transfers. */
    3535                 uint64_t tsNow = hdaR3StreamTimerMain(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3);
    3536 
    3537                 hdaR3StreamMarkStarted(pDevIns, pThis, pStreamShared, tsNow);
     3570                AssertReturn(pStreamShared->State.aSchedule[j].cbPeriod >= 1 && pStreamShared->State.aSchedule[j].cLoops >= 1,
     3571                             pDevIns->pHlpR3->pfnSSMSetLoadError(pSSM, VERR_INTERNAL_ERROR_2, RT_SRC_POS,
     3572                                                                 "Stream #%u, sched #%u: cbPeriod=%u cLoops=%u\n",
     3573                                                                 pStreamShared->u8SD, j,
     3574                                                                 pStreamShared->State.aSchedule[j].cbPeriod,
     3575                                                                 pStreamShared->State.aSchedule[j].cLoops));
     3576                uint32_t cbCur = pStreamShared->State.aSchedule[j].cbPeriod
     3577                               * pStreamShared->State.aSchedule[j].cLoops;
     3578                if (uLpib >= off + cbCur)
     3579                    off += cbCur;
     3580                else
     3581                {
     3582                    uint32_t const offDelta = uLpib - off;
     3583                    uint32_t idxLoop = offDelta / pStreamShared->State.aSchedule[j].cbPeriod;
     3584                    uint32_t offLoop = offDelta % pStreamShared->State.aSchedule[j].cbPeriod;
     3585                    if (offLoop)
     3586                    {
     3587                        /** @todo somehow bake this into the DMA timer logic.   */
     3588                        LogFunc(("stream #%u: LPIB=%#RX32; adjusting due to scheduling clash: -%#x (j=%u idxLoop=%u cbPeriod=%#x)\n",
     3589                                 pStreamShared->u8SD, uLpib, offLoop, j, idxLoop, pStreamShared->State.aSchedule[j].cbPeriod));
     3590                        uLpib -= offLoop;
     3591                        HDA_STREAM_REG(pThis, LPIB, i) = uLpib;
     3592                    }
     3593                    pStreamShared->State.idxSchedule     = (uint16_t)j;
     3594                    pStreamShared->State.idxScheduleLoop = (uint16_t)idxLoop;
     3595                    off = UINT32_MAX;
     3596                    break;
     3597                }
    35383598            }
     3599            Assert(off == UINT32_MAX);
     3600
     3601            /* Now figure out the current BDLE and the offset within it. */
     3602            off = 0;
     3603            for (uint32_t j = 0; j < pStreamShared->State.cBdles; j++)
     3604                if (uLpib >= off + pStreamShared->State.aBdl[j].cb)
     3605                    off += pStreamShared->State.aBdl[j].cb;
     3606                else
     3607                {
     3608                    pStreamShared->State.idxCurBdle = j;
     3609                    pStreamShared->State.offCurBdle = uLpib - off;
     3610                    off = UINT32_MAX;
     3611                    break;
     3612                }
     3613            AssertReturn(off == UINT32_MAX, pDevIns->pHlpR3->pfnSSMSetLoadError(pSSM, VERR_INTERNAL_ERROR_3, RT_SRC_POS,
     3614                                                                                "Stream #%u: LPIB=%#RX32 not found in loaded BDL\n",
     3615                                                                                pStreamShared->u8SD, uLpib));
     3616
     3617            /* Avoid going through the timer here by calling the stream's timer function directly.
     3618             * Should speed up starting the stream transfers. */
     3619            uint64_t tsNow = hdaR3StreamTimerMain(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3);
     3620
     3621            hdaR3StreamMarkStarted(pDevIns, pThis, pStreamShared, tsNow);
    35393622
    35403623            /* Also keep track of the currently active streams. */
     
    35543637 * @param   pThis       Pointer to the shared HDA state.
    35553638 * @param   pThisCC     Pointer to the ring-3 HDA state.
    3556  * @param   pSSM        Pointer to SSM handle.
     3639 * @param   pSSM        The saved state handle.
    35573640 * @param   uVersion    Saved state version to load.
    35583641 */
     
    36453728            /* Note 2: The stream's saving order is/was fixed, so don't touch! */
    36463729
     3730            HDABDLELEGACY BDLE;
     3731
    36473732            /* Output */
    36483733            PHDASTREAM pStreamShared = &pThis->aStreams[4];
    36493734            rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[4], 4 /* Stream descriptor, hardcoded */);
    36503735            AssertRCReturn(rc, rc);
    3651             rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE, sizeof(pStreamShared->State.BDLE),
    3652                                         0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
     3736            RT_ZERO(BDLE);
     3737            rc = pHlp->pfnSSMGetStructEx(pSSM, &BDLE, sizeof(BDLE), 0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
    36533738            AssertRCReturn(rc, rc);
    3654             pStreamShared->State.uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;
     3739            pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */
    36553740
    36563741            /* Microphone-In */
     
    36583743            rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[2], 2 /* Stream descriptor, hardcoded */);
    36593744            AssertRCReturn(rc, rc);
    3660             rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE, sizeof(pStreamShared->State.BDLE),
    3661                                          0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
     3745            rc = pHlp->pfnSSMGetStructEx(pSSM, &BDLE, sizeof(BDLE), 0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
    36623746            AssertRCReturn(rc, rc);
    3663             pStreamShared->State.uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;
     3747            pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */
    36643748
    36653749            /* Line-In */
     
    36673751            rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[0], 0 /* Stream descriptor, hardcoded */);
    36683752            AssertRCReturn(rc, rc);
    3669             rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE, sizeof(pStreamShared->State.BDLE),
    3670                                          0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
     3753            rc = pHlp->pfnSSMGetStructEx(pSSM, &BDLE, sizeof(BDLE), 0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns);
    36713754            AssertRCReturn(rc, rc);
    3672             pStreamShared->State.uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;
     3755            pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */
    36733756            break;
    36743757        }
     
    37313814                    rc = pHlp->pfnSSMGetStructEx(pSSM, &Tmp, sizeof(Tmp), 0 /* fFlags */, g_aV5State1Fields, NULL);
    37323815                    AssertRCReturn(rc, rc);
    3733                     pStreamShared->State.uCurBDLE = Tmp.uCurBDLE;
     3816                    pStreamShared->State.idxCurBdle = (uint8_t)Tmp.uCurBDLE; /* not necessary */
    37343817
    37353818                    for (uint16_t a = 0; a < Tmp.cBLDEs; a++)
     
    37443827                        rc = pHlp->pfnSSMGetStructEx(pSSM, &Tmp, sizeof(Tmp), 0 /* fFlags */, g_aV5State2Fields, NULL);
    37453828                        AssertRCReturn(rc, rc);
    3746                         if (Tmp.u32BDLEIndex == pStreamShared->State.uCurBDLE)
    3747                         {
    3748                             pStreamShared->State.BDLE.State.cbBelowFIFOW = Tmp.cbBelowFIFOW;
    3749                             pStreamShared->State.BDLE.State.u32BufOff    = Tmp.u32BufOff;
    3750                         }
    37513829                    }
    37523830                }
     
    37573835                    AssertRCReturn(rc, rc);
    37583836
    3759                     rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.Desc, sizeof(pStreamShared->State.BDLE.Desc),
    3760                                                 0 /* fFlags */, g_aSSMBDLEDescFields6, pDevIns);
     3837                    HDABDLEDESC IgnDesc;
     3838                    rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnDesc, sizeof(IgnDesc), 0 /* fFlags */, g_aSSMBDLEDescFields6, pDevIns);
    37613839                    AssertRCReturn(rc, rc);
    37623840
    3763                     rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(HDABDLESTATE),
    3764                                                 0 /* fFlags */, g_aSSMBDLEStateFields6, NULL);
     3841                    HDABDLESTATELEGACY IgnState;
     3842                    rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnState, sizeof(IgnState), 0 /* fFlags */, g_aSSMBDLEStateFields6, NULL);
    37653843                    AssertRCReturn(rc, rc);
    37663844
     
    38073885        rc = hdaR3LoadExecLegacy(pDevIns, pThis, pThisCC, pSSM, uVersion);
    38083886        if (RT_SUCCESS(rc))
    3809             rc = hdaR3LoadExecPost(pDevIns, pThis, pThisCC);
     3887            rc = hdaR3LoadExecTail(pDevIns, pThis, pThisCC, pSSM);
    38103888        return rc;
    38113889    }
     
    38973975        /*
    38983976         * Load BDLEs (Buffer Descriptor List Entries) and DMA counters.
     3977         * Obsolete. Derived from LPID now.
    38993978         */
    3900         rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.Desc, sizeof(HDABDLEDESC),
    3901                                     0 /* fFlags */, g_aSSMBDLEDescFields7, NULL);
     3979        HDABDLEDESC IgnDesc;
     3980        rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnDesc, sizeof(IgnDesc), 0 /* fFlags */, g_aSSMBDLEDescFields7, NULL);
    39023981        AssertRCReturn(rc, rc);
    39033982
    3904         rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(HDABDLESTATE),
    3905                                     0 /* fFlags */, g_aSSMBDLEStateFields7, NULL);
     3983        HDABDLESTATELEGACY IgnState;
     3984        rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnState, sizeof(IgnState), 0 /* fFlags */, g_aSSMBDLEStateFields7, NULL);
    39063985        AssertRCReturn(rc, rc);
    39073986
    3908         Log2Func(("[SD%RU8] %R[bdle]\n", pStreamShared->u8SD, &pStreamShared->State.BDLE));
     3987        Log2Func(("[SD%RU8]\n", pStreamShared->u8SD));
    39093988
    39103989        /*
     
    39774056    } /* for cStreams */
    39784057
    3979     rc = hdaR3LoadExecPost(pDevIns, pThis, pThisCC);
     4058    rc = hdaR3LoadExecTail(pDevIns, pThis, pThisCC, pSSM);
    39804059    AssertRC(rc);
    39814060
     
    39884067*   IPRT format type handlers                                                                                                    *
    39894068*********************************************************************************************************************************/
    3990 
    3991 /**
    3992  * @callback_method_impl{FNRTSTRFORMATTYPE}
    3993  */
    3994 static DECLCALLBACK(size_t) hdaR3StrFmtBDLE(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
    3995                                             const char *pszType, void const *pvValue,
    3996                                             int cchWidth, int cchPrecision, unsigned fFlags,
    3997                                             void *pvUser)
    3998 {
    3999     RT_NOREF(pszType, cchWidth,  cchPrecision, fFlags, pvUser);
    4000     PHDABDLE pBDLE = (PHDABDLE)pvValue;
    4001     return RTStrFormat(pfnOutput,  pvArgOutput, NULL, 0,
    4002                        "BDLE(idx:%RU32, off:%RU32, fifow:%RU32, IOC:%RTbool, DMA[%RU32 bytes @ 0x%x])",
    4003                        pBDLE->State.u32BDLIndex, pBDLE->State.u32BufOff, pBDLE->State.cbBelowFIFOW,
    4004                        pBDLE->Desc.fFlags & HDA_BDLE_F_IOC, pBDLE->Desc.u32BufSize, pBDLE->Desc.u64BufAddr);
    4005 }
    40064069
    40074070/**
     
    41154178static void hdaR3DbgPrintStream(PHDASTATE pThis, PCDBGFINFOHLP pHlp, int idxStream)
    41164179{
    4117     const PHDASTREAM pStream = &pThis->aStreams[idxStream];
    41184180
    41194181    pHlp->pfnPrintf(pHlp, "Stream #%d:\n", idxStream);
     
    41224184    pHlp->pfnPrintf(pHlp, "  SD%dFIFOS: %R[sdfifos]\n", idxStream, HDA_STREAM_REG(pThis, FIFOS, idxStream));
    41234185    pHlp->pfnPrintf(pHlp, "  SD%dFIFOW: %R[sdfifow]\n", idxStream, HDA_STREAM_REG(pThis, FIFOW, idxStream));
    4124     pHlp->pfnPrintf(pHlp, "  BDLE     : %R[bdle]\n",    &pStream->State.BDLE);
    4125 }
    4126 
    4127 /** Worker for hdaR3DbgInfoBDLE. */
    4128 static void hdaR3DbgPrintBDLE(PPDMDEVINS pDevIns, PHDASTATE pThis, PCDBGFINFOHLP pHlp, int idxStream)
     4186
     4187    PHDASTREAM const pStream = &pThis->aStreams[idxStream];
     4188    pHlp->pfnPrintf(pHlp, "  Current BDLE%02u: %s%#RX64 LB %#x%s - off=%#x\n", pStream->State.idxCurBdle, "%%" /*vboxdbg phys prefix*/,
     4189                    pStream->State.aBdl[pStream->State.idxCurBdle].GCPhys, pStream->State.aBdl[pStream->State.idxCurBdle].cb,
     4190                    pStream->State.aBdl[pStream->State.idxCurBdle].fFlags ? " IOC" : "", pStream->State.offCurBdle);
     4191}
     4192
     4193/** Worker for hdaR3DbgInfoBDL. */
     4194static void hdaR3DbgPrintBDL(PPDMDEVINS pDevIns, PHDASTATE pThis, PCDBGFINFOHLP pHlp, int idxStream)
    41294195{
    41304196    const PHDASTREAM pStream = &pThis->aStreams[idxStream];
    4131 
    4132     pHlp->pfnPrintf(pHlp, "Stream #%d BDLE:\n",      idxStream);
    41334197
    41344198    uint64_t const u64BaseDMA = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, idxStream),
     
    41364200    uint16_t const u16LVI     = HDA_STREAM_REG(pThis, LVI, idxStream);
    41374201    uint32_t const u32CBL     = HDA_STREAM_REG(pThis, CBL, idxStream);
    4138 
    4139     pHlp->pfnPrintf(pHlp, "  Current: %R[bdle]\n", &pStream->State.BDLE);
    4140     pHlp->pfnPrintf(pHlp, "  Memory - %#RX64 LB %#x (LVI=%u):\n", u64BaseDMA, u16LVI * sizeof(HDABDLEDESC), u16LVI);
     4202    uint8_t const  idxCurBdle = pStream->State.idxCurBdle;
     4203    pHlp->pfnPrintf(pHlp, "Stream #%d BDL: %s%#011RX64 LB %#x (LVI=%u)\n", idxStream, "%%" /*vboxdbg phys prefix*/,
     4204                    u64BaseDMA, u16LVI * sizeof(HDABDLEDESC), u16LVI);
     4205    if (u64BaseDMA || idxCurBdle != 0 || pStream->State.aBdl[idxCurBdle].GCPhys != 0 || pStream->State.aBdl[idxCurBdle].cb != 0)
     4206        pHlp->pfnPrintf(pHlp, "  Current:     BDLE%03u: %s%#011RX64 LB %#x%s - off=%#x  LPIB=%#RX32\n",
     4207                        pStream->State.idxCurBdle, "%%" /*vboxdbg phys prefix*/,
     4208                        pStream->State.aBdl[idxCurBdle].GCPhys, pStream->State.aBdl[idxCurBdle].cb,
     4209                        pStream->State.aBdl[idxCurBdle].fFlags ? " IOC" : "", pStream->State.offCurBdle,
     4210                        HDA_STREAM_REG(pThis, LPIB, idxStream));
    41414211    if (!u64BaseDMA)
    41424212        return;
    41434213
    4144     uint32_t cbTotal = 0;
     4214    /*
     4215     * The BDL:
     4216     */
     4217    uint64_t cbTotal = 0;
    41454218    for (uint16_t i = 0; i < u16LVI + 1; i++)
    41464219    {
    4147         /** @todo shouldn't this be a PCI read?   */
    41484220        HDABDLEDESC bd = {0, 0, 0};
    4149         PDMDevHlpPhysRead(pDevIns, u64BaseDMA + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
     4221        PDMDevHlpPCIPhysRead(pDevIns, u64BaseDMA + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
    41504222
    41514223        char szFlags[64];
     
    41534225        if (bd.fFlags & ~HDA_BDLE_F_IOC)
    41544226            RTStrPrintf(szFlags, sizeof(szFlags), " !!fFlags=%#x!!\n", bd.fFlags);
    4155         pHlp->pfnPrintf(pHlp, "    %sBDLE%03u: %#RX64 LB %#x %s%s\n",
    4156                         pStream->State.BDLE.State.u32BDLIndex == i ? "=>" : "  ", i, bd.u64BufAddr, bd.u32BufSize,
    4157                         bd.fFlags & HDA_BDLE_F_IOC ? " IOC=1" : "", szFlags);
     4227        pHlp->pfnPrintf(pHlp, "    %sBDLE%03u: %s%#011RX64 LB %#06x %s%s\n", idxCurBdle == i ? "=>" : "  ", i, "%%", bd.u64BufAddr,
     4228                        bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC ? " IOC=1" : "", szFlags);
     4229
     4230        if (memcmp(&bd, &pStream->State.aBdl[i], sizeof(bd)) != 0)
     4231        {
     4232            szFlags[0] = '\0';
     4233            if (bd.fFlags & ~HDA_BDLE_F_IOC)
     4234                RTStrPrintf(szFlags, sizeof(szFlags), " !!fFlags=%#x!!\n", bd.fFlags);
     4235            pHlp->pfnPrintf(pHlp, "    !!!loaded: %s%#011RX64 LB %#06x %s%s\n", "%%", pStream->State.aBdl[i].GCPhys,
     4236                            pStream->State.aBdl[i].cb, pStream->State.aBdl[i].fFlags & HDA_BDLE_F_IOC ? " IOC=1" : "", szFlags);
     4237        }
    41584238
    41594239        cbTotal += bd.u32BufSize;
    41604240    }
    4161     pHlp->pfnPrintf(pHlp, "  Total: %RU32 bytes\n", cbTotal);
     4241    pHlp->pfnPrintf(pHlp, "  Total: %#RX64 bytes (%RU64)\n", cbTotal, cbTotal);
    41624242    if (cbTotal != u32CBL)
    4163         pHlp->pfnPrintf(pHlp, "  Warning: %RU32 bytes does not match CBL (%RU32)!\n", cbTotal, u32CBL);
    4164 
     4243        pHlp->pfnPrintf(pHlp, "  Warning: %#RX64 bytes does not match CBL (%#RX64)!\n", cbTotal, u32CBL);
     4244
     4245    /*
     4246     * The scheduling plan.
     4247     */
     4248    uint16_t const idxSchedule = pStream->State.idxSchedule;
     4249    pHlp->pfnPrintf(pHlp, "  Scheduling: %u items, %u prologue.  Current: %u, loop %u.\n",  pStream->State.cSchedule,
     4250                    pStream->State.cSchedulePrologue, idxSchedule, pStream->State.idxScheduleLoop);
     4251    for (uint16_t i = 0; i < pStream->State.cSchedule; i++)
     4252        pHlp->pfnPrintf(pHlp, "    %s#%02u: %#x bytes, %u loop%s, %RU32 ticks. BDLE%u thru BDLE%u\n",
     4253                        i == idxSchedule ? "=>" : "  ", i,
     4254                        pStream->State.aSchedule[i].cbPeriod, pStream->State.aSchedule[i].cLoops,
     4255                        pStream->State.aSchedule[i].cLoops == 1 ? "" : "s",
     4256                        pStream->State.aSchedule[i].cPeriodTicks, pStream->State.aSchedule[i].idxFirst,
     4257                        pStream->State.aSchedule[i].idxFirst + pStream->State.aSchedule[i].cEntries - 1);
     4258
     4259    /*
     4260     * DMA counters:
     4261     */
    41654262    uint64_t const uDPBase = pThis->u64DPBase & DPBASE_ADDR_MASK;
    4166     pHlp->pfnPrintf(pHlp, "  DMA counters %#RX64 LB %#x, %s:\n", uDPBase, u16LVI * 2 * sizeof(uint32_t),
     4263    pHlp->pfnPrintf(pHlp, "  DMA counters %#011RX64 LB %#x, %s:\n", uDPBase, u16LVI * 2 * sizeof(uint32_t),
    41674264                    pThis->fDMAPosition ? "enabled" : "disabled");
    41684265    if (uDPBase)
    41694266        for (uint16_t i = 0; i < u16LVI + 1; i++)
    41704267        {
    4171             /** @todo shouldn't this be a PCI read? */
    41724268            struct
    41734269            {
     
    41754271                uint32_t uReserved;
    41764272            } Buf = { 0 , 0 };
    4177             PDMDevHlpPhysRead(pDevIns, uDPBase + i * sizeof(Buf), &Buf, sizeof(Buf));
     4273            PDMDevHlpPCIPhysRead(pDevIns, uDPBase + i * sizeof(Buf), &Buf, sizeof(Buf));
    41784274
    41794275            char szReserved[64];
     
    41874283}
    41884284
    4189 /** Used by hdaR3DbgInfoStream and hdaR3DbgInfoBDLE. */
     4285/** Used by hdaR3DbgInfoStream and hdaR3DbgInfoBDL. */
    41904286static int hdaR3DbgLookupStrmIdx(PCDBGFINFOHLP pHlp, const char *pszArgs)
    41914287{
     
    42184314 * @callback_method_impl{FNDBGFHANDLERDEV, hdabdle}
    42194315 */
    4220 static DECLCALLBACK(void) hdaR3DbgInfoBDLE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
     4316static DECLCALLBACK(void) hdaR3DbgInfoBDL(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
    42214317{
    42224318    PHDASTATE   pThis     = PDMDEVINS_2_DATA(pDevIns, PHDASTATE);
    42234319    int         idxStream = hdaR3DbgLookupStrmIdx(pHlp, pszArgs);
    42244320    if (idxStream != -1)
    4225         hdaR3DbgPrintBDLE(pDevIns, pThis, pHlp, idxStream);
     4321        hdaR3DbgPrintBDL(pDevIns, pThis, pHlp, idxStream);
    42264322    else
    42274323        for (idxStream = 0; idxStream < HDA_MAX_STREAMS; ++idxStream)
    4228             hdaR3DbgPrintBDLE(pDevIns, pThis, pHlp, idxStream);
     4324            hdaR3DbgPrintBDL(pDevIns, pThis, pHlp, idxStream);
    42294325}
    42304326
     
    50775173     */
    50785174    PDMDevHlpDBGFInfoRegister(pDevIns, "hda",         "HDA registers. (hda [register case-insensitive])", hdaR3DbgInfo);
    5079     PDMDevHlpDBGFInfoRegister(pDevIns, "hdabdle",
    5080                               "HDA buffer descriptor list (BDLE) and DMA stream positions. (hdabdle [stream number])",
    5081                               hdaR3DbgInfoBDLE);
     5175    PDMDevHlpDBGFInfoRegister(pDevIns, "hdabdl",
     5176                              "HDA buffer descriptor list (BDL) and DMA stream positions. (hdabdl [stream number])",
     5177                              hdaR3DbgInfoBDL);
    50825178    PDMDevHlpDBGFInfoRegister(pDevIns, "hdastream",   "HDA stream info. (hdastream [stream number])",    hdaR3DbgInfoStream);
    50835179    PDMDevHlpDBGFInfoRegister(pDevIns, "hdcnodes",    "HDA codec nodes.",                                hdaR3DbgInfoCodecNodes);
     
    50855181    PDMDevHlpDBGFInfoRegister(pDevIns, "hdamixer",    "HDA mixer state.",                                hdaR3DbgInfoMixer);
    50865182
    5087     rc = RTStrFormatTypeRegister("bdle",    hdaR3StrFmtBDLE,    NULL);
    5088     AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);
    50895183    rc = RTStrFormatTypeRegister("sdctl",   hdaR3StrFmtSDCTL,   NULL);
    50905184    AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);
    50915185    rc = RTStrFormatTypeRegister("sdsts",   hdaR3StrFmtSDSTS,   NULL);
    50925186    AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);
     5187    /** @todo the next two are rather pointless.   */
    50935188    rc = RTStrFormatTypeRegister("sdfifos", hdaR3StrFmtSDFIFOS, NULL);
    50945189    AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);
  • trunk/src/VBox/Devices/Audio/DevHDACommon.cpp

    r88080 r88137  
    365365}
    366366
    367 /*
    368  * Reads DMA data from a given HDA output stream.
    369  *
    370  * @return  IPRT status code.
    371  * @param   pDevIns             The device instance.
    372  * @param   pThis               The shared HDA device state (for stats).
    373  * @param   pStreamShared       HDA output stream to read DMA data from - shared bits.
    374  * @param   pStreamR3           HDA output stream to read DMA data from - shared ring-3.
    375  * @param   pvBuf               Where to store the read data.
    376  * @param   cbBuf               How much to read in bytes.
    377  * @param   pcbRead             Returns read bytes from DMA. Optional.
    378  */
    379 int hdaR3DMARead(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3,
    380                  void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
    381 {
    382     RT_NOREF(pThis);
    383     PHDABDLE pBDLE = &pStreamShared->State.BDLE;
    384 
    385     int rc = VINF_SUCCESS;
    386 
    387     uint32_t cbReadTotal = 0;
    388     uint32_t cbLeft      = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
    389 
    390 # ifdef HDA_DEBUG_SILENCE
    391     uint64_t   csSilence = 0;
    392 
    393     pStreamR3->Dbg.cSilenceThreshold = 100;
    394     pStreamR3->Dbg.cbSilenceReadMin  = _1M;
    395 # endif
    396 
    397     RTGCPHYS GCPhysChunk = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
    398 
    399     while (cbLeft)
    400     {
    401         uint32_t cbChunk = RT_MIN(cbLeft, pStreamShared->u8FIFOS);
    402 
    403         rc = PDMDevHlpPhysRead(pDevIns, GCPhysChunk, (uint8_t *)pvBuf + cbReadTotal, cbChunk);
    404         AssertRCBreak(rc);
    405 
    406 # ifdef HDA_DEBUG_SILENCE
    407         uint16_t *pu16Buf = (uint16_t *)pvBuf;
    408         for (size_t i = 0; i < cbChunk / sizeof(uint16_t); i++)
    409         {
    410             if (*pu16Buf == 0)
    411                 csSilence++;
    412             else
    413                 break;
    414             pu16Buf++;
    415         }
    416 # endif
    417         if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
    418         { /* likely */ }
    419         else
    420             DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbReadTotal, cbChunk, 0 /* fFlags */);
    421 
    422         STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
    423 
    424         /* advance */
    425         Assert(cbLeft    >= cbChunk);
    426         GCPhysChunk       = (GCPhysChunk + cbChunk) % pBDLE->Desc.u32BufSize;
    427         cbReadTotal      += cbChunk;
    428         cbLeft           -= cbChunk;
    429     }
    430 
    431 # ifdef HDA_DEBUG_SILENCE
    432     if (csSilence)
    433         pStreamR3->Dbg.csSilence += csSilence;
    434 
    435     if (   csSilence == 0
    436         && pStreamR3->Dbg.csSilence   >  pStreamR3->Dbg.cSilenceThreshold
    437         && pStreamR3->Dbg.cbReadTotal >= pStreamR3->Dbg.cbSilenceReadMin)
    438     {
    439         LogFunc(("Silent block detected: %RU64 audio samples\n", pStreamR3->Dbg.csSilence));
    440         pStreamR3->Dbg.csSilence = 0;
    441     }
    442 # endif
    443 
    444     if (RT_SUCCESS(rc))
    445     {
    446         if (pcbRead)
    447             *pcbRead = cbReadTotal;
    448     }
    449 
    450     return rc;
    451 }
    452 
    453 /**
    454  * Writes audio data from an HDA input stream's FIFO to its associated DMA area.
    455  *
    456  * @return  IPRT status code.
    457  * @param   pDevIns             The device instance.
    458  * @param   pThis               The shared HDA device state (for stats).
    459  * @param   pStreamShared       HDA input stream to write audio data to - shared.
    460  * @param   pStreamR3           HDA input stream to write audio data to - ring-3.
    461  * @param   pvBuf               Data to write.
    462  * @param   cbBuf               How much (in bytes) to write.
    463  * @param   pcbWritten          Returns written bytes on success. Optional.
    464  */
    465 int hdaR3DMAWrite(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3,
    466                   const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    467 {
    468     RT_NOREF(pThis);
    469     PHDABDLE    pBDLE          = &pStreamShared->State.BDLE;
    470     int         rc             = VINF_SUCCESS;
    471     uint32_t    cbWrittenTotal = 0;
    472     uint32_t    cbLeft         = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
    473     RTGCPHYS    GCPhysChunk    = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
    474     while (cbLeft)
    475     {
    476         uint32_t cbChunk = RT_MIN(cbLeft, pStreamShared->u8FIFOS);
    477 
    478         /* Sanity checks. */
    479         Assert(cbChunk <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
    480 
    481         if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
    482         { /* likely */ }
    483         else
    484             DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk, 0 /* fFlags */);
    485 
    486         rc = PDMDevHlpPCIPhysWrite(pDevIns, GCPhysChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
    487         AssertRCReturn(rc, rc);
    488 
    489         STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
    490 
    491         /* advance */
    492         Assert(cbLeft  >= cbChunk);
    493         cbWrittenTotal += (uint32_t)cbChunk;
    494         GCPhysChunk     = (GCPhysChunk + cbChunk) % pBDLE->Desc.u32BufSize;
    495         cbLeft         -= (uint32_t)cbChunk;
    496     }
    497 
    498     if (RT_SUCCESS(rc))
    499     {
    500         if (pcbWritten)
    501             *pcbWritten = cbWrittenTotal;
    502     }
    503     else
    504         LogFunc(("Failed with %Rrc\n", rc));
    505 
    506     return rc;
    507 }
    508 
    509367#endif /* IN_RING3 */
    510368
     
    673531# endif /* LOG_ENABLED */
    674532
    675 /**
    676  * Fetches a Bundle Descriptor List Entry (BDLE) from the DMA engine.
    677  *
    678  * @param   pDevIns             The device instance.
    679  * @param   pBDLE               Where to store the fetched result.
    680  * @param   u64BaseDMA          Address base of DMA engine to use.
    681  * @param   u16Entry            BDLE entry to fetch.
    682  */
    683 int hdaR3BDLEFetch(PPDMDEVINS pDevIns, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry)
    684 {
    685     AssertPtrReturn(pBDLE,   VERR_INVALID_POINTER);
    686     AssertReturn(u64BaseDMA, VERR_INVALID_PARAMETER);
    687 
    688     if (!u64BaseDMA)
    689     {
    690         LogRel2(("HDA: Unable to fetch BDLE #%RU16 - no base DMA address set (yet)\n", u16Entry));
    691         return VERR_NOT_FOUND;
    692     }
    693     /** @todo Compare u16Entry with LVI. */
    694 
    695     int rc = PDMDevHlpPhysRead(pDevIns, u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)),
    696                                &pBDLE->Desc, sizeof(pBDLE->Desc));
    697 
    698     if (RT_SUCCESS(rc))
    699     {
    700         /* Reset internal state. */
    701         RT_ZERO(pBDLE->State);
    702         pBDLE->State.u32BDLIndex = u16Entry;
    703     }
    704 
    705     Log3Func(("Entry #%d @ 0x%x: %R[bdle], rc=%Rrc\n", u16Entry, u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)), pBDLE, rc));
    706 
    707 
    708     return VINF_SUCCESS;
    709 }
    710 
    711 /**
    712  * Tells whether a given BDLE is complete or not.
    713  *
    714  * @return  true if BDLE is complete, false if not.
    715  * @param   pBDLE               BDLE to retrieve status for.
    716  */
    717 bool hdaR3BDLEIsComplete(PHDABDLE pBDLE)
    718 {
    719     bool fIsComplete = false;
    720 
    721     if (   !pBDLE->Desc.u32BufSize /* There can be BDLEs with 0 size. */
    722         || (pBDLE->State.u32BufOff >= pBDLE->Desc.u32BufSize))
    723     {
    724         Assert(pBDLE->State.u32BufOff == pBDLE->Desc.u32BufSize);
    725         fIsComplete = true;
    726     }
    727 
    728     Log3Func(("%R[bdle] => %s\n", pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
    729 
    730     return fIsComplete;
    731 }
    732 
    733 /**
    734  * Tells whether a given BDLE needs an interrupt or not.
    735  *
    736  * @return  true if BDLE needs an interrupt, false if not.
    737  * @param   pBDLE               BDLE to retrieve status for.
    738  */
    739 bool hdaR3BDLENeedsInterrupt(PHDABDLE pBDLE)
    740 {
    741     return (pBDLE->Desc.fFlags & HDA_BDLE_F_IOC);
    742 }
    743 
    744533#endif /* IN_RING3 */
  • trunk/src/VBox/Devices/Audio/DevHDACommon.h

    r88080 r88137  
    547547
    548548/**
    549  * Internal state of a Buffer Descriptor List Entry (BDLE),
    550  * needed to keep track of the data needed for the actual device
    551  * emulation.
    552  */
    553 typedef struct HDABDLESTATE
    554 {
    555     /** Own index within the BDL (Buffer Descriptor List). */
    556     uint32_t     u32BDLIndex;
    557     /** Number of bytes below the stream's FIFO watermark (SDFIFOW).
    558      *  Used to check if we need fill up the FIFO again. */
    559     uint32_t     cbBelowFIFOW;
    560     /** Current offset in DMA buffer (in bytes).*/
    561     uint32_t     u32BufOff;
    562     uint32_t     Padding;
    563 } HDABDLESTATE, *PHDABDLESTATE;
    564 
    565 /**
    566549 * BDL description structure.
    567550 * Do not touch this, as this must match to the HDA specs.
     
    582565AssertCompileSize(HDABDLEDESC, 16); /* Always 16 byte. Also must be aligned on 128-byte boundary. */
    583566
    584 /**
    585  * Buffer Descriptor List Entry (BDLE) (3.6.3).
    586  */
    587 typedef struct HDABDLE
    588 {
    589     /** The actual BDL description. */
    590     HDABDLEDESC  Desc;
    591     /** Internal state of this BDLE.
    592      *  Not part of the actual BDLE registers. */
    593     HDABDLESTATE State;
    594 } HDABDLE;
    595 AssertCompileSizeAlignment(HDABDLE, 8);
    596 /** Pointer to a buffer descriptor list entry (BDLE). */
    597 typedef HDABDLE *PHDABDLE;
    598567
    599568/** @name Object lookup functions.
     
    638607/** @} */
    639608
    640 /** @name DMA utility functions.
    641  * @{
    642  */
    643 #ifdef IN_RING3
    644 int           hdaR3DMARead(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3,
    645                            void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead);
    646 int           hdaR3DMAWrite(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3,
    647                             const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten);
    648 #endif
    649 /** @} */
    650 
    651609/** @name Register functions.
    652610 * @{
     
    665623void          hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE);
    666624# endif
    667 int           hdaR3BDLEFetch(PPDMDEVINS pDevIns, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry);
    668 bool          hdaR3BDLEIsComplete(PHDABDLE pBDLE);
    669 bool          hdaR3BDLENeedsInterrupt(PHDABDLE pBDLE);
    670625#endif /* IN_RING3 */
    671626/** @} */
  • trunk/src/VBox/Devices/Audio/HDAStream.cpp

    r88113 r88137  
    221221    LogFlowFuncLeave();
    222222}
     223
     224
     225/**
     226 * Appends a item to the scheduler.
     227 *
     228 * @returns VBox status code.
     229 * @param   pStreamShared   The stream which scheduler should be modified.
     230 * @param   cbCur           The period length in guest bytes.
     231 * @param   cbMaxPeriod     The max period in guest bytes.
     232 * @param   idxLastBdle     The last BDLE in the period.
     233 * @param   pHostProps      The host PCM properties.
     234 * @param   pGuestProps     The guest PCM properties.
     235 * @param   pcbBorrow       Where to account for bytes borrowed across buffers
     236 *                          to align scheduling items on frame boundraries.
     237 */
     238static int hdaR3StreamAddScheduleItem(PHDASTREAM pStreamShared, uint32_t cbCur, uint32_t cbMaxPeriod, uint32_t idxLastBdle,
     239                                      PCPDMAUDIOPCMPROPS pHostProps, PCPDMAUDIOPCMPROPS pGuestProps, uint32_t *pcbBorrow)
     240{
     241    /* Check that we've got room (shouldn't ever be a problem). */
     242    size_t idx = pStreamShared->State.cSchedule;
     243    AssertLogRelReturn(idx + 1 < RT_ELEMENTS(pStreamShared->State.aSchedule), VERR_INTERNAL_ERROR_5);
     244
     245    /* Figure out the BDLE range for this period. */
     246    uint32_t const idxFirstBdle = idx == 0 ? 0
     247                                :   pStreamShared->State.aSchedule[idx - 1].idxFirst
     248                                  + pStreamShared->State.aSchedule[idx - 1].cEntries;
     249
     250    pStreamShared->State.aSchedule[idx].idxFirst = (uint8_t)idxFirstBdle;
     251    pStreamShared->State.aSchedule[idx].cEntries = idxLastBdle >= idxFirstBdle
     252                                                 ? idxLastBdle - idxFirstBdle + 1
     253                                                 : pStreamShared->State.cBdles - idxFirstBdle + idxLastBdle + 1;
     254
     255    /* Deal with borrowing due to unaligned IOC buffers. */
     256    uint32_t const cbBorrowed = *pcbBorrow;
     257    if (cbBorrowed < cbCur)
     258        cbCur -= cbBorrowed;
     259    else
     260    {
     261        /* Note. We can probably gloss over this, but it's not a situation a sane guest would put us, so don't bother for now. */
     262        ASSERT_GUEST_MSG_FAILED(("#%u: cbBorrow=%#x cbCur=%#x BDLE[%u..%u]\n",
     263                                 pStreamShared->u8SD, cbBorrowed, cbCur, idxFirstBdle, idxLastBdle));
     264        LogRelMax(32, ("HDA: Stream #%u has a scheduling error: cbBorrow=%#x cbCur=%#x BDLE[%u..%u]\n",
     265                       pStreamShared->u8SD, cbBorrowed, cbCur, idxFirstBdle, idxLastBdle));
     266        return VERR_OUT_OF_RANGE;
     267    }
     268
     269    uint32_t cbCurAligned = PDMAudioPropsRoundUpBytesToFrame(pGuestProps, cbCur);
     270    *pcbBorrow = cbCurAligned - cbCur;
     271
     272    /* Do we need to split up the period? */
     273    if (cbCurAligned <= cbMaxPeriod)
     274    {
     275        uint32_t cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbCurAligned));
     276        pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
     277        pStreamShared->State.aSchedule[idx].cLoops   = 1;
     278    }
     279    else
     280    {
     281        /* Reduce till we've below the threshold. */
     282        uint32_t cbLoop = cbCurAligned;
     283        do
     284            cbLoop = cbCurAligned / 2;
     285        while (cbLoop > cbMaxPeriod);
     286        cbLoop = PDMAudioPropsRoundUpBytesToFrame(pGuestProps, cbLoop);
     287
     288        /* Complete the scheduling item. */
     289        uint32_t cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbLoop));
     290        pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
     291        pStreamShared->State.aSchedule[idx].cLoops   = cbCurAligned / cbLoop;
     292
     293        /* If there is a remainder, add it as a separate entry (this is
     294           why the schedule must be more than twice the size of the BDL).*/
     295        cbCurAligned %= cbLoop;
     296        if (cbCurAligned)
     297        {
     298            pStreamShared->State.aSchedule[idx + 1] = pStreamShared->State.aSchedule[idx];
     299            idx++;
     300            cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbCurAligned));
     301            pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
     302            pStreamShared->State.aSchedule[idx].cLoops   = 1;
     303        }
     304    }
     305
     306    /* Done. */
     307    pStreamShared->State.cSchedule = (uint16_t)(idx + 1);
     308
     309    return VINF_SUCCESS;
     310}
     311
     312/**
     313 * Creates the DMA timer schedule for the stream
     314 *
     315 * This is called from the stream setup code.
     316 *
     317 * @returns VBox status code.
     318 * @param   pStreamShared       The stream to create a schedule for.  The BDL
     319 *                              must be loaded.
     320 * @param   cSegments           Number of BDL segments.
     321 * @param   cBufferIrqs         Number of the BDLEs with IOC=1.
     322 * @param   cbTotal             The total BDL length in guest bytes.
     323 * @param   cbMaxPeriod         Max period in guest bytes.   This is in case the
     324 *                              guest want to play the whole "Der Ring des
     325 *                              Nibelungen" cycle in one go.
     326 * @param   cTimerTicksPerSec   The DMA timer frequency.
     327 * @param   pHostProps          The host PCM properties.
     328 * @param   pGuestProps         The guest PCM properties.
     329 */
     330static int hdaR3StreamCreateSchedule(PHDASTREAM pStreamShared, uint32_t cSegments, uint32_t cBufferIrqs, uint32_t cbTotal,
     331                                     uint32_t cbMaxPeriod, uint64_t cTimerTicksPerSec,
     332                                     PCPDMAUDIOPCMPROPS pHostProps, PCPDMAUDIOPCMPROPS pGuestProps)
     333{
     334    int rc;
     335
     336    /*
     337     * Reset scheduling state.
     338     */
     339    RT_ZERO(pStreamShared->State.aSchedule);
     340    pStreamShared->State.cSchedule         = 0;
     341    pStreamShared->State.cSchedulePrologue = 0;
     342    pStreamShared->State.idxSchedule       = 0;
     343    pStreamShared->State.idxScheduleLoop   = 0;
     344
     345    /*
     346     * Do the basic schedule compilation.
     347     */
     348    uint32_t cPotentialPrologue = 0;
     349    uint32_t cbBorrow           = 0;
     350    uint32_t cbCur              = 0;
     351    pStreamShared->State.aSchedule[0].idxFirst = 0;
     352    for (uint32_t i = 0; i < cSegments; i++)
     353    {
     354        cbCur += pStreamShared->State.aBdl[i].cb;
     355        if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
     356        {
     357            rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, i, pHostProps, pGuestProps, &cbBorrow);
     358            ASSERT_GUEST_RC_RETURN(rc, rc);
     359
     360            if (cPotentialPrologue == 0)
     361                cPotentialPrologue = pStreamShared->State.cSchedule;
     362            cbCur = 0;
     363        }
     364    }
     365    AssertLogRelMsgReturn(cbBorrow == 0, ("HDA: Internal scheduling error on stream #%u: cbBorrow=%#x cbTotal=%#x cbCur=%#x\n",
     366                                          pStreamShared->u8SD, cbBorrow, cbTotal, cbCur),
     367                          VERR_INTERNAL_ERROR_3);
     368
     369    /*
     370     * Deal with any loose ends.
     371     */
     372    if (cbCur && cBufferIrqs == 0)
     373    {
     374        /* No IOC. Split the period in two. */
     375        Assert(cbCur == cbTotal);
     376        cbCur = PDMAudioPropsFloorBytesToFrame(pGuestProps, cbCur / 2);
     377        rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, cSegments, pHostProps, pGuestProps, &cbBorrow);
     378        ASSERT_GUEST_RC_RETURN(rc, rc);
     379
     380        rc = hdaR3StreamAddScheduleItem(pStreamShared, cbTotal - cbCur, cbMaxPeriod, cSegments,
     381                                        pHostProps, pGuestProps, &cbBorrow);
     382        ASSERT_GUEST_RC_RETURN(rc, rc);
     383        Assert(cbBorrow == 0);
     384    }
     385    else if (cbCur)
     386    {
     387        /* The last BDLE didn't have IOC set, so we must continue processing
     388           from the start till we hit one that has.  */
     389        uint32_t i;
     390        for (i = 0; i < cSegments; i++)
     391        {
     392            cbCur += pStreamShared->State.aBdl[i].cb;
     393            if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
     394                break;
     395        }
     396        rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, i, pHostProps, pGuestProps, &cbBorrow);
     397        ASSERT_GUEST_RC_RETURN(rc, rc);
     398
     399        /* The initial scheduling items covering the wrap around area are
     400           considered a prologue and must not repeated later. */
     401        Assert(cPotentialPrologue);
     402        pStreamShared->State.cSchedulePrologue = (uint8_t)cPotentialPrologue;
     403    }
     404
     405    /*
     406     * If there is just one BDLE with IOC set, we have to make sure
     407     * we've got at least two periods scheduled, otherwise there is
     408     * a very good chance the guest will overwrite the start of the
     409     * buffer before we ever get around to reading it.
     410     */
     411    if (cBufferIrqs == 1)
     412    {
     413        uint32_t i = pStreamShared->State.cSchedulePrologue;
     414        Assert(i < pStreamShared->State.cSchedule);
     415        if (   i + 1 == pStreamShared->State.cSchedule
     416            && pStreamShared->State.aSchedule[i].cLoops == 1)
     417        {
     418            uint32_t const cbFirstHalf = PDMAudioPropsFloorBytesToFrame(pHostProps, pStreamShared->State.aSchedule[i].cbPeriod / 2);
     419            uint32_t const cbOtherHalf = pStreamShared->State.aSchedule[i].cbPeriod - cbFirstHalf;
     420            pStreamShared->State.aSchedule[i].cbPeriod = cbFirstHalf;
     421            if (cbFirstHalf == cbOtherHalf)
     422                pStreamShared->State.aSchedule[i].cLoops = 2;
     423            else
     424            {
     425                pStreamShared->State.aSchedule[i + 1] = pStreamShared->State.aSchedule[i];
     426                pStreamShared->State.aSchedule[i].cbPeriod = cbOtherHalf;
     427                pStreamShared->State.cSchedule++;
     428            }
     429        }
     430    }
     431
     432    /*
     433     * Go over the schduling entries and calculate the timer ticks for each period.
     434     */
     435    LogRel2(("HDA: Stream #%u schedule: %u items, %u prologue\n",
     436             pStreamShared->u8SD, pStreamShared->State.cSchedule, pStreamShared->State.cSchedulePrologue));
     437    uint64_t const cbHostPerSec = PDMAudioPropsFramesToBytes(pHostProps, pHostProps->uHz);
     438    for (uint32_t i = 0; i < pStreamShared->State.cSchedule; i++)
     439    {
     440        uint64_t const cTicks = ASMMultU64ByU32DivByU32(cTimerTicksPerSec, pStreamShared->State.aSchedule[i].cbPeriod,
     441                                                        cbHostPerSec);
     442        AssertLogRelMsgReturn((uint32_t)cTicks == cTicks, ("cTicks=%RU64 (%#RX64)\n", cTicks, cTicks), VERR_INTERNAL_ERROR_4);
     443        pStreamShared->State.aSchedule[i].cPeriodTicks = RT_MAX((uint32_t)cTicks, 16);
     444        LogRel2(("HDA:  #%u: %u ticks / %u bytes, %u loops, BDLE%u L %u\n", i, pStreamShared->State.aSchedule[i].cPeriodTicks,
     445                 pStreamShared->State.aSchedule[i].cbPeriod, pStreamShared->State.aSchedule[i].cLoops,
     446                 pStreamShared->State.aSchedule[i].idxFirst, pStreamShared->State.aSchedule[i].cEntries));
     447    }
     448
     449    return VINF_SUCCESS;
     450}
     451
    223452
    224453/**
     
    346575             PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->u32CBL)));
    347576
    348     /* Figure out how many transfer fragments we're going to use for this stream. */
    349     uint32_t cTransferFragments = pStreamShared->u16LVI + 1;
     577
     578    /*
     579     * Load the buffer descriptor list.
     580     *
     581     * Section 3.6.2 states that "the BDL should not be modified unless the RUN
     582     * bit is 0", so it should be within the specs to read it once here and not
     583     * re-read any BDLEs later.
     584     */
     585    /* Reset BDL state. */
     586    RT_ZERO(pStreamShared->State.aBdl);
     587    pStreamShared->State.offCurBdle = 0;
     588    pStreamShared->State.idxCurBdle = 0;
     589
     590    uint32_t /*const*/ cTransferFragments = (pStreamShared->u16LVI & 0xff) + 1;
    350591    if (cTransferFragments <= 1)
    351         LogRel(("HDA: Warning: Stream #%RU8 transfer fragments (%RU16) invalid -- buggy guest audio driver!\n",
    352                 uSD, pStreamShared->u16LVI));
     592        LogRel(("HDA: Warning: Stream #%RU8 transfer buffer count invalid: (%RU16)! Buggy guest audio driver!\n", uSD, pStreamShared->u16LVI));
     593    AssertLogRelReturn(cTransferFragments <= RT_ELEMENTS(pStreamShared->State.aBdl), VERR_INTERNAL_ERROR_5);
     594    pStreamShared->State.cBdles = cTransferFragments;
     595
     596    /* Load them. */
     597    rc = PDMDevHlpPCIPhysRead(pDevIns, u64BDLBase, pStreamShared->State.aBdl,
     598                              sizeof(pStreamShared->State.aBdl[0]) * cTransferFragments);
     599    AssertRC(rc);
     600
     601    /* Check what we just loaded.  Refuse overly large buffer lists. */
     602    uint64_t cbTotal     = 0;
     603    uint32_t cBufferIrqs = 0;
     604    for (uint32_t i = 0; i < cTransferFragments; i++)
     605    {
     606        if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
     607            cBufferIrqs++;
     608        cbTotal += pStreamShared->State.aBdl[i].cb;
     609    }
     610    ASSERT_GUEST_STMT_RETURN(cbTotal < _2G,
     611                             LogRelMax(32, ("HDA: Error: Stream #%u is configured with an insane amount of buffer space - refusing do work with it: %RU64 (%#RX64) bytes.\n",
     612                                            uSD, cbTotal, cbTotal)),
     613                             VERR_NOT_SUPPORTED);
     614    ASSERT_GUEST_STMT_RETURN(cbTotal == u32CBL,
     615                             LogRelMax(32, ("HDA: Warning: Stream #%u has a mismatch between CBL and configured buffer space: %RU32 (%#RX32) vs %RU64 (%#RX64)\n",
     616                                            uSD, u32CBL, u32CBL, cbTotal, cbTotal)),
     617                             VERR_NOT_SUPPORTED);
    353618
    354619    /*
    355      * Handle the stream's position adjustment.
    356      */
    357     uint32_t cfPosAdjust = 0;
    358 
    359     LogFunc(("[SD%RU8] fPosAdjustEnabled=%RTbool, cPosAdjustFrames=%RU16\n",
    360              uSD, pThis->fPosAdjustEnabled, pThis->cPosAdjustFrames));
    361 
    362     if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
    363     {
    364         HDABDLE BDLE;
    365         RT_ZERO(BDLE);
    366 
    367         int rc2 = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, 0 /* Entry */);
    368         AssertRC(rc2);
    369 
    370         /* Note: Do *not* check if this BDLE aligns to the stream's frame size.
    371          *       It can happen that this isn't the case on some guests, e.g.
    372          *       on Windows with a 5.1 speaker setup.
    373          *
    374          *       The only thing which counts is that the stream's CBL value
    375          *       properly aligns to the stream's frame size.
    376          */
    377 
    378         /* If no custom set position adjustment is set, apply some
    379          * simple heuristics to detect the appropriate position adjustment. */
    380         if (   !pThis->cPosAdjustFrames
    381         /* Position adjustmenet buffer *must* have the IOC bit set! */
    382             && hdaR3BDLENeedsInterrupt(&BDLE))
    383         {
    384             /** @todo Implement / use a (dynamic) table once this gets more complicated. */
    385 #ifdef VBOX_WITH_INTEL_HDA
    386             /* Intel ICH / PCH: 1 frame. */
    387             if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbGuestFrame))
    388             {
    389                 cfPosAdjust = 1;
    390             }
    391             /* Intel Baytrail / Braswell: 32 frames. */
    392             else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbGuestFrame))
    393             {
    394                 cfPosAdjust = 32;
    395             }
    396 #endif
    397         }
    398         else /* Go with the set default. */
    399             cfPosAdjust = pThis->cPosAdjustFrames;
    400 
    401         if (cfPosAdjust)
    402         {
    403             /* Also adjust the number of fragments, as the position adjustment buffer
    404              * does not count as an own fragment as such.
    405              *
    406              * This e.g. can happen on (newer) Ubuntu guests which use
    407              * 4 (IOC) + 4408 (IOC) + 4408 (IOC) + 4408 (IOC) + 4404 (= 17632) bytes,
    408              * where the first buffer (4) is used as position adjustment.
    409              *
    410              * Only skip a fragment if the whole buffer fragment is used for
    411              * position adjustment.
    412              */
    413             if ((cfPosAdjust * pStreamR3->State.Mapping.cbGuestFrame) == BDLE.Desc.u32BufSize)
    414                 cTransferFragments--;
    415 
    416             /* Initialize position adjustment counter. */
    417             pStreamShared->State.cfPosAdjustDefault = cfPosAdjust;
    418             pStreamShared->State.cfPosAdjustLeft    = cfPosAdjust;
    419             LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", uSD, cfPosAdjust));
    420         }
    421     }
    422 
    423     Log3Func(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU32\n", uSD, cfPosAdjust, cTransferFragments));
     620     * Create a DMA timer schedule.
     621     */
     622     rc = hdaR3StreamCreateSchedule(pStreamShared, cTransferFragments, cBufferIrqs, (uint32_t)cbTotal,
     623                                   PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.GuestProps, 100 /** @todo make configurable */),
     624                                   PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer),
     625                                   &HostProps, &pStreamR3->State.Mapping.GuestProps);
     626    if (RT_FAILURE(rc))
     627        return rc;
     628
     629    pStreamShared->State.cbTransferSize = pStreamShared->State.aSchedule[0].cbPeriod;
     630
     631    /*
     632     * Calculate the transfer Hz for use in the circular buffer calculation.
     633     */
     634    uint32_t cbMaxPeriod = 0;
     635    uint32_t cbMinPeriod = UINT32_MAX;
     636    uint32_t cPeriods    = 0;
     637    for (uint32_t i = 0; i < pStreamShared->State.cSchedule; i++)
     638    {
     639        uint32_t cbPeriod = pStreamShared->State.aSchedule[i].cbPeriod;
     640        cbMaxPeriod = RT_MAX(cbMaxPeriod, cbPeriod);
     641        cbMinPeriod = RT_MIN(cbMinPeriod, cbPeriod);
     642        cPeriods   += pStreamShared->State.aSchedule[i].cLoops;
     643    }
     644    uint64_t const cbTransferPerSec  = RT_MAX(PDMAudioPropsFramesToBytes(&pCfg->Props, pCfg->Props.uHz),
     645                                              4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */);
     646    unsigned uTransferHz = cbTransferPerSec * 1000 / cbMaxPeriod;
     647    LogRel2(("HDA: Stream #%RU8 needs a %u.%03u Hz timer rate (period: %u..%u host bytes)\n",
     648             uSD, uTransferHz / 1000, uTransferHz % 1000, cbMinPeriod, cbMaxPeriod));
     649    uTransferHz /= 1000;
     650
     651    if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */
     652        LogRelMax(32, ("HDA: Warning: Calculated transfer Hz rate for stream #%RU8 looks incorrect (%u), please re-run with audio debug mode and report a bug\n",
     653                       uSD, uTransferHz));
     654
     655    pStreamShared->State.cbAvgTransfer = (uint32_t)(cbTotal + cPeriods - 1) / cPeriods;
    424656
    425657    /*
     
    429661    /* Assign the global device rate to the stream I/O timer as default. */
    430662    pStreamShared->State.uTimerIoHz = pThis->uTimerHz;
    431 
    432     /*
    433      * Determine the transfer Hz the guest OS expects data transfer at.
    434      *
    435      * Guests also expect a very extact DMA timing for reading / writing audio data, so we run on a constant
    436      * (virtual) rate which we expose to the guest.
    437      *
    438      * Data rate examples:
    439      *   * Windows 10 @ 44,1kHz / 16-bit
    440      *      2 channels (stereo):
    441      *          * Default mode: 448 audio frames -> ~10.15ms = 1792 byte every ~10ms.
    442      *          * Fast mode:    128 audio frames -> ~ 2.90ms =  512 byte every ~3ms.
    443      *      6 channels (5.1 surround):
    444      *          * Default mode: Same values as above!
    445      */
    446 
    447     /* Audio data per second the stream needs. */
    448     const uint32_t cbDataPerSec = PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.GuestProps, RT_MS_1SEC);
    449 
    450     /* This is used to indicate whether we're done or should the uTimerIoHz as fallback. */
    451     rc = VINF_SUCCESS;
    452 
    453     /* The transfer Hz depend on the heuristics above, that is,
    454        how often the guest expects to see a new data transfer. */
    455663    ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz,
    456664                                 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD),
    457665                                 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT);
    458     unsigned uTransferHz = pStreamShared->State.uTimerIoHz;
    459 
    460     LogRel2(("HDA: Stream #%RU8 needs %RU32 bytes/s audio data (from the guest).\n", uSD, cbDataPerSec));
    461 
    462     if (pThis->fTransferHeuristicsEnabled) /* Are data transfer heuristics enabled? */
    463     {
    464         /* Don't take frames (as bytes) into account which are part of the position adjustment. */
    465         uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbGuestFrame;
    466         uint32_t cbTransferHeuristics          = 0;
    467         uint32_t cbTransferHeuristicsCur       = 0;
    468         uint32_t cBufferIrqs                   = 0;
    469         for (uint32_t i = 0; i < cTransferFragments; i++)
    470         {
    471             /** @todo wrong read type!   */
    472             HDABDLEDESC bd = { 0, 0, 0 };
    473             PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
    474 
    475             LogRel2(("HDA: Stream #%RU8 BDLE%03u: %#RX64 LB %#x %s (%#x)\n", uSD, i,
    476                      bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC ? " IOC=1" : "", bd.fFlags));
    477 
    478             /* Position adjustment (still) needed / active? */
    479             if (cbTransferHeuristicsPosAdjust)
    480             {
    481                 const uint32_t cbTransferHeuristicsPosAdjustMin = RT_MIN(cbTransferHeuristicsPosAdjust, bd.u32BufSize);
    482 
    483                 bd.u32BufSize                 -= cbTransferHeuristicsPosAdjustMin;
    484                 cbTransferHeuristicsPosAdjust -= cbTransferHeuristicsPosAdjustMin;
    485             }
    486 
    487             /* Anything left to process for the current BDLE after doing the position adjustment? */
    488             if (bd.u32BufSize == 0)
    489                 continue;
    490 
    491             /* Is an interrupt expected for the current BDLE? */
    492             if (bd.fFlags & HDA_BDLE_F_IOC)
    493             {
    494                 cbTransferHeuristicsCur += bd.u32BufSize;
    495                 if (   cbTransferHeuristicsCur == cbTransferHeuristics
    496                     || !cbTransferHeuristics)
    497                     cbTransferHeuristics = cbTransferHeuristicsCur;
    498                 else
    499                 {
    500                     /** @todo r=bird: you need to find the smallest common denominator here, not
    501                      *        just the minimum.  Ignoring this for now as windows has two equal
    502                      *        sized buffers both with IOC set. */
    503                     LogRelMax(32, ("HDA: Uneven IRQ buffer config; i=%u cbCur=%#x cbMin=%#x.\n", i, cbTransferHeuristicsCur, cbTransferHeuristics));
    504                     cbTransferHeuristics = RT_MIN(cbTransferHeuristicsCur, cbTransferHeuristics);
    505                 }
    506                 cbTransferHeuristicsCur = 0;
    507                 cBufferIrqs++;
    508             }
    509             else /* No interrupt expected -> add it to the former BDLE size. */
    510                 cbTransferHeuristicsCur += bd.u32BufSize;
    511         }
    512 
    513         /*
    514          * If the guest doesn't use buffer IRQs or only has one, just split the total
    515          * buffer length in half and use that as timer heuristics.  That gives the
    516          * guest half a buffer to fill while we're processing the other half.
    517          */
    518         if (cBufferIrqs <= 1)
    519             cbTransferHeuristics = pStreamShared->u32CBL / 2;
    520 
    521         /* Paranoia (fall back on I/O timer Hz if this happens). */
    522         if (cbTransferHeuristics >= 8)
    523         {
    524             ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.GuestProps, cbTransferHeuristics),
    525                                     ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
    526                                      uSD, cbTransferHeuristics, cbTransferHeuristics));
    527 
    528             uint64_t const cTimerTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
    529 
    530             /* Convert the heuristics value to host side bytes and that's what we're calculating. */
    531             cbTransferHeuristics = PDMAudioPropsBytesToFrames(&pStreamR3->State.Mapping.GuestProps, cbTransferHeuristics);
    532             cbTransferHeuristics = PDMAudioPropsFramesToBytes(&pCfg->Props, cbTransferHeuristics);
    533 
    534             uint64_t const cbTransferPerSec  = RT_MAX(PDMAudioPropsFramesToBytes(&pCfg->Props, pCfg->Props.uHz),
    535                                                       4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */);
    536 
    537             /* Make sure the period is 250ms (random value) or less, in case the guest
    538                want to play the whole "Der Ring des Nibelungen" cycle in one go.  Just
    539                halve the buffer till we get there. */
    540             while (cbTransferHeuristics > 1024 && cbTransferHeuristics > cbTransferPerSec / 4)
    541                 cbTransferHeuristics = PDMAudioPropsFloorBytesToFrame(&pCfg->Props, cbTransferHeuristics / 2);
    542 
    543             /* Set the transfer size per timer callout. (No chunking, so same.) */
    544             pStreamShared->State.cbTransferSize  = cbTransferHeuristics;
    545             pStreamShared->State.cbTransferChunk = cbTransferHeuristics;
    546             ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pCfg->Props, cbTransferHeuristics),
    547                                     ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
    548                                      uSD, cbTransferHeuristics, cbTransferHeuristics));
    549 
    550             /* Convert to timer ticks. */
    551             pStreamShared->State.cTicksPerByte = (cTimerTicksPerSec + cbTransferPerSec / 2) / cbTransferPerSec;
    552             AssertStmt(pStreamShared->State.cTicksPerByte, pStreamShared->State.cTicksPerByte = 4096);
    553 
    554             pStreamShared->State.cTransferTicks = (cTimerTicksPerSec * cbTransferHeuristics + cbTransferPerSec / 2)
    555                                                 / cbTransferPerSec;
    556 
    557             /* Estimate timer HZ for the circular buffer setup. */
    558             uTransferHz = cbTransferPerSec * 1000 / cbTransferHeuristics;
    559             LogRel2(("HDA: Stream #%RU8 needs a data transfer at least every %RU64 ticks / %RU32 bytes / approx %u.%03u Hz\n",
    560                      uSD, pStreamShared->State.cTransferTicks, cbTransferHeuristics, uTransferHz / 1000, uTransferHz % 1000));
    561             uTransferHz /= 1000;
    562 
    563             /* Indicate that we're done with period calculation. */
    564             rc = VINF_ALREADY_INITIALIZED;
    565         }
    566     }
    567 
    568     if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */
    569         LogRelMax(32, ("HDA: Warning: Calculated transfer Hz rate for stream #%RU8 looks incorrect (%u), please re-run with audio debug mode and report a bug\n",
    570                        uSD, uTransferHz));
    571666
    572667    /* Set I/O scheduling hint for the backends. */
     668    /** @todo r=bird: This is in the 'Device' portion, yet it's used by the
     669     *        audio driver.  You would think stuff in the 'Device' part is
     670     *        private to the device. */
    573671    pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz;
    574672    LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint));
    575673
    576     if (rc != VINF_ALREADY_INITIALIZED && RT_SUCCESS(rc))
    577     {
    578         /*
    579          * Transfer heuristics disabled or failed.
    580          */
    581         Assert(uTransferHz == pStreamShared->State.uTimerIoHz);
    582         LogRel2(("HDA: Stream #%RU8 transfer timer and I/O timer rate is %u Hz.\n", uSD, uTransferHz));
    583 
    584         /* Make sure that the chosen transfer Hz rate dividable by the stream's overall data rate. */
    585         ASSERT_GUEST_LOGREL_MSG_STMT(cbDataPerSec % uTransferHz == 0,
    586                                      ("Transfer data rate (%RU32 bytes/s) for stream #%RU8 does not fit to stream timing (%u Hz)\n",
    587                                       cbDataPerSec, uSD, uTransferHz),
    588                                      uTransferHz = HDA_TIMER_HZ_DEFAULT);
    589 
    590         pStreamShared->State.cbTransferSize = (pStreamR3->State.Mapping.GuestProps.uHz * pStreamR3->State.Mapping.cbGuestFrame)
    591                                             / uTransferHz;
    592         ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize,
    593                                      ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER);
     674
     675    /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
     676    hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
     677
     678#ifdef LOG_ENABLED
     679    hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
     680#endif
     681
     682    /*
     683     * Set up internal ring buffer.
     684     */
     685
     686    /* (Re-)Allocate the stream's internal DMA buffer,
     687     * based on the timing *and* PCM properties we just got above. */
     688    if (pStreamR3->State.pCircBuf)
     689    {
     690        RTCircBufDestroy(pStreamR3->State.pCircBuf);
     691        pStreamR3->State.pCircBuf = NULL;
     692    }
     693    pStreamR3->State.offWrite = 0;
     694    pStreamR3->State.offRead  = 0;
     695
     696    /*
     697     * The default internal ring buffer size must be:
     698     *
     699     *      - Large enough for at least three periodic DMA transfers.
     700     *
     701     *        It is critically important that we don't experience underruns
     702     *        in the DMA OUT code, because it will cause the buffer processing
     703     *        to get skewed and possibly overlap with what the guest is updating.
     704     *        At the time of writing (2021-03-05) there is no code for getting
     705     *        back into sync there.
     706     *
     707     *      - Large enough for at least three I/O scheduling hints.
     708     *
     709     *        We want to lag behind a DMA period or two, but there must be
     710     *        sufficent space for the AIO thread to get schedule and shuffle
     711     *        data thru the mixer and onto the host audio hardware.
     712     *
     713     *      - Both above with plenty to spare.
     714     *
     715     * So, just take the longest of the two periods and multipling it by 6.
     716     * We aren't not talking about very large base buffers heres, so size isn't
     717     * an issue.
     718     *
     719     * Note: Use pCfg->Props as PCM properties here, as we only want to store the
     720     *       samples we actually need, in other words, skipping the interleaved
     721     *       channels we don't support / need to save space.
     722     */
     723    uint32_t msCircBuf = RT_MS_1SEC * 6 / RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz);
     724    msCircBuf = RT_MAX(msCircBuf, pThis->msInitialDelay + RT_MS_1SEC * 6 / uTransferHz);
     725
     726    uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBuf);
     727    LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU32 bytes / %RU64 ms\n",
     728             uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
     729
     730    uint32_t msCircBufCfg = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs;
     731    if (msCircBufCfg) /* Anything set via CFGM? */
     732    {
     733        cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBufCfg);
     734        LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU32 bytes / %RU64 ms\n",
     735                 uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
     736    }
     737
     738    /* Serious paranoia: */
     739    ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf % (pCfg->Props.cbSample * pCfg->Props.cChannels) == 0,
     740                                 ("Ring buffer size (%RU32) for stream #%RU8 not aligned to the (host) frame size (%RU8)\n",
     741                                  cbCircBuf, uSD, pCfg->Props.cbSample * pCfg->Props.cChannels),
     742                                 rc = VERR_INVALID_PARAMETER);
     743    ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, ("Ring buffer size for stream #%RU8 is invalid\n", uSD),
     744                                 rc = VERR_INVALID_PARAMETER);
     745    if (RT_SUCCESS(rc))
     746    {
     747        rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
    594748        if (RT_SUCCESS(rc))
    595749        {
    596750            /*
    597              * Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
    598              * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects.
    599              *
    600              * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size.
     751             * Forward the timer frequency hint to TM as well for better accuracy on
     752             * systems w/o preemption timers (also good for 'info timers').
    601753             */
    602             pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize;
    603             ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk,
    604                                          ("Transfer chunk for stream #%RU8 is invalid\n", uSD),
    605                                          rc = VERR_INVALID_PARAMETER);
    606             if (RT_SUCCESS(rc))
    607             {
    608                 /* Make sure that the transfer chunk does not exceed the overall transfer size. */
    609                 AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize,
    610                            pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize);
    611 
    612                 const uint64_t uTimerFreq  = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
    613 
    614                 const double cTicksPerHz   = uTimerFreq  / uTransferHz;
    615 
    616                 double       cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk;
    617                 if (uTransferHz < 100)
    618                     cTicksPerByte /= 100 / uTransferHz;
    619                 else
    620                     cTicksPerByte *= uTransferHz / 100;
    621                 Assert(cTicksPerByte);
    622 
    623 #define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5))
    624 
    625                 /* Calculate the timer ticks per byte for this stream. */
    626                 pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte);
    627                 Assert(pStreamShared->State.cTicksPerByte);
    628 
    629                 const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte;
    630 
    631                 /* Calculate timer ticks per transfer. */
    632                 pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks);
    633                 Assert(pStreamShared->State.cTransferTicks);
    634 #undef HDA_ROUND_NEAREST
    635 
    636                 LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n",
    637                          uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.GuestProps.uHz,
    638                          pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks,
    639                          pStreamShared->State.cbTransferChunk, PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->State.cbTransferChunk),
    640                          pStreamShared->State.cbTransferSize,  PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->State.cbTransferSize)));
    641             }
    642         }
    643     }
    644 
    645     if (RT_SUCCESS(rc))
    646     {
    647         /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
    648         hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
    649 
    650 #ifdef LOG_ENABLED
    651         hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
    652 #endif
    653 
    654         /*
    655          * Set up internal ring buffer.
    656          */
    657 
    658         /* (Re-)Allocate the stream's internal DMA buffer,
    659          * based on the timing *and* PCM properties we just got above. */
    660         if (pStreamR3->State.pCircBuf)
    661         {
    662             RTCircBufDestroy(pStreamR3->State.pCircBuf);
    663             pStreamR3->State.pCircBuf = NULL;
    664         }
    665         pStreamR3->State.offWrite = 0;
    666         pStreamR3->State.offRead  = 0;
    667 
    668         /*
    669          * The default internal ring buffer size must be:
    670          *
    671          *      - Large enough for at least three periodic DMA transfers.
    672          *
    673          *        It is critically important that we don't experience underruns
    674          *        in the DMA OUT code, because it will cause the buffer processing
    675          *        to get skewed and possibly overlap with what the guest is updating.
    676          *        At the time of writing (2021-03-05) there is no code for getting
    677          *        back into sync there.
    678          *
    679          *      - Large enough for at least three I/O scheduling hints.
    680          *
    681          *        We want to lag behind a DMA period or two, but there must be
    682          *        sufficent space for the AIO thread to get schedule and shuffle
    683          *        data thru the mixer and onto the host audio hardware.
    684          *
    685          *      - Both above with plenty to spare.
    686          *
    687          * So, just take the longest of the two periods and multipling it by 6.
    688          * We aren't not talking about very large base buffers heres, so size isn't
    689          * an issue.
    690          *
    691          * Note: Use pCfg->Props as PCM properties here, as we only want to store the
    692          *       samples we actually need, in other words, skipping the interleaved
    693          *       channels we don't support / need to save space.
    694          */
    695         uint32_t msCircBuf = RT_MS_1SEC * 6 / RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz);
    696         msCircBuf = RT_MAX(msCircBuf, pThis->msInitialDelay + RT_MS_1SEC * 6 / uTransferHz);
    697 
    698         uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBuf);
    699         LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU32 bytes / %RU64 ms\n",
    700                  uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
    701 
    702         uint32_t msCircBufCfg = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs;
    703         if (msCircBufCfg) /* Anything set via CFGM? */
    704         {
    705             cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBufCfg);
    706             LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU32 bytes / %RU64 ms\n",
    707                      uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
    708         }
    709 
    710         /* Serious paranoia: */
    711         ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf % (pCfg->Props.cbSample * pCfg->Props.cChannels) == 0,
    712                                      ("Ring buffer size (%RU32) for stream #%RU8 not aligned to the (host) frame size (%RU8)\n",
    713                                       cbCircBuf, uSD, pCfg->Props.cbSample * pCfg->Props.cChannels),
    714                                      rc = VERR_INVALID_PARAMETER);
    715         ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, ("Ring buffer size for stream #%RU8 is invalid\n", uSD),
    716                                      rc = VERR_INVALID_PARAMETER);
    717         if (RT_SUCCESS(rc))
    718         {
    719             rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
    720             if (RT_SUCCESS(rc))
    721             {
    722                 /*
    723                  * Forward the timer frequency hint to TM as well for better accuracy on
    724                  * systems w/o preemption timers (also good for 'info timers').
    725                  */
    726                 PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
    727             }
     754            PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
    728755        }
    729756    }
     
    734761#ifdef VBOX_WITH_DTRACE
    735762    VBOXDD_HDA_STREAM_SETUP((uint32_t)uSD, rc, pStreamShared->State.Cfg.Props.uHz,
    736                             pStreamShared->State.cTransferTicks, pStreamShared->State.cbTransferSize);
     763                            pStreamShared->State.aSchedule[pStreamShared->State.cSchedule - 1].cPeriodTicks,
     764                            pStreamShared->State.aSchedule[pStreamShared->State.cSchedule - 1].cbPeriod);
    737765#endif
    738766    return rc;
     
    790818    pStreamR3->pMixSink = hdaR3GetDefaultSink(pThisCC, uSD);
    791819
    792     /* Reset position adjustment counter. */
    793     pStreamShared->State.cfPosAdjustLeft = pStreamShared->State.cfPosAdjustDefault;
    794 
    795820    /* Reset transfer stuff. */
    796821    pStreamShared->State.cTransferPendingInterrupts = 0;
     
    804829    pStreamShared->State.tsStart          = 0;
    805830
    806     RT_ZERO(pStreamShared->State.BDLE);
    807     pStreamShared->State.uCurBDLE = 0;
     831    RT_ZERO(pStreamShared->State.aBdl);
     832    RT_ZERO(pStreamShared->State.aSchedule);
     833    pStreamShared->State.offCurBdle        = 0;
     834    pStreamShared->State.cBdles            = 0;
     835    pStreamShared->State.idxCurBdle        = 0;
     836    pStreamShared->State.cSchedulePrologue = 0;
     837    pStreamShared->State.cSchedule         = 0;
     838    pStreamShared->State.idxSchedule       = 0;
     839    pStreamShared->State.idxScheduleLoop   = 0;
    808840
    809841    if (pStreamR3->State.pCircBuf)
     
    11481180
    11491181/**
    1150  * Transfers data of an HDA stream according to its usage (input / output).
    1151  *
    1152  * For an SDO (output) stream this means reading DMA data from the device to
    1153  * the HDA stream's internal FIFO buffer.
    1154  *
    1155  * For an SDI (input) stream this is reading audio data from the HDA stream's
    1156  * internal FIFO buffer and writing it as DMA data to the device.
     1182 * Get the current address and number of bytes left in the current BDLE.
     1183 *
     1184 * @returns The current physical address.
     1185 * @param   pStreamShared   The stream to check.
     1186 * @param   pcbLeft         The number of bytes left at the returned address.
     1187 */
     1188DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGet(PHDASTREAM pStreamShared, uint32_t *pcbLeft)
     1189{
     1190    uint8_t        idxBdle    = pStreamShared->State.idxCurBdle;
     1191    AssertStmt(idxBdle < pStreamShared->State.cBdles, idxBdle = 0);
     1192
     1193    uint32_t const cbCurBdl   = pStreamShared->State.aBdl[idxBdle].cb;
     1194    uint32_t       offCurBdle = pStreamShared->State.offCurBdle;
     1195    AssertStmt(pStreamShared->State.offCurBdle <= cbCurBdl, offCurBdle = cbCurBdl);
     1196
     1197    *pcbLeft = cbCurBdl - offCurBdle;
     1198    return pStreamShared->State.aBdl[idxBdle].GCPhys + offCurBdle;
     1199}
     1200
     1201/**
     1202 * Get the size of the current BDLE.
     1203 *
     1204 * @returns The size (in bytes).
     1205 * @param   pStreamShared   The stream to check.
     1206 */
     1207DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGetSize(PHDASTREAM pStreamShared)
     1208{
     1209    uint8_t idxBdle = pStreamShared->State.idxCurBdle;
     1210    AssertStmt(idxBdle < pStreamShared->State.cBdles, idxBdle = 0);
     1211    return pStreamShared->State.aBdl[idxBdle].cb;
     1212}
     1213
     1214/**
     1215 * Checks if the current BDLE is completed.
     1216 *
     1217 * @retval  true if complete
     1218 * @retval  false if not.
     1219 * @param   pStreamShared   The stream to check.
     1220 */
     1221DECLINLINE(bool) hdaR3StreamDmaBufIsComplete(PHDASTREAM pStreamShared)
     1222{
     1223    uint8_t const  idxBdle    = pStreamShared->State.idxCurBdle;
     1224    AssertReturn(idxBdle < pStreamShared->State.cBdles, true);
     1225
     1226    uint32_t const cbCurBdl   = pStreamShared->State.aBdl[idxBdle].cb;
     1227    uint32_t const offCurBdle = pStreamShared->State.offCurBdle;
     1228    Assert(offCurBdle <= cbCurBdl);
     1229    return offCurBdle >= cbCurBdl;
     1230}
     1231
     1232/**
     1233 * Checks if the current BDLE needs a completion IRQ.
     1234 *
     1235 * @retval  true if IRQ is needed.
     1236 * @retval  false if not.
     1237 * @param   pStreamShared   The stream to check.
     1238 */
     1239DECLINLINE(bool) hdaR3StreamDmaBufNeedsIrq(PHDASTREAM pStreamShared)
     1240{
     1241    uint8_t const idxBdle = pStreamShared->State.idxCurBdle;
     1242    AssertReturn(idxBdle < pStreamShared->State.cBdles, false);
     1243    return (pStreamShared->State.aBdl[idxBdle].fFlags & HDA_BDLE_F_IOC) != 0;
     1244}
     1245
     1246/**
     1247 * Advances the DMA engine to the next BDLE.
     1248 *
     1249 * @param   pStreamShared   The stream which DMA engine is to be updated.
     1250 */
     1251DECLINLINE(void) hdaR3StreamDmaBufAdvanceToNext(PHDASTREAM pStreamShared)
     1252{
     1253    uint8_t idxBdle = pStreamShared->State.idxCurBdle;
     1254    Assert(pStreamShared->State.offCurBdle == pStreamShared->State.aBdl[idxBdle].cb);
     1255
     1256    if (idxBdle < pStreamShared->State.cBdles - 1)
     1257        idxBdle++;
     1258    else
     1259        idxBdle = 0;
     1260    pStreamShared->State.idxCurBdle = idxBdle;
     1261    pStreamShared->State.offCurBdle = 0;
     1262}
     1263
     1264/**
     1265 * Does DMA transfer for an HDA input stream.
     1266 *
     1267 * Reads audio data from the HDA stream's internal DMA buffer and writing to
     1268 * guest memory.
    11571269 *
    11581270 * @returns IPRT status code.
     
    11641276 * @param   cbToProcessMax      How much data (in bytes) to process as maximum.
    11651277 */
    1166 static int hdaR3StreamTransfer(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared,
    1167                                PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax)
     1278static int hdaR3StreamDoDmaInput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared,
     1279                                 PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax)
    11681280{
    11691281    uint8_t const uSD = pStreamShared->u8SD;
    11701282    LogFlowFunc(("ENTER - #%u cbToProcessMax=%#x\n", uSD, cbToProcessMax));
     1283    Assert(hdaGetDirFromSD(uSD) != PDMAUDIODIR_OUT);
    11711284
    11721285    if (RT_LIKELY(cbToProcessMax >= pStreamShared->State.cbTransferSize))
     
    11741287    else
    11751288    {
    1176         Assert(hdaGetDirFromSD(uSD) != PDMAUDIODIR_OUT /* Handled by caller */);
    11771289        /** @todo account for this or something so we can try get back in sync
    11781290         *        later... */
     
    12431355    /* Transfer sanity checks. */
    12441356    Assert(pStreamShared->State.cbTransferSize);
    1245     Assert(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize);
    12461357
    12471358    int rc = VINF_SUCCESS;
    12481359
    1249     /* Fetch first / next BDL entry. */
    1250     PHDABDLE pBDLE = &pStreamShared->State.BDLE;
    1251     if (hdaR3BDLEIsComplete(pBDLE))
    1252     {
    1253         rc = hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
    1254         AssertRC(rc);
    1255     }
    1256 
    1257     uint32_t cbToProcess = RT_MIN(pStreamShared->State.cbTransferSize, pStreamShared->State.cbTransferChunk);
     1360    uint32_t cbToProcess = pStreamShared->State.cbTransferSize;
    12581361
    12591362    Assert(cbToProcess);                                                     /* Nothing to process when there should be data. Accounting bug? */
     
    12871390
    12881391        /* Limit the chunk to the remaining data of the current BDLE. */
    1289         cbChunk = RT_MIN(cbChunk, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
    1290 
    1291         /* If there are position adjustment frames left to be processed,
    1292          * make sure that we process them first as a whole. */
    1293         if (pStreamShared->State.cfPosAdjustLeft)
    1294             cbChunk = RT_MIN(cbChunk, (uint32_t)pStreamShared->State.cfPosAdjustLeft * pStreamR3->State.Mapping.cbGuestFrame);
     1392        uint32_t cbDmaBuf = 0;
     1393        RTGCPHYS GCPhys = hdaR3StreamDmaBufGet(pStreamShared, &cbDmaBuf);
     1394        cbChunk = RT_MIN(cbChunk, cbDmaBuf);
    12951395
    12961396        if (!cbChunk)
    12971397            break;
    12981398
    1299         uint32_t   cbDMA    = 0;
     1399        STAM_PROFILE_START(&pThis->StatIn, a);
     1400
     1401        /*
     1402         * Copy out of the internal DMA buffer.
     1403         * @todo eliminate extra copy.
     1404         */
    13001405        PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
    13011406        uint8_t   *pabFIFO  = pStreamShared->abFIFO;
    1302 
    1303         if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN) /* Input (SDI). */
     1407        uint32_t   cbDMAWritten = 0;
     1408        uint32_t   cbDMAToWrite = cbChunk;
     1409
     1410        /** @todo Do we need interleaving streams support here as well?
     1411         *        Never saw anything else besides mono/stereo mics (yet). */
     1412        while (cbDMAToWrite)
    13041413        {
    1305             STAM_PROFILE_START(&pThis->StatIn, a);
    1306 
    1307             uint32_t cbDMAWritten = 0;
    1308             uint32_t cbDMAToWrite = cbChunk;
    1309 
    1310             /** @todo Do we need interleaving streams support here as well?
    1311              *        Never saw anything else besides mono/stereo mics (yet). */
    1312             while (cbDMAToWrite)
    1313             {
    1314                 void *pvBuf; size_t cbBuf;
    1315                 RTCircBufAcquireReadBlock(pCircBuf, cbDMAToWrite, &pvBuf, &cbBuf);
    1316 
    1317                 if (   !cbBuf
    1318                     && !RTCircBufUsed(pCircBuf))
    1319                     break;
    1320 
    1321                 memcpy(pabFIFO + cbDMAWritten, pvBuf, cbBuf);
     1414            void *pvBuf; size_t cbBuf;
     1415            RTCircBufAcquireReadBlock(pCircBuf, cbDMAToWrite, &pvBuf, &cbBuf);
     1416
     1417            if (   !cbBuf
     1418                && !RTCircBufUsed(pCircBuf))
     1419                break;
     1420
     1421            memcpy(pabFIFO + cbDMAWritten, pvBuf, cbBuf);
    13221422#ifdef VBOX_WITH_DTRACE
    1323                 VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offRead);
     1423            VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offRead);
    13241424#endif
    1325                 pStreamR3->State.offRead += cbBuf;
    1326 
    1327                 RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
    1328 
    1329                 Assert(cbDMAToWrite >= cbBuf);
    1330                 cbDMAToWrite -= (uint32_t)cbBuf;
    1331                 cbDMAWritten += (uint32_t)cbBuf;
    1332                 Assert(cbDMAWritten <= cbChunk);
    1333             }
    1334 
    1335             if (cbDMAToWrite)
    1336             {
    1337                 LogRel2(("HDA: FIFO underflow for stream #%RU8 (%RU32 bytes outstanding)\n", uSD, cbDMAToWrite));
    1338 
    1339                 Assert(cbChunk == cbDMAWritten + cbDMAToWrite);
    1340                 memset((uint8_t *)pabFIFO + cbDMAWritten, 0, cbDMAToWrite);
    1341                 cbDMAWritten = cbChunk;
    1342             }
    1343 
    1344             rc = hdaR3DMAWrite(pDevIns, pThis, pStreamShared, pStreamR3, pabFIFO, cbDMAWritten, &cbDMA /* pcbWritten */);
    1345             if (RT_FAILURE(rc))
    1346                 LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", uSD, rc));
    1347 
    1348             STAM_PROFILE_STOP(&pThis->StatIn, a);
     1425            pStreamR3->State.offRead += cbBuf;
     1426
     1427            RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
     1428
     1429            Assert(cbDMAToWrite >= cbBuf);
     1430            cbDMAToWrite -= (uint32_t)cbBuf;
     1431            cbDMAWritten += (uint32_t)cbBuf;
     1432            Assert(cbDMAWritten <= cbChunk);
    13491433        }
    1350         else if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT) /* Output (SDO). */
     1434
     1435        if (cbDMAToWrite)
    13511436        {
    1352             STAM_PROFILE_START(&pThis->StatOut, a);
    1353 
    1354             rc = hdaR3DMARead(pDevIns, pThis, pStreamShared, pStreamR3, pabFIFO, cbChunk, &cbDMA /* pcbRead */);
    1355             if (RT_SUCCESS(rc))
    1356             {
    1357                 const uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
    1358 
    1359                 /*
    1360                  * Most guests don't use different stream frame sizes than
    1361                  * the default one, so save a bit of CPU time and don't go into
    1362                  * the frame extraction code below.
    1363                  *
    1364                  * Only macOS guests need the frame extraction branch below at the moment AFAIK.
    1365                  */
    1366                 if (pStreamR3->State.Mapping.cbGuestFrame == HDA_FRAME_SIZE_DEFAULT)
    1367                 {
    1368                     uint32_t cbDMARead = 0;
    1369                     uint32_t cbDMALeft = RT_MIN(cbDMA, cbFree);
    1370 
    1371                     while (cbDMALeft)
    1372                     {
    1373                         void *pvBuf; size_t cbBuf;
    1374                         RTCircBufAcquireWriteBlock(pCircBuf, cbDMALeft, &pvBuf, &cbBuf);
    1375 
    1376                         if (cbBuf)
    1377                         {
    1378                             memcpy(pvBuf, pabFIFO + cbDMARead, cbBuf);
    1379                             cbDMARead += (uint32_t)cbBuf;
    1380                             cbDMALeft -= (uint32_t)cbBuf;
    1381 #ifdef VBOX_WITH_DTRACE
    1382                             VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offWrite);
    1383 #endif
    1384                             pStreamR3->State.offWrite += cbBuf;
    1385                         }
    1386 
    1387                         RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
    1388                     }
    1389                 }
    1390                 else
    1391                 {
    1392                     /*
    1393                      * The following code extracts the required audio stream (channel) data
    1394                      * of non-interleaved *and* interleaved audio streams.
    1395                      *
    1396                      * We by default only support 2 channels with 16-bit samples (HDA_FRAME_SIZE),
    1397                      * but an HDA audio stream can have interleaved audio data of multiple audio
    1398                      * channels in such a single stream ("AA,AA,AA vs. AA,BB,AA,BB").
    1399                      *
    1400                      * So take this into account by just handling the first channel in such a stream ("A")
    1401                      * and just discard the other channel's data.
    1402                      *
    1403                      * I know, the following code is horribly slow, but seems to work for now.
    1404                      */
    1405                     /** @todo Optimize channel data extraction! Use some SSE(3) / intrinsics? */
    1406                     for (unsigned m = 0; m < pStreamR3->State.Mapping.cMappings; m++)
    1407                     {
    1408                         const uint32_t cbFrame  = pStreamR3->State.Mapping.cbGuestFrame;
    1409 
    1410                         Assert(cbFree >= cbDMA);
    1411 
    1412                         PPDMAUDIOSTREAMMAP pMap = &pStreamR3->State.Mapping.paMappings[m];
    1413                         AssertPtr(pMap);
    1414 
    1415                         Log3Func(("Mapping #%u: Start (cbDMA=%RU32, cbFrame=%RU32, offNext=%RU32)\n",
    1416                                   m, cbDMA, cbFrame, pMap->offNext));
    1417 
    1418 
    1419                         /* Skip the current DMA chunk if the chunk is smaller than what the current stream mapping needs to read
    1420                          * the next associated frame (pointed to at pMap->cbOff).
    1421                          *
    1422                          * This can happen if the guest did not come up with enough data within a certain time period, especially
    1423                          * when using multi-channel speaker (> 2 channels [stereo]) setups. */
    1424                         if (pMap->offNext > cbChunk)
    1425                         {
    1426                             Log2Func(("Mapping #%u: Skipped (cbChunk=%RU32, cbMapOff=%RU32)\n", m, cbChunk, pMap->offNext));
    1427                             continue;
    1428                         }
    1429 
    1430                         uint8_t *pbSrcBuf = pabFIFO;
    1431                         size_t cbSrcOff   = pMap->offNext;
    1432 
    1433                         for (unsigned i = 0; i < cbDMA / cbFrame; i++)
    1434                         {
    1435                             void *pvDstBuf; size_t cbDstBuf;
    1436                             RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
    1437 
    1438                             Assert(cbDstBuf >= pMap->cbStep);
    1439 
    1440                             if (cbDstBuf)
    1441                             {
    1442                                 Log3Func(("Mapping #%u: Frame #%02u:    cbStep=%u, offFirst=%u, offNext=%u, cbDstBuf=%u, cbSrcOff=%u\n",
    1443                                           m, i, pMap->cbStep, pMap->offFirst, pMap->offNext, cbDstBuf, cbSrcOff));
    1444 
    1445                                 memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
    1446 
    1447 #if 0 /* Too slow, even for release builds, so disabled it. */
    1448                                 if (pStreamR3->Dbg.Runtime.fEnabled)
    1449                                     DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMAMapped, pvDstBuf, cbDstBuf,
    1450                                                          0 /* fFlags */);
    1451 #endif
    1452                                 Assert(cbSrcOff <= cbDMA);
    1453                                 if (cbSrcOff + cbFrame + pMap->offFirst<= cbDMA)
    1454                                     cbSrcOff += cbFrame + pMap->offFirst;
    1455 
    1456 #ifdef VBOX_WITH_DTRACE
    1457                                 VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
    1458 #endif
    1459                                 Log3Func(("Mapping #%u: Frame #%02u:    -> cbSrcOff=%zu\n", m, i, cbSrcOff));
    1460                                 pStreamR3->State.offWrite += cbDstBuf;
    1461                             }
    1462 
    1463                             RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
    1464                         }
    1465 
    1466                         Log3Func(("Mapping #%u: End cbSize=%u, cbDMA=%RU32, cbSrcOff=%zu\n",
    1467                                   m, pMap->cbStep, cbDMA, cbSrcOff));
    1468 
    1469                         Assert(cbSrcOff <= cbDMA);
    1470 
    1471                         const uint32_t cbSrcLeft = cbDMA - (uint32_t)cbSrcOff;
    1472                         if (cbSrcLeft)
    1473                         {
    1474                             Log3Func(("Mapping #%u: cbSrcLeft=%RU32\n", m, cbSrcLeft));
    1475 
    1476                             if (cbSrcLeft >= pMap->cbStep)
    1477                             {
    1478                                 void *pvDstBuf; size_t cbDstBuf;
    1479                                 RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
    1480 
    1481                                 Assert(cbDstBuf >= pMap->cbStep);
    1482 
    1483                                 if (cbDstBuf)
    1484                                 {
    1485                                     memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
    1486 #ifdef VBOX_WITH_DTRACE
    1487                                     VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
    1488 #endif
    1489                                     pStreamR3->State.offWrite += cbDstBuf;
    1490                                 }
    1491 
    1492                                 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
    1493                             }
    1494 
    1495                             Assert(pMap->cbFrame >= cbSrcLeft);
    1496                             pMap->offNext = pMap->cbFrame - cbSrcLeft;
    1497                         }
    1498                         else
    1499                             pMap->offNext = 0;
    1500 
    1501                         Log3Func(("Mapping #%u finish (cbSrcOff=%zu, offNext=%zu)\n", m, cbSrcOff, pMap->offNext));
    1502                     }
    1503                 }
    1504             }
    1505             else
    1506                 LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", uSD, rc));
    1507 
    1508             STAM_PROFILE_STOP(&pThis->StatOut, a);
     1437            LogRel2(("HDA: FIFO underflow for stream #%RU8 (%RU32 bytes outstanding)\n", uSD, cbDMAToWrite));
     1438
     1439            Assert(cbChunk == cbDMAWritten + cbDMAToWrite);
     1440            memset((uint8_t *)pabFIFO + cbDMAWritten, 0, cbDMAToWrite);
     1441            cbDMAWritten = cbChunk;
    15091442        }
    15101443
    1511         else /** @todo Handle duplex streams? */
    1512             AssertFailed();
    1513 
    1514         if (cbDMA)
     1444        /*
     1445         * Write to the guest.
     1446         */
     1447        if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
     1448        { /* likely */ }
     1449        else
     1450            DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pabFIFO, cbDMAWritten, 0 /* fFlags */);
     1451
     1452        rc = PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, pabFIFO, cbDMAWritten);
     1453        AssertLogRelMsgRC(rc, ("HDA: Write at %RGp LB %#x -> %Rrc\n", GCPhys, cbDMAWritten, rc));
     1454
     1455        STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
     1456
     1457        STAM_PROFILE_STOP(&pThis->StatIn, a);
     1458
     1459        /* Advance. */
     1460        pStreamShared->State.offCurBdle += cbDMAWritten;
     1461        Assert(cbLeft >= cbDMAWritten);
     1462        cbLeft        -= cbDMAWritten;
     1463        cbProcessed   += cbDMAWritten;
     1464
     1465        if (hdaR3StreamDmaBufIsComplete(pStreamShared))
    15151466        {
    1516             /* We always increment the position of DMA buffer counter because we're always reading
    1517              * into an intermediate DMA buffer. */
    1518             pBDLE->State.u32BufOff += (uint32_t)cbDMA;
    1519             Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
    1520 
    1521             /* Are we done doing the position adjustment?
    1522              * Only then do the transfer accounting .*/
    1523             if (pStreamShared->State.cfPosAdjustLeft == 0)
    1524             {
    1525                 Assert(cbLeft >= cbDMA);
    1526                 cbLeft        -= cbDMA;
    1527 
    1528                 cbProcessed   += cbDMA;
    1529             }
    1530 
    1531             Log3Func(("[SD%RU8] cbDMA=%RU32 -> %R[bdle]\n", uSD, cbDMA, pBDLE));
    1532         }
    1533 
    1534         if (hdaR3BDLEIsComplete(pBDLE))
    1535         {
    1536             Log3Func(("[SD%RU8] Completed %R[bdle]\n", uSD, pBDLE));
     1467            Log3Func(("[SD%RU8] Completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x (wrote %#x)\n",
     1468                      uSD, pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
     1469                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
     1470                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, cbDMAWritten));
    15371471
    15381472            /* Make sure to also update the wall clock when a BDLE is complete.
     
    15411475                             hdaWalClkGetCurrent(pThis)
    15421476                           + hdaR3StreamPeriodFramesToWalClk(pPeriod,
    1543                                                                pBDLE->Desc.u32BufSize
     1477                                                               hdaR3StreamDmaBufGetSize(pStreamShared)
    15441478                                                             / pStreamR3->State.Mapping.cbGuestFrame),
    15451479                           false /* fForce */);
     
    15531487             *                      Not doing this at the right time will result in ugly sound crackles!
    15541488             */
    1555             hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, pBDLE->Desc.u32BufSize);
    1556 
    1557                 /* Does the current BDLE require an interrupt to be sent? */
    1558             if (   hdaR3BDLENeedsInterrupt(pBDLE)
    1559                 /* Are we done doing the position adjustment?
    1560                  * It can happen that a BDLE which is handled while doing the
    1561                  * position adjustment requires an interrupt on completion (IOC) being set.
    1562                  *
    1563                  * In such a case we need to skip such an interrupt and just move on. */
    1564                 && pStreamShared->State.cfPosAdjustLeft == 0)
     1489            hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaR3StreamDmaBufGetSize(pStreamShared));
     1490
     1491            /* Does the current BDLE require an interrupt to be sent? */
     1492            if (hdaR3StreamDmaBufNeedsIrq(pStreamShared))
    15651493            {
    15661494                /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
     
    15731501                    {
    15741502                        pStreamShared->State.cTransferPendingInterrupts = 1;
    1575 
    1576                         AssertMsg(pStreamShared->State.cTransferPendingInterrupts <= 32,
    1577                                   ("Too many pending interrupts (%RU8) for stream #%RU8\n",
    1578                                    pStreamShared->State.cTransferPendingInterrupts, uSD));
    1579 
    1580                         Log3Func(("[SD%RU8] Scheduling interrupt (now %RU8 total)\n", uSD, pStreamShared->State.cTransferPendingInterrupts));
     1503                        Log3Func(("[SD%RU8] Scheduling interrupt\n", uSD));
    15811504
    15821505                        /*
     
    16021525            }
    16031526
    1604             if (pStreamShared->State.uCurBDLE == pStreamShared->u16LVI)
    1605             {
    1606                 pStreamShared->State.uCurBDLE = 0;
    1607             }
    1608             else
    1609                 pStreamShared->State.uCurBDLE++;
    1610 
    1611             /* Fetch the next BDLE entry. */
    1612             hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
     1527            hdaR3StreamDmaBufAdvanceToNext(pStreamShared);
    16131528        }
    1614 
    1615         /* Do the position adjustment accounting. */
    1616         pStreamShared->State.cfPosAdjustLeft -=
    1617             RT_MIN(pStreamShared->State.cfPosAdjustLeft, cbDMA / pStreamR3->State.Mapping.cbGuestFrame);
    1618 
    1619         if (RT_FAILURE(rc))
    1620             break;
     1529        else
     1530            Log3Func(("[SD%RU8] Not completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x: off=%#RX32, wrote %#x\n",
     1531                      uSD, pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
     1532                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
     1533                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, pStreamShared->State.offCurBdle, cbDMAWritten));
    16211534    }
    16221535
     
    16281541    Assert(cbLeft      == 0);
    16291542
    1630     /* Only do the data accounting if we don't have to do any position
    1631      * adjustment anymore. */
    1632     if (pStreamShared->State.cfPosAdjustLeft == 0)
    1633     {
    1634         hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbGuestFrame,
    1635                                              hdaR3StreamPeriodGetRemainingFrames(pPeriod)));
    1636     }
     1543    hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbGuestFrame,
     1544                                         hdaR3StreamPeriodGetRemainingFrames(pPeriod)));
    16371545
    16381546    const bool fTransferComplete  = cbLeft == 0;
     
    16611569    }
    16621570
    1663     /* Set the next transfer timing slot.
    1664      * This must happen at a constant rate. */
    1665     pStreamShared->State.tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;
    1666 
    16671571    /* Always update this timestamp, no matter what pStreamShared->State.tsTransferNext is. */
    16681572    pStreamShared->State.tsTransferLast = tsNow;
    16691573
    1670     Log3Func(("[SD%RU8] %R[bdle] -- %#RX32/%#RX32 @ %#RX64\n", uSD, pBDLE, cbProcessed, pStreamShared->State.cbTransferSize,
     1574    Log3Func(("[SD%RU8] %#RX32/%#RX32 @ %#RX64\n", uSD, cbProcessed, pStreamShared->State.cbTransferSize,
    16711575              (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT ? pStreamR3->State.offWrite : pStreamR3->State.offRead) - cbProcessed));
    16721576    Log3Func(("[SD%RU8] fTransferComplete=%RTbool, cTransferPendingInterrupts=%RU8\n",
    16731577              uSD, fTransferComplete, pStreamShared->State.cTransferPendingInterrupts));
    1674     Log3Func(("[SD%RU8] tsNow=%RU64, tsTransferNext=%RU64 (in %RU64 ticks)\n",
    1675               uSD, tsNow, pStreamShared->State.tsTransferNext,
    1676               pStreamShared->State.tsTransferNext ? pStreamShared->State.tsTransferNext - tsNow : 0));
    16771578
    16781579    LogFlowFuncLeave();
     
    16971598 * @param   pStreamR3           HDA stream to update (ring-3).
    16981599 * @param   cbToProduce         The max amount of data to produce (i.e. put into
    1699  *                              the circular buffer).  (Caller should already
    1700  *                              have made sure this is at least the size of one
    1701  *                              DMA timer period, so this function doesn't need
    1702  *                              to do any extra underflow handling.)
     1600 *                              the circular buffer).  Unless something is going
     1601 *                              seriously wrong, this will always be transfer
     1602 *                              size for the current period.
    17031603 * @param   tsNowNs             The current RTTimeNano() value.
    17041604 *
     
    17511651    Assert(ASMAtomicReadBool(&pStreamShared->State.fRunning));
    17521652
    1753     /* Transfer sanity checks. */
    1754     Assert(pStreamShared->State.cbTransferSize);
    1755     Assert(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize);
    1756 
    17571653    /*
    17581654     * Some timestamp stuff for logging/debugging.
     
    17641660
    17651661    /*
    1766      * Fetch the next BDL entry.
    1767      */
    1768     int      rc    = VINF_SUCCESS;
    1769     PHDABDLE pBDLE = &pStreamShared->State.BDLE;
    1770     if (hdaR3BDLEIsComplete(pBDLE))
    1771     {
    1772         rc = hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
    1773         AssertRCReturn(rc, rc);
    1774     }
    1775 
    1776     /*
    1777      * The caller should have made sure we've got at least cbTransferSize
    1778      * of buffer available.   We will not transfer more than that.
    1779      */
    1780     uint8_t cSuppressIocs = 0;
    1781     Assert(pStreamShared->State.cbTransferChunk == pStreamShared->State.cbTransferSize);
    1782     if (!pStreamShared->State.cfPosAdjustLeft)
    1783     {
    1784         Assert(cbToProduce >= pStreamShared->State.cbTransferSize);
    1785         if (cbToProduce > pStreamShared->State.cbTransferSize)
    1786             cbToProduce = pStreamShared->State.cbTransferSize;
    1787     }
    1788     else
    1789     {
    1790         /* We currently process the position adjustment BLDE0 and the whole BLDE1
    1791            in one DMA timer callout, ignoring BLDE0.IOC. */
    1792         /** @todo Process the tiny BLDE0 seperatly. */
    1793         uint32_t const cbPosAdjust = PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props,
    1794                                                                 pStreamShared->State.cfPosAdjustLeft);
    1795         Assert(cbToProduce >= pStreamShared->State.cbTransferSize + cbPosAdjust);
    1796         if (cbToProduce >= pStreamShared->State.cbTransferSize + cbPosAdjust)
    1797         {
    1798             cbToProduce = pStreamShared->State.cbTransferSize + cbPosAdjust;
    1799             pStreamShared->State.cfPosAdjustLeft = 0;
    1800             cSuppressIocs = pBDLE->Desc.fFlags & HDA_BDLE_F_IOC ? 1 : 0;
    1801         }
    1802         else
    1803             pStreamShared->State.cfPosAdjustLeft -= PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props, cbToProduce);
    1804     }
    1805     uint32_t cbLeft = cbToProduce;
    1806     Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
    1807 
    1808     /*
    1809      * Whether an interrupt has been sent (asserted) for this transfer period already or not.
    1810      *
    1811      * Note: Windows 10 relies on this, e.g. sending more than one interrupt per transfer period
    1812      *       confuses the Windows' audio driver and will screw up the audio data. So only send
    1813      *       one interrupt per transfer period.
    1814      *
    1815      * Note! This only applies if the transfer heuristics isn't active.
    1816      */
    1817     /** @todo r=bird: Of course the guest gets confused if you bundle interrupts.
    1818      *        Unless the buffers are really small, this is stuff that won't happen
    1819      *        on real hardware.  */
    1820     /** @todo Disallow non-heuristics approach!  It only complicates the code
    1821      *        and messes with the guest if we cover more than one IOC! */
    1822     bool fInterruptSent = false;
    1823 
    1824     /* Set the FIFORDY bit on the stream while doing the transfer. */
     1662     * Set the FIFORDY bit on the stream while doing the transfer.
     1663     */
    18251664    /** @todo r=bird: I don't get the HDA_SDSTS_FIFORDY logic.  Unless we're
    18261665     *        assuming SMP guest and that it can get stream registers while we're
     
    18381677    uint32_t   cbBounce = 0;            /* in case of incomplete frames between buffer segments */
    18391678    PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
     1679    uint32_t   cbLeft   = cbToProduce;
     1680    Assert(cbLeft == pStreamShared->State.cbTransferSize);
     1681    Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
     1682
    18401683    while (cbLeft > 0)
    18411684    {
     
    18451688         * Figure out how much we can read & write in this iteration.
    18461689         */
    1847         uint32_t cbChunk = pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff;
    1848         AssertStmt(cbChunk <= pBDLE->Desc.u32BufSize, cbChunk = 0);
     1690        uint32_t cbChunk = 0;
     1691        RTGCPHYS GCPhys  = hdaR3StreamDmaBufGet(pStreamShared, &cbChunk);
    18491692
    18501693        /* Need to diverge if the frame format differs.  */
     
    18601703             * Read the guest data directly into the internal DMA buffer.
    18611704             */
    1862             RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
    18631705            while (cbChunk > 0)
    18641706            {
     
    18851727
    18861728                /* advance */
    1887                 cbChunk                -= (uint32_t)cbBufDst;
    1888                 GCPhys                 +=           cbBufDst;
    1889                 cbLeft                 -= (uint32_t)cbBufDst;
    1890                 pBDLE->State.u32BufOff += (uint32_t)cbBufDst;
    1891                 Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
     1729                cbChunk                         -= (uint32_t)cbBufDst;
     1730                GCPhys                          +=           cbBufDst;
     1731                cbLeft                          -= (uint32_t)cbBufDst;
     1732                pStreamShared->State.offCurBdle += (uint32_t)cbBufDst;
    18921733            }
    18931734        }
     
    19141755             * Loop till we've covered the chunk.
    19151756             */
    1916             RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
    19171757            Log5Func(("loop0:  GCPhys=%RGp cbChunk=%#x + cbBounce=%#x\n", GCPhys, cbChunk, cbBounce));
    19181758            while (cbChunk > 0)
     
    19691809
    19701810                /* advance */
    1971                 cbChunk                -= cbToRead;
    1972                 GCPhys                 += cbToRead;
    1973                 pBDLE->State.u32BufOff += cbToRead;
    1974                 Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
     1811                cbChunk                         -= cbToRead;
     1812                GCPhys                          += cbToRead;
     1813                pStreamShared->State.offCurBdle += cbToRead;
    19751814                if (cbRemainder)
    19761815                    memmove(&abBounce[0], &abBounce[cbBounce - cbRemainder], cbRemainder);
     
    19851824         * Is the buffer descriptor complete.
    19861825         */
    1987         if (hdaR3BDLEIsComplete(pBDLE))
     1826        if (hdaR3StreamDmaBufIsComplete(pStreamShared))
    19881827        {
    1989             Log3Func(("[SD%RU8] Completed %R[bdle]\n", uSD, pBDLE));
     1828            Log3Func(("[SD%RU8] Completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x\n", uSD, pStreamShared->State.idxCurBdle,
     1829                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
     1830                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
     1831                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags));
    19901832
    19911833            /* Make sure to also update the wall clock when a BDLE is complete.
     
    19951837                             hdaWalClkGetCurrent(pThis)
    19961838                           + hdaR3StreamPeriodFramesToWalClk(pPeriod,
    1997                                                                pBDLE->Desc.u32BufSize
     1839                                                               hdaR3StreamDmaBufGetSize(pStreamShared)
    19981840                                                             / pStreamR3->State.Mapping.cbGuestFrame),
    19991841                           false /* fForce */);
     
    20081850             *                      Not doing this at the right time will result in ugly sound crackles!
    20091851             */
    2010             hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, pBDLE->Desc.u32BufSize);
     1852            hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaR3StreamDmaBufGetSize(pStreamShared));
    20111853
    20121854            /* Does the current BDLE require an interrupt to be sent? */
    2013             if (hdaR3BDLENeedsInterrupt(pBDLE))
     1855            if (hdaR3StreamDmaBufNeedsIrq(pStreamShared))
    20141856            {
    2015                 if (cSuppressIocs == 0)
     1857                /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL
     1858                   register is set we need to generate an interrupt. */
     1859                if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
    20161860                {
    2017                     /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL
    2018                        register is set we need to generate an interrupt. */
    2019                     if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
    2020                     {
    2021                         /* Assert the interrupt before actually fetching the next BDLE below. */
    2022                         if (!fInterruptSent)
    2023                         {
    2024                             pStreamShared->State.cTransferPendingInterrupts = 1;
    2025                             Log3Func(("[SD%RU8] Scheduling interrupt\n", uSD));
    2026 
    2027                             /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
    2028                              * ending / beginning of a period. */
    2029                             /** @todo r=bird: What does the above comment mean? */
    2030                             HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_BCIS;
    2031                             HDA_PROCESS_INTERRUPT(pDevIns, pThis);
    2032                             fInterruptSent = true;
    2033                         }
    2034                     }
     1861                    /* Assert the interrupt before actually fetching the next BDLE below. */
     1862                    pStreamShared->State.cTransferPendingInterrupts = 1;
     1863                    Log3Func(("[SD%RU8] Scheduling interrupt\n", uSD));
     1864
     1865                    /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
     1866                     * ending / beginning of a period. */
     1867                    /** @todo r=bird: What does the above comment mean? */
     1868                    HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_BCIS;
     1869                    HDA_PROCESS_INTERRUPT(pDevIns, pThis);
    20351870                }
    2036                 else
    2037                     cSuppressIocs--;
    20381871            }
    20391872
     
    20411874             * Advance to the next BDLE.
    20421875             */
    2043             if (pStreamShared->State.uCurBDLE >= pStreamShared->u16LVI)
    2044                 pStreamShared->State.uCurBDLE = 0;
    2045             else
    2046                 pStreamShared->State.uCurBDLE++;
    2047             hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
     1876            hdaR3StreamDmaBufAdvanceToNext(pStreamShared);
    20481877        }
    20491878        else
    2050             Log3Func(("[SD%RU8] Not complete %R[bdle]\n", uSD, pBDLE));
     1879            Log3Func(("[SD%RU8] Not completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x: off=%#RX32\n",
     1880                      uSD, pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
     1881                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
     1882                      pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, pStreamShared->State.offCurBdle));
    20511883    }
    20521884
     
    20851917     * Log and leave.
    20861918     */
    2087     Log3Func(("[SD%RU8] %R[bdle] -- %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
    2088               uSD, pBDLE, cbToProduce, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbToProduce,
     1919    Log3Func(("[SD%RU8] %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
     1920              uSD, cbToProduce, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbToProduce,
    20891921              pStreamShared->State.cTransferPendingInterrupts));
    20901922
     
    21531985        && AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink))
    21541986    {
    2155         uint64_t const tsNow          = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
    2156         uint64_t const tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;
     1987        /* Advance the schduling: */
     1988        uint32_t idxSched = pStreamShared->State.idxSchedule;
     1989        AssertStmt(idxSched < RT_ELEMENTS(pStreamShared->State.aSchedule), idxSched = 0);
     1990        uint32_t idxLoop  = pStreamShared->State.idxScheduleLoop + 1;
     1991        if (idxLoop >= pStreamShared->State.aSchedule[idxSched].cLoops)
     1992        {
     1993            idxSched += 1;
     1994            if (   idxSched > pStreamShared->State.cSchedule
     1995                || idxSched >= RT_ELEMENTS(pStreamShared->State.aSchedule) /*paranoia^2*/)
     1996            {
     1997                idxSched = pStreamShared->State.cSchedulePrologue;
     1998                AssertStmt(idxSched < RT_ELEMENTS(pStreamShared->State.aSchedule), idxSched = 0);
     1999            }
     2000            idxLoop = 0;
     2001        }
     2002        pStreamShared->State.idxScheduleLoop = (uint16_t)idxLoop;
     2003
     2004        /* Do the arcual timer arming. */
     2005        uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
     2006        uint64_t const tsTransferNext = tsNow + pStreamShared->State.aSchedule[idxSched].cPeriodTicks;
    21572007        Log3Func(("[SD%RU8] fSinkActive=true, tsTransferNext=%RU64 (in %RU64)\n",
    21582008                  pStreamShared->u8SD, tsTransferNext, tsTransferNext - tsNow));
    2159         pStreamShared->State.tsTransferNext = tsTransferNext; /* legacy */
    21602009        int rc = PDMDevHlpTimerSet(pDevIns, pStreamShared->hTimer, tsTransferNext);
    21612010        AssertRC(rc);
     2011
     2012        /* Some legacy stuff: */
     2013        pStreamShared->State.tsTransferNext = tsTransferNext;
     2014        pStreamShared->State.cbTransferSize = pStreamShared->State.aSchedule[idxSched].cbPeriod;
    21622015
    21632016        return tsNow;
     
    21952048                       PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fInTimer)
    21962049{
    2197     if (!pStreamShared)
     2050    int rc2;
     2051
     2052    /*
     2053     * Make sure we're running and got an active mixer sink.
     2054     */
     2055    if (RT_LIKELY(pStreamShared->State.fRunning))
     2056    { /* likely */ }
     2057    else
    21982058        return;
    21992059
     
    22012061    if (pStreamR3->pMixSink)
    22022062        pSink = pStreamR3->pMixSink->pMixSink;
    2203 
    2204     if (!AudioMixerSinkIsActive(pSink)) /* No sink available? Bail out. */
     2063    if (RT_LIKELY(AudioMixerSinkIsActive(pSink)))
     2064    { /* likely */ }
     2065    else
    22052066        return;
    22062067
    2207     const uint64_t tsNowNs = RTTimeNanoTS();
    2208 
    2209     int rc2;
    2210 
    2211     if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
     2068    /*
     2069     * Get scheduling info common to both input and output streams.
     2070     */
     2071    const uint64_t tsNowNs  = RTTimeNanoTS();
     2072    uint32_t       idxSched = pStreamShared->State.idxSchedule;
     2073    AssertStmt(idxSched < RT_MIN(RT_ELEMENTS(pStreamShared->State.aSchedule), pStreamShared->State.cSchedule), idxSched = 0);
     2074    uint32_t const cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
     2075
     2076    /*
     2077     * Output streams (SDO).
     2078     */
     2079    if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT)
    22122080    {
    22132081        bool fDoRead; /* Whether to push data down the driver stack or not.  */
     
    22212089             */
    22222090            uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
    2223             if (cbStreamFree >= pStreamShared->State.cbTransferSize)
     2091            if (cbStreamFree >= cbPeriod)
    22242092            { /* likely */ }
    22252093            else
     
    22272095                STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems);
    22282096                Log(("hdaR3StreamUpdate: Warning! Stream #%u has insufficient space free: %u bytes, need %u.  Will try move data out of the buffer...\n",
    2229                      pStreamShared->u8SD, cbStreamFree, pStreamShared->State.cbTransferSize));
     2097                     pStreamShared->u8SD, cbStreamFree, cbPeriod));
    22302098# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    22312099                int rc = RTCritSectTryEnter(&pStreamR3->State.AIO.CritSect);
     
    22432111
    22442112                cbStreamFree = hdaR3StreamGetFree(pStreamR3);
    2245                 if (cbStreamFree < pStreamShared->State.cbTransferSize)
     2113                if (cbStreamFree < cbPeriod)
    22462114                {
    22472115                    /* Unable to make sufficient space.  Drop the whole buffer content.
     
    22702138
    22712139            uint64_t const offWriteBefore = pStreamR3->State.offWrite;
    2272             rc2 = hdaR3StreamDoDmaOutput(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, cbStreamFree, tsNowNs);
     2140            rc2 = hdaR3StreamDoDmaOutput(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, RT_MIN(cbStreamFree, cbPeriod), tsNowNs);
    22732141            AssertRC(rc2);
    22742142
     
    22912159            if (!pStreamShared->State.tsAioDelayEnd)
    22922160                fDoRead = pStreamR3->State.offWrite > offWriteBefore
    2293                        || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbTransferSize * 2;
     2161                       || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2;
    22942162            else if (PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer) >= pStreamShared->State.tsAioDelayEnd)
    22952163            {
     
    22982166                fDoRead = true;
    22992167            }
    2300             else if (hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbTransferSize * 2)
     2168            else if (hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2)
    23012169            {
    23022170                Log3Func(("Initial delay done: Passed running short on buffer.\n"));
     
    23132181                      (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS,
    23142182                      pStreamShared->State.Cfg.Device.cMsSchedulingHint, cbStreamFree,
    2315                       pStreamShared->State.cbTransferSize * 2, fDoRead));
     2183                      pStreamShared->State.cbAvgTransfer * 2, fDoRead));
    23162184
    23172185            if (fDoRead)
     
    23392207            hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, tsNowNs);
    23402208    }
    2341     else /* Input (SDI). */
    2342     {
     2209    /*
     2210     * Input stream (SDI).
     2211     */
     2212    else
     2213    {
     2214        /** @todo Re-org this. Like in the output case, the timer thread should try do
     2215         *        AIO work if we have an underrun. */
    23432216# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    23442217        if (!fInTimer)
     
    23952268        {
    23962269# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    2397             if (tsNowNs - pStreamShared->State.tsLastReadNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
     2270            if (   tsNowNs - pStreamShared->State.tsLastReadNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS
     2271                || hdaR3StreamGetUsed(pStreamR3) < RT_MAX(cbPeriod, pStreamShared->State.cbAvgTransfer))
    23982272            {
    23992273                Log5Func(("Notifying AIO thread\n"));
     
    24072281            if (cbStreamUsed)
    24082282            {
    2409                 rc2 = hdaR3StreamTransfer(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, cbStreamUsed);
     2283                rc2 = hdaR3StreamDoDmaInput(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, RT_MIN(cbStreamUsed, cbPeriod));
    24102284                AssertRC(rc2);
    24112285            }
  • trunk/src/VBox/Devices/Audio/HDAStream.h

    r88112 r88137  
    114114typedef struct HDASTREAMSTATE
    115115{
    116     /** Current BDLE to use. Wraps around to 0 if
    117      *  maximum (cBDLE) is reached. */
    118     uint16_t                uCurBDLE;
    119116    /** Flag indicating whether this stream currently is
    120117     *  in reset mode and therefore not acccessible by the guest. */
     
    122119    /** Flag indicating if the stream is in running state or not. */
    123120    volatile bool           fRunning;
     121    /** The stream's I/O timer Hz rate. */
     122    uint16_t                uTimerIoHz;
     123    /** How many interrupts are pending due to
     124     *  BDLE interrupt-on-completion (IOC) bits set. */
     125    uint8_t                 cTransferPendingInterrupts;
    124126    /** Unused, padding. */
    125     uint8_t                 abPadding0[4];
    126     /** Current BDLE (Buffer Descriptor List Entry). */
    127     HDABDLE                 BDLE;
     127    uint8_t                 abPadding1[3];
    128128    /** Timestamp (absolute, in timer ticks) of the last DMA data transfer. */
    129129    uint64_t                tsTransferLast;
     
    135135     * @note This is in host side frames, in case we're doing any mapping. */
    136136    uint32_t                cbTransferSize;
    137     /** Unused, same as cbTransferSize. */
    138     uint32_t                cbTransferChunk;
    139     /** How many interrupts are pending due to
    140      *  BDLE interrupt-on-completion (IOC) bits set. */
    141     uint8_t                 cTransferPendingInterrupts;
    142     uint8_t                 abPadding2[7];
    143     /** The stream's I/O timer Hz rate. */
    144     uint16_t                uTimerIoHz;
    145     /** Number of audio data frames for the position adjustment.
    146      *  0 if no position adjustment is needed. */
    147     uint16_t                cfPosAdjustDefault;
    148     /** How many audio data frames are left to be processed
    149      *  for the position adjustment handling.
    150      *
    151      *  0 if position adjustment handling is done or inactive. */
    152     uint16_t                cfPosAdjustLeft;
    153     uint16_t                u16Padding3;
    154     /** (Virtual) clock ticks per byte. */
    155     uint64_t                cTicksPerByte;
    156     /** (Virtual) clock ticks per transfer. */
    157     uint64_t                cTransferTicks;
     137    /** The size of an average transfer. */
     138    uint32_t                cbAvgTransfer;
    158139    /** The stream's period. Need for timing. */
    159140    HDASTREAMPERIOD         Period;
     
    175156    /** The start time for the playback (on the timer clock). */
    176157    uint64_t                tsStart;
     158
     159    /** @name DMA engine
     160     * @{ */
     161    /** The offset into the current BDLE. */
     162    uint32_t                offCurBdle;
     163    /** LVI + 1 */
     164    uint16_t                cBdles;
     165    /** The index of the current BDLE.
     166     * This is the entry which period is currently "running" on the DMA timer.  */
     167    uint8_t                 idxCurBdle;
     168    /** The number of prologue scheduling steps.
     169     * This is used when the tail BDLEs doesn't have IOC set.  */
     170    uint8_t                 cSchedulePrologue;
     171    /** Number of scheduling steps. */
     172    uint16_t                cSchedule;
     173    /** Current scheduling step. */
     174    uint16_t                idxSchedule;
     175    /** Current loop number within the current scheduling step.  */
     176    uint32_t                idxScheduleLoop;
     177
     178    /** Buffer descriptors and additional timer scheduling state.
     179     * (Same as HDABDLEDESC, with more sensible naming.)  */
     180    struct
     181    {
     182        /** The buffer address. */
     183        uint64_t            GCPhys;
     184        /** The buffer size (guest bytes). */
     185        uint32_t            cb;
     186        /** The flags (only bit 0 is defined).    */
     187        uint32_t            fFlags;
     188    }                       aBdl[256];
     189    /** Scheduling steps. */
     190    struct
     191    {
     192        /** Number of timer ticks per period.
     193         * ASSUMES that we don't need a full second and that the timer resolution
     194         * isn't much higher than nanoseconds. */
     195        uint32_t            cPeriodTicks;
     196        /** The period length in host bytes. */
     197        uint32_t            cbPeriod;
     198        /** Number of times to repeat the period. */
     199        uint32_t            cLoops;
     200        /** The BDL index of the first entry.   */
     201        uint8_t             idxFirst;
     202        /** The number of BDL entries. */
     203        uint8_t             cEntries;
     204        uint8_t             abPadding[2];
     205    }                       aSchedule[512+8];
     206    /** @} */
    177207} HDASTREAMSTATE;
    178208AssertCompileSizeAlignment(HDASTREAMSTATE, 8);
     209AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 16);
     210AssertCompileMemberAlignment(HDASTREAMSTATE, aSchedule, 16);
    179211
    180212/**
Note: See TracChangeset for help on using the changeset viewer.

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