/*****************************************************************************
 * miniport.cpp - SB16 wave miniport implementation
 *****************************************************************************
 * Copyright (c) Microsoft Corporation 1997-1999.  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);
}

/*****************************************************************************
 * 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))
    {
        //
        // Get the port address.
        //
        PortBase =
            PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart);

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

    //
    // Release instantiated objects in case of failure.
    //
    if (! NT_SUCCESS(ntStatus))
    {
        if (DmaChannel8)
        {
            DmaChannel8->Release();
            DmaChannel8 = NULL;
        }
        if (DmaChannel16)
        {
            DmaChannel16->Release();
            DmaChannel16 = NULL;
        }
    }

    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(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 (Port)
    {
        Port->Release();
    }
    if (DmaChannel8)
    {
        DmaChannel8->Release();
    }
    if (DmaChannel16)
    {
        DmaChannel16->Release();
    }
    if (ServiceGroup)
    {
        ServiceGroup->Release();
    }
    if (AdapterCommon)
    {
        AdapterCommon->Release();
    }
}

/*****************************************************************************
 * 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();

    //
    // 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->SetWaveServiceGroup(ServiceGroup);
        }
    }

    if (NT_SUCCESS(ntStatus))
    {
        ntStatus = ProcessResources(ResourceList);
    }

    if( !NT_SUCCESS(ntStatus) )
    {
        //
        // clean up our mess
        //

        // clean up AdapterCommon
        if( AdapterCommon )
        {
            // clean up the service group
            if( ServiceGroup )
            {
                AdapterCommon->SetWaveServiceGroup(NULL);
                ServiceGroup->Release();
                ServiceGroup = NULL;
            }

            AdapterCommon->Release();
            AdapterCommon = NULL;
        }

        // release the port
        Port->Release();
        Port = NULL;
    }

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

/*****************************************************************************
 * 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    MatchingDataRange,
    IN      PKSDATARANGE    DataRange,
    IN      ULONG           OutputBufferLength,
    OUT     PVOID           ResultantFormat,
    OUT     PULONG          ResultantFormatLength
)
{
    BOOLEAN                         DigitalAudio;
    NTSTATUS                        Status;
    ULONG                           RequiredSize;
    ULONG                           SampleFrequency;
    USHORT                          BitsPerSample;
    
    //
    // Let's do the complete work here.
    //
    if (!IsEqualGUIDAligned( 
            MatchingDataRange->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( 
                MatchingDataRange->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) ||
            !IsEqualGUIDAligned(     
               MatchingDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_PCM )) {
            return STATUS_INVALID_PARAMETER;
        }
        DigitalAudio = TRUE;
        
        //
        // wired enough, the specifier here does not define the format of MatchingDataRange
        // but the format that is expected to be returned in ResultantFormat.
        //
        if (IsEqualGUIDAligned( 
                MatchingDataRange->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) DataRange;
        
        // Fill the structure
        if (IsEqualGUIDAligned( 
                MatchingDataRange->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 = *MatchingDataRange;
            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 = *MatchingDataRange;
            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) MatchingDataRange)->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) MatchingDataRange)->MaximumSampleFrequency) ||
                (SampleFrequency < ((PKSDATARANGE_AUDIO) MatchingDataRange)->MinimumSampleFrequency))
            {
                return STATUS_NO_MATCH;
            }
        }
        else
        {
            SampleFrequency = min( AudioRange->MaximumSampleFrequency,
                 ((PKSDATARANGE_AUDIO) MatchingDataRange)->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) MatchingDataRange)->MaximumBitsPerSample) ||
                (BitsPerSample < ((PKSDATARANGE_AUDIO) MatchingDataRange)->MinimumBitsPerSample))
            {
                return STATUS_NO_MATCH;
            }
        }
        else
        {
            BitsPerSample = (USHORT) min( AudioRange->MaximumBitsPerSample,
                          ((PKSDATARANGE_AUDIO) MatchingDataRange)->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, MatchingDataRange, 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();

                *OutDmaChannel = dmaChannel;
                dmaChannel->AddRef();

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

/*****************************************************************************
 * 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(this));
    }
    else
    if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclicStream))
    {
        *Object = PVOID(PMINIPORTWAVECYCLICSTREAM(this));
    }
    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);
    
    SetFormat( DataFormat );

    return STATUS_SUCCESS;
}

/*****************************************************************************
 * 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;
    *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))
        {
            PWAVEFORMATEX waveFormat = PWAVEFORMATEX(Format + 1);

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

#pragma code_seg()

/*****************************************************************************
 * CMiniportWaveCyclicStreamSB16::GetPosition()
 *****************************************************************************
 * Gets the current position.
 */
STDMETHODIMP
CMiniportWaveCyclicStreamSB16::
GetPosition
(
    OUT     PULONG  Position
)
{
    // Not PAGED_CODE().  May be called at dispatch level.

    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;
}
    
#pragma code_seg("PAGE")

/*****************************************************************************
 * CMiniportWaveCyclicStreamSB16::SetState()
 *****************************************************************************
 * Sets the state of the channel.
 */
STDMETHODIMP
CMiniportWaveCyclicStreamSB16::
SetState
(
    IN      KSSTATE     NewState
)
{
    PAGED_CODE();

    _DbgPrintF(DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStreamSB16::SetState]"));

    NTSTATUS ntStatus = STATUS_SUCCESS;

    //
    // The acquire state is not distinguishable from the stop 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);
                }

                if ( !(Miniport->AllocatedRender &&
                       Miniport->AllocatedCapture))
                {
                    Miniport->AdapterCommon->ResetController();
                }

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

#pragma code_seg()

/*****************************************************************************
 * CMiniportWaveCyclicStreamSB16::Silence()
 *****************************************************************************
 * Fills a buffer with silence.
 */
STDMETHODIMP_(void)
CMiniportWaveCyclicStreamSB16::
Silence
(
    IN      PVOID   Buffer,
    IN      ULONG   ByteCount
)
{
    RtlFillMemory(Buffer,ByteCount,Format16Bit ? 0 : 0x7f);
}
