/***************************************************************************** * miniport.cpp - SB16 wave miniport implementation ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved. */ #include "minwave.h" #define STR_MODULENAME "sb16wave: " #pragma code_seg("PAGE") /***************************************************************************** * CreateMiniportWaveCyclicSB16() ***************************************************************************** * Creates a cyclic wave miniport object for the SB16 adapter. This uses a * macro from STDUNK.H to do all the work. */ NTSTATUS CreateMiniportWaveCyclicSB16 ( OUT PUNKNOWN * Unknown, IN REFCLSID, IN PUNKNOWN UnknownOuter OPTIONAL, IN POOL_TYPE PoolType ) { PAGED_CODE(); ASSERT(Unknown); STD_CREATE_BODY_(CMiniportWaveCyclicSB16,Unknown,UnknownOuter,PoolType,PMINIPORTWAVECYCLIC); } /***************************************************************************** * MapUsingTable() ***************************************************************************** * Performs a table-based mapping, returning the table index of the indicated * value. -1 is returned if the value is not found. */ int MapUsingTable ( IN ULONG Value, IN PULONG Map, IN ULONG MapSize ) { PAGED_CODE(); ASSERT(Map); for (int result = 0; result < int(MapSize); result++) { if (*Map++ == Value) { return result; } } return -1; } /***************************************************************************** * CMiniportWaveCyclicSB16::ConfigureDevice() ***************************************************************************** * Configures the hardware to use the indicated interrupt and DMA channels. * Returns FALSE iff the configuration is invalid. */ BOOLEAN CMiniportWaveCyclicSB16:: ConfigureDevice ( IN ULONG Interrupt, IN ULONG Dma8Bit, IN ULONG Dma16Bit ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::ConfigureDevice]")); // // Tables mapping DMA and IRQ values to register bit offsets. // static ULONG validDma[] = { 0, 1, ULONG(-1), 3, ULONG(-1), 5, 6, 7 } ; static ULONG validIrq[] = { 9, 5, 7, 10 } ; // // Make sure we are using the right DMA channels. // if (Dma8Bit > 3) { return FALSE; } if (Dma16Bit < 5) { return FALSE; } // // Generate the register value for interrupts. // int bit = MapUsingTable(Interrupt,validIrq,SIZEOF_ARRAY(validIrq)); if (bit == -1) { return FALSE; } BYTE irqConfig = BYTE(1 << bit); // // Generate the register value for DMA. // bit = MapUsingTable(Dma8Bit,validDma,SIZEOF_ARRAY(validDma)); if (bit == -1) { return FALSE; } BYTE dmaConfig = BYTE(1 << bit); if (Dma16Bit != ULONG(-1)) { bit = MapUsingTable(Dma16Bit,validDma,SIZEOF_ARRAY(validDma)); if (bit == -1) { return FALSE; } dmaConfig |= BYTE(1 << bit); } // // Inform the hardware. // AdapterCommon->MixerRegWrite(DSP_MIX_IRQCONFIG,irqConfig); AdapterCommon->MixerRegWrite(DSP_MIX_DMACONFIG,dmaConfig); return TRUE; } /***************************************************************************** * CMiniportWaveCyclicSB16::ProcessResources() ***************************************************************************** * Processes the resource list, setting up helper objects accordingly. */ NTSTATUS CMiniportWaveCyclicSB16:: ProcessResources ( IN PRESOURCELIST ResourceList ) { PAGED_CODE(); ASSERT(ResourceList); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::ProcessResources]")); ULONG intNumber = ULONG(-1); ULONG dma8Bit = ULONG(-1); ULONG dma16Bit = ULONG(-1); // // Get counts for the types of resources. // ULONG countIO = ResourceList->NumberOfPorts(); ULONG countIRQ = ResourceList->NumberOfInterrupts(); ULONG countDMA = ResourceList->NumberOfDmas(); #if (DBG) _DbgPrintF(DEBUGLVL_VERBOSE,("Starting SB16 wave on IRQ 0x%X", ResourceList->FindUntranslatedInterrupt(0)->u.Interrupt.Level) ); _DbgPrintF(DEBUGLVL_VERBOSE,("Starting SB16 wave on Port 0x%X", ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart) ); for (ULONG i = 0; i < countDMA; i++) { _DbgPrintF(DEBUGLVL_VERBOSE,("Starting SB16 wave on DMA 0x%X", ResourceList->FindUntranslatedDma(i)->u.Dma.Channel) ); } #endif NTSTATUS ntStatus = STATUS_SUCCESS; // // Make sure we have the expected number of resources. // if ( (countIO != 1) || (countIRQ < 1) || (countDMA < 1) ) { _DbgPrintF(DEBUGLVL_TERSE,("unknown configuraton; check your code!")); ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } if (NT_SUCCESS(ntStatus)) { // // Instantiate a DMA channel for 8-bit transfers. // ntStatus = Port->NewSlaveDmaChannel ( &DmaChannel8, NULL, ResourceList, 0, MAXLEN_DMA_BUFFER, FALSE, // DemandMode Compatible ); // // Allocate the buffer for 8-bit transfers. // if (NT_SUCCESS(ntStatus)) { ULONG lDMABufferLength = MAXLEN_DMA_BUFFER; do { ntStatus = DmaChannel8->AllocateBuffer(lDMABufferLength,NULL); lDMABufferLength >>= 1; } while (!NT_SUCCESS(ntStatus) && (lDMABufferLength > (PAGE_SIZE / 2))); } if (NT_SUCCESS(ntStatus)) { dma8Bit = ResourceList->FindUntranslatedDma(0)->u.Dma.Channel; if (countDMA > 1) { // // Instantiate a DMA channel for 16-bit transfers. // ntStatus = Port->NewSlaveDmaChannel ( &DmaChannel16, NULL, ResourceList, 1, MAXLEN_DMA_BUFFER, FALSE, Compatible ); // // Allocate the buffer for 16-bit transfers. // if (NT_SUCCESS(ntStatus)) { ULONG lDMABufferLength = MAXLEN_DMA_BUFFER; do { ntStatus = DmaChannel16->AllocateBuffer(lDMABufferLength,NULL); lDMABufferLength >>= 1; } while (!NT_SUCCESS(ntStatus) && (lDMABufferLength > (PAGE_SIZE / 2))); } if (NT_SUCCESS(ntStatus)) { dma16Bit = ResourceList->FindUntranslatedDma(1)->u.Dma.Channel; } } if (NT_SUCCESS(ntStatus)) { // // Get the interrupt number and configure the device. // intNumber = ResourceList-> FindUntranslatedInterrupt(0)->u.Interrupt.Level; if (! ConfigureDevice(intNumber,dma8Bit,dma16Bit)) { _DbgPrintF(DEBUGLVL_TERSE,("ConfigureDevice Failure")); ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR; } } else { _DbgPrintF(DEBUGLVL_TERSE,("NewSlaveDmaChannel 2 Failure %X", ntStatus )); } } else { _DbgPrintF(DEBUGLVL_TERSE,("NewSlaveDmaChannel 1 Failure %X", ntStatus )); } } // // In case of failure object gets destroyed and cleans up. // return ntStatus; } /***************************************************************************** * CMiniportWaveCyclicSB16::ValidateFormat() ***************************************************************************** * Validates a wave format. */ NTSTATUS CMiniportWaveCyclicSB16:: ValidateFormat ( IN PKSDATAFORMAT Format ) { PAGED_CODE(); ASSERT(Format); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::ValidateFormat]")); NTSTATUS ntStatus; // // A WAVEFORMATEX structure should appear after the generic KSDATAFORMAT // if the GUIDs turn out as we expect. // PWAVEFORMATEX waveFormat = PWAVEFORMATEX(Format + 1); // // 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 ( (Format->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) && IsEqualGUIDAligned(Format->MajorFormat,KSDATAFORMAT_TYPE_AUDIO) && IsEqualGUIDAligned(Format->SubFormat,KSDATAFORMAT_SUBTYPE_PCM) && IsEqualGUIDAligned(Format->Specifier,KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) && (waveFormat->wFormatTag == WAVE_FORMAT_PCM) && ((waveFormat->wBitsPerSample == 8) || (waveFormat->wBitsPerSample == 16)) && ((waveFormat->nChannels == 1) || (waveFormat->nChannels == 2)) && ((waveFormat->nSamplesPerSec >= 5000) && (waveFormat->nSamplesPerSec <= 44100)) ) { ntStatus = STATUS_SUCCESS; } else { ntStatus = STATUS_INVALID_PARAMETER; } return ntStatus; } /***************************************************************************** * CMiniportWaveCyclicSB16::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. This function works just like a COM QueryInterface * call and is used if the object is not being aggregated. */ STDMETHODIMP CMiniportWaveCyclicSB16:: NonDelegatingQueryInterface ( IN REFIID Interface, OUT PVOID * Object ) { PAGED_CODE(); ASSERT(Object); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::NonDelegatingQueryInterface]")); if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { *Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLIC(this))); } else if (IsEqualGUIDAligned(Interface,IID_IMiniport)) { *Object = PVOID(PMINIPORT(this)); } else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclic)) { *Object = PVOID(PMINIPORTWAVECYCLIC(this)); } else { *Object = NULL; } if (*Object) { // // We reference the interface for the caller. // PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } /***************************************************************************** * CMiniportWaveCyclicSB16::~CMiniportWaveCyclicSB16() ***************************************************************************** * Destructor. */ CMiniportWaveCyclicSB16:: ~CMiniportWaveCyclicSB16 ( void ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::~CMiniportWaveCyclicSB16]")); if (AdapterCommon) { AdapterCommon->SetWaveMiniport (NULL); AdapterCommon->Release(); AdapterCommon = NULL; } if (Port) { Port->Release(); Port = NULL; } if (DmaChannel8) { DmaChannel8->Release(); DmaChannel8 = NULL; } if (DmaChannel16) { DmaChannel16->Release(); DmaChannel16 = NULL; } if (ServiceGroup) { ServiceGroup->Release(); ServiceGroup = NULL; } } /***************************************************************************** * CMiniportWaveCyclicSB16::Init() ***************************************************************************** * Initializes a the miniport. */ STDMETHODIMP CMiniportWaveCyclicSB16:: Init ( IN PUNKNOWN UnknownAdapter, IN PRESOURCELIST ResourceList, IN PPORTWAVECYCLIC Port_ ) { PAGED_CODE(); ASSERT(UnknownAdapter); ASSERT(ResourceList); ASSERT(Port_); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::init]")); // // AddRef() is required because we are keeping this pointer. // Port = Port_; Port->AddRef(); // // Initialize the member variables. // ServiceGroup = NULL; DmaChannel8 = NULL; DmaChannel16 = NULL; // // We want the IAdapterCommon interface on the adapter common object, // which is given to us as a IUnknown. The QueryInterface call gives us // an AddRefed pointer to the interface we want. // NTSTATUS ntStatus = UnknownAdapter->QueryInterface ( IID_IAdapterCommon, (PVOID *) &AdapterCommon ); // // We need a service group for notifications. We will bind all the // streams that are created to this single service group. All interrupt // notifications ask for service on this group, so all streams will get // serviced. The PcNewServiceGroup() call returns an AddRefed pointer. // The adapter needs a copy of the service group since it is doing the // ISR. // if (NT_SUCCESS(ntStatus)) { KeInitializeMutex(&SampleRateSync,1); ntStatus = PcNewServiceGroup(&ServiceGroup,NULL); } if (NT_SUCCESS(ntStatus)) { AdapterCommon->SetWaveMiniport ((PWAVEMINIPORTSB16)this); ntStatus = ProcessResources(ResourceList); } // // In case of failure object gets destroyed and destructor cleans up. // return ntStatus; } /***************************************************************************** * PinDataRangesStream ***************************************************************************** * Structures indicating range of valid format values for streaming pins. */ static KSDATARANGE_AUDIO PinDataRangesStream[] = { { { sizeof(KSDATARANGE_AUDIO), 0, 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) }, 2, // Max number of channels. 8, // Minimum number of bits per sample. 16, // Maximum number of bits per channel. 5000, // Minimum rate. 44100 // Maximum rate. } }; /***************************************************************************** * PinDataRangePointersStream ***************************************************************************** * List of pointers to structures indicating range of valid format values * for streaming pins. */ static PKSDATARANGE PinDataRangePointersStream[] = { PKSDATARANGE(&PinDataRangesStream[0]) }; /***************************************************************************** * PinDataRangesBridge ***************************************************************************** * Structures indicating range of valid format values for bridge pins. */ static KSDATARANGE PinDataRangesBridge[] = { { sizeof(KSDATARANGE), 0, 0, 0, STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) } }; /***************************************************************************** * PinDataRangePointersBridge ***************************************************************************** * List of pointers to structures indicating range of valid format values * for bridge pins. */ static PKSDATARANGE PinDataRangePointersBridge[] = { &PinDataRangesBridge[0] }; /***************************************************************************** * MiniportPins ***************************************************************************** * List of pins. */ static PCPIN_DESCRIPTOR MiniportPins[] = { // Wave In Streaming Pin (Capture) { 1,1,0, NULL, { 0, NULL, 0, NULL, SIZEOF_ARRAY(PinDataRangePointersStream), PinDataRangePointersStream, KSPIN_DATAFLOW_OUT, KSPIN_COMMUNICATION_SINK, (GUID *) &PINNAME_CAPTURE, &KSAUDFNAME_RECORDING_CONTROL, // this name shows up as the recording panel name in SoundVol. 0 } }, // Wave In Bridge Pin (Capture - From Topology) { 0,0,0, NULL, { 0, NULL, 0, NULL, SIZEOF_ARRAY(PinDataRangePointersBridge), PinDataRangePointersBridge, KSPIN_DATAFLOW_IN, KSPIN_COMMUNICATION_NONE, (GUID *) &KSCATEGORY_AUDIO, NULL, 0 } }, // Wave Out Streaming Pin (Renderer) { 1,1,0, NULL, { 0, NULL, 0, NULL, SIZEOF_ARRAY(PinDataRangePointersStream), PinDataRangePointersStream, KSPIN_DATAFLOW_IN, KSPIN_COMMUNICATION_SINK, (GUID *) &KSCATEGORY_AUDIO, NULL, 0 } }, // Wave Out Bridge Pin (Renderer) { 0,0,0, NULL, { 0, NULL, 0, NULL, SIZEOF_ARRAY(PinDataRangePointersBridge), PinDataRangePointersBridge, KSPIN_DATAFLOW_OUT, KSPIN_COMMUNICATION_NONE, (GUID *) &KSCATEGORY_AUDIO, NULL, 0 } } }; /***************************************************************************** * TopologyNodes ***************************************************************************** * List of nodes. */ static PCNODE_DESCRIPTOR MiniportNodes[] = { { 0, // Flags NULL, // AutomationTable &KSNODETYPE_ADC, // Type NULL // Name }, { 0, // Flags NULL, // AutomationTable &KSNODETYPE_DAC, // Type NULL // Name } }; /***************************************************************************** * MiniportConnections ***************************************************************************** * List of connections. */ static PCCONNECTION_DESCRIPTOR MiniportConnections[] = { { PCFILTER_NODE, 1, 0, 1 }, // Bridge in to ADC. { 0, 0, PCFILTER_NODE, 0 }, // ADC to stream pin (capture). { PCFILTER_NODE, 2, 1, 1 }, // Stream in to DAC. { 1, 0, PCFILTER_NODE, 3 } // DAC to Bridge. }; /***************************************************************************** * MiniportFilterDescriptor ***************************************************************************** * Complete miniport description. */ static PCFILTER_DESCRIPTOR MiniportFilterDescriptor = { 0, // Version &AutomationFilter, // 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 - use the default categories (audio, render, capture) }; /***************************************************************************** * CMiniportWaveCyclicSB16::GetDescription() ***************************************************************************** * Gets the topology. */ STDMETHODIMP CMiniportWaveCyclicSB16:: GetDescription ( OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor ) { PAGED_CODE(); ASSERT(OutFilterDescriptor); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::GetDescription]")); *OutFilterDescriptor = &MiniportFilterDescriptor; return STATUS_SUCCESS; } /***************************************************************************** * CMiniportWaveCyclicSB16::DataRangeIntersection() ***************************************************************************** * Tests a data range intersection. */ STDMETHODIMP CMiniportWaveCyclicSB16:: DataRangeIntersection ( IN ULONG PinId, IN PKSDATARANGE ClientDataRange, IN PKSDATARANGE MyDataRange, IN ULONG OutputBufferLength, OUT PVOID ResultantFormat, OUT PULONG ResultantFormatLength ) { PAGED_CODE(); BOOLEAN DigitalAudio; NTSTATUS Status; ULONG RequiredSize; ULONG SampleFrequency; USHORT BitsPerSample; // // Let's do the complete work here. // if (!IsEqualGUIDAligned(ClientDataRange->Specifier,KSDATAFORMAT_SPECIFIER_NONE)) { // // The miniport did not resolve this format. If the dataformat // is not PCM audio and requires a specifier, bail out. // if ( !IsEqualGUIDAligned(ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) || !IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_PCM )) { return STATUS_INVALID_PARAMETER; } DigitalAudio = TRUE; // // weird enough, the specifier here does not define the format of ClientDataRange // but the format that is expected to be returned in ResultantFormat. // if (IsEqualGUIDAligned(ClientDataRange->Specifier,KSDATAFORMAT_SPECIFIER_DSOUND)) { RequiredSize = sizeof(KSDATAFORMAT_DSOUND); } else { RequiredSize = sizeof(KSDATAFORMAT_WAVEFORMATEX); } } else { DigitalAudio = FALSE; RequiredSize = sizeof(KSDATAFORMAT); } // // Validate return buffer size, if the request is only for the // size of the resultant structure, return it now. // if (!OutputBufferLength) { *ResultantFormatLength = RequiredSize; return STATUS_BUFFER_OVERFLOW; } else if (OutputBufferLength < RequiredSize) { return STATUS_BUFFER_TOO_SMALL; } // There was a specifier ... if (DigitalAudio) { PKSDATARANGE_AUDIO AudioRange; PWAVEFORMATEX WaveFormatEx; AudioRange = (PKSDATARANGE_AUDIO) MyDataRange; // Fill the structure if (IsEqualGUIDAligned(ClientDataRange->Specifier,KSDATAFORMAT_SPECIFIER_DSOUND)) { PKSDATAFORMAT_DSOUND DSoundFormat; DSoundFormat = (PKSDATAFORMAT_DSOUND) ResultantFormat; _DbgPrintF(DEBUGLVL_VERBOSE,("returning KSDATAFORMAT_DSOUND format intersection")); DSoundFormat->BufferDesc.Flags = 0 ; DSoundFormat->BufferDesc.Control = 0 ; DSoundFormat->DataFormat = *ClientDataRange; DSoundFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_DSOUND; DSoundFormat->DataFormat.FormatSize = RequiredSize; WaveFormatEx = &DSoundFormat->BufferDesc.WaveFormatEx; *ResultantFormatLength = RequiredSize; } else { PKSDATAFORMAT_WAVEFORMATEX WaveFormat; WaveFormat = (PKSDATAFORMAT_WAVEFORMATEX) ResultantFormat; _DbgPrintF(DEBUGLVL_VERBOSE,("returning KSDATAFORMAT_WAVEFORMATEX format intersection") ); WaveFormat->DataFormat = *ClientDataRange; WaveFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; WaveFormat->DataFormat.FormatSize = RequiredSize; WaveFormatEx = &WaveFormat->WaveFormatEx; *ResultantFormatLength = RequiredSize; } // // Return a format that intersects the given audio range, // using our maximum support as the "best" format. // WaveFormatEx->wFormatTag = WAVE_FORMAT_PCM; WaveFormatEx->nChannels = (USHORT) min( AudioRange->MaximumChannels, ((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumChannels ); // // Check if the pin is still free // if (!PinId) { if (AllocatedCapture) { return STATUS_NO_MATCH; } } else { if (AllocatedRender) { return STATUS_NO_MATCH; } } // // Check if one pin is in use -> use same sample frequency. // if (AllocatedCapture || AllocatedRender) { SampleFrequency = SamplingFrequency; if ( (SampleFrequency > ((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumSampleFrequency) || (SampleFrequency < ((PKSDATARANGE_AUDIO) ClientDataRange)->MinimumSampleFrequency)) { return STATUS_NO_MATCH; } } else { SampleFrequency = min( AudioRange->MaximumSampleFrequency, ((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumSampleFrequency ); } WaveFormatEx->nSamplesPerSec = SampleFrequency; // // Check if one pin is in use -> use other bits per sample. // if (AllocatedCapture || AllocatedRender) { if (Allocated8Bit) { BitsPerSample = 16; } else { BitsPerSample = 8; } if ((BitsPerSample > ((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumBitsPerSample) || (BitsPerSample < ((PKSDATARANGE_AUDIO) ClientDataRange)->MinimumBitsPerSample)) { return STATUS_NO_MATCH; } } else { BitsPerSample = (USHORT) min( AudioRange->MaximumBitsPerSample, ((PKSDATARANGE_AUDIO) ClientDataRange)->MaximumBitsPerSample ); } WaveFormatEx->wBitsPerSample = BitsPerSample; WaveFormatEx->nBlockAlign = (WaveFormatEx->wBitsPerSample * WaveFormatEx->nChannels) / 8; WaveFormatEx->nAvgBytesPerSec = (WaveFormatEx->nSamplesPerSec * WaveFormatEx->nBlockAlign); WaveFormatEx->cbSize = 0; ((PKSDATAFORMAT) ResultantFormat)->SampleSize = WaveFormatEx->nBlockAlign; _DbgPrintF(DEBUGLVL_VERBOSE,("Channels = %d", WaveFormatEx->nChannels) ); _DbgPrintF(DEBUGLVL_VERBOSE,("Samples/sec = %d", WaveFormatEx->nSamplesPerSec) ); _DbgPrintF(DEBUGLVL_VERBOSE,("Bits/sample = %d", WaveFormatEx->wBitsPerSample) ); } else { // There was no specifier. Return only the KSDATAFORMAT structure. // // Copy the data format structure. // _DbgPrintF(DEBUGLVL_VERBOSE,("returning default format intersection") ); RtlCopyMemory(ResultantFormat, ClientDataRange, sizeof( KSDATAFORMAT ) ); *ResultantFormatLength = sizeof( KSDATAFORMAT ); } return STATUS_SUCCESS; } /***************************************************************************** * CMiniportWaveCyclicSB16::NewStream() ***************************************************************************** * Creates a new stream. This function is called when a streaming pin is * created. */ STDMETHODIMP CMiniportWaveCyclicSB16:: NewStream ( OUT PMINIPORTWAVECYCLICSTREAM * OutStream, IN PUNKNOWN OuterUnknown, IN POOL_TYPE PoolType, IN ULONG Channel, IN BOOLEAN Capture, IN PKSDATAFORMAT DataFormat, OUT PDMACHANNEL * OutDmaChannel, OUT PSERVICEGROUP * OutServiceGroup ) { PAGED_CODE(); ASSERT(OutStream); ASSERT(DataFormat); ASSERT(OutDmaChannel); ASSERT(OutServiceGroup); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicSB16::NewStream]")); NTSTATUS ntStatus = STATUS_SUCCESS; // // Make sure the hardware is not already in use. // if (Capture) { if (AllocatedCapture) { ntStatus = STATUS_INVALID_DEVICE_REQUEST; } } else { if (AllocatedRender) { ntStatus = STATUS_INVALID_DEVICE_REQUEST; } } // // Determine if the format is valid. // if (NT_SUCCESS(ntStatus)) { ntStatus = ValidateFormat(DataFormat); } if(NT_SUCCESS(ntStatus)) { // if we're trying to start a full-duplex stream. if(AllocatedCapture || AllocatedRender) { // make sure the requested sampling rate is the // same as the currently running one... PWAVEFORMATEX waveFormat = PWAVEFORMATEX(DataFormat + 1); if( SamplingFrequency != waveFormat->nSamplesPerSec ) { // Bad format.... ntStatus = STATUS_INVALID_PARAMETER; } } } PDMACHANNELSLAVE dmaChannel = NULL; PWAVEFORMATEX waveFormat = PWAVEFORMATEX(DataFormat + 1); // // Get the required DMA channel if it's not already in use. // if (NT_SUCCESS(ntStatus)) { if (waveFormat->wBitsPerSample == 8) { if (! Allocated8Bit) { dmaChannel = DmaChannel8; } } else { if (! Allocated16Bit) { dmaChannel = DmaChannel16; } } } if (! dmaChannel) { ntStatus = STATUS_INVALID_DEVICE_REQUEST; } else { // // Instantiate a stream. // CMiniportWaveCyclicStreamSB16 *stream = new(PoolType) CMiniportWaveCyclicStreamSB16(OuterUnknown); if (stream) { stream->AddRef(); ntStatus = stream->Init ( this, Channel, Capture, DataFormat, dmaChannel ); if (NT_SUCCESS(ntStatus)) { if (Capture) { AllocatedCapture = TRUE; } else { AllocatedRender = TRUE; } if (waveFormat->wBitsPerSample == 8) { Allocated8Bit = TRUE; } else { Allocated16Bit = TRUE; } *OutStream = PMINIPORTWAVECYCLICSTREAM(stream); stream->AddRef(); #if OVERRIDE_DMA_CHANNEL *OutDmaChannel = PDMACHANNEL(stream); stream->AddRef(); #else // OVERRIDE_DMA_CHANNEL *OutDmaChannel = dmaChannel; dmaChannel->AddRef(); #endif // OVERRIDE_DMA_CHANNEL *OutServiceGroup = ServiceGroup; ServiceGroup->AddRef(); // // The stream, the DMA channel, and the service group have // references now for the caller. The caller expects these // references to be there. // } // // This is our private reference to the stream. The caller has // its own, so we can release in any case. // stream->Release(); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } return ntStatus; } /***************************************************************************** * CMiniportWaveCyclic::RestoreSampleRate() ***************************************************************************** * Restores the sample rate. */ STDMETHODIMP_(void) CMiniportWaveCyclicSB16::RestoreSampleRate (void) { if (AllocatedCapture) { _DbgPrintF(DEBUGLVL_VERBOSE, ("Restoring Capture Sample Rate")); AdapterCommon->WriteController(DSP_CMD_SETADCRATE); AdapterCommon->WriteController((BYTE)(SamplingFrequency >> 8)); AdapterCommon->WriteController((BYTE) SamplingFrequency); } if (AllocatedRender) { _DbgPrintF(DEBUGLVL_VERBOSE, ("Restoring Render Sample Rate")); AdapterCommon->WriteController(DSP_CMD_SETDACRATE); AdapterCommon->WriteController((BYTE)(SamplingFrequency >> 8)); AdapterCommon->WriteController((BYTE) SamplingFrequency); } } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. This function works just like a COM QueryInterface * call and is used if the object is not being aggregated. */ STDMETHODIMP CMiniportWaveCyclicStreamSB16:: NonDelegatingQueryInterface ( IN REFIID Interface, OUT PVOID * Object ) { PAGED_CODE(); ASSERT(Object); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::NonDelegatingQueryInterface]")); if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { *Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLICSTREAM(this))); } else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclicStream)) { *Object = PVOID(PMINIPORTWAVECYCLICSTREAM(this)); } else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream)) { *Object = (PVOID)(PDRMAUDIOSTREAM(this)); } #if OVERRIDE_DMA_CHANNEL else if (IsEqualGUIDAligned (Interface, IID_IDmaChannel)) { *Object = (PVOID)(PDMACHANNEL(this)); } #endif // OVERRIDE_DMA_CHANNEL else { *Object = NULL; } if (*Object) { PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::~CMiniportWaveCyclicStreamSB16() ***************************************************************************** * Destructor. */ CMiniportWaveCyclicStreamSB16:: ~CMiniportWaveCyclicStreamSB16 ( void ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::~CMiniportWaveCyclicStreamSB16]")); if (DmaChannel) { DmaChannel->Release(); } if (Miniport) { // // Clear allocation flags in the miniport. // if (Capture) { Miniport->AllocatedCapture = FALSE; } else { Miniport->AllocatedRender = FALSE; } if (Format16Bit) { Miniport->Allocated16Bit = FALSE; } else { Miniport->Allocated8Bit = FALSE; } Miniport->AdapterCommon->SaveMixerSettingsToRegistry(); Miniport->Release(); } } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::Init() ***************************************************************************** * Initializes a stream. */ NTSTATUS CMiniportWaveCyclicStreamSB16:: Init ( IN CMiniportWaveCyclicSB16 * Miniport_, IN ULONG Channel_, IN BOOLEAN Capture_, IN PKSDATAFORMAT DataFormat, IN PDMACHANNELSLAVE DmaChannel_ ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::Init]")); ASSERT(Miniport_); ASSERT(DataFormat); ASSERT(NT_SUCCESS(Miniport_->ValidateFormat(DataFormat))); ASSERT(DmaChannel_); PWAVEFORMATEX waveFormat = PWAVEFORMATEX(DataFormat + 1); // // We must add references because the caller will not do it for us. // Miniport = Miniport_; Miniport->AddRef(); DmaChannel = DmaChannel_; DmaChannel->AddRef(); Channel = Channel_; Capture = Capture_; FormatStereo = (waveFormat->nChannels == 2); Format16Bit = (waveFormat->wBitsPerSample == 16); State = KSSTATE_STOP; RestoreInputMixer = FALSE; KeWaitForSingleObject ( &Miniport->SampleRateSync, Executive, KernelMode, FALSE, NULL ); Miniport->SamplingFrequency = waveFormat->nSamplesPerSec; KeReleaseMutex(&Miniport->SampleRateSync,FALSE); return SetFormat( DataFormat ); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SetNotificationFreq() ***************************************************************************** * Sets the notification frequency. */ STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamSB16:: SetNotificationFreq ( IN ULONG Interval, OUT PULONG FramingSize ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::SetNotificationFreq]")); Miniport->NotificationInterval = Interval; // // This value needs to be sample block aligned for DMA to work correctly. // *FramingSize = (1 << (FormatStereo + Format16Bit)) * (Miniport->SamplingFrequency * Interval / 1000); return Miniport->NotificationInterval; } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SetFormat() ***************************************************************************** * Sets the wave format. */ STDMETHODIMP CMiniportWaveCyclicStreamSB16:: SetFormat ( IN PKSDATAFORMAT Format ) { PAGED_CODE(); ASSERT(Format); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::SetFormat]")); NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; if(State != KSSTATE_RUN) { ntStatus = Miniport->ValidateFormat(Format); PWAVEFORMATEX waveFormat = PWAVEFORMATEX(Format + 1); KeWaitForSingleObject ( &Miniport->SampleRateSync, Executive, KernelMode, FALSE, NULL ); // check for full-duplex stuff if( NT_SUCCESS(ntStatus) && Miniport->AllocatedCapture && Miniport->AllocatedRender ) { // no new formats.... bad... if( Miniport->SamplingFrequency != waveFormat->nSamplesPerSec ) { // Bad format.... ntStatus = STATUS_INVALID_PARAMETER; } } // TODO: Validate sample size. if (NT_SUCCESS(ntStatus)) { Miniport->SamplingFrequency = waveFormat->nSamplesPerSec; BYTE command = ( Capture ? DSP_CMD_SETADCRATE : DSP_CMD_SETDACRATE ); Miniport->AdapterCommon->WriteController ( command ); Miniport->AdapterCommon->WriteController ( (BYTE)(waveFormat->nSamplesPerSec >> 8) ); Miniport->AdapterCommon->WriteController ( (BYTE) waveFormat->nSamplesPerSec ); _DbgPrintF(DEBUGLVL_VERBOSE,(" SampleRate: %d",waveFormat->nSamplesPerSec)); } KeReleaseMutex(&Miniport->SampleRateSync,FALSE); } return ntStatus; } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SetState() ***************************************************************************** * Sets the state of the channel. */ STDMETHODIMP CMiniportWaveCyclicStreamSB16:: SetState ( IN KSSTATE NewState ) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::SetState %x]", NewState)); NTSTATUS ntStatus = STATUS_SUCCESS; // // The acquire state is not distinguishable from the pause state for our // purposes. // if (NewState == KSSTATE_ACQUIRE) { NewState = KSSTATE_PAUSE; } if (State != NewState) { switch (NewState) { case KSSTATE_PAUSE: if (State == KSSTATE_RUN) { if (Capture) { // restore if previously setup for mono recording // (this should really be done via the topology miniport) if(RestoreInputMixer) { Miniport->AdapterCommon->MixerRegWrite( DSP_MIX_ADCMIXIDX_L, InputMixerLeft ); RestoreInputMixer = FALSE; } } // TODO: Wait for DMA to complete if (Format16Bit) { Miniport->AdapterCommon->WriteController(DSP_CMD_HALTAUTODMA16); // TODO: wait... Miniport->AdapterCommon->WriteController(DSP_CMD_PAUSEDMA16); } else { Miniport->AdapterCommon->WriteController(DSP_CMD_HALTAUTODMA); // TODO: wait... Miniport->AdapterCommon->WriteController(DSP_CMD_PAUSEDMA); } Miniport->AdapterCommon->WriteController(DSP_CMD_SPKROFF); DmaChannel->Stop(); } break; case KSSTATE_RUN: { BYTE mode; if (Capture) { // setup for mono recording // (this should really be done via the topology miniport) if(! FormatStereo) { InputMixerLeft = Miniport->AdapterCommon->MixerRegRead( DSP_MIX_ADCMIXIDX_L ); UCHAR InputMixerRight = Miniport->AdapterCommon->MixerRegRead( DSP_MIX_ADCMIXIDX_R ); UCHAR TempMixerValue = InputMixerLeft | (InputMixerRight & 0x2A); Miniport->AdapterCommon->MixerRegWrite( DSP_MIX_ADCMIXIDX_L, TempMixerValue ); RestoreInputMixer = TRUE; } // // Turn on capture. // Miniport->AdapterCommon->WriteController(DSP_CMD_SPKROFF); if (Format16Bit) { Miniport->AdapterCommon->WriteController(DSP_CMD_STARTADC16); mode = 0x10; } else { Miniport->AdapterCommon->WriteController(DSP_CMD_STARTADC8); mode = 0x00; } } else { Miniport->AdapterCommon->WriteController(DSP_CMD_SPKRON); if (Format16Bit) { Miniport->AdapterCommon->WriteController(DSP_CMD_STARTDAC16); mode = 0x10; } else { Miniport->AdapterCommon->WriteController(DSP_CMD_STARTDAC8); mode = 0x00; } } if (FormatStereo) { mode |= 0x20; } // // Start DMA. // DmaChannel->Start(DmaChannel->BufferSize(),!Capture); Miniport->AdapterCommon->WriteController(mode) ; // // Calculate sample count for interrupts. // ULONG bufferSizeInFrames = DmaChannel->BufferSize(); if( Format16Bit ) { bufferSizeInFrames /= 2; } if( FormatStereo ) { bufferSizeInFrames /= 2; } ULONG frameCount = ((Miniport->SamplingFrequency * Miniport->NotificationInterval) / 1000); if (frameCount > bufferSizeInFrames) { frameCount = bufferSizeInFrames; } frameCount--; _DbgPrintF( DEBUGLVL_VERBOSE, ("Run. Setting frame count to %X",frameCount)); Miniport->AdapterCommon->WriteController((BYTE) frameCount) ; Miniport->AdapterCommon->WriteController((BYTE) (frameCount >> 8)); } break; case KSSTATE_STOP: break; } State = NewState; } return ntStatus; } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SetContentId ***************************************************************************** * This routine gets called by drmk.sys to pass the content to the driver. * The driver has to enforce the rights passed. */ STDMETHODIMP_(NTSTATUS) CMiniportWaveCyclicStreamSB16::SetContentId ( IN ULONG contentId, IN PCDRMRIGHTS drmRights ) { PAGED_CODE (); _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::SetContentId]")); // // if (drmRights.CopyProtect==TRUE) // Mute waveout capture. // Sb16 does not have waveout capture path. Therefore // the sample driver is not using this attribute. // Also if the driver is writing data to other types of media (disk, etc), it // should stop doing it. // (MSVAD\simple stops writing to disk). // (AC97 disables waveout capture) // // if (drmRights.DigitalOutputDisable == TRUE) // Mute S/PDIF out. // If the device cannot mute S/PDIF out properly, it should return an error // code. // Sb16 does not have S/PDIF out. Therefore the sample driver is not using // this attribute. // // // To learn more about enforcing rights, please look at AC97 sample. // // To learn more about managing multiple streams, please look at MSVAD. // return STATUS_SUCCESS; } #pragma code_seg() /***************************************************************************** * CMiniportWaveCyclicStreamSB16::GetPosition() ***************************************************************************** * Gets the current position. May be called at dispatch level. */ STDMETHODIMP CMiniportWaveCyclicStreamSB16:: GetPosition ( OUT PULONG Position ) { ASSERT(Position); ULONG transferCount = DmaChannel->TransferCount(); if (DmaChannel && transferCount) { *Position = DmaChannel->ReadCounter(); ASSERT(*Position <= transferCount); if (*Position != 0) { *Position = transferCount - *Position; } } else { *Position = 0; } return STATUS_SUCCESS; } STDMETHODIMP CMiniportWaveCyclicStreamSB16::NormalizePhysicalPosition( IN OUT PLONGLONG PhysicalPosition ) /*++ Routine Description: Given a physical position based on the actual number of bytes transferred, this function converts the position to a time-based value of 100ns units. Arguments: IN OUT PLONGLONG PhysicalPosition - value to convert. Return: STATUS_SUCCESS or an appropriate error code. --*/ { *PhysicalPosition = (_100NS_UNITS_PER_SECOND / (1 << (FormatStereo + Format16Bit)) * *PhysicalPosition) / Miniport->SamplingFrequency; return STATUS_SUCCESS; } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::Silence() ***************************************************************************** * Fills a buffer with silence. */ STDMETHODIMP_(void) CMiniportWaveCyclicStreamSB16:: Silence ( IN PVOID Buffer, IN ULONG ByteCount ) { RtlFillMemory(Buffer,ByteCount,Format16Bit ? 0 : 0x80); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::ServiceWaveISR() ***************************************************************************** * Service the ISR - notify the port. */ STDMETHODIMP_(void) CMiniportWaveCyclicSB16::ServiceWaveISR (void) { if (Port && ServiceGroup) { Port->Notify (ServiceGroup); } } #if OVERRIDE_DMA_CHANNEL #pragma code_seg("PAGE") /***************************************************************************** * CMiniportWaveCyclicStreamSB16::AllocateBuffer() ***************************************************************************** * Allocate a buffer for this DMA channel. */ STDMETHODIMP CMiniportWaveCyclicStreamSB16::AllocateBuffer ( IN ULONG BufferSize, IN PPHYSICAL_ADDRESS PhysicalAddressConstraint OPTIONAL ) { PAGED_CODE(); return DmaChannel->AllocateBuffer(BufferSize,PhysicalAddressConstraint); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::FreeBuffer() ***************************************************************************** * Free the buffer for this DMA channel. */ STDMETHODIMP_(void) CMiniportWaveCyclicStreamSB16::FreeBuffer(void) { PAGED_CODE(); DmaChannel->FreeBuffer(); } #pragma code_seg() /***************************************************************************** * CMiniportWaveCyclicStreamSB16::TransferCount() ***************************************************************************** * Return the amount of data to be transfered via DMA. */ STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamSB16::TransferCount(void) { return DmaChannel->TransferCount(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::MaximumBufferSize() ***************************************************************************** * Return the maximum size that can be allocated to this DMA buffer. */ STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamSB16::MaximumBufferSize(void) { return DmaChannel->MaximumBufferSize(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::AllocatedBufferSize() ***************************************************************************** * Return the original size allocated to this DMA buffer -- the maximum value * that can be sent to SetBufferSize(). */ STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamSB16::AllocatedBufferSize(void) { return DmaChannel->AllocatedBufferSize(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::BufferSize() ***************************************************************************** * Return the current size of the DMA buffer. */ STDMETHODIMP_(ULONG) CMiniportWaveCyclicStreamSB16::BufferSize(void) { return DmaChannel->BufferSize(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SetBufferSize() ***************************************************************************** * Change the size of the DMA buffer. This cannot exceed the initial * buffer size returned by AllocatedBufferSize(). */ STDMETHODIMP_(void) CMiniportWaveCyclicStreamSB16::SetBufferSize(IN ULONG BufferSize) { DmaChannel->SetBufferSize(BufferSize); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::SystemAddress() ***************************************************************************** * Return the virtual address of this DMA buffer. */ STDMETHODIMP_(PVOID) CMiniportWaveCyclicStreamSB16::SystemAddress(void) { return DmaChannel->SystemAddress(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::PhysicalAddress() ***************************************************************************** * Return the actual physical address of this DMA buffer. */ STDMETHODIMP_(PHYSICAL_ADDRESS) CMiniportWaveCyclicStreamSB16::PhysicalAddress(void) { return DmaChannel->PhysicalAddress(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::GetAdapterObject() ***************************************************************************** * Return the DMA adapter object (defined in wdm.h). */ STDMETHODIMP_(PADAPTER_OBJECT) CMiniportWaveCyclicStreamSB16::GetAdapterObject(void) { return DmaChannel->GetAdapterObject(); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::CopyTo() ***************************************************************************** * Copy data into the DMA buffer. If you need to modify data on render * (playback), modify this routine to taste. */ STDMETHODIMP_(void) CMiniportWaveCyclicStreamSB16::CopyTo ( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { DmaChannel->CopyTo(Destination, Source, ByteCount); } /***************************************************************************** * CMiniportWaveCyclicStreamSB16::CopyFrom() ***************************************************************************** * Copy data out of the DMA buffer. If you need to modify data on capture * (recording), modify this routine to taste. */ STDMETHODIMP_(void) CMiniportWaveCyclicStreamSB16::CopyFrom ( IN PVOID Destination, IN PVOID Source, IN ULONG ByteCount ) { DmaChannel->CopyFrom(Destination, Source, ByteCount); } #endif // OVERRIDE_DMA_CHANNEL