VirtualBox

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

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

DevHDACommon: Added review comment on some inexplicable code in hdaTimerSet. Andy?

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