1930 lines
54 KiB
C++
1930 lines
54 KiB
C++
/*****************************************************************************
|
|
* 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
|