Files
appsandbox/tools/vad/wavstream.cpp
James Stringer e25fedb943 Fix VAD audio driver validation and refcount bugs
- M15 wavminiport.cpp: reject PinId >= WaveDescriptor->PinCount in the
  proposed-format property handler.
- M16 wavstream.cpp: guard divide-by-zero on m_ulDmaMovementRate and
  nAvgBytesPerSec in buffer allocation and presentation-position.
- L14 topology.cpp: set PropertyRequest->ValueSize in both jack
  description GET paths.
- L15 adaptercommon.cpp: balance the adapter instance count on the
  allocation-failure path.
2026-06-02 21:51:42 -04:00

1077 lines
28 KiB
C++

#include "driverdefs.h"
#include <limits.h>
#include <ks.h>
#include "pinnodes.h"
#include "wavminiport.h"
#include "wavstream.h"
#pragma warning (disable : 4127)
//=============================================================================
// CVadWaveStream
//=============================================================================
#pragma code_seg("PAGE")
CVadWaveStream::~CVadWaveStream(void)
{
PAGED_CODE();
if (NULL != m_pMiniport)
{
if (m_bUnregisterStream)
{
m_pMiniport->StreamClosed(m_ulPin, this);
m_bUnregisterStream = FALSE;
}
m_pMiniport->Release();
m_pMiniport = NULL;
}
if (m_pDpc)
{
ExFreePoolWithTag(m_pDpc, VAD_WAVRT_POOLTAG);
m_pDpc = NULL;
}
if (m_pTimer)
{
ExFreePoolWithTag(m_pTimer, VAD_WAVRT_POOLTAG);
m_pTimer = NULL;
}
if (m_pbMuted)
{
ExFreePoolWithTag(m_pbMuted, VAD_WAVRT_POOLTAG);
m_pbMuted = NULL;
}
if (m_plVolumeLevel)
{
ExFreePoolWithTag(m_plVolumeLevel, VAD_WAVRT_POOLTAG);
m_plVolumeLevel = NULL;
}
if (m_plPeakMeter)
{
ExFreePoolWithTag(m_plPeakMeter, VAD_WAVRT_POOLTAG);
m_plPeakMeter = NULL;
}
if (m_pWfExt)
{
ExFreePoolWithTag(m_pWfExt, VAD_WAVRT_POOLTAG);
m_pWfExt = NULL;
}
if (m_pNotificationTimer)
{
ExDeleteTimer
(
m_pNotificationTimer,
TRUE,
TRUE,
NULL
);
}
KeFlushQueuedDpcs();
DPF_ENTER(("[CVadWaveStream::~CVadWaveStream]"));
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS
CVadWaveStream::Init
(
_In_ PCVadWaveMiniport Miniport_,
_In_ PPORTWAVERTSTREAM PortStream_,
_In_ ULONG Pin_,
_In_ BOOLEAN Capture_,
_In_ PKSDATAFORMAT DataFormat_,
_In_ GUID SignalProcessingMode
)
{
PAGED_CODE();
PWAVEFORMATEX pWfEx = NULL;
NTSTATUS ntStatus = STATUS_SUCCESS;
m_pMiniport = NULL;
m_ulPin = 0;
m_bUnregisterStream = FALSE;
m_bCapture = FALSE;
m_ulDmaBufferSize = 0;
m_pDmaBuffer = NULL;
m_ulNotificationsPerBuffer = 0;
m_KsState = KSSTATE_STOP;
m_pTimer = NULL;
m_pDpc = NULL;
m_llPacketCounter = 0;
m_ullPlayPosition = 0;
m_ullWritePosition = 0;
m_ullDmaTimeStamp = 0;
m_hnsElapsedTimeCarryForward = 0;
m_ullLastDPCTimeStamp = 0;
m_hnsDPCTimeCarryForward = 0;
m_ulDmaMovementRate = 0;
m_byteDisplacementCarryForward = 0;
m_bLfxEnabled = FALSE;
m_pbMuted = NULL;
m_plVolumeLevel = NULL;
m_plPeakMeter = NULL;
m_pWfExt = NULL;
m_ullLinearPosition = 0;
m_ullPresentationPosition = 0;
m_ulContentId = 0;
m_ulCurrentWritePosition = 0;
m_ulLastOsReadPacket = ULONG_MAX;
m_ulLastOsWritePacket = ULONG_MAX;
m_IsCurrentWritePositionUpdated = 0;
m_SignalProcessingMode = SignalProcessingMode;
m_bEoSReceived = FALSE;
m_bLastBufferRendered = FALSE;
m_pPortStream = PortStream_;
InitializeListHead(&m_NotificationList);
m_ulNotificationIntervalMs = 0;
KeInitializeSpinLock(&m_PositionSpinLock);
m_pNotificationTimer = ExAllocateTimer(
VadTimerNotifyRT,
this,
EX_TIMER_HIGH_RESOLUTION
);
if (!m_pNotificationTimer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
pWfEx = GetWaveFormatEx(DataFormat_);
if (NULL == pWfEx)
{
return STATUS_UNSUCCESSFUL;
}
m_pMiniport = reinterpret_cast<CVadWaveMiniport*>(Miniport_);
if (m_pMiniport == NULL)
{
return STATUS_INVALID_PARAMETER;
}
m_pMiniport->AddRef();
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
m_ulPin = Pin_;
m_bCapture = Capture_;
m_ulDmaMovementRate = pWfEx->nAvgBytesPerSec;
m_pDpc = (PRKDPC)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDPC), VAD_WAVRT_POOLTAG);
if (!m_pDpc)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(WAVEFORMATEX) + pWfEx->cbSize, VAD_WAVRT_POOLTAG);
if (m_pWfExt == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(m_pWfExt, pWfEx, sizeof(WAVEFORMATEX) + pWfEx->cbSize);
m_pbMuted = (PBOOL)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(BOOL), VAD_WAVRT_POOLTAG);
if (m_pbMuted == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
m_plVolumeLevel = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), VAD_WAVRT_POOLTAG);
if (m_plVolumeLevel == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
m_plPeakMeter = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), VAD_WAVRT_POOLTAG);
if (m_plPeakMeter == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
ntStatus = m_pMiniport->StreamCreated(m_ulPin, this);
if (NT_SUCCESS(ntStatus))
{
m_bUnregisterStream = TRUE;
}
return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CVadWaveStream::NonDelegatingQueryInterface
(
_In_ REFIID Interface,
_COM_Outptr_ PVOID * Object
)
{
PAGED_CODE();
ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(PMINIPORTWAVERTSTREAM(this)));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStream))
{
*Object = PVOID(PMINIPORTWAVERTSTREAM(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStreamNotification))
{
*Object = PVOID(PMINIPORTWAVERTSTREAMNOTIFICATION(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTOutputStream))
{
*Object = PVOID(PMINIPORTWAVERTOUTPUTSTREAM(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IDrmAudioStream))
{
*Object = (PVOID)(IDrmAudioStream*)this;
}
else
{
*Object = NULL;
}
if (*Object)
{
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::AllocateBufferWithNotification
(
_In_ ULONG NotificationCount_,
_In_ ULONG RequestedSize_,
_Out_ PMDL *AudioBufferMdl_,
_Out_ ULONG *ActualSize_,
_Out_ ULONG *OffsetFromFirstPage_,
_Out_ MEMORY_CACHING_TYPE *CacheType_
)
{
PAGED_CODE();
ULONG ulBufferDurationMs = 0;
if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign))
{
return STATUS_UNSUCCESSFUL;
}
if ((NotificationCount_ == 0) || (RequestedSize_ % NotificationCount_ != 0))
{
return STATUS_INVALID_PARAMETER;
}
if (0 == m_ulDmaMovementRate)
{
return STATUS_INVALID_PARAMETER;
}
RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign);
PHYSICAL_ADDRESS highAddress;
highAddress.HighPart = 0;
highAddress.LowPart = MAXULONG;
PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl(highAddress, RequestedSize_);
if (NULL == pBufferMdl)
{
return STATUS_UNSUCCESSFUL;
}
m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached);
m_ulNotificationsPerBuffer = NotificationCount_;
m_ulDmaBufferSize = RequestedSize_;
ulBufferDurationMs = (RequestedSize_ * 1000) / m_ulDmaMovementRate;
m_ulNotificationIntervalMs = ulBufferDurationMs / NotificationCount_;
*AudioBufferMdl_ = pBufferMdl;
*ActualSize_ = RequestedSize_;
*OffsetFromFirstPage_ = 0;
*CacheType_ = MmCached;
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg("PAGE")
VOID CVadWaveStream::FreeBufferWithNotification
(
_In_ PMDL Mdl_,
_In_ ULONG Size_
)
{
UNREFERENCED_PARAMETER(Size_);
PAGED_CODE();
if (Mdl_ != NULL)
{
if (m_pDmaBuffer != NULL)
{
m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_);
m_pDmaBuffer = NULL;
}
m_pPortStream->FreePagesFromMdl(Mdl_);
}
m_ulDmaBufferSize = 0;
m_ulNotificationsPerBuffer = 0;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::RegisterNotificationEvent
(
_In_ PKEVENT NotificationEvent_
)
{
UNREFERENCED_PARAMETER(NotificationEvent_);
PAGED_CODE();
NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePool2(
POOL_FLAG_NON_PAGED,
sizeof(NotificationListEntry),
VAD_WAVRT_POOLTAG);
if (NULL == nleNew)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
nleNew->NotificationEvent = NotificationEvent_;
if (!IsListEmpty(&m_NotificationList))
{
PLIST_ENTRY leCurrent = m_NotificationList.Flink;
while (leCurrent != &m_NotificationList)
{
NotificationListEntry* nleCurrent = CONTAINING_RECORD(leCurrent, NotificationListEntry, ListEntry);
if (nleCurrent->NotificationEvent == NotificationEvent_)
{
ExFreePoolWithTag(nleNew, VAD_WAVRT_POOLTAG);
return STATUS_UNSUCCESSFUL;
}
leCurrent = leCurrent->Flink;
}
}
InsertTailList(&m_NotificationList, &(nleNew->ListEntry));
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::UnregisterNotificationEvent
(
_In_ PKEVENT NotificationEvent_
)
{
UNREFERENCED_PARAMETER(NotificationEvent_);
PAGED_CODE();
if (!IsListEmpty(&m_NotificationList))
{
PLIST_ENTRY leCurrent = m_NotificationList.Flink;
while (leCurrent != &m_NotificationList)
{
NotificationListEntry* nleCurrent = CONTAINING_RECORD(leCurrent, NotificationListEntry, ListEntry);
if (nleCurrent->NotificationEvent == NotificationEvent_)
{
RemoveEntryList(leCurrent);
ExFreePoolWithTag(nleCurrent, VAD_WAVRT_POOLTAG);
return STATUS_SUCCESS;
}
leCurrent = leCurrent->Flink;
}
}
return STATUS_NOT_FOUND;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::GetClockRegister
(
_Out_ PKSRTAUDIO_HWREGISTER Register_
)
{
UNREFERENCED_PARAMETER(Register_);
PAGED_CODE();
return STATUS_NOT_IMPLEMENTED;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::GetPositionRegister
(
_Out_ PKSRTAUDIO_HWREGISTER Register_
)
{
UNREFERENCED_PARAMETER(Register_);
PAGED_CODE();
return STATUS_NOT_IMPLEMENTED;
}
//=============================================================================
#pragma code_seg("PAGE")
VOID CVadWaveStream::GetHWLatency
(
_Out_ PKSRTAUDIO_HWLATENCY Latency_
)
{
PAGED_CODE();
ASSERT(Latency_);
Latency_->ChipsetDelay = 0;
Latency_->CodecDelay = 0;
Latency_->FifoSize = 0;
}
//=============================================================================
#pragma code_seg("PAGE")
VOID CVadWaveStream::FreeAudioBuffer
(
_In_opt_ PMDL Mdl_,
_In_ ULONG Size_
)
{
UNREFERENCED_PARAMETER(Size_);
PAGED_CODE();
if (Mdl_ != NULL)
{
if (m_pDmaBuffer != NULL)
{
m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_);
m_pDmaBuffer = NULL;
}
m_pPortStream->FreePagesFromMdl(Mdl_);
}
m_ulDmaBufferSize = 0;
m_ulNotificationsPerBuffer = 0;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::AllocateAudioBuffer
(
_In_ ULONG RequestedSize_,
_Out_ PMDL *AudioBufferMdl_,
_Out_ ULONG *ActualSize_,
_Out_ ULONG *OffsetFromFirstPage_,
_Out_ MEMORY_CACHING_TYPE *CacheType_
)
{
PAGED_CODE();
if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign))
{
return STATUS_UNSUCCESSFUL;
}
RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign);
PHYSICAL_ADDRESS highAddress;
highAddress.HighPart = 0;
highAddress.LowPart = MAXULONG;
PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl(highAddress, RequestedSize_);
if (NULL == pBufferMdl)
{
return STATUS_UNSUCCESSFUL;
}
m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached);
m_ulDmaBufferSize = RequestedSize_;
m_ulNotificationsPerBuffer = 0;
*AudioBufferMdl_ = pBufferMdl;
*ActualSize_ = RequestedSize_;
*OffsetFromFirstPage_ = 0;
*CacheType_ = MmCached;
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg()
NTSTATUS CVadWaveStream::GetPosition
(
_Out_ KSAUDIO_POSITION *Position_
)
{
NTSTATUS ntStatus;
KIRQL oldIrql;
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
if (m_KsState == KSSTATE_RUN)
{
LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL);
UpdatePosition(ilQPC);
}
Position_->PlayOffset = m_ullPlayPosition;
Position_->WriteOffset = m_ullWritePosition;
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
ntStatus = STATUS_SUCCESS;
return ntStatus;
}
//=============================================================================
#pragma code_seg()
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS CVadWaveStream::SetWritePacket
(
_In_ ULONG PacketNumber,
_In_ DWORD Flags,
_In_ ULONG EosPacketLength
)
{
UNREFERENCED_PARAMETER(EosPacketLength);
NTSTATUS ntStatus;
if (m_ulNotificationsPerBuffer == 0)
{
return STATUS_NOT_SUPPORTED;
}
ULONG oldLastOsWritePacket = m_ulLastOsWritePacket;
if (m_bEoSReceived)
{
return STATUS_INVALID_DEVICE_STATE;
}
KIRQL oldIrql;
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
LONGLONG currentPacket = m_llPacketCounter;
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
ULONG expectedPacket = LODWORD(currentPacket);
if (m_KsState == KSSTATE_RUN)
{
expectedPacket++;
}
LONG deltaFromExpectedPacket = PacketNumber - expectedPacket;
if (deltaFromExpectedPacket < 0)
{
return STATUS_DATA_LATE_ERROR;
}
else if (deltaFromExpectedPacket > 0)
{
return STATUS_DATA_OVERRUN;
}
ULONG packetSize = (m_ulDmaBufferSize / m_ulNotificationsPerBuffer);
ULONG packetIndex = PacketNumber % m_ulNotificationsPerBuffer;
ULONG ulCurrentWritePosition = packetIndex * packetSize;
if (Flags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM)
{
return STATUS_INVALID_PARAMETER;
}
else
{
m_ulLastOsWritePacket = PacketNumber;
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
ntStatus = SetCurrentWritePositionInternal(ulCurrentWritePosition);
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
}
if (!NT_SUCCESS(ntStatus))
{
m_ulLastOsWritePacket = oldLastOsWritePacket;
}
return ntStatus;
}
//=============================================================================
#pragma code_seg()
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS CVadWaveStream::GetOutputStreamPresentationPosition
(
_Out_ KSAUDIO_PRESENTATION_POSITION *pPresentationPosition
)
{
ASSERT(pPresentationPosition);
if (m_ulNotificationsPerBuffer == 0)
{
return STATUS_NOT_SUPPORTED;
}
return GetPresentationPosition(pPresentationPosition);
}
//=============================================================================
#pragma code_seg()
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS CVadWaveStream::GetPacketCount
(
_Out_ ULONG *pPacketCount
)
{
ASSERT(pPacketCount);
if (m_ulNotificationsPerBuffer == 0)
{
return STATUS_NOT_SUPPORTED;
}
KIRQL oldIrql;
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
if (m_KsState == KSSTATE_RUN)
{
LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL);
UpdatePosition(ilQPC);
}
*pPacketCount = LODWORD(m_llPacketCounter);
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg()
NTSTATUS CVadWaveStream::GetPositions(
_Out_opt_ ULONGLONG* _pullLinearBufferPosition,
_Out_opt_ ULONGLONG* _pullPresentationPosition,
_Out_opt_ LARGE_INTEGER* _pliQPCTime
)
{
DPF_ENTER(("[CVadWaveStream::GetPositions]"));
NTSTATUS ntStatus;
LARGE_INTEGER ilQPC;
KIRQL oldIrql;
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
ilQPC = KeQueryPerformanceCounter(NULL);
if (m_KsState == KSSTATE_RUN)
{
UpdatePosition(ilQPC);
}
if (_pullLinearBufferPosition)
{
*_pullLinearBufferPosition = m_ullLinearPosition;
}
if (_pullPresentationPosition)
{
*_pullPresentationPosition = m_ullPresentationPosition;
}
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
if (_pliQPCTime)
{
*_pliQPCTime = ilQPC;
}
ntStatus = STATUS_SUCCESS;
return ntStatus;
}
//=============================================================================
#pragma code_seg()
NTSTATUS CVadWaveStream::GetPresentationPosition(
_Out_ KSAUDIO_PRESENTATION_POSITION* _pPresentationPosition
)
{
ASSERT(_pPresentationPosition);
LARGE_INTEGER timeStamp;
DPF_ENTER(("[CVadWaveStream::GetPresentationPosition]"));
ULONGLONG ullLinearPosition = { 0 };
ULONGLONG ullPresentationPosition = { 0 };
NTSTATUS status = STATUS_SUCCESS;
status = GetPositions(&ullLinearPosition, &ullPresentationPosition, &timeStamp);
if (!NT_SUCCESS(status))
{
return status;
}
if (0 == m_pWfExt->Format.nAvgBytesPerSec)
{
return STATUS_INVALID_DEVICE_STATE;
}
_pPresentationPosition->u64PositionInBlocks = ullPresentationPosition * m_pWfExt->Format.nSamplesPerSec / m_pWfExt->Format.nAvgBytesPerSec;
_pPresentationPosition->u64QPCPosition = (UINT64)timeStamp.QuadPart;
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg()
NTSTATUS CVadWaveStream::SetCurrentWritePositionInternal(
_In_ ULONG _ulCurrentWritePosition
)
{
DPF_ENTER(("[CVadWaveStream::SetCurrentWritePositionInternal]"));
ASSERT(m_bEoSReceived == FALSE);
if (m_bEoSReceived)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
if (_ulCurrentWritePosition > m_ulDmaBufferSize)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj();
pAdapterComm->WriteEtwEvent(eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION,
m_ullLinearPosition,
m_ulCurrentWritePosition,
_ulCurrentWritePosition,
0);
if (m_ulNotificationIntervalMs > 0)
{
if (m_ulCurrentWritePosition == _ulCurrentWritePosition)
{
pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT,
m_ullLinearPosition,
m_ulCurrentWritePosition,
3,
_ulCurrentWritePosition);
}
}
m_ulCurrentWritePosition = _ulCurrentWritePosition;
InterlockedExchange(&m_IsCurrentWritePositionUpdated, 1);
return STATUS_SUCCESS;
}
//=============================================================================
#pragma code_seg()
NTSTATUS CVadWaveStream::SetState
(
_In_ KSSTATE State_
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
KIRQL oldIrql;
switch (State_)
{
case KSSTATE_STOP:
if (m_KsState == KSSTATE_ACQUIRE)
{
// Acquire stream resources
}
KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
m_llPacketCounter = 0;
m_ullPlayPosition = 0;
m_ullWritePosition = 0;
m_ullLinearPosition = 0;
m_ullPresentationPosition = 0;
m_ulLastOsReadPacket = ULONG_MAX;
m_ulCurrentWritePosition = 0;
m_ulLastOsWritePacket = ULONG_MAX;
m_bEoSReceived = FALSE;
m_bLastBufferRendered = FALSE;
KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
break;
case KSSTATE_ACQUIRE:
if (m_KsState == KSSTATE_STOP)
{
// Acquire stream resources
}
break;
case KSSTATE_PAUSE:
if (m_KsState > KSSTATE_PAUSE)
{
// Run -> Pause
if (m_ulNotificationIntervalMs > 0)
{
ExCancelTimer(m_pNotificationTimer, NULL);
KeFlushQueuedDpcs();
if (m_ullLastDPCTimeStamp > 0)
{
LARGE_INTEGER qpc;
LARGE_INTEGER qpcFrequency;
LONGLONG hnsCurrentTime;
qpc = KeQueryPerformanceCounter(&qpcFrequency);
hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, qpc);
m_hnsDPCTimeCarryForward = hnsCurrentTime - m_ullLastDPCTimeStamp + m_hnsDPCTimeCarryForward;
}
}
}
GetPositions(NULL, NULL, NULL);
break;
case KSSTATE_RUN:
{
LARGE_INTEGER ullPerfCounterTemp;
ullPerfCounterTemp = KeQueryPerformanceCounter(&m_ullPerformanceCounterFrequency);
m_ullLastDPCTimeStamp = m_ullDmaTimeStamp = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ullPerfCounterTemp);
if (m_ulNotificationIntervalMs > 0)
{
ExSetTimer
(
m_pNotificationTimer,
(-1) * HNSTIME_PER_MILLISECOND,
HNSTIME_PER_MILLISECOND,
NULL
);
}
break;
}
}
m_KsState = State_;
return ntStatus;
}
//=============================================================================
#pragma code_seg("PAGE")
NTSTATUS CVadWaveStream::SetFormat
(
_In_ KSDATAFORMAT *DataFormat_
)
{
UNREFERENCED_PARAMETER(DataFormat_);
PAGED_CODE();
return STATUS_NOT_SUPPORTED;
}
//=============================================================================
#pragma code_seg()
VOID CVadWaveStream::UpdatePosition
(
_In_ LARGE_INTEGER ilQPC
)
{
// Convert ticks to 100ns units
LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ilQPC);
ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward) / 10000;
m_hnsElapsedTimeCarryForward = (hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward) % 10000;
ULONG ByteDisplacement = ((m_ulDmaMovementRate * TimeElapsedInMS) + m_byteDisplacementCarryForward) / 1000;
m_byteDisplacementCarryForward = ((m_ulDmaMovementRate * TimeElapsedInMS) + m_byteDisplacementCarryForward) % 1000;
// Increment presentation position even after last buffer is rendered
m_ullPresentationPosition += ByteDisplacement;
// Render path: handle EoS boundary
if (m_bEoSReceived)
{
if (m_ullWritePosition <= m_ulCurrentWritePosition)
{
ByteDisplacement = min(ByteDisplacement, m_ulCurrentWritePosition - (ULONG)m_ullWritePosition);
}
else if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize < m_ullWritePosition)
{
if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize > m_ulCurrentWritePosition)
{
ByteDisplacement = ByteDisplacement - (((ULONG)m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize - m_ulCurrentWritePosition);
}
}
}
if (m_bEoSReceived && !m_bLastBufferRendered
&& (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize == m_ulCurrentWritePosition)
{
m_bLastBufferRendered = TRUE;
}
// Audio data is silently discarded — no read/write of the DMA buffer
// Advance the DMA position and wrap at buffer length
m_ullPlayPosition = m_ullWritePosition =
(m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize;
m_ullLinearPosition += ByteDisplacement;
m_ullDmaTimeStamp = hnsCurrentTime;
}
//=============================================================================
#pragma code_seg("PAGE")
STDMETHODIMP_(NTSTATUS)
CVadWaveStream::SetContentId
(
_In_ ULONG contentId,
_In_ PCDRMRIGHTS drmRights
)
{
PAGED_CODE();
UNREFERENCED_PARAMETER(drmRights);
DPF_ENTER(("[CVadWaveStream::SetContentId]"));
NTSTATUS ntStatus;
ULONG ulOldContentId = contentId;
m_ulContentId = contentId;
ntStatus = m_pMiniport->UpdateDrmRights();
if (!NT_SUCCESS(ntStatus))
{
m_ulContentId = ulOldContentId;
}
return ntStatus;
}
//=============================================================================
#pragma code_seg()
void
VadTimerNotifyRT
(
_In_ PEX_TIMER Timer,
_In_opt_ PVOID DeferredContext
)
{
LARGE_INTEGER qpc;
LARGE_INTEGER qpcFrequency;
BOOL bufferCompleted = FALSE;
UNREFERENCED_PARAMETER(Timer);
_IRQL_limited_to_(DISPATCH_LEVEL);
CVadWaveStream* _this = (CVadWaveStream*)DeferredContext;
if (NULL == _this)
{
return;
}
KIRQL oldIrql;
KeAcquireSpinLock(&_this->m_PositionSpinLock, &oldIrql);
qpc = KeQueryPerformanceCounter(&qpcFrequency);
LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(_this->m_ullPerformanceCounterFrequency.QuadPart, qpc);
ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward) / 10000;
if (TimeElapsedInMS >= _this->m_ulNotificationIntervalMs)
{
_this->m_hnsDPCTimeCarryForward = hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward - (_this->m_ulNotificationIntervalMs * 10000);
_this->m_ullLastDPCTimeStamp = hnsCurrentTime;
bufferCompleted = TRUE;
}
if (!bufferCompleted && !_this->m_bEoSReceived)
{
goto End;
}
_this->UpdatePosition(qpc);
if (!_this->m_bEoSReceived)
{
_this->m_llPacketCounter++;
}
if (_this->m_KsState != KSSTATE_RUN)
{
goto End;
}
PADAPTERCOMMON pAdapterComm = _this->m_pMiniport->GetAdapterCommObj();
if (!_this->IsCurrentWaveRTWritePositionUpdated() && !_this->m_bEoSReceived)
{
pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT,
_this->m_ullLinearPosition,
_this->GetCurrentWaveRTWritePosition(),
1,
0);
}
if (!IsListEmpty(&_this->m_NotificationList) &&
(bufferCompleted || _this->m_bLastBufferRendered))
{
PLIST_ENTRY leCurrent = _this->m_NotificationList.Flink;
while (leCurrent != &_this->m_NotificationList)
{
NotificationListEntry* nleCurrent = CONTAINING_RECORD(leCurrent, NotificationListEntry, ListEntry);
KeSetEvent(nleCurrent->NotificationEvent, 0, 0);
leCurrent = leCurrent->Flink;
}
}
if (_this->m_bLastBufferRendered)
{
ExCancelTimer(_this->m_pNotificationTimer, NULL);
}
End:
KeReleaseSpinLock(&_this->m_PositionSpinLock, oldIrql);
return;
}