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