VirtualBox

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

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

Audio: Moving some of the DrvAudio.h stuff into PDM - VBox/vmm/pdmaudioinline.h. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.9 KB
Line 
1/* $Id: HDAStream.cpp 88028 2021-03-08 19:31:22Z 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 * Sets up ((re-)iniitalizes) an HDA stream.
226 *
227 * @returns IPRT status code. VINF_NO_CHANGE if the stream does not need
228 * be set-up again because the stream's (hardware) parameters did
229 * not change.
230 * @param pDevIns The device instance.
231 * @param pThis The shared HDA device state (for HW register
232 * parameters).
233 * @param pStreamShared HDA stream to set up, shared portion.
234 * @param pStreamR3 HDA stream to set up, ring-3 portion.
235 * @param uSD Stream descriptor number to assign it.
236 */
237int hdaR3StreamSetUp(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD)
238{
239 /* This must be valid all times. */
240 AssertReturn(uSD < HDA_MAX_STREAMS, VERR_INVALID_PARAMETER);
241
242 /* These member can only change on data corruption, despite what the code does further down (bird). */
243 AssertReturn(pStreamShared->u8SD == uSD, VERR_WRONG_ORDER);
244 AssertReturn(pStreamR3->u8SD == uSD, VERR_WRONG_ORDER);
245
246 const uint64_t u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, uSD),
247 HDA_STREAM_REG(pThis, BDPU, uSD));
248 const uint16_t u16LVI = HDA_STREAM_REG(pThis, LVI, uSD);
249 const uint32_t u32CBL = HDA_STREAM_REG(pThis, CBL, uSD);
250 const uint8_t u8FIFOS = HDA_STREAM_REG(pThis, FIFOS, uSD) + 1;
251 uint8_t u8FIFOW = hdaSDFIFOWToBytes(HDA_STREAM_REG(pThis, FIFOW, uSD));
252 const uint16_t u16FMT = HDA_STREAM_REG(pThis, FMT, uSD);
253
254 /* Is the bare minimum set of registers configured for the stream?
255 * If not, bail out early, as there's nothing to do here for us (yet). */
256 if ( !u64BDLBase
257 || !u16LVI
258 || !u32CBL
259 || !u8FIFOS
260 || !u8FIFOW
261 || !u16FMT)
262 {
263 LogFunc(("[SD%RU8] Registers not set up yet, skipping (re-)initialization\n", uSD));
264 return VINF_SUCCESS;
265 }
266
267 PDMAUDIOPCMPROPS Props;
268 int rc = hdaR3SDFMTToPCMProps(u16FMT, &Props);
269 if (RT_FAILURE(rc))
270 {
271 LogRelMax(32, ("HDA: Warning: Format 0x%x for stream #%RU8 not supported\n", HDA_STREAM_REG(pThis, FMT, uSD), uSD));
272 return rc;
273 }
274
275 /* Reset (any former) stream map. */
276 hdaR3StreamMapReset(&pStreamR3->State.Mapping);
277
278 /*
279 * Initialize the stream mapping in any case, regardless if
280 * we support surround audio or not. This is needed to handle
281 * the supported channels within a single audio stream, e.g. mono/stereo.
282 *
283 * In other words, the stream mapping *always* knows the real
284 * number of channels in a single audio stream.
285 */
286 rc = hdaR3StreamMapInit(&pStreamR3->State.Mapping, &Props);
287 AssertRCReturn(rc, rc);
288
289 ASSERT_GUEST_LOGREL_MSG_RETURN( pStreamR3->State.Mapping.cbFrameSize > 0
290 && u32CBL % pStreamR3->State.Mapping.cbFrameSize == 0,
291 ("CBL for stream #%RU8 does not align to frame size (u32CBL=%u cbFrameSize=%u)\n",
292 uSD, u32CBL, pStreamR3->State.Mapping.cbFrameSize),
293 VERR_INVALID_PARAMETER);
294
295#ifndef VBOX_WITH_AUDIO_HDA_51_SURROUND
296 if (Props.cChannels > 2)
297 {
298 /*
299 * When not running with surround support enabled, override the audio channel count
300 * with stereo (2) channels so that we at least can properly work with those.
301 *
302 * Note: This also involves dealing with surround setups the guest might has set up for us.
303 */
304 LogRelMax(32, ("HDA: Warning: More than stereo (2) channels are not supported (%RU8 requested), falling back to stereo channels for stream #%RU8\n",
305 Props.cChannels, uSD));
306 Props.cChannels = 2;
307 Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(Props.cbSample, Props.cChannels);
308 }
309#endif
310
311 /* Make sure the guest behaves regarding the stream's FIFO. */
312 ASSERT_GUEST_LOGREL_MSG_STMT(u8FIFOW <= u8FIFOS,
313 ("Guest tried setting a bigger FIFOW (%RU8) than FIFOS (%RU8), limiting\n", u8FIFOW, u8FIFOS),
314 u8FIFOW = u8FIFOS /* ASSUMES that u8FIFOS has been validated. */);
315
316 pStreamShared->u8SD = uSD;
317
318 /* Update all register copies so that we later know that something has changed. */
319 pStreamShared->u64BDLBase = u64BDLBase;
320 pStreamShared->u16LVI = u16LVI;
321 pStreamShared->u32CBL = u32CBL;
322 pStreamShared->u8FIFOS = u8FIFOS;
323 pStreamShared->u8FIFOW = u8FIFOW;
324 pStreamShared->u16FMT = u16FMT;
325
326 PPDMAUDIOSTREAMCFG pCfg = &pStreamShared->State.Cfg;
327 pCfg->Props = Props;
328
329 /* Set the stream's direction. */
330 pCfg->enmDir = hdaGetDirFromSD(uSD);
331
332 /* The the stream's name, based on the direction. */
333 switch (pCfg->enmDir)
334 {
335 case PDMAUDIODIR_IN:
336# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
337# error "Implement me!"
338# else
339 pCfg->u.enmSrc = PDMAUDIORECSRC_LINE;
340 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
341 RTStrCopy(pCfg->szName, sizeof(pCfg->szName), "Line In");
342# endif
343 break;
344
345 case PDMAUDIODIR_OUT:
346 /* Destination(s) will be set in hdaAddStreamOut(),
347 * based on the channels / stream layout. */
348 break;
349
350 default:
351 AssertFailedReturn(VERR_NOT_SUPPORTED);
352 break;
353 }
354
355 LogRel2(("HDA: Stream #%RU8 DMA @ 0x%x (%RU32 bytes = %RU64ms total)\n",
356 uSD, pStreamShared->u64BDLBase, pStreamShared->u32CBL,
357 PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->u32CBL)));
358
359 /* Figure out how many transfer fragments we're going to use for this stream. */
360 uint32_t cTransferFragments = pStreamShared->u16LVI + 1;
361 if (cTransferFragments <= 1)
362 LogRel(("HDA: Warning: Stream #%RU8 transfer fragments (%RU16) invalid -- buggy guest audio driver!\n",
363 uSD, pStreamShared->u16LVI));
364
365 /*
366 * Handle the stream's position adjustment.
367 */
368 uint32_t cfPosAdjust = 0;
369
370 LogFunc(("[SD%RU8] fPosAdjustEnabled=%RTbool, cPosAdjustFrames=%RU16\n",
371 uSD, pThis->fPosAdjustEnabled, pThis->cPosAdjustFrames));
372
373 if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
374 {
375 HDABDLE BDLE;
376 RT_ZERO(BDLE);
377
378 int rc2 = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, 0 /* Entry */);
379 AssertRC(rc2);
380
381 /* Note: Do *not* check if this BDLE aligns to the stream's frame size.
382 * It can happen that this isn't the case on some guests, e.g.
383 * on Windows with a 5.1 speaker setup.
384 *
385 * The only thing which counts is that the stream's CBL value
386 * properly aligns to the stream's frame size.
387 */
388
389 /* If no custom set position adjustment is set, apply some
390 * simple heuristics to detect the appropriate position adjustment. */
391 if ( !pThis->cPosAdjustFrames
392 /* Position adjustmenet buffer *must* have the IOC bit set! */
393 && hdaR3BDLENeedsInterrupt(&BDLE))
394 {
395 /** @todo Implement / use a (dynamic) table once this gets more complicated. */
396#ifdef VBOX_WITH_INTEL_HDA
397 /* Intel ICH / PCH: 1 frame. */
398 if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbFrameSize))
399 {
400 cfPosAdjust = 1;
401 }
402 /* Intel Baytrail / Braswell: 32 frames. */
403 else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbFrameSize))
404 {
405 cfPosAdjust = 32;
406 }
407#endif
408 }
409 else /* Go with the set default. */
410 cfPosAdjust = pThis->cPosAdjustFrames;
411
412 if (cfPosAdjust)
413 {
414 /* Also adjust the number of fragments, as the position adjustment buffer
415 * does not count as an own fragment as such.
416 *
417 * This e.g. can happen on (newer) Ubuntu guests which use
418 * 4 (IOC) + 4408 (IOC) + 4408 (IOC) + 4408 (IOC) + 4404 (= 17632) bytes,
419 * where the first buffer (4) is used as position adjustment.
420 *
421 * Only skip a fragment if the whole buffer fragment is used for
422 * position adjustment.
423 */
424 if ((cfPosAdjust * pStreamR3->State.Mapping.cbFrameSize) == BDLE.Desc.u32BufSize)
425 cTransferFragments--;
426
427 /* Initialize position adjustment counter. */
428 pStreamShared->State.cfPosAdjustDefault = cfPosAdjust;
429 pStreamShared->State.cfPosAdjustLeft = cfPosAdjust;
430 LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", uSD, cfPosAdjust));
431 }
432 }
433
434 Log3Func(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU32\n", uSD, cfPosAdjust, cTransferFragments));
435
436 /*
437 * Set up data transfer stuff.
438 */
439
440 /* Assign the global device rate to the stream I/O timer as default. */
441 pStreamShared->State.uTimerIoHz = pThis->uTimerHz;
442
443 /*
444 * Determine the transfer Hz the guest OS expects data transfer at.
445 *
446 * Guests also expect a very extact DMA timing for reading / writing audio data, so we run on a constant
447 * (virtual) rate which we expose to the guest.
448 *
449 * Data rate examples:
450 * * Windows 10 @ 44,1kHz / 16-bit
451 * 2 channels (stereo):
452 * * Default mode: 448 audio frames -> ~10.15ms = 1792 byte every ~10ms.
453 * * Fast mode: 128 audio frames -> ~ 2.90ms = 512 byte every ~3ms.
454 * 6 channels (5.1 surround):
455 * * Default mode: Same values as above!
456 */
457
458 /* Audio data per second the stream needs. */
459 const uint32_t cbDataPerSec = PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.PCMProps, RT_MS_1SEC);
460
461 /* This is used to indicate whether we're done or should the uTimerIoHz as fallback. */
462 rc = VINF_SUCCESS;
463
464 /* The transfer Hz depend on the heuristics above, that is,
465 how often the guest expects to see a new data transfer. */
466 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.uTimerIoHz,
467 ("I/O timer Hz rate for stream #%RU8 is invalid\n", uSD),
468 pStreamShared->State.uTimerIoHz = HDA_TIMER_HZ_DEFAULT);
469 unsigned uTransferHz = pStreamShared->State.uTimerIoHz;
470
471 LogRel2(("HDA: Stream #%RU8 needs %RU32 bytes/s audio data\n", uSD, cbDataPerSec));
472
473 if (pThis->fTransferHeuristicsEnabled) /* Are data transfer heuristics enabled? */
474 {
475 /* Don't take frames (as bytes) into account which are part of the position adjustment. */
476 uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbFrameSize;
477 uint32_t cbTransferHeuristics = 0;
478 uint32_t cbTransferHeuristicsCur = 0;
479 uint32_t cBufferIrqs = 0;
480 for (uint32_t i = 0; i < cTransferFragments; i++)
481 {
482 /** @todo wrong read type! */
483 HDABDLEDESC bd = { 0, 0, 0 };
484 PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
485
486 LogRel2(("HDA: Stream #%RU8 BDLE%03u: %#RX64 LB %#x %s (%#x)\n", uSD, i,
487 bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC ? " IOC=1" : "", bd.fFlags));
488
489 /* Position adjustment (still) needed / active? */
490 if (cbTransferHeuristicsPosAdjust)
491 {
492 const uint32_t cbTransferHeuristicsPosAdjustMin = RT_MIN(cbTransferHeuristicsPosAdjust, bd.u32BufSize);
493
494 bd.u32BufSize -= cbTransferHeuristicsPosAdjustMin;
495 cbTransferHeuristicsPosAdjust -= cbTransferHeuristicsPosAdjustMin;
496 }
497
498 /* Anything left to process for the current BDLE after doing the position adjustment? */
499 if (bd.u32BufSize == 0)
500 continue;
501
502 /* Is an interrupt expected for the current BDLE? */
503 if (bd.fFlags & HDA_BDLE_F_IOC)
504 {
505 cbTransferHeuristicsCur += bd.u32BufSize;
506 if ( cbTransferHeuristicsCur == cbTransferHeuristics
507 || !cbTransferHeuristics)
508 cbTransferHeuristics = cbTransferHeuristicsCur;
509 else
510 {
511 /** @todo r=bird: you need to find the smallest common denominator here, not
512 * just the minimum. Ignoring this for now as windows has two equal
513 * sized buffers both with IOC set. */
514 LogRelMax(32, ("HDA: Uneven IRQ buffer config; i=%u cbCur=%#x cbMin=%#x.\n", i, cbTransferHeuristicsCur, cbTransferHeuristics));
515 cbTransferHeuristics = RT_MIN(cbTransferHeuristicsCur, cbTransferHeuristics);
516 }
517 cbTransferHeuristicsCur = 0;
518 cBufferIrqs++;
519 }
520 else /* No interrupt expected -> add it to the former BDLE size. */
521 cbTransferHeuristicsCur += bd.u32BufSize;
522 }
523
524 /*
525 * If the guest doesn't use buffer IRQs or only has one, just split the total
526 * buffer length in half and use that as timer heuristics. That gives the
527 * guest half a buffer to fill while we're processing the other half.
528 */
529 if (cBufferIrqs <= 1)
530 cbTransferHeuristics = pStreamShared->u32CBL / 2;
531
532 /* Paranoia (fall back on I/O timer Hz if this happens). */
533 if (cbTransferHeuristics >= 8)
534 {
535 ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics),
536 ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
537 uSD, cbTransferHeuristics, cbTransferHeuristics));
538
539 uint64_t const cTimerTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
540 uint64_t const cbTransferPerSec = RT_MAX(pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize,
541 4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */);
542
543 /* Make sure the period is 250ms (random value) or less, in case the guest
544 want to play the whole "Der Ring des Nibelungen" cycle in one go. Just
545 halve the buffer till we get there. */
546 while (cbTransferHeuristics > 1024 && cbTransferHeuristics > cbTransferPerSec / 4)
547 cbTransferHeuristics = PDMAudioPropsFloorBytesToFrame(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics / 2);
548
549 /* Set the transfer size per timer callout. (No chunking, so same.) */
550 pStreamShared->State.cbTransferSize = cbTransferHeuristics;
551 pStreamShared->State.cbTransferChunk = cbTransferHeuristics;
552 ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics),
553 ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
554 uSD, cbTransferHeuristics, cbTransferHeuristics));
555
556 /* Convert to timer ticks. */
557 pStreamShared->State.cTicksPerByte = (cTimerTicksPerSec + cbTransferPerSec / 2) / cbTransferPerSec;
558 AssertStmt(pStreamShared->State.cTicksPerByte, pStreamShared->State.cTicksPerByte = 4096);
559
560 pStreamShared->State.cTransferTicks = (cTimerTicksPerSec * cbTransferHeuristics + cbTransferPerSec / 2)
561 / cbTransferPerSec;
562
563 /* Estimate timer HZ for the circular buffer setup. */
564 uTransferHz = cbTransferPerSec * 1000 / cbTransferHeuristics;
565 LogRel2(("HDA: Stream #%RU8 needs a data transfer at least every %RU64 ticks / %RU32 bytes / approx %u.%03u Hz\n",
566 uSD, pStreamShared->State.cTransferTicks, cbTransferHeuristics, uTransferHz / 1000, uTransferHz % 1000));
567 uTransferHz /= 1000;
568
569 /* Indicate that we're done with period calculation. */
570 rc = VINF_ALREADY_INITIALIZED;
571 }
572 }
573
574 if (uTransferHz > 400) /* Anything above 400 Hz looks fishy -- tell the user. */
575 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",
576 uSD, uTransferHz));
577
578 /* Set I/O scheduling hint for the backends. */
579 pCfg->Device.cMsSchedulingHint = RT_MS_1SEC / pStreamShared->State.uTimerIoHz;
580 LogRel2(("HDA: Stream #%RU8 set scheduling hint for the backends to %RU32ms\n", uSD, pCfg->Device.cMsSchedulingHint));
581
582 if (rc != VINF_ALREADY_INITIALIZED && RT_SUCCESS(rc))
583 {
584 /*
585 * Transfer heuristics disabled or failed.
586 */
587 Assert(uTransferHz == pStreamShared->State.uTimerIoHz);
588 LogRel2(("HDA: Stream #%RU8 transfer timer and I/O timer rate is %u Hz.\n", uSD, uTransferHz));
589
590 /* Make sure that the chosen transfer Hz rate dividable by the stream's overall data rate. */
591 ASSERT_GUEST_LOGREL_MSG_STMT(cbDataPerSec % uTransferHz == 0,
592 ("Transfer data rate (%RU32 bytes/s) for stream #%RU8 does not fit to stream timing (%u Hz)\n",
593 cbDataPerSec, uSD, uTransferHz),
594 uTransferHz = HDA_TIMER_HZ_DEFAULT);
595
596 pStreamShared->State.cbTransferSize = (pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize)
597 / uTransferHz;
598 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize,
599 ("Transfer size for stream #%RU8 is invalid\n", uSD), rc = VERR_INVALID_PARAMETER);
600 if (RT_SUCCESS(rc))
601 {
602 /*
603 * Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
604 * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects.
605 *
606 * As we don't do chunked transfers the moment, the chunk size equals the overall transfer size.
607 */
608 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize;
609 ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferChunk,
610 ("Transfer chunk for stream #%RU8 is invalid\n", uSD),
611 rc = VERR_INVALID_PARAMETER);
612 if (RT_SUCCESS(rc))
613 {
614 /* Make sure that the transfer chunk does not exceed the overall transfer size. */
615 AssertStmt(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize,
616 pStreamShared->State.cbTransferChunk = pStreamShared->State.cbTransferSize);
617
618 const uint64_t uTimerFreq = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
619
620 const double cTicksPerHz = uTimerFreq / uTransferHz;
621
622 double cTicksPerByte = cTicksPerHz / (double)pStreamShared->State.cbTransferChunk;
623 if (uTransferHz < 100)
624 cTicksPerByte /= 100 / uTransferHz;
625 else
626 cTicksPerByte *= uTransferHz / 100;
627 Assert(cTicksPerByte);
628
629#define HDA_ROUND_NEAREST(a_X) ((a_X) >= 0 ? (uint32_t)((a_X) + 0.5) : (uint32_t)((a_X) - 0.5))
630
631 /* Calculate the timer ticks per byte for this stream. */
632 pStreamShared->State.cTicksPerByte = HDA_ROUND_NEAREST(cTicksPerByte);
633 Assert(pStreamShared->State.cTicksPerByte);
634
635 const double cTransferTicks = pStreamShared->State.cbTransferChunk * cTicksPerByte;
636
637 /* Calculate timer ticks per transfer. */
638 pStreamShared->State.cTransferTicks = HDA_ROUND_NEAREST(cTransferTicks);
639 Assert(pStreamShared->State.cTransferTicks);
640#undef HDA_ROUND_NEAREST
641
642 LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n",
643 uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.PCMProps.uHz,
644 pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks,
645 pStreamShared->State.cbTransferChunk, PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->State.cbTransferChunk),
646 pStreamShared->State.cbTransferSize, PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->State.cbTransferSize)));
647 }
648 }
649 }
650
651 if (RT_SUCCESS(rc))
652 {
653 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
654 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis, HDA_STREAM_REG(pThis, LPIB, uSD));
655
656#ifdef LOG_ENABLED
657 hdaR3BDLEDumpAll(pDevIns, pThis, pStreamShared->u64BDLBase, pStreamShared->u16LVI + 1);
658#endif
659
660 /*
661 * Set up internal ring buffer.
662 */
663
664 /* (Re-)Allocate the stream's internal DMA buffer,
665 * based on the timing *and* PCM properties we just got above. */
666 if (pStreamR3->State.pCircBuf)
667 {
668 RTCircBufDestroy(pStreamR3->State.pCircBuf);
669 pStreamR3->State.pCircBuf = NULL;
670 }
671 pStreamR3->State.offWrite = 0;
672 pStreamR3->State.offRead = 0;
673
674 /*
675 * The default internal ring buffer size must be:
676 *
677 * - Large enough for at least three periodic DMA transfers.
678 *
679 * It is critically important that we don't experience underruns
680 * in the DMA OUT code, because it will cause the buffer processing
681 * to get skewed and possibly overlap with what the guest is updating.
682 * At the time of writing (2021-03-05) there is no code for getting
683 * back into sync there.
684 *
685 * - Large enough for at least three I/O scheduling hints.
686 *
687 * We want to lag behind a DMA period or two, but there must be
688 * sufficent space for the AIO thread to get schedule and shuffle
689 * data thru the mixer and onto the host audio hardware.
690 *
691 * - Both above with plenty to spare.
692 *
693 * So, just take the longest of the two periods and multipling it by 6.
694 * We aren't not talking about very large base buffers heres, so size isn't
695 * an issue.
696 *
697 * Note: Use pCfg->Props as PCM properties here, as we only want to store the
698 * samples we actually need, in other words, skipping the interleaved
699 * channels we don't support / need to save space.
700 */
701 uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props,
702 RT_MS_1SEC * 6 / RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz));
703 LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU32 bytes / %RU64 ms\n",
704 uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
705
706 uint32_t msCircBufCfg = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs;
707 if (msCircBufCfg) /* Anything set via CFGM? */
708 {
709 cbCircBuf = PDMAudioPropsMilliToBytes(&pCfg->Props, msCircBufCfg);
710 LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU32 bytes / %RU64 ms\n",
711 uSD, cbCircBuf, PDMAudioPropsBytesToMilli(&pCfg->Props, cbCircBuf)));
712 }
713
714 /* Serious paranoia: */
715 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf % (pCfg->Props.cbSample * pCfg->Props.cChannels) == 0,
716 ("Ring buffer size (%RU32) for stream #%RU8 not aligned to the (host) frame size (%RU8)\n",
717 cbCircBuf, uSD, pCfg->Props.cbSample * pCfg->Props.cChannels),
718 rc = VERR_INVALID_PARAMETER);
719 ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf, ("Ring buffer size for stream #%RU8 is invalid\n", uSD),
720 rc = VERR_INVALID_PARAMETER);
721 if (RT_SUCCESS(rc))
722 {
723 rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
724 if (RT_SUCCESS(rc))
725 {
726 /*
727 * Forward the timer frequency hint to TM as well for better accuracy on
728 * systems w/o preemption timers (also good for 'info timers').
729 */
730 PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
731 }
732 }
733 }
734
735 if (RT_FAILURE(rc))
736 LogRelMax(32, ("HDA: Initializing stream #%RU8 failed with %Rrc\n", uSD, rc));
737
738#ifdef VBOX_WITH_DTRACE
739 VBOXDD_HDA_STREAM_SETUP((uint32_t)uSD, rc, pStreamShared->State.Cfg.Props.uHz,
740 pStreamShared->State.cTransferTicks, pStreamShared->State.cbTransferSize);
741#endif
742 return rc;
743}
744
745/**
746 * Resets an HDA stream.
747 *
748 * @param pThis The shared HDA device state.
749 * @param pThisCC The ring-3 HDA device state.
750 * @param pStreamShared HDA stream to reset (shared).
751 * @param pStreamR3 HDA stream to reset (ring-3).
752 * @param uSD Stream descriptor (SD) number to use for this stream.
753 */
754void hdaR3StreamReset(PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD)
755{
756 AssertPtr(pThis);
757 AssertPtr(pStreamShared);
758 AssertPtr(pStreamR3);
759 Assert(uSD < HDA_MAX_STREAMS);
760 AssertMsg(!pStreamShared->State.fRunning, ("[SD%RU8] Cannot reset stream while in running state\n", uSD));
761
762 LogFunc(("[SD%RU8] Reset\n", uSD));
763
764 /*
765 * Set reset state.
766 */
767 Assert(ASMAtomicReadBool(&pStreamShared->State.fInReset) == false); /* No nested calls. */
768 ASMAtomicXchgBool(&pStreamShared->State.fInReset, true);
769
770 /*
771 * Second, initialize the registers.
772 */
773 /* See 6.2.33: Clear on reset. */
774 HDA_STREAM_REG(pThis, STS, uSD) = 0;
775 /* According to the ICH6 datasheet, 0x40000 is the default value for stream descriptor register 23:20
776 * bits are reserved for stream number 18.2.33, resets SDnCTL except SRST bit. */
777 HDA_STREAM_REG(pThis, CTL, uSD) = 0x40000 | (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_SRST);
778 /* ICH6 defines default values (120 bytes for input and 192 bytes for output descriptors) of FIFO size. 18.2.39. */
779 HDA_STREAM_REG(pThis, FIFOS, uSD) = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? HDA_SDIFIFO_120B : HDA_SDOFIFO_192B;
780 /* See 18.2.38: Always defaults to 0x4 (32 bytes). */
781 HDA_STREAM_REG(pThis, FIFOW, uSD) = HDA_SDFIFOW_32B;
782 HDA_STREAM_REG(pThis, LPIB, uSD) = 0;
783 HDA_STREAM_REG(pThis, CBL, uSD) = 0;
784 HDA_STREAM_REG(pThis, LVI, uSD) = 0;
785 HDA_STREAM_REG(pThis, FMT, uSD) = 0;
786 HDA_STREAM_REG(pThis, BDPU, uSD) = 0;
787 HDA_STREAM_REG(pThis, BDPL, uSD) = 0;
788
789#ifdef HDA_USE_DMA_ACCESS_HANDLER
790 hdaR3StreamUnregisterDMAHandlers(pThis, pStream);
791#endif
792
793 /* Assign the default mixer sink to the stream. */
794 pStreamR3->pMixSink = hdaR3GetDefaultSink(pThisCC, uSD);
795
796 /* Reset position adjustment counter. */
797 pStreamShared->State.cfPosAdjustLeft = pStreamShared->State.cfPosAdjustDefault;
798
799 /* Reset transfer stuff. */
800 pStreamShared->State.cTransferPendingInterrupts = 0;
801 pStreamShared->State.tsTransferLast = 0;
802 pStreamShared->State.tsTransferNext = 0;
803
804 /* Initialize timestamps. */
805 pStreamShared->State.tsLastTransferNs = 0;
806 pStreamShared->State.tsLastReadNs = 0;
807
808 RT_ZERO(pStreamShared->State.BDLE);
809 pStreamShared->State.uCurBDLE = 0;
810
811 if (pStreamR3->State.pCircBuf)
812 RTCircBufReset(pStreamR3->State.pCircBuf);
813 pStreamR3->State.offWrite = 0;
814 pStreamR3->State.offRead = 0;
815
816 /* Reset the stream's period. */
817 hdaR3StreamPeriodReset(&pStreamShared->State.Period);
818
819#ifdef DEBUG
820 pStreamR3->Dbg.cReadsTotal = 0;
821 pStreamR3->Dbg.cbReadTotal = 0;
822 pStreamR3->Dbg.tsLastReadNs = 0;
823 pStreamR3->Dbg.cWritesTotal = 0;
824 pStreamR3->Dbg.cbWrittenTotal = 0;
825 pStreamR3->Dbg.cWritesHz = 0;
826 pStreamR3->Dbg.cbWrittenHz = 0;
827 pStreamR3->Dbg.tsWriteSlotBegin = 0;
828#endif
829
830 /* Report that we're done resetting this stream. */
831 HDA_STREAM_REG(pThis, CTL, uSD) = 0;
832
833#ifdef VBOX_WITH_DTRACE
834 VBOXDD_HDA_STREAM_RESET((uint32_t)uSD);
835#endif
836 LogFunc(("[SD%RU8] Reset\n", uSD));
837
838 /* Exit reset mode. */
839 ASMAtomicXchgBool(&pStreamShared->State.fInReset, false);
840}
841
842/**
843 * Enables or disables an HDA audio stream.
844 *
845 * @returns IPRT status code.
846 * @param pStreamShared HDA stream to enable or disable - shared bits.
847 * @param pStreamR3 HDA stream to enable or disable - ring-3 bits.
848 * @param fEnable Whether to enable or disble the stream.
849 */
850int hdaR3StreamEnable(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fEnable)
851{
852 AssertPtr(pStreamR3);
853 AssertPtr(pStreamShared);
854
855 LogFunc(("[SD%RU8] fEnable=%RTbool, pMixSink=%p\n", pStreamShared->u8SD, fEnable, pStreamR3->pMixSink));
856
857 int rc = VINF_SUCCESS;
858
859 AUDMIXSINKCMD enmCmd = fEnable
860 ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE;
861
862 /* First, enable or disable the stream and the stream's sink, if any. */
863 if ( pStreamR3->pMixSink
864 && pStreamR3->pMixSink->pMixSink)
865 rc = AudioMixerSinkCtl(pStreamR3->pMixSink->pMixSink, enmCmd);
866
867 if ( RT_SUCCESS(rc)
868 && fEnable
869 && pStreamR3->Dbg.Runtime.fEnabled)
870 {
871 Assert(DrvAudioHlpPcmPropsAreValid(&pStreamShared->State.Cfg.Props));
872
873 if (fEnable)
874 {
875 if (!DrvAudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileStream))
876 {
877 int rc2 = DrvAudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileStream, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
878 &pStreamShared->State.Cfg.Props);
879 AssertRC(rc2);
880 }
881
882 if (!DrvAudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileDMARaw))
883 {
884 int rc2 = DrvAudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileDMARaw, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
885 &pStreamShared->State.Cfg.Props);
886 AssertRC(rc2);
887 }
888
889 if (!DrvAudioHlpFileIsOpen(pStreamR3->Dbg.Runtime.pFileDMAMapped))
890 {
891 int rc2 = DrvAudioHlpFileOpen(pStreamR3->Dbg.Runtime.pFileDMAMapped, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
892 &pStreamShared->State.Cfg.Props);
893 AssertRC(rc2);
894 }
895 }
896 }
897
898 if (RT_SUCCESS(rc))
899 {
900 pStreamShared->State.fRunning = fEnable;
901 }
902
903 LogFunc(("[SD%RU8] rc=%Rrc\n", pStreamShared->u8SD, rc));
904 return rc;
905}
906
907#if 0 /* Not used atm. */
908static uint32_t hdaR3StreamGetPosition(PHDASTATE pThis, PHDASTREAM pStreamShared)
909{
910 return HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD);
911}
912#endif
913
914/**
915 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
916 * setting its associated LPIB register and DMA position buffer (if enabled) to an absolute value.
917 *
918 * @param pStreamShared HDA stream to update read / write position for (shared).
919 * @param pDevIns The device instance.
920 * @param pThis The shared HDA device state.
921 * @param uLPIB Absolute position (in bytes) to set current read / write position to.
922 */
923static void hdaR3StreamSetPositionAbs(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uLPIB)
924{
925 AssertPtrReturnVoid(pStreamShared);
926 AssertReturnVoid (uLPIB <= pStreamShared->u32CBL); /* Make sure that we don't go out-of-bounds. */
927
928 Log3Func(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n", pStreamShared->u8SD, uLPIB, pThis->fDMAPosition));
929
930 /* Update LPIB in any case. */
931 HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) = uLPIB;
932
933 /* Do we need to tell the current DMA position? */
934 if (pThis->fDMAPosition)
935 {
936 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns,
937 pThis->u64DPBase + (pStreamShared->u8SD * 2 * sizeof(uint32_t)),
938 (void *)&uLPIB, sizeof(uint32_t));
939 AssertRC(rc2);
940 }
941}
942
943/**
944 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
945 * adding a value to its associated LPIB register and DMA position buffer (if enabled).
946 *
947 * @note Handles automatic CBL wrap-around.
948 *
949 * @param pStreamShared HDA stream to update read / write position for (shared).
950 * @param pDevIns The device instance.
951 * @param pThis The shared HDA device state.
952 * @param uToAdd Position (in bytes) to add to the current read / write position.
953 */
954void hdaR3StreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uToAdd)
955{
956 if (!uToAdd) /* No need to update anything if 0. */
957 return;
958
959 hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis,
960 (HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) + uToAdd) % pStreamShared->u32CBL);
961}
962
963/**
964 * Retrieves the available size of (buffered) audio data (in bytes) of a given HDA stream.
965 *
966 * @returns Available data (in bytes).
967 * @param pStreamR3 HDA stream to retrieve size for (ring-3).
968 */
969static uint32_t hdaR3StreamGetUsed(PHDASTREAMR3 pStreamR3)
970{
971 AssertPtrReturn(pStreamR3, 0);
972
973 if (pStreamR3->State.pCircBuf)
974 return (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf);
975 return 0;
976}
977
978/**
979 * Retrieves the free size of audio data (in bytes) of a given HDA stream.
980 *
981 * @returns Free data (in bytes).
982 * @param pStreamR3 HDA stream to retrieve size for (ring-3).
983 */
984static uint32_t hdaR3StreamGetFree(PHDASTREAMR3 pStreamR3)
985{
986 AssertPtrReturn(pStreamR3, 0);
987
988 if (pStreamR3->State.pCircBuf)
989 return (uint32_t)RTCircBufFree(pStreamR3->State.pCircBuf);
990 return 0;
991}
992
993/**
994 * Returns whether a next transfer for a given stream is scheduled or not.
995 *
996 * This takes pending stream interrupts into account as well as the next scheduled
997 * transfer timestamp.
998 *
999 * @returns True if a next transfer is scheduled, false if not.
1000 * @param pStreamShared HDA stream to retrieve schedule status for (shared).
1001 * @param tsNow The current time.
1002 */
1003bool hdaR3StreamTransferIsScheduled(PHDASTREAM pStreamShared, uint64_t tsNow)
1004{
1005 if (pStreamShared)
1006 {
1007 if (pStreamShared->State.fRunning)
1008 {
1009 if (pStreamShared->State.cTransferPendingInterrupts)
1010 {
1011 Log3Func(("[SD%RU8] Scheduled (%RU8 IRQs pending)\n", pStreamShared->u8SD, pStreamShared->State.cTransferPendingInterrupts));
1012 return true;
1013 }
1014
1015 if (pStreamShared->State.tsTransferNext > tsNow)
1016 {
1017 Log3Func(("[SD%RU8] Scheduled in %RU64\n", pStreamShared->u8SD, pStreamShared->State.tsTransferNext - tsNow));
1018 return true;
1019 }
1020 }
1021 }
1022 return false;
1023}
1024
1025/**
1026 * Returns the (virtual) clock timestamp of the next transfer, if any.
1027 * Will return 0 if no new transfer is scheduled.
1028 *
1029 * @returns The (virtual) clock timestamp of the next transfer.
1030 * @param pStreamShared HDA stream to retrieve timestamp for (shared).
1031 */
1032uint64_t hdaR3StreamTransferGetNext(PHDASTREAM pStreamShared)
1033{
1034 return pStreamShared->State.tsTransferNext;
1035}
1036
1037/**
1038 * Writes audio data from a mixer sink into an HDA stream's DMA buffer.
1039 *
1040 * @returns IPRT status code.
1041 * @param pStreamR3 HDA stream to write to (ring-3).
1042 * @param pvBuf Data buffer to write.
1043 * If NULL, silence will be written.
1044 * @param cbBuf Number of bytes of data buffer to write.
1045 * @param pcbWritten Number of bytes written. Optional.
1046 */
1047static int hdaR3StreamWrite(PHDASTREAMR3 pStreamR3, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1048{
1049 Assert(cbBuf);
1050
1051 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
1052 AssertPtr(pCircBuf);
1053
1054 uint32_t cbWrittenTotal = 0;
1055 uint32_t cbLeft = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1056
1057 while (cbLeft)
1058 {
1059 void *pvDst;
1060 size_t cbDst;
1061 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, &pvDst, &cbDst);
1062
1063 if (cbDst)
1064 {
1065 if (pvBuf)
1066 memcpy(pvDst, (uint8_t *)pvBuf + cbWrittenTotal, cbDst);
1067 else /* Send silence. */
1068 {
1069 /** @todo Use a sample spec for "silence" based on the PCM parameters.
1070 * For now we ASSUME that silence equals NULLing the data. */
1071 RT_BZERO(pvDst, cbDst);
1072 }
1073#ifdef VBOX_WITH_DTRACE
1074 VBOXDD_HDA_STREAM_AIO_IN((uint32_t)pStreamR3->u8SD, (uint32_t)cbDst, pStreamR3->State.offWrite);
1075#endif
1076 pStreamR3->State.offWrite += cbDst;
1077
1078 if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
1079 { /* likely */ }
1080 else
1081 DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileStream, pvDst, cbDst, 0 /* fFlags */);
1082 }
1083
1084 RTCircBufReleaseWriteBlock(pCircBuf, cbDst);
1085
1086 Assert(cbLeft >= (uint32_t)cbDst);
1087 cbLeft -= (uint32_t)cbDst;
1088 cbWrittenTotal += (uint32_t)cbDst;
1089 }
1090
1091 Log3Func(("cbWrittenTotal=%#RX32 @ %#RX64\n", cbWrittenTotal, pStreamR3->State.offWrite - cbWrittenTotal));
1092
1093 if (pcbWritten)
1094 *pcbWritten = cbWrittenTotal;
1095
1096 return VINF_SUCCESS;
1097}
1098
1099
1100/**
1101 * Reads audio data from an HDA stream's DMA buffer and writes into a specified mixer sink.
1102 *
1103 * @returns IPRT status code.
1104 * @param pStreamR3 HDA stream to read audio data from (ring-3).
1105 * @param cbToRead Number of bytes to read.
1106 * @param pcbRead Number of bytes read. Optional.
1107 */
1108static int hdaR3StreamRead(PHDASTREAMR3 pStreamR3, uint32_t cbToRead, uint32_t *pcbRead)
1109{
1110 Assert(cbToRead);
1111
1112 PHDAMIXERSINK pSink = pStreamR3->pMixSink;
1113 AssertMsgReturnStmt(pSink, ("[SD%RU8] Can't read from a stream with no sink attached\n", pStreamR3->u8SD),
1114 if (pcbRead) *pcbRead = 0,
1115 VINF_SUCCESS);
1116
1117 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
1118 AssertPtr(pCircBuf);
1119
1120 int rc = VINF_SUCCESS;
1121
1122 uint32_t cbReadTotal = 0;
1123 uint32_t cbLeft = RT_MIN(cbToRead, (uint32_t)RTCircBufUsed(pCircBuf));
1124
1125 while (cbLeft)
1126 {
1127 void *pvSrc;
1128 size_t cbSrc;
1129
1130 uint32_t cbWritten = 0;
1131
1132 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, &pvSrc, &cbSrc);
1133
1134 if (cbSrc)
1135 {
1136 if (pStreamR3->Dbg.Runtime.fEnabled)
1137 DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileStream, pvSrc, cbSrc, 0 /* fFlags */);
1138
1139 rc = AudioMixerSinkWrite(pSink->pMixSink, AUDMIXOP_COPY, pvSrc, (uint32_t)cbSrc, &cbWritten);
1140 AssertRC(rc);
1141 Assert(cbSrc >= cbWritten);
1142
1143 Log2Func(("[SD%RU8] %#RX32/%#zx bytes read @ %#RX64\n", pStreamR3->u8SD, cbWritten, cbSrc, pStreamR3->State.offRead));
1144#ifdef VBOX_WITH_DTRACE
1145 VBOXDD_HDA_STREAM_AIO_OUT(pStreamR3->u8SD, (uint32_t)cbSrc, pStreamR3->State.offRead);
1146#endif
1147 pStreamR3->State.offRead += cbSrc;
1148 }
1149
1150 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1151
1152 if ( !cbWritten /* Nothing written? */
1153 || RT_FAILURE(rc))
1154 break;
1155
1156 Assert(cbLeft >= cbWritten);
1157 cbLeft -= cbWritten;
1158
1159 cbReadTotal += cbWritten;
1160 }
1161
1162 if (pcbRead)
1163 *pcbRead = cbReadTotal;
1164
1165 return rc;
1166}
1167
1168/**
1169 * Transfers data of an HDA stream according to its usage (input / output).
1170 *
1171 * For an SDO (output) stream this means reading DMA data from the device to
1172 * the HDA stream's internal FIFO buffer.
1173 *
1174 * For an SDI (input) stream this is reading audio data from the HDA stream's
1175 * internal FIFO buffer and writing it as DMA data to the device.
1176 *
1177 * @returns IPRT status code.
1178 * @param pDevIns The device instance.
1179 * @param pThis The shared HDA device state.
1180 * @param pThisCC The ring-3 HDA device state.
1181 * @param pStreamShared HDA stream to update (shared).
1182 * @param pStreamR3 HDA stream to update (ring-3).
1183 * @param cbToProcessMax How much data (in bytes) to process as maximum.
1184 */
1185static int hdaR3StreamTransfer(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared,
1186 PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax)
1187{
1188 uint8_t const uSD = pStreamShared->u8SD;
1189 LogFlowFunc(("ENTER - #%u cbToProcessMax=%#x\n", uSD, cbToProcessMax));
1190
1191 if (RT_LIKELY(cbToProcessMax >= pStreamShared->State.cbTransferSize))
1192 { /*likely*/ }
1193 else
1194 {
1195 /** @todo account for this or something so we can try get back in sync
1196 * later... */
1197 LogFlowFunc(("Internal DMA/AIO buffer %s (%#x, wanted at least %#x)\n",
1198 hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT ? "overflow" : "underflow",
1199 cbToProcessMax, pStreamShared->State.cbTransferSize));
1200 STAM_REL_COUNTER_INC(&pStreamR3->State.StatDmaFlowErrors);
1201#ifdef VBOX_WITH_DTRACE
1202 VBOXDD_HDA_STREAM_DMA_FLOWERROR(uSD, cbToProcessMax, pStreamShared->State.cbTransferSize,
1203 hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT ? 1 : 0);
1204#endif
1205 }
1206
1207 hdaStreamLock(pStreamShared);
1208
1209 PHDASTREAMPERIOD pPeriod = &pStreamShared->State.Period;
1210
1211 bool fProceed = true;
1212
1213 /* Stream not running (anymore)? */
1214 if (!pStreamShared->State.fRunning)
1215 {
1216 Log3Func(("[SD%RU8] Not running, skipping transfer\n", uSD));
1217 fProceed = false;
1218 }
1219
1220 else if (HDA_STREAM_REG(pThis, STS, uSD) & HDA_SDSTS_BCIS)
1221 {
1222 Log3Func(("[SD%RU8] BCIS bit set, skipping transfer\n", uSD));
1223#ifdef HDA_STRICT
1224 /* Timing emulation bug or guest is misbehaving -- let me know. */
1225 AssertMsgFailed(("BCIS bit for stream #%RU8 still set when it shouldn't\n", uSD));
1226#endif
1227 fProceed = false;
1228 }
1229
1230 if (!fProceed)
1231 {
1232 hdaStreamUnlock(pStreamShared);
1233 return VINF_SUCCESS;
1234 }
1235
1236 /* Update real-time timestamp. */
1237 const uint64_t tsNowNs = RTTimeNanoTS();
1238#ifdef LOG_ENABLED
1239 const uint64_t tsDeltaMs = (tsNowNs - pStreamShared->State.tsLastTransferNs) / RT_NS_1MS;
1240 Log3Func(("[SD%RU8] tsDeltaNs=%RU64ms\n", uSD, tsDeltaMs));
1241#endif
1242 pStreamShared->State.tsLastTransferNs = tsNowNs;
1243
1244 const uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer);
1245
1246 if (!pStreamShared->State.tsTransferLast)
1247 pStreamShared->State.tsTransferLast = tsNow;
1248
1249 pStreamShared->State.tsTransferLast = tsNow;
1250
1251 /* Register sanity checks. */
1252 Assert(uSD < HDA_MAX_STREAMS);
1253 Assert(pStreamShared->u64BDLBase);
1254 Assert(pStreamShared->u32CBL);
1255 Assert(pStreamShared->u8FIFOS);
1256
1257 /* State sanity checks. */
1258 Assert(ASMAtomicReadBool(&pStreamShared->State.fInReset) == false);
1259 Assert(ASMAtomicReadBool(&pStreamShared->State.fRunning));
1260
1261 /* Transfer sanity checks. */
1262 Assert(pStreamShared->State.cbTransferSize);
1263 Assert(pStreamShared->State.cbTransferChunk <= pStreamShared->State.cbTransferSize);
1264
1265 int rc = VINF_SUCCESS;
1266
1267 /* Fetch first / next BDL entry. */
1268 PHDABDLE pBDLE = &pStreamShared->State.BDLE;
1269 if (hdaR3BDLEIsComplete(pBDLE))
1270 {
1271 rc = hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
1272 AssertRC(rc);
1273 }
1274
1275 uint32_t cbToProcess = RT_MIN(pStreamShared->State.cbTransferSize, pStreamShared->State.cbTransferChunk);
1276
1277 Assert(cbToProcess); /* Nothing to process when there should be data. Accounting bug? */
1278
1279 /* More data to process than maximum allowed? */
1280#ifdef HDA_STRICT
1281 AssertStmt(cbToProcess <= cbToProcessMax, cbToProcess = cbToProcessMax);
1282#else
1283 if (cbToProcess > cbToProcessMax)
1284 cbToProcess = cbToProcessMax;
1285#endif
1286
1287 uint32_t cbProcessed = 0;
1288 uint32_t cbLeft = cbToProcess;
1289
1290 /* Whether an interrupt has been sent (asserted) for this transfer period already or not.
1291 *
1292 * Note: Windows 10 relies on this, e.g. sending more than one interrupt per transfer period
1293 * confuses the Windows' audio driver and will screw up the audio data. So only send
1294 * one interrupt per transfer period.
1295 */
1296 bool fInterruptSent = false;
1297
1298 /* Set the FIFORDY bit on the stream while doing the transfer. */
1299 HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_FIFORDY;
1300
1301 while (cbLeft)
1302 {
1303 /* Limit the chunk to the stream's FIFO size and what's left to process. */
1304 uint32_t cbChunk = RT_MIN(cbLeft, pStreamShared->u8FIFOS);
1305
1306 /* Limit the chunk to the remaining data of the current BDLE. */
1307 cbChunk = RT_MIN(cbChunk, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
1308
1309 /* If there are position adjustment frames left to be processed,
1310 * make sure that we process them first as a whole. */
1311 if (pStreamShared->State.cfPosAdjustLeft)
1312 cbChunk = RT_MIN(cbChunk, uint32_t(pStreamShared->State.cfPosAdjustLeft * pStreamR3->State.Mapping.cbFrameSize));
1313
1314 if (!cbChunk)
1315 break;
1316
1317 uint32_t cbDMA = 0;
1318 PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
1319 uint8_t *pabFIFO = pStreamShared->abFIFO;
1320
1321 if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN) /* Input (SDI). */
1322 {
1323 STAM_PROFILE_START(&pThis->StatIn, a);
1324
1325 uint32_t cbDMAWritten = 0;
1326 uint32_t cbDMAToWrite = cbChunk;
1327
1328 /** @todo Do we need interleaving streams support here as well?
1329 * Never saw anything else besides mono/stereo mics (yet). */
1330 while (cbDMAToWrite)
1331 {
1332 void *pvBuf; size_t cbBuf;
1333 RTCircBufAcquireReadBlock(pCircBuf, cbDMAToWrite, &pvBuf, &cbBuf);
1334
1335 if ( !cbBuf
1336 && !RTCircBufUsed(pCircBuf))
1337 break;
1338
1339 memcpy(pabFIFO + cbDMAWritten, pvBuf, cbBuf);
1340#ifdef VBOX_WITH_DTRACE
1341 VBOXDD_HDA_STREAM_DMA_IN((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offRead);
1342#endif
1343 pStreamR3->State.offRead += cbBuf;
1344
1345 RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
1346
1347 Assert(cbDMAToWrite >= cbBuf);
1348 cbDMAToWrite -= (uint32_t)cbBuf;
1349 cbDMAWritten += (uint32_t)cbBuf;
1350 Assert(cbDMAWritten <= cbChunk);
1351 }
1352
1353 if (cbDMAToWrite)
1354 {
1355 LogRel2(("HDA: FIFO underflow for stream #%RU8 (%RU32 bytes outstanding)\n", uSD, cbDMAToWrite));
1356
1357 Assert(cbChunk == cbDMAWritten + cbDMAToWrite);
1358 memset((uint8_t *)pabFIFO + cbDMAWritten, 0, cbDMAToWrite);
1359 cbDMAWritten = cbChunk;
1360 }
1361
1362 rc = hdaR3DMAWrite(pDevIns, pThis, pStreamShared, pStreamR3, pabFIFO, cbDMAWritten, &cbDMA /* pcbWritten */);
1363 if (RT_FAILURE(rc))
1364 LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", uSD, rc));
1365
1366 STAM_PROFILE_STOP(&pThis->StatIn, a);
1367 }
1368 else if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT) /* Output (SDO). */
1369 {
1370 STAM_PROFILE_START(&pThis->StatOut, a);
1371
1372 rc = hdaR3DMARead(pDevIns, pThis, pStreamShared, pStreamR3, pabFIFO, cbChunk, &cbDMA /* pcbRead */);
1373 if (RT_SUCCESS(rc))
1374 {
1375 const uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
1376
1377 /*
1378 * Most guests don't use different stream frame sizes than
1379 * the default one, so save a bit of CPU time and don't go into
1380 * the frame extraction code below.
1381 *
1382 * Only macOS guests need the frame extraction branch below at the moment AFAIK.
1383 */
1384 if (pStreamR3->State.Mapping.cbFrameSize == HDA_FRAME_SIZE_DEFAULT)
1385 {
1386 uint32_t cbDMARead = 0;
1387 uint32_t cbDMALeft = RT_MIN(cbDMA, cbFree);
1388
1389 while (cbDMALeft)
1390 {
1391 void *pvBuf; size_t cbBuf;
1392 RTCircBufAcquireWriteBlock(pCircBuf, cbDMALeft, &pvBuf, &cbBuf);
1393
1394 if (cbBuf)
1395 {
1396 memcpy(pvBuf, pabFIFO + cbDMARead, cbBuf);
1397 cbDMARead += (uint32_t)cbBuf;
1398 cbDMALeft -= (uint32_t)cbBuf;
1399#ifdef VBOX_WITH_DTRACE
1400 VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offWrite);
1401#endif
1402 pStreamR3->State.offWrite += cbBuf;
1403 }
1404
1405 RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
1406 }
1407 }
1408 else
1409 {
1410 /*
1411 * The following code extracts the required audio stream (channel) data
1412 * of non-interleaved *and* interleaved audio streams.
1413 *
1414 * We by default only support 2 channels with 16-bit samples (HDA_FRAME_SIZE),
1415 * but an HDA audio stream can have interleaved audio data of multiple audio
1416 * channels in such a single stream ("AA,AA,AA vs. AA,BB,AA,BB").
1417 *
1418 * So take this into account by just handling the first channel in such a stream ("A")
1419 * and just discard the other channel's data.
1420 *
1421 * I know, the following code is horribly slow, but seems to work for now.
1422 */
1423 /** @todo Optimize channel data extraction! Use some SSE(3) / intrinsics? */
1424 for (unsigned m = 0; m < pStreamR3->State.Mapping.cMappings; m++)
1425 {
1426 const uint32_t cbFrame = pStreamR3->State.Mapping.cbFrameSize;
1427
1428 Assert(cbFree >= cbDMA);
1429
1430 PPDMAUDIOSTREAMMAP pMap = &pStreamR3->State.Mapping.paMappings[m];
1431 AssertPtr(pMap);
1432
1433 Log3Func(("Mapping #%u: Start (cbDMA=%RU32, cbFrame=%RU32, offNext=%RU32)\n",
1434 m, cbDMA, cbFrame, pMap->offNext));
1435
1436
1437 /* Skip the current DMA chunk if the chunk is smaller than what the current stream mapping needs to read
1438 * the next associated frame (pointed to at pMap->cbOff).
1439 *
1440 * This can happen if the guest did not come up with enough data within a certain time period, especially
1441 * when using multi-channel speaker (> 2 channels [stereo]) setups. */
1442 if (pMap->offNext > cbChunk)
1443 {
1444 Log2Func(("Mapping #%u: Skipped (cbChunk=%RU32, cbMapOff=%RU32)\n", m, cbChunk, pMap->offNext));
1445 continue;
1446 }
1447
1448 uint8_t *pbSrcBuf = pabFIFO;
1449 size_t cbSrcOff = pMap->offNext;
1450
1451 for (unsigned i = 0; i < cbDMA / cbFrame; i++)
1452 {
1453 void *pvDstBuf; size_t cbDstBuf;
1454 RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
1455
1456 Assert(cbDstBuf >= pMap->cbStep);
1457
1458 if (cbDstBuf)
1459 {
1460 Log3Func(("Mapping #%u: Frame #%02u: cbStep=%u, offFirst=%u, offNext=%u, cbDstBuf=%u, cbSrcOff=%u\n",
1461 m, i, pMap->cbStep, pMap->offFirst, pMap->offNext, cbDstBuf, cbSrcOff));
1462
1463 memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
1464
1465#if 0 /* Too slow, even for release builds, so disabled it. */
1466 if (pStreamR3->Dbg.Runtime.fEnabled)
1467 DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMAMapped, pvDstBuf, cbDstBuf,
1468 0 /* fFlags */);
1469#endif
1470 Assert(cbSrcOff <= cbDMA);
1471 if (cbSrcOff + cbFrame + pMap->offFirst<= cbDMA)
1472 cbSrcOff += cbFrame + pMap->offFirst;
1473
1474#ifdef VBOX_WITH_DTRACE
1475 VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
1476#endif
1477 Log3Func(("Mapping #%u: Frame #%02u: -> cbSrcOff=%zu\n", m, i, cbSrcOff));
1478 pStreamR3->State.offWrite += cbDstBuf;
1479 }
1480
1481 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
1482 }
1483
1484 Log3Func(("Mapping #%u: End cbSize=%u, cbDMA=%RU32, cbSrcOff=%zu\n",
1485 m, pMap->cbStep, cbDMA, cbSrcOff));
1486
1487 Assert(cbSrcOff <= cbDMA);
1488
1489 const uint32_t cbSrcLeft = cbDMA - (uint32_t)cbSrcOff;
1490 if (cbSrcLeft)
1491 {
1492 Log3Func(("Mapping #%u: cbSrcLeft=%RU32\n", m, cbSrcLeft));
1493
1494 if (cbSrcLeft >= pMap->cbStep)
1495 {
1496 void *pvDstBuf; size_t cbDstBuf;
1497 RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
1498
1499 Assert(cbDstBuf >= pMap->cbStep);
1500
1501 if (cbDstBuf)
1502 {
1503 memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
1504#ifdef VBOX_WITH_DTRACE
1505 VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
1506#endif
1507 pStreamR3->State.offWrite += cbDstBuf;
1508 }
1509
1510 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
1511 }
1512
1513 Assert(pMap->cbFrame >= cbSrcLeft);
1514 pMap->offNext = pMap->cbFrame - cbSrcLeft;
1515 }
1516 else
1517 pMap->offNext = 0;
1518
1519 Log3Func(("Mapping #%u finish (cbSrcOff=%zu, offNext=%zu)\n", m, cbSrcOff, pMap->offNext));
1520 }
1521 }
1522 }
1523 else
1524 LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", uSD, rc));
1525
1526 STAM_PROFILE_STOP(&pThis->StatOut, a);
1527 }
1528
1529 else /** @todo Handle duplex streams? */
1530 AssertFailed();
1531
1532 if (cbDMA)
1533 {
1534 /* We always increment the position of DMA buffer counter because we're always reading
1535 * into an intermediate DMA buffer. */
1536 pBDLE->State.u32BufOff += (uint32_t)cbDMA;
1537 Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
1538
1539 /* Are we done doing the position adjustment?
1540 * Only then do the transfer accounting .*/
1541 if (pStreamShared->State.cfPosAdjustLeft == 0)
1542 {
1543 Assert(cbLeft >= cbDMA);
1544 cbLeft -= cbDMA;
1545
1546 cbProcessed += cbDMA;
1547 }
1548
1549 Log3Func(("[SD%RU8] cbDMA=%RU32 -> %R[bdle]\n", uSD, cbDMA, pBDLE));
1550 }
1551
1552 if (hdaR3BDLEIsComplete(pBDLE))
1553 {
1554 Log3Func(("[SD%RU8] Completed %R[bdle]\n", uSD, pBDLE));
1555
1556 /* Make sure to also update the wall clock when a BDLE is complete.
1557 * Needed for Windows 10 guests. */
1558 hdaR3WalClkSet(pThis, pThisCC,
1559 hdaWalClkGetCurrent(pThis)
1560 + hdaR3StreamPeriodFramesToWalClk(pPeriod,
1561 pBDLE->Desc.u32BufSize
1562 / pStreamR3->State.Mapping.cbFrameSize),
1563 false /* fForce */);
1564
1565 /*
1566 * Update the stream's current position.
1567 * Do this as accurate and close to the actual data transfer as possible.
1568 * All guetsts rely on this, depending on the mechanism they use (LPIB register or DMA counters).
1569 *
1570 * Note for Windows 10: The OS' driver is *very* picky about *when* the (DMA) positions get updated!
1571 * Not doing this at the right time will result in ugly sound crackles!
1572 */
1573 hdaR3StreamSetPositionAdd(pStreamShared, pDevIns, pThis, pBDLE->Desc.u32BufSize);
1574
1575 /* Does the current BDLE require an interrupt to be sent? */
1576 if ( hdaR3BDLENeedsInterrupt(pBDLE)
1577 /* Are we done doing the position adjustment?
1578 * It can happen that a BDLE which is handled while doing the
1579 * position adjustment requires an interrupt on completion (IOC) being set.
1580 *
1581 * In such a case we need to skip such an interrupt and just move on. */
1582 && pStreamShared->State.cfPosAdjustLeft == 0)
1583 {
1584 /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
1585 * we need to generate an interrupt.
1586 */
1587 if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
1588 {
1589 /* Assert the interrupt before actually fetching the next BDLE below. */
1590 if (!fInterruptSent)
1591 {
1592 pStreamShared->State.cTransferPendingInterrupts = 1;
1593
1594 AssertMsg(pStreamShared->State.cTransferPendingInterrupts <= 32,
1595 ("Too many pending interrupts (%RU8) for stream #%RU8\n",
1596 pStreamShared->State.cTransferPendingInterrupts, uSD));
1597
1598 Log3Func(("[SD%RU8] Scheduling interrupt (now %RU8 total)\n", uSD, pStreamShared->State.cTransferPendingInterrupts));
1599
1600 /*
1601 * Set the stream's BCIS bit.
1602 *
1603 * Note: This only must be done if the whole period is complete, and not if only
1604 * one specific BDL entry is complete (if it has the IOC bit set).
1605 *
1606 * This will otherwise confuses the guest when it 1) deasserts the interrupt,
1607 * 2) reads SDSTS (with BCIS set) and then 3) too early reads a (wrong) WALCLK value.
1608 *
1609 * snd_hda_intel on Linux will tell.
1610 */
1611 HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_BCIS;
1612
1613 /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
1614 * ending / beginning a period. */
1615 HDA_PROCESS_INTERRUPT(pDevIns, pThis);
1616
1617 fInterruptSent = true;
1618 }
1619 }
1620 }
1621
1622 if (pStreamShared->State.uCurBDLE == pStreamShared->u16LVI)
1623 {
1624 pStreamShared->State.uCurBDLE = 0;
1625 }
1626 else
1627 pStreamShared->State.uCurBDLE++;
1628
1629 /* Fetch the next BDLE entry. */
1630 hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
1631 }
1632
1633 /* Do the position adjustment accounting. */
1634 pStreamShared->State.cfPosAdjustLeft -=
1635 RT_MIN(pStreamShared->State.cfPosAdjustLeft, cbDMA / pStreamR3->State.Mapping.cbFrameSize);
1636
1637 if (RT_FAILURE(rc))
1638 break;
1639 }
1640
1641 /* Remove the FIFORDY bit again. */
1642 HDA_STREAM_REG(pThis, STS, uSD) &= ~HDA_SDSTS_FIFORDY;
1643
1644 /* Sanity. */
1645 Assert(cbProcessed == cbToProcess);
1646 Assert(cbLeft == 0);
1647
1648 /* Only do the data accounting if we don't have to do any position
1649 * adjustment anymore. */
1650 if (pStreamShared->State.cfPosAdjustLeft == 0)
1651 {
1652 hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbFrameSize,
1653 hdaR3StreamPeriodGetRemainingFrames(pPeriod)));
1654 }
1655
1656 const bool fTransferComplete = cbLeft == 0;
1657 if (fTransferComplete)
1658 {
1659 /*
1660 * Try updating the wall clock.
1661 *
1662 * Note 1) Only certain guests (like Linux' snd_hda_intel) rely on the WALCLK register
1663 * in order to determine the correct timing of the sound device. Other guests
1664 * like Windows 7 + 10 (or even more exotic ones like Haiku) will completely
1665 * ignore this.
1666 *
1667 * Note 2) When updating the WALCLK register too often / early (or even in a non-monotonic
1668 * fashion) this *will* upset guest device drivers and will completely fuck up the
1669 * sound output. Running VLC on the guest will tell!
1670 */
1671 const bool fWalClkSet = hdaR3WalClkSet(pThis, pThisCC,
1672 RT_MIN( hdaWalClkGetCurrent(pThis)
1673 + hdaR3StreamPeriodFramesToWalClk(pPeriod,
1674 cbProcessed
1675 / pStreamR3->State.Mapping.cbFrameSize),
1676 hdaR3WalClkGetMax(pThis, pThisCC)),
1677 false /* fForce */);
1678 RT_NOREF(fWalClkSet);
1679 }
1680
1681 /* Set the next transfer timing slot.
1682 * This must happen at a constant rate. */
1683 pStreamShared->State.tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;
1684
1685 /* Always update this timestamp, no matter what pStreamShared->State.tsTransferNext is. */
1686 pStreamShared->State.tsTransferLast = tsNow;
1687
1688 Log3Func(("[SD%RU8] %R[bdle] -- %#RX32/%#RX32 @ %#RX64\n", uSD, pBDLE, cbProcessed, pStreamShared->State.cbTransferSize,
1689 (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT ? pStreamR3->State.offWrite : pStreamR3->State.offRead) - cbProcessed));
1690 Log3Func(("[SD%RU8] fTransferComplete=%RTbool, cTransferPendingInterrupts=%RU8\n",
1691 uSD, fTransferComplete, pStreamShared->State.cTransferPendingInterrupts));
1692 Log3Func(("[SD%RU8] tsNow=%RU64, tsTransferNext=%RU64 (in %RU64 ticks)\n",
1693 uSD, tsNow, pStreamShared->State.tsTransferNext,
1694 pStreamShared->State.tsTransferNext ? pStreamShared->State.tsTransferNext - tsNow : 0));
1695
1696 LogFlowFuncLeave();
1697
1698 hdaStreamUnlock(pStreamShared);
1699
1700 return VINF_SUCCESS;
1701}
1702
1703/**
1704 * The stream's main function when called by the timer.
1705 *
1706 * Note: This function also will be called without timer invocation
1707 * when starting (enabling) the stream to minimize startup latency.
1708 *
1709 * @param pDevIns The device instance.
1710 * @param pThis The shared HDA device state.
1711 * @param pThisCC The ring-3 HDA device state.
1712 * @param pStreamShared HDA stream to update (shared bits).
1713 * @param pStreamR3 HDA stream to update (ring-3 bits).
1714 */
1715void hdaR3StreamTimerMain(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
1716 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3)
1717{
1718 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1719 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pStreamShared->hTimer));
1720
1721 hdaR3StreamUpdate(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, true /* fInTimer */);
1722
1723 /* Flag indicating whether to kick the timer again for a new data processing round. */
1724 bool fSinkActive = false;
1725 if (pStreamR3->pMixSink)
1726 fSinkActive = AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink);
1727
1728#ifdef LOG_ENABLED
1729 const uint8_t uSD = pStreamShared->u8SD;
1730#endif
1731
1732 if (fSinkActive)
1733 {
1734 const uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pStreamShared->hTimer); /* (For virtual sync this remains the same for the whole callout IIRC) */
1735 const bool fTimerScheduled = hdaR3StreamTransferIsScheduled(pStreamShared, tsNow);
1736
1737 uint64_t tsTransferNext = 0;
1738 if (fTimerScheduled)
1739 {
1740 Assert(pStreamShared->State.tsTransferNext); /* Make sure that a new transfer timestamp is set. */
1741 tsTransferNext = pStreamShared->State.tsTransferNext;
1742 }
1743 else /* Schedule at the precalculated rate. */
1744 tsTransferNext = tsNow + pStreamShared->State.cTransferTicks;
1745
1746 Log3Func(("[SD%RU8] fSinksActive=%RTbool, fTimerScheduled=%RTbool, tsTransferNext=%RU64 (in %RU64)\n",
1747 uSD, fSinkActive, fTimerScheduled, tsTransferNext, tsTransferNext - tsNow));
1748
1749 hdaR3TimerSet(pDevIns, pStreamShared, tsTransferNext,
1750 true /*fForce*/, tsNow);
1751 }
1752 else
1753 Log3Func(("[SD%RU8] fSinksActive=%RTbool\n", uSD, fSinkActive));
1754}
1755
1756/**
1757 * Updates a HDA stream by doing its required data transfers.
1758 *
1759 * The host sink(s) set the overall pace.
1760 *
1761 * This routine is called by both, the synchronous and the asynchronous
1762 * (VBOX_WITH_AUDIO_HDA_ASYNC_IO), implementations.
1763 *
1764 * When running synchronously, the device DMA transfers *and* the mixer sink
1765 * processing is within the device timer.
1766 *
1767 * When running asynchronously, only the device DMA transfers are done in the
1768 * device timer, whereas the mixer sink processing then is done in the stream's
1769 * own async I/O thread. This thread also will call this function
1770 * (with fInTimer set to @c false).
1771 *
1772 * @param pDevIns The device instance.
1773 * @param pThis The shared HDA device state.
1774 * @param pThisCC The ring-3 HDA device state.
1775 * @param pStreamShared HDA stream to update (shared bits).
1776 * @param pStreamR3 HDA stream to update (ring-3 bits).
1777 * @param fInTimer Whether to this function was called from the timer
1778 * context or an asynchronous I/O stream thread (if supported).
1779 */
1780void hdaR3StreamUpdate(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC,
1781 PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fInTimer)
1782{
1783 if (!pStreamShared)
1784 return;
1785
1786 PAUDMIXSINK pSink = NULL;
1787 if (pStreamR3->pMixSink)
1788 pSink = pStreamR3->pMixSink->pMixSink;
1789
1790 if (!AudioMixerSinkIsActive(pSink)) /* No sink available? Bail out. */
1791 return;
1792
1793 const uint64_t tsNowNs = RTTimeNanoTS();
1794
1795 int rc2;
1796
1797 if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
1798 {
1799 bool fDoRead = fInTimer; /* Whether to read from the HDA stream or not. */
1800
1801 /*
1802 * Do DMA work.
1803 */
1804# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1805 if (fInTimer)
1806# endif
1807 {
1808 uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1809 if (cbStreamFree)
1810 { /* likely */ }
1811 else
1812 {
1813 LogRel2(("HDA: Warning: Hit stream #%RU8 overflow, dropping audio data\n", pStreamShared->u8SD));
1814# ifdef HDA_STRICT
1815 AssertMsgFailed(("Hit stream #%RU8 overflow -- timing bug?\n", pStreamShared->u8SD));
1816# endif
1817 /* When hitting an overflow, drop all remaining data to make space for current data.
1818 * This is needed in order to keep the device emulation running at a constant rate,
1819 * at the cost of losing valid (but too much) data. */
1820 RTCircBufReset(pStreamR3->State.pCircBuf);
1821 pStreamR3->State.offWrite = 0;
1822 pStreamR3->State.offRead = 0;
1823 cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1824 }
1825
1826 /* Do the DMA transfer. */
1827 rc2 = hdaR3StreamTransfer(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, cbStreamFree);
1828 AssertRC(rc2);
1829
1830 /* Never read yet? Set initial timestamp. */
1831 if (pStreamShared->State.tsLastReadNs == 0)
1832 pStreamShared->State.tsLastReadNs = tsNowNs;
1833
1834 /*
1835 * Push data to down thru the mixer to and to the host drivers?
1836 *
1837 * This is generally done at the rate given by cMsSchedulingHint,
1838 * however we must also check available DMA buffer space. There
1839 * should be at least two periodic transfer units worth of space
1840 * available now.
1841 */
1842 Assert(tsNowNs >= pStreamShared->State.tsLastReadNs);
1843 /** @todo convert cMsSchedulingHint to nano seconds and save a div. */
1844 const uint64_t msDelta = (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS;
1845 cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1846 if ( cbStreamFree < pStreamShared->State.cbTransferSize * 2
1847 || msDelta >= pStreamShared->State.Cfg.Device.cMsSchedulingHint)
1848 fDoRead = true;
1849
1850 Log3Func(("msDelta=%RU64 (vs %u) cbStreamFree=%#x (vs %#x) => fDoRead=%RTbool\n", msDelta,
1851 pStreamShared->State.Cfg.Device.cMsSchedulingHint, cbStreamFree,
1852 pStreamShared->State.cbTransferSize * 2, fDoRead));
1853
1854 if (fDoRead)
1855 {
1856# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1857 /* Notify the async I/O worker thread that there's work to do. */
1858 Log5Func(("Notifying AIO thread\n"));
1859 rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
1860 AssertRC(rc2);
1861# endif
1862 /* Update last read timestamp so that we know when to run next. */
1863 pStreamShared->State.tsLastReadNs = tsNowNs;
1864 }
1865 }
1866
1867# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1868 if (!fInTimer) /* In async I/O thread */
1869# else
1870 if (fDoRead)
1871# endif
1872 {
1873 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
1874 uint32_t const cbStreamReadable = hdaR3StreamGetUsed(pStreamR3);
1875 uint32_t cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable);
1876 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
1877 cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStreamR3->State.Mapping.PCMProps, cbToReadFromStream);
1878
1879 Assert(tsNowNs >= pStreamShared->State.tsLastReadNs);
1880 Log3Func(("[SD%RU8] msDeltaLastRead=%RI64\n",
1881 pStreamShared->u8SD, (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS));
1882 Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32 -> cbToReadFromStream=%RU32\n",
1883 pStreamShared->u8SD, cbSinkWritable, cbStreamReadable, cbToReadFromStream));
1884
1885 if (cbToReadFromStream)
1886 {
1887 /* Read (guest output) data and write it to the stream's sink. */
1888 rc2 = hdaR3StreamRead(pStreamR3, cbToReadFromStream, NULL /* pcbRead */);
1889 AssertRC(rc2);
1890 }
1891
1892 /* When running synchronously, update the associated sink here.
1893 * Otherwise this will be done in the async I/O thread. */
1894 rc2 = AudioMixerSinkUpdate(pSink);
1895 AssertRC(rc2);
1896 }
1897 }
1898 else /* Input (SDI). */
1899 {
1900# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1901 if (!fInTimer)
1902# endif
1903 {
1904 rc2 = AudioMixerSinkUpdate(pSink);
1905 AssertRC(rc2);
1906
1907 /* Is the sink ready to be read (host input data) from? If so, by how much? */
1908 uint32_t cbSinkReadable = AudioMixerSinkGetReadable(pSink);
1909
1910 /* How much (guest input) data is available for writing at the moment for the HDA stream? */
1911 const uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
1912
1913 Log3Func(("[SD%RU8] cbSinkReadable=%RU32, cbStreamFree=%RU32\n", pStreamShared->u8SD, cbSinkReadable, cbStreamFree));
1914
1915 /* Do not read more than the HDA stream can hold at the moment.
1916 * The host sets the overall pace. */
1917 if (cbSinkReadable > cbStreamFree)
1918 cbSinkReadable = cbStreamFree;
1919
1920 if (cbSinkReadable)
1921 {
1922 void *pvFIFO = &pStreamShared->abFIFO[0];
1923 uint32_t cbFIFO = (uint32_t)sizeof(pStreamShared->abFIFO);
1924
1925 while (cbSinkReadable)
1926 {
1927 uint32_t cbRead;
1928 rc2 = AudioMixerSinkRead(pSink, AUDMIXOP_COPY,
1929 pvFIFO, RT_MIN(cbSinkReadable, cbFIFO), &cbRead);
1930 AssertRCBreak(rc2);
1931
1932 if (!cbRead)
1933 {
1934 AssertMsgFailed(("Nothing read from sink, even if %RU32 bytes were (still) announced\n", cbSinkReadable));
1935 break;
1936 }
1937
1938 /* Write (guest input) data to the stream which was read from stream's sink before. */
1939 uint32_t cbWritten;
1940 rc2 = hdaR3StreamWrite(pStreamR3, pvFIFO, cbRead, &cbWritten);
1941 AssertRCBreak(rc2);
1942 AssertBreak(cbWritten > 0); /* Should never happen, as we know how much we can write. */
1943
1944 Assert(cbSinkReadable >= cbRead);
1945 cbSinkReadable -= cbRead;
1946 }
1947 }
1948 }
1949# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1950 else /* fInTimer */
1951# endif
1952 {
1953# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1954 if (tsNowNs - pStreamShared->State.tsLastReadNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
1955 {
1956 Log5Func(("Notifying AIO thread\n"));
1957 rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
1958 AssertRC(rc2);
1959
1960 pStreamShared->State.tsLastReadNs = tsNowNs;
1961 }
1962# endif
1963 const uint32_t cbStreamUsed = hdaR3StreamGetUsed(pStreamR3);
1964 if (cbStreamUsed)
1965 {
1966 rc2 = hdaR3StreamTransfer(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, cbStreamUsed);
1967 AssertRC(rc2);
1968 }
1969 }
1970 }
1971}
1972
1973#endif /* IN_RING3 */
1974
1975/**
1976 * Locks an HDA stream for serialized access.
1977 *
1978 * @returns IPRT status code.
1979 * @param pStreamShared HDA stream to lock (shared bits).
1980 */
1981void hdaStreamLock(PHDASTREAM pStreamShared)
1982{
1983 AssertPtrReturnVoid(pStreamShared);
1984# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1985 int rc2 = PDMCritSectEnter(&pStreamShared->CritSect, VINF_SUCCESS);
1986 AssertRC(rc2);
1987#endif
1988}
1989
1990/**
1991 * Unlocks a formerly locked HDA stream.
1992 *
1993 * @returns IPRT status code.
1994 * @param pStreamShared HDA stream to unlock (shared bits).
1995 */
1996void hdaStreamUnlock(PHDASTREAM pStreamShared)
1997{
1998 AssertPtrReturnVoid(pStreamShared);
1999# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
2000 int rc2 = PDMCritSectLeave(&pStreamShared->CritSect);
2001 AssertRC(rc2);
2002# endif
2003}
2004
2005#ifdef IN_RING3
2006
2007#if 0 /* unused - no prototype even */
2008/**
2009 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
2010 * updating its associated LPIB register and DMA position buffer (if enabled).
2011 *
2012 * @returns Set LPIB value.
2013 * @param pDevIns The device instance.
2014 * @param pStream HDA stream to update read / write position for.
2015 * @param u32LPIB New LPIB (position) value to set.
2016 */
2017uint32_t hdaR3StreamUpdateLPIB(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint32_t u32LPIB)
2018{
2019 AssertMsg(u32LPIB <= pStreamShared->u32CBL,
2020 ("[SD%RU8] New LPIB (%RU32) exceeds CBL (%RU32)\n", pStreamShared->u8SD, u32LPIB, pStreamShared->u32CBL));
2021
2022 u32LPIB = RT_MIN(u32LPIB, pStreamShared->u32CBL);
2023
2024 LogFlowFunc(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
2025 pStreamShared->u8SD, u32LPIB, pThis->fDMAPosition));
2026
2027 /* Update LPIB in any case. */
2028 HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) = u32LPIB;
2029
2030 /* Do we need to tell the current DMA position? */
2031 if (pThis->fDMAPosition)
2032 {
2033 int rc2 = PDMDevHlpPCIPhysWrite(pDevIns,
2034 pThis->u64DPBase + (pStreamShared->u8SD * 2 * sizeof(uint32_t)),
2035 (void *)&u32LPIB, sizeof(uint32_t));
2036 AssertRC(rc2);
2037 }
2038
2039 return u32LPIB;
2040}
2041#endif
2042
2043# ifdef HDA_USE_DMA_ACCESS_HANDLER
2044/**
2045 * Registers access handlers for a stream's BDLE DMA accesses.
2046 *
2047 * @returns true if registration was successful, false if not.
2048 * @param pStream HDA stream to register BDLE access handlers for.
2049 */
2050bool hdaR3StreamRegisterDMAHandlers(PHDASTREAM pStream)
2051{
2052 /* At least LVI and the BDL base must be set. */
2053 if ( !pStreamShared->u16LVI
2054 || !pStreamShared->u64BDLBase)
2055 {
2056 return false;
2057 }
2058
2059 hdaR3StreamUnregisterDMAHandlers(pStream);
2060
2061 LogFunc(("Registering ...\n"));
2062
2063 int rc = VINF_SUCCESS;
2064
2065 /*
2066 * Create BDLE ranges.
2067 */
2068
2069 struct BDLERANGE
2070 {
2071 RTGCPHYS uAddr;
2072 uint32_t uSize;
2073 } arrRanges[16]; /** @todo Use a define. */
2074
2075 size_t cRanges = 0;
2076
2077 for (uint16_t i = 0; i < pStreamShared->u16LVI + 1; i++)
2078 {
2079 HDABDLE BDLE;
2080 rc = hdaR3BDLEFetch(pDevIns, &BDLE, pStreamShared->u64BDLBase, i /* Index */);
2081 if (RT_FAILURE(rc))
2082 break;
2083
2084 bool fAddRange = true;
2085 BDLERANGE *pRange;
2086
2087 if (cRanges)
2088 {
2089 pRange = &arrRanges[cRanges - 1];
2090
2091 /* Is the current range a direct neighbor of the current BLDE? */
2092 if ((pRange->uAddr + pRange->uSize) == BDLE.Desc.u64BufAddr)
2093 {
2094 /* Expand the current range by the current BDLE's size. */
2095 pRange->uSize += BDLE.Desc.u32BufSize;
2096
2097 /* Adding a new range in this case is not needed anymore. */
2098 fAddRange = false;
2099
2100 LogFunc(("Expanding range %zu by %RU32 (%RU32 total now)\n", cRanges - 1, BDLE.Desc.u32BufSize, pRange->uSize));
2101 }
2102 }
2103
2104 /* Do we need to add a new range? */
2105 if ( fAddRange
2106 && cRanges < RT_ELEMENTS(arrRanges))
2107 {
2108 pRange = &arrRanges[cRanges];
2109
2110 pRange->uAddr = BDLE.Desc.u64BufAddr;
2111 pRange->uSize = BDLE.Desc.u32BufSize;
2112
2113 LogFunc(("Adding range %zu - 0x%x (%RU32)\n", cRanges, pRange->uAddr, pRange->uSize));
2114
2115 cRanges++;
2116 }
2117 }
2118
2119 LogFunc(("%zu ranges total\n", cRanges));
2120
2121 /*
2122 * Register all ranges as DMA access handlers.
2123 */
2124
2125 for (size_t i = 0; i < cRanges; i++)
2126 {
2127 BDLERANGE *pRange = &arrRanges[i];
2128
2129 PHDADMAACCESSHANDLER pHandler = (PHDADMAACCESSHANDLER)RTMemAllocZ(sizeof(HDADMAACCESSHANDLER));
2130 if (!pHandler)
2131 {
2132 rc = VERR_NO_MEMORY;
2133 break;
2134 }
2135
2136 RTListAppend(&pStream->State.lstDMAHandlers, &pHandler->Node);
2137
2138 pHandler->pStream = pStream; /* Save a back reference to the owner. */
2139
2140 char szDesc[32];
2141 RTStrPrintf(szDesc, sizeof(szDesc), "HDA[SD%RU8 - RANGE%02zu]", pStream->u8SD, i);
2142
2143 int rc2 = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3), PGMPHYSHANDLERKIND_WRITE,
2144 hdaDMAAccessHandler,
2145 NULL, NULL, NULL,
2146 NULL, NULL, NULL,
2147 szDesc, &pHandler->hAccessHandlerType);
2148 AssertRCBreak(rc2);
2149
2150 pHandler->BDLEAddr = pRange->uAddr;
2151 pHandler->BDLESize = pRange->uSize;
2152
2153 /* Get first and last pages of the BDLE range. */
2154 RTGCPHYS pgFirst = pRange->uAddr & ~PAGE_OFFSET_MASK;
2155 RTGCPHYS pgLast = RT_ALIGN(pgFirst + pRange->uSize, PAGE_SIZE);
2156
2157 /* Calculate the region size (in pages). */
2158 RTGCPHYS regionSize = RT_ALIGN(pgLast - pgFirst, PAGE_SIZE);
2159
2160 pHandler->GCPhysFirst = pgFirst;
2161 pHandler->GCPhysLast = pHandler->GCPhysFirst + (regionSize - 1);
2162
2163 LogFunc(("\tRegistering region '%s': 0x%x - 0x%x (region size: %zu)\n",
2164 szDesc, pHandler->GCPhysFirst, pHandler->GCPhysLast, regionSize));
2165 LogFunc(("\tBDLE @ 0x%x - 0x%x (%RU32)\n",
2166 pHandler->BDLEAddr, pHandler->BDLEAddr + pHandler->BDLESize, pHandler->BDLESize));
2167
2168 rc2 = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
2169 pHandler->GCPhysFirst, pHandler->GCPhysLast,
2170 pHandler->hAccessHandlerType, pHandler, NIL_RTR0PTR, NIL_RTRCPTR,
2171 szDesc);
2172 AssertRCBreak(rc2);
2173
2174 pHandler->fRegistered = true;
2175 }
2176
2177 LogFunc(("Registration ended with rc=%Rrc\n", rc));
2178
2179 return RT_SUCCESS(rc);
2180}
2181
2182/**
2183 * Unregisters access handlers of a stream's BDLEs.
2184 *
2185 * @param pStream HDA stream to unregister BDLE access handlers for.
2186 */
2187void hdaR3StreamUnregisterDMAHandlers(PHDASTREAM pStream)
2188{
2189 LogFunc(("\n"));
2190
2191 PHDADMAACCESSHANDLER pHandler, pHandlerNext;
2192 RTListForEachSafe(&pStream->State.lstDMAHandlers, pHandler, pHandlerNext, HDADMAACCESSHANDLER, Node)
2193 {
2194 if (!pHandler->fRegistered) /* Handler not registered? Skip. */
2195 continue;
2196
2197 LogFunc(("Unregistering 0x%x - 0x%x (%zu)\n",
2198 pHandler->GCPhysFirst, pHandler->GCPhysLast, pHandler->GCPhysLast - pHandler->GCPhysFirst));
2199
2200 int rc2 = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
2201 pHandler->GCPhysFirst);
2202 AssertRC(rc2);
2203
2204 RTListNodeRemove(&pHandler->Node);
2205
2206 RTMemFree(pHandler);
2207 pHandler = NULL;
2208 }
2209
2210 Assert(RTListIsEmpty(&pStream->State.lstDMAHandlers));
2211}
2212
2213# endif /* HDA_USE_DMA_ACCESS_HANDLER */
2214# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
2215
2216/**
2217 * @callback_method_impl{FNRTTHREAD,
2218 * Asynchronous I/O thread for a HDA stream.
2219 *
2220 * This will do the heavy lifting work for us as soon as it's getting notified
2221 * by another thread.}
2222 */
2223static DECLCALLBACK(int) hdaR3StreamAsyncIOThread(RTTHREAD hThreadSelf, void *pvUser)
2224{
2225 PHDASTREAMR3 const pStreamR3 = (PHDASTREAMR3)pvUser;
2226 PHDASTREAMSTATEAIO const pAIO = &pStreamR3->State.AIO;
2227 PHDASTATE const pThis = pStreamR3->pHDAStateShared;
2228 PHDASTATER3 const pThisCC = pStreamR3->pHDAStateR3;
2229 PPDMDEVINS const pDevIns = pThisCC->pDevIns;
2230 PHDASTREAM const pStreamShared = &pThis->aStreams[pStreamR3 - &pThisCC->aStreams[0]];
2231 Assert(pStreamR3 - &pThisCC->aStreams[0] == pStreamR3->u8SD);
2232 Assert(pStreamShared->u8SD == pStreamR3->u8SD);
2233
2234 /* Signal parent thread that we've started */
2235 ASMAtomicWriteBool(&pAIO->fStarted, true);
2236 RTThreadUserSignal(hThreadSelf);
2237
2238 LogFunc(("[SD%RU8] Started\n", pStreamShared->u8SD));
2239
2240 while (!ASMAtomicReadBool(&pAIO->fShutdown))
2241 {
2242 int rc2 = RTSemEventWait(pAIO->hEvent, RT_INDEFINITE_WAIT);
2243 if (RT_SUCCESS(rc2))
2244 { /* likely */ }
2245 else
2246 break;
2247
2248 if (!ASMAtomicReadBool(&pAIO->fShutdown))
2249 { /* likely */ }
2250 else
2251 break;
2252
2253 rc2 = RTCritSectEnter(&pAIO->CritSect);
2254 AssertRC(rc2);
2255 if (RT_SUCCESS(rc2))
2256 {
2257 if (pAIO->fEnabled)
2258 hdaR3StreamUpdate(pDevIns, pThis, pThisCC, pStreamShared, pStreamR3, false /* fInTimer */);
2259
2260 int rc3 = RTCritSectLeave(&pAIO->CritSect);
2261 AssertRC(rc3);
2262 }
2263 }
2264
2265 LogFunc(("[SD%RU8] Ended\n", pStreamShared->u8SD));
2266 ASMAtomicWriteBool(&pAIO->fStarted, false);
2267
2268 return VINF_SUCCESS;
2269}
2270
2271/**
2272 * Creates the async I/O thread for a specific HDA audio stream.
2273 *
2274 * @returns IPRT status code.
2275 * @param pStreamR3 HDA audio stream to create the async I/O thread for.
2276 */
2277int hdaR3StreamAsyncIOCreate(PHDASTREAMR3 pStreamR3)
2278{
2279 PHDASTREAMSTATEAIO pAIO = &pStreamR3->State.AIO;
2280
2281 int rc;
2282
2283 if (!ASMAtomicReadBool(&pAIO->fStarted))
2284 {
2285 pAIO->fShutdown = false;
2286 pAIO->fEnabled = true; /* Enabled by default. */
2287
2288 rc = RTSemEventCreate(&pAIO->hEvent);
2289 if (RT_SUCCESS(rc))
2290 {
2291 rc = RTCritSectInit(&pAIO->CritSect);
2292 if (RT_SUCCESS(rc))
2293 {
2294 rc = RTThreadCreateF(&pAIO->hThread, hdaR3StreamAsyncIOThread, pStreamR3, 0 /*cbStack*/,
2295 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "hdaAIO%RU8", pStreamR3->u8SD);
2296 if (RT_SUCCESS(rc))
2297 rc = RTThreadUserWait(pAIO->hThread, 10 * 1000 /* 10s timeout */);
2298 }
2299 }
2300 }
2301 else
2302 rc = VINF_SUCCESS;
2303
2304 LogFunc(("[SD%RU8] Returning %Rrc\n", pStreamR3->u8SD, rc));
2305 return rc;
2306}
2307
2308/**
2309 * Destroys the async I/O thread of a specific HDA audio stream.
2310 *
2311 * @returns IPRT status code.
2312 * @param pStreamR3 HDA audio stream to destroy the async I/O thread for.
2313 */
2314static int hdaR3StreamAsyncIODestroy(PHDASTREAMR3 pStreamR3)
2315{
2316 PHDASTREAMSTATEAIO pAIO = &pStreamR3->State.AIO;
2317
2318 if (!ASMAtomicReadBool(&pAIO->fStarted))
2319 return VINF_SUCCESS;
2320
2321 ASMAtomicWriteBool(&pAIO->fShutdown, true);
2322
2323 int rc = hdaR3StreamAsyncIONotify(pStreamR3);
2324 AssertRC(rc);
2325
2326 int rcThread;
2327 rc = RTThreadWait(pAIO->hThread, 30 * 1000 /* 30s timeout */, &rcThread);
2328 LogFunc(("Async I/O thread ended with %Rrc (%Rrc)\n", rc, rcThread));
2329
2330 if (RT_SUCCESS(rc))
2331 {
2332 pAIO->hThread = NIL_RTTHREAD;
2333
2334 rc = RTCritSectDelete(&pAIO->CritSect);
2335 AssertRC(rc);
2336
2337 rc = RTSemEventDestroy(pAIO->hEvent);
2338 AssertRC(rc);
2339 pAIO->hEvent = NIL_RTSEMEVENT;
2340
2341 pAIO->fStarted = false;
2342 pAIO->fShutdown = false;
2343 pAIO->fEnabled = false;
2344 }
2345
2346 LogFunc(("[SD%RU8] Returning %Rrc\n", pStreamR3->u8SD, rc));
2347 return rc;
2348}
2349
2350/**
2351 * Lets the stream's async I/O thread know that there is some data to process.
2352 *
2353 * @returns IPRT status code.
2354 * @param pStreamR3 HDA stream to notify async I/O thread for.
2355 */
2356static int hdaR3StreamAsyncIONotify(PHDASTREAMR3 pStreamR3)
2357{
2358 return RTSemEventSignal(pStreamR3->State.AIO.hEvent);
2359}
2360
2361/**
2362 * Locks the async I/O thread of a specific HDA audio stream.
2363 *
2364 * @param pStreamR3 HDA stream to lock async I/O thread for.
2365 */
2366void hdaR3StreamAsyncIOLock(PHDASTREAMR3 pStreamR3)
2367{
2368 PHDASTREAMSTATEAIO pAIO = &pStreamR3->State.AIO;
2369
2370 if (!ASMAtomicReadBool(&pAIO->fStarted))
2371 return;
2372
2373 int rc2 = RTCritSectEnter(&pAIO->CritSect);
2374 AssertRC(rc2);
2375}
2376
2377/**
2378 * Unlocks the async I/O thread of a specific HDA audio stream.
2379 *
2380 * @param pStreamR3 HDA stream to unlock async I/O thread for.
2381 */
2382void hdaR3StreamAsyncIOUnlock(PHDASTREAMR3 pStreamR3)
2383{
2384 PHDASTREAMSTATEAIO pAIO = &pStreamR3->State.AIO;
2385
2386 if (!ASMAtomicReadBool(&pAIO->fStarted))
2387 return;
2388
2389 int rc2 = RTCritSectLeave(&pAIO->CritSect);
2390 AssertRC(rc2);
2391}
2392
2393/**
2394 * Enables (resumes) or disables (pauses) the async I/O thread.
2395 *
2396 * @param pStreamR3 HDA stream to enable/disable async I/O thread for.
2397 * @param fEnable Whether to enable or disable the I/O thread.
2398 *
2399 * @remarks Does not do locking.
2400 */
2401void hdaR3StreamAsyncIOEnable(PHDASTREAMR3 pStreamR3, bool fEnable)
2402{
2403 PHDASTREAMSTATEAIO pAIO = &pStreamR3->State.AIO;
2404 ASMAtomicXchgBool(&pAIO->fEnabled, fEnable);
2405}
2406
2407# endif /* VBOX_WITH_AUDIO_HDA_ASYNC_IO */
2408#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