VirtualBox

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

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

Audio: doxygen and stats tweaks. bugref:9890

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