windows-nt/Source/XPSP1/NT/drivers/ddk/wdmaudio/msvad/basewave.cpp
2020-09-26 16:20:57 +08:00

1038 lines
23 KiB
C++

/*++
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