VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHdaStream.cpp@ 88994

Last change on this file since 88994 was 88991, checked in by vboxsync, 4 years ago

Audio: Worked over draining, starting with the internal DMA buffer (instead of just the pre-buffer and backend buffer) and using the async I/O thread to keep calling PDMIAUDIOCONNECTOR::pfnStreamIterate and PDMIHOSTAUDIO::pfnStreamPlay (NULL buffer) every so often till the draining is done. Also put a rough deadline on the draining. The PDMAUDIOSTREAMCMD_DISABLE is now defined to stop playback/capturing immediately, even when already draining (if possible). This gets rid of the timers in DrvAudio and windows backends. DrvAudio no longer issue an DISABLE command at the end of the drain, it assumes the backend does that internally itself. After issuing PDMAUDIOSTREAMCMD_DRAIN the client (be it mixer or drvaudio) will not provide any more data for the buffers via pfnStreamPlay. Only tested windows, needs to re-test all platforms. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.6 KB
Line 
1/* $Id: DevHdaStream.cpp 88991 2021-05-12 00:46:35Z vboxsync $ */
2/** @file
3 * Intel HD Audio Controller Emulation - Streams.
4 */
5
6/*
7 * Copyright (C) 2017-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_HDA
23#include <VBox/log.h>
24
25#include <iprt/mem.h>
26#include <iprt/semaphore.h>
27
28#include <VBox/AssertGuest.h>
29#include <VBox/vmm/pdmdev.h>
30#include <VBox/vmm/pdmaudioifs.h>
31#include <VBox/vmm/pdmaudioinline.h>
32
33#include "AudioHlp.h"
34
35#include "DevHda.h"
36#include "DevHdaStream.h"
37
38#ifdef VBOX_WITH_DTRACE
39# include "dtrace/VBoxDD.h"
40#endif
41
42#ifdef IN_RING3 /* whole file */
43
44
45/*********************************************************************************************************************************
46* Internal Functions *
47*********************************************************************************************************************************/
48static void hdaR3StreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB);
49static void hdaR3StreamUpdateDma(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
50 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3);
51
52
53
54/**
55 * Creates an HDA stream.
56 *
57 * @returns VBox status code.
58 * @param pStreamShared The HDA stream to construct - shared bits.
59 * @param pStreamR3 The HDA stream to construct - ring-3 bits.
60 * @param pThis The shared HDA device instance.
61 * @param pThisCC The ring-3 HDA device instance.
62 * @param uSD Stream descriptor number to assign.
63 */
64int hdaR3StreamConstruct(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PHDASTATE pThis, PHDASTATER3 pThisCC, uint8_t uSD)
65{
66 int rc;
67
68 pStreamR3->u8SD = uSD;
69 pStreamShared->u8SD = uSD;
70 pStreamR3->pMixSink = NULL;
71 pStreamR3->pHDAStateShared = pThis;
72 pStreamR3->pHDAStateR3 = pThisCC;
73 Assert(pStreamShared->hTimer != NIL_TMTIMERHANDLE); /* hdaR3Construct initalized this one already. */
74
75 pStreamShared->State.fInReset = false;
76 pStreamShared->State.fRunning = false;
77#ifdef HDA_USE_DMA_ACCESS_HANDLER
78 RTListInit(&pStreamR3->State.lstDMAHandlers);
79#endif
80
81 AssertPtr(pStreamR3->pHDAStateR3);
82 AssertPtr(pStreamR3->pHDAStateR3->pDevIns);
83 rc = PDMDevHlpCritSectInit(pStreamR3->pHDAStateR3->pDevIns, &pStreamShared->CritSect,
84 RT_SRC_POS, "hda_sd#%RU8", pStreamShared->u8SD);
85 AssertRCReturn(rc, rc);
86
87#ifdef DEBUG
88 rc = RTCritSectInit(&pStreamR3->Dbg.CritSect);
89 AssertRCReturn(rc, rc);
90#endif
91
92 const bool fIsInput = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN;
93
94 if (fIsInput)
95 {
96 pStreamShared->State.Cfg.u.enmSrc = PDMAUDIORECSRC_UNKNOWN;
97 pStreamShared->State.Cfg.enmDir = PDMAUDIODIR_IN;
98 }
99 else
100 {
101 pStreamShared->State.Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_UNKNOWN;
102 pStreamShared->State.Cfg.enmDir = PDMAUDIODIR_OUT;
103 }
104
105 pStreamR3->Dbg.Runtime.fEnabled = pThisCC->Dbg.fEnabled;
106
107 if (pStreamR3->Dbg.Runtime.fEnabled)
108 {
109 char szFile[64];
110 char szPath[RTPATH_MAX];
111
112 /* pFileStream */
113 if (fIsInput)
114 RTStrPrintf(szFile, sizeof(szFile), "hdaStreamWriteSD%RU8", uSD);
115 else
116 RTStrPrintf(szFile, sizeof(szFile), "hdaStreamReadSD%RU8", uSD);
117
118 int rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThisCC->Dbg.pszOutPath, szFile,
119 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
120 AssertRC(rc2);
121
122 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStreamR3->Dbg.Runtime.pFileStream);
123 AssertRC(rc2);
124
125 /* pFileDMARaw */
126 if (fIsInput)
127 RTStrPrintf(szFile, sizeof(szFile), "hdaDMARawWriteSD%RU8", uSD);
128 else
129 RTStrPrintf(szFile, sizeof(szFile), "hdaDMARawReadSD%RU8", uSD);
130
131 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThisCC->Dbg.pszOutPath, szFile,
132 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
133 AssertRC(rc2);
134
135 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStreamR3->Dbg.Runtime.pFileDMARaw);
136 AssertRC(rc2);
137
138 /* pFileDMAMapped */
139 if (fIsInput)
140 RTStrPrintf(szFile, sizeof(szFile), "hdaDMAWriteMappedSD%RU8", uSD);
141 else
142 RTStrPrintf(szFile, sizeof(szFile), "hdaDMAReadMappedSD%RU8", uSD);
143
144 rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThisCC->Dbg.pszOutPath, szFile,
145 0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
146 AssertRC(rc2);
147
148 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStreamR3->Dbg.Runtime.pFileDMAMapped);
149 AssertRC(rc2);
150
151 /* Delete stale debugging files from a former run. */
152 AudioHlpFileDelete(pStreamR3->Dbg.Runtime.pFileStream);
153 AudioHlpFileDelete(pStreamR3->Dbg.Runtime.pFileDMARaw);
154 AudioHlpFileDelete(pStreamR3->Dbg.Runtime.pFileDMAMapped);
155 }
156
157 return rc;
158}
159
160/**
161 * Destroys an HDA stream.
162 *
163 * @param pStreamShared The HDA stream to destroy - shared bits.
164 * @param pStreamR3 The HDA stream to destroy - ring-3 bits.
165 */
166void hdaR3StreamDestroy(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
167{
168 LogFlowFunc(("[SD%RU8] Destroying ...\n", pStreamShared->u8SD));
169 int rc2;
170
171 hdaR3StreamMapDestroy(&pStreamR3->State.Mapping);
172
173 if (pStreamR3->State.pAioRegSink)
174 {
175 rc2 = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3);
176 AssertRC(rc2);
177 pStreamR3->State.pAioRegSink = NULL;
178 }
179
180 if (PDMCritSectIsInitialized(&pStreamShared->CritSect))
181 {
182 rc2 = PDMR3CritSectDelete(&pStreamShared->CritSect);
183 AssertRC(rc2);
184 }
185
186 if (pStreamR3->State.pCircBuf)
187 {
188 RTCircBufDestroy(pStreamR3->State.pCircBuf);
189 pStreamR3->State.pCircBuf = NULL;
190 pStreamR3->State.StatDmaBufSize = 0;
191 pStreamR3->State.StatDmaBufUsed = 0;
192 }
193
194#ifdef DEBUG
195 if (RTCritSectIsInitialized(&pStreamR3->Dbg.CritSect))
196 {
197 rc2 = RTCritSectDelete(&pStreamR3->Dbg.CritSect);
198 AssertRC(rc2);
199 }
200#endif
201
202 if (pStreamR3->Dbg.Runtime.fEnabled)
203 {
204 AudioHlpFileDestroy(pStreamR3->Dbg.Runtime.pFileStream);
205 pStreamR3->Dbg.Runtime.pFileStream = NULL;
206
207 AudioHlpFileDestroy(pStreamR3->Dbg.Runtime.pFileDMARaw);
208 pStreamR3->Dbg.Runtime.pFileDMARaw = NULL;
209
210 AudioHlpFileDestroy(pStreamR3->Dbg.Runtime.pFileDMAMapped);
211 pStreamR3->Dbg.Runtime.pFileDMAMapped = NULL;
212 }
213
214 LogFlowFuncLeave();
215}
216
217
218/**
219 * Appends a item to the scheduler.
220 *
221 * @returns VBox status code.
222 * @param pStreamShared The stream which scheduler should be modified.
223 * @param cbCur The period length in guest bytes.
224 * @param cbMaxPeriod The max period in guest bytes.
225 * @param idxLastBdle The last BDLE in the period.
226 * @param pHostProps The host PCM properties.
227 * @param pGuestProps The guest PCM properties.
228 * @param pcbBorrow Where to account for bytes borrowed across buffers
229 * to align scheduling items on frame boundraries.
230 */
231static int hdaR3StreamAddScheduleItem(PHDASTREAM pStreamShared, uint32_t cbCur, uint32_t cbMaxPeriod, uint32_t idxLastBdle,
232 PCPDMAUDIOPCMPROPS pHostProps, PCPDMAUDIOPCMPROPS pGuestProps, uint32_t *pcbBorrow)
233{
234 /* Check that we've got room (shouldn't ever be a problem). */
235 size_t idx = pStreamShared->State.cSchedule;
236 AssertLogRelReturn(idx + 1 < RT_ELEMENTS(pStreamShared->State.aSchedule), VERR_INTERNAL_ERROR_5);
237
238 /* Figure out the BDLE range for this period. */
239 uint32_t const idxFirstBdle = idx == 0 ? 0
240 : pStreamShared->State.aSchedule[idx - 1].idxFirst
241 + pStreamShared->State.aSchedule[idx - 1].cEntries;
242
243 pStreamShared->State.aSchedule[idx].idxFirst = (uint8_t)idxFirstBdle;
244 pStreamShared->State.aSchedule[idx].cEntries = idxLastBdle >= idxFirstBdle
245 ? idxLastBdle - idxFirstBdle + 1
246 : pStreamShared->State.cBdles - idxFirstBdle + idxLastBdle + 1;
247
248 /* Deal with borrowing due to unaligned IOC buffers. */
249 uint32_t const cbBorrowed = *pcbBorrow;
250 if (cbBorrowed < cbCur)
251 cbCur -= cbBorrowed;
252 else
253 {
254 /* 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. */
255 ASSERT_GUEST_MSG_FAILED(("#%u: cbBorrow=%#x cbCur=%#x BDLE[%u..%u]\n",
256 pStreamShared->u8SD, cbBorrowed, cbCur, idxFirstBdle, idxLastBdle));
257 LogRelMax(32, ("HDA: Stream #%u has a scheduling error: cbBorrow=%#x cbCur=%#x BDLE[%u..%u]\n",
258 pStreamShared->u8SD, cbBorrowed, cbCur, idxFirstBdle, idxLastBdle));
259 return VERR_OUT_OF_RANGE;
260 }
261
262 uint32_t cbCurAligned = PDMAudioPropsRoundUpBytesToFrame(pGuestProps, cbCur);
263 *pcbBorrow = cbCurAligned - cbCur;
264
265 /* Do we need to split up the period? */
266 if (cbCurAligned <= cbMaxPeriod)
267 {
268 uint32_t cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbCurAligned));
269 pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
270 pStreamShared->State.aSchedule[idx].cLoops = 1;
271 }
272 else
273 {
274 /* Reduce till we've below the threshold. */
275 uint32_t cbLoop = cbCurAligned;
276 do
277 cbLoop = cbCurAligned / 2;
278 while (cbLoop > cbMaxPeriod);
279 cbLoop = PDMAudioPropsRoundUpBytesToFrame(pGuestProps, cbLoop);
280
281 /* Complete the scheduling item. */
282 uint32_t cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbLoop));
283 pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
284 pStreamShared->State.aSchedule[idx].cLoops = cbCurAligned / cbLoop;
285
286 /* If there is a remainder, add it as a separate entry (this is
287 why the schedule must be more than twice the size of the BDL).*/
288 cbCurAligned %= cbLoop;
289 if (cbCurAligned)
290 {
291 pStreamShared->State.aSchedule[idx + 1] = pStreamShared->State.aSchedule[idx];
292 idx++;
293 cbHost = PDMAudioPropsFramesToBytes(pHostProps, PDMAudioPropsBytesToFrames(pGuestProps, cbCurAligned));
294 pStreamShared->State.aSchedule[idx].cbPeriod = cbHost;
295 pStreamShared->State.aSchedule[idx].cLoops = 1;
296 }
297 }
298
299 /* Done. */
300 pStreamShared->State.cSchedule = (uint16_t)(idx + 1);
301
302 return VINF_SUCCESS;
303}
304
305/**
306 * Creates the DMA timer schedule for the stream
307 *
308 * This is called from the stream setup code.
309 *
310 * @returns VBox status code.
311 * @param pStreamShared The stream to create a schedule for. The BDL
312 * must be loaded.
313 * @param cSegments Number of BDL segments.
314 * @param cBufferIrqs Number of the BDLEs with IOC=1.
315 * @param cbTotal The total BDL length in guest bytes.
316 * @param cbMaxPeriod Max period in guest bytes. This is in case the
317 * guest want to play the whole "Der Ring des
318 * Nibelungen" cycle in one go.
319 * @param cTimerTicksPerSec The DMA timer frequency.
320 * @param pHostProps The host PCM properties.
321 * @param pGuestProps The guest PCM properties.
322 */
323static int hdaR3StreamCreateSchedule(PHDASTREAM pStreamShared, uint32_t cSegments, uint32_t cBufferIrqs, uint32_t cbTotal,
324 uint32_t cbMaxPeriod, uint64_t cTimerTicksPerSec,
325 PCPDMAUDIOPCMPROPS pHostProps, PCPDMAUDIOPCMPROPS pGuestProps)
326{
327 int rc;
328
329 /*
330 * Reset scheduling state.
331 */
332 RT_ZERO(pStreamShared->State.aSchedule);
333 pStreamShared->State.cSchedule = 0;
334 pStreamShared->State.cSchedulePrologue = 0;
335 pStreamShared->State.idxSchedule = 0;
336 pStreamShared->State.idxScheduleLoop = 0;
337
338 /*
339 * Do the basic schedule compilation.
340 */
341 uint32_t cPotentialPrologue = 0;
342 uint32_t cbBorrow = 0;
343 uint32_t cbCur = 0;
344 pStreamShared->State.aSchedule[0].idxFirst = 0;
345 for (uint32_t i = 0; i < cSegments; i++)
346 {
347 cbCur += pStreamShared->State.aBdl[i].cb;
348 if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
349 {
350 rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, i, pHostProps, pGuestProps, &cbBorrow);
351 ASSERT_GUEST_RC_RETURN(rc, rc);
352
353 if (cPotentialPrologue == 0)
354 cPotentialPrologue = pStreamShared->State.cSchedule;
355 cbCur = 0;
356 }
357 }
358 AssertLogRelMsgReturn(cbBorrow == 0, ("HDA: Internal scheduling error on stream #%u: cbBorrow=%#x cbTotal=%#x cbCur=%#x\n",
359 pStreamShared->u8SD, cbBorrow, cbTotal, cbCur),
360 VERR_INTERNAL_ERROR_3);
361
362 /*
363 * Deal with any loose ends.
364 */
365 if (cbCur && cBufferIrqs == 0)
366 {
367 /* No IOC. Split the period in two. */
368 Assert(cbCur == cbTotal);
369 cbCur = PDMAudioPropsFloorBytesToFrame(pGuestProps, cbCur / 2);
370 rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, cSegments, pHostProps, pGuestProps, &cbBorrow);
371 ASSERT_GUEST_RC_RETURN(rc, rc);
372
373 rc = hdaR3StreamAddScheduleItem(pStreamShared, cbTotal - cbCur, cbMaxPeriod, cSegments,
374 pHostProps, pGuestProps, &cbBorrow);
375 ASSERT_GUEST_RC_RETURN(rc, rc);
376 Assert(cbBorrow == 0);
377 }
378 else if (cbCur)
379 {
380 /* The last BDLE didn't have IOC set, so we must continue processing
381 from the start till we hit one that has. */
382 uint32_t i;
383 for (i = 0; i < cSegments; i++)
384 {
385 cbCur += pStreamShared->State.aBdl[i].cb;
386 if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
387 break;
388 }
389 rc = hdaR3StreamAddScheduleItem(pStreamShared, cbCur, cbMaxPeriod, i, pHostProps, pGuestProps, &cbBorrow);
390 ASSERT_GUEST_RC_RETURN(rc, rc);
391
392 /* The initial scheduling items covering the wrap around area are
393 considered a prologue and must not repeated later. */
394 Assert(cPotentialPrologue);
395 pStreamShared->State.cSchedulePrologue = (uint8_t)cPotentialPrologue;
396 }
397
398 /*
399 * If there is just one BDLE with IOC set, we have to make sure
400 * we've got at least two periods scheduled, otherwise there is
401 * a very good chance the guest will overwrite the start of the
402 * buffer before we ever get around to reading it.
403 */
404 if (cBufferIrqs == 1)
405 {
406 uint32_t i = pStreamShared->State.cSchedulePrologue;
407 Assert(i < pStreamShared->State.cSchedule);
408 if ( i + 1 == pStreamShared->State.cSchedule
409 && pStreamShared->State.aSchedule[i].cLoops == 1)
410 {
411 uint32_t const cbFirstHalf = PDMAudioPropsFloorBytesToFrame(pHostProps, pStreamShared->State.aSchedule[i].cbPeriod / 2);
412 uint32_t const cbOtherHalf = pStreamShared->State.aSchedule[i].cbPeriod - cbFirstHalf;
413 pStreamShared->State.aSchedule[i].cbPeriod = cbFirstHalf;
414 if (cbFirstHalf == cbOtherHalf)
415 pStreamShared->State.aSchedule[i].cLoops = 2;
416 else
417 {
418 pStreamShared->State.aSchedule[i + 1] = pStreamShared->State.aSchedule[i];
419 pStreamShared->State.aSchedule[i].cbPeriod = cbOtherHalf;
420 pStreamShared->State.cSchedule++;
421 }
422 }
423 }
424
425 /*
426 * Go over the schduling entries and calculate the timer ticks for each period.
427 */
428 LogRel2(("HDA: Stream #%u schedule: %u items, %u prologue\n",
429 pStreamShared->u8SD, pStreamShared->State.cSchedule, pStreamShared->State.cSchedulePrologue));
430 uint64_t const cbHostPerSec = PDMAudioPropsFramesToBytes(pHostProps, pHostProps->uHz);
431 for (uint32_t i = 0; i < pStreamShared->State.cSchedule; i++)
432 {
433 uint64_t const cTicks = ASMMultU64ByU32DivByU32(cTimerTicksPerSec, pStreamShared->State.aSchedule[i].cbPeriod,
434 cbHostPerSec);
435 AssertLogRelMsgReturn((uint32_t)cTicks == cTicks, ("cTicks=%RU64 (%#RX64)\n", cTicks, cTicks), VERR_INTERNAL_ERROR_4);
436 pStreamShared->State.aSchedule[i].cPeriodTicks = RT_MAX((uint32_t)cTicks, 16);
437 LogRel2(("HDA: #%u: %u ticks / %u bytes, %u loops, BDLE%u L %u\n", i, pStreamShared->State.aSchedule[i].cPeriodTicks,
438 pStreamShared->State.aSchedule[i].cbPeriod, pStreamShared->State.aSchedule[i].cLoops,
439 pStreamShared->State.aSchedule[i].idxFirst, pStreamShared->State.aSchedule[i].cEntries));
440 }
441
442 return VINF_SUCCESS;
443}
444
445
446/**
447 * Sets up ((re-)iniitalizes) an HDA stream.
448 *
449 * @returns VBox status code. VINF_NO_CHANGE if the stream does not need
450 * be set-up again because the stream's (hardware) parameters did
451 * not change.
452 * @param pDevIns The device instance.
453 * @param pThis The shared HDA device state (for HW register
454 * parameters).
455 * @param pStreamShared HDA stream to set up, shared portion.
456 * @param pStreamR3 HDA stream to set up, ring-3 portion.
457 * @param uSD Stream descriptor number to assign it.
458 */
459int hdaR3StreamSetUp(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD)
460{
461 /* This must be valid all times. */
462 AssertReturn(uSD < HDA_MAX_STREAMS, VERR_INVALID_PARAMETER);
463
464 /* These member can only change on data corruption, despite what the code does further down (bird). */
465 AssertReturn(pStreamShared->u8SD == uSD, VERR_WRONG_ORDER);
466 AssertReturn(pStreamR3->u8SD == uSD, VERR_WRONG_ORDER);
467
468 const uint64_t u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, uSD),
469 HDA_STREAM_REG(pThis, BDPU, uSD));
470 const uint16_t u16LVI = HDA_STREAM_REG(pThis, LVI, uSD);
471 const uint32_t u32CBL = HDA_STREAM_REG(pThis, CBL, uSD);
472 const uint8_t u8FIFOS = HDA_STREAM_REG(pThis, FIFOS, uSD) + 1;
473 uint8_t u8FIFOW = hdaSDFIFOWToBytes(HDA_STREAM_REG(pThis, FIFOW, uSD));
474 const uint16_t u16FMT = HDA_STREAM_REG(pThis, FMT, uSD);
475
476 /* Is the bare minimum set of registers configured for the stream?
477 * If not, bail out early, as there's nothing to do here for us (yet). */
478 if ( !u64BDLBase
479 || !u16LVI
480 || !u32CBL
481 || !u8FIFOS
482 || !u8FIFOW
483 || !u16FMT)
484 {
485 LogFunc(("[SD%RU8] Registers not set up yet, skipping (re-)initialization\n", uSD));
486 return VINF_SUCCESS;
487 }
488
489 PDMAUDIOPCMPROPS HostProps;
490 int rc = hdaR3SDFMTToPCMProps(u16FMT, &HostProps);
491 if (RT_FAILURE(rc))
492 {
493 LogRelMax(32, ("HDA: Warning: Format 0x%x for stream #%RU8 not supported\n", HDA_STREAM_REG(pThis, FMT, uSD), uSD));
494 return rc;
495 }
496
497 /*
498 * Initialize the stream mapping in any case, regardless if
499 * we support surround audio or not. This is needed to handle
500 * the supported channels within a single audio stream, e.g. mono/stereo.
501 *
502 * In other words, the stream mapping *always* knows the real
503 * number of channels in a single audio stream.
504 */
505 /** @todo r=bird: this is not done at the wrong time. We don't have the host
506 * output side set up yet, so we cannot really do proper mapping setup.
507 * However, we really need this further down when we configure the internal DMA
508 * buffer size. For now we just assume it's all stereo on the host side.
509 * This is not compatible with microphone support. */
510# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
511# error "Implement me!"
512# endif
513 rc = hdaR3StreamMapInit(&pStreamR3->State.Mapping, 2 /*cHostChannels*/, &HostProps);
514 AssertRCReturn(rc, rc);
515
516 ASSERT_GUEST_LOGREL_MSG_RETURN( pStreamR3->State.Mapping.cbGuestFrame > 0
517 && u32CBL % pStreamR3->State.Mapping.cbGuestFrame == 0,
518 ("CBL for stream #%RU8 does not align to frame size (u32CBL=%u cbFrameSize=%u)\n",
519 uSD, u32CBL, pStreamR3->State.Mapping.cbGuestFrame),
520 VERR_INVALID_PARAMETER);
521
522 /* Make sure the guest behaves regarding the stream's FIFO. */
523 ASSERT_GUEST_LOGREL_MSG_STMT(u8FIFOW <= u8FIFOS,
524 ("Guest tried setting a bigger FIFOW (%RU8) than FIFOS (%RU8), limiting\n", u8FIFOW, u8FIFOS),
525 u8FIFOW = u8FIFOS /* ASSUMES that u8FIFOS has been validated. */);
526
527 pStreamShared->u8SD = uSD;
528
529 /* Update all register copies so that we later know that something has changed. */
530 pStreamShared->u64BDLBase = u64BDLBase;
531 pStreamShared->u16LVI = u16LVI;
532 pStreamShared->u32CBL = u32CBL;
533 pStreamShared->u8FIFOS = u8FIFOS;
534 pStreamShared->u8FIFOW = u8FIFOW;
535 pStreamShared->u16FMT = u16FMT;
536
537 PPDMAUDIOSTREAMCFG pCfg = &pStreamShared->State.Cfg;
538 pCfg->Props = HostProps;
539
540 /* Set the stream's direction. */
541 pCfg->enmDir = hdaGetDirFromSD(uSD);
542
543 /* The the stream's name, based on the direction. */
544 switch (pCfg->enmDir)
545 {
546 case PDMAUDIODIR_IN:
547# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
548# error "Implement me!"
549# else
550 pCfg->u.enmSrc = PDMAUDIORECSRC_LINE;
551 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
552 RTStrCopy(pCfg->szName, sizeof(pCfg->szName), "Line In");
553# endif
554 break;
555
556 case PDMAUDIODIR_OUT:
557 /* Destination(s) will be set in hdaR3AddStreamOut(),
558 * based on the channels / stream layout. */
559 break;
560
561 default:
562 AssertFailedReturn(VERR_NOT_SUPPORTED);
563 break;
564 }
565
566 LogRel2(("HDA: Stream #%RU8 DMA @ 0x%x (%RU32 bytes = %RU64ms total)\n",
567 uSD, pStreamShared->u64BDLBase, pStreamShared->u32CBL,
568 PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->u32CBL)));
569
570
571 /*
572 * Load the buffer descriptor list.
573 *
574 * Section 3.6.2 states that "the BDL should not be modified unless the RUN
575 * bit is 0", so it should be within the specs to read it once here and not
576 * re-read any BDLEs later.
577 */
578 /* Reset BDL state. */
579 RT_ZERO(pStreamShared->State.aBdl);
580 pStreamShared->State.offCurBdle = 0;
581 pStreamShared->State.idxCurBdle = 0;
582
583 uint32_t /*const*/ cTransferFragments = (pStreamShared->u16LVI & 0xff) + 1;
584 if (cTransferFragments <= 1)
585 LogRel(("HDA: Warning: Stream #%RU8 transfer buffer count invalid: (%RU16)! Buggy guest audio driver!\n", uSD, pStreamShared->u16LVI));
586 AssertLogRelReturn(cTransferFragments <= RT_ELEMENTS(pStreamShared->State.aBdl), VERR_INTERNAL_ERROR_5);
587 pStreamShared->State.cBdles = cTransferFragments;
588
589 /* Load them. */
590 rc = PDMDevHlpPCIPhysRead(pDevIns, u64BDLBase, pStreamShared->State.aBdl,
591 sizeof(pStreamShared->State.aBdl[0]) * cTransferFragments);
592 AssertRC(rc);
593
594 /* Check what we just loaded. Refuse overly large buffer lists. */
595 uint64_t cbTotal = 0;
596 uint32_t cBufferIrqs = 0;
597 for (uint32_t i = 0; i < cTransferFragments; i++)
598 {
599 if (pStreamShared->State.aBdl[i].fFlags & HDA_BDLE_F_IOC)
600 cBufferIrqs++;
601 cbTotal += pStreamShared->State.aBdl[i].cb;
602 }
603 ASSERT_GUEST_STMT_RETURN(cbTotal < _2G,
604 LogRelMax(32, ("HDA: Error: Stream #%u is configured with an insane amount of buffer space - refusing do work with it: %RU64 (%#RX64) bytes.\n",
605 uSD, cbTotal, cbTotal)),
606 VERR_NOT_SUPPORTED);
607 ASSERT_GUEST_STMT_RETURN(cbTotal == u32CBL,
608 LogRelMax(32, ("HDA: Warning: Stream #%u has a mismatch between CBL and configured buffer space: %RU32 (%#RX32) vs %RU64 (%#RX64)\n",
609 uSD, u32CBL, u32CBL, cbTotal, cbTotal)),
610 VERR_NOT_SUPPORTED);
611
612 /*
613 * Create a DMA timer schedule.
614 */
615 rc = hdaR3StreamCreateSchedule(pStreamShared, cTransferFragments, cBufferIrqs, (uint32_t)cbTotal,
616 PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.GuestProps, 100 /** @todo make configurable */),
617 PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer),
618 &HostProps, &pStreamR3->State.Mapping.GuestProps);
619 if (RT_FAILURE(rc))
620 return rc;
621
622 pStreamShared->State.cbTransferSize = pStreamShared->State.aSchedule[0].cbPeriod;
623
624 /*
625 * Calculate the transfer Hz for use in the circular buffer calculation.
626 */
627 uint32_t cbMaxPeriod = 0;
628 uint32_t cbMinPeriod = UINT32_MAX;
629 uint32_t cPeriods = 0;
630 for (uint32_t i = 0; i < pStreamShared->State.cSchedule; i++)
631 {
632 uint32_t cbPeriod = pStreamShared->State.aSchedule[i].cbPeriod;
633 cbMaxPeriod = RT_MAX(cbMaxPeriod, cbPeriod);
634 cbMinPeriod = RT_MIN(cbMinPeriod, cbPeriod);
635 cPeriods += pStreamShared->State.aSchedule[i].cLoops;
636 }
637 uint64_t const cbTransferPerSec = RT_MAX(PDMAudioPropsFramesToBytes(&pCfg->Props, pCfg->Props.uHz),
638 4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */);
639 unsigned uTransferHz = cbTransferPerSec * 1000 / cbMaxPeriod;
640 LogRel2(("HDA: Stream #%RU8 needs a %u.%03u Hz timer rate (period: %u..%u host bytes)\n",
641 uSD, uTransferHz / 1000, uTransferHz % 1000, cbMinPeriod, cbMaxPeriod));
642 uTransferHz /= 1000;
643
644 if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */
645 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",
646 uSD, uTransferHz));
647
648 pStreamShared->State.cbAvgTransfer = (uint32_t)(cbTotal + cPeriods - 1) / cPeriods;
649
650 /* For input streams we must determin a pre-buffering requirement.
651 We use the initial delay as a basis here, though we must have at
652 least two max periods worth of data queued up due to the way we
653 work the AIO thread. */
654 pStreamShared->State.fInputPreBuffered = false;
655 pStreamShared->State.cbInputPreBuffer = PDMAudioPropsMilliToBytes(&pCfg->Props, pThis->msInitialDelay);
656 pStreamShared->State.cbInputPreBuffer = RT_MIN(cbMaxPeriod * 2, pStreamShared->State.cbInputPreBuffer);
657
658 /*
659 * Set up data transfer stuff.
660 */
661
662 /* Assign the global device rate to the stream I/O timer as default. */
663 pStreamShared->State.uTimerIoHz = pThis->uTimerHz;
664 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz,
665 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD),
666 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT);
667
668 /* Set I/O scheduling hint for the backends. */
669 /** @todo r=bird: This is in the 'Device' portion, yet it's used by the
670 * audio driver. You would think stuff in the 'Device' part is
671 * private to the device. */
672 pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz;
673 LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint));
674
675
676 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
677 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
678
679#ifdef LOG_ENABLED
680 hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
681#endif
682
683 /*
684 * Set up internal ring buffer.
685 */
686
687 /* (Re-)Allocate the stream's internal DMA buffer,
688 * based on the timing *and* PCM properties we just got above. */
689 if (pStreamR3->State.pCircBuf)
690 {
691 RTCircBufDestroy(pStreamR3->State.pCircBuf);
692 pStreamR3->State.pCircBuf = NULL;
693 pStreamR3->State.StatDmaBufSize = 0;
694 pStreamR3->State.StatDmaBufUsed = 0;
695 }
696 pStreamR3->State.offWrite = 0;
697 pStreamR3->State.offRead = 0;
698
699 /*
700 * The default internal ring buffer size must be:
701 *
702 * - Large enough for at least three periodic DMA transfers.
703 *
704 * It is critically important that we don't experience underruns
705 * in the DMA OUT code, because it will cause the buffer processing
706 * to get skewed and possibly overlap with what the guest is updating.
707 * At the time of writing (2021-03-05) there is no code for getting
708 * back into sync there.
709 *
710 * - Large enough for at least three I/O scheduling hints.
711 *
712 * We want to lag behind a DMA period or two, but there must be
713 * sufficent space for the AIO thread to get schedule and shuffle
714 * data thru the mixer and onto the host audio hardware.
715 *
716 * - Both above with plenty to spare.
717 *
718 * So, just take the longest of the two periods and multipling it by 6.
719 * We aren't not talking about very large base buffers heres, so size isn't
720 * an issue.
721 *
722 * Note: Use pCfg->Props as PCM properties here, as we only want to store the
723 * samples we actually need, in other words, skipping the interleaved
724 * channels we don't support / need to save space.
725 */
726 uint32_t msCircBuf = RT_MS_1SEC * 6 / RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz);
727 msCircBuf = RT_MAX(msCircBuf, pThis->msInitialDelay + RT_MS_1SEC * 6 / uTransferHz);
728
729 uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBuf);
730 LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU32 bytes / %RU64 ms\n",
731 uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
732
733 uint32_t msCircBufCfg = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs;
734 if (msCircBufCfg) /* Anything set via CFGM? */
735 {
736 cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBufCfg);
737 LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU32 bytes / %RU64 ms\n",
738 uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
739 }
740
741 /* Serious paranoia: */
742 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf % PDMAudioPropsFrameSize(&pCfg->Props) == 0,
743 ("Ring buffer size (%RU32) for stream #%RU8 not aligned to the (host) frame size (%RU8)\n",
744 cbCircBuf, uSD, PDMAudioPropsFrameSize(&pCfg->Props)),
745 rc = VERR_INVALID_PARAMETER);
746 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, ("Ring buffer size for stream #%RU8 is invalid\n", uSD),
747 rc = VERR_INVALID_PARAMETER);
748 if (RT_SUCCESS(rc))
749 {
750 rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
751 if (RT_SUCCESS(rc))
752 {
753 pStreamR3->State.StatDmaBufSize = cbCircBuf;
754
755 /*
756 * Forward the timer frequency hint to TM as well for better accuracy on
757 * systems w/o preemption timers (also good for 'info timers').
758 */
759 PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
760 }
761 }
762
763 if (RT_FAILURE(rc))
764 LogRelMax(32, ("HDA: Initializing stream #%RU8 failed with %Rrc\n", uSD, rc));
765
766#ifdef VBOX_WITH_DTRACE
767 VBOXDD_HDA_STREAM_SETUP((uint32_t)uSD, rc, pStreamShared->State.Cfg.Props.uHz,
768 pStreamShared->State.aSchedule[pStreamShared->State.cSchedule - 1].cPeriodTicks,
769 pStreamShared->State.aSchedule[pStreamShared->State.cSchedule - 1].cbPeriod);
770#endif
771 return rc;
772}
773
774/**
775 * Resets an HDA stream.
776 *
777 * @param pThis The shared HDA device state.
778 * @param pThisCC The ring-3 HDA device state.
779 * @param pStreamShared HDA stream to reset (shared).
780 * @param pStreamR3 HDA stream to reset (ring-3).
781 * @param uSD Stream descriptor (SD) number to use for this stream.
782 */
783void hdaR3StreamReset(PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD)
784{
785 LogFunc(("[SD%RU8] Reset\n", uSD));
786
787 /*
788 * Assert some sanity.
789 */
790 AssertPtr(pThis);
791 AssertPtr(pStreamShared);
792 AssertPtr(pStreamR3);
793 Assert(uSD < HDA_MAX_STREAMS);
794 Assert(pStreamShared->u8SD == uSD);
795 Assert(pStreamR3->u8SD == uSD);
796 AssertMsg(!pStreamShared->State.fRunning, ("[SD%RU8] Cannot reset stream while in running state\n", uSD));
797
798 /*
799 * Set reset state.
800 */
801 Assert(ASMAtomicReadBool(&pStreamShared->State.fInReset) == false); /* No nested calls. */
802 ASMAtomicXchgBool(&pStreamShared->State.fInReset, true);
803
804 /*
805 * Second, initialize the registers.
806 */
807 /* See 6.2.33: Clear on reset. */
808 HDA_STREAM_REG(pThis, STS, uSD) = 0;
809 /* According to the ICH6 datasheet, 0x40000 is the default value for stream descriptor register 23:20
810 * bits are reserved for stream number 18.2.33, resets SDnCTL except SRST bit. */
811 HDA_STREAM_REG(pThis, CTL, uSD) = HDA_SDCTL_TP | (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_SRST);
812 /* ICH6 defines default values (120 bytes for input and 192 bytes for output descriptors) of FIFO size. 18.2.39. */
813 HDA_STREAM_REG(pThis, FIFOS, uSD) = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? HDA_SDIFIFO_120B : HDA_SDOFIFO_192B;
814 /* See 18.2.38: Always defaults to 0x4 (32 bytes). */
815 HDA_STREAM_REG(pThis, FIFOW, uSD) = HDA_SDFIFOW_32B;
816 HDA_STREAM_REG(pThis, LPIB, uSD) = 0;
817 HDA_STREAM_REG(pThis, CBL, uSD) = 0;
818 HDA_STREAM_REG(pThis, LVI, uSD) = 0;
819 HDA_STREAM_REG(pThis, FMT, uSD) = 0;
820 HDA_STREAM_REG(pThis, BDPU, uSD) = 0;
821 HDA_STREAM_REG(pThis, BDPL, uSD) = 0;
822
823#ifdef HDA_USE_DMA_ACCESS_HANDLER
824 hdaR3StreamUnregisterDMAHandlers(pThis, pStream);
825#endif
826
827 /* Assign the default mixer sink to the stream. */
828 pStreamR3->pMixSink = hdaR3GetDefaultSink(pThisCC, uSD);
829 if (pStreamR3->State.pAioRegSink)
830 {
831 int rc2 = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3);
832 AssertRC(rc2);
833 pStreamR3->State.pAioRegSink = NULL;
834 }
835
836 /* Reset transfer stuff. */
837 pStreamShared->State.cTransferPendingInterrupts = 0;
838 pStreamShared->State.tsTransferLast = 0;
839 pStreamShared->State.tsTransferNext = 0;
840
841 /* Initialize timestamps. */
842 pStreamShared->State.tsLastTransferNs = 0;
843 pStreamShared->State.tsLastReadNs = 0;
844 pStreamShared->State.tsAioDelayEnd = UINT64_MAX;
845 pStreamShared->State.tsStart = 0;
846
847 RT_ZERO(pStreamShared->State.aBdl);
848 RT_ZERO(pStreamShared->State.aSchedule);
849 pStreamShared->State.offCurBdle = 0;
850 pStreamShared->State.cBdles = 0;
851 pStreamShared->State.idxCurBdle = 0;
852 pStreamShared->State.cSchedulePrologue = 0;
853 pStreamShared->State.cSchedule = 0;
854 pStreamShared->State.idxSchedule = 0;
855 pStreamShared->State.idxScheduleLoop = 0;
856 pStreamShared->State.fInputPreBuffered = false;
857
858 if (pStreamR3->State.pCircBuf)
859 RTCircBufReset(pStreamR3->State.pCircBuf);
860 pStreamR3->State.offWrite = 0;
861 pStreamR3->State.offRead = 0;
862
863#ifdef DEBUG
864 pStreamR3->Dbg.cReadsTotal = 0;
865 pStreamR3->Dbg.cbReadTotal = 0;
866 pStreamR3->Dbg.tsLastReadNs = 0;
867 pStreamR3->Dbg.cWritesTotal = 0;
868 pStreamR3->Dbg.cbWrittenTotal = 0;
869 pStreamR3->Dbg.cWritesHz = 0;
870 pStreamR3->Dbg.cbWrittenHz = 0;
871 pStreamR3->Dbg.tsWriteSlotBegin = 0;
872#endif
873
874 /* Report that we're done resetting this stream. */
875 HDA_STREAM_REG(pThis, CTL, uSD) = 0;
876
877#ifdef VBOX_WITH_DTRACE
878 VBOXDD_HDA_STREAM_RESET((uint32_t)uSD);
879#endif
880 LogFunc(("[SD%RU8] Reset\n", uSD));
881
882 /* Exit reset mode. */
883 ASMAtomicXchgBool(&pStreamShared->State.fInReset, false);
884}
885
886/**
887 * Enables or disables an HDA audio stream.
888 *
889 * @returns VBox status code.
890 * @param pThis The shared HDA device state.
891 * @param pStreamShared HDA stream to enable or disable - shared bits.
892 * @param pStreamR3 HDA stream to enable or disable - ring-3 bits.
893 * @param fEnable Whether to enable or disble the stream.
894 */
895int hdaR3StreamEnable(PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fEnable)
896{
897 AssertPtr(pStreamR3);
898 AssertPtr(pStreamShared);
899
900 LogFunc(("[SD%RU8] fEnable=%RTbool, pMixSink=%p\n", pStreamShared->u8SD, fEnable, pStreamR3->pMixSink));
901
902 /* First, enable or disable the stream and the stream's sink, if any. */
903 int rc = VINF_SUCCESS;
904 PAUDMIXSINK const pSink = pStreamR3->pMixSink ? pStreamR3->pMixSink->pMixSink : NULL;
905 if (pSink)
906 {
907 if (fEnable)
908 {
909 if (pStreamR3->State.pAioRegSink != pSink)
910 {
911 if (pStreamR3->State.pAioRegSink)
912 {
913 rc = AudioMixerSinkRemoveUpdateJob(pStreamR3->State.pAioRegSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3);
914 AssertRC(rc);
915 }
916 rc = AudioMixerSinkAddUpdateJob(pSink, hdaR3StreamUpdateAsyncIoJob, pStreamR3,
917 pStreamShared->State.Cfg.Device.cMsSchedulingHint);
918 AssertLogRelRC(rc);
919 pStreamR3->State.pAioRegSink = RT_SUCCESS(rc) ? pSink : NULL;
920 }
921 rc = AudioMixerSinkStart(pSink);
922 }
923 else
924 rc = AudioMixerSinkDrainAndStop(pSink,
925 pStreamR3->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf) : 0);
926 }
927 if ( RT_SUCCESS(rc)
928 && fEnable
929 && pStreamR3->Dbg.Runtime.fEnabled)
930 {
931 Assert(AudioHlpPcmPropsAreValid(&pStreamShared->State.Cfg.Props));
932
933 if (fEnable)
934 {
935 if (!AudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileStream))
936 {
937 int rc2 = AudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileStream, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
938 &pStreamShared->State.Cfg.Props);
939 AssertRC(rc2);
940 }
941
942 if (!AudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileDMARaw))
943 {
944 int rc2 = AudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileDMARaw, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
945 &pStreamShared->State.Cfg.Props);
946 AssertRC(rc2);
947 }
948
949 if (!AudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileDMAMapped))
950 {
951 int rc2 = AudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileDMAMapped, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
952 &pStreamShared->State.Cfg.Props);
953 AssertRC(rc2);
954 }
955 }
956 }
957
958 if (RT_SUCCESS(rc))
959 {
960 if (fEnable)
961 pStreamShared->State.tsTransferLast = 0; /* Make sure it's not stale and messes up WALCLK calculations. */
962 pStreamShared->State.fRunning = fEnable;
963
964 /*
965 * Set the FIFORDY bit when we start running and clear it when stopping.
966 *
967 * This prevents Linux from timing out in snd_hdac_stream_sync when starting
968 * a stream. Technically, Linux also uses the SSYNC feature there, but we
969 * can get away with just setting the FIFORDY bit for now.
970 */
971 if (fEnable)
972 HDA_STREAM_REG(pThis, STS, pStreamShared->u8SD) |= HDA_SDSTS_FIFORDY;
973 else
974 HDA_STREAM_REG(pThis, STS, pStreamShared->u8SD) &= ~HDA_SDSTS_FIFORDY;
975 }
976
977 LogFunc(("[SD%RU8] rc=%Rrc\n", pStreamShared->u8SD, rc));
978 return rc;
979}
980
981/**
982 * Marks the stream as started.
983 *
984 * Used after the stream has been enabled and the DMA timer has been armed.
985 */
986void hdaR3StreamMarkStarted(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint64_t tsNow)
987{
988 pStreamShared->State.tsLastReadNs = RTTimeNanoTS();
989 pStreamShared->State.tsStart = tsNow;
990 pStreamShared->State.tsAioDelayEnd = tsNow + PDMDevHlpTimerFromMilli(pDevIns, pStreamShared->hTimer, pThis->msInitialDelay);
991 Log3Func(("#%u: tsStart=%RU64 tsAioDelayEnd=%RU64 tsLastReadNs=%RU64\n", pStreamShared->u8SD,
992 pStreamShared->State.tsStart, pStreamShared->State.tsAioDelayEnd, pStreamShared->State.tsLastReadNs));
993
994}
995
996/**
997 * Marks the stream as stopped.
998 */
999void hdaR3StreamMarkStopped(PHDASTREAM pStreamShared)
1000{
1001 Log3Func(("#%u\n", pStreamShared->u8SD));
1002 pStreamShared->State.tsAioDelayEnd = UINT64_MAX;
1003}
1004
1005
1006#if 0 /* Not used atm. */
1007static uint32_t hdaR3StreamGetPosition(PHDASTATE pThis, PHDASTREAM pStreamShared)
1008{
1009 return HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD);
1010}
1011#endif
1012
1013/**
1014 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
1015 * setting its associated LPIB register and DMA position buffer (if enabled) to an absolute value.
1016 *
1017 * @param pStreamShared HDA stream to update read / write position for (shared).
1018 * @param pDevIns The device instance.
1019 * @param pThis The shared HDA device state.
1020 * @param uLPIB Absolute position (in bytes) to set current read / write position to.
1021 */
1022static void hdaR3StreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB)
1023{
1024 AssertPtrReturnVoid(pStreamShared);
1025 AssertReturnVoid (uLPIB <= pStreamShared->u32CBL); /* Make sure that we don't go out-of-bounds. */
1026
1027 Log3Func(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n", pStreamShared->u8SD, uLPIB, pThis->fDMAPosition));
1028
1029 /* Update LPIB in any case. */
1030 HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) = uLPIB;
1031
1032 /* Do we need to tell the current DMA position? */
1033 if (pThis->fDMAPosition)
1034 {
1035 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns,
1036 pThis->u64DPBase + (pStreamShared->u8SD * 2 * sizeof(uint32_t)),
1037 (void *)&uLPIB, sizeof(uint32_t));
1038 AssertRC(rc2);
1039 }
1040}
1041
1042/**
1043 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
1044 * adding a value to its associated LPIB register and DMA position buffer (if enabled).
1045 *
1046 * @note Handles automatic CBL wrap-around.
1047 *
1048 * @param pStreamShared HDA stream to update read / write position for (shared).
1049 * @param pDevIns The device instance.
1050 * @param pThis The shared HDA device state.
1051 * @param cbToAdd Position (in bytes) to add to the current read / write position.
1052 */
1053static void hdaR3StreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t cbToAdd)
1054{
1055 if (cbToAdd) /* No need to update anything if 0. */
1056 {
1057 uint32_t const uCBL = pStreamShared->u32CBL;
1058 if (uCBL) /* paranoia */
1059 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis,
1060 (HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) + cbToAdd) % uCBL);
1061 }
1062}
1063
1064/**
1065 * Retrieves the available size of (buffered) audio data (in bytes) of a given HDA stream.
1066 *
1067 * @returns Available data (in bytes).
1068 * @param pStreamR3 HDA stream to retrieve size for (ring-3).
1069 */
1070static uint32_t hdaR3StreamGetUsed(PHDASTREAMR3 pStreamR3)
1071{
1072 AssertPtrReturn(pStreamR3, 0);
1073
1074 if (pStreamR3->State.pCircBuf)
1075 return (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1076 return 0;
1077}
1078
1079/**
1080 * Retrieves the free size of audio data (in bytes) of a given HDA stream.
1081 *
1082 * @returns Free data (in bytes).
1083 * @param pStreamR3 HDA stream to retrieve size for (ring-3).
1084 */
1085static uint32_t hdaR3StreamGetFree(PHDASTREAMR3 pStreamR3)
1086{
1087 AssertPtrReturn(pStreamR3, 0);
1088
1089 if (pStreamR3->State.pCircBuf)
1090 return (uint32_t)RTCircBufFree(pStreamR3->State.pCircBuf);
1091 return 0;
1092}
1093
1094/**
1095 * Get the current address and number of bytes left in the current BDLE.
1096 *
1097 * @returns The current physical address.
1098 * @param pStreamShared The stream to check.
1099 * @param pcbLeft The number of bytes left at the returned address.
1100 */
1101DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGet(PHDASTREAM pStreamShared, uint32_t *pcbLeft)
1102{
1103 uint8_t idxBdle = pStreamShared->State.idxCurBdle;
1104 AssertStmt(idxBdle < pStreamShared->State.cBdles, idxBdle = 0);
1105
1106 uint32_t const cbCurBdl = pStreamShared->State.aBdl[idxBdle].cb;
1107 uint32_t offCurBdle = pStreamShared->State.offCurBdle;
1108 AssertStmt(pStreamShared->State.offCurBdle <= cbCurBdl, offCurBdle = cbCurBdl);
1109
1110 *pcbLeft = cbCurBdl - offCurBdle;
1111 return pStreamShared->State.aBdl[idxBdle].GCPhys + offCurBdle;
1112}
1113
1114/**
1115 * Get the size of the current BDLE.
1116 *
1117 * @returns The size (in bytes).
1118 * @param pStreamShared The stream to check.
1119 */
1120DECLINLINE(RTGCPHYS) hdaR3StreamDmaBufGetSize(PHDASTREAM pStreamShared)
1121{
1122 uint8_t idxBdle = pStreamShared->State.idxCurBdle;
1123 AssertStmt(idxBdle < pStreamShared->State.cBdles, idxBdle = 0);
1124 return pStreamShared->State.aBdl[idxBdle].cb;
1125}
1126
1127/**
1128 * Checks if the current BDLE is completed.
1129 *
1130 * @retval true if complete
1131 * @retval false if not.
1132 * @param pStreamShared The stream to check.
1133 */
1134DECLINLINE(bool) hdaR3StreamDmaBufIsComplete(PHDASTREAM pStreamShared)
1135{
1136 uint8_t const idxBdle = pStreamShared->State.idxCurBdle;
1137 AssertReturn(idxBdle < pStreamShared->State.cBdles, true);
1138
1139 uint32_t const cbCurBdl = pStreamShared->State.aBdl[idxBdle].cb;
1140 uint32_t const offCurBdle = pStreamShared->State.offCurBdle;
1141 Assert(offCurBdle <= cbCurBdl);
1142 return offCurBdle >= cbCurBdl;
1143}
1144
1145/**
1146 * Checks if the current BDLE needs a completion IRQ.
1147 *
1148 * @retval true if IRQ is needed.
1149 * @retval false if not.
1150 * @param pStreamShared The stream to check.
1151 */
1152DECLINLINE(bool) hdaR3StreamDmaBufNeedsIrq(PHDASTREAM pStreamShared)
1153{
1154 uint8_t const idxBdle = pStreamShared->State.idxCurBdle;
1155 AssertReturn(idxBdle < pStreamShared->State.cBdles, false);
1156 return (pStreamShared->State.aBdl[idxBdle].fFlags & HDA_BDLE_F_IOC) != 0;
1157}
1158
1159/**
1160 * Advances the DMA engine to the next BDLE.
1161 *
1162 * @param pStreamShared The stream which DMA engine is to be updated.
1163 */
1164DECLINLINE(void) hdaR3StreamDmaBufAdvanceToNext(PHDASTREAM pStreamShared)
1165{
1166 uint8_t idxBdle = pStreamShared->State.idxCurBdle;
1167 Assert(pStreamShared->State.offCurBdle == pStreamShared->State.aBdl[idxBdle].cb);
1168
1169 if (idxBdle < pStreamShared->State.cBdles - 1)
1170 idxBdle++;
1171 else
1172 idxBdle = 0;
1173 pStreamShared->State.idxCurBdle = idxBdle;
1174 pStreamShared->State.offCurBdle = 0;
1175}
1176
1177/**
1178 * Common do-DMA prologue code.
1179 *
1180 * @retval true if DMA processing can take place
1181 * @retval false if caller should return immediately.
1182 * @param pThis The shared HDA device state.
1183 * @param pStreamShared HDA stream to update (shared).
1184 * @param uSD The stream ID (for asserting).
1185 * @param tsNowNs The current RTTimeNano() value.
1186 * @param pszFunction The function name (for logging).
1187 */
1188DECLINLINE(bool) hdaR3StreamDoDmaPrologue(PHDASTATE pThis, PHDASTREAM pStreamShared, uint8_t uSD,
1189 uint64_t tsNowNs, const char *pszFunction)
1190{
1191 RT_NOREF(uSD, pszFunction);
1192
1193 /*
1194 * Check if we should skip town...
1195 */
1196 /* Stream not running (anymore)? */
1197 if (pStreamShared->State.fRunning)
1198 { /* likely */ }
1199 else
1200 {
1201 Log3(("%s: [SD%RU8] Not running, skipping transfer\n", pszFunction, uSD));
1202 return false;
1203 }
1204
1205 if (!(HDA_STREAM_REG(pThis, STS, uSD) & HDA_SDSTS_BCIS))
1206 { /* likely */ }
1207 else
1208 {
1209 Log3(("%s: [SD%RU8] BCIS bit set, skipping transfer\n", pszFunction, uSD));
1210#ifdef HDA_STRICT
1211 /* Timing emulation bug or guest is misbehaving -- let me know. */
1212 AssertMsgFailed(("%s: BCIS bit for stream #%RU8 still set when it shouldn't\n", pszFunction, uSD));
1213#endif
1214 return false;
1215 }
1216
1217 /*
1218 * Stream sanity checks.
1219 */
1220 /* Register sanity checks. */
1221 Assert(uSD < HDA_MAX_STREAMS);
1222 Assert(pStreamShared->u64BDLBase);
1223 Assert(pStreamShared->u32CBL);
1224 Assert(pStreamShared->u8FIFOS);
1225
1226 /* State sanity checks. */
1227 Assert(ASMAtomicReadBool(&pStreamShared->State.fInReset) == false);
1228 Assert(ASMAtomicReadBool(&pStreamShared->State.fRunning));
1229
1230 /*
1231 * Some timestamp stuff for logging/debugging.
1232 */
1233 /*const uint64_t tsNowNs = RTTimeNanoTS();*/
1234 Log3(("%s: [SD%RU8] tsDeltaNs=%'RU64 ns\n", pszFunction, uSD, tsNowNs - pStreamShared->State.tsLastTransferNs));
1235 pStreamShared->State.tsLastTransferNs = tsNowNs;
1236
1237 return true;
1238}
1239
1240/**
1241 * Common do-DMA epilogue.
1242 *
1243 * @param pDevIns The device instance.
1244 * @param pStreamShared The HDA stream (shared).
1245 * @param pStreamR3 The HDA stream (ring-3).
1246 */
1247DECLINLINE(void) hdaR3StreamDoDmaEpilogue(PPDMDEVINS pDevIns, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
1248{
1249 /*
1250 * We must update this in the epilogue rather than in the prologue
1251 * as it is used for WALCLK calculation and we must make sure the
1252 * guest doesn't think we've processed the current period till we
1253 * actually have.
1254 */
1255 pStreamShared->State.tsTransferLast = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer);
1256
1257 /*
1258 * Update the buffer statistics.
1259 */
1260 pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1261}
1262
1263/**
1264 * Completes a BDLE at the end of a DMA loop iteration, if possible.
1265 *
1266 * @param pDevIns The device instance.
1267 * @param pThis The shared HDA device state.
1268 * @param pStreamShared HDA stream to update (shared).
1269 * @param pszFunction The function name (for logging).
1270 */
1271DECLINLINE(void) hdaR3StreamDoDmaMaybeCompleteBuffer(PPDMDEVINS pDevIns, PHDASTATE pThis,
1272 PHDASTREAM pStreamShared, const char *pszFunction)
1273{
1274 RT_NOREF(pszFunction);
1275
1276 /*
1277 * Is the buffer descriptor complete.
1278 */
1279 if (hdaR3StreamDmaBufIsComplete(pStreamShared))
1280 {
1281 Log3(("%s: [SD%RU8] Completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x\n", pszFunction, pStreamShared->u8SD,
1282 pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
1283 pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
1284 pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags));
1285
1286 /*
1287 * Update the stream's current position.
1288 *
1289 * Do this as accurate and close to the actual data transfer as possible.
1290 * All guetsts rely on this, depending on the mechanism they use (LPIB register or DMA counters).
1291 *
1292 * Note for Windows 10: The OS' driver is *very* picky about *when* the (DMA) positions get updated!
1293 * Not doing this at the right time will result in ugly sound crackles!
1294 */
1295 hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, hdaR3StreamDmaBufGetSize(pStreamShared));
1296
1297 /* Does the current BDLE require an interrupt to be sent? */
1298 if (hdaR3StreamDmaBufNeedsIrq(pStreamShared))
1299 {
1300 /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL
1301 register is set we need to generate an interrupt. */
1302 if (HDA_STREAM_REG(pThis, CTL, pStreamShared->u8SD) & HDA_SDCTL_IOCE)
1303 {
1304 /* Assert the interrupt before actually fetching the next BDLE below. */
1305 pStreamShared->State.cTransferPendingInterrupts = 1;
1306 Log3(("%s: [SD%RU8] Scheduling interrupt\n", pszFunction, pStreamShared->u8SD));
1307
1308 /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
1309 * ending / beginning of a period. */
1310 /** @todo r=bird: What does the above comment mean? */
1311 HDA_STREAM_REG(pThis, STS, pStreamShared->u8SD) |= HDA_SDSTS_BCIS;
1312 HDA_PROCESS_INTERRUPT(pDevIns, pThis);
1313 }
1314 }
1315
1316 /*
1317 * Advance to the next BDLE.
1318 */
1319 hdaR3StreamDmaBufAdvanceToNext(pStreamShared);
1320 }
1321 else
1322 Log3(("%s: [SD%RU8] Not completed BDLE%u %#RX64 LB %#RX32 fFlags=%#x: off=%#RX32\n", pszFunction, pStreamShared->u8SD,
1323 pStreamShared->State.idxCurBdle, pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].GCPhys,
1324 pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].cb,
1325 pStreamShared->State.aBdl[pStreamShared->State.idxCurBdle].fFlags, pStreamShared->State.offCurBdle));
1326}
1327
1328/**
1329 * Does DMA transfer for an HDA input stream.
1330 *
1331 * Reads audio data from the HDA stream's internal DMA buffer and writing to
1332 * guest memory.
1333 *
1334 * @param pDevIns The device instance.
1335 * @param pThis The shared HDA device state.
1336 * @param pStreamShared HDA stream to update (shared).
1337 * @param pStreamR3 HDA stream to update (ring-3).
1338 * @param cbToConsume The max amount of data to consume from the
1339 * internal DMA buffer. The caller will make sure
1340 * this is always the transfer size fo the current
1341 * period (unless something is seriously wrong).
1342 * @param fWriteSilence Whether to feed the guest silence rather than
1343 * fetching bytes from the internal DMA buffer.
1344 * This is set initially while we pre-buffer a
1345 * little bit of input, so we can better handle
1346 * time catch-ups and other schduling fun.
1347 * @param tsNowNs The current RTTimeNano() value.
1348 *
1349 * @remarks Caller owns the stream lock.
1350 */
1351static void hdaR3StreamDoDmaInput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared,
1352 PHDASTREAMR3 pStreamR3, uint32_t cbToConsume, bool fWriteSilence, uint64_t tsNowNs)
1353{
1354 uint8_t const uSD = pStreamShared->u8SD;
1355 LogFlowFunc(("ENTER - #%u cbToConsume=%#x%s\n", uSD, cbToConsume, fWriteSilence ? " silence" : ""));
1356
1357 /*
1358 * Common prologue.
1359 */
1360 if (hdaR3StreamDoDmaPrologue(pThis, pStreamShared, uSD, tsNowNs, "hdaR3StreamDoDmaInput"))
1361 { /* likely */ }
1362 else
1363 return;
1364
1365 /*
1366 *
1367 * The DMA copy loop.
1368 *
1369 */
1370 uint8_t abBounce[4096 + 128]; /* Most guest does at most 4KB BDLE. So, 4KB + space for a partial frame to reduce loops. */
1371 uint32_t cbBounce = 0; /* in case of incomplete frames between buffer segments */
1372 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
1373 uint32_t cbLeft = cbToConsume;
1374 Assert(cbLeft == pStreamShared->State.cbTransferSize);
1375 Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
1376
1377 while (cbLeft > 0)
1378 {
1379 STAM_PROFILE_START(&pThis->StatIn, a);
1380
1381 /*
1382 * Figure out how much we can read & write in this iteration.
1383 */
1384 uint32_t cbChunk = 0;
1385 RTGCPHYS GCPhys = hdaR3StreamDmaBufGet(pStreamShared, &cbChunk);
1386
1387 /* Need to diverge if the frame format differs or if we're writing silence. */
1388 if ( !pStreamR3->State.Mapping.fMappingNeeded
1389 && !fWriteSilence)
1390 {
1391 if (cbChunk <= cbLeft)
1392 { /* very likely */ }
1393 else
1394 cbChunk = cbLeft;
1395
1396 /*
1397 * Write the host data directly into the guest buffers.
1398 */
1399 while (cbChunk > 0)
1400 {
1401 /* Grab internal DMA buffer space and read into it. */
1402 void /*const*/ *pvBufSrc;
1403 size_t cbBufSrc;
1404 RTCircBufAcquireReadBlock(pCircBuf, cbChunk, &pvBufSrc, &cbBufSrc);
1405 AssertBreakStmt(cbBufSrc, RTCircBufReleaseReadBlock(pCircBuf, 0));
1406
1407 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, pvBufSrc, cbBufSrc);
1408 AssertRC(rc2);
1409
1410#ifdef HDA_DEBUG_SILENCE
1411 fix me if relevant;
1412#endif
1413 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
1414 { /* likely */ }
1415 else
1416 AudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufSrc, cbBufSrc, 0 /* fFlags */);
1417
1418#ifdef VBOX_WITH_DTRACE
1419 VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBufSrc, pStreamR3->State.offRead);
1420#endif
1421 pStreamR3->State.offRead += cbBufSrc;
1422 RTCircBufReleaseReadBlock(pCircBuf, cbBufSrc);
1423 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufSrc);
1424
1425 /* advance */
1426 cbChunk -= (uint32_t)cbBufSrc;
1427 GCPhys += cbBufSrc;
1428 cbLeft -= (uint32_t)cbBufSrc;
1429 pStreamShared->State.offCurBdle += (uint32_t)cbBufSrc;
1430 }
1431 }
1432 /*
1433 * Either we've got some initial silence to write, or we need to do
1434 * channel mapping. Both produces guest output into the bounce buffer,
1435 * which is then copied into guest memory. The bounce buffer may keep
1436 * partial frames there for the next BDLE, if an BDLE isn't frame aligned.
1437 *
1438 * Note! cbLeft is relative to the input (host) frame size.
1439 * cbChunk OTOH is relative to output (guest) size.
1440 */
1441 else
1442 {
1443 Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
1444 uint32_t const cbLeftGuest = PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps,
1445 PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props,
1446 cbLeft));
1447 if (cbChunk <= cbLeftGuest)
1448 { /* very likely */ }
1449 else
1450 cbChunk = cbLeftGuest;
1451
1452 /*
1453 * Work till we've covered the chunk.
1454 */
1455 Log5Func(("loop0: GCPhys=%RGp cbChunk=%#x + cbBounce=%#x\n", GCPhys, cbChunk, cbBounce));
1456 while (cbChunk > 0)
1457 {
1458 /* Figure out how much we need to convert into the bounce buffer: */
1459 uint32_t cbGuest = PDMAudioPropsRoundUpBytesToFrame(&pStreamR3->State.Mapping.GuestProps, cbChunk - cbBounce);
1460 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStreamR3->State.Mapping.GuestProps,
1461 RT_MIN(cbGuest, sizeof(abBounce) - cbBounce));
1462 size_t cbBufSrc;
1463 if (!fWriteSilence)
1464 {
1465 /** @todo we could loop here to optimize buffer wrap around. Not important now though. */
1466 void /*const*/ *pvBufSrc;
1467 RTCircBufAcquireReadBlock(pCircBuf, PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFrames),
1468 &pvBufSrc, &cbBufSrc);
1469
1470 uint32_t const cFramesToConvert = PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props,
1471 (uint32_t)cbBufSrc);
1472 Assert(PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFramesToConvert) == cbBufSrc);
1473 Assert(cFramesToConvert > 0);
1474 Assert(cFramesToConvert <= cFrames);
1475
1476 pStreamR3->State.Mapping.pfnHostToGuest(&abBounce[cbBounce], pvBufSrc, cFramesToConvert,
1477 &pStreamR3->State.Mapping);
1478 Log5Func((" loop1: cbBounce=%#05x cFramesToConvert=%#05x cbBufSrc=%#x%s\n",
1479 cbBounce, cFramesToConvert, cbBufSrc, ASMMemIsZero(pvBufSrc, cbBufSrc) ? " all zero" : ""));
1480#ifdef HDA_DEBUG_SILENCE
1481 fix me if relevant;
1482#endif
1483 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
1484 { /* likely */ }
1485 else
1486 AudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufSrc, cbBufSrc, 0 /* fFlags */);
1487
1488#ifdef VBOX_WITH_DTRACE
1489 VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBufSrc, pStreamR3->State.offRead);
1490#endif
1491
1492 pStreamR3->State.offRead += cbBufSrc;
1493 RTCircBufReleaseReadBlock(pCircBuf, cbBufSrc);
1494
1495 cFrames = cFramesToConvert;
1496 cbGuest = cbBounce + PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFrames);
1497 }
1498 else
1499 {
1500 cbBufSrc = PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFrames);
1501 cbGuest = PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFrames);
1502 PDMAudioPropsClearBuffer(&pStreamR3->State.Mapping.GuestProps,
1503 &abBounce[cbBounce], cbGuest, cFrames);
1504 cbGuest += cbBounce;
1505 }
1506
1507 /* Write it to the guest buffer. */
1508 uint32_t cbGuestActual = RT_MIN(cbGuest, cbChunk);
1509 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, abBounce, cbGuestActual);
1510 AssertRC(rc2);
1511 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbGuestActual);
1512
1513 /* advance */
1514 cbLeft -= (uint32_t)cbBufSrc;
1515 cbChunk -= cbGuestActual;
1516 GCPhys += cbGuestActual;
1517 pStreamShared->State.offCurBdle += cbGuestActual;
1518
1519 cbBounce = cbGuest - cbGuestActual;
1520 if (cbBounce)
1521 memmove(abBounce, &abBounce[cbGuestActual], cbBounce);
1522
1523 Log5Func((" loop1: GCPhys=%RGp cbGuestActual=%#x cbBounce=%#x cFrames=%#x\n", GCPhys, cbGuestActual, cbBounce, cFrames));
1524 }
1525 Log5Func(("loop0: GCPhys=%RGp cbBounce=%#x cbLeft=%#x\n", GCPhys, cbBounce, cbLeft));
1526 }
1527
1528 STAM_PROFILE_STOP(&pThis->StatIn, a);
1529
1530 /*
1531 * Complete the buffer if necessary (common with the output DMA code).
1532 */
1533 hdaR3StreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaInput");
1534 }
1535
1536 Assert(cbLeft == 0); /* There shall be no break statements in the above loop, so cbLeft is always zero here! */
1537 AssertMsg(cbBounce == 0, ("%#x\n", cbBounce));
1538
1539 /*
1540 * Common epilogue.
1541 */
1542 hdaR3StreamDoDmaEpilogue(pDevIns, pStreamShared, pStreamR3);
1543
1544 /*
1545 * Log and leave.
1546 */
1547 Log3Func(("LEAVE - [SD%RU8] %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
1548 uSD, cbToConsume, pStreamShared->State.cbTransferSize, pStreamR3->State.offRead - cbToConsume,
1549 pStreamShared->State.cTransferPendingInterrupts));
1550}
1551
1552
1553/**
1554 * Input streams: Pulls data from the mixer, putting it in the internal DMA
1555 * buffer.
1556 *
1557 * @param pStreamR3 HDA stream to update (ring-3 bits).
1558 * @param pSink The mixer sink to pull from.
1559 */
1560static void hdaR3StreamPullFromMixer(PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink)
1561{
1562#ifdef LOG_ENABLED
1563 uint64_t const offWriteOld = pStreamR3->State.offWrite;
1564#endif
1565 pStreamR3->State.offWrite = AudioMixerSinkTransferFromCircBuf(pSink,
1566 pStreamR3->State.pCircBuf,
1567 pStreamR3->State.offWrite,
1568 pStreamR3->u8SD,
1569 pStreamR3->Dbg.Runtime.fEnabled
1570 ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
1571
1572 Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
1573 pStreamR3->State.offWrite - offWriteOld, pStreamR3->State.offWrite));
1574
1575 /* Update buffer stats. */
1576 pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1577}
1578
1579
1580/**
1581 * Does DMA transfer for an HDA output stream.
1582 *
1583 * This transfers one DMA timer period worth of data from the guest and into the
1584 * internal DMA buffer.
1585 *
1586 * @param pDevIns The device instance.
1587 * @param pThis The shared HDA device state.
1588 * @param pStreamShared HDA stream to update (shared).
1589 * @param pStreamR3 HDA stream to update (ring-3).
1590 * @param cbToProduce The max amount of data to produce (i.e. put into
1591 * the circular buffer). Unless something is going
1592 * seriously wrong, this will always be transfer
1593 * size for the current period.
1594 * @param tsNowNs The current RTTimeNano() value.
1595 *
1596 * @remarks Caller owns the stream lock.
1597 */
1598static void hdaR3StreamDoDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared,
1599 PHDASTREAMR3 pStreamR3, uint32_t cbToProduce, uint64_t tsNowNs)
1600{
1601 uint8_t const uSD = pStreamShared->u8SD;
1602 LogFlowFunc(("ENTER - #%u cbToProduce=%#x\n", uSD, cbToProduce));
1603
1604 /*
1605 * Common prologue.
1606 */
1607 if (hdaR3StreamDoDmaPrologue(pThis, pStreamShared, uSD, tsNowNs, "hdaR3StreamDoDmaOutput"))
1608 { /* likely */ }
1609 else
1610 return;
1611
1612 /*
1613 *
1614 * The DMA copy loop.
1615 *
1616 */
1617 uint8_t abBounce[4096 + 128]; /* Most guest does at most 4KB BDLE. So, 4KB + space for a partial frame to reduce loops. */
1618 uint32_t cbBounce = 0; /* in case of incomplete frames between buffer segments */
1619 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
1620 uint32_t cbLeft = cbToProduce;
1621 Assert(cbLeft == pStreamShared->State.cbTransferSize);
1622 Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
1623
1624 while (cbLeft > 0)
1625 {
1626 STAM_PROFILE_START(&pThis->StatOut, a);
1627
1628 /*
1629 * Figure out how much we can read & write in this iteration.
1630 */
1631 uint32_t cbChunk = 0;
1632 RTGCPHYS GCPhys = hdaR3StreamDmaBufGet(pStreamShared, &cbChunk);
1633
1634 /* Need to diverge if the frame format differs. */
1635 if ( !pStreamR3->State.Mapping.fMappingNeeded
1636 /** @todo && pStreamShared->State.fFrameAlignedBuffers */)
1637 {
1638 if (cbChunk <= cbLeft)
1639 { /* very likely */ }
1640 else
1641 cbChunk = cbLeft;
1642
1643 /*
1644 * Read the guest data directly into the internal DMA buffer.
1645 */
1646 while (cbChunk > 0)
1647 {
1648 /* Grab internal DMA buffer space and read into it. */
1649 void *pvBufDst;
1650 size_t cbBufDst;
1651 RTCircBufAcquireWriteBlock(pCircBuf, cbChunk, &pvBufDst, &cbBufDst);
1652 AssertBreakStmt(cbBufDst, RTCircBufReleaseWriteBlock(pCircBuf, 0));
1653
1654 int rc2 = PDMDevHlpPhysRead(pDevIns, GCPhys, pvBufDst, cbBufDst);
1655 AssertRC(rc2);
1656
1657#ifdef HDA_DEBUG_SILENCE
1658 fix me if relevant;
1659#endif
1660 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
1661 { /* likely */ }
1662 else
1663 AudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufDst, cbBufDst, 0 /* fFlags */);
1664
1665#ifdef VBOX_WITH_DTRACE
1666 VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBufDst, pStreamR3->State.offWrite);
1667#endif
1668 pStreamR3->State.offWrite += cbBufDst;
1669 RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
1670 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufDst);
1671
1672 /* advance */
1673 cbChunk -= (uint32_t)cbBufDst;
1674 GCPhys += cbBufDst;
1675 cbLeft -= (uint32_t)cbBufDst;
1676 pStreamShared->State.offCurBdle += (uint32_t)cbBufDst;
1677 }
1678 }
1679 /*
1680 * Need to map the frame content, so we need to read the guest data
1681 * into a temporary buffer, though the output can be directly written
1682 * into the internal buffer as it is assumed to be frame aligned.
1683 *
1684 * Note! cbLeft is relative to the output frame size.
1685 * cbChunk OTOH is relative to input size.
1686 */
1687 else
1688 {
1689 Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
1690 uint32_t const cbLeftGuest = PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps,
1691 PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props,
1692 cbLeft));
1693 if (cbChunk <= cbLeftGuest)
1694 { /* very likely */ }
1695 else
1696 cbChunk = cbLeftGuest;
1697
1698 /*
1699 * Loop till we've covered the chunk.
1700 */
1701 Log5Func(("loop0: GCPhys=%RGp cbChunk=%#x + cbBounce=%#x\n", GCPhys, cbChunk, cbBounce));
1702 while (cbChunk > 0)
1703 {
1704 /* Read into the bounce buffer. */
1705 uint32_t const cbToRead = RT_MIN(cbChunk, sizeof(abBounce) - cbBounce);
1706 int rc2 = PDMDevHlpPhysRead(pDevIns, GCPhys, &abBounce[cbBounce], cbToRead);
1707 AssertRC(rc2);
1708 cbBounce += cbToRead;
1709
1710 /* Convert the size to whole frames and a remainder. */
1711 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStreamR3->State.Mapping.GuestProps, cbBounce);
1712 uint32_t const cbRemainder = cbBounce - PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFrames);
1713 Log5Func((" loop1: GCPhys=%RGp cbToRead=%#x cbBounce=%#x cFrames=%#x\n", GCPhys, cbToRead, cbBounce, cFrames));
1714
1715 /*
1716 * Convert from the bounce buffer and into the internal DMA buffer.
1717 */
1718 uint32_t offBounce = 0;
1719 while (cFrames > 0)
1720 {
1721 void *pvBufDst;
1722 size_t cbBufDst;
1723 RTCircBufAcquireWriteBlock(pCircBuf, PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFrames),
1724 &pvBufDst, &cbBufDst);
1725
1726 uint32_t const cFramesToConvert = PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props, (uint32_t)cbBufDst);
1727 Assert(PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFramesToConvert) == cbBufDst);
1728 Assert(cFramesToConvert > 0);
1729 Assert(cFramesToConvert <= cFrames);
1730
1731 pStreamR3->State.Mapping.pfnGuestToHost(pvBufDst, &abBounce[offBounce], cFramesToConvert,
1732 &pStreamR3->State.Mapping);
1733 Log5Func((" loop2: offBounce=%#05x cFramesToConvert=%#05x cbBufDst=%#x%s\n",
1734 offBounce, cFramesToConvert, cbBufDst, ASMMemIsZero(pvBufDst, cbBufDst) ? " all zero" : ""));
1735
1736# ifdef HDA_DEBUG_SILENCE
1737 fix me if relevant;
1738# endif
1739 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
1740 { /* likely */ }
1741 else
1742 AudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufDst, cbBufDst, 0 /* fFlags */);
1743
1744 pStreamR3->State.offWrite += cbBufDst;
1745 RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
1746 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufDst);
1747
1748 /* advance */
1749 cbLeft -= (uint32_t)cbBufDst;
1750 cFrames -= cFramesToConvert;
1751 offBounce += PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFramesToConvert);
1752 }
1753
1754 /* advance */
1755 cbChunk -= cbToRead;
1756 GCPhys += cbToRead;
1757 pStreamShared->State.offCurBdle += cbToRead;
1758 if (cbRemainder)
1759 memmove(&abBounce[0], &abBounce[cbBounce - cbRemainder], cbRemainder);
1760 cbBounce = cbRemainder;
1761 }
1762 Log5Func(("loop0: GCPhys=%RGp cbBounce=%#x cbLeft=%#x\n", GCPhys, cbBounce, cbLeft));
1763 }
1764
1765 STAM_PROFILE_STOP(&pThis->StatOut, a);
1766
1767 /*
1768 * Complete the buffer if necessary (common with the output DMA code).
1769 */
1770 hdaR3StreamDoDmaMaybeCompleteBuffer(pDevIns, pThis, pStreamShared, "hdaR3StreamDoDmaOutput");
1771 }
1772
1773 Assert(cbLeft == 0); /* There shall be no break statements in the above loop, so cbLeft is always zero here! */
1774 AssertMsg(cbBounce == 0, ("%#x\n", cbBounce));
1775
1776 /*
1777 * Common epilogue.
1778 */
1779 hdaR3StreamDoDmaEpilogue(pDevIns, pStreamShared, pStreamR3);
1780
1781 /*
1782 * Log and leave.
1783 */
1784 Log3Func(("LEAVE - [SD%RU8] %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
1785 uSD, cbToProduce, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbToProduce,
1786 pStreamShared->State.cTransferPendingInterrupts));
1787}
1788
1789
1790/**
1791 * Output streams: Pushes data to the mixer.
1792 *
1793 * @param pStreamShared HDA stream to update (shared bits).
1794 * @param pStreamR3 HDA stream to update (ring-3 bits).
1795 * @param pSink The mixer sink to push to.
1796 * @param nsNow The current RTTimeNanoTS() value.
1797 */
1798static void hdaR3StreamPushToMixer(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PAUDMIXSINK pSink, uint64_t nsNow)
1799{
1800#ifdef LOG_ENABLED
1801 uint64_t const offReadOld = pStreamR3->State.offRead;
1802#endif
1803 pStreamR3->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
1804 pStreamR3->State.pCircBuf,
1805 pStreamR3->State.offRead,
1806 pStreamR3->u8SD,
1807 pStreamR3->Dbg.Runtime.fEnabled
1808 ? pStreamR3->Dbg.Runtime.pFileStream : NULL);
1809
1810 Assert(nsNow >= pStreamShared->State.tsLastReadNs);
1811 Log3Func(("[SD%RU8] nsDeltaLastRead=%RI64 transferred=%#RX64 bytes -> @%#RX64\n", pStreamR3->u8SD,
1812 nsNow - pStreamShared->State.tsLastReadNs, pStreamR3->State.offRead - offReadOld, pStreamR3->State.offRead));
1813 RT_NOREF(pStreamShared, nsNow);
1814
1815 /* Update buffer stats. */
1816 pStreamR3->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
1817}
1818
1819
1820/**
1821 * The stream's main function when called by the timer.
1822 *
1823 * @note This function also will be called without timer invocation when
1824 * starting (enabling) the stream to minimize startup latency.
1825 *
1826 * @returns Current timer time if the timer is enabled, otherwise zero.
1827 * @param pDevIns The device instance.
1828 * @param pThis The shared HDA device state.
1829 * @param pThisCC The ring-3 HDA device state.
1830 * @param pStreamShared HDA stream to update (shared bits).
1831 * @param pStreamR3 HDA stream to update (ring-3 bits).
1832 */
1833uint64_t hdaR3StreamTimerMain(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
1834 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
1835{
1836 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1837 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pStreamShared->hTimer));
1838
1839 /* Do the work: */
1840 hdaR3StreamUpdateDma(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3);
1841
1842 /* Re-arm the timer if the sink is still active: */
1843 if ( pStreamShared->State.fRunning
1844 && pStreamR3->pMixSink
1845 && AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink))
1846 {
1847 /* Advance the schduling: */
1848 uint32_t idxSched = pStreamShared->State.idxSchedule;
1849 AssertStmt(idxSched < RT_ELEMENTS(pStreamShared->State.aSchedule), idxSched = 0);
1850 uint32_t idxLoop = pStreamShared->State.idxScheduleLoop + 1;
1851 if (idxLoop >= pStreamShared->State.aSchedule[idxSched].cLoops)
1852 {
1853 idxSched += 1;
1854 if ( idxSched >= pStreamShared->State.cSchedule
1855 || idxSched >= RT_ELEMENTS(pStreamShared->State.aSchedule) /*paranoia^2*/)
1856 {
1857 idxSched = pStreamShared->State.cSchedulePrologue;
1858 AssertStmt(idxSched < RT_ELEMENTS(pStreamShared->State.aSchedule), idxSched = 0);
1859 }
1860 pStreamShared->State.idxSchedule = idxSched;
1861 idxLoop = 0;
1862 }
1863 pStreamShared->State.idxScheduleLoop = (uint16_t)idxLoop;
1864
1865 /* Do the actual timer re-arming. */
1866 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
1867 uint64_t const tsTransferNext = tsNow + pStreamShared->State.aSchedule[idxSched].cPeriodTicks;
1868 Log3Func(("[SD%RU8] fSinkActive=true, tsTransferNext=%RU64 (in %RU64)\n",
1869 pStreamShared->u8SD, tsTransferNext, tsTransferNext - tsNow));
1870 int rc = PDMDevHlpTimerSet(pDevIns, pStreamShared->hTimer, tsTransferNext);
1871 AssertRC(rc);
1872
1873 /* Some legacy stuff: */
1874 pStreamShared->State.tsTransferNext = tsTransferNext;
1875 pStreamShared->State.cbTransferSize = pStreamShared->State.aSchedule[idxSched].cbPeriod;
1876
1877 return tsNow;
1878 }
1879
1880 Log3Func(("[SD%RU8] fSinkActive=false\n", pStreamShared->u8SD));
1881 return 0;
1882}
1883
1884
1885/**
1886 * Updates a HDA stream by doing DMA transfers.
1887 *
1888 * Will do mixer transfers too to try fix an overrun/underrun situation.
1889 *
1890 * The host sink(s) set the overall pace (bird: no it doesn't, the DMA timer
1891 * does - we just hope like heck it matches the speed at which the *backend*
1892 * host audio driver processes samples).
1893 *
1894 * @param pDevIns The device instance.
1895 * @param pThis The shared HDA device state.
1896 * @param pThisCC The ring-3 HDA device state.
1897 * @param pStreamShared HDA stream to update (shared bits).
1898 * @param pStreamR3 HDA stream to update (ring-3 bits).
1899 */
1900static void hdaR3StreamUpdateDma(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
1901 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
1902{
1903 RT_NOREF(pThisCC);
1904 int rc2;
1905
1906 /*
1907 * Make sure we're running and got an active mixer sink.
1908 */
1909 if (RT_LIKELY(pStreamShared->State.fRunning))
1910 { /* likely */ }
1911 else
1912 return;
1913
1914 PAUDMIXSINK pSink = NULL;
1915 if (pStreamR3->pMixSink)
1916 pSink = pStreamR3->pMixSink->pMixSink;
1917 if (RT_LIKELY(AudioMixerSinkIsActive(pSink)))
1918 { /* likely */ }
1919 else
1920 return;
1921
1922 /*
1923 * Get scheduling info common to both input and output streams.
1924 */
1925 const uint64_t tsNowNs = RTTimeNanoTS();
1926 uint32_t idxSched = pStreamShared->State.idxSchedule;
1927 AssertStmt(idxSched < RT_MIN(RT_ELEMENTS(pStreamShared->State.aSchedule), pStreamShared->State.cSchedule), idxSched = 0);
1928 uint32_t const cbPeriod = pStreamShared->State.aSchedule[idxSched].cbPeriod;
1929
1930 /*
1931 * Output streams (SDO).
1932 */
1933 if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT)
1934 {
1935 /*
1936 * Check how much room we have in our DMA buffer. There should be at
1937 * least one period worth of space there or we're in an overflow situation.
1938 */
1939 uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1940 if (cbStreamFree >= cbPeriod)
1941 { /* likely */ }
1942 else
1943 {
1944 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems);
1945 Log(("hdaR3StreamUpdateDma: Warning! Stream #%u has insufficient space free: %u bytes, need %u. Will try move data out of the buffer...\n",
1946 pStreamShared->u8SD, cbStreamFree, cbPeriod));
1947 int rc = AudioMixerSinkTryLock(pSink);
1948 if (RT_SUCCESS(rc))
1949 {
1950 hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, tsNowNs);
1951 AudioMixerSinkUpdate(pSink);
1952 AudioMixerSinkUnlock(pSink);
1953 }
1954 else
1955 RTThreadYield();
1956 Log(("hdaR3StreamUpdateDma: Gained %u bytes.\n", hdaR3StreamGetFree(pStreamR3) - cbStreamFree));
1957
1958 cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1959 if (cbStreamFree < cbPeriod)
1960 {
1961 /* Unable to make sufficient space. Drop the whole buffer content.
1962 * This is needed in order to keep the device emulation running at a constant rate,
1963 * at the cost of losing valid (but too much) data. */
1964 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowErrors);
1965 LogRel2(("HDA: Warning: Hit stream #%RU8 overflow, dropping %u bytes of audio data\n",
1966 pStreamShared->u8SD, hdaR3StreamGetUsed(pStreamR3)));
1967# ifdef HDA_STRICT
1968 AssertMsgFailed(("Hit stream #%RU8 overflow -- timing bug?\n", pStreamShared->u8SD));
1969# endif
1970 RTCircBufReset(pStreamR3->State.pCircBuf);
1971 pStreamR3->State.offWrite = 0;
1972 pStreamR3->State.offRead = 0;
1973 cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1974 }
1975 }
1976
1977 /*
1978 * Do the DMA transfer.
1979 */
1980 rc2 = PDMDevHlpCritSectEnter(pDevIns, &pStreamShared->CritSect, VERR_IGNORED);
1981 AssertRC(rc2);
1982
1983 uint64_t const offWriteBefore = pStreamR3->State.offWrite;
1984 hdaR3StreamDoDmaOutput(pDevIns, pThis, pStreamShared, pStreamR3, RT_MIN(cbStreamFree, cbPeriod), tsNowNs);
1985
1986 rc2 = PDMDevHlpCritSectLeave(pDevIns, &pStreamShared->CritSect);
1987 AssertRC(rc2);
1988
1989 /*
1990 * Should we push data to down thru the mixer to and to the host drivers?
1991 *
1992 * We initially delay this by pThis->msInitialDelay, but after than we'll
1993 * kick the AIO thread every time we've put more data in the buffer (which is
1994 * every time) as the host audio device needs to get data in a timely manner.
1995 *
1996 * (We used to try only wake up the AIO thread according to pThis->uIoTimer
1997 * and host wall clock, but that meant we would miss a wakup after the DMA
1998 * timer was called a little late or if TM entered into catch-up mode.)
1999 */
2000 bool fKickAioThread;
2001 if (!pStreamShared->State.tsAioDelayEnd)
2002 fKickAioThread = pStreamR3->State.offWrite > offWriteBefore
2003 || hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2;
2004 else if (PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer) >= pStreamShared->State.tsAioDelayEnd)
2005 {
2006 Log3Func(("Initial delay done: Passed tsAioDelayEnd.\n"));
2007 pStreamShared->State.tsAioDelayEnd = 0;
2008 fKickAioThread = true;
2009 }
2010 else if (hdaR3StreamGetFree(pStreamR3) < pStreamShared->State.cbAvgTransfer * 2)
2011 {
2012 Log3Func(("Initial delay done: Passed running short on buffer.\n"));
2013 pStreamShared->State.tsAioDelayEnd = 0;
2014 fKickAioThread = true;
2015 }
2016 else
2017 {
2018 Log3Func(("Initial delay pending...\n"));
2019 fKickAioThread = false;
2020 }
2021
2022 Log3Func(("msDelta=%RU64 (vs %u) cbStreamFree=%#x (vs %#x) => fKickAioThread=%RTbool\n",
2023 (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS,
2024 pStreamShared->State.Cfg.Device.cMsSchedulingHint, cbStreamFree,
2025 pStreamShared->State.cbAvgTransfer * 2, fKickAioThread));
2026
2027 if (fKickAioThread)
2028 {
2029 /* Notify the async I/O worker thread that there's work to do. */
2030 Log5Func(("Notifying AIO thread\n"));
2031 rc2 = AudioMixerSinkSignalUpdateJob(pSink);
2032 AssertRC(rc2);
2033 /* Update last read timestamp for logging/debugging. */
2034 pStreamShared->State.tsLastReadNs = tsNowNs;
2035 }
2036 }
2037 /*
2038 * Input stream (SDI).
2039 */
2040 else
2041 {
2042 Assert(hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_IN);
2043
2044 /*
2045 * See how much data we've got buffered...
2046 */
2047 bool fWriteSilence = false;
2048 uint32_t cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
2049 if (pStreamShared->State.fInputPreBuffered && cbStreamUsed >= cbPeriod)
2050 { /*likely*/ }
2051 /*
2052 * Because it may take a while for the input stream to get going (at
2053 * least with pulseaudio), we feed the guest silence till we've
2054 * pre-buffer a reasonable amount of audio.
2055 */
2056 else if (!pStreamShared->State.fInputPreBuffered)
2057 {
2058 if (cbStreamUsed < pStreamShared->State.cbInputPreBuffer)
2059 {
2060 Log3(("hdaR3StreamUpdateDma: Pre-buffering (got %#x out of %#x bytes)...\n",
2061 cbStreamUsed, pStreamShared->State.cbInputPreBuffer));
2062 fWriteSilence = true;
2063 }
2064 else
2065 {
2066 Log3(("hdaR3StreamUpdateDma: Completed pre-buffering (got %#x, needed %#x bytes).\n",
2067 cbStreamUsed, pStreamShared->State.cbInputPreBuffer));
2068 pStreamShared->State.fInputPreBuffered = true;
2069 fWriteSilence = true; /* For now, just do the most conservative thing. */
2070 }
2071 cbStreamUsed = cbPeriod;
2072 }
2073 /*
2074 * When we're low on data, we must really try fetch some ourselves
2075 * as buffer underruns must not happen.
2076 */
2077 else
2078 {
2079 /** @todo We're ending up here to frequently with pulse audio at least (just
2080 * watch the stream stats in the statistcs viewer, and way to often we
2081 * have to inject silence bytes. I suspect part of the problem is
2082 * that the HDA device require a much better latency than what the
2083 * pulse audio is configured for by default (10 ms vs 150ms). */
2084 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowProblems);
2085 Log(("hdaR3StreamUpdateDma: Warning! Stream #%u has insufficient data available: %u bytes, need %u. Will try move pull more data into the buffer...\n",
2086 pStreamShared->u8SD, cbStreamUsed, cbPeriod));
2087 int rc = AudioMixerSinkTryLock(pSink);
2088 if (RT_SUCCESS(rc))
2089 {
2090 AudioMixerSinkUpdate(pSink);
2091 hdaR3StreamPullFromMixer(pStreamR3, pSink);
2092 AudioMixerSinkUnlock(pSink);
2093 }
2094 else
2095 RTThreadYield();
2096 Log(("hdaR3StreamUpdateDma: Gained %u bytes.\n", hdaR3StreamGetUsed(pStreamR3) - cbStreamUsed));
2097 cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
2098 if (cbStreamUsed < cbPeriod)
2099 {
2100 /* Unable to find sufficient input data by simple prodding.
2101 In order to keep a constant byte stream following thru the DMA
2102 engine into the guest, we will try again and then fall back on
2103 filling the gap with silence. */
2104 uint32_t cbSilence = 0;
2105 do
2106 {
2107 AudioMixerSinkLock(pSink);
2108
2109 cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
2110 if (cbStreamUsed < cbPeriod)
2111 {
2112 hdaR3StreamPullFromMixer(pStreamR3, pSink);
2113 cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
2114 while (cbStreamUsed < cbPeriod)
2115 {
2116 void *pvDstBuf;
2117 size_t cbDstBuf;
2118 RTCircBufAcquireWriteBlock(pStreamR3->State.pCircBuf, cbPeriod - cbStreamUsed,
2119 &pvDstBuf, &cbDstBuf);
2120 RT_BZERO(pvDstBuf, cbDstBuf);
2121 RTCircBufReleaseWriteBlock(pStreamR3->State.pCircBuf, cbDstBuf);
2122 cbSilence += (uint32_t)cbDstBuf;
2123 cbStreamUsed += (uint32_t)cbDstBuf;
2124 }
2125 }
2126
2127 AudioMixerSinkUnlock(pSink);
2128 } while (cbStreamUsed < cbPeriod);
2129 if (cbSilence > 0)
2130 {
2131 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowErrors);
2132 STAM_REL_COUNTER_ADD(&pStreamR3->State.StatDmaFlowErrorBytes, cbSilence);
2133 LogRel2(("HDA: Warning: Stream #%RU8 underrun, added %u bytes of silence (%u us)\n", pStreamShared->u8SD,
2134 cbSilence, PDMAudioPropsBytesToMicro(&pStreamR3->State.Mapping.GuestProps, cbSilence)));
2135 }
2136 }
2137 }
2138
2139 /*
2140 * Do the DMA'ing.
2141 */
2142 if (cbStreamUsed)
2143 {
2144 rc2 = PDMDevHlpCritSectEnter(pDevIns, &pStreamShared->CritSect, VERR_IGNORED);
2145 AssertRC(rc2);
2146
2147 hdaR3StreamDoDmaInput(pDevIns, pThis, pStreamShared, pStreamR3,
2148 RT_MIN(cbStreamUsed, cbPeriod), fWriteSilence, tsNowNs);
2149
2150 rc2 = PDMDevHlpCritSectLeave(pDevIns, &pStreamShared->CritSect);
2151 AssertRC(rc2);
2152 }
2153
2154 /*
2155 * We should always kick the AIO thread.
2156 */
2157 /** @todo This isn't entirely ideal. If we get into an underrun situation,
2158 * we ideally want the AIO thread to run right before the DMA timer
2159 * rather than right after it ran. */
2160 Log5Func(("Notifying AIO thread\n"));
2161 rc2 = AudioMixerSinkSignalUpdateJob(pSink);
2162 AssertRC(rc2);
2163 pStreamShared->State.tsLastReadNs = tsNowNs;
2164 }
2165}
2166
2167
2168/**
2169 * @callback_method_impl{FNAUDMIXSINKUPDATE}
2170 *
2171 * For output streams this moves data from the internal DMA buffer (in which
2172 * hdaR3StreamUpdateDma put it), thru the mixer and to the various backend audio
2173 * devices.
2174 *
2175 * For input streams this pulls data from the backend audio device(s), thru the
2176 * mixer and puts it in the internal DMA buffer ready for hdaR3StreamUpdateDma
2177 * to pump into guest memory.
2178 */
2179DECLCALLBACK(void) hdaR3StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser)
2180{
2181 PHDASTATE const pThis = PDMDEVINS_2_DATA(pDevIns, PHDASTATE);
2182 PHDASTATER3 const pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHDASTATER3);
2183 PHDASTREAMR3 const pStreamR3 = (PHDASTREAMR3)pvUser;
2184 PHDASTREAM const pStreamShared = &pThis->aStreams[pStreamR3 - &pThisCC->aStreams[0]];
2185 Assert(pStreamR3 - &pThisCC->aStreams[0] == pStreamR3->u8SD);
2186 Assert(pStreamShared->u8SD == pStreamR3->u8SD);
2187 RT_NOREF(pSink);
2188
2189 /*
2190 * Make sure we haven't change sink and that it's still active (it
2191 * should be or we wouldn't have been called).
2192 */
2193 AssertReturnVoid(pStreamR3->pMixSink && pSink == pStreamR3->pMixSink->pMixSink);
2194 AssertReturnVoid(AudioMixerSinkIsActive(pSink));
2195
2196 /*
2197 * Output streams (SDO).
2198 */
2199 if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT)
2200 hdaR3StreamPushToMixer(pStreamShared, pStreamR3, pSink, RTTimeNanoTS());
2201 /*
2202 * Input stream (SDI).
2203 */
2204 else
2205 {
2206 Assert(hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_IN);
2207 hdaR3StreamPullFromMixer(pStreamR3, pSink);
2208 }
2209}
2210
2211#endif /* IN_RING3 */
2212
2213/**
2214 * Locks an HDA stream for serialized access.
2215 *
2216 * @returns VBox status code.
2217 * @param pStreamShared HDA stream to lock (shared bits).
2218 */
2219void hdaStreamLock(PHDASTREAM pStreamShared)
2220{
2221 AssertPtrReturnVoid(pStreamShared);
2222 int rc2 = PDMCritSectEnter(&pStreamShared->CritSect, VINF_SUCCESS);
2223 AssertRC(rc2);
2224}
2225
2226/**
2227 * Unlocks a formerly locked HDA stream.
2228 *
2229 * @returns VBox status code.
2230 * @param pStreamShared HDA stream to unlock (shared bits).
2231 */
2232void hdaStreamUnlock(PHDASTREAM pStreamShared)
2233{
2234 AssertPtrReturnVoid(pStreamShared);
2235 int rc2 = PDMCritSectLeave(&pStreamShared->CritSect);
2236 AssertRC(rc2);
2237}
2238
2239#ifdef IN_RING3
2240
2241#if 0 /* unused - no prototype even */
2242/**
2243 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
2244 * updating its associated LPIB register and DMA position buffer (if enabled).
2245 *
2246 * @returns Set LPIB value.
2247 * @param pDevIns The device instance.
2248 * @param pStream HDA stream to update read / write position for.
2249 * @param u32LPIB New LPIB (position) value to set.
2250 */
2251uint32_t hdaR3StreamUpdateLPIB(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint32_t u32LPIB)
2252{
2253 AssertMsg(u32LPIB <= pStreamShared->u32CBL,
2254 ("[SD%RU8] New LPIB (%RU32) exceeds CBL (%RU32)\n", pStreamShared->u8SD, u32LPIB, pStreamShared->u32CBL));
2255
2256 u32LPIB = RT_MIN(u32LPIB, pStreamShared->u32CBL);
2257
2258 LogFlowFunc(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
2259 pStreamShared->u8SD, u32LPIB, pThis->fDMAPosition));
2260
2261 /* Update LPIB in any case. */
2262 HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) = u32LPIB;
2263
2264 /* Do we need to tell the current DMA position? */
2265 if (pThis->fDMAPosition)
2266 {
2267 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns,
2268 pThis->u64DPBase + (pStreamShared->u8SD * 2 * sizeof(uint32_t)),
2269 (void *)&u32LPIB, sizeof(uint32_t));
2270 AssertRC(rc2);
2271 }
2272
2273 return u32LPIB;
2274}
2275#endif
2276
2277# ifdef HDA_USE_DMA_ACCESS_HANDLER
2278/**
2279 * Registers access handlers for a stream's BDLE DMA accesses.
2280 *
2281 * @returns true if registration was successful, false if not.
2282 * @param pStream HDA stream to register BDLE access handlers for.
2283 */
2284bool hdaR3StreamRegisterDMAHandlers(PHDASTREAM pStream)
2285{
2286 /* At least LVI and the BDL base must be set. */
2287 if ( !pStreamShared->u16LVI
2288 || !pStreamShared->u64BDLBase)
2289 {
2290 return false;
2291 }
2292
2293 hdaR3StreamUnregisterDMAHandlers(pStream);
2294
2295 LogFunc(("Registering ...\n"));
2296
2297 int rc = VINF_SUCCESS;
2298
2299 /*
2300 * Create BDLE ranges.
2301 */
2302
2303 struct BDLERANGE
2304 {
2305 RTGCPHYS uAddr;
2306 uint32_t uSize;
2307 } arrRanges[16]; /** @todo Use a define. */
2308
2309 size_t cRanges = 0;
2310
2311 for (uint16_t i = 0; i < pStreamShared->u16LVI + 1; i++)
2312 {
2313 HDABDLE BDLE;
2314 rc = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, i /* Index */);
2315 if (RT_FAILURE(rc))
2316 break;
2317
2318 bool fAddRange = true;
2319 BDLERANGE *pRange;
2320
2321 if (cRanges)
2322 {
2323 pRange = &arrRanges[cRanges - 1];
2324
2325 /* Is the current range a direct neighbor of the current BLDE? */
2326 if ((pRange->uAddr + pRange->uSize) == BDLE.Desc.u64BufAddr)
2327 {
2328 /* Expand the current range by the current BDLE's size. */
2329 pRange->uSize += BDLE.Desc.u32BufSize;
2330
2331 /* Adding a new range in this case is not needed anymore. */
2332 fAddRange = false;
2333
2334 LogFunc(("Expanding range %zu by %RU32 (%RU32 total now)\n", cRanges - 1, BDLE.Desc.u32BufSize, pRange->uSize));
2335 }
2336 }
2337
2338 /* Do we need to add a new range? */
2339 if ( fAddRange
2340 && cRanges < RT_ELEMENTS(arrRanges))
2341 {
2342 pRange = &arrRanges[cRanges];
2343
2344 pRange->uAddr = BDLE.Desc.u64BufAddr;
2345 pRange->uSize = BDLE.Desc.u32BufSize;
2346
2347 LogFunc(("Adding range %zu - 0x%x (%RU32)\n", cRanges, pRange->uAddr, pRange->uSize));
2348
2349 cRanges++;
2350 }
2351 }
2352
2353 LogFunc(("%zu ranges total\n", cRanges));
2354
2355 /*
2356 * Register all ranges as DMA access handlers.
2357 */
2358
2359 for (size_t i = 0; i < cRanges; i++)
2360 {
2361 BDLERANGE *pRange = &arrRanges[i];
2362
2363 PHDADMAACCESSHANDLER pHandler = (PHDADMAACCESSHANDLER)RTMemAllocZ(sizeof(HDADMAACCESSHANDLER));
2364 if (!pHandler)
2365 {
2366 rc = VERR_NO_MEMORY;
2367 break;
2368 }
2369
2370 RTListAppend(&pStream->State.lstDMAHandlers, &pHandler->Node);
2371
2372 pHandler->pStream = pStream; /* Save a back reference to the owner. */
2373
2374 char szDesc[32];
2375 RTStrPrintf(szDesc, sizeof(szDesc), "HDA[SD%RU8 - RANGE%02zu]", pStream->u8SD, i);
2376
2377 int rc2 = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3), PGMPHYSHANDLERKIND_WRITE,
2378 hdaDMAAccessHandler,
2379 NULL, NULL, NULL,
2380 NULL, NULL, NULL,
2381 szDesc, &pHandler->hAccessHandlerType);
2382 AssertRCBreak(rc2);
2383
2384 pHandler->BDLEAddr = pRange->uAddr;
2385 pHandler->BDLESize = pRange->uSize;
2386
2387 /* Get first and last pages of the BDLE range. */
2388 RTGCPHYS pgFirst = pRange->uAddr & ~PAGE_OFFSET_MASK;
2389 RTGCPHYS pgLast = RT_ALIGN(pgFirst + pRange->uSize, PAGE_SIZE);
2390
2391 /* Calculate the region size (in pages). */
2392 RTGCPHYS regionSize = RT_ALIGN(pgLast - pgFirst, PAGE_SIZE);
2393
2394 pHandler->GCPhysFirst = pgFirst;
2395 pHandler->GCPhysLast = pHandler->GCPhysFirst + (regionSize - 1);
2396
2397 LogFunc(("\tRegistering region '%s': 0x%x - 0x%x (region size: %zu)\n",
2398 szDesc, pHandler->GCPhysFirst, pHandler->GCPhysLast, regionSize));
2399 LogFunc(("\tBDLE @ 0x%x - 0x%x (%RU32)\n",
2400 pHandler->BDLEAddr, pHandler->BDLEAddr + pHandler->BDLESize, pHandler->BDLESize));
2401
2402 rc2 = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
2403 pHandler->GCPhysFirst, pHandler->GCPhysLast,
2404 pHandler->hAccessHandlerType, pHandler, NIL_RTR0PTR, NIL_RTRCPTR,
2405 szDesc);
2406 AssertRCBreak(rc2);
2407
2408 pHandler->fRegistered = true;
2409 }
2410
2411 LogFunc(("Registration ended with rc=%Rrc\n", rc));
2412
2413 return RT_SUCCESS(rc);
2414}
2415
2416/**
2417 * Unregisters access handlers of a stream's BDLEs.
2418 *
2419 * @param pStream HDA stream to unregister BDLE access handlers for.
2420 */
2421void hdaR3StreamUnregisterDMAHandlers(PHDASTREAM pStream)
2422{
2423 LogFunc(("\n"));
2424
2425 PHDADMAACCESSHANDLER pHandler, pHandlerNext;
2426 RTListForEachSafe(&pStream->State.lstDMAHandlers, pHandler, pHandlerNext, HDADMAACCESSHANDLER, Node)
2427 {
2428 if (!pHandler->fRegistered) /* Handler not registered? Skip. */
2429 continue;
2430
2431 LogFunc(("Unregistering 0x%x - 0x%x (%zu)\n",
2432 pHandler->GCPhysFirst, pHandler->GCPhysLast, pHandler->GCPhysLast - pHandler->GCPhysFirst));
2433
2434 int rc2 = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
2435 pHandler->GCPhysFirst);
2436 AssertRC(rc2);
2437
2438 RTListNodeRemove(&pHandler->Node);
2439
2440 RTMemFree(pHandler);
2441 pHandler = NULL;
2442 }
2443
2444 Assert(RTListIsEmpty(&pStream->State.lstDMAHandlers));
2445}
2446
2447# endif /* HDA_USE_DMA_ACCESS_HANDLER */
2448
2449#endif /* IN_RING3 */
Note: See TracBrowser for help on using the repository browser.

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