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

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;
}