windows-nt/Source/XPSP1/NT/drivers/ddk/wdmaudio/msvad/pcmex/minwave.cpp

781 lines
17 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-2000 Microsoft Corporation All Rights Reserved
Module Name:
minwave.cpp
Abstract:
Implementation of wavecyclic miniport.
--*/
#include <msvad.h>
#include <common.h>
#include "pcmex.h"
#include "minwave.h"
#include "wavtable.h"
#pragma code_seg("PAGE")
//=============================================================================
// CMiniportWaveCyclic
//=============================================================================
//=============================================================================
NTSTATUS
CreateMiniportWaveCyclicMSVAD
(
OUT PUNKNOWN * Unknown,
IN REFCLSID,
IN PUNKNOWN UnknownOuter OPTIONAL,
IN POOL_TYPE PoolType
)
/*++
Routine Description:
Create the wavecyclic miniport.
Arguments:
Unknown -
RefClsId -
UnknownOuter -
PoolType -
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(Unknown);
STD_CREATE_BODY(CMiniportWaveCyclic, Unknown, UnknownOuter, PoolType);
}
//=============================================================================
CMiniportWaveCyclic::~CMiniportWaveCyclic
(
void
)
/*++
Routine Description:
Destructor for wavecyclic miniport
Arguments:
Return Value:
NT status code.
--*/
{
PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclic::~CMiniportWaveCyclic]"));
} // ~CMiniportWaveCyclic
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclic::DataRangeIntersection
(
IN ULONG PinId,
IN PKSDATARANGE ClientDataRange,
IN PKSDATARANGE MyDataRange,
IN ULONG OutputBufferLength,
OUT PVOID ResultantFormat,
OUT PULONG ResultantFormatLength
)
/*++
Routine Description:
The DataRangeIntersection function determines the highest quality
intersection of two data ranges.
Arguments:
PinId - Pin for which data intersection is being determined.
ClientDataRange - Pointer to KSDATARANGE structure which contains the data
range submitted by client in the data range intersection
property request.
MyDataRange - Pin's data range to be compared with client's data
range. In this case we actually ignore our own data
range, because we know that we only support one range.
OutputBufferLength - Size of the buffer pointed to by the resultant format
parameter.
ResultantFormat - Pointer to value where the resultant format should be
returned.
ResultantFormatLength - Actual length of the resultant format placed in
ResultantFormat. This should be less than or equal
to OutputBufferLength.
Return Value:
NT status code.
--*/
{
PAGED_CODE();
// This code is the same as AC97 sample intersection handler.
//
// Check the size of output buffer. Note that we are returning
// WAVEFORMATPCMEX.
//
if (!OutputBufferLength)
{
*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
return STATUS_BUFFER_OVERFLOW;
}
if (OutputBufferLength < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)))
{
return STATUS_BUFFER_TOO_SMALL;
}
// Fill in the structure the datarange structure.
//
RtlCopyMemory(ResultantFormat, MyDataRange, sizeof(KSDATAFORMAT));
// Modify the size of the data format structure to fit the WAVEFORMATPCMEX
// structure.
//
((PKSDATAFORMAT)ResultantFormat)->FormatSize =
sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
// Append the WAVEFORMATPCMEX structure
//
PWAVEFORMATPCMEX pWfxExt =
(PWAVEFORMATPCMEX)((PKSDATAFORMAT)ResultantFormat + 1);
pWfxExt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pWfxExt->Format.nChannels =
(WORD)((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumChannels;
pWfxExt->Format.nSamplesPerSec =
((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumSampleFrequency;
pWfxExt->Format.wBitsPerSample = (WORD)
((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumBitsPerSample;
pWfxExt->Format.nBlockAlign =
(pWfxExt->Format.wBitsPerSample * pWfxExt->Format.nChannels) / 8;
pWfxExt->Format.nAvgBytesPerSec =
pWfxExt->Format.nSamplesPerSec * pWfxExt->Format.nBlockAlign;
pWfxExt->Format.cbSize = 22;
pWfxExt->Samples.wValidBitsPerSample = pWfxExt->Format.wBitsPerSample;
pWfxExt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// This should be set to wave port's channel config
// MSVAD ds3dhw implements this properly.
//
pWfxExt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
// Now overwrite also the sample size in the ksdataformat structure.
((PKSDATAFORMAT)ResultantFormat)->SampleSize = pWfxExt->Format.nBlockAlign;
// That we will return.
//
*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
return STATUS_SUCCESS;
} // DataRangeIntersection
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclic::GetDescription
(
OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
)
/*++
Routine Description:
The GetDescription function gets a pointer to a filter description.
It provides a location to deposit a pointer in miniport's description
structure. This is the placeholder for the FromNode or ToNode fields in
connections which describe connections to the filter's pins.
Arguments:
OutFilterDescriptor - Pointer to the filter description.
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(OutFilterDescriptor);
return
CMiniportWaveCyclicMSVAD::GetDescription(OutFilterDescriptor);
} // GetDescription
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclic::Init
(
IN PUNKNOWN UnknownAdapter_,
IN PRESOURCELIST ResourceList_,
IN PPORTWAVECYCLIC Port_
)
/*++
Routine Description:
The Init function initializes the miniport. Callers of this function
should run at IRQL PASSIVE_LEVEL
Arguments:
UnknownAdapter - A pointer to the Iuknown interface of the adapter object.
ResourceList - Pointer to the resource list to be supplied to the miniport
during initialization. The port driver is free to examine the
contents of the ResourceList. The port driver will not be
modify the ResourceList contents.
Port - Pointer to the topology port object that is linked with this miniport.
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(UnknownAdapter_);
ASSERT(Port_);
NTSTATUS ntStatus;
DPF_ENTER(("[CMiniportWaveCyclic::Init]"));
m_MaxOutputStreams = MAX_OUTPUT_STREAMS;
m_MaxInputStreams = MAX_INPUT_STREAMS;
m_MaxTotalStreams = MAX_TOTAL_STREAMS;
m_MinChannels = MIN_CHANNELS;
m_MaxChannelsPcm = MAX_CHANNELS_PCM;
m_MinBitsPerSamplePcm = MIN_BITS_PER_SAMPLE_PCM;
m_MaxBitsPerSamplePcm = MAX_BITS_PER_SAMPLE_PCM;
m_MinSampleRatePcm = MIN_SAMPLE_RATE;
m_MaxSampleRatePcm = MAX_SAMPLE_RATE;
ntStatus =
CMiniportWaveCyclicMSVAD::Init
(
UnknownAdapter_,
ResourceList_,
Port_
);
if (NT_SUCCESS(ntStatus))
{
// Set filter descriptor.
m_FilterDescriptor = &MiniportFilterDescriptor;
m_fCaptureAllocated = FALSE;
m_fRenderAllocated = FALSE;
}
return ntStatus;
} // Init
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclic::NewStream
(
OUT PMINIPORTWAVECYCLICSTREAM * OutStream,
IN PUNKNOWN OuterUnknown,
IN POOL_TYPE PoolType,
IN ULONG Pin,
IN BOOLEAN Capture,
IN PKSDATAFORMAT DataFormat,
OUT PDMACHANNEL * OutDmaChannel,
OUT PSERVICEGROUP * OutServiceGroup
)
/*++
Routine Description:
The NewStream function creates a new instance of a logical stream
associated with a specified physical channel. Callers of NewStream should
run at IRQL PASSIVE_LEVEL.
Arguments:
OutStream -
OuterUnknown -
PoolType -
Pin -
Capture -
DataFormat -
OutDmaChannel -
OutServiceGroup -
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(OutStream);
ASSERT(DataFormat);
ASSERT(OutDmaChannel);
ASSERT(OutServiceGroup);
DPF_ENTER(("[CMiniportWaveCyclic::NewStream]"));
NTSTATUS ntStatus = STATUS_SUCCESS;
PCMiniportWaveCyclicStream stream = NULL;
// Check if we have enough streams.
if (Capture)
{
if (m_fCaptureAllocated)
{
DPF(D_TERSE, ("[Only one capture stream supported]"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
if (m_fRenderAllocated)
{
DPF(D_TERSE, ("[Only one render stream supported]"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
// Determine if the format is valid.
//
if (NT_SUCCESS(ntStatus))
{
ntStatus = ValidateFormat(DataFormat);
}
// Instantiate a stream. Stream must be in
// NonPagedPool because of file saving.
//
if (NT_SUCCESS(ntStatus))
{
stream = new (NonPagedPool, MSVAD_POOLTAG)
CMiniportWaveCyclicStream(OuterUnknown);
if (stream)
{
stream->AddRef();
ntStatus =
stream->Init
(
this,
Pin,
Capture,
DataFormat
);
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (NT_SUCCESS(ntStatus))
{
if (Capture)
{
m_fCaptureAllocated = TRUE;
}
else
{
m_fRenderAllocated = TRUE;
}
*OutStream = PMINIPORTWAVECYCLICSTREAM(stream);
(*OutStream)->AddRef();
*OutDmaChannel = PDMACHANNEL(stream);
(*OutDmaChannel)->AddRef();
*OutServiceGroup = m_ServiceGroup;
(*OutServiceGroup)->AddRef();
// The stream, the DMA channel, and the service group have
// references now for the caller. The caller expects these
// references to be there.
}
// This is our private reference to the stream. The caller has
// its own, so we can release in any case.
//
if (stream)
{
stream->Release();
}
return ntStatus;
} // NewStream
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclic::NonDelegatingQueryInterface
(
IN REFIID Interface,
OUT PVOID * Object
)
/*++
Routine Description:
QueryInterface
Arguments:
Interface - GUID
Object - interface pointer to be returned.
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLIC(this)));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniport))
{
*Object = PVOID(PMINIPORT(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveCyclic))
{
*Object = PVOID(PMINIPORTWAVECYCLIC(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
// We reference the interface for the caller.
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface
//=============================================================================
NTSTATUS
CMiniportWaveCyclic::ValidateFormat
(
IN PKSDATAFORMAT pDataFormat
)
/*++
Routine Description:
Validates that the given dataformat is valid. This is for supporting
WAVEFORMATEXTENSIBLE.
Arguments:
pDataFormat - The dataformat for validation.
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(pDataFormat);
DPF_ENTER(("[CMiniportWaveCyclicMSVAD::ValidateFormat]"));
NTSTATUS ntStatus;
PWAVEFORMATEX pwfx;
// Let the default Validator handle the request.
//
ntStatus = CMiniportWaveCyclicMSVAD::ValidateFormat(pDataFormat);
if (NT_SUCCESS(ntStatus))
{
return ntStatus;
}
// If the format is not known check for WAVEFORMATEXTENSIBLE.
//
pwfx = GetWaveFormatEx(pDataFormat);
if (pwfx)
{
if (IS_VALID_WAVEFORMATEX_GUID(&pDataFormat->SubFormat))
{
USHORT wfxID = EXTRACT_WAVEFORMATEX_ID(&pDataFormat->SubFormat);
switch (wfxID)
{
// This is for WAVE_FORMAT_EXTENSIBLE support.
//
case WAVE_FORMAT_EXTENSIBLE:
{
PWAVEFORMATEXTENSIBLE pwfxExt =
(PWAVEFORMATEXTENSIBLE) pwfx;
ntStatus = ValidateWfxExt(pwfxExt);
break;
}
default:
DPF(D_TERSE, ("Invalid format EXTRACT_WAVEFORMATEX_ID!"));
break;
}
}
else
{
DPF(D_TERSE, ("Invalid pDataFormat->SubFormat!") );
}
}
return ntStatus;
} // ValidateFormat
//=============================================================================
NTSTATUS
CMiniportWaveCyclic::ValidateWfxExt
(
IN PWAVEFORMATEXTENSIBLE pWfxExt
)
/*++
Routine Description:
Given a waveformatextensible, verifies that the format is in device
datarange.
Arguments:
pWfxExt - wave format extensible structure
Return Value:
NT status code.
--*/
{
PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclic::ValidateWfxExtPcm]"));
// First verify that the subformat is OK
//
if (pWfxExt)
{
if(IsEqualGUIDAligned(pWfxExt->SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
{
PWAVEFORMATEX pWfx = (PWAVEFORMATEX) pWfxExt;
if
(
pWfx->cbSize ==
sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
)
{
// Do any channel specific stuff here.
//
return CMiniportWaveCyclicMSVAD::ValidatePcm(pWfx);
}
}
}
DPF(D_TERSE, ("Invalid PCM format"));
return STATUS_INVALID_PARAMETER;
} // ValidateWfxExtPcm
//=============================================================================
// CMiniportWaveStreamCyclicSimple
//=============================================================================
//=============================================================================
CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream
(
void
)
/*++
Routine Description:
Destructor for wavecyclicstream
Arguments:
Return Value:
NT status code.
--*/
{
PAGED_CODE();
DPF_ENTER(("[CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream]"));
if (NULL != m_pMiniportLocal)
{
if (m_fCapture)
{
m_pMiniportLocal->m_fCaptureAllocated = FALSE;
}
else
{
m_pMiniportLocal->m_fRenderAllocated = FALSE;
}
}
} // ~CMiniportWaveCyclicStream
//=============================================================================
NTSTATUS
CMiniportWaveCyclicStream::Init
(
IN PCMiniportWaveCyclic 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_ -
Pin_ -
Capture_ -
DataFormat -
DmaChannel_ -
Return Value:
NT status code.
--*/
{
PAGED_CODE();
m_pMiniportLocal = Miniport_;
return
CMiniportWaveCyclicStreamMSVAD::Init
(
Miniport_,
Pin_,
Capture_,
DataFormat_
);
} // Init
//=============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportWaveCyclicStream::NonDelegatingQueryInterface
(
IN REFIID Interface,
OUT PVOID * Object
)
/*++
Routine Description:
QueryInterface
Arguments:
Interface - GUID
Object - interface pointer to be returned
Return Value:
NT status code.
--*/
{
PAGED_CODE();
ASSERT(Object);
if (IsEqualGUIDAligned(Interface, IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLICSTREAM(this)));
}
else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveCyclicStream))
{
*Object = PVOID(PMINIPORTWAVECYCLICSTREAM(this));
}
else if (IsEqualGUIDAligned(Interface, IID_IDmaChannel))
{
*Object = PVOID(PDMACHANNEL(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
} // NonDelegatingQueryInterface
#pragma code_seg()