add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.

This commit is contained in:
maksw committed 2026-08-11 18:38:36 +02:00
1 parent 8b254f84f4
commit 4add6789ba
177 files changed
+22565 -3070

No files matched your search

+403
View File
@@ -0,0 +1,403 @@
// Intel High Definition Audio PCI attachment and controller lifetime management.
#include "hda.h"
#include <string.h>
const NTL_DDK* gHdaDdk;
static DRIVER_OBJECT gHdaDriver;
static HDA_CONTROLLER* gHdaControllers[HDA_MAX_CONTROLLERS];
static uint32_t gHdaControllerCount;
static SPINLOCK gHdaRegistryLock = SPINLOCK_INIT;
static const IO_DEVICE_ID gHdaIds[] = {
{ IoBusPci, { "PCI\\VEN_8086&DEV_2668", sizeof("PCI\\VEN_8086&DEV_2668") - 1u } },
{ IoBusPci, { "PCI\\VEN_15AD&DEV_1977", sizeof("PCI\\VEN_15AD&DEV_1977") - 1u } },
{ IoBusPci, { "PCI\\CC_040300", sizeof("PCI\\CC_040300") - 1u } },
};
static const IO_DRIVER_ID_TABLE gHdaIdTable = {
gHdaIds,
sizeof(gHdaIds) / sizeof(gHdaIds[0]),
};
// Registers a controller in the shared interrupt scan set.
static KSTATUS HdaRegisterController(HDA_CONTROLLER* controller) {
if (!controller)
return NTLSTATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gHdaRegistryLock);
for (uint32_t index = 0; index < gHdaControllerCount; index++) {
if (gHdaControllers[index] == controller) {
KeReleaseSpinlock(&gHdaRegistryLock, flags);
return NTLSTATUS_SUCCESS;
}
}
if (gHdaControllerCount == HDA_MAX_CONTROLLERS) {
KeReleaseSpinlock(&gHdaRegistryLock, flags);
return NTLSTATUS_NO_MEMORY;
}
gHdaControllers[gHdaControllerCount++] = controller;
KeReleaseSpinlock(&gHdaRegistryLock, flags);
return NTLSTATUS_SUCCESS;
}
// Removes a controller from the shared interrupt scan set.
static void HdaUnregisterController(HDA_CONTROLLER* controller) {
if (!controller)
return;
uintptr_t flags = KeAcquireSpinlock(&gHdaRegistryLock);
for (uint32_t index = 0; index < gHdaControllerCount; index++) {
if (gHdaControllers[index] != controller)
continue;
gHdaControllers[index] = gHdaControllers[gHdaControllerCount - 1u];
gHdaControllers[gHdaControllerCount - 1u] = (HDA_CONTROLLER*)0;
gHdaControllerCount--;
break;
}
KeReleaseSpinlock(&gHdaRegistryLock, flags);
}
// Dispatches a hardware interrupt to every live HDA controller.
static void HdaIsr(void) {
DEVICE_OBJECT* devices[HDA_MAX_CONTROLLERS];
uint32_t count = 0;
uintptr_t flags = KeAcquireSpinlock(&gHdaRegistryLock);
count = gHdaControllerCount;
if (count > HDA_MAX_CONTROLLERS)
count = HDA_MAX_CONTROLLERS;
for (uint32_t index = 0; index < count; index++) {
HDA_CONTROLLER* controller = gHdaControllers[index];
devices[index] = controller ? controller->Fdo : (DEVICE_OBJECT*)0;
if (devices[index])
IoReferenceDeviceObject(devices[index]);
}
KeReleaseSpinlock(&gHdaRegistryLock, flags);
for (uint32_t index = 0; index < count; index++) {
DEVICE_OBJECT* device = devices[index];
if (!device)
continue;
HDA_CONTROLLER* controller = (HDA_CONTROLLER*)device->DeviceExtension;
if (controller && controller->Magic == HDA_CONTROLLER_MAGIC)
HdaHandleInterrupt(controller);
IoDereferenceDeviceObject(device);
}
}
// Registers the global ISR once for each kernel IRQ slot used by HDA.
static KSTATUS HdaRegisterIrq(uint8_t irq) {
if (irq >= 24u)
return NTLSTATUS_NOT_SUPPORTED;
KeIrqRegister(irq, HdaIsr);
return NTLSTATUS_SUCCESS;
}
// Maps BAR0 and validates the stream-descriptor aperture described by GCAP.
static KSTATUS HdaMapRegisters(HDA_CONTROLLER* controller) {
PCI_BAR bar;
KSTATUS status = PciGetBar(controller->Pdo, 0, &bar);
if (!KSUCCESS(status))
return status;
if (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)
return NTLSTATUS_NOT_SUPPORTED;
status = PciMapBar(controller->Pdo,
0,
PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_UC,
(void**)&controller->Registers,
&controller->RegisterBytes);
if (!KSUCCESS(status))
return status;
if (controller->RegisterBytes < HDA_REG_STREAM_BASE)
return NTLSTATUS_BUFFER_TOO_SMALL;
controller->Gcap = HdaRead16(controller, HDA_REG_GCAP);
controller->InputStreamCount =
(uint8_t)((controller->Gcap & HDA_GCAP_ISS_MASK) >> HDA_GCAP_ISS_SHIFT);
controller->OutputStreamCount =
(uint8_t)((controller->Gcap & HDA_GCAP_OSS_MASK) >> HDA_GCAP_OSS_SHIFT);
controller->BidirectionalStreamCount =
(uint8_t)((controller->Gcap & HDA_GCAP_BSS_MASK) >> HDA_GCAP_BSS_SHIFT);
controller->TotalStreamCount =
(uint8_t)(controller->InputStreamCount +
controller->OutputStreamCount +
controller->BidirectionalStreamCount);
if ((controller->OutputStreamCount == 0 &&
controller->BidirectionalStreamCount == 0) ||
controller->TotalStreamCount == 0 ||
controller->TotalStreamCount > 30u)
return NTLSTATUS_NOT_SUPPORTED;
uint64_t requiredBytes = HDA_REG_STREAM_BASE +
(uint64_t)controller->TotalStreamCount *
HDA_REG_STREAM_STRIDE;
if (controller->RegisterBytes < requiredBytes)
return NTLSTATUS_BUFFER_TOO_SMALL;
// Dedicated output descriptors precede bidirectional descriptors. Use the
// first bidirectional descriptor when no output-only descriptor exists.
uint8_t outputDescriptorBase = controller->InputStreamCount;
uint8_t bidirectionalDescriptorBase =
(uint8_t)(controller->InputStreamCount + controller->OutputStreamCount);
controller->Output.DescriptorIndex = controller->OutputStreamCount
? outputDescriptorBase
: bidirectionalDescriptorBase;
controller->Output.StreamTag = HDA_OUTPUT_STREAM_TAG;
return NTLSTATUS_SUCCESS;
}
// Configures MSI first and enables INTx only after MSI setup reports failure.
static KSTATUS HdaConfigureInterrupt(HDA_CONTROLLER* controller) {
KSTATUS status = PciEnableMsiAuto(controller->Pdo, &controller->Irq);
if (KSUCCESS(status)) {
controller->UsingMsi = true;
return NTLSTATUS_SUCCESS;
}
gHdaDdk->Debug->Print(
"HDA: MSI setup failed status=0x%x; attempting explicit INTx fallback\n",
status);
controller->UsingMsi = false;
status = PciGetInterrupt(controller->Pdo, &controller->Irq);
if (!KSUCCESS(status))
return status;
if (controller->Irq == 0xFFu || controller->Irq >= 24u)
return NTLSTATUS_NOT_SUPPORTED;
status = PciEnableDevice(controller->Pdo,
PCI_ENABLE_MEMORY |
PCI_ENABLE_BUSMASTER |
PCI_ENABLE_INTX);
return status;
}
// Waits until queued or active interrupt work no longer references the controller.
static KSTATUS HdaWaitForControllerInterruptWorkIdle(HDA_CONTROLLER* controller) {
if (!controller)
return NTLSTATUS_INVALID_PARAMETER;
for (uint32_t elapsed = 0; elapsed < 100000u; elapsed += 10u) {
if (controller->IsrActive == 0 &&
controller->Dpc.Inserted == 0 && controller->DpcActive == 0)
return NTLSTATUS_SUCCESS;
gHdaDdk->Timer->Stall(10);
}
return NTLSTATUS_TIMEOUT;
}
// Starts one PCI HDA controller and leaves endpoint registration to codec discovery.
static KSTATUS HdaStartDevice(HDA_CONTROLLER* controller) {
if (!controller || controller->Magic != HDA_CONTROLLER_MAGIC ||
!controller->Pdo || !controller->Fdo)
return NTLSTATUS_INVALID_PARAMETER;
if (controller->Started)
return NTLSTATUS_SUCCESS;
KSTATUS status = PciGetDeviceInfo(controller->Pdo, &controller->PciInfo);
if (!KSUCCESS(status))
return status;
if (controller->PciInfo.ClassCode != 0x04u ||
controller->PciInfo.Subclass != 0x03u ||
controller->PciInfo.ProgIf != 0x00u)
return NTLSTATUS_NOT_SUPPORTED;
status = PciEnableDevice(controller->Pdo,
PCI_ENABLE_MEMORY |
PCI_ENABLE_BUSMASTER |
PCI_DISABLE_INTX);
if (!KSUCCESS(status))
return status;
status = HdaMapRegisters(controller);
if (!KSUCCESS(status))
return status;
status = HdaConfigureInterrupt(controller);
if (!KSUCCESS(status))
return status;
status = HdaAllocateDma(controller);
if (!KSUCCESS(status))
return status;
KeInitializeDpc(&controller->Dpc, HdaDpcRoutine, controller);
status = HdaStartRecoveryWorker(controller);
if (!KSUCCESS(status)) {
HdaReleaseDma(controller);
return status;
}
status = HdaInitializeController(controller);
if (!KSUCCESS(status)) {
HdaStopRecoveryWorker(controller);
HdaQuiesceController(controller);
HdaReleaseDma(controller);
return status;
}
status = HdaRegisterController(controller);
if (!KSUCCESS(status)) {
HdaStopRecoveryWorker(controller);
HdaQuiesceController(controller);
HdaReleaseDma(controller);
return status;
}
status = HdaRegisterIrq(controller->Irq);
if (!KSUCCESS(status)) {
HdaUnregisterController(controller);
HdaStopRecoveryWorker(controller);
HdaQuiesceController(controller);
HdaReleaseDma(controller);
return status;
}
controller->Started = true;
controller->Fdo->PnpState = DevicePnpStarted;
status = HdaCodecInitialize(controller);
if (!KSUCCESS(status) && status != NTLSTATUS_NOT_SUPPORTED &&
status != NTLSTATUS_NOT_FOUND) {
controller->Started = false;
controller->Fdo->PnpState = DevicePnpNotStarted;
HdaUnregisterController(controller);
HdaStopRecoveryWorker(controller);
HdaQuiesceController(controller);
HdaReleaseDma(controller);
return status;
}
if (!KSUCCESS(status)) {
gHdaDdk->Debug->Print(
"HDA: no analog endpoint registered status=0x%x\n",
status);
}
HdaArmInterrupts(controller);
HdaHandleInterrupt(controller);
return NTLSTATUS_SUCCESS;
}
// Handles PnP lifetime transitions for one HDA function device object.
static KSTATUS HdaDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return NTLSTATUS_INVALID_PARAMETER;
HDA_CONTROLLER* controller = (HDA_CONTROLLER*)device->DeviceExtension;
if (controller->Magic != HDA_CONTROLLER_MAGIC)
return NTLSTATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* stack = IoGetCurrentIrpStackLocation(irp);
if (!stack || stack->MajorFunction != IRP_MJ_PNP)
return NTLSTATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)stack->MinorFunction) {
case IRP_MN_START_DEVICE:
return HdaStartDevice(controller);
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
return NTLSTATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
controller->Started = false;
controller->SurpriseRemoved = true;
HdaUnregisterController(controller);
HdaStopRecoveryWorker(controller);
KSTATUS surpriseDpcStatus =
HdaWaitForControllerInterruptWorkIdle(controller);
if (!KSUCCESS(surpriseDpcStatus)) {
KePanic(KE_PANIC_CODE_DRIVER,
"HDA: interrupt work did not quiesce during surprise removal status=0x%x\n",
surpriseDpcStatus);
}
HdaCodecStop(controller, IoAudioSurpriseRemoval);
HdaCodecDestroy(controller);
device->PnpState = DevicePnpSurpriseRemoved;
return NTLSTATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
if (!controller->SurpriseRemoved && controller->Registers)
HdaWrite32(controller, HDA_REG_INTCTL, 0);
controller->Started = false;
HdaUnregisterController(controller);
HdaStopRecoveryWorker(controller);
KSTATUS dpcStatus = HdaWaitForControllerInterruptWorkIdle(controller);
if (!KSUCCESS(dpcStatus)) {
KePanic(KE_PANIC_CODE_DRIVER,
"HDA: interrupt work did not quiesce during removal status=0x%x\n",
dpcStatus);
}
HdaCodecStop(controller,
controller->SurpriseRemoved
? IoAudioSurpriseRemoval
: IoAudioOrderlyRemoval);
HdaCodecDestroy(controller);
if (!controller->SurpriseRemoved)
HdaQuiesceController(controller);
controller->Removed = true;
HdaReleaseDma(controller);
if (!controller->SurpriseRemoved)
(void)PciEnableDevice(controller->Pdo, PCI_DISABLE_INTX);
device->PnpState = DevicePnpDeleted;
IoDetachDevice(device);
IoDeleteDevice(device);
return NTLSTATUS_SUCCESS;
default:
return IoForwardIrpToLower(device, irp);
}
}
// Attaches one HDA function device object to a matching PCI PDO.
static KSTATUS HdaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject ||
physicalDeviceObject->Type != DeviceObjectPhysical)
return NTLSTATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS status = IoCreateDevice(driver, sizeof(HDA_CONTROLLER), &fdo);
if (!KSUCCESS(status))
return status;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("hda"));
HDA_CONTROLLER* controller = (HDA_CONTROLLER*)fdo->DeviceExtension;
memset(controller, 0, sizeof(*controller));
controller->Magic = HDA_CONTROLLER_MAGIC;
controller->Fdo = fdo;
controller->Pdo = physicalDeviceObject;
controller->Irq = 0xFFu;
controller->StateLock = (SPINLOCK)SPINLOCK_INIT;
controller->Output.StreamTag = HDA_OUTPUT_STREAM_TAG;
controller->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!controller->LowerDevice) {
IoDeleteDevice(fdo);
return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
// Registers the Intel HDA transport driver with the I/O manager.
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
(void)init;
if (!ddk || !ddk->Debug || !ddk->Debug->Print || !ddk->Dma || !ddk->Timer ||
!ddk->Timer->QueryTicks || !ddk->Timer->Stall || !ddk->Audio ||
!ddk->Scheduler || !ddk->Scheduler->DelayThread)
return NTLSTATUS_INVALID_PARAMETER;
gHdaDdk = ddk;
gHdaControllerCount = 0;
memset(gHdaControllers, 0, sizeof(gHdaControllers));
memset(&gHdaDriver, 0, sizeof(gHdaDriver));
IoInitializeDriverObject(&gHdaDriver, SYS_SV_LITERAL("hda"));
gHdaDriver.IdTable = &gHdaIdTable;
gHdaDriver.DriverAddDevice = HdaAddDevice;
gHdaDriver.MajorFunction[IRP_MJ_PNP] = HdaDispatchPnp;
KSTATUS status = IoRegisterDriverObject(&gHdaDriver);
if (!KSUCCESS(status))
gHdaDdk->Debug->Print("HDA: IoRegisterDriverObject failed status=0x%x\n", status);
return status;
}
+389
View File
@@ -0,0 +1,389 @@
// Intel High Definition Audio transport declarations shared by the controller and stream implementation.
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/ddk/ddk.h>
#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 long PendingPeriodInterrupts;
volatile long 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 long IsrActive;
volatile long 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;
};
extern const NTL_DDK* gHdaDdk;
// 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);
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff