// Intel High Definition Audio transport declarations shared by the controller and stream implementation. #pragma once #include #include #include #include #include #include #define HDA_CONTROLLER_MAGIC 0x4144484Eu #define HDA_MAX_CONTROLLERS 8u #define HDA_MAX_CODECS 15u #define HDA_MAX_NODE_ID 0xFFu #define HDA_MAX_UNSOLICITED_RESPONSES 32u #define HDA_OUTPUT_SAMPLE_RATE 48000u #define HDA_OUTPUT_CHANNELS 2u #define HDA_OUTPUT_VALID_BITS 16u #define HDA_OUTPUT_BYTES_PER_SAMPLE 2u #define HDA_OUTPUT_BYTES_PER_FRAME 4u #define HDA_OUTPUT_PERIOD_FRAMES 512u #define HDA_OUTPUT_PERIOD_COUNT 4u #define HDA_OUTPUT_PERIOD_BYTES (HDA_OUTPUT_PERIOD_FRAMES * HDA_OUTPUT_BYTES_PER_FRAME) #define HDA_OUTPUT_BUFFER_BYTES (HDA_OUTPUT_PERIOD_COUNT * HDA_OUTPUT_PERIOD_BYTES) #define HDA_OUTPUT_STREAM_FORMAT 0x0011u #define HDA_OUTPUT_STREAM_TAG 1u #define HDA_MAX_LOGICAL_ENDPOINTS 3u #define HDA_REG_GCAP 0x00u #define HDA_REG_VMIN 0x02u #define HDA_REG_VMAJ 0x03u #define HDA_REG_GCTL 0x08u #define HDA_REG_STATESTS 0x0Eu #define HDA_REG_INTCTL 0x20u #define HDA_REG_INTSTS 0x24u #define HDA_REG_WALLCLK 0x30u #define HDA_REG_CORBLBASE 0x40u #define HDA_REG_CORBUBASE 0x44u #define HDA_REG_CORBWP 0x48u #define HDA_REG_CORBRP 0x4Au #define HDA_REG_CORBCTL 0x4Cu #define HDA_REG_CORBSTS 0x4Du #define HDA_REG_CORBSIZE 0x4Eu #define HDA_REG_RIRBLBASE 0x50u #define HDA_REG_RIRBUBASE 0x54u #define HDA_REG_RIRBWP 0x58u #define HDA_REG_RINTCNT 0x5Au #define HDA_REG_RIRBCTL 0x5Cu #define HDA_REG_RIRBSTS 0x5Du #define HDA_REG_RIRBSIZE 0x5Eu #define HDA_REG_DPLBASE 0x70u #define HDA_REG_DPUBASE 0x74u #define HDA_REG_STREAM_BASE 0x80u #define HDA_REG_STREAM_STRIDE 0x20u #define HDA_GCAP_64OK 0x0001u #define HDA_GCAP_BSS_MASK 0x00F8u #define HDA_GCAP_BSS_SHIFT 3u #define HDA_GCAP_ISS_MASK 0x0F00u #define HDA_GCAP_ISS_SHIFT 8u #define HDA_GCAP_OSS_MASK 0xF000u #define HDA_GCAP_OSS_SHIFT 12u #define HDA_GCTL_RESET 0x00000001u #define HDA_GCTL_UNSOLICITED_ENABLE 0x00000100u #define HDA_INT_CONTROLLER_ENABLE 0x40000000u #define HDA_INT_GLOBAL_ENABLE 0x80000000u #define HDA_INT_CONTROLLER_STATUS 0x40000000u #define HDA_INT_GLOBAL_STATUS 0x80000000u #define HDA_CORBRP_POINTER_MASK 0x00FFu #define HDA_CORBRP_RESET 0x8000u #define HDA_CORBCTL_MEMORY_ERROR_INTERRUPT 0x01u #define HDA_CORBCTL_RUN 0x02u #define HDA_CORBSTS_MEMORY_ERROR 0x01u #define HDA_RIRBWP_RESET 0x8000u #define HDA_RIRBCTL_RESPONSE_INTERRUPT 0x01u #define HDA_RIRBCTL_RUN 0x02u #define HDA_RIRBCTL_OVERRUN_INTERRUPT 0x04u #define HDA_RIRBSTS_RESPONSE 0x01u #define HDA_RIRBSTS_OVERRUN 0x04u #define HDA_RIRB_RESPONSE_CODEC_MASK 0x0000000Fu #define HDA_RIRB_RESPONSE_UNSOLICITED 0x00000010u #define HDA_RING_SIZE_CAP_2 0x10u #define HDA_RING_SIZE_CAP_16 0x20u #define HDA_RING_SIZE_CAP_256 0x40u #define HDA_RING_SIZE_SELECT_2 0x00u #define HDA_RING_SIZE_SELECT_16 0x01u #define HDA_RING_SIZE_SELECT_256 0x02u #define HDA_DPLBASE_ENABLE 0x00000001u #define HDA_STREAM_REG_CTL 0x00u #define HDA_STREAM_REG_STS 0x03u #define HDA_STREAM_REG_LPIB 0x04u #define HDA_STREAM_REG_CBL 0x08u #define HDA_STREAM_REG_LVI 0x0Cu #define HDA_STREAM_REG_FORMAT 0x12u #define HDA_STREAM_REG_BDLPL 0x18u #define HDA_STREAM_REG_BDLPU 0x1Cu #define HDA_STREAM_CTL_RESET 0x000001u #define HDA_STREAM_CTL_RUN 0x000002u #define HDA_STREAM_CTL_INTERRUPT_COMPLETE 0x000004u #define HDA_STREAM_CTL_INTERRUPT_FIFO_ERROR 0x000008u #define HDA_STREAM_CTL_INTERRUPT_DESCRIPTOR_ERROR 0x000010u #define HDA_STREAM_CTL_INTERRUPT_MASK 0x00001Cu #define HDA_STREAM_CTL_TAG_SHIFT 20u #define HDA_STREAM_CTL_TAG_MASK 0x00F00000u #define HDA_STREAM_STS_COMPLETE 0x04u #define HDA_STREAM_STS_FIFO_ERROR 0x08u #define HDA_STREAM_STS_DESCRIPTOR_ERROR 0x10u #define HDA_STREAM_STS_ERROR_MASK 0x18u #define HDA_STREAM_STS_INTERRUPT_MASK 0x1Cu #define HDA_BDL_FLAG_INTERRUPT_ON_COMPLETION 0x00000001u #define HDA_VERB_GET_PARAMETER(parameterId) (0xF0000u | ((uint32_t)(parameterId) & 0xFFu)) #define HDA_PARAMETER_VENDOR_ID 0x00u #define HDA_CONTROLLER_RESET_TIMEOUT_US 100000u #define HDA_STREAM_RESET_TIMEOUT_US 10000u #define HDA_RING_RESET_TIMEOUT_US 10000u #define HDA_CODEC_COMMAND_TIMEOUT_US 100000u #define HDA_CODEC_COMMAND_ATTEMPTS 2u #define HDA_CODEC_RESET_SETTLE_US 1000u #define HDA_STREAM_ERROR_WINDOW_US 1000000ull #define HDA_STREAM_ERRORS_BEFORE_CONTROLLER_RESET 3u #define HDA_RECOVERY_NONE 0u #define HDA_RECOVERY_STREAM 1u #define HDA_RECOVERY_CONTROLLER 2u #pragma pack(push, 1) typedef struct HDA_BDL_ENTRY { uint64_t Address; uint32_t Length; uint32_t Flags; } HDA_BDL_ENTRY; typedef struct HDA_RIRB_ENTRY { uint32_t Response; uint32_t ResponseExtra; } HDA_RIRB_ENTRY; #pragma pack(pop) _Static_assert(sizeof(HDA_BDL_ENTRY) == 16u, "HDA BDL entries must be 16 bytes"); _Static_assert(sizeof(HDA_RIRB_ENTRY) == 8u, "HDA RIRB entries must be 8 bytes"); typedef struct HDA_CONTROLLER HDA_CONTROLLER; typedef struct HDA_CODEC_STATE HDA_CODEC_STATE; typedef struct HDA_OUTPUT_DESCRIPTION { void* PeriodBuffer; size_t PeriodBufferBytes; uint32_t PeriodCount; uint32_t FramesPerPeriod; size_t PeriodStrideBytes; IO_AUDIO_FORMAT HardwareFormat; uint8_t StreamTag; } HDA_OUTPUT_DESCRIPTION; typedef struct HDA_TRANSPORT_CALLBACKS { // Reports one BDL-owned period after completion interrupt processing. void (*PeriodComplete)(void* context, uint32_t completedPeriodIndex, uint64_t presentedFrames, uint64_t finePositionFrames, uint64_t interruptCount, uint64_t timestampTicks); // Delivers one unsolicited codec response outside the controller lock. void (*UnsolicitedResponse)(void* context, uint8_t codecAddress, uint32_t response, uint32_t responseExtra); // Restores codec programming in a muted state while transport start and stop are serialized. KSTATUS (*RestoreAfterControllerReset)(void* context); // Unmutes the restored route only after controller recovery has restarted DMA. KSTATUS (*ResumeAfterControllerReset)(void* context); // Reports a recovery failure that leaves the transport stopped. void (*TransportFailure)(void* context, KSTATUS status); } HDA_TRANSPORT_CALLBACKS; typedef struct HDA_UNSOLICITED_RESPONSE { uint32_t Response; uint32_t ResponseExtra; } HDA_UNSOLICITED_RESPONSE; typedef struct HDA_OUTPUT_RING { IO_DMA_BUFFER* Dma; uint8_t* Buffer; uint64_t DeviceAddress; uint8_t NextLogicalPeriod; uint64_t PresentedFrames; } HDA_OUTPUT_RING; typedef struct HDA_OUTPUT_STREAM { uint8_t DescriptorIndex; uint8_t StreamTag; uint8_t BdlPeriodMap[HDA_OUTPUT_PERIOD_COUNT]; uint8_t NextCompletionBdl; bool Prepared; bool Running; volatile int32_t PendingPeriodInterrupts; volatile int32_t PendingDmaErrors; HDA_OUTPUT_RING Rings[HDA_MAX_LOGICAL_ENDPOINTS]; uint8_t ActiveRingIndex; IO_DMA_BUFFER* BdlDma; HDA_BDL_ENTRY* Bdl; uint64_t BdlDeviceAddress; uint64_t PositionRunBaseFrames; uint64_t LastPositionFrames; uint64_t PositionWrapFrames; uint32_t LastHardwarePositionBytes; bool PositionInitialized; uint64_t InterruptCount; uint64_t PeriodCompletionCount; uint64_t DmaErrorCount; uint64_t StreamResetCount; uint64_t PositionBufferReadCount; uint64_t LpibReadCount; uint64_t PositionDiscontinuityCount; } HDA_OUTPUT_STREAM; struct HDA_CONTROLLER { uint32_t Magic; DEVICE_OBJECT* Fdo; DEVICE_OBJECT* Pdo; DEVICE_OBJECT* LowerDevice; PCI_DEVICE_INFO PciInfo; volatile uint8_t* Registers; uint64_t RegisterBytes; uint16_t Gcap; uint8_t InputStreamCount; uint8_t OutputStreamCount; uint8_t BidirectionalStreamCount; uint8_t TotalStreamCount; uint16_t CodecMask; uint8_t Irq; bool UsingMsi; bool Started; bool Removed; bool SurpriseRemoved; SPINLOCK StateLock; KDPC Dpc; volatile int32_t IsrActive; volatile int32_t DpcActive; IO_DMA_ADAPTER* DmaAdapter; IO_DMA_BUFFER* CorbDma; IO_DMA_BUFFER* RirbDma; IO_DMA_BUFFER* PositionDma; uint32_t* Corb; HDA_RIRB_ENTRY* Rirb; volatile uint64_t* PositionBuffer; uint64_t CorbDeviceAddress; uint64_t RirbDeviceAddress; uint64_t PositionDeviceAddress; uint16_t CorbEntryCount; uint16_t RirbEntryCount; uint16_t CorbWritePointer; uint16_t RirbReadPointer; MUTEX* CommandMutex; MUTEX* StreamMutex; bool CommandWaiting; bool CommandResponseReady; uint8_t CommandCodecAddress; uint32_t CommandResponse; uint32_t CommandResponseExtra; uint64_t CodecCommandCount; uint64_t CodecCommandRetryCount; uint64_t CodecCommandFailureCount; uint64_t RirbResponseCount; uint64_t RirbUnsolicitedCount; uint64_t RirbOverrunCount; uint64_t CorbMemoryErrorCount; HDA_UNSOLICITED_RESPONSE Unsolicited[HDA_MAX_UNSOLICITED_RESPONSES]; uint8_t UnsolicitedRead; uint8_t UnsolicitedWrite; uint8_t UnsolicitedCount; HDA_OUTPUT_STREAM Output; HDA_TRANSPORT_CALLBACKS Callbacks; void* CallbackContext; SEMAPHORE* RecoverySemaphore; EVENT* RecoveryFinished; THREAD* RecoveryThread; bool RecoveryWorkerRunning; bool RecoveryInProgress; uint8_t PendingRecovery; uint64_t StreamErrorWindowStart; uint32_t StreamErrorsInWindow; uint64_t ControllerResetCount; HDA_CODEC_STATE* Codec; uint64_t InterruptCount; uint64_t ControllerInterruptCount; }; // Returns one byte from the controller register aperture. uint8_t HdaRead8(const HDA_CONTROLLER* controller, uint32_t offset); // Returns one word from the controller register aperture. uint16_t HdaRead16(const HDA_CONTROLLER* controller, uint32_t offset); // Returns one dword from the controller register aperture. uint32_t HdaRead32(const HDA_CONTROLLER* controller, uint32_t offset); // Writes one byte to the controller register aperture. void HdaWrite8(HDA_CONTROLLER* controller, uint32_t offset, uint8_t value); // Writes one word to the controller register aperture. void HdaWrite16(HDA_CONTROLLER* controller, uint32_t offset, uint16_t value); // Writes one dword to the controller register aperture. void HdaWrite32(HDA_CONTROLLER* controller, uint32_t offset, uint32_t value); // Returns the register offset of the controller's one output stream descriptor. uint32_t HdaOutputStreamBase(const HDA_CONTROLLER* controller); // Allocates the transport and output DMA resources for one controller. KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller); // Releases all DMA and synchronization resources owned by one controller. void HdaReleaseDma(HDA_CONTROLLER* controller); // Resets and initializes the controller, command rings, position buffer, and output descriptor. KSTATUS HdaInitializeController(HDA_CONTROLLER* controller); // Arms controller and output-stream interrupts after ISR registration. void HdaArmInterrupts(HDA_CONTROLLER* controller); // Stops hardware access without releasing software resources. void HdaQuiesceController(HDA_CONTROLLER* controller); // Sends one 20-bit codec verb payload through CORB/RIRB with one retry. KSTATUS HdaCodecVerb(HDA_CONTROLLER* controller, uint8_t codecAddress, uint8_t nodeId, uint32_t verbPayload, uint32_t* responseOut); // Copies one logical endpoint ring contract for codec and endpoint setup. KSTATUS HdaDescribeOutput(HDA_CONTROLLER* controller, uint8_t ringIndex, HDA_OUTPUT_DESCRIPTION* descriptionOut); // Selects one logical endpoint ring while the stream is stopped. KSTATUS HdaSelectOutputRing(HDA_CONTROLLER* controller, uint8_t ringIndex); // Replaces the transport callbacks while the output stream is stopped. KSTATUS HdaSetTransportCallbacks(HDA_CONTROLLER* controller, const HDA_TRANSPORT_CALLBACKS* callbacks, void* context); // Starts the prepared fixed-format output stream. KSTATUS HdaStartOutput(HDA_CONTROLLER* controller); // Stops the output stream while retaining its DMA ring. void HdaStopOutput(HDA_CONTROLLER* controller); // Returns the monotonic fine position derived from the position buffer or LPIB. uint64_t HdaQueryOutputPositionFrames(HDA_CONTROLLER* controller); // Captures one controller interrupt and queues deferred processing. void HdaHandleInterrupt(HDA_CONTROLLER* controller); // Runs period delivery, unsolicited callbacks, and recovery scheduling at DPC level. void HdaDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2); // Starts the passive recovery worker for one controller. KSTATUS HdaStartRecoveryWorker(HDA_CONTROLLER* controller); // Stops and joins the passive recovery worker for one controller. void HdaStopRecoveryWorker(HDA_CONTROLLER* controller); // Discovers codec topology and registers usable analog render endpoints. KSTATUS HdaCodecInitialize(HDA_CONTROLLER* controller); // Stops jack workers and unregisters codec endpoints. void HdaCodecStop(HDA_CONTROLLER* controller, IO_AUDIO_REMOVAL_MODE mode); // Releases codec topology storage after interrupt work is quiescent. void HdaCodecDestroy(HDA_CONTROLLER* controller);