1666 lines
59 KiB
C++
1666 lines
59 KiB
C++
/********************************************************************************
|
|
** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
**
|
|
** Portions Copyright (c) 1998-1999 Intel Corporation
|
|
**
|
|
********************************************************************************/
|
|
|
|
// Every debug output has "Modulname text"
|
|
static char STR_MODULENAME[] = "ICH Wave: ";
|
|
|
|
#include "minwave.h"
|
|
#include "ichwave.h"
|
|
|
|
/*****************************************************************************
|
|
* PinDataRangesPCMStream
|
|
*****************************************************************************
|
|
* The next 3 arrays contain information about the data ranges of the pin for
|
|
* wave capture, wave render and mic capture.
|
|
* These arrays are filled dynamically by BuildDataRangeInformation().
|
|
*/
|
|
|
|
static KSDATARANGE_AUDIO PinDataRangesPCMStreamRender[WAVE_SAMPLERATES_TESTED];
|
|
static KSDATARANGE_AUDIO PinDataRangesPCMStreamCapture[WAVE_SAMPLERATES_TESTED];
|
|
static KSDATARANGE_AUDIO PinDataRangesMicStream[MIC_SAMPLERATES_TESTED];
|
|
|
|
static KSDATARANGE PinDataRangesAnalogBridge[] =
|
|
{
|
|
{
|
|
sizeof(KSDATARANGE),
|
|
0,
|
|
0,
|
|
0,
|
|
STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
|
|
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
|
|
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* PinDataRangesPointersPCMStream
|
|
*****************************************************************************
|
|
* The next 3 arrays contain the pointers to the data range information of
|
|
* the pin for wave capture, wave render and mic capture.
|
|
* These arrays are filled dynamically by BuildDataRangeInformation().
|
|
*/
|
|
static PKSDATARANGE PinDataRangePointersPCMStreamRender[WAVE_SAMPLERATES_TESTED];
|
|
static PKSDATARANGE PinDataRangePointersPCMStreamCapture[WAVE_SAMPLERATES_TESTED];
|
|
static PKSDATARANGE PinDataRangePointersMicStream[MIC_SAMPLERATES_TESTED];
|
|
|
|
/*****************************************************************************
|
|
* PinDataRangePointerAnalogStream
|
|
*****************************************************************************
|
|
* This structure pointers to the data range structures for the wave pins.
|
|
*/
|
|
static PKSDATARANGE PinDataRangePointersAnalogBridge[] =
|
|
{
|
|
(PKSDATARANGE) PinDataRangesAnalogBridge
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
* Wave Miniport Topology
|
|
*========================
|
|
*
|
|
* +-----------+
|
|
* | |
|
|
* Capture (PIN_WAVEIN) <---|2 --ADC-- 3|<=== (PIN_WAVEIN_BRIDGE)
|
|
* | |
|
|
* Render (PIN_WAVEOUT) --->|0 --DAC-- 1|===> (PIN_WAVEOUT_BRIDGE)
|
|
* | |
|
|
* Mic (PIN_MICIN) <---|4 --ADC-- 5|<=== (PIN_MICIN_BRIDGE)
|
|
* +-----------+
|
|
*
|
|
* Note that the exposed pins (left side) have to be a multiple of 2
|
|
* since there are some dependencies in the stream object.
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* MiniportPins
|
|
*****************************************************************************
|
|
* This structure describes pin (stream) types provided by this miniport.
|
|
* The field that sets the number of data range entries (SIZEOF_ARRAY) is
|
|
* overwritten by BuildDataRangeInformation().
|
|
*/
|
|
static PCPIN_DESCRIPTOR MiniportPins[] =
|
|
{
|
|
// PIN_WAVEOUT
|
|
{
|
|
1,1,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersPCMStreamRender), // DataRangesCount
|
|
PinDataRangePointersPCMStreamRender, // DataRanges
|
|
KSPIN_DATAFLOW_IN, // DataFlow
|
|
KSPIN_COMMUNICATION_SINK, // Communication
|
|
(GUID *) &KSCATEGORY_AUDIO, // Category
|
|
NULL, // Name
|
|
0 // Reserved
|
|
}
|
|
},
|
|
|
|
// PIN_WAVEOUT_BRIDGE
|
|
{
|
|
0,0,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount
|
|
PinDataRangePointersAnalogBridge, // DataRanges
|
|
KSPIN_DATAFLOW_OUT, // DataFlow
|
|
KSPIN_COMMUNICATION_NONE, // Communication
|
|
(GUID *) &KSCATEGORY_AUDIO, // Category
|
|
NULL, // Name
|
|
0 // Reserved
|
|
}
|
|
},
|
|
|
|
// PIN_WAVEIN
|
|
{
|
|
1,1,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersPCMStreamCapture), // DataRangesCount
|
|
PinDataRangePointersPCMStreamCapture, // DataRanges
|
|
KSPIN_DATAFLOW_OUT, // DataFlow
|
|
KSPIN_COMMUNICATION_SINK, // Communication
|
|
(GUID *) &PINNAME_CAPTURE, // Category
|
|
&KSAUDFNAME_RECORDING_CONTROL, // Name
|
|
0 // Reserved
|
|
}
|
|
},
|
|
|
|
// PIN_WAVEIN_BRIDGE
|
|
{
|
|
0,0,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount
|
|
PinDataRangePointersAnalogBridge, // DataRanges
|
|
KSPIN_DATAFLOW_IN, // DataFlow
|
|
KSPIN_COMMUNICATION_NONE, // Communication
|
|
(GUID *) &KSCATEGORY_AUDIO, // Category
|
|
NULL, // Name
|
|
0 // Reserved
|
|
}
|
|
},
|
|
|
|
//
|
|
// The Microphone pins are not used if PINC_MICIN_PRESENT is not set.
|
|
// To remove them, Init() will reduce the "PinCount" in the
|
|
// MiniportFilterDescriptor.
|
|
//
|
|
// PIN_MICIN
|
|
{
|
|
1,1,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersMicStream),// DataRangesCount
|
|
PinDataRangePointersMicStream, // DataRanges
|
|
KSPIN_DATAFLOW_OUT, // DataFlow
|
|
KSPIN_COMMUNICATION_SINK, // Communication
|
|
(GUID *) &KSCATEGORY_AUDIO, // Category
|
|
NULL, // Name
|
|
0 // Reserved
|
|
}
|
|
},
|
|
|
|
// PIN_MICIN_BRIDGE
|
|
{
|
|
0,0,0, // InstanceCount
|
|
NULL, // AutomationTable
|
|
{ // KsPinDescriptor
|
|
0, // InterfacesCount
|
|
NULL, // Interfaces
|
|
0, // MediumsCount
|
|
NULL, // Mediums
|
|
SIZEOF_ARRAY(PinDataRangePointersAnalogBridge), // DataRangesCount
|
|
PinDataRangePointersAnalogBridge, // DataRanges
|
|
KSPIN_DATAFLOW_IN, // DataFlow
|
|
KSPIN_COMMUNICATION_NONE, // Communication
|
|
(GUID *) &KSCATEGORY_AUDIO, // Category
|
|
NULL, // Name
|
|
0 // Reserved
|
|
}
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* PropertiesDAC
|
|
*****************************************************************************
|
|
* Properties for the DAC node.
|
|
*/
|
|
static PCPROPERTY_ITEM PropertiesDAC[] =
|
|
{
|
|
{
|
|
&KSPROPSETID_Audio,
|
|
KSPROPERTY_AUDIO_CHANNEL_CONFIG,
|
|
KSPROPERTY_TYPE_SET,
|
|
CMiniportWaveICH::PropertyChannelConfig
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* AutomationVolume
|
|
*****************************************************************************
|
|
* Automation table for volume controls.
|
|
*/
|
|
DEFINE_PCAUTOMATION_TABLE_PROP (AutomationDAC, PropertiesDAC);
|
|
|
|
/*****************************************************************************
|
|
* TopologyNodes
|
|
*****************************************************************************
|
|
* List of nodes.
|
|
*/
|
|
static PCNODE_DESCRIPTOR MiniportNodes[] =
|
|
{
|
|
// NODE_WAVEOUT_DAC
|
|
{
|
|
0, // Flags
|
|
&AutomationDAC, // AutomationTable
|
|
&KSNODETYPE_DAC, // Type
|
|
NULL // Name
|
|
},
|
|
// NODE_WAVEIN_ADC
|
|
{
|
|
0, // Flags
|
|
NULL, // AutomationTable
|
|
&KSNODETYPE_ADC, // Type
|
|
NULL // Name
|
|
},
|
|
//
|
|
// The Microphone node is not used if PINC_MICIN_PRESENT is not set.
|
|
// To remove them, Init() will reduce the "NodeCount" in the
|
|
// MiniportFilterDescriptor.
|
|
//
|
|
// NODE_MICIN_ADC
|
|
{
|
|
0, // Flags
|
|
NULL, // AutomationTable
|
|
&KSNODETYPE_ADC, // Type
|
|
NULL // Name
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* MiniportConnections
|
|
*****************************************************************************
|
|
* This structure identifies the connections between filter pins and
|
|
* node pins.
|
|
*/
|
|
static PCCONNECTION_DESCRIPTOR MiniportConnections[] =
|
|
{
|
|
//from_node from_pin to_node to_pin
|
|
{ PCFILTER_NODE, PIN_WAVEOUT, NODE_WAVEOUT_DAC, 1},
|
|
{ NODE_WAVEOUT_DAC, 0, PCFILTER_NODE, PIN_WAVEOUT_BRIDGE},
|
|
{ PCFILTER_NODE, PIN_WAVEIN_BRIDGE, NODE_WAVEIN_ADC, 1},
|
|
{ NODE_WAVEIN_ADC, 0, PCFILTER_NODE, PIN_WAVEIN},
|
|
//
|
|
// The Microphone connection is not used if PINC_MICIN_PRESENT is not set.
|
|
// To remove them, Init() will reduce the "ConnectionCount" in the
|
|
// MiniportFilterDescriptor.
|
|
//
|
|
{ PCFILTER_NODE, PIN_MICIN_BRIDGE, NODE_MICIN_ADC, 1},
|
|
{ NODE_MICIN_ADC, 0, PCFILTER_NODE, PIN_MICIN}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* MiniportFilterDescriptor
|
|
*****************************************************************************
|
|
* Complete miniport description.
|
|
* Init() modifies the pin count, node count and connection count in absence
|
|
* of the MicIn recording line.
|
|
*/
|
|
static PCFILTER_DESCRIPTOR MiniportFilterDescriptor =
|
|
{
|
|
0, // Version
|
|
NULL, // AutomationTable
|
|
sizeof(PCPIN_DESCRIPTOR), // PinSize
|
|
SIZEOF_ARRAY(MiniportPins), // PinCount
|
|
MiniportPins, // Pins
|
|
sizeof(PCNODE_DESCRIPTOR), // NodeSize
|
|
SIZEOF_ARRAY(MiniportNodes), // NodeCount
|
|
MiniportNodes, // Nodes
|
|
SIZEOF_ARRAY(MiniportConnections), // ConnectionCount
|
|
MiniportConnections, // Connections
|
|
0, // CategoryCount
|
|
NULL // Categories: NULL->use defaults (audio, render, capture)
|
|
};
|
|
|
|
#pragma code_seg("PAGE")
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::PropertyChannelConfig
|
|
*****************************************************************************
|
|
* This is the property handler for KSPROPERTY_AUDIO_CHANNEL_CONFIG of the
|
|
* DAC node. It sets the channel configuration (how many channels, how user
|
|
* was setting up the speakers).
|
|
*/
|
|
NTSTATUS CMiniportWaveICH::PropertyChannelConfig
|
|
(
|
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (PropertyRequest);
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::PropertyChannelConfig]"));
|
|
|
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
|
// The major target is the object pointer to the wave miniport.
|
|
CMiniportWaveICH *that =
|
|
(CMiniportWaveICH *) (PMINIPORTWAVEPCI)PropertyRequest->MajorTarget;
|
|
|
|
ASSERT (that);
|
|
|
|
// We only have a set defined.
|
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
|
|
{
|
|
// validate buffer size.
|
|
if (PropertyRequest->ValueSize < sizeof(LONG))
|
|
return ntStatus;
|
|
|
|
// The "Value" is the input buffer with the channel config.
|
|
if (PropertyRequest->Value)
|
|
{
|
|
// We can accept different channel configurations, depending
|
|
// on the number of channels we can play.
|
|
if (that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT))
|
|
{
|
|
if (that->AdapterCommon->GetPinConfig (PINC_CENTER_LFE_PRESENT))
|
|
{
|
|
// we accept 5.1
|
|
if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_5POINT1)
|
|
{
|
|
that->m_dwChannelMask = *(PLONG)PropertyRequest->Value;
|
|
that->m_wChannels = 6;
|
|
that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// accept also surround or quad.
|
|
if ((*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_QUAD) ||
|
|
(*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_SURROUND))
|
|
{
|
|
that->m_dwChannelMask = *(PLONG)PropertyRequest->Value;
|
|
that->m_wChannels = 4;
|
|
that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// accept also stereo speakers.
|
|
if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_STEREO)
|
|
{
|
|
that->m_dwChannelMask = *(PLONG)PropertyRequest->Value;
|
|
that->m_wChannels = 2;
|
|
that->AdapterCommon->WriteChannelConfigDefault (that->m_dwChannelMask);
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CreateMiniportWaveICH
|
|
*****************************************************************************
|
|
* Creates a ICH wave miniport object for the ICH adapter.
|
|
* This uses a macro from STDUNK.H to do all the work.
|
|
*/
|
|
NTSTATUS CreateMiniportWaveICH
|
|
(
|
|
OUT PUNKNOWN *Unknown,
|
|
IN REFCLSID,
|
|
IN PUNKNOWN UnknownOuter OPTIONAL,
|
|
IN POOL_TYPE PoolType
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (Unknown);
|
|
|
|
DOUT (DBG_PRINT, ("[CreateMiniportWaveICH]"));
|
|
|
|
STD_CREATE_BODY_(CMiniportWaveICH,Unknown,UnknownOuter,PoolType,
|
|
PMINIPORTWAVEPCI);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::NonDelegatingQueryInterface
|
|
*****************************************************************************
|
|
* Obtains an interface. This function works just like a COM QueryInterface
|
|
* call and is used if the object is not being aggregated.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) CMiniportWaveICH::NonDelegatingQueryInterface
|
|
(
|
|
IN REFIID Interface,
|
|
OUT PVOID *Object
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (Object);
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::NonDelegatingQueryInterface]"));
|
|
|
|
// Is it IID_IUnknown?
|
|
if (IsEqualGUIDAligned (Interface, IID_IUnknown))
|
|
{
|
|
*Object = (PVOID)(PUNKNOWN)(PMINIPORTWAVEPCI)this;
|
|
}
|
|
// or IID_IMiniport ...
|
|
else if (IsEqualGUIDAligned (Interface, IID_IMiniport))
|
|
{
|
|
*Object = (PVOID)(PMINIPORT)this;
|
|
}
|
|
// or IID_IMiniportWavePci ...
|
|
else if (IsEqualGUIDAligned (Interface, IID_IMiniportWavePci))
|
|
{
|
|
*Object = (PVOID)(PMINIPORTWAVEPCI)this;
|
|
}
|
|
// or IID_IPowerNotify ...
|
|
else if (IsEqualGUIDAligned (Interface, IID_IPowerNotify))
|
|
{
|
|
*Object = (PVOID)(PPOWERNOTIFY)this;
|
|
}
|
|
else
|
|
{
|
|
// nothing found, must be an unknown interface.
|
|
*Object = NULL;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We reference the interface for the caller.
|
|
//
|
|
((PUNKNOWN)(*Object))->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::~CMiniportWaveICH
|
|
*****************************************************************************
|
|
* Destructor.
|
|
*/
|
|
CMiniportWaveICH::~CMiniportWaveICH ()
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::~CMiniportWaveICH]"));
|
|
|
|
//
|
|
// Release the DMA channel.
|
|
//
|
|
if (DmaChannel)
|
|
{
|
|
DmaChannel->Release ();
|
|
DmaChannel = NULL;
|
|
}
|
|
|
|
//
|
|
// Release the interrupt sync.
|
|
//
|
|
if (InterruptSync)
|
|
{
|
|
InterruptSync->Release ();
|
|
InterruptSync = NULL;
|
|
}
|
|
|
|
//
|
|
// Release adapter common object.
|
|
//
|
|
if (AdapterCommon)
|
|
{
|
|
AdapterCommon->Release ();
|
|
AdapterCommon = NULL;
|
|
}
|
|
|
|
//
|
|
// Release the port.
|
|
//
|
|
if (Port)
|
|
{
|
|
Port->Release ();
|
|
Port = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::Init
|
|
*****************************************************************************
|
|
* Initializes the miniport.
|
|
* Initializes variables and modifies the wave topology if needed.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) CMiniportWaveICH::Init
|
|
(
|
|
IN PUNKNOWN UnknownAdapter,
|
|
IN PRESOURCELIST ResourceList,
|
|
IN PPORTWAVEPCI Port_,
|
|
OUT PSERVICEGROUP *ServiceGroup_
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (UnknownAdapter);
|
|
ASSERT (ResourceList);
|
|
ASSERT (Port_);
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::Init]"));
|
|
|
|
//
|
|
// AddRef() is required because we are keeping this pointer.
|
|
//
|
|
Port = Port_;
|
|
Port->AddRef ();
|
|
|
|
//
|
|
// No miniport service group
|
|
//
|
|
*ServiceGroup_ = NULL;
|
|
|
|
//
|
|
// Set initial device power state
|
|
//
|
|
m_PowerState = PowerDeviceD0;
|
|
|
|
NTSTATUS ntStatus = UnknownAdapter->
|
|
QueryInterface (IID_IAdapterCommon, (PVOID *)&AdapterCommon);
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
//
|
|
// Alter the topology for the wave miniport.
|
|
//
|
|
if (!(AdapterCommon->GetPinConfig (PINC_MICIN_PRESENT) &&
|
|
AdapterCommon->GetPinConfig (PINC_MIC_PRESENT)))
|
|
{
|
|
//
|
|
// Remove the pins, nodes and connections for the MICIN.
|
|
//
|
|
MiniportFilterDescriptor.PinCount = SIZEOF_ARRAY(MiniportPins) - 2;
|
|
MiniportFilterDescriptor.NodeCount = SIZEOF_ARRAY(MiniportNodes) - 1;
|
|
MiniportFilterDescriptor.ConnectionCount = SIZEOF_ARRAY(MiniportConnections) - 2;
|
|
}
|
|
|
|
//
|
|
// Process the resources.
|
|
//
|
|
ntStatus = ProcessResources (ResourceList);
|
|
|
|
//
|
|
// Get the default channel config
|
|
//
|
|
AdapterCommon->ReadChannelConfigDefault (&m_dwChannelMask, &m_wChannels);
|
|
|
|
//
|
|
// If we came till that point, check the CoDec for supported standard
|
|
// sample rates. This function will then fill the data range information
|
|
//
|
|
if (NT_SUCCESS (ntStatus))
|
|
ntStatus = BuildDataRangeInformation ();
|
|
}
|
|
|
|
//
|
|
// If we fail we get destroyed anyway (that's where we clean up).
|
|
//
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::ProcessResources
|
|
*****************************************************************************
|
|
* Processes the resource list, setting up helper objects accordingly.
|
|
* Sets up the Interrupt + Service routine and DMA.
|
|
*/
|
|
NTSTATUS CMiniportWaveICH::ProcessResources
|
|
(
|
|
IN PRESOURCELIST ResourceList
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (ResourceList);
|
|
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::ProcessResources]"));
|
|
|
|
|
|
ULONG countIRQ = ResourceList->NumberOfInterrupts ();
|
|
if (countIRQ < 1)
|
|
{
|
|
DOUT (DBG_ERROR, ("Unknown configuration for wave miniport!"));
|
|
return STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
}
|
|
|
|
//
|
|
// Create an interrupt sync object
|
|
//
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
ntStatus = PcNewInterruptSync (&InterruptSync,
|
|
NULL,
|
|
ResourceList,
|
|
0,
|
|
InterruptSyncModeNormal);
|
|
|
|
if (!NT_SUCCESS (ntStatus) || !InterruptSync)
|
|
{
|
|
DOUT (DBG_ERROR, ("Failed to create an interrupt sync!"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Register our ISR.
|
|
//
|
|
ntStatus = InterruptSync->RegisterServiceRoutine (InterruptServiceRoutine,
|
|
(PVOID)this, FALSE);
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("Failed to register ISR!"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Connect the interrupt.
|
|
//
|
|
ntStatus = InterruptSync->Connect ();
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("Failed to connect the ISR with InterruptSync!"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Create the DMA Channel object.
|
|
//
|
|
ntStatus = Port->NewMasterDmaChannel (&DmaChannel, // OutDmaChannel
|
|
NULL, // OuterUnknown (opt)
|
|
NonPagedPool, // Pool Type
|
|
NULL, // ResourceList (opt)
|
|
TRUE, // ScatterGather
|
|
TRUE, // Dma32BitAddresses
|
|
FALSE, // Dma64BitAddresses
|
|
FALSE, // IgnoreCount
|
|
Width32Bits, // DmaWidth
|
|
MaximumDmaSpeed, // DmaSpeed
|
|
0x1FFFE, // MaximumLength (128KByte -2)
|
|
0); // DmaPort
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("Failed on NewMasterDmaChannel!"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Get the DMA adapter.
|
|
//
|
|
AdapterObject = DmaChannel->GetAdapterObject ();
|
|
|
|
//
|
|
// On failure object is destroyed which cleans up.
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::BuildDataRangeInformation
|
|
*****************************************************************************
|
|
* This function dynamically build the data range information for the pins.
|
|
* It also connects the static arrays with the data range information
|
|
* structure.
|
|
* If this function returns with an error the miniport should be destroyed.
|
|
*
|
|
* To build the data range information, we test the most popular sample rates,
|
|
* the functions calls ProgramSampleRate in AdapterCommon object to actually
|
|
* program the sample rate. After probing that way for multiple sample rates,
|
|
* the original value, which is 48KHz is, gets restored.
|
|
* We have to test the sample rates for playback, capture and microphone
|
|
* separately. Every time we succeed, we update the data range information and
|
|
* the pointers that point to it.
|
|
*/
|
|
NTSTATUS CMiniportWaveICH::BuildDataRangeInformation (void)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
NTSTATUS ntStatus;
|
|
int nWavePlaybackEntries = 0;
|
|
int nWaveRecordingEntries = 0;
|
|
int nMicEntries = 0;
|
|
int nChannels;
|
|
int nLoop;
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::BuildDataRangeInformation]"));
|
|
|
|
//
|
|
// Calculate the number of max. channels available in the codec.
|
|
//
|
|
if (AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT))
|
|
{
|
|
if (AdapterCommon->GetPinConfig (PINC_CENTER_LFE_PRESENT))
|
|
{
|
|
nChannels = 6;
|
|
}
|
|
else
|
|
{
|
|
nChannels = 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nChannels = 2;
|
|
}
|
|
|
|
// Check for the render sample rates.
|
|
for (nLoop = 0; nLoop < WAVE_SAMPLERATES_TESTED; nLoop++)
|
|
{
|
|
ntStatus = AdapterCommon->ProgramSampleRate (AC97REG_FRONT_SAMPLERATE,
|
|
dwWaveSampleRates[nLoop]);
|
|
|
|
// We support the sample rate?
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
// Add it to the PinDataRange
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.FormatSize = sizeof(KSDATARANGE_AUDIO);
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.Flags = 0;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.SampleSize = nChannels * 2;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.Reserved = 0;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].DataRange.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].MaximumChannels = nChannels;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].MinimumBitsPerSample = 16;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].MaximumBitsPerSample = 16;
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].MinimumSampleFrequency = dwWaveSampleRates[nLoop];
|
|
PinDataRangesPCMStreamRender[nWavePlaybackEntries].MaximumSampleFrequency = dwWaveSampleRates[nLoop];
|
|
|
|
// Add it to the PinDataRangePointer
|
|
PinDataRangePointersPCMStreamRender[nWavePlaybackEntries] = (PKSDATARANGE)&PinDataRangesPCMStreamRender[nWavePlaybackEntries];
|
|
|
|
// Increase count
|
|
nWavePlaybackEntries++;
|
|
}
|
|
}
|
|
|
|
// Check for the capture sample rates.
|
|
for (nLoop = 0; nLoop < WAVE_SAMPLERATES_TESTED; nLoop++)
|
|
{
|
|
ntStatus = AdapterCommon->ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, dwWaveSampleRates[nLoop]);
|
|
|
|
// We support the sample rate?
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
// Add it to the PinDataRange
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.FormatSize = sizeof(KSDATARANGE_AUDIO);
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.Flags = 0;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.SampleSize = 4;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.Reserved = 0;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].DataRange.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].MaximumChannels = 2;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].MinimumBitsPerSample = 16;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].MaximumBitsPerSample = 16;
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].MinimumSampleFrequency = dwWaveSampleRates[nLoop];
|
|
PinDataRangesPCMStreamCapture[nWaveRecordingEntries].MaximumSampleFrequency = dwWaveSampleRates[nLoop];
|
|
|
|
// Add it to the PinDataRangePointer
|
|
PinDataRangePointersPCMStreamCapture[nWaveRecordingEntries] = (PKSDATARANGE)&PinDataRangesPCMStreamCapture[nWaveRecordingEntries];
|
|
|
|
// Increase count
|
|
nWaveRecordingEntries++;
|
|
}
|
|
}
|
|
|
|
// Check for the MIC sample rates.
|
|
for (nLoop = 0; nLoop < MIC_SAMPLERATES_TESTED; nLoop++)
|
|
{
|
|
ntStatus = AdapterCommon->ProgramSampleRate (AC97REG_MIC_SAMPLERATE, dwMicSampleRates[nLoop]);
|
|
|
|
// We support the sample rate?
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
// Add it to the PinDataRange
|
|
PinDataRangesMicStream[nMicEntries].DataRange.FormatSize = sizeof(KSDATARANGE_AUDIO);
|
|
PinDataRangesMicStream[nMicEntries].DataRange.Flags = 0;
|
|
PinDataRangesMicStream[nMicEntries].DataRange.SampleSize = 2;
|
|
PinDataRangesMicStream[nMicEntries].DataRange.Reserved = 0;
|
|
PinDataRangesMicStream[nMicEntries].DataRange.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
|
PinDataRangesMicStream[nMicEntries].DataRange.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
PinDataRangesMicStream[nMicEntries].DataRange.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
PinDataRangesMicStream[nMicEntries].MaximumChannels = 1;
|
|
PinDataRangesMicStream[nMicEntries].MinimumBitsPerSample = 16;
|
|
PinDataRangesMicStream[nMicEntries].MaximumBitsPerSample = 16;
|
|
PinDataRangesMicStream[nMicEntries].MinimumSampleFrequency = dwMicSampleRates[nLoop];
|
|
PinDataRangesMicStream[nMicEntries].MaximumSampleFrequency = dwMicSampleRates[nLoop];
|
|
|
|
// Add it to the PinDataRangePointer
|
|
PinDataRangePointersMicStream[nMicEntries] = (PKSDATARANGE)&PinDataRangesMicStream[nMicEntries];
|
|
|
|
// Increase count
|
|
nMicEntries++;
|
|
}
|
|
}
|
|
|
|
// Now go through the pin descriptor list and change the data range entries to the actual number.
|
|
for (nLoop = 0; nLoop < SIZEOF_ARRAY(MiniportPins); nLoop++)
|
|
{
|
|
if (MiniportPins[nLoop].KsPinDescriptor.DataRanges == PinDataRangePointersPCMStreamRender)
|
|
MiniportPins[nLoop].KsPinDescriptor.DataRangesCount = nWavePlaybackEntries;
|
|
if (MiniportPins[nLoop].KsPinDescriptor.DataRanges == PinDataRangePointersPCMStreamCapture)
|
|
MiniportPins[nLoop].KsPinDescriptor.DataRangesCount = nWaveRecordingEntries;
|
|
if (MiniportPins[nLoop].KsPinDescriptor.DataRanges == PinDataRangePointersMicStream)
|
|
MiniportPins[nLoop].KsPinDescriptor.DataRangesCount = nMicEntries;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::NewStream
|
|
*****************************************************************************
|
|
* Creates a new stream.
|
|
* This function is called when a streaming pin is created.
|
|
* It checks if the channel is already in use, tests the data format, creates
|
|
* and initializes the stream object.
|
|
*/
|
|
STDMETHODIMP CMiniportWaveICH::NewStream
|
|
(
|
|
OUT PMINIPORTWAVEPCISTREAM *Stream,
|
|
IN PUNKNOWN OuterUnknown,
|
|
IN POOL_TYPE PoolType,
|
|
IN PPORTWAVEPCISTREAM PortStream,
|
|
IN ULONG Channel_,
|
|
IN BOOLEAN Capture,
|
|
IN PKSDATAFORMAT DataFormat,
|
|
OUT PDMACHANNEL *DmaChannel_,
|
|
OUT PSERVICEGROUP *ServiceGroup
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (Stream);
|
|
ASSERT (PortStream);
|
|
ASSERT (DataFormat);
|
|
ASSERT (DmaChannel_);
|
|
ASSERT (ServiceGroup);
|
|
|
|
CMiniportWaveICHStream *pWaveICHStream = NULL;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::NewStream]"));
|
|
|
|
//
|
|
// Validate the channel (pin id).
|
|
//
|
|
if ((Channel_ != PIN_WAVEOUT) && (Channel_ != PIN_WAVEIN) &&
|
|
(Channel_ != PIN_MICIN))
|
|
{
|
|
DOUT (DBG_ERROR, ("[NewStream] Invalid channel passed!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check if the pin is already in use
|
|
//
|
|
ULONG Channel = Channel_ >> 1;
|
|
if (Streams[Channel])
|
|
{
|
|
DOUT (DBG_ERROR, ("[NewStream] Pin is already in use!"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check parameters.
|
|
//
|
|
ntStatus = TestDataFormat (DataFormat, (WavePins)Channel_);
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_VSR, ("[NewStream] TestDataFormat failed!"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Create a new stream.
|
|
//
|
|
ntStatus = CreateMiniportWaveICHStream (&pWaveICHStream, OuterUnknown,
|
|
PoolType);
|
|
|
|
//
|
|
// Return in case of an error.
|
|
//
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("[NewStream] Failed to create stream!"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Initialize the stream.
|
|
//
|
|
ntStatus = pWaveICHStream->Init (this,
|
|
PortStream,
|
|
Channel,
|
|
Capture,
|
|
DataFormat,
|
|
ServiceGroup);
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
//
|
|
// Release the stream and clean up.
|
|
//
|
|
DOUT (DBG_ERROR, ("[NewStream] Failed to init stream!"));
|
|
pWaveICHStream->Release ();
|
|
// In case the stream passed us a ServiceGroup, portcls will ignore all parameters
|
|
// on a failure, so we have to release it here.
|
|
if (*ServiceGroup)
|
|
(*ServiceGroup)->Release();
|
|
*ServiceGroup = NULL;
|
|
*Stream = NULL;
|
|
*DmaChannel_ = NULL;
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Save the pointers.
|
|
//
|
|
*Stream = (PMINIPORTWAVEPCISTREAM)pWaveICHStream;
|
|
*DmaChannel_ = DmaChannel;
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::GetDescription
|
|
*****************************************************************************
|
|
* Gets the topology.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) CMiniportWaveICH::GetDescription
|
|
(
|
|
OUT PPCFILTER_DESCRIPTOR *OutFilterDescriptor
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (OutFilterDescriptor);
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::GetDescription]"));
|
|
|
|
*OutFilterDescriptor = &MiniportFilterDescriptor;
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::DataRangeIntersection
|
|
*****************************************************************************
|
|
* Tests a data range intersection.
|
|
* Cause the AC97 controller does not support mono render or capture, we have
|
|
* to check the max. channel field (unfortunately, there is no MinimumChannel
|
|
* and MaximumChannel field, just a MaximumChannel field).
|
|
* If the MaximumChannel is 2, then we can pass this to the default handler of
|
|
* portcls which always chooses the most (SampleFrequency, Channel, Bits etc.)
|
|
*
|
|
* This DataRangeIntersection function is strictly only for the exposed formats
|
|
* in this sample driver. If you intend to add other formats like AC3 then
|
|
* you have to be make sure that you check the GUIDs and the data range, since
|
|
* portcls only checks the data range for waveformatex.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) CMiniportWaveICH::DataRangeIntersection
|
|
(
|
|
IN ULONG PinId,
|
|
IN PKSDATARANGE ClientsDataRange,
|
|
IN PKSDATARANGE MyDataRange,
|
|
IN ULONG OutputBufferLength,
|
|
OUT PVOID ResultantFormat,
|
|
OUT PULONG ResultantFormatLength
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::DataRangeIntersection]"));
|
|
|
|
//
|
|
// This function gets only called if the GUIDS in the KSDATARANGE_AUDIO
|
|
// structure that we attached to the pin are equal with the requested
|
|
// format (see "BuildDataRangeInformation).
|
|
// Additionally, for waveformatex portcls checks that the requested sample
|
|
// frequency range fits into our exposed sample frequency range. Since we
|
|
// only have discrete sample frequencies in the pin's data range, we don't
|
|
// have to check that either.
|
|
// There is one exception to this rule: portcls clones all WAVEFORMATEX
|
|
// data ranges to DSOUND dataranges, so we might get a data range
|
|
// intersection that has a DSOUND specifier. We don't support that
|
|
// since this is only used for HW acceleration
|
|
//
|
|
if (IsEqualGUIDAligned (ClientsDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND))
|
|
{
|
|
DOUT (DBG_PRINT, ("[DataRangeIntersection] We don't support DSOUND specifier"));
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Start with checking the size of the output buffer.
|
|
//
|
|
if (!OutputBufferLength)
|
|
{
|
|
*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
if (OutputBufferLength < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[DataRangeIntersection] Buffer too small"));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// We can only play or record multichannel (>=2 channels) except for the MIC
|
|
// recording channel where we can only record mono. Portcls checked the channels
|
|
// already, however, since we have no minimum channels field, the KSDATARANGE_AUDIO
|
|
// could have MaximumChannels = 1.
|
|
//
|
|
if (PinId != PIN_MICIN)
|
|
{
|
|
// reject mono format for normal wave playback or capture.
|
|
if (((PKSDATARANGE_AUDIO)ClientsDataRange)->MaximumChannels < 2)
|
|
{
|
|
DOUT (DBG_WARNING, ("[DataRangeIntersection] Mono requested for WaveIn or WaveOut"));
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill in the structure the datarange structure.
|
|
// KSDATARANGE and KSDATAFORMAT are the same.
|
|
//
|
|
*(PKSDATAFORMAT)ResultantFormat = *MyDataRange;
|
|
|
|
//
|
|
// Modify the size of the data format structure to fit the WAVEFORMATPCMEX
|
|
// structure.
|
|
//
|
|
((PKSDATAFORMAT)ResultantFormat)->FormatSize =
|
|
sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
|
|
//
|
|
// Append the WAVEFORMATPCMEX structur.
|
|
//
|
|
PWAVEFORMATPCMEX WaveFormat = (PWAVEFORMATPCMEX)((PKSDATAFORMAT)ResultantFormat + 1);
|
|
|
|
// We want a WAFEFORMATEXTENSIBLE which is equal to WAVEFORMATPCMEX.
|
|
WaveFormat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
// Set the number of channels
|
|
if (PinId == PIN_WAVEOUT)
|
|
{
|
|
// Get the max. possible channels for playback.
|
|
ULONG nMaxChannels = min (((PKSDATARANGE_AUDIO)ClientsDataRange)->MaximumChannels, m_wChannels);
|
|
|
|
// We cannot play uneven number of channels
|
|
if (nMaxChannels & 0x01)
|
|
nMaxChannels--;
|
|
// ... and also 0 channels wouldn't be a good request.
|
|
if (!nMaxChannels)
|
|
return STATUS_NO_MATCH;
|
|
|
|
WaveFormat->Format.nChannels = (WORD)nMaxChannels;
|
|
}
|
|
else
|
|
// This will be 2 for normal record and 1 for MIC record.
|
|
WaveFormat->Format.nChannels = (WORD)((PKSDATARANGE_AUDIO)MyDataRange)->MaximumChannels;
|
|
|
|
//
|
|
// Hack for codecs that have only one sample rate converter that has both
|
|
// playback and recording data.
|
|
//
|
|
if ((Streams[PIN_WAVEIN_OFFSET] || Streams[PIN_WAVEOUT_OFFSET]) &&
|
|
!AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
|
|
{
|
|
//
|
|
// We have to return this sample rate that is used in the open stream.
|
|
//
|
|
ULONG ulFrequency;
|
|
|
|
if (Streams[PIN_WAVEIN_OFFSET])
|
|
ulFrequency = Streams[PIN_WAVEIN_OFFSET]->GetCurrentSampleRate();
|
|
else
|
|
ulFrequency = Streams[PIN_WAVEOUT_OFFSET]->GetCurrentSampleRate();
|
|
|
|
//
|
|
// Check if this sample rate is in the requested data range of the client.
|
|
//
|
|
if ((((PKSDATARANGE_AUDIO)ClientsDataRange)->MaximumSampleFrequency < ulFrequency) ||
|
|
(((PKSDATARANGE_AUDIO)ClientsDataRange)->MinimumSampleFrequency > ulFrequency))
|
|
{
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
WaveFormat->Format.nSamplesPerSec = ulFrequency;
|
|
}
|
|
else
|
|
{
|
|
// Since we have discrete frequencies in the data range, min = max.
|
|
WaveFormat->Format.nSamplesPerSec = ((PKSDATARANGE_AUDIO)MyDataRange)->MaximumSampleFrequency;
|
|
}
|
|
|
|
// Will be 16.
|
|
WaveFormat->Format.wBitsPerSample = (WORD)((PKSDATARANGE_AUDIO)MyDataRange)->MaximumBitsPerSample;
|
|
// Will be 2 * channels.
|
|
WaveFormat->Format.nBlockAlign = (WaveFormat->Format.wBitsPerSample * WaveFormat->Format.nChannels) / 8;
|
|
// That is played in a sec.
|
|
WaveFormat->Format.nAvgBytesPerSec = WaveFormat->Format.nSamplesPerSec * WaveFormat->Format.nBlockAlign;
|
|
// WAVEFORMATPCMEX
|
|
WaveFormat->Format.cbSize = 22;
|
|
// We have as many valid bits as the bit depth is (16).
|
|
WaveFormat->Samples.wValidBitsPerSample = WaveFormat->Format.wBitsPerSample;
|
|
// Set the channel mask
|
|
if (PinId == PIN_WAVEOUT)
|
|
{
|
|
// If we can play in our configuration, then set the channel mask
|
|
if (WaveFormat->Format.nChannels == m_wChannels)
|
|
// Set the playback channel mask to the current speaker config.
|
|
WaveFormat->dwChannelMask = m_dwChannelMask;
|
|
else
|
|
{
|
|
//
|
|
// We have to set a channel mask.
|
|
// nChannles can only be 4 if we are in 6 channel mode. In that
|
|
// case it must be a QUAD configurations. The only other value
|
|
// allowed is 2 channels, which defaults to stereo.
|
|
//
|
|
if (WaveFormat->Format.nChannels == 4)
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
|
|
else
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This will be KSAUDIO_SPEAKER_STEREO for normal record and KSAUDIO_SPEAKER_MONO
|
|
// for MIC record.
|
|
if (PinId == PIN_MICIN)
|
|
// MicIn -> 1 channel
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_MONO;
|
|
else
|
|
// normal record -> 2 channels
|
|
WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
|
}
|
|
// Here we specify the subtype of the WAVEFORMATEXTENSIBLE.
|
|
WaveFormat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
|
|
// Now overwrite also the sample size in the ksdataformat structure.
|
|
((PKSDATAFORMAT)ResultantFormat)->SampleSize = WaveFormat->Format.nBlockAlign;
|
|
|
|
//
|
|
// That we will return.
|
|
//
|
|
*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX);
|
|
|
|
DOUT (DBG_STREAM, ("[DataRangeIntersection] Frequency: %d, Channels: %d, bps: %d, ChannelMask: %X",
|
|
WaveFormat->Format.nSamplesPerSec, WaveFormat->Format.nChannels,
|
|
WaveFormat->Format.wBitsPerSample, WaveFormat->dwChannelMask));
|
|
|
|
// Let portcls do some work ...
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::TestDataFormat
|
|
*****************************************************************************
|
|
* Checks if the passed data format is known to the driver and verifies that
|
|
* the number of channels, the width of one sample match to the AC97
|
|
* specification.
|
|
*/
|
|
NTSTATUS CMiniportWaveICH::TestDataFormat
|
|
(
|
|
IN PKSDATAFORMAT Format,
|
|
IN WavePins Pin
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (Format);
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::TestDataFormat]"));
|
|
|
|
//
|
|
// KSDATAFORMAT contains three GUIDs to support extensible format. The
|
|
// first two GUIDs identify the type of data. The third indicates the
|
|
// type of specifier used to indicate format specifics. We are only
|
|
// supporting PCM audio formats that use WAVEFORMATEX.
|
|
//
|
|
if (!IsEqualGUIDAligned (Format->MajorFormat, KSDATAFORMAT_TYPE_AUDIO) ||
|
|
!IsEqualGUIDAligned (Format->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) ||
|
|
!IsEqualGUIDAligned (Format->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
|
|
{
|
|
DOUT (DBG_ERROR, ("[TestDataFormat] Invalid format type!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
|
|
|
|
//
|
|
// If the size doesn't match, then something is messed up.
|
|
//
|
|
if (Format->FormatSize < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid FormatSize!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We only support PCM, 16-bit.
|
|
//
|
|
if (waveFormat->Format.wBitsPerSample != 16)
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Bits Per Sample must be 16!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We support WaveFormatPCMEX (=WAVEFORMATEXTENSIBLE) or WaveFormatPCM.
|
|
//
|
|
if ((waveFormat->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) &&
|
|
(waveFormat->Format.wFormatTag != WAVE_FORMAT_PCM))
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid Format Tag!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Make additional checks for the WAVEFORMATEXTENSIBLE
|
|
//
|
|
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
|
{
|
|
//
|
|
// If the size doesn't match, then something is messed up.
|
|
//
|
|
if (Format->FormatSize < (sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid FormatSize!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check also the subtype (PCM) and the size of the extended data.
|
|
//
|
|
if (!IsEqualGUIDAligned (waveFormat->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) ||
|
|
(waveFormat->Format.cbSize < (sizeof(WAVEFORMATPCMEX) - sizeof(WAVEFORMATEX))))
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Unsupported WAVEFORMATEXTENSIBLE!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check the channel mask. We support 1, 2 channels or whatever was set
|
|
// with the Speaker config dialog.
|
|
//
|
|
if (((waveFormat->Format.nChannels == 1) &&
|
|
(waveFormat->dwChannelMask != KSAUDIO_SPEAKER_MONO)) ||
|
|
((waveFormat->Format.nChannels == 2) &&
|
|
(waveFormat->dwChannelMask != KSAUDIO_SPEAKER_STEREO)) ||
|
|
((waveFormat->Format.nChannels == m_wChannels) &&
|
|
(waveFormat->dwChannelMask != m_dwChannelMask)))
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Channel Mask!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check the number of channels.
|
|
//
|
|
switch (Pin)
|
|
{
|
|
case PIN_MICIN: // 1 channel
|
|
if (waveFormat->Format.nChannels != 1)
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid Number of Channels for PIN_MICIN!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
case PIN_WAVEIN: // 2 channels
|
|
if (waveFormat->Format.nChannels != 2)
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid Number of Channels for PIN_WAVEIN!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
case PIN_WAVEOUT: // channel and mask from PropertyChannelConfig or standard.
|
|
if (waveFormat->Format.nChannels != m_wChannels)
|
|
{
|
|
DOUT (DBG_WARNING, ("[TestDataFormat] Invalid Number of Channels for PIN_WAVEOUT!"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Print the information.
|
|
//
|
|
if (waveFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
|
|
{
|
|
DOUT (DBG_STREAM, ("[TestDataFormat] PCMEX - Frequency: %d, Channels: %d, bps: %d, ChannelMask: %X",
|
|
waveFormat->Format.nSamplesPerSec, waveFormat->Format.nChannels,
|
|
waveFormat->Format.wBitsPerSample, waveFormat->dwChannelMask));
|
|
}
|
|
else
|
|
{
|
|
DOUT (DBG_STREAM, ("[TestDataFormat] PCM - Frequency: %d, Channels: %d, bps: %d",
|
|
waveFormat->Format.nSamplesPerSec, waveFormat->Format.nChannels,
|
|
waveFormat->Format.wBitsPerSample));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::PowerChangeNotify
|
|
*****************************************************************************
|
|
* This routine gets called as a result of hooking up the IPowerNotify
|
|
* interface. This interface indicates the driver's desire to receive explicit
|
|
* notification of power state changes. The interface provides a single method
|
|
* (or callback) that is called by the miniport's corresponding port driver in
|
|
* response to a power state change. Using wave audio as an example, when the
|
|
* device is requested to go to a sleep state the port driver pauses any
|
|
* active streams and then calls the power notify callback to inform the
|
|
* miniport of the impending power down. The miniport then has an opportunity
|
|
* to save any necessary context before the adapter's PowerChangeState method
|
|
* is called. The process is reversed when the device is powering up. PortCls
|
|
* first calls the adapter's PowerChangeState method to power up the adapter.
|
|
* The port driver then calls the miniport's callback to allow the miniport to
|
|
* restore its context. Finally, the port driver unpauses any previously paused
|
|
* active audio streams.
|
|
*/
|
|
STDMETHODIMP_(void) CMiniportWaveICH::PowerChangeNotify
|
|
(
|
|
IN POWER_STATE NewState
|
|
)
|
|
{
|
|
PAGED_CODE ();
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
DOUT (DBG_PRINT, ("[CMiniportWaveICH::PowerChangeNotify]"));
|
|
|
|
//
|
|
// Check to see if this is the current power state.
|
|
//
|
|
if (NewState.DeviceState == m_PowerState)
|
|
{
|
|
DOUT (DBG_POWER, ("New device state equals old state."));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check the new device state.
|
|
//
|
|
if ((NewState.DeviceState < PowerDeviceD0) ||
|
|
(NewState.DeviceState > PowerDeviceD3))
|
|
{
|
|
DOUT (DBG_ERROR, ("Unknown device state: D%d.",
|
|
(ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
|
|
return;
|
|
}
|
|
|
|
DOUT (DBG_POWER, ("Changing state to D%d.", (ULONG)NewState.DeviceState -
|
|
(ULONG)PowerDeviceD0));
|
|
|
|
//
|
|
// In case we return to D0 power state from a D3 state, restore the
|
|
// interrupt connection.
|
|
//
|
|
if (NewState.DeviceState == PowerDeviceD0)
|
|
{
|
|
ntStatus = InterruptSync->Connect ();
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("Failed to connect the ISR with InterruptSync!"));
|
|
// We can do nothing else than just continue ...
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the stream routine which takes care of the DMA engine.
|
|
// That's all we have to do.
|
|
//
|
|
for (int loop = PIN_WAVEOUT_OFFSET; loop < PIN_MICIN_OFFSET; loop++)
|
|
{
|
|
if (Streams[loop])
|
|
{
|
|
ntStatus = Streams[loop]->PowerChangeNotify (NewState);
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
DOUT (DBG_ERROR, ("PowerChangeNotify D%d for the stream failed",
|
|
(ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// In case we go to any sleep state we disconnect the interrupt service
|
|
// reoutine from the interrupt.
|
|
// Normally this is not required to do, but for some reason this fixes
|
|
// a problem where we won't have any interrupts on specific motherboards
|
|
// after resume.
|
|
//
|
|
if (NewState.DeviceState != PowerDeviceD0)
|
|
{
|
|
InterruptSync->Disconnect ();
|
|
}
|
|
|
|
//
|
|
// Save the new state. This local value is used to determine when to
|
|
// cache property accesses and when to permit the driver from accessing
|
|
// the hardware.
|
|
//
|
|
m_PowerState = NewState.DeviceState;
|
|
DOUT (DBG_POWER, ("Entering D%d",
|
|
(ULONG)m_PowerState - (ULONG)PowerDeviceD0));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Non paged code begins here
|
|
*****************************************************************************
|
|
*/
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* CMiniportWaveICH::Service
|
|
*****************************************************************************
|
|
* Processing routine for dealing with miniport interrupts. This routine is
|
|
* called at DISPATCH_LEVEL.
|
|
*/
|
|
STDMETHODIMP_(void) CMiniportWaveICH::Service (void)
|
|
{
|
|
// not needed
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* InterruptServiceRoutine
|
|
*****************************************************************************
|
|
* The task of the ISR is to clear an interrupt from this device so we don't
|
|
* get an interrupt storm and schedule a DPC which actually does the
|
|
* real work.
|
|
*/
|
|
NTSTATUS CMiniportWaveICH::InterruptServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
ASSERT (InterruptSync);
|
|
ASSERT (DynamicContext);
|
|
|
|
ULONG GlobalStatus;
|
|
USHORT DMAStatusRegister;
|
|
|
|
//
|
|
// Get our context which is a pointer to class CMiniportWaveICH.
|
|
//
|
|
CMiniportWaveICH *that = (CMiniportWaveICH *)DynamicContext;
|
|
|
|
//
|
|
// Check for a valid AdapterCommon pointer.
|
|
//
|
|
if (!that->AdapterCommon)
|
|
{
|
|
//
|
|
// In case we didn't handle the interrupt, unsuccessful tells the system
|
|
// to call the next interrupt handler in the chain.
|
|
//
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// From this point down, basically in the complete ISR, we cannot use
|
|
// relative addresses (stream class base address + X_CR for example)
|
|
// cause we might get called when the stream class is destroyed or
|
|
// not existent. This doesn't make too much sense (that there is an
|
|
// interrupt for a non-existing stream) but could happen and we have
|
|
// to deal with the interrupt.
|
|
//
|
|
|
|
//
|
|
// Read the global register to check the interrupt bits
|
|
//
|
|
GlobalStatus = that->AdapterCommon->ReadBMControlRegister32 (GLOB_STA);
|
|
|
|
//
|
|
// Check for weird return values. Could happen if the PCI device is already
|
|
// disabled and another device that shares this interrupt generated an
|
|
// interrupt.
|
|
// The register should never have all bits cleared or set.
|
|
//
|
|
if (!GlobalStatus || (GlobalStatus == 0xFFFFFFFF))
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check for PCM out interrupt.
|
|
//
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
if (GlobalStatus & GLOB_STA_POINT)
|
|
{
|
|
//
|
|
// Read PCM out DMA status registers.
|
|
//
|
|
DMAStatusRegister = (USHORT)that->AdapterCommon->
|
|
ReadBMControlRegister16 (PO_SR);
|
|
|
|
|
|
//
|
|
// We could now check for every possible error condition
|
|
// (like FIFO error) and monitor the different errors, but currently
|
|
// we have the same action for every INT and therefore we simplify
|
|
// this routine enormous with just clearing the bits.
|
|
//
|
|
if (that->Streams[PIN_WAVEOUT_OFFSET])
|
|
{
|
|
//
|
|
// ACK the interrupt.
|
|
//
|
|
that->AdapterCommon->WriteBMControlRegister (PO_SR, DMAStatusRegister);
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Request DPC service for PCM out.
|
|
//
|
|
if ((that->Port) && (that->Streams[PIN_WAVEOUT_OFFSET]->ServiceGroup))
|
|
{
|
|
that->Port->Notify (that->Streams[PIN_WAVEOUT_OFFSET]->ServiceGroup);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bad, bad. Shouldn't print in an ISR!
|
|
//
|
|
DOUT (DBG_ERROR, ("WaveOut INT fired but no stream object there."));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for PCM in interrupt.
|
|
//
|
|
if (GlobalStatus & GLOB_STA_PIINT)
|
|
{
|
|
//
|
|
// Read PCM in DMA status registers.
|
|
//
|
|
DMAStatusRegister = (USHORT)that->AdapterCommon->
|
|
ReadBMControlRegister16 (PI_SR);
|
|
|
|
//
|
|
// We could now check for every possible error condition
|
|
// (like FIFO error) and monitor the different errors, but currently
|
|
// we have the same action for every INT and therefore we simplify
|
|
// this routine enormous with just clearing the bits.
|
|
//
|
|
if (that->Streams[PIN_WAVEIN_OFFSET])
|
|
{
|
|
//
|
|
// ACK the interrupt.
|
|
//
|
|
that->AdapterCommon->WriteBMControlRegister (PI_SR, DMAStatusRegister);
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Request DPC service for PCM in.
|
|
//
|
|
if ((that->Port) && (that->Streams[PIN_WAVEIN_OFFSET]->ServiceGroup))
|
|
{
|
|
that->Port->Notify (that->Streams[PIN_WAVEIN_OFFSET]->ServiceGroup);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bad, bad. Shouldn't print in an ISR!
|
|
//
|
|
DOUT (DBG_ERROR, ("WaveIn INT fired but no stream object there."));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for MIC in interrupt.
|
|
//
|
|
if (GlobalStatus & GLOB_STA_MINT)
|
|
{
|
|
//
|
|
// Read MIC in DMA status registers.
|
|
//
|
|
DMAStatusRegister = (USHORT)that->AdapterCommon->
|
|
ReadBMControlRegister16 (MC_SR);
|
|
|
|
//
|
|
// We could now check for every possible error condition
|
|
// (like FIFO error) and monitor the different errors, but currently
|
|
// we have the same action for every INT and therefore we simplify
|
|
// this routine enormous with just clearing the bits.
|
|
//
|
|
if (that->Streams[PIN_MICIN_OFFSET])
|
|
{
|
|
//
|
|
// ACK the interrupt.
|
|
//
|
|
that->AdapterCommon->WriteBMControlRegister (MC_SR, DMAStatusRegister);
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Request DPC service for PCM out.
|
|
//
|
|
if ((that->Port) && (that->Streams[PIN_MICIN_OFFSET]->ServiceGroup))
|
|
{
|
|
that->Port->Notify (that->Streams[PIN_MICIN_OFFSET]->ServiceGroup);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bad, bad. Shouldn't print in an ISR!
|
|
//
|
|
DOUT (DBG_ERROR, ("MicIn INT fired but no stream object there."));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|