VirtualBox

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

Last change on this file since 70704 was 70629, checked in by vboxsync, 7 years ago

Audio/HDA: Check if critical sections are initialized in hdaStreamDestroy().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.9 KB
Line 
1/* $Id: HDAStream.cpp 70629 2018-01-18 13:48:32Z vboxsync $ */
2/** @file
3 * HDAStream.cpp - Stream functions for HD Audio.
4 */
5
6/*
7 * Copyright (C) 2017 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/vmm/pdmdev.h>
29#include <VBox/vmm/pdmaudioifs.h>
30
31#include "DrvAudio.h"
32
33#include "DevHDA.h"
34#include "HDAStream.h"
35
36
37#ifdef IN_RING3
38
39/**
40 * Creates an HDA stream.
41 *
42 * @returns IPRT status code.
43 * @param pStream HDA stream to create.
44 * @param pThis HDA state to assign the HDA stream to.
45 * @param u8SD Stream descriptor number to assign.
46 */
47int hdaStreamCreate(PHDASTREAM pStream, PHDASTATE pThis, uint8_t u8SD)
48{
49 RT_NOREF(pThis);
50 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
51
52 pStream->u8SD = u8SD;
53 pStream->pMixSink = NULL;
54 pStream->pHDAState = pThis;
55
56 pStream->State.fInReset = false;
57 pStream->State.fRunning = false;
58#ifdef HDA_USE_DMA_ACCESS_HANDLER
59 RTListInit(&pStream->State.lstDMAHandlers);
60#endif
61
62 int rc = RTCircBufCreate(&pStream->State.pCircBuf, _64K); /** @todo Make this configurable. */
63 if (RT_SUCCESS(rc))
64 {
65 rc = hdaStreamPeriodCreate(&pStream->State.Period);
66 if (RT_SUCCESS(rc))
67 rc = RTCritSectInit(&pStream->State.CritSect);
68 }
69
70 int rc2;
71
72#ifdef DEBUG
73 rc2 = RTCritSectInit(&pStream->Dbg.CritSect);
74 AssertRC(rc2);
75#endif
76
77 pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
78
79 if (pStream->Dbg.Runtime.fEnabled)
80 {
81 char szFile[64];
82
83 if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN)
84 RTStrPrintf(szFile, sizeof(szFile), "hdaStreamWriteSD%RU8", pStream->u8SD);
85 else
86 RTStrPrintf(szFile, sizeof(szFile), "hdaStreamReadSD%RU8", pStream->u8SD);
87
88 char szPath[RTPATH_MAX + 1];
89 rc2 = DrvAudioHlpGetFileName(szPath, sizeof(szPath), pThis->Dbg.szOutPath, szFile,
90 0 /* uInst */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
91 AssertRC(rc2);
92 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szPath, PDMAUDIOFILE_FLAG_NONE, &pStream->Dbg.Runtime.pFileStream);
93 AssertRC(rc2);
94
95 if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN)
96 RTStrPrintf(szFile, sizeof(szFile), "hdaDMAWriteSD%RU8", pStream->u8SD);
97 else
98 RTStrPrintf(szFile, sizeof(szFile), "hdaDMAReadSD%RU8", pStream->u8SD);
99
100 rc2 = DrvAudioHlpGetFileName(szPath, sizeof(szPath), pThis->Dbg.szOutPath, szFile,
101 0 /* uInst */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
102 AssertRC(rc2);
103
104 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szPath, PDMAUDIOFILE_FLAG_NONE, &pStream->Dbg.Runtime.pFileDMA);
105 AssertRC(rc2);
106
107 /* Delete stale debugging files from a former run. */
108 DrvAudioHlpFileDelete(pStream->Dbg.Runtime.pFileStream);
109 DrvAudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
110 }
111
112 return rc;
113}
114
115/**
116 * Destroys an HDA stream.
117 *
118 * @param pStream HDA stream to destroy.
119 */
120void hdaStreamDestroy(PHDASTREAM pStream)
121{
122 AssertPtrReturnVoid(pStream);
123
124 LogFlowFunc(("[SD%RU8]: Destroying ...\n", pStream->u8SD));
125
126 hdaStreamMapDestroy(&pStream->State.Mapping);
127
128 int rc2;
129
130#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
131 rc2 = hdaStreamAsyncIODestroy(pStream);
132 AssertRC(rc2);
133#endif
134
135 if (RTCritSectIsInitialized(&pStream->State.CritSect))
136 {
137 rc2 = RTCritSectDelete(&pStream->State.CritSect);
138 AssertRC(rc2);
139 }
140
141 if (pStream->State.pCircBuf)
142 {
143 RTCircBufDestroy(pStream->State.pCircBuf);
144 pStream->State.pCircBuf = NULL;
145 }
146
147 hdaStreamPeriodDestroy(&pStream->State.Period);
148
149#ifdef DEBUG
150 if (RTCritSectIsInitialized(&pStream->Dbg.CritSect))
151 {
152 rc2 = RTCritSectDelete(&pStream->Dbg.CritSect);
153 AssertRC(rc2);
154 }
155#endif
156
157 if (pStream->Dbg.Runtime.fEnabled)
158 {
159 DrvAudioHlpFileDestroy(pStream->Dbg.Runtime.pFileStream);
160 pStream->Dbg.Runtime.pFileStream = NULL;
161
162 DrvAudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
163 pStream->Dbg.Runtime.pFileDMA = NULL;
164 }
165
166 LogFlowFuncLeave();
167}
168
169/**
170 * Initializes an HDA stream.
171 *
172 * @returns IPRT status code.
173 * @param pStream HDA stream to initialize.
174 * @param uSD SD (stream descriptor) number to assign the HDA stream to.
175 */
176int hdaStreamInit(PHDASTREAM pStream, uint8_t uSD)
177{
178 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
179
180 PHDASTATE pThis = pStream->pHDAState;
181 AssertPtr(pThis);
182
183 pStream->u8SD = uSD;
184 pStream->u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, pStream->u8SD),
185 HDA_STREAM_REG(pThis, BDPU, pStream->u8SD));
186 pStream->u16LVI = HDA_STREAM_REG(pThis, LVI, pStream->u8SD);
187 pStream->u32CBL = HDA_STREAM_REG(pThis, CBL, pStream->u8SD);
188 pStream->u16FIFOS = HDA_STREAM_REG(pThis, FIFOS, pStream->u8SD) + 1;
189
190 PPDMAUDIOSTREAMCFG pCfg = &pStream->State.Cfg;
191
192 int rc = hdaSDFMTToPCMProps(HDA_STREAM_REG(pThis, FMT, uSD), &pCfg->Props);
193 if (RT_FAILURE(rc))
194 {
195 LogRel(("HDA: Warning: Format 0x%x for stream #%RU8 not supported\n", HDA_STREAM_REG(pThis, FMT, uSD), uSD));
196 return rc;
197 }
198
199 /* Set the stream's direction. */
200 pCfg->enmDir = hdaGetDirFromSD(pStream->u8SD);
201
202 /* The the stream's name, based on the direction. */
203 switch (pCfg->enmDir)
204 {
205 case PDMAUDIODIR_IN:
206# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
207# error "Implement me!"
208# else
209 pCfg->DestSource.Source = PDMAUDIORECSOURCE_LINE;
210 pCfg->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
211 RTStrCopy(pCfg->szName, sizeof(pCfg->szName), "Line In");
212# endif
213 break;
214
215 case PDMAUDIODIR_OUT:
216 /* Destination(s) will be set in hdaAddStreamOut(),
217 * based on the channels / stream layout. */
218 break;
219
220 default:
221 rc = VERR_NOT_SUPPORTED;
222 break;
223 }
224
225 if ( !pStream->u32CBL
226 || !pStream->u16LVI
227 || !pStream->u64BDLBase
228 || !pStream->u16FIFOS)
229 {
230 return VINF_SUCCESS;
231 }
232
233 /* Set the stream's frame size. */
234 pStream->State.cbFrameSize = pCfg->Props.cChannels * (pCfg->Props.cBits / 8 /* To bytes */);
235 LogFunc(("[SD%RU8] cChannels=%RU8, cBits=%RU8 -> cbFrameSize=%RU32\n",
236 pStream->u8SD, pCfg->Props.cChannels, pCfg->Props.cBits, pStream->State.cbFrameSize));
237 Assert(pStream->State.cbFrameSize); /* Frame size must not be 0. */
238
239 /*
240 * Initialize the stream mapping in any case, regardless if
241 * we support surround audio or not. This is needed to handle
242 * the supported channels within a single audio stream, e.g. mono/stereo.
243 *
244 * In other words, the stream mapping *always* knows the real
245 * number of channels in a single audio stream.
246 */
247 rc = hdaStreamMapInit(&pStream->State.Mapping, &pCfg->Props);
248 AssertRCReturn(rc, rc);
249
250 LogFunc(("[SD%RU8] DMA @ 0x%x (%RU32 bytes), LVI=%RU16, FIFOS=%RU16, Hz=%RU32, rc=%Rrc\n",
251 pStream->u8SD, pStream->u64BDLBase, pStream->u32CBL, pStream->u16LVI, pStream->u16FIFOS,
252 pStream->State.Cfg.Props.uHz, rc));
253
254 /* Make sure that mandatory parameters are set up correctly. */
255 AssertStmt(pStream->u32CBL % pStream->State.cbFrameSize == 0, rc = VERR_INVALID_PARAMETER);
256 AssertStmt(pStream->u16LVI >= 1, rc = VERR_INVALID_PARAMETER);
257
258 if (RT_SUCCESS(rc))
259 {
260 /* Make sure that the chosen Hz rate dividable by the stream's rate. */
261 if (pStream->State.Cfg.Props.uHz % pThis->u16TimerHz != 0)
262 LogRel(("HDA: Device timer (%RU32) does not fit to stream #RU8 timing (%RU32)\n",
263 pThis->u16TimerHz, pStream->State.Cfg.Props.uHz));
264
265 /* Figure out how many transfer fragments we're going to use for this stream. */
266 /** @todo Use a more dynamic fragment size? */
267 Assert(pStream->u16LVI <= UINT8_MAX - 1);
268 uint8_t cFragments = pStream->u16LVI + 1;
269 if (cFragments <= 1)
270 cFragments = 2; /* At least two fragments (BDLEs) must be present. */
271
272 /*
273 * Handle the stream's position adjustment.
274 */
275 uint32_t cfPosAdjust = 0;
276
277 LogFunc(("[SD%RU8] fPosAdjustEnabled=%RTbool, cPosAdjustFrames=%RU16\n",
278 pStream->u8SD, pThis->fPosAdjustEnabled, pThis->cPosAdjustFrames));
279
280 if (pThis->fPosAdjustEnabled) /* Is the position adjustment enabled at all? */
281 {
282 HDABDLE BDLE;
283 RT_ZERO(BDLE);
284
285 int rc2 = hdaBDLEFetch(pThis, &BDLE, pStream->u64BDLBase, 0 /* Entry */);
286 AssertRC(rc2);
287
288 /* Note: Do *not* check if this BDLE aligns to the stream's frame size.
289 * It can happen that this isn't the case on some guests, e.g.
290 * on Windows with a 5.1 speaker setup.
291 *
292 * The only thing which counts is that the stream's CBL value
293 * properly aligns to the stream's frame size.
294 */
295
296 /* If no custom set position adjustment is set, apply some
297 * simple heuristics to detect the appropriate position adjustment. */
298 if ( !pThis->cPosAdjustFrames
299 /* Position adjustmenet buffer *must* have the IOC bit set! */
300 && hdaBDLENeedsInterrupt(&BDLE))
301 {
302 /** @todo Implement / use a (dynamic) table once this gets more complicated. */
303#ifdef VBOX_WITH_INTEL_HDA
304 /* Intel ICH / PCH: 1 frame. */
305 if (BDLE.Desc.u32BufSize == 1 * pStream->State.cbFrameSize)
306 {
307 cfPosAdjust = 1;
308 }
309 /* Intel Baytrail / Braswell: 32 frames. */
310 else if (BDLE.Desc.u32BufSize == 32 * pStream->State.cbFrameSize)
311 {
312 cfPosAdjust = 32;
313 }
314#endif
315 }
316 else /* Go with the set default. */
317 cfPosAdjust = pThis->cPosAdjustFrames;
318
319 if (cfPosAdjust)
320 {
321 /* Also adjust the number of fragments, as the position adjustment buffer
322 * does not count as an own fragment as such.
323 *
324 * This e.g. can happen on (newer) Ubuntu guests which use
325 * 4 (IOC) + 4408 (IOC) + 4408 (IOC) + 4408 (IOC) + 4404 (= 17632) bytes,
326 * where the first buffer (4) is used as position adjustment.
327 *
328 * Only skip a fragment if the whole buffer fragment is used for
329 * position adjustment.
330 */
331 if ( (cfPosAdjust * pStream->State.cbFrameSize) == BDLE.Desc.u32BufSize
332 && cFragments)
333 {
334 cFragments--;
335 }
336
337 /* Initialize position adjustment counter. */
338 pStream->State.cPosAdjustFramesLeft = cfPosAdjust;
339 LogRel2(("HDA: Position adjustment for stream #%RU8 active (%RU32 frames)\n", pStream->u8SD, cfPosAdjust));
340 }
341 }
342
343 LogFunc(("[SD%RU8] cfPosAdjust=%RU32, cFragments=%RU8\n", pStream->u8SD, cfPosAdjust, cFragments));
344
345 /*
346 * Set up data transfer transfer stuff.
347 */
348
349 /* Calculate the fragment size the guest OS expects interrupt delivery at. */
350 pStream->State.cbTransferSize = pStream->u32CBL / cFragments;
351 Assert(pStream->State.cbTransferSize);
352 Assert(pStream->State.cbTransferSize % pStream->State.cbFrameSize == 0);
353
354 /* Calculate the bytes we need to transfer to / from the stream's DMA per iteration.
355 * This is bound to the device's Hz rate and thus to the (virtual) timing the device expects. */
356 pStream->State.cbTransferChunk = (pStream->State.Cfg.Props.uHz / pThis->u16TimerHz) * pStream->State.cbFrameSize;
357 Assert(pStream->State.cbTransferChunk);
358 Assert(pStream->State.cbTransferChunk % pStream->State.cbFrameSize == 0);
359
360 /* Make sure that the transfer chunk does not exceed the overall transfer size. */
361 if (pStream->State.cbTransferChunk > pStream->State.cbTransferSize)
362 pStream->State.cbTransferChunk = pStream->State.cbTransferSize;
363
364 pStream->State.cbTransferProcessed = 0;
365 pStream->State.cTransferPendingInterrupts = 0;
366 pStream->State.cbDMALeft = 0;
367
368 const uint64_t cTicksPerHz = TMTimerGetFreq(pThis->pTimer) / pThis->u16TimerHz;
369
370 /* Calculate the timer ticks per byte for this stream. */
371 pStream->State.cTicksPerByte = cTicksPerHz / pStream->State.cbTransferChunk;
372 Assert(pStream->State.cTicksPerByte);
373
374 /* Calculate timer ticks per transfer. */
375 pStream->State.cTransferTicks = pStream->State.cbTransferChunk * pStream->State.cTicksPerByte;
376
377 /* Initialize the transfer timestamps. */
378 pStream->State.tsTransferLast = 0;
379 pStream->State.tsTransferNext = 0;
380
381 LogFunc(("[SD%RU8] Timer %uHz (%RU64 ticks per Hz), cTicksPerByte=%RU64, cbTransferChunk=%RU32, cTransferTicks=%RU64, " \
382 "cbTransferSize=%RU32\n",
383 pStream->u8SD, pThis->u16TimerHz, cTicksPerHz, pStream->State.cTicksPerByte,
384 pStream->State.cbTransferChunk, pStream->State.cTransferTicks, pStream->State.cbTransferSize));
385
386 /* Make sure to also update the stream's DMA counter (based on its current LPIB value). */
387 hdaStreamSetPosition(pStream, HDA_STREAM_REG(pThis, LPIB, pStream->u8SD));
388
389#ifdef LOG_ENABLED
390 hdaBDLEDumpAll(pThis, pStream->u64BDLBase, pStream->u16LVI + 1);
391#endif
392 }
393
394 if (RT_FAILURE(rc))
395 LogRel(("HDA: Initializing stream #%RU8 failed with %Rrc\n", pStream->u8SD, rc));
396
397 return rc;
398}
399
400/**
401 * Resets an HDA stream.
402 *
403 * @param pThis HDA state.
404 * @param pStream HDA stream to reset.
405 * @param uSD Stream descriptor (SD) number to use for this stream.
406 */
407void hdaStreamReset(PHDASTATE pThis, PHDASTREAM pStream, uint8_t uSD)
408{
409 AssertPtrReturnVoid(pThis);
410 AssertPtrReturnVoid(pStream);
411 AssertReturnVoid(uSD < HDA_MAX_STREAMS);
412
413# ifdef VBOX_STRICT
414 AssertReleaseMsg(!pStream->State.fRunning, ("[SD%RU8] Cannot reset stream while in running state\n", uSD));
415# endif
416
417 LogFunc(("[SD%RU8]: Reset\n", uSD));
418
419 /*
420 * Set reset state.
421 */
422 Assert(ASMAtomicReadBool(&pStream->State.fInReset) == false); /* No nested calls. */
423 ASMAtomicXchgBool(&pStream->State.fInReset, true);
424
425 /*
426 * Second, initialize the registers.
427 */
428 HDA_STREAM_REG(pThis, STS, uSD) = HDA_SDSTS_FIFORDY;
429 /* According to the ICH6 datasheet, 0x40000 is the default value for stream descriptor register 23:20
430 * bits are reserved for stream number 18.2.33, resets SDnCTL except SRST bit. */
431 HDA_STREAM_REG(pThis, CTL, uSD) = 0x40000 | (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_SRST);
432 /* ICH6 defines default values (120 bytes for input and 192 bytes for output descriptors) of FIFO size. 18.2.39. */
433 HDA_STREAM_REG(pThis, FIFOS, uSD) = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? HDA_SDIFIFO_120B : HDA_SDOFIFO_192B;
434 /* See 18.2.38: Always defaults to 0x4 (32 bytes). */
435 HDA_STREAM_REG(pThis, FIFOW, uSD) = HDA_SDFIFOW_32B;
436 HDA_STREAM_REG(pThis, LPIB, uSD) = 0;
437 HDA_STREAM_REG(pThis, CBL, uSD) = 0;
438 HDA_STREAM_REG(pThis, LVI, uSD) = 0;
439 HDA_STREAM_REG(pThis, FMT, uSD) = 0;
440 HDA_STREAM_REG(pThis, BDPU, uSD) = 0;
441 HDA_STREAM_REG(pThis, BDPL, uSD) = 0;
442
443#ifdef HDA_USE_DMA_ACCESS_HANDLER
444 hdaStreamUnregisterDMAHandlers(pThis, pStream);
445#endif
446
447 /* Assign the default mixer sink to the stream. */
448 pStream->pMixSink = hdaGetDefaultSink(pThis, uSD);
449
450 pStream->State.tsTransferLast = 0;
451 pStream->State.tsTransferNext = 0;
452
453 RT_ZERO(pStream->State.BDLE);
454 pStream->State.uCurBDLE = 0;
455
456 if (pStream->State.pCircBuf)
457 RTCircBufReset(pStream->State.pCircBuf);
458
459 /* Reset stream map. */
460 hdaStreamMapReset(&pStream->State.Mapping);
461
462 /* (Re-)initialize the stream with current values. */
463 int rc2 = hdaStreamInit(pStream, uSD);
464 AssertRC(rc2);
465
466 /* Reset the stream's period. */
467 hdaStreamPeriodReset(&pStream->State.Period);
468
469#ifdef DEBUG
470 pStream->Dbg.cReadsTotal = 0;
471 pStream->Dbg.cbReadTotal = 0;
472 pStream->Dbg.tsLastReadNs = 0;
473 pStream->Dbg.cWritesTotal = 0;
474 pStream->Dbg.cbWrittenTotal = 0;
475 pStream->Dbg.cWritesHz = 0;
476 pStream->Dbg.cbWrittenHz = 0;
477 pStream->Dbg.tsWriteSlotBegin = 0;
478#endif
479
480 /* Report that we're done resetting this stream. */
481 HDA_STREAM_REG(pThis, CTL, uSD) = 0;
482
483 LogFunc(("[SD%RU8] Reset\n", uSD));
484
485 /* Exit reset mode. */
486 ASMAtomicXchgBool(&pStream->State.fInReset, false);
487}
488
489/**
490 * Enables or disables an HDA audio stream.
491 *
492 * @returns IPRT status code.
493 * @param pStream HDA stream to enable or disable.
494 * @param fEnable Whether to enable or disble the stream.
495 */
496int hdaStreamEnable(PHDASTREAM pStream, bool fEnable)
497{
498 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
499
500 LogFunc(("[SD%RU8]: fEnable=%RTbool, pMixSink=%p\n", pStream->u8SD, fEnable, pStream->pMixSink));
501
502 int rc = VINF_SUCCESS;
503
504 if (!pStream->pMixSink) /* Stream attached to a sink? */
505 {
506 AssertMsgFailed(("Stream #%RU8 not has no mixer sink attached\n", pStream->u8SD));
507 return VERR_WRONG_ORDER;
508 }
509
510 AUDMIXSINKCMD enmCmd = fEnable
511 ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE;
512
513 /* First, enable or disable the stream and the stream's sink, if any. */
514 if (pStream->pMixSink->pMixSink)
515 rc = AudioMixerSinkCtl(pStream->pMixSink->pMixSink, enmCmd);
516
517 if ( RT_SUCCESS(rc)
518 && pStream->Dbg.Runtime.fEnabled)
519 {
520 Assert(DrvAudioHlpPCMPropsAreValid(&pStream->State.Cfg.Props));
521
522 if (fEnable)
523 {
524 int rc2 = DrvAudioHlpFileOpen(pStream->Dbg.Runtime.pFileStream, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
525 &pStream->State.Cfg.Props);
526 AssertRC(rc2);
527
528 rc2 = DrvAudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
529 &pStream->State.Cfg.Props);
530 AssertRC(rc2);
531 }
532 else
533 {
534 int rc2 = DrvAudioHlpFileClose(pStream->Dbg.Runtime.pFileStream);
535 AssertRC(rc2);
536
537 rc2 = DrvAudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
538 AssertRC(rc2);
539 }
540 }
541
542 if (RT_SUCCESS(rc))
543 {
544 pStream->State.fRunning = fEnable;
545 }
546
547 LogFunc(("[SD%RU8] rc=%Rrc\n", pStream->u8SD, rc));
548 return rc;
549}
550
551uint32_t hdaStreamGetPosition(PHDASTATE pThis, PHDASTREAM pStream)
552{
553 return HDA_STREAM_REG(pThis, LPIB, pStream->u8SD);
554}
555
556/**
557 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
558 * updating its associated LPIB register and DMA position buffer (if enabled).
559 *
560 * @param pStream HDA stream to update read / write position for.
561 * @param u32LPIB Absolute position (in bytes) to set current read / write position to.
562 */
563void hdaStreamSetPosition(PHDASTREAM pStream, uint32_t u32LPIB)
564{
565 AssertPtrReturnVoid(pStream);
566
567 Log3Func(("[SD%RU8] LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
568 pStream->u8SD, u32LPIB, pStream->pHDAState->fDMAPosition));
569
570 /* Update LPIB in any case. */
571 HDA_STREAM_REG(pStream->pHDAState, LPIB, pStream->u8SD) = u32LPIB;
572
573 /* Do we need to tell the current DMA position? */
574 if (pStream->pHDAState->fDMAPosition)
575 {
576 int rc2 = PDMDevHlpPCIPhysWrite(pStream->pHDAState->CTX_SUFF(pDevIns),
577 pStream->pHDAState->u64DPBase + (pStream->u8SD * 2 * sizeof(uint32_t)),
578 (void *)&u32LPIB, sizeof(uint32_t));
579 AssertRC(rc2);
580 }
581}
582
583/**
584 * Retrieves the available size of (buffered) audio data (in bytes) of a given HDA stream.
585 *
586 * @returns Available data (in bytes).
587 * @param pStream HDA stream to retrieve size for.
588 */
589uint32_t hdaStreamGetUsed(PHDASTREAM pStream)
590{
591 AssertPtrReturn(pStream, 0);
592
593 if (!pStream->State.pCircBuf)
594 return 0;
595
596 return (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
597}
598
599/**
600 * Retrieves the free size of audio data (in bytes) of a given HDA stream.
601 *
602 * @returns Free data (in bytes).
603 * @param pStream HDA stream to retrieve size for.
604 */
605uint32_t hdaStreamGetFree(PHDASTREAM pStream)
606{
607 AssertPtrReturn(pStream, 0);
608
609 if (!pStream->State.pCircBuf)
610 return 0;
611
612 return (uint32_t)RTCircBufFree(pStream->State.pCircBuf);
613}
614
615/**
616 * Returns whether a next transfer for a given stream is scheduled or not.
617 * This takes pending stream interrupts into account as well as the next scheduled
618 * transfer timestamp.
619 *
620 * @returns True if a next transfer is scheduled, false if not.
621 * @param pStream HDA stream to retrieve schedule status for.
622 */
623bool hdaStreamTransferIsScheduled(PHDASTREAM pStream)
624{
625 if (!pStream)
626 return false;
627
628 AssertPtrReturn(pStream->pHDAState, false);
629
630 const bool fScheduled = pStream->State.fRunning
631 && ( pStream->State.cTransferPendingInterrupts
632 || pStream->State.tsTransferNext > TMTimerGet(pStream->pHDAState->pTimer));
633
634 Log3Func(("[SD%RU8] tsTransferNext=%RU64, cTransferPendingInterrupts=%RU8 -> %RTbool\n",
635 pStream->u8SD, pStream->State.tsTransferNext, pStream->State.cTransferPendingInterrupts, fScheduled));
636
637 return fScheduled;
638}
639
640/**
641 * Returns the (virtual) clock timestamp of the next transfer, if any.
642 * Will return 0 if no new transfer is scheduled.
643 *
644 * @returns The (virtual) clock timestamp of the next transfer.
645 * @param pStream HDA stream to retrieve timestamp for.
646 */
647uint64_t hdaStreamTransferGetNext(PHDASTREAM pStream)
648{
649 return pStream->State.tsTransferNext;
650}
651
652/**
653 * Writes audio data from a mixer sink into an HDA stream's DMA buffer.
654 *
655 * @returns IPRT status code.
656 * @param pStream HDA stream to write to.
657 * @param pvBuf Data buffer to write.
658 * If NULL, silence will be written.
659 * @param cbBuf Number of bytes of data buffer to write.
660 * @param pcbWritten Number of bytes written. Optional.
661 */
662int hdaStreamWrite(PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
663{
664 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
665 /* pvBuf is optional. */
666 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
667 /* pcbWritten is optional. */
668
669 PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
670 AssertPtr(pCircBuf);
671
672 int rc = VINF_SUCCESS;
673
674 uint32_t cbWrittenTotal = 0;
675 uint32_t cbLeft = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
676
677 while (cbLeft)
678 {
679 void *pvDst;
680 size_t cbDst;
681
682 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, &pvDst, &cbDst);
683
684 if (cbDst)
685 {
686 if (pvBuf)
687 {
688 memcpy(pvDst, (uint8_t *)pvBuf + cbWrittenTotal, cbDst);
689 }
690 else /* Send silence. */
691 {
692 /** @todo Use a sample spec for "silence" based on the PCM parameters.
693 * For now we ASSUME that silence equals NULLing the data. */
694 RT_BZERO(pvDst, cbDst);
695 }
696
697 if (pStream->Dbg.Runtime.fEnabled)
698 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileStream, pvDst, cbDst, 0 /* fFlags */);
699 }
700
701 RTCircBufReleaseWriteBlock(pCircBuf, cbDst);
702
703 if (RT_FAILURE(rc))
704 break;
705
706 Assert(cbLeft >= (uint32_t)cbDst);
707 cbLeft -= (uint32_t)cbDst;
708
709 cbWrittenTotal += (uint32_t)cbDst;
710 }
711
712 if (pcbWritten)
713 *pcbWritten = cbWrittenTotal;
714
715 return rc;
716}
717
718
719/**
720 * Reads audio data from an HDA stream's DMA buffer and writes into a specified mixer sink.
721 *
722 * @returns IPRT status code.
723 * @param pStream HDA stream to read audio data from.
724 * @param cbToRead Number of bytes to read.
725 * @param pcbRead Number of bytes read. Optional.
726 */
727int hdaStreamRead(PHDASTREAM pStream, uint32_t cbToRead, uint32_t *pcbRead)
728{
729 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
730 AssertReturn(cbToRead, VERR_INVALID_PARAMETER);
731 /* pcbWritten is optional. */
732
733 PHDAMIXERSINK pSink = pStream->pMixSink;
734 if (!pSink)
735 {
736 AssertMsgFailed(("[SD%RU8]: Can't read from a stream with no sink attached\n", pStream->u8SD));
737
738 if (pcbRead)
739 *pcbRead = 0;
740 return VINF_SUCCESS;
741 }
742
743 PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
744 AssertPtr(pCircBuf);
745
746 int rc = VINF_SUCCESS;
747
748 uint32_t cbReadTotal = 0;
749 uint32_t cbLeft = RT_MIN(cbToRead, (uint32_t)RTCircBufUsed(pCircBuf));
750
751 while (cbLeft)
752 {
753 void *pvSrc;
754 size_t cbSrc;
755
756 uint32_t cbWritten = 0;
757
758 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, &pvSrc, &cbSrc);
759
760 if (cbSrc)
761 {
762 if (pStream->Dbg.Runtime.fEnabled)
763 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileStream, pvSrc, cbSrc, 0 /* fFlags */);
764
765 rc = AudioMixerSinkWrite(pSink->pMixSink, AUDMIXOP_COPY, pvSrc, (uint32_t)cbSrc, &cbWritten);
766 AssertRC(rc);
767
768 Assert(cbSrc >= cbWritten);
769 Log2Func(("[SD%RU8]: %zu/%zu bytes read\n", pStream->u8SD, cbWritten, cbSrc));
770 }
771
772 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
773
774 if (RT_FAILURE(rc))
775 break;
776
777 Assert(cbLeft >= cbWritten);
778 cbLeft -= cbWritten;
779
780 cbReadTotal += cbWritten;
781 }
782
783 if (pcbRead)
784 *pcbRead = cbReadTotal;
785
786 return rc;
787}
788
789/**
790 * Transfers data of an HDA stream according to its usage (input / output).
791 *
792 * For an SDO (output) stream this means reading DMA data from the device to
793 * the HDA stream's internal FIFO buffer.
794 *
795 * For an SDI (input) stream this is reading audio data from the HDA stream's
796 * internal FIFO buffer and writing it as DMA data to the device.
797 *
798 * @returns IPRT status code.
799 * @param pStream HDA stream to update.
800 * @param cbToProcessMax How much data (in bytes) to process as maximum.
801 */
802int hdaStreamTransfer(PHDASTREAM pStream, uint32_t cbToProcessMax)
803{
804 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
805
806 hdaStreamLock(pStream);
807
808 PHDASTATE pThis = pStream->pHDAState;
809 AssertPtr(pThis);
810
811 PHDASTREAMPERIOD pPeriod = &pStream->State.Period;
812 if (!hdaStreamPeriodLock(pPeriod))
813 return VERR_ACCESS_DENIED;
814
815 bool fProceed = true;
816
817 /* Stream not running? */
818 if (!pStream->State.fRunning)
819 {
820 Log3Func(("[SD%RU8] Not running\n", pStream->u8SD));
821 fProceed = false;
822 }
823 else if (HDA_STREAM_REG(pThis, STS, pStream->u8SD) & HDA_SDSTS_BCIS)
824 {
825 Log3Func(("[SD%RU8] BCIS bit set\n", pStream->u8SD));
826 fProceed = false;
827 }
828
829 if (!fProceed)
830 {
831 hdaStreamPeriodUnlock(pPeriod);
832 hdaStreamUnlock(pStream);
833 return VINF_SUCCESS;
834 }
835
836 const uint64_t tsNow = TMTimerGet(pThis->pTimer);
837
838 if (!pStream->State.tsTransferLast)
839 pStream->State.tsTransferLast = tsNow;
840
841#ifdef DEBUG
842 const int64_t iTimerDelta = tsNow - pStream->State.tsTransferLast;
843 Log3Func(("[SD%RU8] Time now=%RU64, last=%RU64 -> %RI64 ticks delta\n",
844 pStream->u8SD, tsNow, pStream->State.tsTransferLast, iTimerDelta));
845#endif
846
847 pStream->State.tsTransferLast = tsNow;
848
849 /* Sanity checks. */
850 Assert(pStream->u8SD < HDA_MAX_STREAMS);
851 Assert(pStream->u64BDLBase);
852 Assert(pStream->u32CBL);
853 Assert(pStream->u16FIFOS);
854
855 /* State sanity checks. */
856 Assert(ASMAtomicReadBool(&pStream->State.fInReset) == false);
857
858 int rc = VINF_SUCCESS;
859
860 /* Fetch first / next BDL entry. */
861 PHDABDLE pBDLE = &pStream->State.BDLE;
862 if (hdaBDLEIsComplete(pBDLE))
863 {
864 rc = hdaBDLEFetch(pThis, pBDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
865 AssertRC(rc);
866 }
867
868 uint32_t cbToProcess = RT_MIN(pStream->State.cbTransferSize - pStream->State.cbTransferProcessed,
869 pStream->State.cbTransferChunk);
870
871 Log3Func(("[SD%RU8] cbToProcess=%RU32, cbToProcessMax=%RU32\n", pStream->u8SD, cbToProcess, cbToProcessMax));
872
873 if (cbToProcess > cbToProcessMax)
874 {
875 if (pStream->State.Cfg.enmDir == PDMAUDIODIR_IN)
876 LogRelMax2(64, ("HDA: Warning: FIFO underflow for stream #%RU8 (still %RU32 bytes needed)\n",
877 pStream->u8SD, cbToProcess - cbToProcessMax));
878 else
879 LogRelMax2(64, ("HDA: Warning: FIFO overflow for stream #%RU8 (%RU32 bytes outstanding)\n",
880 pStream->u8SD, cbToProcess - cbToProcessMax));
881
882 LogFunc(("[SD%RU8] Warning: Limiting transfer (cbToProcess=%RU32, cbToProcessMax=%RU32)\n",
883 pStream->u8SD, cbToProcess, cbToProcessMax));
884
885 /* Never process more than a stream currently can handle. */
886 cbToProcess = cbToProcessMax;
887 }
888
889 uint32_t cbProcessed = 0;
890 uint32_t cbLeft = cbToProcess;
891
892 uint8_t abChunk[HDA_FIFO_MAX + 1];
893 while (cbLeft)
894 {
895 /* Limit the chunk to the stream's FIFO size and what's left to process. */
896 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
897
898 /* Limit the chunk to the remaining data of the current BDLE. */
899 cbChunk = RT_MIN(cbChunk, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
900
901 /* If there are position adjustment frames left to be processed,
902 * make sure that we process them first as a whole. */
903 if (pStream->State.cPosAdjustFramesLeft)
904 cbChunk = RT_MIN(cbChunk, uint32_t(pStream->State.cPosAdjustFramesLeft * pStream->State.cbFrameSize));
905
906 Log3Func(("[SD%RU8] cbChunk=%RU32, cPosAdjustFramesLeft=%RU16\n",
907 pStream->u8SD, cbChunk, pStream->State.cPosAdjustFramesLeft));
908
909 if (!cbChunk)
910 break;
911
912 uint32_t cbDMA = 0;
913 PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
914
915 if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_IN) /* Input (SDI). */
916 {
917 STAM_PROFILE_START(&pThis->StatIn, a);
918
919 uint32_t cbDMAWritten = 0;
920 uint32_t cbDMAToWrite = cbChunk;
921
922 /** @todo Do we need interleaving streams support here as well?
923 * Never saw anything else besides mono/stereo mics (yet). */
924 while (cbDMAToWrite)
925 {
926 void *pvBuf; size_t cbBuf;
927 RTCircBufAcquireReadBlock(pCircBuf, cbDMAToWrite, &pvBuf, &cbBuf);
928
929 if ( !cbBuf
930 && !RTCircBufUsed(pCircBuf))
931 break;
932
933 memcpy(abChunk + cbDMAWritten, pvBuf, cbBuf);
934
935 RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
936
937 Assert(cbDMAToWrite >= cbBuf);
938 cbDMAToWrite -= (uint32_t)cbBuf;
939 cbDMAWritten += (uint32_t)cbBuf;
940 Assert(cbDMAWritten <= cbChunk);
941 }
942
943 if (cbDMAToWrite)
944 {
945 LogRel2(("HDA: FIFO underflow for stream #%RU8 (%RU32 bytes outstanding)\n", pStream->u8SD, cbDMAToWrite));
946
947 Assert(cbChunk == cbDMAWritten + cbDMAToWrite);
948 memset((uint8_t *)abChunk + cbDMAWritten, 0, cbDMAToWrite);
949 cbDMAWritten = cbChunk;
950 }
951
952 rc = hdaDMAWrite(pThis, pStream, abChunk, cbDMAWritten, &cbDMA /* pcbWritten */);
953 if (RT_FAILURE(rc))
954 LogRel(("HDA: Writing to stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
955
956 STAM_PROFILE_STOP(&pThis->StatIn, a);
957 }
958 else if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
959 {
960 STAM_PROFILE_START(&pThis->StatOut, a);
961
962 rc = hdaDMARead(pThis, pStream, abChunk, cbChunk, &cbDMA /* pcbRead */);
963 if (RT_SUCCESS(rc))
964 {
965 uint32_t cbDMARead = pStream->State.cbDMALeft ? pStream->State.cbFrameSize - pStream->State.cbDMALeft : 0;
966 uint32_t cbDMALeft = RT_MIN(cbDMA, (uint32_t)RTCircBufFree(pCircBuf));
967
968#ifndef VBOX_WITH_HDA_AUDIO_INTERLEAVING_STREAMS_SUPPORT
969 /**
970 * The following code extracts the required audio stream (channel) data
971 * of non-interleaved *and* interleaved audio streams.
972 *
973 * We by default only support 2 channels with 16-bit samples (HDA_FRAME_SIZE),
974 * but an HDA audio stream can have interleaved audio data of multiple audio
975 * channels in such a single stream ("AA,AA,AA vs. AA,BB,AA,BB").
976 *
977 * So take this into account by just handling the first channel in such a stream ("A")
978 * and just discard the other channel's data.
979 *
980 * @todo Optimize this stuff -- copying only one frame a time is expensive.
981 */
982 while (cbDMALeft >= pStream->State.cbFrameSize)
983 {
984 void *pvBuf; size_t cbBuf;
985 RTCircBufAcquireWriteBlock(pCircBuf, HDA_FRAME_SIZE, &pvBuf, &cbBuf);
986
987 AssertBreak(cbDMARead <= sizeof(abChunk));
988
989 if (cbBuf)
990 memcpy(pvBuf, abChunk + cbDMARead, cbBuf);
991
992 RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
993
994 Assert(cbDMALeft >= pStream->State.cbFrameSize);
995 cbDMALeft -= pStream->State.cbFrameSize;
996 cbDMARead += pStream->State.cbFrameSize;
997 }
998
999 pStream->State.cbDMALeft = cbDMALeft;
1000 Assert(pStream->State.cbDMALeft < pStream->State.cbFrameSize);
1001
1002 const size_t cbFree = RTCircBufFree(pCircBuf);
1003 if (!cbFree)
1004 LogRel2(("HDA: FIFO of stream #%RU8 full, discarding audio data\n", pStream->u8SD));
1005#else
1006 /** @todo This needs making use of HDAStreamMap + HDAStreamChannel. */
1007# error "Implement reading interleaving streams support here."
1008#endif
1009 }
1010 else
1011 LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", pStream->u8SD, rc));
1012
1013 STAM_PROFILE_STOP(&pThis->StatOut, a);
1014 }
1015
1016 else /** @todo Handle duplex streams? */
1017 AssertFailed();
1018
1019 if (cbDMA)
1020 {
1021 /* We always increment the position of DMA buffer counter because we're always reading
1022 * into an intermediate buffer. */
1023 pBDLE->State.u32BufOff += (uint32_t)cbDMA;
1024 Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
1025
1026 /* Are we done doing the position adjustment?
1027 * Only then do the transfer accounting .*/
1028 if (pStream->State.cPosAdjustFramesLeft == 0)
1029 {
1030 Assert(cbLeft >= cbDMA);
1031 cbLeft -= cbDMA;
1032
1033 cbProcessed += cbDMA;
1034 }
1035
1036 /**
1037 * Update the stream's current position.
1038 * Do this as accurate and close to the actual data transfer as possible.
1039 * All guetsts rely on this, depending on the mechanism they use (LPIB register or DMA counters).
1040 */
1041 uint32_t cbStreamPos = hdaStreamGetPosition(pThis, pStream);
1042 if (cbStreamPos == pStream->u32CBL)
1043 cbStreamPos = 0;
1044
1045 hdaStreamSetPosition(pStream, cbStreamPos + cbDMA);
1046 }
1047
1048 if (hdaBDLEIsComplete(pBDLE))
1049 {
1050 Log3Func(("[SD%RU8] Complete: %R[bdle]\n", pStream->u8SD, pBDLE));
1051
1052 /* Does the current BDLE require an interrupt to be sent? */
1053 if ( hdaBDLENeedsInterrupt(pBDLE)
1054 /* Are we done doing the position adjustment?
1055 * It can happen that a BDLE which is handled while doing the
1056 * position adjustment requires an interrupt on completion (IOC) being set.
1057 *
1058 * In such a case we need to skip such an interrupt and just move on. */
1059 && pStream->State.cPosAdjustFramesLeft == 0)
1060 {
1061 /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
1062 * we need to generate an interrupt.
1063 */
1064 if (HDA_STREAM_REG(pThis, CTL, pStream->u8SD) & HDA_SDCTL_IOCE)
1065 {
1066 pStream->State.cTransferPendingInterrupts++;
1067
1068 AssertMsg(pStream->State.cTransferPendingInterrupts <= 32,
1069 ("Too many pending interrupts (%RU8) for stream #%RU8\n",
1070 pStream->State.cTransferPendingInterrupts, pStream->u8SD));
1071 }
1072 }
1073
1074 if (pStream->State.uCurBDLE == pStream->u16LVI)
1075 {
1076 pStream->State.uCurBDLE = 0;
1077 }
1078 else
1079 pStream->State.uCurBDLE++;
1080
1081 /* Fetch the next BDLE entry. */
1082 hdaBDLEFetch(pThis, pBDLE, pStream->u64BDLBase, pStream->State.uCurBDLE);
1083 }
1084
1085 /* Do the position adjustment accounting. */
1086 pStream->State.cPosAdjustFramesLeft -= RT_MIN(pStream->State.cPosAdjustFramesLeft, cbDMA / pStream->State.cbFrameSize);
1087
1088 if (RT_FAILURE(rc))
1089 break;
1090 }
1091
1092 Log3Func(("[SD%RU8] cbToProcess=%RU32, cbProcessed=%RU32, cbLeft=%RU32, %R[bdle], rc=%Rrc\n",
1093 pStream->u8SD, cbToProcess, cbProcessed, cbLeft, pBDLE, rc));
1094
1095 /* Sanity. */
1096 Assert(cbProcessed == cbToProcess);
1097 Assert(cbLeft == 0);
1098
1099 /* Only do the data accounting if we don't have to do any position
1100 * adjustment anymore. */
1101 if (pStream->State.cPosAdjustFramesLeft == 0)
1102 {
1103 hdaStreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStream->State.cbFrameSize, hdaStreamPeriodGetRemainingFrames(pPeriod)));
1104
1105 pStream->State.cbTransferProcessed += cbProcessed;
1106 }
1107
1108 /* Make sure that we never report more stuff processed than initially announced. */
1109 if (pStream->State.cbTransferProcessed > pStream->State.cbTransferSize)
1110 pStream->State.cbTransferProcessed = pStream->State.cbTransferSize;
1111
1112 uint32_t cbTransferLeft = pStream->State.cbTransferSize - pStream->State.cbTransferProcessed;
1113 bool fTransferComplete = !cbTransferLeft;
1114 uint64_t tsTransferNext = 0;
1115
1116 if (fTransferComplete)
1117 {
1118 /**
1119 * Try updating the wall clock.
1120 *
1121 * Note 1) Only certain guests (like Linux' snd_hda_intel) rely on the WALCLK register
1122 * in order to determine the correct timing of the sound device. Other guests
1123 * like Windows 7 + 10 (or even more exotic ones like Haiku) will completely
1124 * ignore this.
1125 *
1126 * Note 2) When updating the WALCLK register too often / early (or even in a non-monotonic
1127 * fashion) this *will* upset guest device drivers and will completely fuck up the
1128 * sound output. Running VLC on the guest will tell!
1129 */
1130 const bool fWalClkSet = hdaWalClkSet(pThis,
1131 hdaWalClkGetCurrent(pThis)
1132 + hdaStreamPeriodFramesToWalClk(pPeriod, pStream->State.cbTransferProcessed / pStream->State.cbFrameSize),
1133 false /* fForce */);
1134 RT_NOREF(fWalClkSet);
1135 }
1136
1137 /* Does the period have any interrupts outstanding? */
1138 if (pStream->State.cTransferPendingInterrupts)
1139 {
1140 Log3Func(("[SD%RU8] Scheduling interrupt\n", pStream->u8SD));
1141
1142 /**
1143 * Set the stream's BCIS bit.
1144 *
1145 * Note: This only must be done if the whole period is complete, and not if only
1146 * one specific BDL entry is complete (if it has the IOC bit set).
1147 *
1148 * This will otherwise confuses the guest when it 1) deasserts the interrupt,
1149 * 2) reads SDSTS (with BCIS set) and then 3) too early reads a (wrong) WALCLK value.
1150 *
1151 * snd_hda_intel on Linux will tell.
1152 */
1153 HDA_STREAM_REG(pThis, STS, pStream->u8SD) |= HDA_SDSTS_BCIS;
1154
1155 /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
1156 * ending / beginning a period. */
1157#ifndef DEBUG
1158 hdaProcessInterrupt(pThis);
1159#else
1160 hdaProcessInterrupt(pThis, __FUNCTION__);
1161#endif
1162 }
1163 else /* Transfer still in-flight -- schedule the next timing slot. */
1164 {
1165 uint32_t cbTransferNext = cbTransferLeft;
1166
1167 /* No data left to transfer anymore or do we have more data left
1168 * than we can transfer per timing slot? Clamp. */
1169 if ( !cbTransferNext
1170 || cbTransferNext > pStream->State.cbTransferChunk)
1171 {
1172 cbTransferNext = pStream->State.cbTransferChunk;
1173 }
1174
1175 tsTransferNext = tsNow + (cbTransferNext * pStream->State.cTicksPerByte);
1176
1177 /**
1178 * If the current transfer is complete, reset our counter.
1179 *
1180 * This can happen for examlpe if the guest OS (like macOS) sets up
1181 * big BDLEs without IOC bits set (but for the last one) and the
1182 * transfer is complete before we reach such a BDL entry.
1183 */
1184 if (fTransferComplete)
1185 pStream->State.cbTransferProcessed = 0;
1186 }
1187
1188 /* If we need to do another transfer, (re-)arm the device timer. */
1189 if (tsTransferNext) /* Can be 0 if no next transfer is needed. */
1190 {
1191 Log3Func(("[SD%RU8] Scheduling timer\n", pStream->u8SD));
1192
1193 TMTimerUnlock(pThis->pTimer);
1194
1195 hdaTimerSet(pThis, tsTransferNext, false /* fForce */);
1196
1197 TMTimerLock(pThis->pTimer, VINF_SUCCESS);
1198
1199 pStream->State.tsTransferNext = tsTransferNext;
1200 }
1201
1202 pStream->State.tsTransferLast = tsNow;
1203
1204 Log3Func(("[SD%RU8] cbTransferLeft=%RU32 -- %RU32/%RU32\n",
1205 pStream->u8SD, cbTransferLeft, pStream->State.cbTransferProcessed, pStream->State.cbTransferSize));
1206 Log3Func(("[SD%RU8] fTransferComplete=%RTbool, cTransferPendingInterrupts=%RU8\n",
1207 pStream->u8SD, fTransferComplete, pStream->State.cTransferPendingInterrupts));
1208 Log3Func(("[SD%RU8] tsNow=%RU64, tsTransferNext=%RU64 (in %RU64 ticks)\n",
1209 pStream->u8SD, tsNow, tsTransferNext, tsTransferNext - tsNow));
1210
1211 hdaStreamPeriodUnlock(pPeriod);
1212 hdaStreamUnlock(pStream);
1213
1214 return VINF_SUCCESS;
1215}
1216
1217/**
1218 * Updates a HDA stream by doing its required data transfers.
1219 * The host sink(s) set the overall pace.
1220 *
1221 * This routine is called by both, the synchronous and the asynchronous, implementations.
1222 *
1223 * @param pStream HDA stream to update.
1224 * @param fInTimer Whether to this function was called from the timer
1225 * context or an asynchronous I/O stream thread (if supported).
1226 */
1227void hdaStreamUpdate(PHDASTREAM pStream, bool fInTimer)
1228{
1229 if (!pStream)
1230 return;
1231
1232 PAUDMIXSINK pSink = NULL;
1233 if ( pStream->pMixSink
1234 && pStream->pMixSink->pMixSink)
1235 {
1236 pSink = pStream->pMixSink->pMixSink;
1237 }
1238
1239 if (!AudioMixerSinkIsActive(pSink)) /* No sink available? Bail out. */
1240 return;
1241
1242 int rc2;
1243
1244 if (hdaGetDirFromSD(pStream->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
1245 {
1246 /* Is the HDA stream ready to be written (guest output data) to? If so, by how much? */
1247 const uint32_t cbFree = hdaStreamGetFree(pStream);
1248
1249 if ( fInTimer
1250 && cbFree)
1251 {
1252 Log3Func(("[SD%RU8] cbFree=%RU32\n", pStream->u8SD, cbFree));
1253
1254 /* Do the DMA transfer. */
1255 rc2 = hdaStreamTransfer(pStream, cbFree);
1256 AssertRC(rc2);
1257 }
1258
1259 /* How much (guest output) data is available at the moment for the HDA stream? */
1260 uint32_t cbUsed = hdaStreamGetUsed(pStream);
1261
1262#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1263 if ( fInTimer
1264 && cbUsed)
1265 {
1266 rc2 = hdaStreamAsyncIONotify(pStream);
1267 AssertRC(rc2);
1268 }
1269 else
1270 {
1271#endif
1272 const uint32_t cbSinkWritable = AudioMixerSinkGetWritable(pSink);
1273
1274 /* Do not write more than the sink can hold at the moment.
1275 * The host sets the overall pace. */
1276 if (cbUsed > cbSinkWritable)
1277 cbUsed = cbSinkWritable;
1278
1279 if (cbUsed)
1280 {
1281 /* Read (guest output) data and write it to the stream's sink. */
1282 rc2 = hdaStreamRead(pStream, cbUsed, NULL /* pcbRead */);
1283 AssertRC(rc2);
1284 }
1285
1286 /* When running synchronously, update the associated sink here.
1287 * Otherwise this will be done in the stream's dedicated async I/O thread. */
1288 rc2 = AudioMixerSinkUpdate(pSink);
1289 AssertRC(rc2);
1290
1291#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1292 }
1293#endif
1294 }
1295 else /* Input (SDI). */
1296 {
1297#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1298 if (fInTimer)
1299 {
1300#endif
1301 rc2 = AudioMixerSinkUpdate(pSink);
1302 AssertRC(rc2);
1303
1304 /* Is the sink ready to be read (host input data) from? If so, by how much? */
1305 uint32_t cbReadable = AudioMixerSinkGetReadable(pSink);
1306
1307 /* How much (guest input) data is available for writing at the moment for the HDA stream? */
1308 const uint32_t cbFree = hdaStreamGetFree(pStream);
1309
1310 Log3Func(("[SD%RU8] cbReadable=%RU32, cbFree=%RU32\n", pStream->u8SD, cbReadable, cbFree));
1311
1312 /* Do not write more than the sink can hold at the moment.
1313 * The host sets the overall pace. */
1314 if (cbReadable > cbFree)
1315 cbReadable = cbFree;
1316
1317 if (cbReadable)
1318 {
1319 uint8_t abFIFO[HDA_FIFO_MAX + 1];
1320 while (cbReadable)
1321 {
1322 uint32_t cbRead;
1323 rc2 = AudioMixerSinkRead(pSink, AUDMIXOP_COPY,
1324 abFIFO, RT_MIN(cbReadable, (uint32_t)sizeof(abFIFO)), &cbRead);
1325 AssertRCBreak(rc2);
1326
1327 if (!cbRead)
1328 {
1329 AssertMsgFailed(("Nothing read from sink, even if %RU32 bytes were (still) announced\n", cbReadable));
1330 break;
1331 }
1332
1333 /* Write (guest input) data to the stream which was read from stream's sink before. */
1334 uint32_t cbWritten;
1335 rc2 = hdaStreamWrite(pStream, abFIFO, cbRead, &cbWritten);
1336 AssertRCBreak(rc2);
1337
1338 if (!cbWritten)
1339 {
1340 AssertFailed(); /* Should never happen, as we know how much we can write. */
1341 break;
1342 }
1343
1344 Assert(cbReadable >= cbRead);
1345 cbReadable -= cbRead;
1346 }
1347 }
1348 #if 0
1349 else /* Send silence as input. */
1350 {
1351 cbReadable = pStream->State.cbTransferSize - pStream->State.cbTransferProcessed;
1352
1353 Log3Func(("[SD%RU8] Sending silence (%RU32 bytes)\n", pStream->u8SD, cbReadable));
1354
1355 if (cbReadable)
1356 {
1357 rc2 = hdaStreamWrite(pStream, NULL /* Silence */, cbReadable, NULL /* pcbWritten */);
1358 AssertRC(rc2);
1359 }
1360 }
1361 #endif
1362
1363 const uint32_t cbToTransfer = hdaStreamGetUsed(pStream);
1364 if (cbToTransfer)
1365 {
1366 rc2 = hdaStreamTransfer(pStream, cbToTransfer);
1367 AssertRC(rc2);
1368 }
1369#ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1370 } /* fInTimer */
1371#endif
1372 }
1373}
1374
1375/**
1376 * Locks an HDA stream for serialized access.
1377 *
1378 * @returns IPRT status code.
1379 * @param pStream HDA stream to lock.
1380 */
1381void hdaStreamLock(PHDASTREAM pStream)
1382{
1383 AssertPtrReturnVoid(pStream);
1384 int rc2 = RTCritSectEnter(&pStream->State.CritSect);
1385 AssertRC(rc2);
1386}
1387
1388/**
1389 * Unlocks a formerly locked HDA stream.
1390 *
1391 * @returns IPRT status code.
1392 * @param pStream HDA stream to unlock.
1393 */
1394void hdaStreamUnlock(PHDASTREAM pStream)
1395{
1396 AssertPtrReturnVoid(pStream);
1397 int rc2 = RTCritSectLeave(&pStream->State.CritSect);
1398 AssertRC(rc2);
1399}
1400
1401/**
1402 * Updates an HDA stream's current read or write buffer position (depending on the stream type) by
1403 * updating its associated LPIB register and DMA position buffer (if enabled).
1404 *
1405 * @returns Set LPIB value.
1406 * @param pStream HDA stream to update read / write position for.
1407 * @param u32LPIB New LPIB (position) value to set.
1408 */
1409uint32_t hdaStreamUpdateLPIB(PHDASTREAM pStream, uint32_t u32LPIB)
1410{
1411 AssertPtrReturn(pStream, 0);
1412
1413 AssertMsg(u32LPIB <= pStream->u32CBL,
1414 ("[SD%RU8] New LPIB (%RU32) exceeds CBL (%RU32)\n", pStream->u8SD, u32LPIB, pStream->u32CBL));
1415
1416 const PHDASTATE pThis = pStream->pHDAState;
1417
1418 u32LPIB = RT_MIN(u32LPIB, pStream->u32CBL);
1419
1420 LogFlowFunc(("[SD%RU8]: LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
1421 pStream->u8SD, u32LPIB, pThis->fDMAPosition));
1422
1423 /* Update LPIB in any case. */
1424 HDA_STREAM_REG(pThis, LPIB, pStream->u8SD) = u32LPIB;
1425
1426 /* Do we need to tell the current DMA position? */
1427 if (pThis->fDMAPosition)
1428 {
1429 int rc2 = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
1430 pThis->u64DPBase + (pStream->u8SD * 2 * sizeof(uint32_t)),
1431 (void *)&u32LPIB, sizeof(uint32_t));
1432 AssertRC(rc2);
1433 }
1434
1435 return u32LPIB;
1436}
1437
1438# ifdef HDA_USE_DMA_ACCESS_HANDLER
1439/**
1440 * Registers access handlers for a stream's BDLE DMA accesses.
1441 *
1442 * @returns true if registration was successful, false if not.
1443 * @param pStream HDA stream to register BDLE access handlers for.
1444 */
1445bool hdaStreamRegisterDMAHandlers(PHDASTREAM pStream)
1446{
1447 /* At least LVI and the BDL base must be set. */
1448 if ( !pStream->u16LVI
1449 || !pStream->u64BDLBase)
1450 {
1451 return false;
1452 }
1453
1454 hdaStreamUnregisterDMAHandlers(pStream);
1455
1456 LogFunc(("Registering ...\n"));
1457
1458 int rc = VINF_SUCCESS;
1459
1460 /*
1461 * Create BDLE ranges.
1462 */
1463
1464 struct BDLERANGE
1465 {
1466 RTGCPHYS uAddr;
1467 uint32_t uSize;
1468 } arrRanges[16]; /** @todo Use a define. */
1469
1470 size_t cRanges = 0;
1471
1472 for (uint16_t i = 0; i < pStream->u16LVI + 1; i++)
1473 {
1474 HDABDLE BDLE;
1475 rc = hdaBDLEFetch(pThis, &BDLE, pStream->u64BDLBase, i /* Index */);
1476 if (RT_FAILURE(rc))
1477 break;
1478
1479 bool fAddRange = true;
1480 BDLERANGE *pRange;
1481
1482 if (cRanges)
1483 {
1484 pRange = &arrRanges[cRanges - 1];
1485
1486 /* Is the current range a direct neighbor of the current BLDE? */
1487 if ((pRange->uAddr + pRange->uSize) == BDLE.Desc.u64BufAdr)
1488 {
1489 /* Expand the current range by the current BDLE's size. */
1490 pRange->uSize += BDLE.Desc.u32BufSize;
1491
1492 /* Adding a new range in this case is not needed anymore. */
1493 fAddRange = false;
1494
1495 LogFunc(("Expanding range %zu by %RU32 (%RU32 total now)\n", cRanges - 1, BDLE.Desc.u32BufSize, pRange->uSize));
1496 }
1497 }
1498
1499 /* Do we need to add a new range? */
1500 if ( fAddRange
1501 && cRanges < RT_ELEMENTS(arrRanges))
1502 {
1503 pRange = &arrRanges[cRanges];
1504
1505 pRange->uAddr = BDLE.Desc.u64BufAdr;
1506 pRange->uSize = BDLE.Desc.u32BufSize;
1507
1508 LogFunc(("Adding range %zu - 0x%x (%RU32)\n", cRanges, pRange->uAddr, pRange->uSize));
1509
1510 cRanges++;
1511 }
1512 }
1513
1514 LogFunc(("%zu ranges total\n", cRanges));
1515
1516 /*
1517 * Register all ranges as DMA access handlers.
1518 */
1519
1520 for (size_t i = 0; i < cRanges; i++)
1521 {
1522 BDLERANGE *pRange = &arrRanges[i];
1523
1524 PHDADMAACCESSHANDLER pHandler = (PHDADMAACCESSHANDLER)RTMemAllocZ(sizeof(HDADMAACCESSHANDLER));
1525 if (!pHandler)
1526 {
1527 rc = VERR_NO_MEMORY;
1528 break;
1529 }
1530
1531 RTListAppend(&pStream->State.lstDMAHandlers, &pHandler->Node);
1532
1533 pHandler->pStream = pStream; /* Save a back reference to the owner. */
1534
1535 char szDesc[32];
1536 RTStrPrintf(szDesc, sizeof(szDesc), "HDA[SD%RU8 - RANGE%02zu]", pStream->u8SD, i);
1537
1538 int rc2 = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3), PGMPHYSHANDLERKIND_WRITE,
1539 hdaDMAAccessHandler,
1540 NULL, NULL, NULL,
1541 NULL, NULL, NULL,
1542 szDesc, &pHandler->hAccessHandlerType);
1543 AssertRCBreak(rc2);
1544
1545 pHandler->BDLEAddr = pRange->uAddr;
1546 pHandler->BDLESize = pRange->uSize;
1547
1548 /* Get first and last pages of the BDLE range. */
1549 RTGCPHYS pgFirst = pRange->uAddr & ~PAGE_OFFSET_MASK;
1550 RTGCPHYS pgLast = RT_ALIGN(pgFirst + pRange->uSize, PAGE_SIZE);
1551
1552 /* Calculate the region size (in pages). */
1553 RTGCPHYS regionSize = RT_ALIGN(pgLast - pgFirst, PAGE_SIZE);
1554
1555 pHandler->GCPhysFirst = pgFirst;
1556 pHandler->GCPhysLast = pHandler->GCPhysFirst + (regionSize - 1);
1557
1558 LogFunc(("\tRegistering region '%s': 0x%x - 0x%x (region size: %zu)\n",
1559 szDesc, pHandler->GCPhysFirst, pHandler->GCPhysLast, regionSize));
1560 LogFunc(("\tBDLE @ 0x%x - 0x%x (%RU32)\n",
1561 pHandler->BDLEAddr, pHandler->BDLEAddr + pHandler->BDLESize, pHandler->BDLESize));
1562
1563 rc2 = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
1564 pHandler->GCPhysFirst, pHandler->GCPhysLast,
1565 pHandler->hAccessHandlerType, pHandler, NIL_RTR0PTR, NIL_RTRCPTR,
1566 szDesc);
1567 AssertRCBreak(rc2);
1568
1569 pHandler->fRegistered = true;
1570 }
1571
1572 LogFunc(("Registration ended with rc=%Rrc\n", rc));
1573
1574 return RT_SUCCESS(rc);
1575}
1576
1577/**
1578 * Unregisters access handlers of a stream's BDLEs.
1579 *
1580 * @param pStream HDA stream to unregister BDLE access handlers for.
1581 */
1582void hdaStreamUnregisterDMAHandlers(PHDASTREAM pStream)
1583{
1584 LogFunc(("\n"));
1585
1586 PHDADMAACCESSHANDLER pHandler, pHandlerNext;
1587 RTListForEachSafe(&pStream->State.lstDMAHandlers, pHandler, pHandlerNext, HDADMAACCESSHANDLER, Node)
1588 {
1589 if (!pHandler->fRegistered) /* Handler not registered? Skip. */
1590 continue;
1591
1592 LogFunc(("Unregistering 0x%x - 0x%x (%zu)\n",
1593 pHandler->GCPhysFirst, pHandler->GCPhysLast, pHandler->GCPhysLast - pHandler->GCPhysFirst));
1594
1595 int rc2 = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pStream->pHDAState->pDevInsR3),
1596 pHandler->GCPhysFirst);
1597 AssertRC(rc2);
1598
1599 RTListNodeRemove(&pHandler->Node);
1600
1601 RTMemFree(pHandler);
1602 pHandler = NULL;
1603 }
1604
1605 Assert(RTListIsEmpty(&pStream->State.lstDMAHandlers));
1606}
1607# endif /* HDA_USE_DMA_ACCESS_HANDLER */
1608
1609# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
1610/**
1611 * Asynchronous I/O thread for a HDA stream.
1612 * This will do the heavy lifting work for us as soon as it's getting notified by another thread.
1613 *
1614 * @returns IPRT status code.
1615 * @param hThreadSelf Thread handle.
1616 * @param pvUser User argument. Must be of type PHDASTREAMTHREADCTX.
1617 */
1618DECLCALLBACK(int) hdaStreamAsyncIOThread(RTTHREAD hThreadSelf, void *pvUser)
1619{
1620 PHDASTREAMTHREADCTX pCtx = (PHDASTREAMTHREADCTX)pvUser;
1621 AssertPtr(pCtx);
1622
1623 PHDASTREAM pStream = pCtx->pStream;
1624 AssertPtr(pStream);
1625
1626 PHDASTREAMSTATEAIO pAIO = &pCtx->pStream->State.AIO;
1627
1628 ASMAtomicXchgBool(&pAIO->fStarted, true);
1629
1630 RTThreadUserSignal(hThreadSelf);
1631
1632 LogFunc(("[SD%RU8]: Started\n", pStream->u8SD));
1633
1634 for (;;)
1635 {
1636 int rc2 = RTSemEventWait(pAIO->Event, RT_INDEFINITE_WAIT);
1637 if (RT_FAILURE(rc2))
1638 break;
1639
1640 if (ASMAtomicReadBool(&pAIO->fShutdown))
1641 break;
1642
1643 rc2 = RTCritSectEnter(&pAIO->CritSect);
1644 if (RT_SUCCESS(rc2))
1645 {
1646 if (!pAIO->fEnabled)
1647 {
1648 RTCritSectLeave(&pAIO->CritSect);
1649 continue;
1650 }
1651
1652 hdaStreamUpdate(pStream, false /* fInTimer */);
1653
1654 int rc3 = RTCritSectLeave(&pAIO->CritSect);
1655 AssertRC(rc3);
1656 }
1657
1658 AssertRC(rc2);
1659 }
1660
1661 LogFunc(("[SD%RU8]: Ended\n", pStream->u8SD));
1662
1663 ASMAtomicXchgBool(&pAIO->fStarted, false);
1664
1665 return VINF_SUCCESS;
1666}
1667
1668/**
1669 * Creates the async I/O thread for a specific HDA audio stream.
1670 *
1671 * @returns IPRT status code.
1672 * @param pStream HDA audio stream to create the async I/O thread for.
1673 */
1674int hdaStreamAsyncIOCreate(PHDASTREAM pStream)
1675{
1676 PHDASTREAMSTATEAIO pAIO = &pStream->State.AIO;
1677
1678 int rc;
1679
1680 if (!ASMAtomicReadBool(&pAIO->fStarted))
1681 {
1682 pAIO->fShutdown = false;
1683
1684 rc = RTSemEventCreate(&pAIO->Event);
1685 if (RT_SUCCESS(rc))
1686 {
1687 rc = RTCritSectInit(&pAIO->CritSect);
1688 if (RT_SUCCESS(rc))
1689 {
1690 HDASTREAMTHREADCTX Ctx = { pStream->pHDAState, pStream };
1691
1692 char szThreadName[64];
1693 RTStrPrintf2(szThreadName, sizeof(szThreadName), "hdaAIO%RU8", pStream->u8SD);
1694
1695 rc = RTThreadCreate(&pAIO->Thread, hdaStreamAsyncIOThread, &Ctx,
1696 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, szThreadName);
1697 if (RT_SUCCESS(rc))
1698 rc = RTThreadUserWait(pAIO->Thread, 10 * 1000 /* 10s timeout */);
1699 }
1700 }
1701 }
1702 else
1703 rc = VINF_SUCCESS;
1704
1705 LogFunc(("[SD%RU8]: Returning %Rrc\n", pStream->u8SD, rc));
1706 return rc;
1707}
1708
1709/**
1710 * Destroys the async I/O thread of a specific HDA audio stream.
1711 *
1712 * @returns IPRT status code.
1713 * @param pStream HDA audio stream to destroy the async I/O thread for.
1714 */
1715int hdaStreamAsyncIODestroy(PHDASTREAM pStream)
1716{
1717 PHDASTREAMSTATEAIO pAIO = &pStream->State.AIO;
1718
1719 if (!ASMAtomicReadBool(&pAIO->fStarted))
1720 return VINF_SUCCESS;
1721
1722 ASMAtomicWriteBool(&pAIO->fShutdown, true);
1723
1724 int rc = hdaStreamAsyncIONotify(pStream);
1725 AssertRC(rc);
1726
1727 int rcThread;
1728 rc = RTThreadWait(pAIO->Thread, 30 * 1000 /* 30s timeout */, &rcThread);
1729 LogFunc(("Async I/O thread ended with %Rrc (%Rrc)\n", rc, rcThread));
1730
1731 if (RT_SUCCESS(rc))
1732 {
1733 rc = RTCritSectDelete(&pAIO->CritSect);
1734 AssertRC(rc);
1735
1736 rc = RTSemEventDestroy(pAIO->Event);
1737 AssertRC(rc);
1738
1739 pAIO->fStarted = false;
1740 pAIO->fShutdown = false;
1741 pAIO->fEnabled = false;
1742 }
1743
1744 LogFunc(("[SD%RU8]: Returning %Rrc\n", pStream->u8SD, rc));
1745 return rc;
1746}
1747
1748/**
1749 * Lets the stream's async I/O thread know that there is some data to process.
1750 *
1751 * @returns IPRT status code.
1752 * @param pStream HDA stream to notify async I/O thread for.
1753 */
1754int hdaStreamAsyncIONotify(PHDASTREAM pStream)
1755{
1756 return RTSemEventSignal(pStream->State.AIO.Event);
1757}
1758
1759/**
1760 * Locks the async I/O thread of a specific HDA audio stream.
1761 *
1762 * @param pStream HDA stream to lock async I/O thread for.
1763 */
1764void hdaStreamAsyncIOLock(PHDASTREAM pStream)
1765{
1766 PHDASTREAMSTATEAIO pAIO = &pStream->State.AIO;
1767
1768 if (!ASMAtomicReadBool(&pAIO->fStarted))
1769 return;
1770
1771 int rc2 = RTCritSectEnter(&pAIO->CritSect);
1772 AssertRC(rc2);
1773}
1774
1775/**
1776 * Unlocks the async I/O thread of a specific HDA audio stream.
1777 *
1778 * @param pStream HDA stream to unlock async I/O thread for.
1779 */
1780void hdaStreamAsyncIOUnlock(PHDASTREAM pStream)
1781{
1782 PHDASTREAMSTATEAIO pAIO = &pStream->State.AIO;
1783
1784 if (!ASMAtomicReadBool(&pAIO->fStarted))
1785 return;
1786
1787 int rc2 = RTCritSectLeave(&pAIO->CritSect);
1788 AssertRC(rc2);
1789}
1790
1791/**
1792 * Enables (resumes) or disables (pauses) the async I/O thread.
1793 *
1794 * @param pStream HDA stream to enable/disable async I/O thread for.
1795 * @param fEnable Whether to enable or disable the I/O thread.
1796 *
1797 * @remarks Does not do locking.
1798 */
1799void hdaStreamAsyncIOEnable(PHDASTREAM pStream, bool fEnable)
1800{
1801 PHDASTREAMSTATEAIO pAIO = &pStream->State.AIO;
1802 ASMAtomicXchgBool(&pAIO->fEnabled, fEnable);
1803}
1804# endif /* VBOX_WITH_AUDIO_HDA_ASYNC_IO */
1805
1806#endif /* IN_RING3 */
1807
Note: See TracBrowser for help on using the repository browser.

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