windows-nt/Source/XPSP1/NT/drivers/ddk/wdmaudio/dmusuart/miniport.cpp

1744 lines
53 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************
* miniport.cpp - UART miniport implementation
*****************************************************************************
* Copyright (c) 1997-2000 Microsoft Corporation. All Rights Reserved.
*
* Feb 98 MartinP -- based on UART, began deltas for DirectMusic.
*/
#include "private.h"
#include "ksdebug.h"
#include "stdio.h"
#define STR_MODULENAME "DMusUART:Miniport: "
#pragma code_seg("PAGE")
/*****************************************************************************
* PinDataRangesStreamLegacy
* PinDataRangesStreamDMusic
*****************************************************************************
* Structures indicating range of valid format values for live pins.
*/
static
KSDATARANGE_MUSIC PinDataRangesStreamLegacy =
{
{
sizeof(KSDATARANGE_MUSIC),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
},
STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT),
0,
0,
0xFFFF
};
static
KSDATARANGE_MUSIC PinDataRangesStreamDMusic =
{
{
sizeof(KSDATARANGE_MUSIC),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_DIRECTMUSIC),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
},
STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT),
0,
0,
0xFFFF
};
/*****************************************************************************
* PinDataRangePointersStreamLegacy
* PinDataRangePointersStreamDMusic
* PinDataRangePointersStreamCombined
*****************************************************************************
* List of pointers to structures indicating range of valid format values
* for live pins.
*/
static
PKSDATARANGE PinDataRangePointersStreamLegacy[] =
{
PKSDATARANGE(&PinDataRangesStreamLegacy)
};
static
PKSDATARANGE PinDataRangePointersStreamDMusic[] =
{
PKSDATARANGE(&PinDataRangesStreamDMusic)
};
static
PKSDATARANGE PinDataRangePointersStreamCombined[] =
{
PKSDATARANGE(&PinDataRangesStreamLegacy)
,PKSDATARANGE(&PinDataRangesStreamDMusic)
};
/*****************************************************************************
* PinDataRangesBridge
*****************************************************************************
* Structures indicating range of valid format values for bridge pins.
*/
static
KSDATARANGE PinDataRangesBridge[] =
{
{
sizeof(KSDATARANGE),
0,
0,
0,
STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI_BUS),
STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
}
};
/*****************************************************************************
* PinDataRangePointersBridge
*****************************************************************************
* List of pointers to structures indicating range of valid format values
* for bridge pins.
*/
static
PKSDATARANGE PinDataRangePointersBridge[] =
{
&PinDataRangesBridge[0]
};
/*****************************************************************************
* SynthProperties
*****************************************************************************
* List of properties in the Synth set.
*/
static
PCPROPERTY_ITEM
SynthProperties[] =
{
// Global: S/Get synthesizer caps
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_CAPS,
KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
PropertyHandler_Synth
},
// Global: S/Get port parameters
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_PORTPARAMETERS,
KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
PropertyHandler_Synth
},
// Per stream: S/Get channel groups
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_CHANNELGROUPS,
KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
PropertyHandler_Synth
},
// Per stream: Get current latency time
{
&KSPROPSETID_Synth,
KSPROPERTY_SYNTH_LATENCYCLOCK,
KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
PropertyHandler_Synth
}
};
DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth, SynthProperties);
DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth2, SynthProperties);
#define kMaxNumCaptureStreams 1
#define kMaxNumLegacyRenderStreams 1
#define kMaxNumDMusicRenderStreams 1
/*****************************************************************************
* MiniportPins
*****************************************************************************
* List of pins.
*/
static
PCPIN_DESCRIPTOR MiniportPins[] =
{
{
kMaxNumLegacyRenderStreams,kMaxNumLegacyRenderStreams,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersStreamLegacy), // DataRangesCount
PinDataRangePointersStreamLegacy, // DataRanges
KSPIN_DATAFLOW_IN, // DataFlow
KSPIN_COMMUNICATION_SINK, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
&KSAUDFNAME_MIDI, // Name
0 // Reserved
}
},
{
kMaxNumDMusicRenderStreams,kMaxNumDMusicRenderStreams,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersStreamDMusic), // DataRangesCount
PinDataRangePointersStreamDMusic, // DataRanges
KSPIN_DATAFLOW_IN, // DataFlow
KSPIN_COMMUNICATION_SINK, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
&KSAUDFNAME_DMUSIC_MPU_OUT, // Name
0 // Reserved
}
},
{
0,0,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount
PinDataRangePointersBridge, // DataRanges
KSPIN_DATAFLOW_OUT, // DataFlow
KSPIN_COMMUNICATION_NONE, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
}
},
{
0,0,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount
PinDataRangePointersBridge, // DataRanges
KSPIN_DATAFLOW_IN, // DataFlow
KSPIN_COMMUNICATION_NONE, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
NULL, // Name
0 // Reserved
}
},
{
kMaxNumCaptureStreams,kMaxNumCaptureStreams,0, // InstanceCount
NULL, // AutomationTable
{ // KsPinDescriptor
0, // InterfacesCount
NULL, // Interfaces
0, // MediumsCount
NULL, // Mediums
SIZEOF_ARRAY(PinDataRangePointersStreamCombined), // DataRangesCount
PinDataRangePointersStreamCombined, // DataRanges
KSPIN_DATAFLOW_OUT, // DataFlow
KSPIN_COMMUNICATION_SINK, // Communication
(GUID *) &KSCATEGORY_AUDIO, // Category
&KSAUDFNAME_DMUSIC_MPU_IN, // Name
0 // Reserved
}
}
};
/*****************************************************************************
* MiniportNodes
*****************************************************************************
* List of nodes.
*/
#define CONST_PCNODE_DESCRIPTOR(n) { 0, NULL, &n, NULL }
#define CONST_PCNODE_DESCRIPTOR_AUTO(n,a) { 0, &a, &n, NULL }
static
PCNODE_DESCRIPTOR MiniportNodes[] =
{
CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth)
, CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth2)
};
/*****************************************************************************
* MiniportConnections
*****************************************************************************
* List of connections.
*/
enum {
eSynthNode = 0
, eInputNode
};
enum {
eFilterInputPinLeg = 0,
eFilterInputPinDM,
eBridgeOutputPin,
eBridgeInputPin,
eFilterOutputPin
};
static
PCCONNECTION_DESCRIPTOR MiniportConnections[] =
{ // From To
// Node pin Node pin
{ PCFILTER_NODE, eFilterInputPinLeg, PCFILTER_NODE, eBridgeOutputPin } // Legacy Stream in to synth.
, { PCFILTER_NODE, eFilterInputPinDM, eSynthNode, KSNODEPIN_STANDARD_IN } // DM Stream in to synth.
, { eSynthNode, KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eBridgeOutputPin } // Synth to bridge out.
, { PCFILTER_NODE, eBridgeInputPin, eInputNode, KSNODEPIN_STANDARD_IN } // Bridge in to input.
, { eInputNode, KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eFilterOutputPin } // Input to DM/Legacy Stream out.
};
/*****************************************************************************
* MiniportCategories
*****************************************************************************
* List of categories.
*/
static
GUID MiniportCategories[] =
{
STATICGUIDOF(KSCATEGORY_AUDIO),
STATICGUIDOF(KSCATEGORY_RENDER),
STATICGUIDOF(KSCATEGORY_CAPTURE)
};
/*****************************************************************************
* MiniportFilterDescriptor
*****************************************************************************
* Complete miniport filter 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
SIZEOF_ARRAY(MiniportCategories), // CategoryCount
MiniportCategories // Categories
};
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::GetDescription()
*****************************************************************************
* Gets the topology.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUART::
GetDescription
(
OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
)
{
PAGED_CODE();
ASSERT(OutFilterDescriptor);
_DbgPrintF(DEBUGLVL_BLAB,("GetDescription"));
*OutFilterDescriptor = &MiniportFilterDescriptor;
return STATUS_SUCCESS;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CreateMiniportDMusUART()
*****************************************************************************
* Creates a MPU-401 miniport driver for the adapter. This uses a
* macro from STDUNK.H to do all the work.
*/
NTSTATUS
CreateMiniportDMusUART
(
OUT PUNKNOWN * Unknown,
IN REFCLSID,
IN PUNKNOWN UnknownOuter OPTIONAL,
IN POOL_TYPE PoolType
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("CreateMiniportDMusUART"));
ASSERT(Unknown);
STD_CREATE_BODY_( CMiniportDMusUART,
Unknown,
UnknownOuter,
PoolType,
PMINIPORTDMUS);
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::ProcessResources()
*****************************************************************************
* Processes the resource list, setting up helper objects accordingly.
*/
NTSTATUS
CMiniportDMusUART::
ProcessResources
(
IN PRESOURCELIST ResourceList
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("ProcessResources"));
ASSERT(ResourceList);
if (!ResourceList)
{
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
//
// Get counts for the types of resources.
//
ULONG countIO = ResourceList->NumberOfPorts();
ULONG countIRQ = ResourceList->NumberOfInterrupts();
ULONG countDMA = ResourceList->NumberOfDmas();
ULONG lengthIO = ResourceList->FindTranslatedPort(0)->u.Port.Length;
#if (DBG)
_DbgPrintF(DEBUGLVL_VERBOSE,("Starting MPU401 Port 0x%X",
ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart) );
#endif
NTSTATUS ntStatus = STATUS_SUCCESS;
//
// Make sure we have the expected number of resources.
//
if ( (countIO != 1)
|| (countIRQ > 1)
|| (countDMA != 0)
|| (lengthIO == 0)
)
{
_DbgPrintF(DEBUGLVL_TERSE,("Unknown ResourceList configuraton"));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
}
if (NT_SUCCESS(ntStatus))
{
//
// Get the port address.
//
m_pPortBase =
PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart);
ntStatus = InitializeHardware(m_pInterruptSync,m_pPortBase);
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::NonDelegatingQueryInterface()
*****************************************************************************
* Obtains an interface. This function works just like a COM QueryInterface
* call and is used if the object is not being aggregated.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUART::
NonDelegatingQueryInterface
(
REFIID Interface,
PVOID * Object
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("Miniport::NonDelegatingQueryInterface"));
ASSERT(Object);
if (IsEqualGUIDAligned(Interface,IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(PMINIPORTDMUS(this)));
}
else
if (IsEqualGUIDAligned(Interface,IID_IMiniport))
{
*Object = PVOID(PMINIPORT(this));
}
else
if (IsEqualGUIDAligned(Interface,IID_IMiniportDMus))
{
*Object = PVOID(PMINIPORTDMUS(this));
}
else
if (IsEqualGUIDAligned(Interface,IID_IMusicTechnology))
{
*Object = PVOID(PMUSICTECHNOLOGY(this));
}
else
if (IsEqualGUIDAligned(Interface,IID_IPowerNotify))
{
*Object = PVOID(PPOWERNOTIFY(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
//
// We reference the interface for the caller.
//
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::~CMiniportDMusUART()
*****************************************************************************
* Destructor.
*/
CMiniportDMusUART::~CMiniportDMusUART(void)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("~CMiniportDMusUART"));
ASSERT(0 == m_NumCaptureStreams);
ASSERT(0 == m_NumRenderStreams);
// reset the HW so we don't get anymore interrupts
if (m_UseIRQ && m_pInterruptSync)
{
(void) m_pInterruptSync->CallSynchronizedRoutine(InitMPU,PVOID(m_pPortBase));
}
else
{
(void) InitMPU(NULL,PVOID(m_pPortBase));
}
if (m_pInterruptSync)
{
m_pInterruptSync->Release();
m_pInterruptSync = NULL;
}
if (m_pServiceGroup)
{
m_pServiceGroup->Release();
m_pServiceGroup = NULL;
}
if (m_pPort)
{
m_pPort->Release();
m_pPort = NULL;
}
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::Init()
*****************************************************************************
* Initializes a the miniport.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUART::
Init
(
IN PUNKNOWN UnknownInterruptSync OPTIONAL,
IN PRESOURCELIST ResourceList,
IN PPORTDMUS Port_,
OUT PSERVICEGROUP * ServiceGroup
)
{
PAGED_CODE();
ASSERT(ResourceList);
if (!ResourceList)
{
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
ASSERT(Port_);
ASSERT(ServiceGroup);
_DbgPrintF(DEBUGLVL_BLAB,("Init"));
*ServiceGroup = NULL;
m_pPortBase = 0;
m_fMPUInitialized = FALSE;
// This will remain unspecified if the miniport does not get any power
// messages.
//
m_PowerState.DeviceState = PowerDeviceUnspecified;
//
// AddRef() is required because we are keeping this pointer.
//
m_pPort = Port_;
m_pPort->AddRef();
// Set dataformat.
//
if (IsEqualGUIDAligned(m_MusicFormatTechnology, GUID_NULL))
{
RtlCopyMemory( &m_MusicFormatTechnology,
&KSMUSIC_TECHNOLOGY_PORT,
sizeof(GUID));
}
RtlCopyMemory( &PinDataRangesStreamLegacy.Technology,
&m_MusicFormatTechnology,
sizeof(GUID));
RtlCopyMemory( &PinDataRangesStreamDMusic.Technology,
&m_MusicFormatTechnology,
sizeof(GUID));
for (ULONG bufferCount = 0;bufferCount < kMPUInputBufferSize;bufferCount++)
{
m_MPUInputBuffer[bufferCount] = 0;
}
m_MPUInputBufferHead = 0;
m_MPUInputBufferTail = 0;
m_InputTimeStamp = 0;
m_KSStateInput = KSSTATE_STOP;
NTSTATUS ntStatus = STATUS_SUCCESS;
m_NumRenderStreams = 0;
m_NumCaptureStreams = 0;
m_UseIRQ = TRUE;
if (ResourceList->NumberOfInterrupts() == 0)
{
m_UseIRQ = FALSE;
}
ntStatus = PcNewServiceGroup(&m_pServiceGroup,NULL);
if (NT_SUCCESS(ntStatus) && !m_pServiceGroup) // keep any error
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus))
{
*ServiceGroup = m_pServiceGroup;
m_pServiceGroup->AddRef();
//
// Register the service group with the port early so the port is
// prepared to handle interrupts.
//
m_pPort->RegisterServiceGroup(m_pServiceGroup);
}
if (NT_SUCCESS(ntStatus) && m_UseIRQ)
{
//
// Due to a bug in the InterruptSync design, we shouldn't share
// the interrupt sync object. Whoever goes away first
// will disconnect it, and the other points off into nowhere.
//
// Instead we generate our own interrupt sync object.
//
UnknownInterruptSync = NULL;
if (UnknownInterruptSync)
{
ntStatus =
UnknownInterruptSync->QueryInterface
(
IID_IInterruptSync,
(PVOID *) &m_pInterruptSync
);
if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus))
{ // run this ISR first
ntStatus = m_pInterruptSync->
RegisterServiceRoutine(DMusMPUInterruptServiceRoutine,PVOID(this),TRUE);
}
}
else
{ // create our own interruptsync mechanism.
ntStatus =
PcNewInterruptSync
(
&m_pInterruptSync,
NULL,
ResourceList,
0, // Resource Index
InterruptSyncModeNormal // Run ISRs once until we get SUCCESS
);
if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus))
{
ntStatus = m_pInterruptSync->RegisterServiceRoutine(
DMusMPUInterruptServiceRoutine,
PVOID(this),
TRUE); // run this ISR first
}
if (NT_SUCCESS(ntStatus))
{
ntStatus = m_pInterruptSync->Connect();
}
}
}
if (NT_SUCCESS(ntStatus))
{
ntStatus = ProcessResources(ResourceList);
}
if (!NT_SUCCESS(ntStatus))
{
//
// clean up our mess
//
// clean up the interrupt sync
if( m_pInterruptSync )
{
m_pInterruptSync->Release();
m_pInterruptSync = NULL;
}
// clean up the service group
if( m_pServiceGroup )
{
m_pServiceGroup->Release();
m_pServiceGroup = NULL;
}
// clean up the out param service group.
if (*ServiceGroup)
{
(*ServiceGroup)->Release();
(*ServiceGroup) = NULL;
}
// release the port
m_pPort->Release();
m_pPort = NULL;
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::NewStream()
*****************************************************************************
* Gets the topology.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUART::
NewStream
(
OUT PMXF * MXF,
IN PUNKNOWN OuterUnknown OPTIONAL,
IN POOL_TYPE PoolType,
IN ULONG PinID,
IN DMUS_STREAM_TYPE StreamType,
IN PKSDATAFORMAT DataFormat,
OUT PSERVICEGROUP * ServiceGroup,
IN PAllocatorMXF AllocatorMXF,
IN PMASTERCLOCK MasterClock,
OUT PULONGLONG SchedulePreFetch
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB, ("NewStream"));
NTSTATUS ntStatus = STATUS_SUCCESS;
// In 100 ns, we want stuff as soon as it comes in
//
*SchedulePreFetch = 0;
// if we don't have any streams already open, get the hardware ready.
if ((!m_NumCaptureStreams) && (!m_NumRenderStreams))
{
ntStatus = ResetHardware(m_pPortBase);
if (!NT_SUCCESS(ntStatus))
{
_DbgPrintF(DEBUGLVL_TERSE, ("CMiniportDMusUART::NewStream ResetHardware failed"));
return ntStatus;
}
}
if ( ((m_NumCaptureStreams < kMaxNumCaptureStreams)
&& (StreamType == DMUS_STREAM_MIDI_CAPTURE))
|| ((m_NumRenderStreams < kMaxNumLegacyRenderStreams + kMaxNumDMusicRenderStreams)
&& (StreamType == DMUS_STREAM_MIDI_RENDER))
)
{
CMiniportDMusUARTStream *pStream =
new(PoolType) CMiniportDMusUARTStream(OuterUnknown);
if (pStream)
{
pStream->AddRef();
ntStatus =
pStream->Init(this,m_pPortBase,(StreamType == DMUS_STREAM_MIDI_CAPTURE),AllocatorMXF,MasterClock);
if (NT_SUCCESS(ntStatus))
{
*MXF = PMXF(pStream);
(*MXF)->AddRef();
if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
{
m_NumCaptureStreams++;
*ServiceGroup = m_pServiceGroup;
(*ServiceGroup)->AddRef();
}
else
{
m_NumRenderStreams++;
*ServiceGroup = NULL;
}
}
pStream->Release();
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
{
_DbgPrintF(DEBUGLVL_TERSE,("NewStream failed, too many capture streams"));
}
else if (StreamType == DMUS_STREAM_MIDI_RENDER)
{
_DbgPrintF(DEBUGLVL_TERSE,("NewStream failed, too many render streams"));
}
else
{
_DbgPrintF(DEBUGLVL_TERSE,("NewStream invalid stream type"));
}
}
return ntStatus;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUART::SetTechnology()
*****************************************************************************
* Sets pindatarange technology.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUART::
SetTechnology
(
IN const GUID * Technology
)
{
PAGED_CODE();
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
// Fail if miniport has already been initialized.
//
if (NULL == m_pPort)
{
RtlCopyMemory(&m_MusicFormatTechnology, Technology, sizeof(GUID));
ntStatus = STATUS_SUCCESS;
}
return ntStatus;
} // SetTechnology
/*****************************************************************************
* CMiniportDMusUART::PowerChangeNotify()
*****************************************************************************
* Handle power state change for the miniport.
*/
#pragma code_seg("PAGE")
STDMETHODIMP_(void)
CMiniportDMusUART::
PowerChangeNotify
(
IN POWER_STATE PowerState
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState));
switch (PowerState.DeviceState)
{
case PowerDeviceD0:
if (m_PowerState.DeviceState != PowerDeviceD0)
{
if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase)))
{
_DbgPrintF(DEBUGLVL_TERSE, ("InitializeHardware failed when resuming"));
}
}
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
default:
break;
}
m_PowerState.DeviceState = PowerState.DeviceState;
} // PowerChangeNotify
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::NonDelegatingQueryInterface()
*****************************************************************************
* Obtains an interface. This function works just like a COM QueryInterface
* call and is used if the object is not being aggregated.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUARTStream::
NonDelegatingQueryInterface
(
REFIID Interface,
PVOID * Object
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("Stream::NonDelegatingQueryInterface"));
ASSERT(Object);
if (IsEqualGUIDAligned(Interface,IID_IUnknown))
{
*Object = PVOID(PUNKNOWN(this));
}
else
if (IsEqualGUIDAligned(Interface,IID_IMXF))
{
*Object = PVOID(PMXF(this));
}
else
{
*Object = NULL;
}
if (*Object)
{
//
// We reference the interface for the caller.
//
PUNKNOWN(*Object)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::~CMiniportDMusUARTStream()
*****************************************************************************
* Destructs a stream.
*/
CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_BLAB,("~CMiniportDMusUARTStream"));
KeCancelTimer(&m_TimerEvent);
if (m_DMKEvtQueue)
{
if (m_AllocatorMXF)
{
m_AllocatorMXF->PutMessage(m_DMKEvtQueue);
}
else
{
_DbgPrintF(DEBUGLVL_ERROR,("~CMiniportDMusUARTStream, no allocator, can't flush DMKEvts"));
}
m_DMKEvtQueue = NULL;
}
if (m_AllocatorMXF)
{
m_AllocatorMXF->Release();
m_AllocatorMXF = NULL;
}
if (m_pMiniport)
{
if (m_fCapture)
{
m_pMiniport->m_NumCaptureStreams--;
}
else
{
m_pMiniport->m_NumRenderStreams--;
}
m_pMiniport->Release();
}
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::Init()
*****************************************************************************
* Initializes a stream.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUARTStream::
Init
(
IN CMiniportDMusUART * pMiniport,
IN PUCHAR pPortBase,
IN BOOLEAN fCapture,
IN PAllocatorMXF allocatorMXF,
IN PMASTERCLOCK masterClock
)
{
PAGED_CODE();
ASSERT(pMiniport);
ASSERT(pPortBase);
_DbgPrintF(DEBUGLVL_BLAB,("Init"));
m_NumFailedMPUTries = 0;
m_TimerQueued = FALSE;
KeInitializeSpinLock(&m_DpcSpinLock);
m_pMiniport = pMiniport;
m_pMiniport->AddRef();
pMiniport->m_MasterClock = masterClock;
m_pPortBase = pPortBase;
m_fCapture = fCapture;
m_SnapshotTimeStamp = 0;
m_DMKEvtQueue = NULL;
m_DMKEvtOffset = 0;
m_NumberOfRetries = 0;
if (allocatorMXF)
{
allocatorMXF->AddRef();
m_AllocatorMXF = allocatorMXF;
m_sinkMXF = m_AllocatorMXF;
}
else
{
return STATUS_INVALID_PARAMETER;
}
KeInitializeDpc
(
&m_Dpc,
&::DMusUARTTimerDPC,
PVOID(this)
);
KeInitializeTimer(&m_TimerEvent);
return STATUS_SUCCESS;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::SetState()
*****************************************************************************
* Sets the state of the channel.
*/
STDMETHODIMP_(NTSTATUS)
CMiniportDMusUARTStream::
SetState
(
IN KSSTATE NewState
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_VERBOSE,("SetState %d",NewState));
if (NewState == KSSTATE_RUN)
{
if (m_pMiniport->m_fMPUInitialized)
{
LARGE_INTEGER timeDue100ns;
timeDue100ns.QuadPart = 0;
KeSetTimer(&m_TimerEvent,timeDue100ns,&m_Dpc);
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("CMiniportDMusUARTStream::SetState KSSTATE_RUN failed due to uninitialized MPU"));
return STATUS_INVALID_DEVICE_STATE;
}
}
if (m_fCapture)
{
m_pMiniport->m_KSStateInput = NewState;
if (NewState == KSSTATE_STOP) // STOPping
{
m_pMiniport->m_MPUInputBufferHead = 0; // Previously read bytes are discarded.
m_pMiniport->m_MPUInputBufferTail = 0; // The entire FIFO is available.
}
}
return STATUS_SUCCESS;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUART::Service()
*****************************************************************************
* DPC-mode service call from the port.
*/
STDMETHODIMP_(void)
CMiniportDMusUART::
Service
( void
)
{
_DbgPrintF(DEBUGLVL_BLAB, ("Service"));
if (!m_NumCaptureStreams)
{
// we should never get here....
// if we do, we must have read some trash,
// so just reset the input FIFO
m_MPUInputBufferTail = m_MPUInputBufferHead = 0;
}
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::ConnectOutput()
*****************************************************************************
* Writes outgoing MIDI data.
*/
NTSTATUS
CMiniportDMusUARTStream::
ConnectOutput(PMXF sinkMXF)
{
PAGED_CODE();
if (m_fCapture)
{
if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF))
{
_DbgPrintF(DEBUGLVL_BLAB, ("ConnectOutput"));
m_sinkMXF = sinkMXF;
return STATUS_SUCCESS;
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("ConnectOutput failed"));
}
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("ConnectOutput called on renderer; failed"));
}
return STATUS_UNSUCCESSFUL;
}
#pragma code_seg("PAGE")
/*****************************************************************************
* CMiniportDMusUARTStream::DisconnectOutput()
*****************************************************************************
* Writes outgoing MIDI data.
*/
NTSTATUS
CMiniportDMusUARTStream::
DisconnectOutput(PMXF sinkMXF)
{
PAGED_CODE();
if (m_fCapture)
{
if ((m_sinkMXF == sinkMXF) || (!sinkMXF))
{
_DbgPrintF(DEBUGLVL_BLAB, ("DisconnectOutput"));
m_sinkMXF = m_AllocatorMXF;
return STATUS_SUCCESS;
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("DisconnectOutput failed"));
}
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("DisconnectOutput called on renderer; failed"));
}
return STATUS_UNSUCCESSFUL;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUARTStream::PutMessageLocked()
*****************************************************************************
* Now that the spinlock is held, add this message to the queue.
*
* Writes an outgoing MIDI message.
* We don't sort a new message into the queue -- we append it.
* This is fine, since the sequencer feeds us sequenced data.
* Timestamps will ascend by design.
*/
NTSTATUS CMiniportDMusUARTStream::PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDMUS_KERNEL_EVENT aDMKEvt;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
if (!m_fCapture)
{
_DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to render stream"));
if (pDMKEvt)
{
// m_DpcSpinLock already held
if (m_DMKEvtQueue)
{
aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue
while (aDMKEvt->pNextEvt)
{
aDMKEvt = aDMKEvt->pNextEvt;
}
aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue
}
else // currently nothing in queue
{
m_DMKEvtQueue = pDMKEvt;
if (m_DMKEvtOffset)
{
_DbgPrintF(DEBUGLVL_ERROR, ("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset));
m_DMKEvtOffset = 0;
}
}
// m_DpcSpinLock already held
}
if (!m_TimerQueued)
{
(void) ConsumeEvents();
}
}
else // capture
{
_DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to capture stream"));
ASSERT(NULL == pDMKEvt);
SourceEvtsToPort();
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUARTStream::PutMessage()
*****************************************************************************
* Writes an outgoing MIDI message.
* We don't sort a new message into the queue -- we append it.
* This is fine, since the sequencer feeds us sequenced data.
* Timestamps will ascend by design.
*/
NTSTATUS CMiniportDMusUARTStream::PutMessage(PDMUS_KERNEL_EVENT pDMKEvt)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDMUS_KERNEL_EVENT aDMKEvt;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
if (!m_fCapture)
{
_DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to render stream"));
if (pDMKEvt)
{
KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
if (m_DMKEvtQueue)
{
aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue
while (aDMKEvt->pNextEvt)
{
aDMKEvt = aDMKEvt->pNextEvt;
}
aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue
}
else // currently nothing in queue
{
m_DMKEvtQueue = pDMKEvt;
if (m_DMKEvtOffset)
{
_DbgPrintF(DEBUGLVL_ERROR, ("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset));
m_DMKEvtOffset = 0;
}
}
KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
}
if (!m_TimerQueued)
{
(void) ConsumeEvents();
}
}
else // capture
{
_DbgPrintF(DEBUGLVL_BLAB, ("PutMessage to capture stream"));
ASSERT(NULL == pDMKEvt);
SourceEvtsToPort();
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUARTStream::ConsumeEvents()
*****************************************************************************
* Attempts to empty the render message queue.
* Called either from DPC timer or upon IRP submittal.
// TODO: support packages right
// process the package (actually, should do this above.
// treat the package as a list fragment that shouldn't be sorted.
// better yet, go through each event in the package, and when
// an event is exhausted, delete it and decrement m_offset.
*/
NTSTATUS CMiniportDMusUARTStream::ConsumeEvents(void)
{
PDMUS_KERNEL_EVENT aDMKEvt;
NTSTATUS ntStatus = STATUS_SUCCESS;
ULONG bytesRemaining = 0,bytesWritten = 0;
LARGE_INTEGER aMillisecIn100ns;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
m_TimerQueued = FALSE;
while (m_DMKEvtQueue) // do we have anything to play at all?
{
aDMKEvt = m_DMKEvtQueue; // event we try to play
if (aDMKEvt->cbEvent)
{
bytesRemaining = aDMKEvt->cbEvent - m_DMKEvtOffset; // number of bytes left in this evt
ASSERT(bytesRemaining > 0);
if (bytesRemaining <= 0)
{
bytesRemaining = aDMKEvt->cbEvent;
}
if (aDMKEvt->cbEvent <= sizeof(PBYTE)) // short message
{
_DbgPrintF(DEBUGLVL_BLAB, ("ConsumeEvents(%02x%02x%02x%02x)",aDMKEvt->uData.abData[0],aDMKEvt->uData.abData[1],aDMKEvt->uData.abData[2],aDMKEvt->uData.abData[3]));
ntStatus = Write(aDMKEvt->uData.abData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
}
else if (PACKAGE_EVT(aDMKEvt))
{
ASSERT(m_DMKEvtOffset == 0);
m_DMKEvtOffset = 0;
_DbgPrintF(DEBUGLVL_BLAB, ("ConsumeEvents(Package)"));
ntStatus = PutMessageLocked(aDMKEvt->uData.pPackageEvt); // we already own the spinlock
// null this because we are about to throw it in the allocator
aDMKEvt->uData.pPackageEvt = NULL;
aDMKEvt->cbEvent = 0;
bytesWritten = bytesRemaining;
}
else // SysEx message
{
_DbgPrintF(DEBUGLVL_BLAB, ("ConsumeEvents(%02x%02x%02x%02x)",aDMKEvt->uData.pbData[0],aDMKEvt->uData.pbData[1],aDMKEvt->uData.pbData[2],aDMKEvt->uData.pbData[3]));
ntStatus = Write(aDMKEvt->uData.pbData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
}
} // if (aDMKEvt->cbEvent)
if (STATUS_SUCCESS != ntStatus)
{
_DbgPrintF(DEBUGLVL_TERSE, ("ConsumeEvents: Write returned 0x%08x",ntStatus));
bytesWritten = bytesRemaining; // just bail on this event and try next time
}
ASSERT(bytesWritten <= bytesRemaining);
if (bytesWritten == bytesRemaining)
{
m_DMKEvtQueue = m_DMKEvtQueue->pNextEvt;
aDMKEvt->pNextEvt = NULL;
m_AllocatorMXF->PutMessage(aDMKEvt); // throw back in free pool
m_DMKEvtOffset = 0; // start fresh on next evt
m_NumberOfRetries = 0;
} // but wait ... there's more!
else // our FIFO is full for now.
{
// update our offset by that amount we did write
m_DMKEvtOffset += bytesWritten;
ASSERT(m_DMKEvtOffset < aDMKEvt->cbEvent);
_DbgPrintF(DEBUGLVL_BLAB,("ConsumeEvents tried %d, wrote %d, at offset %d",bytesRemaining,bytesWritten,m_DMKEvtOffset));
aMillisecIn100ns.QuadPart = -(kOneMillisec); // set timer, come back later
m_TimerQueued = TRUE;
m_NumberOfRetries++;
ntStatus = KeSetTimer( &m_TimerEvent, aMillisecIn100ns, &m_Dpc );
break;
} // we didn't write it all
} // go back, Jack, do it again (while m_DMKEvtQueue)
KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* CMiniportDMusUARTStream::HandlePortParams()
*****************************************************************************
* Writes an outgoing MIDI message.
*/
NTSTATUS
CMiniportDMusUARTStream::
HandlePortParams
(
IN PPCPROPERTY_REQUEST pRequest
)
{
PAGED_CODE();
NTSTATUS ntStatus;
if (pRequest->Verb & KSPROPERTY_TYPE_SET)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTH_PORTPARAMS), TRUE);
if (NT_SUCCESS(ntStatus))
{
RtlCopyMemory(pRequest->Value, pRequest->Instance, sizeof(SYNTH_PORTPARAMS));
PSYNTH_PORTPARAMS Params = (PSYNTH_PORTPARAMS)pRequest->Value;
if (Params->ValidParams & ~SYNTH_PORTPARAMS_CHANNELGROUPS)
{
Params->ValidParams &= SYNTH_PORTPARAMS_CHANNELGROUPS;
}
if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS))
{
Params->ChannelGroups = 1;
}
else if (Params->ChannelGroups != 1)
{
Params->ChannelGroups = 1;
}
pRequest->ValueSize = sizeof(SYNTH_PORTPARAMS);
}
return ntStatus;
}
#pragma code_seg()
/*****************************************************************************
* DMusTimerDPC()
*****************************************************************************
* The timer DPC callback. Thunks to a C++ member function.
* This is called by the OS in response to the DirectMusic pin
* wanting to wakeup later to process more DirectMusic stuff.
*/
VOID
NTAPI
DMusUARTTimerDPC
(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
ASSERT(DeferredContext);
CMiniportDMusUARTStream *aStream;
aStream = (CMiniportDMusUARTStream *) DeferredContext;
if (aStream)
{
_DbgPrintF(DEBUGLVL_BLAB,("DMusUARTTimerDPC"));
if (false == aStream->m_fCapture)
{
(void) aStream->ConsumeEvents();
}
// ignores return value!
}
}
/*****************************************************************************
* DirectMusic properties
****************************************************************************/
#pragma code_seg("PAGE")
/*
* Properties concerning synthesizer functions.
*/
const WCHAR wszDescOut[] = L"DMusic MPU-401 Out ";
const WCHAR wszDescIn[] = L"DMusic MPU-401 In ";
NTSTATUS PropertyHandler_Synth
(
IN PPCPROPERTY_REQUEST pRequest
)
{
NTSTATUS ntStatus;
PAGED_CODE();
if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
{
ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
if (NT_SUCCESS(ntStatus))
{
// if return buffer can hold a ULONG, return the access flags
PULONG AccessFlags = PULONG(pRequest->Value);
*AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT;
switch (pRequest->PropertyItem->Id)
{
case KSPROPERTY_SYNTH_CAPS:
case KSPROPERTY_SYNTH_CHANNELGROUPS:
*AccessFlags |= KSPROPERTY_TYPE_GET;
}
switch (pRequest->PropertyItem->Id)
{
case KSPROPERTY_SYNTH_CHANNELGROUPS:
*AccessFlags |= KSPROPERTY_TYPE_SET;
}
ntStatus = STATUS_SUCCESS;
pRequest->ValueSize = sizeof(ULONG);
switch (pRequest->PropertyItem->Id)
{
case KSPROPERTY_SYNTH_PORTPARAMETERS:
if (pRequest->MinorTarget)
{
*AccessFlags |= KSPROPERTY_TYPE_GET;
}
else
{
pRequest->ValueSize = 0;
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
}
}
}
else
{
ntStatus = STATUS_SUCCESS;
switch(pRequest->PropertyItem->Id)
{
case KSPROPERTY_SYNTH_CAPS:
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_Synth:KSPROPERTY_SYNTH_CAPS"));
if (pRequest->Verb & KSPROPERTY_TYPE_SET)
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
if (NT_SUCCESS(ntStatus))
{
ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTHCAPS), TRUE);
if (NT_SUCCESS(ntStatus))
{
SYNTHCAPS *caps = (SYNTHCAPS*)pRequest->Value;
int increment;
RtlZeroMemory(caps, sizeof(SYNTHCAPS));
// XXX Different guids for different instances!
//
if (pRequest->Node == eSynthNode)
{
increment = sizeof(wszDescOut) - 2;
RtlCopyMemory( caps->Description,wszDescOut,increment);
caps->Guid = CLSID_MiniportDriverDMusUART;
}
else
{
increment = sizeof(wszDescIn) - 2;
RtlCopyMemory( caps->Description,wszDescIn,increment);
caps->Guid = CLSID_MiniportDriverDMusUARTCapture;
}
caps->Flags = SYNTH_PC_EXTERNAL;
caps->MemorySize = 0;
caps->MaxChannelGroups = 1;
caps->MaxVoices = 0xFFFFFFFF;
caps->MaxAudioChannels = 0xFFFFFFFF;
caps->EffectFlags = 0;
CMiniportDMusUART *aMiniport;
ASSERT(pRequest->MajorTarget);
aMiniport = (CMiniportDMusUART *)(PMINIPORTDMUS)(pRequest->MajorTarget);
WCHAR wszDesc2[16];
int cLen;
cLen = swprintf(wszDesc2,L"[%03X]\0",PtrToUlong(aMiniport->m_pPortBase));
cLen *= sizeof(WCHAR);
RtlCopyMemory((WCHAR *)((DWORD_PTR)(caps->Description) + increment),
wszDesc2,
cLen);
pRequest->ValueSize = sizeof(SYNTHCAPS);
}
}
break;
case KSPROPERTY_SYNTH_PORTPARAMETERS:
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_Synth:KSPROPERTY_SYNTH_PORTPARAMETERS"));
{
CMiniportDMusUARTStream *aStream;
aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
if (aStream)
{
ntStatus = aStream->HandlePortParams(pRequest);
}
else
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
}
break;
case KSPROPERTY_SYNTH_CHANNELGROUPS:
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_Synth:KSPROPERTY_SYNTH_CHANNELGROUPS"));
ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
if (NT_SUCCESS(ntStatus))
{
*(PULONG)(pRequest->Value) = 1;
pRequest->ValueSize = sizeof(ULONG);
}
break;
case KSPROPERTY_SYNTH_LATENCYCLOCK:
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_Synth:KSPROPERTY_SYNTH_LATENCYCLOCK"));
if(pRequest->Verb & KSPROPERTY_TYPE_SET)
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONGLONG), TRUE);
if(NT_SUCCESS(ntStatus))
{
REFERENCE_TIME rtLatency;
CMiniportDMusUARTStream *aStream;
aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
if(aStream == NULL)
{
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
else
{
aStream->m_pMiniport->m_MasterClock->GetTime(&rtLatency);
*((PULONGLONG)pRequest->Value) = rtLatency;
pRequest->ValueSize = sizeof(ULONGLONG);
}
}
}
break;
default:
_DbgPrintF(DEBUGLVL_TERSE,("Unhandled property in PropertyHandler_Synth"));
break;
}
}
return ntStatus;
}
/*****************************************************************************
* ValidatePropertyRequest()
*****************************************************************************
* Validates pRequest.
* Checks if the ValueSize is valid
* Checks if the Value is valid
*
* This does not update pRequest->ValueSize if it returns NT_SUCCESS.
* Caller must set pRequest->ValueSize in case of NT_SUCCESS.
*/
NTSTATUS ValidatePropertyRequest
(
IN PPCPROPERTY_REQUEST pRequest,
IN ULONG ulValueSize,
IN BOOLEAN fValueRequired
)
{
NTSTATUS ntStatus;
if (pRequest->ValueSize >= ulValueSize)
{
if (fValueRequired && NULL == pRequest->Value)
{
ntStatus = STATUS_INVALID_PARAMETER;
}
else
{
ntStatus = STATUS_SUCCESS;
}
}
else if (0 == pRequest->ValueSize)
{
ntStatus = STATUS_BUFFER_OVERFLOW;
}
else
{
ntStatus = STATUS_BUFFER_TOO_SMALL;
}
if (STATUS_BUFFER_OVERFLOW == ntStatus)
{
pRequest->ValueSize = ulValueSize;
}
else
{
pRequest->ValueSize = 0;
}
return ntStatus;
} // ValidatePropertyRequest
#pragma code_seg()