[idd] audio: add the mediated PCM data plane

Expose cancel-safe direct-I/O queues for reading rendered frames and
writing captured frames through the private broker endpoint.

Separate render and capture storage, publish format and state
generations, and preserve local loopback while no broker is bound.
This commit is contained in:
Geoffrey McRae
2026-09-02 00:02:57 +10:00
parent e55cb8bd61
commit 5e07786468
6 changed files with 1322 additions and 63 deletions

View File

@@ -112,33 +112,140 @@ NTSTATUS InstallWaveSubdevice(
}
CAudioAdapter::CAudioAdapter() :
m_refCount(1)
m_refCount(1),
m_brokerActive(0),
m_renderDiscontinuity(0),
m_renderState{},
m_captureState{}
{
KeInitializeSpinLock(&m_transportLock);
KeInitializeSpinLock(&m_streamStateLock);
m_renderState.state = KSSTATE_STOP;
m_captureState.state = KSSTATE_STOP;
}
CAudioAdapter::~CAudioAdapter()
{
m_ring.Destroy();
m_captureRing.Destroy();
m_renderRing.Destroy();
}
NTSTATUS CAudioAdapter::Initialize()
{
NTSTATUS status = m_clock.Initialize();
if (NT_SUCCESS(status))
status = m_ring.Init();
status = m_renderRing.Init();
if (NT_SUCCESS(status))
status = m_captureRing.Init();
return status;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS CAudioAdapter::ConfigureRender(
NTSTATUS CAudioAdapter::ConfigureStream(
_In_ EndpointKind kind,
_In_ const FormatFingerprint &format)
{
return m_ring.Configure(format);
CAudioRingBuffer &ring = kind == EndpointKind::Render ?
m_renderRing : m_captureRing;
KIRQL transportIrql;
KeAcquireSpinLock(&m_transportLock, &transportIrql);
const NTSTATUS status = ring.Configure(format);
if (!NT_SUCCESS(status))
{
KeReleaseSpinLock(&m_transportLock, transportIrql);
return status;
}
KIRQL oldIrql;
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
m_renderState : m_captureState;
stream.format = format;
stream.formatValid = true;
++stream.generation;
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
KeReleaseSpinLock(&m_transportLock, transportIrql);
BrokerDeviceNotifyStreamChanged(this, kind);
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void CAudioAdapter::SetStreamState(
_In_ EndpointKind kind,
_In_ KSSTATE state)
{
bool changed;
bool reset;
KIRQL transportIrql;
KeAcquireSpinLock(&m_transportLock, &transportIrql);
KIRQL oldIrql;
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
AudioStreamSnapshot &stream = kind == EndpointKind::Render ?
m_renderState : m_captureState;
changed = stream.state != state;
reset = changed && state == KSSTATE_STOP;
stream.state = state;
if (reset)
++stream.generation;
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
if (reset)
{
if (kind == EndpointKind::Render)
m_renderRing.Reset();
else
m_captureRing.Reset();
}
KeReleaseSpinLock(&m_transportLock, transportIrql);
if (changed)
BrokerDeviceNotifyStreamChanged(this, kind);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void CAudioAdapter::GetStreamSnapshot(
_In_ EndpointKind kind,
_Out_ AudioStreamSnapshot *snapshot)
{
if (!snapshot)
return;
KIRQL oldIrql;
KeAcquireSpinLock(&m_streamStateLock, &oldIrql);
*snapshot = kind == EndpointKind::Render ?
m_renderState : m_captureState;
KeReleaseSpinLock(&m_streamStateLock, oldIrql);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void CAudioAdapter::SetBrokerActive(_In_ bool active)
{
KIRQL oldIrql;
KeAcquireSpinLock(&m_transportLock, &oldIrql);
const LONG value = active ? 1 : 0;
if (InterlockedExchange(&m_brokerActive, value) == value)
{
KeReleaseSpinLock(&m_transportLock, oldIrql);
return;
}
m_captureRing.Reset();
m_renderRing.Reset();
InterlockedExchange(&m_renderDiscontinuity, active ? 1 : 0);
KeReleaseSpinLock(&m_transportLock, oldIrql);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
void CAudioAdapter::ResetCapture()
{
m_ring.Reset();
KIRQL oldIrql;
KeAcquireSpinLock(&m_transportLock, &oldIrql);
if (InterlockedCompareExchange(&m_brokerActive, 0, 0))
m_captureRing.Reset();
else
m_renderRing.Reset();
KeReleaseSpinLock(&m_transportLock, oldIrql);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
@@ -146,7 +253,14 @@ void CAudioAdapter::WriteRenderFrames(
_In_reads_bytes_(bytes) const BYTE *data,
_In_ ULONG bytes)
{
m_ring.Push(data, bytes);
ULONG droppedBytes = 0;
KIRQL oldIrql;
KeAcquireSpinLock(&m_transportLock, &oldIrql);
m_renderRing.Push(data, bytes, &droppedBytes);
if (droppedBytes)
InterlockedExchange(&m_renderDiscontinuity, 1);
KeReleaseSpinLock(&m_transportLock, oldIrql);
BrokerDeviceNotifyRenderAvailable(this, bytes, droppedBytes);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
@@ -155,7 +269,16 @@ void CAudioAdapter::ReadCaptureFrames(
_Out_writes_bytes_(bytes) BYTE *data,
_In_ ULONG bytes)
{
m_ring.Pop(format, data, bytes);
KIRQL oldIrql;
KeAcquireSpinLock(&m_transportLock, &oldIrql);
const bool brokerActive =
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
CAudioRingBuffer &ring = brokerActive ?
m_captureRing : m_renderRing;
const ULONG copied = ring.Pop(format, data, bytes);
KeReleaseSpinLock(&m_transportLock, oldIrql);
if (brokerActive && copied < bytes)
BrokerDeviceRecordCaptureUnderrun(this, bytes - copied);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
@@ -163,7 +286,107 @@ void CAudioAdapter::DiscardCaptureFrames(
_In_ const FormatFingerprint &format,
_In_ ULONGLONG bytes)
{
m_ring.Discard(format, bytes);
KIRQL oldIrql;
KeAcquireSpinLock(&m_transportLock, &oldIrql);
const bool brokerActive =
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0;
CAudioRingBuffer &ring = brokerActive ?
m_captureRing : m_renderRing;
const ULONG discarded = ring.Discard(format, bytes);
KeReleaseSpinLock(&m_transportLock, oldIrql);
if (brokerActive && bytes > discarded)
BrokerDeviceRecordCaptureUnderrun(this, bytes - discarded);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS CAudioAdapter::ReadRenderFrames(
_In_ const FormatFingerprint &format,
_In_ ULONGLONG streamGeneration,
_Out_writes_bytes_(capacity) BYTE *data,
_In_ ULONG capacity,
_Out_ PULONG bytesRead,
_Out_ bool *discontinuity)
{
if (!data || !bytesRead || !discontinuity || !format.blockAlign)
return STATUS_INVALID_PARAMETER;
*bytesRead = 0;
*discontinuity = false;
KIRQL transportIrql;
KeAcquireSpinLock(&m_transportLock, &transportIrql);
KIRQL stateIrql;
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
const bool active =
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
m_renderState.state == KSSTATE_RUN &&
m_renderState.formatValid &&
m_renderState.generation == streamGeneration &&
EqualFormatFingerprint(m_renderState.format, format);
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
if (!active)
{
KeReleaseSpinLock(&m_transportLock, transportIrql);
return STATUS_DEVICE_NOT_READY;
}
ULONG bytes = m_renderRing.Available();
if (bytes > capacity)
bytes = capacity;
bytes -= bytes % format.blockAlign;
if (!bytes)
{
KeReleaseSpinLock(&m_transportLock, transportIrql);
return STATUS_SUCCESS;
}
const ULONG copied = m_renderRing.Pop(format, data, bytes);
if (copied)
{
*discontinuity = InterlockedExchange(
&m_renderDiscontinuity, 0) != 0;
}
*bytesRead = copied;
KeReleaseSpinLock(&m_transportLock, transportIrql);
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS CAudioAdapter::WriteCaptureFrames(
_In_ const FormatFingerprint &format,
_In_ ULONGLONG streamGeneration,
_In_ bool discontinuity,
_In_reads_bytes_(bytes) const BYTE *data,
_In_ ULONG bytes,
_Out_ PULONG droppedBytes)
{
if (!data || !droppedBytes)
return STATUS_INVALID_PARAMETER;
*droppedBytes = 0;
KIRQL transportIrql;
KeAcquireSpinLock(&m_transportLock, &transportIrql);
KIRQL stateIrql;
KeAcquireSpinLock(&m_streamStateLock, &stateIrql);
const bool active =
InterlockedCompareExchange(&m_brokerActive, 0, 0) != 0 &&
m_captureState.state == KSSTATE_RUN &&
m_captureState.formatValid &&
m_captureState.generation == streamGeneration &&
EqualFormatFingerprint(m_captureState.format, format);
KeReleaseSpinLock(&m_streamStateLock, stateIrql);
if (!active)
{
KeReleaseSpinLock(&m_transportLock, transportIrql);
return STATUS_DEVICE_NOT_READY;
}
if (discontinuity)
m_captureRing.Reset();
const ULONG written = m_captureRing.Push(data, bytes, droppedBytes);
KeReleaseSpinLock(&m_transportLock, transportIrql);
return written == bytes ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
}
_IRQL_requires_max_(DISPATCH_LEVEL)