VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHDACommon.cpp@ 82323

Last change on this file since 82323 was 82323, checked in by vboxsync, 5 years ago

DevIchAc97,DevHDA: PDMCritSect -> PDMDevHlpCritSect. No need to take the locks again in the timer callback. Removed incorrect unlocking/locking around hdaR3TimerSet call in hdaR3StreamTransfer (didn't do much with the double locking, but makes a difference now that that has been eliminated). bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.3 KB
Line 
1/* $Id: DevHDACommon.cpp 82323 2019-12-02 14:33:12Z vboxsync $ */
2/** @file
3 * DevHDACommon.cpp - Shared HDA device functions.
4 */
5
6/*
7 * Copyright (C) 2017-2019 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#include <iprt/assert.h>
23#include <iprt/errcore.h>
24
25#include <VBox/AssertGuest.h>
26
27#define LOG_GROUP LOG_GROUP_DEV_HDA
28#include <VBox/log.h>
29
30#include "DrvAudio.h"
31
32#include "DevHDA.h"
33#include "DevHDACommon.h"
34
35#include "HDAStream.h"
36
37
38/**
39 * Processes (de/asserts) the interrupt according to the HDA's current state.
40 *
41 * @returns IPRT status code.
42 * @param pDevIns The device instance.
43 * @param pThis HDA state.
44 * @param pszSource Caller information.
45 */
46#if defined(LOG_ENABLED) || defined(DOXYGEN_RUNNING)
47int hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis, const char *pszSource)
48#else
49int hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis)
50#endif
51{
52 uint32_t uIntSts = hdaGetINTSTS(pThis);
53
54 HDA_REG(pThis, INTSTS) = uIntSts;
55
56 /* NB: It is possible to have GIS set even when CIE/SIEn are all zero; the GIS bit does
57 * not control the interrupt signal. See Figure 4 on page 54 of the HDA 1.0a spec.
58 */
59 /* Global Interrupt Enable (GIE) set? */
60 if ( (HDA_REG(pThis, INTCTL) & HDA_INTCTL_GIE)
61 && (HDA_REG(pThis, INTSTS) & HDA_REG(pThis, INTCTL) & (HDA_INTCTL_CIE | HDA_STRMINT_MASK)))
62 {
63 Log3Func(("Asserted (%s)\n", pszSource));
64
65 PDMDevHlpPCISetIrq(pDevIns, 0, 1 /* Assert */);
66 pThis->u8IRQL = 1;
67
68#ifdef DEBUG
69 pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
70 pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
71#endif
72 }
73 else
74 {
75 Log3Func(("Deasserted (%s)\n", pszSource));
76
77 PDMDevHlpPCISetIrq(pDevIns, 0, 0 /* Deassert */);
78 pThis->u8IRQL = 0;
79 }
80
81 return VINF_SUCCESS;
82}
83
84/**
85 * Retrieves the currently set value for the wall clock.
86 *
87 * @return IPRT status code.
88 * @return Currently set wall clock value.
89 * @param pThis HDA state.
90 *
91 * @remark Operation is atomic.
92 */
93uint64_t hdaWalClkGetCurrent(PHDASTATE pThis)
94{
95 return ASMAtomicReadU64(&pThis->u64WalClk);
96}
97
98#ifdef IN_RING3
99
100/**
101 * Sets the actual WALCLK register to the specified wall clock value.
102 * The specified wall clock value only will be set (unless fForce is set to true) if all
103 * handled HDA streams have passed (in time) that value. This guarantees that the WALCLK
104 * register stays in sync with all handled HDA streams.
105 *
106 * @return true if the WALCLK register has been updated, false if not.
107 * @param pThis HDA state.
108 * @param u64WalClk Wall clock value to set WALCLK register to.
109 * @param fForce Whether to force setting the wall clock value or not.
110 */
111bool hdaR3WalClkSet(PHDASTATE pThis, uint64_t u64WalClk, bool fForce)
112{
113 const bool fFrontPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkFront)->State.Period,
114 u64WalClk);
115 const uint64_t u64FrontAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(pThis, &pThis->SinkFront)->State.Period);
116# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
117# error "Implement me!"
118# endif
119
120 const bool fLineInPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period, u64WalClk);
121 const uint64_t u64LineInAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(pThis, &pThis->SinkLineIn)->State.Period);
122# ifdef VBOX_WITH_HDA_MIC_IN
123 const bool fMicInPassed = hdaR3StreamPeriodHasPassedAbsWalClk (&hdaR3GetStreamFromSink(pThis, &pThis->SinkMicIn)->State.Period, u64WalClk);
124 const uint64_t u64MicInAbsWalClk = hdaR3StreamPeriodGetAbsElapsedWalClk(&hdaR3GetStreamFromSink(pThis, &pThis->SinkMicIn)->State.Period);
125# endif
126
127# ifdef VBOX_STRICT
128 const uint64_t u64WalClkCur = ASMAtomicReadU64(&pThis->u64WalClk);
129# endif
130
131 /* Only drive the WALCLK register forward if all (active) stream periods have passed
132 * the specified point in time given by u64WalClk. */
133 if ( ( fFrontPassed
134# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
135# error "Implement me!"
136# endif
137 && fLineInPassed
138# ifdef VBOX_WITH_HDA_MIC_IN
139 && fMicInPassed
140# endif
141 )
142 || fForce)
143 {
144 if (!fForce)
145 {
146 /* Get the maximum value of all periods we need to handle.
147 * Not the most elegant solution, but works for now ... */
148 u64WalClk = RT_MAX(u64WalClk, u64FrontAbsWalClk);
149# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
150# error "Implement me!"
151# endif
152 u64WalClk = RT_MAX(u64WalClk, u64LineInAbsWalClk);
153# ifdef VBOX_WITH_HDA_MIC_IN
154 u64WalClk = RT_MAX(u64WalClk, u64MicInAbsWalClk);
155# endif
156
157# ifdef VBOX_STRICT
158 AssertMsg(u64WalClk >= u64WalClkCur,
159 ("Setting WALCLK to a value going backwards does not make any sense (old %RU64 vs. new %RU64)\n",
160 u64WalClkCur, u64WalClk));
161 if (u64WalClk == u64WalClkCur) /* Setting a stale value? */
162 {
163 if (pThis->u8WalClkStaleCnt++ > 3)
164 AssertMsgFailed(("Setting WALCLK to a stale value (%RU64) too often isn't a good idea really. "
165 "Good luck with stuck audio stuff.\n", u64WalClk));
166 }
167 else
168 pThis->u8WalClkStaleCnt = 0;
169# endif
170 }
171
172 /* Set the new WALCLK value. */
173 ASMAtomicWriteU64(&pThis->u64WalClk, u64WalClk);
174 }
175
176 const uint64_t u64WalClkNew = hdaWalClkGetCurrent(pThis);
177
178 Log3Func(("Cur: %RU64, New: %RU64 (force %RTbool) -> %RU64 %s\n",
179 u64WalClkCur, u64WalClk, fForce,
180 u64WalClkNew, u64WalClkNew == u64WalClk ? "[OK]" : "[DELAYED]"));
181
182 return (u64WalClkNew == u64WalClk);
183}
184
185/**
186 * Returns the default (mixer) sink from a given SD#.
187 * Returns NULL if no sink is found.
188 *
189 * @return PHDAMIXERSINK
190 * @param pThis HDA state.
191 * @param uSD SD# to return mixer sink for.
192 * NULL if not found / handled.
193 */
194PHDAMIXERSINK hdaR3GetDefaultSink(PHDASTATE pThis, uint8_t uSD)
195{
196 if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN)
197 {
198 const uint8_t uFirstSDI = 0;
199
200 if (uSD == uFirstSDI) /* First SDI. */
201 return &pThis->SinkLineIn;
202# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
203 if (uSD == uFirstSDI + 1)
204 return &pThis->SinkMicIn;
205# else
206 /* If we don't have a dedicated Mic-In sink, use the always present Line-In sink. */
207 return &pThis->SinkLineIn;
208# endif
209 }
210 else
211 {
212 const uint8_t uFirstSDO = HDA_MAX_SDI;
213
214 if (uSD == uFirstSDO)
215 return &pThis->SinkFront;
216# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
217 if (uSD == uFirstSDO + 1)
218 return &pThis->SinkCenterLFE;
219 if (uSD == uFirstSDO + 2)
220 return &pThis->SinkRear;
221# endif
222 }
223
224 return NULL;
225}
226
227#endif /* IN_RING3 */
228
229/**
230 * Returns the audio direction of a specified stream descriptor.
231 *
232 * The register layout specifies that input streams (SDI) come first,
233 * followed by the output streams (SDO). So every stream ID below HDA_MAX_SDI
234 * is an input stream, whereas everything >= HDA_MAX_SDI is an output stream.
235 *
236 * Note: SDnFMT register does not provide that information, so we have to judge
237 * for ourselves.
238 *
239 * @return Audio direction.
240 */
241PDMAUDIODIR hdaGetDirFromSD(uint8_t uSD)
242{
243 AssertReturn(uSD < HDA_MAX_STREAMS, PDMAUDIODIR_UNKNOWN);
244
245 if (uSD < HDA_MAX_SDI)
246 return PDMAUDIODIR_IN;
247
248 return PDMAUDIODIR_OUT;
249}
250
251/**
252 * Returns the HDA stream of specified stream descriptor number.
253 *
254 * @return Pointer to HDA stream, or NULL if none found.
255 */
256PHDASTREAM hdaGetStreamFromSD(PHDASTATE pThis, uint8_t uSD)
257{
258 AssertPtr(pThis);
259 ASSERT_GUEST_MSG_RETURN(uSD < HDA_MAX_STREAMS, ("uSD=%u (%#x)\n", uSD, uSD), NULL);
260 return &pThis->aStreams[uSD];
261}
262
263#ifdef IN_RING3
264
265/**
266 * Returns the HDA stream of specified HDA sink.
267 *
268 * @return Pointer to HDA stream, or NULL if none found.
269 */
270PHDASTREAM hdaR3GetStreamFromSink(PHDASTATE pThis, PHDAMIXERSINK pSink)
271{
272 AssertPtrReturn(pThis, NULL);
273 AssertPtrReturn(pSink, NULL);
274
275 /** @todo Do something with the channel mapping here? */
276 return pSink->pStream;
277}
278
279/**
280 * Reads DMA data from a given HDA output stream.
281 *
282 * @return IPRT status code.
283 * @param pThis HDA state.
284 * @param pStream HDA output stream to read DMA data from.
285 * @param pvBuf Where to store the read data.
286 * @param cbBuf How much to read in bytes.
287 * @param pcbRead Returns read bytes from DMA. Optional.
288 */
289int hdaR3DMARead(PHDASTATE pThis, PHDASTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
290{
291 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
292 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
293 /* pcbRead is optional. */
294
295 PHDABDLE pBDLE = &pStream->State.BDLE;
296
297 int rc = VINF_SUCCESS;
298
299 uint32_t cbReadTotal = 0;
300 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
301
302# ifdef HDA_DEBUG_SILENCE
303 uint64_t csSilence = 0;
304
305 pStream->Dbg.cSilenceThreshold = 100;
306 pStream->Dbg.cbSilenceReadMin = _1M;
307# endif
308
309 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
310
311 while (cbLeft)
312 {
313 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
314
315 rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), addrChunk, (uint8_t *)pvBuf + cbReadTotal, cbChunk);
316 if (RT_FAILURE(rc))
317 break;
318
319# ifdef HDA_DEBUG_SILENCE
320 uint16_t *pu16Buf = (uint16_t *)pvBuf;
321 for (size_t i = 0; i < cbChunk / sizeof(uint16_t); i++)
322 {
323 if (*pu16Buf == 0)
324 csSilence++;
325 else
326 break;
327 pu16Buf++;
328 }
329# endif
330 if (pStream->Dbg.Runtime.fEnabled)
331 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbReadTotal, cbChunk, 0 /* fFlags */);
332
333 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbChunk);
334 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
335
336 Assert(cbLeft >= cbChunk);
337 cbLeft -= cbChunk;
338
339 cbReadTotal += cbChunk;
340 }
341
342# ifdef HDA_DEBUG_SILENCE
343 if (csSilence)
344 pStream->Dbg.csSilence += csSilence;
345
346 if ( csSilence == 0
347 && pStream->Dbg.csSilence > pStream->Dbg.cSilenceThreshold
348 && pStream->Dbg.cbReadTotal >= pStream->Dbg.cbSilenceReadMin)
349 {
350 LogFunc(("Silent block detected: %RU64 audio samples\n", pStream->Dbg.csSilence));
351 pStream->Dbg.csSilence = 0;
352 }
353# endif
354
355 if (RT_SUCCESS(rc))
356 {
357 if (pcbRead)
358 *pcbRead = cbReadTotal;
359 }
360
361 return rc;
362}
363
364/**
365 * Writes audio data from an HDA input stream's FIFO to its associated DMA area.
366 *
367 * @return IPRT status code.
368 * @param pThis HDA state.
369 * @param pStream HDA input stream to write audio data to.
370 * @param pvBuf Data to write.
371 * @param cbBuf How much (in bytes) to write.
372 * @param pcbWritten Returns written bytes on success. Optional.
373 */
374int hdaR3DMAWrite(PHDASTATE pThis, PHDASTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
375{
376 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
377 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
378 /* pcbWritten is optional. */
379
380 PHDABDLE pBDLE = &pStream->State.BDLE;
381
382 int rc = VINF_SUCCESS;
383
384 uint32_t cbWrittenTotal = 0;
385 uint32_t cbLeft = RT_MIN(cbBuf, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
386
387 RTGCPHYS addrChunk = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
388
389 while (cbLeft)
390 {
391 uint32_t cbChunk = RT_MIN(cbLeft, pStream->u16FIFOS);
392
393 /* Sanity checks. */
394 Assert(cbChunk <= pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
395
396 if (pStream->Dbg.Runtime.fEnabled)
397 DrvAudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMARaw, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk, 0 /* fFlags */);
398
399 rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
400 addrChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
401 if (RT_FAILURE(rc))
402 break;
403
404 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbChunk);
405 addrChunk = (addrChunk + cbChunk) % pBDLE->Desc.u32BufSize;
406
407 Assert(cbLeft >= cbChunk);
408 cbLeft -= (uint32_t)cbChunk;
409
410 cbWrittenTotal += (uint32_t)cbChunk;
411 }
412
413 if (RT_SUCCESS(rc))
414 {
415 if (pcbWritten)
416 *pcbWritten = cbWrittenTotal;
417 }
418 else
419 LogFunc(("Failed with %Rrc\n", rc));
420
421 return rc;
422}
423
424#endif /* IN_RING3 */
425
426/**
427 * Returns a new INTSTS value based on the current device state.
428 *
429 * @returns Determined INTSTS register value.
430 * @param pThis HDA state.
431 *
432 * @remark This function does *not* set INTSTS!
433 */
434uint32_t hdaGetINTSTS(PHDASTATE pThis)
435{
436 uint32_t intSts = 0;
437
438 /* Check controller interrupts (RIRB, STATEST). */
439 if (HDA_REG(pThis, RIRBSTS) & HDA_REG(pThis, RIRBCTL) & (HDA_RIRBCTL_ROIC | HDA_RIRBCTL_RINTCTL))
440 {
441 intSts |= HDA_INTSTS_CIS; /* Set the Controller Interrupt Status (CIS). */
442 }
443
444 /* Check SDIN State Change Status Flags. */
445 if (HDA_REG(pThis, STATESTS) & HDA_REG(pThis, WAKEEN))
446 {
447 intSts |= HDA_INTSTS_CIS; /* Touch Controller Interrupt Status (CIS). */
448 }
449
450 /* For each stream, check if any interrupt status bit is set and enabled. */
451 for (uint8_t iStrm = 0; iStrm < HDA_MAX_STREAMS; ++iStrm)
452 {
453 if (HDA_STREAM_REG(pThis, STS, iStrm) & HDA_STREAM_REG(pThis, CTL, iStrm) & (HDA_SDCTL_DEIE | HDA_SDCTL_FEIE | HDA_SDCTL_IOCE))
454 {
455 Log3Func(("[SD%d] interrupt status set\n", iStrm));
456 intSts |= RT_BIT(iStrm);
457 }
458 }
459
460 if (intSts)
461 intSts |= HDA_INTSTS_GIS; /* Set the Global Interrupt Status (GIS). */
462
463 Log3Func(("-> 0x%x\n", intSts));
464
465 return intSts;
466}
467
468#ifdef IN_RING3
469
470/**
471 * Converts an HDA stream's SDFMT register into a given PCM properties structure.
472 *
473 * @return IPRT status code.
474 * @param u16SDFMT The HDA stream's SDFMT value to convert.
475 * @param pProps PCM properties structure to hold converted result on success.
476 */
477int hdaR3SDFMTToPCMProps(uint16_t u16SDFMT, PPDMAUDIOPCMPROPS pProps)
478{
479 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
480
481# define EXTRACT_VALUE(v, mask, shift) ((v & ((mask) << (shift))) >> (shift))
482
483 int rc = VINF_SUCCESS;
484
485 uint32_t u32Hz = EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BASE_RATE_MASK, HDA_SDFMT_BASE_RATE_SHIFT)
486 ? 44100 : 48000;
487 uint32_t u32HzMult = 1;
488 uint32_t u32HzDiv = 1;
489
490 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT))
491 {
492 case 0: u32HzMult = 1; break;
493 case 1: u32HzMult = 2; break;
494 case 2: u32HzMult = 3; break;
495 case 3: u32HzMult = 4; break;
496 default:
497 LogFunc(("Unsupported multiplier %x\n",
498 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT)));
499 rc = VERR_NOT_SUPPORTED;
500 break;
501 }
502 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT))
503 {
504 case 0: u32HzDiv = 1; break;
505 case 1: u32HzDiv = 2; break;
506 case 2: u32HzDiv = 3; break;
507 case 3: u32HzDiv = 4; break;
508 case 4: u32HzDiv = 5; break;
509 case 5: u32HzDiv = 6; break;
510 case 6: u32HzDiv = 7; break;
511 case 7: u32HzDiv = 8; break;
512 default:
513 LogFunc(("Unsupported divisor %x\n",
514 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT)));
515 rc = VERR_NOT_SUPPORTED;
516 break;
517 }
518
519 uint8_t cBytes = 0;
520 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT))
521 {
522 case 0:
523 cBytes = 1;
524 break;
525 case 1:
526 cBytes = 2;
527 break;
528 case 4:
529 cBytes = 4;
530 break;
531 default:
532 AssertMsgFailed(("Unsupported bits per sample %x\n",
533 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT)));
534 rc = VERR_NOT_SUPPORTED;
535 break;
536 }
537
538 if (RT_SUCCESS(rc))
539 {
540 RT_BZERO(pProps, sizeof(PDMAUDIOPCMPROPS));
541
542 pProps->cbSample = cBytes;
543 pProps->fSigned = true;
544 pProps->cChannels = (u16SDFMT & 0xf) + 1;
545 pProps->uHz = u32Hz * u32HzMult / u32HzDiv;
546 pProps->cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
547 }
548
549# undef EXTRACT_VALUE
550 return rc;
551}
552
553# ifdef LOG_ENABLED
554void hdaR3BDLEDumpAll(PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
555{
556 LogFlowFunc(("BDLEs @ 0x%x (%RU16):\n", u64BDLBase, cBDLE));
557 if (!u64BDLBase)
558 return;
559
560 uint32_t cbBDLE = 0;
561 for (uint16_t i = 0; i < cBDLE; i++)
562 {
563 HDABDLEDESC bd;
564 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
565
566 LogFunc(("\t#%03d BDLE(adr:0x%llx, size:%RU32, ioc:%RTbool)\n",
567 i, bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_FLAG_IOC));
568
569 cbBDLE += bd.u32BufSize;
570 }
571
572 LogFlowFunc(("Total: %RU32 bytes\n", cbBDLE));
573
574 if (!pThis->u64DPBase) /* No DMA base given? Bail out. */
575 return;
576
577 LogFlowFunc(("DMA counters:\n"));
578
579 for (int i = 0; i < cBDLE; i++)
580 {
581 uint32_t uDMACnt;
582 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), (pThis->u64DPBase & DPBASE_ADDR_MASK) + (i * 2 * sizeof(uint32_t)),
583 &uDMACnt, sizeof(uDMACnt));
584
585 LogFlowFunc(("\t#%03d DMA @ 0x%x\n", i , uDMACnt));
586 }
587}
588# endif /* LOG_ENABLED */
589
590/**
591 * Fetches a Bundle Descriptor List Entry (BDLE) from the DMA engine.
592 *
593 * @param pThis Pointer to HDA state.
594 * @param pBDLE Where to store the fetched result.
595 * @param u64BaseDMA Address base of DMA engine to use.
596 * @param u16Entry BDLE entry to fetch.
597 */
598int hdaR3BDLEFetch(PHDASTATE pThis, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry)
599{
600 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
601 AssertPtrReturn(pBDLE, VERR_INVALID_POINTER);
602 AssertReturn(u64BaseDMA, VERR_INVALID_PARAMETER);
603
604 if (!u64BaseDMA)
605 {
606 LogRel2(("HDA: Unable to fetch BDLE #%RU16 - no base DMA address set (yet)\n", u16Entry));
607 return VERR_NOT_FOUND;
608 }
609 /** @todo Compare u16Entry with LVI. */
610
611 int rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)),
612 &pBDLE->Desc, sizeof(pBDLE->Desc));
613
614 if (RT_SUCCESS(rc))
615 {
616 /* Reset internal state. */
617 RT_ZERO(pBDLE->State);
618 pBDLE->State.u32BDLIndex = u16Entry;
619 }
620
621 Log3Func(("Entry #%d @ 0x%x: %R[bdle], rc=%Rrc\n", u16Entry, u64BaseDMA + (u16Entry * sizeof(HDABDLEDESC)), pBDLE, rc));
622
623
624 return VINF_SUCCESS;
625}
626
627/**
628 * Tells whether a given BDLE is complete or not.
629 *
630 * @return true if BDLE is complete, false if not.
631 * @param pBDLE BDLE to retrieve status for.
632 */
633bool hdaR3BDLEIsComplete(PHDABDLE pBDLE)
634{
635 bool fIsComplete = false;
636
637 if ( !pBDLE->Desc.u32BufSize /* There can be BDLEs with 0 size. */
638 || (pBDLE->State.u32BufOff >= pBDLE->Desc.u32BufSize))
639 {
640 Assert(pBDLE->State.u32BufOff == pBDLE->Desc.u32BufSize);
641 fIsComplete = true;
642 }
643
644 Log3Func(("%R[bdle] => %s\n", pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
645
646 return fIsComplete;
647}
648
649/**
650 * Tells whether a given BDLE needs an interrupt or not.
651 *
652 * @return true if BDLE needs an interrupt, false if not.
653 * @param pBDLE BDLE to retrieve status for.
654 */
655bool hdaR3BDLENeedsInterrupt(PHDABDLE pBDLE)
656{
657 return (pBDLE->Desc.fFlags & HDA_BDLE_FLAG_IOC);
658}
659
660/**
661 * Sets the virtual device timer to a new expiration time.
662 *
663 * @returns Whether the new expiration time was set or not.
664 * @param pThis HDA state.
665 * @param pStream HDA stream to set timer for.
666 * @param tsExpire New (virtual) expiration time to set.
667 * @param fForce Whether to force setting the expiration time or not.
668 *
669 * @remark This function takes all active HDA streams and their
670 * current timing into account. This is needed to make sure
671 * that all streams can match their needed timing.
672 *
673 * To achieve this, the earliest (lowest) timestamp of all
674 * active streams found will be used for the next scheduling slot.
675 *
676 * Forcing a new expiration time will override the above mechanism.
677 */
678bool hdaR3TimerSet(PHDASTATE pThis, PHDASTREAM pStream, uint64_t tsExpire, bool fForce)
679{
680 AssertPtrReturn(pThis, false);
681 AssertPtrReturn(pStream, false);
682
683 uint64_t tsExpireMin = tsExpire;
684
685 if (!fForce)
686 {
687 if (hdaR3StreamTransferIsScheduled(pStream))
688 tsExpireMin = RT_MIN(tsExpireMin, hdaR3StreamTransferGetNext(pStream));
689 }
690
691 AssertPtr(pThis->pTimer[pStream->u8SD]);
692
693 const uint64_t tsNow = TMTimerGet(pThis->pTimer[pStream->u8SD]);
694
695 /*
696 * Make sure to not go backwards in time, as this will assert in TMTimerSet().
697 * This in theory could happen in hdaR3StreamTransferGetNext() from above.
698 */
699 if (tsExpireMin < tsNow)
700 tsExpireMin = tsNow;
701
702 int rc = TMTimerSet(pThis->pTimer[pStream->u8SD], tsExpireMin);
703 AssertRC(rc);
704
705 return RT_SUCCESS(rc);
706}
707
708#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