|
|
/*++
Copyright (c) 1997-2000 Microsoft Corporation All Rights Reserved
Module Name:
basewave.cpp
Abstract:
Implementation of wavecyclic miniport.
--*/
#include <msvad.h>
#include "common.h"
#include "basewave.h"
//=============================================================================
// CMiniportWaveCyclicMSVAD
//=============================================================================
//=============================================================================
#pragma code_seg("PAGE")
CMiniportWaveCyclicMSVAD::CMiniportWaveCyclicMSVAD ( void ) /*++
Routine Description:
Constructor for wavecyclic miniport.
Arguments:
Return Value:
--*/ { PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::CMiniportWaveCyclicMSVAD]"));
// Initialize members.
//
m_AdapterCommon = NULL; m_Port = NULL; m_FilterDescriptor = NULL;
m_NotificationInterval = 0; m_SamplingFrequency = 0;
m_ServiceGroup = NULL; m_MaxDmaBufferSize = DMA_BUFFER_SIZE;
m_MaxOutputStreams = 0; m_MaxInputStreams = 0; m_MaxTotalStreams = 0;
m_MinChannels = 0; m_MaxChannelsPcm = 0; m_MinBitsPerSamplePcm = 0; m_MaxBitsPerSamplePcm = 0; m_MinSampleRatePcm = 0; m_MaxSampleRatePcm = 0; } // CMiniportWaveCyclicMSVAD
//=============================================================================
CMiniportWaveCyclicMSVAD::~CMiniportWaveCyclicMSVAD ( void ) /*++
Routine Description:
Destructor for wavecyclic miniport
Arguments:
Return Value:
--*/ { PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::~CMiniportWaveCyclicMSVAD]"));
if (m_Port) { m_Port->Release(); }
if (m_ServiceGroup) { m_ServiceGroup->Release(); }
if (m_AdapterCommon) { m_AdapterCommon->Release(); } } // ~CMiniportWaveCyclicMSVAD
//=============================================================================
STDMETHODIMP CMiniportWaveCyclicMSVAD::GetDescription ( OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor ) /*++
Routine Description:
The GetDescription function gets a pointer to a filter description. The descriptor is defined in wavtable.h for each MSVAD sample.
Arguments:
OutFilterDescriptor - Pointer to the filter description
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(OutFilterDescriptor);
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::GetDescription]"));
*OutFilterDescriptor = m_FilterDescriptor;
return (STATUS_SUCCESS); } // GetDescription
//=============================================================================
STDMETHODIMP CMiniportWaveCyclicMSVAD::Init ( IN PUNKNOWN UnknownAdapter_, IN PRESOURCELIST ResourceList_, IN PPORTWAVECYCLIC Port_ ) /*++
Routine Description:
Arguments:
UnknownAdapter_ - pointer to adapter common.
ResourceList_ - resource list. MSVAD does not use resources.
Port_ - pointer to the port
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(UnknownAdapter_); ASSERT(Port_);
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::Init]"));
// AddRef() is required because we are keeping this pointer.
//
m_Port = Port_; m_Port->AddRef();
// We want the IAdapterCommon interface on the adapter common object,
// which is given to us as a IUnknown. The QueryInterface call gives us
// an AddRefed pointer to the interface we want.
//
NTSTATUS ntStatus = UnknownAdapter_->QueryInterface ( IID_IAdapterCommon, (PVOID *) &m_AdapterCommon );
if (NT_SUCCESS(ntStatus)) { KeInitializeMutex(&m_SampleRateSync, 1); ntStatus = PcNewServiceGroup(&m_ServiceGroup, NULL);
if (NT_SUCCESS(ntStatus)) { m_AdapterCommon->SetWaveServiceGroup(m_ServiceGroup); } }
if (!NT_SUCCESS(ntStatus)) { // clean up AdapterCommon
//
if (m_AdapterCommon) { // clean up the service group
//
if (m_ServiceGroup) { m_AdapterCommon->SetWaveServiceGroup(NULL); m_ServiceGroup->Release(); m_ServiceGroup = NULL; }
m_AdapterCommon->Release(); m_AdapterCommon = NULL; }
// release the port
//
m_Port->Release(); m_Port = NULL; }
return ntStatus; } // Init
//=============================================================================
NTSTATUS CMiniportWaveCyclicMSVAD::PropertyHandlerCpuResources ( IN PPCPROPERTY_REQUEST PropertyRequest ) /*++
Routine Description:
Processes KSPROPERTY_AUDIO_CPURESOURCES
Arguments:
PropertyRequest - property request structure
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(PropertyRequest);
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::PropertyHandlerCpuResources]"));
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) { ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(LONG), 0); if (NT_SUCCESS(ntStatus)) { *(PLONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU; PropertyRequest->ValueSize = sizeof(LONG); ntStatus = STATUS_SUCCESS; } } else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { ntStatus = PropertyHandler_BasicSupport ( PropertyRequest, KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, VT_I4 ); }
return ntStatus; } // PropertyHandlerCpuResources
//=============================================================================
NTSTATUS CMiniportWaveCyclicMSVAD::PropertyHandlerGeneric ( IN PPCPROPERTY_REQUEST PropertyRequest ) /*++
Routine Description:
Handles all properties for this miniport.
Arguments:
PropertyRequest - property request structure
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(PropertyRequest); ASSERT(PropertyRequest->PropertyItem);
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
switch (PropertyRequest->PropertyItem->Id) { case KSPROPERTY_AUDIO_CPU_RESOURCES: ntStatus = PropertyHandlerCpuResources(PropertyRequest); break;
default: DPF(D_TERSE, ("[PropertyHandlerGeneric: Invalid Device Request]")); ntStatus = STATUS_INVALID_DEVICE_REQUEST; }
return ntStatus; } // PropertyHandlerGeneric
//=============================================================================
NTSTATUS CMiniportWaveCyclicMSVAD::ValidateFormat ( IN PKSDATAFORMAT pDataFormat ) /*++
Routine Description:
Validates that the given dataformat is valid. This version of the driver only supports PCM.
Arguments:
pDataFormat - The dataformat for validation.
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(pDataFormat);
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::ValidateFormat]"));
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; PWAVEFORMATEX pwfx;
pwfx = GetWaveFormatEx(pDataFormat); if (pwfx) { if (IS_VALID_WAVEFORMATEX_GUID(&pDataFormat->SubFormat)) { USHORT wfxID = EXTRACT_WAVEFORMATEX_ID(&pDataFormat->SubFormat);
switch (wfxID) { case WAVE_FORMAT_PCM: { switch (pwfx->wFormatTag) { case WAVE_FORMAT_PCM: { ntStatus = ValidatePcm(pwfx); break; } } break; }
default: DPF(D_TERSE, ("Invalid format EXTRACT_WAVEFORMATEX_ID!")); break; } } else { DPF(D_TERSE, ("Invalid pDataFormat->SubFormat!") ); } }
return ntStatus; } // ValidateFormat
//-----------------------------------------------------------------------------
NTSTATUS CMiniportWaveCyclicMSVAD::ValidatePcm ( IN PWAVEFORMATEX pWfx ) /*++
Routine Description:
Given a waveformatex and format size validates that the format is in device datarange.
Arguments:
pWfx - wave format structure.
Return Value:
NT status code.
--*/ { PAGED_CODE();
DPF_ENTER(("CMiniportWaveCyclicMSVAD::ValidatePcm"));
if ( pWfx && (pWfx->cbSize == 0) && (pWfx->nChannels >= m_MinChannels) && (pWfx->nChannels <= m_MaxChannelsPcm) && (pWfx->nSamplesPerSec >= m_MinSampleRatePcm) && (pWfx->nSamplesPerSec <= m_MaxSampleRatePcm) && (pWfx->wBitsPerSample >= m_MinBitsPerSamplePcm) && (pWfx->wBitsPerSample <= m_MaxBitsPerSamplePcm) ) { return STATUS_SUCCESS; }
DPF(D_TERSE, ("Invalid PCM format"));
return STATUS_INVALID_PARAMETER; } // ValidatePcm
//=============================================================================
// CMiniportWaveCyclicStreamMSVAD
//=============================================================================
CMiniportWaveCyclicStreamMSVAD::CMiniportWaveCyclicStreamMSVAD ( void ) { m_pMiniport = NULL; m_fCapture = FALSE; m_fFormat16Bit = FALSE; m_fFormatStereo = FALSE; m_ksState = KSSTATE_STOP; m_ulPin = (ULONG)-1;
m_pDpc = NULL; m_pTimer = NULL;
m_fDmaActive = FALSE; m_ulDmaPosition = 0; m_pvDmaBuffer = NULL; m_ulDmaBufferSize = 0; m_ulDmaMovementRate = 0; m_ullDmaTimeStamp = 0; }
//=============================================================================
CMiniportWaveCyclicStreamMSVAD::~CMiniportWaveCyclicStreamMSVAD ( void ) /*++
Routine Description:
Destructor for wavecyclic stream
Arguments:
void
Return Value:
--*/ { PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicStreamMS::~CMiniportWaveCyclicStreamMS]"));
if (m_pTimer) { KeCancelTimer(m_pTimer); ExFreePool(m_pTimer); }
if (m_pDpc) { ExFreePool( m_pDpc ); }
// Free the DMA buffer
//
FreeBuffer(); } // ~CMiniportWaveCyclicStreamMSVAD
//=============================================================================
NTSTATUS CMiniportWaveCyclicStreamMSVAD::Init ( IN PCMiniportWaveCyclicMSVAD Miniport_, IN ULONG Pin_, IN BOOLEAN Capture_, IN PKSDATAFORMAT DataFormat_ ) /*++
Routine Description:
Initializes the stream object. Allocate a DMA buffer, timer and DPC
Arguments:
Miniport_ - miniport object
Pin_ - pin id
Capture_ - TRUE if this is a capture stream
DataFormat_ - new dataformat
Return Value:
NT status code.
--*/ { PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicStreamMSVAD::Init]"));
ASSERT(Miniport_); ASSERT(DataFormat_);
NTSTATUS ntStatus = STATUS_SUCCESS; PWAVEFORMATEX pWfx;
pWfx = GetWaveFormatEx(DataFormat_); if (!pWfx) { DPF(D_TERSE, ("Invalid DataFormat param in NewStream")); ntStatus = STATUS_INVALID_PARAMETER; }
if (NT_SUCCESS(ntStatus)) { m_pMiniport = Miniport_;
m_ulPin = Pin_; m_fCapture = Capture_; m_fFormatStereo = (pWfx->nChannels == 2); m_fFormat16Bit = (pWfx->wBitsPerSample == 16); m_ksState = KSSTATE_STOP; m_ulDmaPosition = 0; m_fDmaActive = FALSE; m_pDpc = NULL; m_pTimer = NULL; m_pvDmaBuffer = NULL;
// If this is not the capture stream, create the output file.
//
if (!m_fCapture) { DPF(D_TERSE, ("SaveData %X", &m_SaveData)); ntStatus = m_SaveData.SetDataFormat(DataFormat_); if (NT_SUCCESS(ntStatus)) { ntStatus = m_SaveData.Initialize(); }
} }
// Allocate DMA buffer for this stream.
//
if (NT_SUCCESS(ntStatus)) { ntStatus = AllocateBuffer(m_pMiniport->m_MaxDmaBufferSize, NULL); }
// Set sample frequency. Note that m_SampleRateSync access should
// be syncronized.
//
if (NT_SUCCESS(ntStatus)) { ntStatus = KeWaitForSingleObject ( &m_pMiniport->m_SampleRateSync, Executive, KernelMode, FALSE, NULL ); if (NT_SUCCESS(ntStatus)) { m_pMiniport->m_SamplingFrequency = pWfx->nSamplesPerSec; KeReleaseMutex(&m_pMiniport->m_SampleRateSync, FALSE); } else { DPF(D_TERSE, ("[SamplingFrequency Sync failed: %08X]", ntStatus)); } }
if (NT_SUCCESS(ntStatus)) { ntStatus = SetFormat(DataFormat_); }
if (NT_SUCCESS(ntStatus)) { m_pDpc = (PRKDPC) ExAllocatePoolWithTag ( NonPagedPool, sizeof(KDPC), MSVAD_POOLTAG ); if (!m_pDpc) { DPF(D_TERSE, ("[Could not allocate memory for DPC]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(ntStatus)) { m_pTimer = (PKTIMER) ExAllocatePoolWithTag ( NonPagedPool, sizeof(KTIMER), MSVAD_POOLTAG ); if (!m_pTimer) { DPF(D_TERSE, ("[Could not allocate memory for Timer]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(ntStatus)) { KeInitializeDpc(m_pDpc, TimerNotify, m_pMiniport); KeInitializeTimerEx(m_pTimer, NotificationTimer); }
return ntStatus; } // Init
#pragma code_seg()
//=============================================================================
// CMiniportWaveCyclicStreamMSVAD IMiniportWaveCyclicStream
//=============================================================================
//=============================================================================
STDMETHODIMP CMiniportWaveCyclicStreamMSVAD::GetPosition ( OUT PULONG Position ) /*++
Routine Description:
The GetPosition function gets the current position of the DMA read or write pointer for the stream. Callers of GetPosition should run at IRQL <= DISPATCH_LEVEL.
Arguments:
Position - Position of the DMA pointer
Return Value:
NT status code.
--*/ { if (m_fDmaActive) { ULONGLONG CurrentTime = KeQueryInterruptTime();
ULONG TimeElapsedInMS = ( (ULONG) (CurrentTime - m_ullDmaTimeStamp) ) / 10000;
ULONG ByteDisplacement = (m_ulDmaMovementRate * TimeElapsedInMS) / 1000;
m_ulDmaPosition = (m_ulDmaPosition + ByteDisplacement) % m_ulDmaBufferSize;
*Position = m_ulDmaPosition;
m_ullDmaTimeStamp = CurrentTime; } else { *Position = m_ulDmaPosition; }
return STATUS_SUCCESS; } // GetPosition
//=============================================================================
STDMETHODIMP CMiniportWaveCyclicStreamMSVAD::NormalizePhysicalPosition ( IN OUT PLONGLONG PhysicalPosition ) /*++
Routine Description:
Given a physical position based on the actual number of bytes transferred, NormalizePhysicalPosition converts the position to a time-based value of 100 nanosecond units. Callers of NormalizePhysicalPosition can run at any IRQL.
Arguments:
PhysicalPosition - On entry this variable contains the value to convert. On return it contains the converted value
Return Value:
NT status code.
--*/ { *PhysicalPosition = ( _100NS_UNITS_PER_SECOND / ( 1 << ( m_fFormatStereo + m_fFormat16Bit ) ) * *PhysicalPosition ) / m_pMiniport->m_SamplingFrequency;
return STATUS_SUCCESS; } // NormalizePhysicalPosition
#pragma code_seg("PAGE")
//=============================================================================
STDMETHODIMP_(NTSTATUS) CMiniportWaveCyclicStreamMSVAD::SetFormat ( IN PKSDATAFORMAT Format ) /*++
Routine Description:
The SetFormat function changes the format associated with a stream. Callers of SetFormat should run at IRQL PASSIVE_LEVEL
Arguments:
Format - Pointer to a KSDATAFORMAT structure which indicates the new format of the stream.
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(Format);
DPF_ENTER(("[CMiniportWaveCyclicStreamMSVAD::SetFormat]"));
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; PWAVEFORMATEX pWfx;
if (m_ksState != KSSTATE_RUN) { // MSVAD does not validate the format.
//
pWfx = GetWaveFormatEx(Format); if (pWfx) { ntStatus = KeWaitForSingleObject ( &m_pMiniport->m_SampleRateSync, Executive, KernelMode, FALSE, NULL ); if (NT_SUCCESS(ntStatus)) { if (!m_fCapture) { ntStatus = m_SaveData.SetDataFormat(Format); }
m_fFormatStereo = (pWfx->nChannels == 2); m_fFormat16Bit = (pWfx->wBitsPerSample == 16); m_pMiniport->m_SamplingFrequency = pWfx->nSamplesPerSec; m_ulDmaMovementRate = pWfx->nAvgBytesPerSec;
DPF(D_TERSE, ("New Format: %d", pWfx->nSamplesPerSec)); }
KeReleaseMutex(&m_pMiniport->m_SampleRateSync, FALSE); } }
return ntStatus; } // SetFormat
//=============================================================================
STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamMSVAD::SetNotificationFreq ( IN ULONG Interval, OUT PULONG FramingSize ) /*++
Routine Description:
The SetNotificationFrequency function sets the frequency at which notification interrupts are generated. Callers of SetNotificationFrequency should run at IRQL PASSIVE_LEVEL.
Arguments:
Interval - Value indicating the interval between interrupts, expressed in milliseconds
FramingSize - Pointer to a ULONG value where the number of bytes equivalent to Interval milliseconds is returned
Return Value:
NT status code.
--*/ { PAGED_CODE();
ASSERT(FramingSize);
DPF_ENTER(("[CMiniportWaveCyclicStreamMSVAD::SetNotificationFreq]"));
m_pMiniport->m_NotificationInterval = Interval;
*FramingSize = ( 1 << ( m_fFormatStereo + m_fFormat16Bit ) ) * m_pMiniport->m_SamplingFrequency * Interval / 1000;
return m_pMiniport->m_NotificationInterval; } // SetNotificationFreq
//=============================================================================
STDMETHODIMP CMiniportWaveCyclicStreamMSVAD::SetState ( IN KSSTATE NewState ) /*++
Routine Description:
The SetState function sets the new state of playback or recording for the stream. SetState should run at IRQL PASSIVE_LEVEL
Arguments:
NewState - KSSTATE indicating the new state for the stream.
Return Value:
NT status code.
--*/ { PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicStreamMSVAD::SetState]"));
NTSTATUS ntStatus = STATUS_SUCCESS;
// The acquire state is not distinguishable from the stop state for our
// purposes.
//
if (NewState == KSSTATE_ACQUIRE) { NewState = KSSTATE_STOP; }
if (m_ksState != NewState) { switch(NewState) { case KSSTATE_PAUSE: { DPF(D_TERSE, ("KSSTATE_PAUSE"));
m_fDmaActive = FALSE; } break;
case KSSTATE_RUN: { DPF(D_TERSE, ("KSSTATE_RUN"));
LARGE_INTEGER delay;
// Set the timer for DPC.
//
m_ullDmaTimeStamp = KeQueryInterruptTime(); m_fDmaActive = TRUE; delay.HighPart = 0; delay.LowPart = m_pMiniport->m_NotificationInterval;
KeSetTimerEx ( m_pTimer, delay, m_pMiniport->m_NotificationInterval, m_pDpc ); } break;
case KSSTATE_STOP:
DPF(D_TERSE, ("KSSTATE_STOP"));
m_fDmaActive = FALSE; m_ulDmaPosition = 0;
KeCancelTimer( m_pTimer );
// Wait until all work items are completed.
//
if (!m_fCapture) { m_SaveData.WaitAllWorkItems(); }
break; }
m_ksState = NewState; }
return ntStatus; } // SetState
#pragma code_seg()
//=============================================================================
STDMETHODIMP_(void) CMiniportWaveCyclicStreamMSVAD::Silence ( IN PVOID Buffer, IN ULONG ByteCount ) /*++
Routine Description:
The Silence function is used to copy silence samplings to a certain location. Callers of Silence can run at any IRQL
Arguments:
Buffer - Pointer to the buffer where the silence samplings should be deposited.
ByteCount - Size of buffer indicating number of bytes to be deposited.
Return Value:
NT status code.
--*/ { RtlFillMemory(Buffer, ByteCount, m_fFormat16Bit ? 0 : 0x80); } // Silence
//=============================================================================
void TimerNotify ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SA1, IN PVOID SA2 ) /*++
Routine Description:
Dpc routine. This simulates an interrupt service routine. The Dpc will be called whenever CMiniportWaveCyclicStreamMSVAD::m_pTimer triggers.
Arguments:
Dpc - the Dpc object
DeferredContext - Pointer to a caller-supplied context to be passed to the DeferredRoutine when it is called
SA1 - System argument 1
SA2 - System argument 2
Return Value:
NT status code.
--*/ { PCMiniportWaveCyclicMSVAD pMiniport = (PCMiniportWaveCyclicMSVAD) DeferredContext;
if (pMiniport && pMiniport->m_Port) { pMiniport->m_Port->Notify(pMiniport->m_ServiceGroup); } } // TimerNotify
|