VirtualBox

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

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

DevHDA: Build fix. bugref:9890

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