/* $Id: DevHdaStream.h $ */ /** @file * Intel HD Audio Controller Emulation - Streams. */ /* * Copyright (C) 2017-2022 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ #ifndef VBOX_INCLUDED_SRC_Audio_DevHdaStream_h #define VBOX_INCLUDED_SRC_Audio_DevHdaStream_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif #ifndef VBOX_INCLUDED_SRC_Audio_DevHda_h # error "Only include DevHda.h!" #endif /** * Structure containing HDA stream debug stuff, configurable at runtime. */ typedef struct HDASTREAMDEBUGRT { /** Whether debugging is enabled or not. */ bool fEnabled; uint8_t Padding[7]; /** File for dumping stream reads / writes. * For input streams, this dumps data being written to the device FIFO, * whereas for output streams this dumps data being read from the device FIFO. */ R3PTRTYPE(PAUDIOHLPFILE) pFileStream; /** File for dumping raw DMA reads / writes. * For input streams, this dumps data being written to the device DMA, * whereas for output streams this dumps data being read from the device DMA. */ R3PTRTYPE(PAUDIOHLPFILE) pFileDMARaw; /** File for dumping mapped (that is, extracted) DMA reads / writes. */ R3PTRTYPE(PAUDIOHLPFILE) pFileDMAMapped; } HDASTREAMDEBUGRT; /** * Structure containing HDA stream debug information. */ typedef struct HDASTREAMDEBUG { /** Runtime debug info. */ HDASTREAMDEBUGRT Runtime; uint64_t au64Alignment[2]; } HDASTREAMDEBUG; /** * Internal state of a HDA stream. */ typedef struct HDASTREAMSTATE { /** Flag indicating whether this stream currently is * in reset mode and therefore not acccessible by the guest. */ volatile bool fInReset; /** Flag indicating if the stream is in running state or not. */ volatile bool fRunning; /** How many interrupts are pending due to * BDLE interrupt-on-completion (IOC) bits set. */ uint8_t cTransferPendingInterrupts; /** Input streams only: Set when we switch from feeding the guest silence and * commits to proving actual audio input bytes. */ bool fInputPreBuffered; /** Input streams only: The number of bytes we need to prebuffer. */ uint32_t cbInputPreBuffer; /** Timestamp (absolute, in timer ticks) of the last DMA data transfer. * @note This is used for wall clock (WALCLK) calculations. */ uint64_t volatile tsTransferLast; /** The stream's current configuration (matches SDnFMT). */ PDMAUDIOSTREAMCFG Cfg; /** Timestamp (real time, in ns) of last DMA transfer. */ uint64_t tsLastTransferNs; /** Timestamp (real time, in ns) of last stream read (to backends). * When running in async I/O mode, this differs from \a tsLastTransferNs, * because reading / processing will be done in a separate stream. */ uint64_t tsLastReadNs; /** The start time for the playback (on the timer clock). */ uint64_t tsStart; /** @name DMA engine * @{ */ /** Timestamp (absolute, in timer ticks) of the next DMA data transfer. * Next for determining the next scheduling window. * Can be 0 if no next transfer is scheduled. */ uint64_t tsTransferNext; /** The size of the current DMA transfer period. */ uint32_t cbCurDmaPeriod; /** The size of an average transfer. */ uint32_t cbAvgTransfer; /** Current circular buffer read offset (for tracing & logging). */ uint64_t offRead; /** Current circular buffer write offset (for tracing & logging). */ uint64_t offWrite; /** The offset into the current BDLE. */ uint32_t offCurBdle; /** LVI + 1 */ uint16_t cBdles; /** The index of the current BDLE. * This is the entry which period is currently "running" on the DMA timer. */ uint8_t idxCurBdle; /** The number of prologue scheduling steps. * This is used when the tail BDLEs doesn't have IOC set. */ uint8_t cSchedulePrologue; /** Number of scheduling steps. */ uint16_t cSchedule; /** Current scheduling step. */ uint16_t idxSchedule; /** Current loop number within the current scheduling step. */ uint32_t idxScheduleLoop; /** Buffer descriptors and additional timer scheduling state. * (Same as HDABDLEDESC, with more sensible naming.) */ struct { /** The buffer address. */ uint64_t GCPhys; /** The buffer size (guest bytes). */ uint32_t cb; /** The flags (only bit 0 is defined). */ uint32_t fFlags; } aBdl[256]; /** Scheduling steps. */ struct { /** Number of timer ticks per period. * ASSUMES that we don't need a full second and that the timer resolution * isn't much higher than nanoseconds. */ uint32_t cPeriodTicks; /** The period length in host bytes. */ uint32_t cbPeriod; /** Number of times to repeat the period. */ uint32_t cLoops; /** The BDL index of the first entry. */ uint8_t idxFirst; /** The number of BDL entries. */ uint8_t cEntries; uint8_t abPadding[2]; } aSchedule[512+8]; #ifdef VBOX_HDA_WITH_ON_REG_ACCESS_DMA /** Number of valid bytes in abDma. * @note Volatile to prevent the compiler from re-reading it after we've * validated the value in ring-0. */ uint32_t volatile cbDma; /** Total number of bytes going via abDma this timer period. */ uint32_t cbDmaTotal; /** DMA bounce buffer for ring-0 register reads (LPIB). */ uint8_t abDma[2048 - 8]; #endif /** @} */ } HDASTREAMSTATE; AssertCompileSizeAlignment(HDASTREAMSTATE, 16); AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 8); AssertCompileMemberAlignment(HDASTREAMSTATE, aBdl, 16); AssertCompileMemberAlignment(HDASTREAMSTATE, aSchedule, 16); /** * An HDA stream (SDI / SDO) - shared. * * @note This HDA stream has nothing to do with a regular audio stream handled * by the audio connector or the audio mixer. This HDA stream is a serial * data in/out stream (SDI/SDO) defined in hardware and can contain * multiple audio streams in one single SDI/SDO (interleaving streams). * * Contains only register values which do *not* change until a stream reset * occurs. */ typedef struct HDASTREAM { /** Internal state of this stream. */ HDASTREAMSTATE State; /** Stream descriptor number (SDn). */ uint8_t u8SD; /** Current channel index. * For a stereo stream, this is u8Channel + 1. */ uint8_t u8Channel; /** FIFO Watermark (checked + translated in bytes, FIFOW). * This will be update from hdaRegWriteSDFIFOW() and also copied * hdaR3StreamInit() for some reason. */ uint8_t u8FIFOW; /** @name Register values at stream setup. * These will all be copied in hdaR3StreamInit(). * @{ */ /** FIFO Size (checked + translated in bytes, FIFOS). * This is supposedly the max number of bytes we'll be DMA'ing in one chunk * and correspondingly the LPIB & wall clock update jumps. However, we're * not at all being honest with the guest about this. */ uint8_t u8FIFOS; /** Cyclic Buffer Length (SDnCBL) - Represents the size of the ring buffer. */ uint32_t u32CBL; /** Last Valid Index (SDnLVI). */ uint16_t u16LVI; /** Format (SDnFMT). */ uint16_t u16FMT; uint8_t abPadding[4]; /** DMA base address (SDnBDPU - SDnBDPL). */ uint64_t u64BDLBase; /** @} */ /** The timer for pumping data thru the attached LUN drivers. */ TMTIMERHANDLE hTimer; /** Pad the structure size to a 64 byte alignment. */ uint64_t au64Padding1[2]; } HDASTREAM; AssertCompileMemberAlignment(HDASTREAM, State.aBdl, 16); AssertCompileMemberAlignment(HDASTREAM, State.aSchedule, 16); AssertCompileSizeAlignment(HDASTREAM, 64); /** Pointer to an HDA stream (SDI / SDO). */ typedef HDASTREAM *PHDASTREAM; /** * An HDA stream (SDI / SDO) - ring-3 bits. */ typedef struct HDASTREAMR3 { /** Stream descriptor number (SDn). */ uint8_t u8SD; uint8_t abPadding[7]; /** The shared state for the parent HDA device. */ R3PTRTYPE(PHDASTATE) pHDAStateShared; /** The ring-3 state for the parent HDA device. */ R3PTRTYPE(PHDASTATER3) pHDAStateR3; /** Pointer to HDA sink this stream is attached to. */ R3PTRTYPE(PHDAMIXERSINK) pMixSink; /** Internal state of this stream. */ struct { /** Circular buffer (FIFO) for holding DMA'ed data. */ R3PTRTYPE(PRTCIRCBUF) pCircBuf; /** The mixer sink this stream has registered AIO update callback with. * This is NULL till we register it, typically in hdaR3StreamEnable. * (The problem with following the pMixSink assignment is that hdaR3StreamReset * sets it without updating the HDA sink structure, so things get out of * wack in hdaR3MixerControl later in the initial device reset.) */ PAUDMIXSINK pAioRegSink; /** Size of the DMA buffer (pCircBuf) in bytes. */ uint32_t StatDmaBufSize; /** Number of used bytes in the DMA buffer (pCircBuf). */ uint32_t StatDmaBufUsed; /** Counter for all under/overflows problems. */ STAMCOUNTER StatDmaFlowProblems; /** Counter for unresovled under/overflows problems. */ STAMCOUNTER StatDmaFlowErrors; /** Number of bytes involved in unresolved flow errors. */ STAMCOUNTER StatDmaFlowErrorBytes; /** DMA skipped because buffer interrupt pending. */ STAMCOUNTER StatDmaSkippedPendingBcis; STAMPROFILE StatStart; STAMPROFILE StatReset; STAMPROFILE StatStop; } State; /** Debug bits. */ HDASTREAMDEBUG Dbg; uint64_t au64Alignment[3]; } HDASTREAMR3; AssertCompileSizeAlignment(HDASTREAMR3, 64); /** Pointer to an HDA stream (SDI / SDO). */ typedef HDASTREAMR3 *PHDASTREAMR3; /** @name Stream functions (all contexts). * @{ */ VBOXSTRICTRC hdaStreamDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint64_t tsNow, uint32_t cbToTransfer); VBOXSTRICTRC hdaStreamMaybeDoOnAccessDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint64_t tsNow); /** @} */ #ifdef IN_RING3 /** @name Stream functions (ring-3). * @{ */ int hdaR3StreamConstruct(PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, PHDASTATE pThis, PHDASTATER3 pThisCC, uint8_t uSD); void hdaR3StreamDestroy(PHDASTREAMR3 pStreamR3); int hdaR3StreamSetUp(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD); void hdaR3StreamReset(PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, uint8_t uSD); int hdaR3StreamEnable(PHDASTATE pThis, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3, bool fEnable); void hdaR3StreamMarkStarted(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTREAM pStreamShared, uint64_t tsNow); void hdaR3StreamMarkStopped(PHDASTREAM pStreamShared); uint64_t hdaR3StreamTimerMain(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared, PHDASTREAMR3 pStreamR3); DECLCALLBACK(void) hdaR3StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser); /** @} */ /** @name Helper functions associated with the stream code. * @{ */ int hdaR3SDFMTToPCMProps(uint16_t u16SDFMT, PPDMAUDIOPCMPROPS pProps); # ifdef LOG_ENABLED void hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE); # endif /** @} */ #endif /* IN_RING3 */ #endif /* !VBOX_INCLUDED_SRC_Audio_DevHdaStream_h */