1744 lines
53 KiB
C++
1744 lines
53 KiB
C++
/*****************************************************************************
|
|
* 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()
|
|
|