Changeset 88137 in vbox for trunk/src/VBox/Devices/Audio
- Timestamp:
- Mar 16, 2021 12:22:05 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 143285
- Location:
- trunk/src/VBox/Devices/Audio
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DevHDA.cpp
r88112 r88137 250 250 #endif 251 251 } HDADRIVER; 252 253 254 /** Internal state of this BDLE. 255 * Not part of the actual BDLE registers. 256 * @note Only for saved state. */ 257 typedef 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 */ 273 typedef struct HDABDLELEGACY 274 { 275 /** The actual BDL description. */ 276 HDABDLEDESC Desc; 277 HDABDLESTATELEGACY State; 278 } HDABDLELEGACY; 279 AssertCompileSize(HDABDLELEGACY, 32); 252 280 253 281 … … 469 497 static SSMFIELD const g_aSSMBDLEStateFields6[] = 470 498 { 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), 475 503 SSMFIELD_ENTRY_TERM() 476 504 }; … … 479 507 static SSMFIELD const g_aSSMBDLEStateFields7[] = 480 508 { 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), 484 512 SSMFIELD_ENTRY_TERM() 485 513 }; … … 489 517 { 490 518 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 */ 492 520 SSMFIELD_ENTRY_OLD(fStop, 1), /* Deprecated; see SSMR3PutBool(). */ 493 521 SSMFIELD_ENTRY_OLD(fRunning, 1), /* Deprecated; using the HDA_SDCTL_RUN bit is sufficient. */ … … 499 527 static SSMFIELD const g_aSSMStreamStateFields7[] = 500 528 { 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. */ 502 531 SSMFIELD_ENTRY(HDASTREAMSTATE, fInReset), 503 532 SSMFIELD_ENTRY(HDASTREAMSTATE, tsTransferNext), … … 518 547 static SSMFIELD const g_aSSMStreamBdleFields1234[] = 519 548 { 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 */ 528 557 SSMFIELD_ENTRY_TERM() 529 558 }; … … 3354 3383 if (RT_SUCCESS(rc)) 3355 3384 { 3356 PHDABDLE pState = (PHDABDLE)pvStruct;3385 HDABDLELEGACY *pState = (HDABDLELEGACY *)pvStruct; 3357 3386 pState->Desc.fFlags = fIoc ? HDA_BDLE_F_IOC : 0; 3358 3387 } … … 3379 3408 AssertRCReturn(rc, rc); 3380 3409 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); 3383 3413 AssertRCReturn(rc, rc); 3384 3414 3385 rc = pHlp->pfnSSMPutStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(pStreamShared->State.BDLE.State),3386 3415 HDABDLESTATELEGACY TmpState = { pStreamShared->State.idxCurBdle, 0, pStreamShared->State.offCurBdle, 0 }; 3416 rc = pHlp->pfnSSMPutStructEx(pSSM, &TmpState, sizeof(TmpState), 0 /*fFlags*/, g_aSSMBDLEStateFields7, NULL); 3387 3417 AssertRCReturn(rc, rc); 3388 3418 … … 3477 3507 3478 3508 /** 3479 * Does required post processing when loading asaved state.3509 * Does tail processing after having loaded our saved state. 3480 3510 * 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 */ 3518 static int hdaR3LoadExecTail(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PSSMHANDLE pSSM) 3486 3519 { 3487 3520 int rc = VINF_SUCCESS; /** @todo r=bird: Really, what's the point of this? */ … … 3523 3556 hdaR3StreamRegisterDMAHandlers(pThis, pStreamShared); 3524 3557 #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++) 3532 3569 { 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 } 3538 3598 } 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); 3539 3622 3540 3623 /* Also keep track of the currently active streams. */ … … 3554 3637 * @param pThis Pointer to the shared HDA state. 3555 3638 * @param pThisCC Pointer to the ring-3 HDA state. 3556 * @param pSSM Pointer to SSMhandle.3639 * @param pSSM The saved state handle. 3557 3640 * @param uVersion Saved state version to load. 3558 3641 */ … … 3645 3728 /* Note 2: The stream's saving order is/was fixed, so don't touch! */ 3646 3729 3730 HDABDLELEGACY BDLE; 3731 3647 3732 /* Output */ 3648 3733 PHDASTREAM pStreamShared = &pThis->aStreams[4]; 3649 3734 rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[4], 4 /* Stream descriptor, hardcoded */); 3650 3735 AssertRCReturn(rc, rc); 3651 rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE, sizeof(pStreamShared->State.BDLE),3652 3736 RT_ZERO(BDLE); 3737 rc = pHlp->pfnSSMGetStructEx(pSSM, &BDLE, sizeof(BDLE), 0 /* fFlags */, g_aSSMStreamBdleFields1234, pDevIns); 3653 3738 AssertRCReturn(rc, rc); 3654 pStreamShared->State. uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;3739 pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */ 3655 3740 3656 3741 /* Microphone-In */ … … 3658 3743 rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[2], 2 /* Stream descriptor, hardcoded */); 3659 3744 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); 3662 3746 AssertRCReturn(rc, rc); 3663 pStreamShared->State. uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;3747 pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */ 3664 3748 3665 3749 /* Line-In */ … … 3667 3751 rc = hdaR3StreamSetUp(pDevIns, pThis, pStreamShared, &pThisCC->aStreams[0], 0 /* Stream descriptor, hardcoded */); 3668 3752 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); 3671 3754 AssertRCReturn(rc, rc); 3672 pStreamShared->State. uCurBDLE = pStreamShared->State.BDLE.State.u32BDLIndex;3755 pStreamShared->State.idxCurBdle = (uint8_t)BDLE.State.u32BDLIndex; /* not necessary */ 3673 3756 break; 3674 3757 } … … 3731 3814 rc = pHlp->pfnSSMGetStructEx(pSSM, &Tmp, sizeof(Tmp), 0 /* fFlags */, g_aV5State1Fields, NULL); 3732 3815 AssertRCReturn(rc, rc); 3733 pStreamShared->State. uCurBDLE = Tmp.uCurBDLE;3816 pStreamShared->State.idxCurBdle = (uint8_t)Tmp.uCurBDLE; /* not necessary */ 3734 3817 3735 3818 for (uint16_t a = 0; a < Tmp.cBLDEs; a++) … … 3744 3827 rc = pHlp->pfnSSMGetStructEx(pSSM, &Tmp, sizeof(Tmp), 0 /* fFlags */, g_aV5State2Fields, NULL); 3745 3828 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 }3751 3829 } 3752 3830 } … … 3757 3835 AssertRCReturn(rc, rc); 3758 3836 3759 rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.Desc, sizeof(pStreamShared->State.BDLE.Desc),3760 3837 HDABDLEDESC IgnDesc; 3838 rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnDesc, sizeof(IgnDesc), 0 /* fFlags */, g_aSSMBDLEDescFields6, pDevIns); 3761 3839 AssertRCReturn(rc, rc); 3762 3840 3763 rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(HDABDLESTATE),3764 3841 HDABDLESTATELEGACY IgnState; 3842 rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnState, sizeof(IgnState), 0 /* fFlags */, g_aSSMBDLEStateFields6, NULL); 3765 3843 AssertRCReturn(rc, rc); 3766 3844 … … 3807 3885 rc = hdaR3LoadExecLegacy(pDevIns, pThis, pThisCC, pSSM, uVersion); 3808 3886 if (RT_SUCCESS(rc)) 3809 rc = hdaR3LoadExec Post(pDevIns, pThis, pThisCC);3887 rc = hdaR3LoadExecTail(pDevIns, pThis, pThisCC, pSSM); 3810 3888 return rc; 3811 3889 } … … 3897 3975 /* 3898 3976 * Load BDLEs (Buffer Descriptor List Entries) and DMA counters. 3977 * Obsolete. Derived from LPID now. 3899 3978 */ 3900 rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.Desc, sizeof(HDABDLEDESC),3901 3979 HDABDLEDESC IgnDesc; 3980 rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnDesc, sizeof(IgnDesc), 0 /* fFlags */, g_aSSMBDLEDescFields7, NULL); 3902 3981 AssertRCReturn(rc, rc); 3903 3982 3904 rc = pHlp->pfnSSMGetStructEx(pSSM, &pStreamShared->State.BDLE.State, sizeof(HDABDLESTATE),3905 3983 HDABDLESTATELEGACY IgnState; 3984 rc = pHlp->pfnSSMGetStructEx(pSSM, &IgnState, sizeof(IgnState), 0 /* fFlags */, g_aSSMBDLEStateFields7, NULL); 3906 3985 AssertRCReturn(rc, rc); 3907 3986 3908 Log2Func(("[SD%RU8] %R[bdle]\n", pStreamShared->u8SD, &pStreamShared->State.BDLE));3987 Log2Func(("[SD%RU8]\n", pStreamShared->u8SD)); 3909 3988 3910 3989 /* … … 3977 4056 } /* for cStreams */ 3978 4057 3979 rc = hdaR3LoadExec Post(pDevIns, pThis, pThisCC);4058 rc = hdaR3LoadExecTail(pDevIns, pThis, pThisCC, pSSM); 3980 4059 AssertRC(rc); 3981 4060 … … 3988 4067 * IPRT format type handlers * 3989 4068 *********************************************************************************************************************************/ 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 }4006 4069 4007 4070 /** … … 4115 4178 static void hdaR3DbgPrintStream(PHDASTATE pThis, PCDBGFINFOHLP pHlp, int idxStream) 4116 4179 { 4117 const PHDASTREAM pStream = &pThis->aStreams[idxStream];4118 4180 4119 4181 pHlp->pfnPrintf(pHlp, "Stream #%d:\n", idxStream); … … 4122 4184 pHlp->pfnPrintf(pHlp, " SD%dFIFOS: %R[sdfifos]\n", idxStream, HDA_STREAM_REG(pThis, FIFOS, idxStream)); 4123 4185 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. */ 4194 static void hdaR3DbgPrintBDL(PPDMDEVINS pDevIns, PHDASTATE pThis, PCDBGFINFOHLP pHlp, int idxStream) 4129 4195 { 4130 4196 const PHDASTREAM pStream = &pThis->aStreams[idxStream]; 4131 4132 pHlp->pfnPrintf(pHlp, "Stream #%d BDLE:\n", idxStream);4133 4197 4134 4198 uint64_t const u64BaseDMA = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, idxStream), … … 4136 4200 uint16_t const u16LVI = HDA_STREAM_REG(pThis, LVI, idxStream); 4137 4201 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)); 4141 4211 if (!u64BaseDMA) 4142 4212 return; 4143 4213 4144 uint32_t cbTotal = 0; 4214 /* 4215 * The BDL: 4216 */ 4217 uint64_t cbTotal = 0; 4145 4218 for (uint16_t i = 0; i < u16LVI + 1; i++) 4146 4219 { 4147 /** @todo shouldn't this be a PCI read? */4148 4220 HDABDLEDESC bd = {0, 0, 0}; 4149 PDMDevHlpP hysRead(pDevIns, u64BaseDMA + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));4221 PDMDevHlpPCIPhysRead(pDevIns, u64BaseDMA + i * sizeof(HDABDLEDESC), &bd, sizeof(bd)); 4150 4222 4151 4223 char szFlags[64]; … … 4153 4225 if (bd.fFlags & ~HDA_BDLE_F_IOC) 4154 4226 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 } 4158 4238 4159 4239 cbTotal += bd.u32BufSize; 4160 4240 } 4161 pHlp->pfnPrintf(pHlp, " Total: % RU32 bytes\n", cbTotal);4241 pHlp->pfnPrintf(pHlp, " Total: %#RX64 bytes (%RU64)\n", cbTotal, cbTotal); 4162 4242 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 */ 4165 4262 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), 4167 4264 pThis->fDMAPosition ? "enabled" : "disabled"); 4168 4265 if (uDPBase) 4169 4266 for (uint16_t i = 0; i < u16LVI + 1; i++) 4170 4267 { 4171 /** @todo shouldn't this be a PCI read? */4172 4268 struct 4173 4269 { … … 4175 4271 uint32_t uReserved; 4176 4272 } Buf = { 0 , 0 }; 4177 PDMDevHlpP hysRead(pDevIns, uDPBase + i * sizeof(Buf), &Buf, sizeof(Buf));4273 PDMDevHlpPCIPhysRead(pDevIns, uDPBase + i * sizeof(Buf), &Buf, sizeof(Buf)); 4178 4274 4179 4275 char szReserved[64]; … … 4187 4283 } 4188 4284 4189 /** Used by hdaR3DbgInfoStream and hdaR3DbgInfoBDL E. */4285 /** Used by hdaR3DbgInfoStream and hdaR3DbgInfoBDL. */ 4190 4286 static int hdaR3DbgLookupStrmIdx(PCDBGFINFOHLP pHlp, const char *pszArgs) 4191 4287 { … … 4218 4314 * @callback_method_impl{FNDBGFHANDLERDEV, hdabdle} 4219 4315 */ 4220 static DECLCALLBACK(void) hdaR3DbgInfoBDL E(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)4316 static DECLCALLBACK(void) hdaR3DbgInfoBDL(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs) 4221 4317 { 4222 4318 PHDASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PHDASTATE); 4223 4319 int idxStream = hdaR3DbgLookupStrmIdx(pHlp, pszArgs); 4224 4320 if (idxStream != -1) 4225 hdaR3DbgPrintBDL E(pDevIns, pThis, pHlp, idxStream);4321 hdaR3DbgPrintBDL(pDevIns, pThis, pHlp, idxStream); 4226 4322 else 4227 4323 for (idxStream = 0; idxStream < HDA_MAX_STREAMS; ++idxStream) 4228 hdaR3DbgPrintBDL E(pDevIns, pThis, pHlp, idxStream);4324 hdaR3DbgPrintBDL(pDevIns, pThis, pHlp, idxStream); 4229 4325 } 4230 4326 … … 5077 5173 */ 5078 5174 PDMDevHlpDBGFInfoRegister(pDevIns, "hda", "HDA registers. (hda [register case-insensitive])", hdaR3DbgInfo); 5079 PDMDevHlpDBGFInfoRegister(pDevIns, "hdabdl e",5080 "HDA buffer descriptor list (BDL E) and DMA stream positions. (hdabdle[stream number])",5081 hdaR3DbgInfoBDL E);5175 PDMDevHlpDBGFInfoRegister(pDevIns, "hdabdl", 5176 "HDA buffer descriptor list (BDL) and DMA stream positions. (hdabdl [stream number])", 5177 hdaR3DbgInfoBDL); 5082 5178 PDMDevHlpDBGFInfoRegister(pDevIns, "hdastream", "HDA stream info. (hdastream [stream number])", hdaR3DbgInfoStream); 5083 5179 PDMDevHlpDBGFInfoRegister(pDevIns, "hdcnodes", "HDA codec nodes.", hdaR3DbgInfoCodecNodes); … … 5085 5181 PDMDevHlpDBGFInfoRegister(pDevIns, "hdamixer", "HDA mixer state.", hdaR3DbgInfoMixer); 5086 5182 5087 rc = RTStrFormatTypeRegister("bdle", hdaR3StrFmtBDLE, NULL);5088 AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc);5089 5183 rc = RTStrFormatTypeRegister("sdctl", hdaR3StrFmtSDCTL, NULL); 5090 5184 AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc); 5091 5185 rc = RTStrFormatTypeRegister("sdsts", hdaR3StrFmtSDSTS, NULL); 5092 5186 AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc); 5187 /** @todo the next two are rather pointless. */ 5093 5188 rc = RTStrFormatTypeRegister("sdfifos", hdaR3StrFmtSDFIFOS, NULL); 5094 5189 AssertMsgReturn(RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS, ("%Rrc\n", rc), rc); -
trunk/src/VBox/Devices/Audio/DevHDACommon.cpp
r88080 r88137 365 365 } 366 366 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_SILENCE391 uint64_t csSilence = 0;392 393 pStreamR3->Dbg.cSilenceThreshold = 100;394 pStreamR3->Dbg.cbSilenceReadMin = _1M;395 # endif396 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_SILENCE407 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 else413 break;414 pu16Buf++;415 }416 # endif417 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))418 { /* likely */ }419 else420 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_SILENCE432 if (csSilence)433 pStreamR3->Dbg.csSilence += csSilence;434 435 if ( csSilence == 0436 && pStreamR3->Dbg.csSilence > pStreamR3->Dbg.cSilenceThreshold437 && 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 # endif443 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 else484 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 else504 LogFunc(("Failed with %Rrc\n", rc));505 506 return rc;507 }508 509 367 #endif /* IN_RING3 */ 510 368 … … 673 531 # endif /* LOG_ENABLED */ 674 532 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 744 533 #endif /* IN_RING3 */ -
trunk/src/VBox/Devices/Audio/DevHDACommon.h
r88080 r88137 547 547 548 548 /** 549 * Internal state of a Buffer Descriptor List Entry (BDLE),550 * needed to keep track of the data needed for the actual device551 * emulation.552 */553 typedef struct HDABDLESTATE554 {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 /**566 549 * BDL description structure. 567 550 * Do not touch this, as this must match to the HDA specs. … … 582 565 AssertCompileSize(HDABDLEDESC, 16); /* Always 16 byte. Also must be aligned on 128-byte boundary. */ 583 566 584 /**585 * Buffer Descriptor List Entry (BDLE) (3.6.3).586 */587 typedef struct HDABDLE588 {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;598 567 599 568 /** @name Object lookup functions. … … 638 607 /** @} */ 639 608 640 /** @name DMA utility functions.641 * @{642 */643 #ifdef IN_RING3644 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 #endif649 /** @} */650 651 609 /** @name Register functions. 652 610 * @{ … … 665 623 void hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE); 666 624 # endif 667 int hdaR3BDLEFetch(PPDMDEVINS pDevIns, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry);668 bool hdaR3BDLEIsComplete(PHDABDLE pBDLE);669 bool hdaR3BDLENeedsInterrupt(PHDABDLE pBDLE);670 625 #endif /* IN_RING3 */ 671 626 /** @} */ -
trunk/src/VBox/Devices/Audio/HDAStream.cpp
r88113 r88137 221 221 LogFlowFuncLeave(); 222 222 } 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 */ 238 static 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 */ 330 static 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 223 452 224 453 /** … … 346 575 PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->u32CBL))); 347 576 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; 350 591 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); 353 618 354 619 /* 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; 424 656 425 657 /* … … 429 661 /* Assign the global device rate to the stream I/O timer as default. */ 430 662 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 constant436 * (virtual) rate which we expose to the guest.437 *438 * Data rate examples:439 * * Windows 10 @ 44,1kHz / 16-bit440 * 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. */455 663 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz, 456 664 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD), 457 665 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 == cbTransferHeuristics496 || !cbTransferHeuristics)497 cbTransferHeuristics = cbTransferHeuristicsCur;498 else499 {500 /** @todo r=bird: you need to find the smallest common denominator here, not501 * just the minimum. Ignoring this for now as windows has two equal502 * 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 total515 * buffer length in half and use that as timer heuristics. That gives the516 * 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 guest538 want to play the whole "Der Ring des Nibelungen" cycle in one go. Just539 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));571 666 572 667 /* 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. */ 573 671 pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz; 574 672 LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint)); 575 673 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); 594 748 if (RT_SUCCESS(rc)) 595 749 { 596 750 /* 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'). 601 753 */ 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); 728 755 } 729 756 } … … 734 761 #ifdef VBOX_WITH_DTRACE 735 762 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); 737 765 #endif 738 766 return rc; … … 790 818 pStreamR3->pMixSink = hdaR3GetDefaultSink(pThisCC, uSD); 791 819 792 /* Reset position adjustment counter. */793 pStreamShared->State.cfPosAdjustLeft = pStreamShared->State.cfPosAdjustDefault;794 795 820 /* Reset transfer stuff. */ 796 821 pStreamShared->State.cTransferPendingInterrupts = 0; … … 804 829 pStreamShared->State.tsStart = 0; 805 830 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; 808 840 809 841 if (pStreamR3->State.pCircBuf) … … 1148 1180 1149 1181 /** 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 */ 1188 DECLINLINE(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 */ 1207 DECLINLINE(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 */ 1221 DECLINLINE(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 */ 1239 DECLINLINE(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 */ 1251 DECLINLINE(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. 1157 1269 * 1158 1270 * @returns IPRT status code. … … 1164 1276 * @param cbToProcessMax How much data (in bytes) to process as maximum. 1165 1277 */ 1166 static int hdaR3Stream Transfer(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared,1167 PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax)1278 static int hdaR3StreamDoDmaInput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared, 1279 PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax) 1168 1280 { 1169 1281 uint8_t const uSD = pStreamShared->u8SD; 1170 1282 LogFlowFunc(("ENTER - #%u cbToProcessMax=%#x\n", uSD, cbToProcessMax)); 1283 Assert(hdaGetDirFromSD(uSD) != PDMAUDIODIR_OUT); 1171 1284 1172 1285 if (RT_LIKELY(cbToProcessMax >= pStreamShared->State.cbTransferSize)) … … 1174 1287 else 1175 1288 { 1176 Assert(hdaGetDirFromSD(uSD) != PDMAUDIODIR_OUT /* Handled by caller */);1177 1289 /** @todo account for this or something so we can try get back in sync 1178 1290 * later... */ … … 1243 1355 /* Transfer sanity checks. */ 1244 1356 Assert(pStreamShared->State.cbTransferSize); 1245 Assert(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize);1246 1357 1247 1358 int rc = VINF_SUCCESS; 1248 1359 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; 1258 1361 1259 1362 Assert(cbToProcess); /* Nothing to process when there should be data. Accounting bug? */ … … 1287 1390 1288 1391 /* 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); 1295 1395 1296 1396 if (!cbChunk) 1297 1397 break; 1298 1398 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 */ 1300 1405 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf; 1301 1406 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) 1304 1413 { 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); 1322 1422 #ifdef VBOX_WITH_DTRACE 1323 1423 VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offRead); 1324 1424 #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); 1349 1433 } 1350 else if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT) /* Output (SDO). */ 1434 1435 if (cbDMAToWrite) 1351 1436 { 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; 1509 1442 } 1510 1443 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)) 1515 1466 { 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)); 1537 1471 1538 1472 /* Make sure to also update the wall clock when a BDLE is complete. … … 1541 1475 hdaWalClkGetCurrent(pThis) 1542 1476 + hdaR3StreamPeriodFramesToWalClk(pPeriod, 1543 pBDLE->Desc.u32BufSize1477 hdaR3StreamDmaBufGetSize(pStreamShared) 1544 1478 / pStreamR3->State.Mapping.cbGuestFrame), 1545 1479 false /* fForce */); … … 1553 1487 * Not doing this at the right time will result in ugly sound crackles! 1554 1488 */ 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)) 1565 1493 { 1566 1494 /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set … … 1573 1501 { 1574 1502 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)); 1581 1504 1582 1505 /* … … 1602 1525 } 1603 1526 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); 1613 1528 } 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)); 1621 1534 } 1622 1535 … … 1628 1541 Assert(cbLeft == 0); 1629 1542 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))); 1637 1545 1638 1546 const bool fTransferComplete = cbLeft == 0; … … 1661 1569 } 1662 1570 1663 /* Set the next transfer timing slot.1664 * This must happen at a constant rate. */1665 pStreamShared->State.tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;1666 1667 1571 /* Always update this timestamp, no matter what pStreamShared->State.tsTransferNext is. */ 1668 1572 pStreamShared->State.tsTransferLast = tsNow; 1669 1573 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, 1671 1575 (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT ? pStreamR3->State.offWrite : pStreamR3->State.offRead) - cbProcessed)); 1672 1576 Log3Func(("[SD%RU8] fTransferComplete=%RTbool, cTransferPendingInterrupts=%RU8\n", 1673 1577 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));1677 1578 1678 1579 LogFlowFuncLeave(); … … 1697 1598 * @param pStreamR3 HDA stream to update (ring-3). 1698 1599 * @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. 1703 1603 * @param tsNowNs The current RTTimeNano() value. 1704 1604 * … … 1751 1651 Assert(ASMAtomicReadBool(&pStreamShared->State.fRunning)); 1752 1652 1753 /* Transfer sanity checks. */1754 Assert(pStreamShared->State.cbTransferSize);1755 Assert(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize);1756 1757 1653 /* 1758 1654 * Some timestamp stuff for logging/debugging. … … 1764 1660 1765 1661 /* 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 */ 1825 1664 /** @todo r=bird: I don't get the HDA_SDSTS_FIFORDY logic. Unless we're 1826 1665 * assuming SMP guest and that it can get stream registers while we're … … 1838 1677 uint32_t cbBounce = 0; /* in case of incomplete frames between buffer segments */ 1839 1678 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 1840 1683 while (cbLeft > 0) 1841 1684 { … … 1845 1688 * Figure out how much we can read & write in this iteration. 1846 1689 */ 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); 1849 1692 1850 1693 /* Need to diverge if the frame format differs. */ … … 1860 1703 * Read the guest data directly into the internal DMA buffer. 1861 1704 */ 1862 RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;1863 1705 while (cbChunk > 0) 1864 1706 { … … 1885 1727 1886 1728 /* 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; 1892 1733 } 1893 1734 } … … 1914 1755 * Loop till we've covered the chunk. 1915 1756 */ 1916 RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;1917 1757 Log5Func(("loop0: GCPhys=%RGp cbChunk=%#x + cbBounce=%#x\n", GCPhys, cbChunk, cbBounce)); 1918 1758 while (cbChunk > 0) … … 1969 1809 1970 1810 /* 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; 1975 1814 if (cbRemainder) 1976 1815 memmove(&abBounce[0], &abBounce[cbBounce - cbRemainder], cbRemainder); … … 1985 1824 * Is the buffer descriptor complete. 1986 1825 */ 1987 if (hdaR3 BDLEIsComplete(pBDLE))1826 if (hdaR3StreamDmaBufIsComplete(pStreamShared)) 1988 1827 { 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)); 1990 1832 1991 1833 /* Make sure to also update the wall clock when a BDLE is complete. … … 1995 1837 hdaWalClkGetCurrent(pThis) 1996 1838 + hdaR3StreamPeriodFramesToWalClk(pPeriod, 1997 pBDLE->Desc.u32BufSize1839 hdaR3StreamDmaBufGetSize(pStreamShared) 1998 1840 / pStreamR3->State.Mapping.cbGuestFrame), 1999 1841 false /* fForce */); … … 2008 1850 * Not doing this at the right time will result in ugly sound crackles! 2009 1851 */ 2010 hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, pBDLE->Desc.u32BufSize);1852 hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaR3StreamDmaBufGetSize(pStreamShared)); 2011 1853 2012 1854 /* Does the current BDLE require an interrupt to be sent? */ 2013 if (hdaR3 BDLENeedsInterrupt(pBDLE))1855 if (hdaR3StreamDmaBufNeedsIrq(pStreamShared)) 2014 1856 { 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) 2016 1860 { 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); 2035 1870 } 2036 else2037 cSuppressIocs--;2038 1871 } 2039 1872 … … 2041 1874 * Advance to the next BDLE. 2042 1875 */ 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); 2048 1877 } 2049 1878 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)); 2051 1883 } 2052 1884 … … 2085 1917 * Log and leave. 2086 1918 */ 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, 2089 1921 pStreamShared->State.cTransferPendingInterrupts)); 2090 1922 … … 2153 1985 && AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink)) 2154 1986 { 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; 2157 2007 Log3Func(("[SD%RU8] fSinkActive=true, tsTransferNext=%RU64 (in %RU64)\n", 2158 2008 pStreamShared->u8SD, tsTransferNext, tsTransferNext - tsNow)); 2159 pStreamShared->State.tsTransferNext = tsTransferNext; /* legacy */2160 2009 int rc = PDMDevHlpTimerSet(pDevIns, pStreamShared->hTimer, tsTransferNext); 2161 2010 AssertRC(rc); 2011 2012 /* Some legacy stuff: */ 2013 pStreamShared->State.tsTransferNext = tsTransferNext; 2014 pStreamShared->State.cbTransferSize = pStreamShared->State.aSchedule[idxSched].cbPeriod; 2162 2015 2163 2016 return tsNow; … … 2195 2048 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fInTimer) 2196 2049 { 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 2198 2058 return; 2199 2059 … … 2201 2061 if (pStreamR3->pMixSink) 2202 2062 pSink = pStreamR3->pMixSink->pMixSink; 2203 2204 if (!AudioMixerSinkIsActive(pSink)) /* No sink available? Bail out. */ 2063 if (RT_LIKELY(AudioMixerSinkIsActive(pSink))) 2064 { /* likely */ } 2065 else 2205 2066 return; 2206 2067 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) 2212 2080 { 2213 2081 bool fDoRead; /* Whether to push data down the driver stack or not. */ … … 2221 2089 */ 2222 2090 uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3); 2223 if (cbStreamFree >= pStreamShared->State.cbTransferSize)2091 if (cbStreamFree >= cbPeriod) 2224 2092 { /* likely */ } 2225 2093 else … … 2227 2095 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems); 2228 2096 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)); 2230 2098 # ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO 2231 2099 int rc = RTCritSectTryEnter(&pStreamR3->State.AIO.CritSect); … … 2243 2111 2244 2112 cbStreamFree = hdaR3StreamGetFree(pStreamR3); 2245 if (cbStreamFree < pStreamShared->State.cbTransferSize)2113 if (cbStreamFree < cbPeriod) 2246 2114 { 2247 2115 /* Unable to make sufficient space. Drop the whole buffer content. … … 2270 2138 2271 2139 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); 2273 2141 AssertRC(rc2); 2274 2142 … … 2291 2159 if (!pStreamShared->State.tsAioDelayEnd) 2292 2160 fDoRead = pStreamR3->State.offWrite > offWriteBefore 2293 || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cb TransferSize* 2;2161 || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2; 2294 2162 else if (PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer) >= pStreamShared->State.tsAioDelayEnd) 2295 2163 { … … 2298 2166 fDoRead = true; 2299 2167 } 2300 else if (hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cb TransferSize* 2)2168 else if (hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2) 2301 2169 { 2302 2170 Log3Func(("Initial delay done: Passed running short on buffer.\n")); … … 2313 2181 (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS, 2314 2182 pStreamShared->State.Cfg.Device.cMsSchedulingHint, cbStreamFree, 2315 pStreamShared->State.cb TransferSize* 2, fDoRead));2183 pStreamShared->State.cbAvgTransfer * 2, fDoRead)); 2316 2184 2317 2185 if (fDoRead) … … 2339 2207 hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, tsNowNs); 2340 2208 } 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. */ 2343 2216 # ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO 2344 2217 if (!fInTimer) … … 2395 2268 { 2396 2269 # 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)) 2398 2272 { 2399 2273 Log5Func(("Notifying AIO thread\n")); … … 2407 2281 if (cbStreamUsed) 2408 2282 { 2409 rc2 = hdaR3Stream Transfer(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, cbStreamUsed);2283 rc2 = hdaR3StreamDoDmaInput(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, RT_MIN(cbStreamUsed, cbPeriod)); 2410 2284 AssertRC(rc2); 2411 2285 } -
trunk/src/VBox/Devices/Audio/HDAStream.h
r88112 r88137 114 114 typedef struct HDASTREAMSTATE 115 115 { 116 /** Current BDLE to use. Wraps around to 0 if117 * maximum (cBDLE) is reached. */118 uint16_t uCurBDLE;119 116 /** Flag indicating whether this stream currently is 120 117 * in reset mode and therefore not acccessible by the guest. */ … … 122 119 /** Flag indicating if the stream is in running state or not. */ 123 120 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; 124 126 /** Unused, padding. */ 125 uint8_t abPadding0[4]; 126 /** Current BDLE (Buffer Descriptor List Entry). */ 127 HDABDLE BDLE; 127 uint8_t abPadding1[3]; 128 128 /** Timestamp (absolute, in timer ticks) of the last DMA data transfer. */ 129 129 uint64_t tsTransferLast; … … 135 135 * @note This is in host side frames, in case we're doing any mapping. */ 136 136 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; 158 139 /** The stream's period. Need for timing. */ 159 140 HDASTREAMPERIOD Period; … … 175 156 /** The start time for the playback (on the timer clock). */ 176 157 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 /** @} */ 177 207 } HDASTREAMSTATE; 178 208 AssertCompileSizeAlignment(HDASTREAMSTATE, 8); 209 AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 16); 210 AssertCompileMemberAlignment(HDASTREAMSTATE, aSchedule, 16); 179 211 180 212 /**
Note:
See TracChangeset
for help on using the changeset viewer.