VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/HDAStream.cpp@ 88137

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

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

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