Sample Code
Windows Driver Samples/ AC97 Driver Sample/ C++/ driver/ wavepciminiport.cpp/
/******************************************************************************** ** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved. ** ** Portions Copyright (c) 1998-1999 Intel Corporation ** ********************************************************************************/ /* The file wavepciminiport.cpp was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */ // Every debug output has "Modulname text" static char STR_MODULENAME[] = "AC97 Wave: "; #include "wavepciminiport.h" #include "wavepcistream.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; } /***************************************************************************** * CreateAC97MiniportWavePCI ***************************************************************************** * Creates a AC97 wave miniport object for the AC97 adapter. * This uses a macro from STDUNK.H to do all the work. */ NTSTATUS CreateAC97MiniportWavePCI ( OUT PUNKNOWN *Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, _When_((PoolType & NonPagedPoolMustSucceed) != 0, __drv_reportError("Must succeed pool allocations are forbidden. " "Allocation failures cause a system crash")) IN POOL_TYPE PoolType ) { PAGED_CODE (); ASSERT (Unknown); DOUT (DBG_PRINT, ("[CreateAC97MiniportWavePCI]")); STD_CREATE_BODY_WITH_TAG_(CMiniportWaveICH,Unknown,UnknownOuter,PoolType, PoolTag, 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, _COM_Outptr_ 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_IAC97AdapterCommon, (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 = (PDMA_ADAPTER)DmaChannel->GetAdapterObject (); // // On failure object is destroyed which cleans up. // return STATUS_SUCCESS; } /***************************************************************************** * CMiniportWaveICH::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. */ _Use_decl_annotations_ STDMETHODIMP CMiniportWaveICH::NewStream ( PMINIPORTWAVEPCISTREAM *Stream, PUNKNOWN OuterUnknown, POOL_TYPE PoolType, PPORTWAVEPCISTREAM PortStream, ULONG Channel_, BOOLEAN Capture, PKSDATAFORMAT DataFormat, PDMACHANNEL *DmaChannel_, 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. */ _Use_decl_annotations_ STDMETHODIMP_(NTSTATUS) CMiniportWaveICH::DataRangeIntersection ( ULONG PinId, PKSDATARANGE ClientsDataRange, PKSDATARANGE MyDataRange, ULONG OutputBufferLength, PVOID ResultantFormat, 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 and validating that it is not NULL. // if (!OutputBufferLength || NULL == ResultantFormat) { *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 ) { UNREFERENCED_PARAMETER(InterruptSync); 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; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials