1038 lines
23 KiB
C++
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
|
||
|
|