mirror of
https://github.com/reactos/reactos.git
synced 2024-09-11 05:12:43 +00:00
![Timo Kreuzer](/assets/img/avatar_default.png)
Fix for MSVC warning "C2323: 'operator new': non-member operator new or delete functions may not be declared static or in a namespace other than the global namespace." See https://docs.microsoft.com/en-us/cpp/porting/visual-cpp-what-s-new-2003-through-2015?view=vs-2019 section "Overloaded operator new and operator delete"
2704 lines
80 KiB
C++
2704 lines
80 KiB
C++
/*****************************************************************************
|
|
* miniport_dmus.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.hpp"
|
|
|
|
#ifndef YDEBUG
|
|
#define NDEBUG
|
|
#endif
|
|
|
|
#include <debug.h>
|
|
|
|
// + for absolute / - for relative
|
|
|
|
#define kOneMillisec (10 * 1000)
|
|
|
|
//
|
|
// MPU401 ports
|
|
//
|
|
#define MPU401_REG_STATUS 0x01 // Status register
|
|
#define MPU401_DRR 0x40 // Output ready (for command or data)
|
|
// if this bit is set, the output FIFO is FULL
|
|
#define MPU401_DSR 0x80 // Input ready (for data)
|
|
// if this bit is set, the input FIFO is empty
|
|
|
|
#define MPU401_REG_DATA 0x00 // Data in
|
|
#define MPU401_REG_COMMAND 0x01 // Commands
|
|
#define MPU401_CMD_RESET 0xFF // Reset command
|
|
#define MPU401_CMD_UART 0x3F // Switch to UART mod
|
|
|
|
|
|
/*****************************************************************************
|
|
* Prototypes
|
|
*/
|
|
|
|
NTSTATUS NTAPI InitMPU(IN PINTERRUPTSYNC InterruptSync,IN PVOID DynamicContext);
|
|
NTSTATUS ResetHardware(PUCHAR portBase);
|
|
NTSTATUS ValidatePropertyRequest(IN PPCPROPERTY_REQUEST pRequest, IN ULONG ulValueSize, IN BOOLEAN fValueRequired);
|
|
NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest);
|
|
NTSTATUS NTAPI DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
|
|
VOID NTAPI DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2);
|
|
NTSTATUS NTAPI SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext);
|
|
/*****************************************************************************
|
|
* Constants
|
|
*/
|
|
|
|
const BOOLEAN COMMAND = TRUE;
|
|
const BOOLEAN DATA = FALSE;
|
|
const ULONG kMPUInputBufferSize = 128;
|
|
|
|
|
|
/*****************************************************************************
|
|
* Classes
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUART
|
|
*****************************************************************************
|
|
* MPU-401 miniport. This object is associated with the device and is
|
|
* created when the device is started. The class inherits IMiniportDMus
|
|
* so it can expose this interface and CUnknown so it automatically gets
|
|
* reference counting and aggregation support.
|
|
*/
|
|
class CMiniportDMusUART
|
|
: public IMiniportDMus,
|
|
public IMusicTechnology,
|
|
public IPowerNotify
|
|
{
|
|
private:
|
|
LONG m_Ref; // Reference count
|
|
KSSTATE m_KSStateInput; // Miniport state (RUN/PAUSE/ACQUIRE/STOP)
|
|
PPORTDMUS m_pPort; // Callback interface.
|
|
PUCHAR m_pPortBase; // Base port address.
|
|
PINTERRUPTSYNC m_pInterruptSync; // Interrupt synchronization object.
|
|
PSERVICEGROUP m_pServiceGroup; // Service group for capture.
|
|
PMASTERCLOCK m_MasterClock; // for input data
|
|
REFERENCE_TIME m_InputTimeStamp; // capture data timestamp
|
|
USHORT m_NumRenderStreams; // Num active render streams.
|
|
USHORT m_NumCaptureStreams; // Num active capture streams.
|
|
ULONG m_MPUInputBufferHead; // Index of the newest byte in the FIFO.
|
|
ULONG m_MPUInputBufferTail; // Index of the oldest empty space in the FIFO.
|
|
GUID m_MusicFormatTechnology;
|
|
POWER_STATE m_PowerState; // Saved power state (D0 = full power, D3 = off)
|
|
BOOLEAN m_fMPUInitialized; // Is the MPU HW initialized.
|
|
BOOLEAN m_UseIRQ; // FALSE if no IRQ is used for MIDI.
|
|
UCHAR m_MPUInputBuffer[kMPUInputBufferSize]; // Internal SW FIFO.
|
|
|
|
/*************************************************************************
|
|
* CMiniportDMusUART methods
|
|
*
|
|
* These are private member functions used internally by the object.
|
|
* See MINIPORT.CPP for specific descriptions.
|
|
*/
|
|
NTSTATUS ProcessResources
|
|
(
|
|
IN PRESOURCELIST ResourceList
|
|
);
|
|
NTSTATUS InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase);
|
|
|
|
public:
|
|
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
|
|
|
|
STDMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
InterlockedIncrement(&m_Ref);
|
|
return m_Ref;
|
|
}
|
|
STDMETHODIMP_(ULONG) Release()
|
|
{
|
|
InterlockedDecrement(&m_Ref);
|
|
|
|
if (!m_Ref)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_Ref;
|
|
}
|
|
|
|
CMiniportDMusUART(IUnknown * Unknown){}
|
|
virtual ~CMiniportDMusUART();
|
|
|
|
/*************************************************************************
|
|
* IMiniport methods
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
GetDescription
|
|
( OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
|
|
);
|
|
STDMETHODIMP_(NTSTATUS)
|
|
DataRangeIntersection
|
|
( IN ULONG PinId
|
|
, IN PKSDATARANGE DataRange
|
|
, IN PKSDATARANGE MatchingDataRange
|
|
, IN ULONG OutputBufferLength
|
|
, OUT PVOID ResultantFormat
|
|
, OUT PULONG ResultantFormatLength
|
|
)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* IMiniportDMus methods
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) Init
|
|
(
|
|
IN PUNKNOWN UnknownAdapter,
|
|
IN PRESOURCELIST ResourceList,
|
|
IN PPORTDMUS Port,
|
|
OUT PSERVICEGROUP * ServiceGroup
|
|
);
|
|
STDMETHODIMP_(NTSTATUS) NewStream
|
|
(
|
|
OUT PMXF * Stream,
|
|
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
|
|
);
|
|
STDMETHODIMP_(void) Service
|
|
( void
|
|
);
|
|
|
|
/*************************************************************************
|
|
* IMusicTechnology methods
|
|
*/
|
|
IMP_IMusicTechnology;
|
|
|
|
/*************************************************************************
|
|
* IPowerNotify methods
|
|
*/
|
|
IMP_IPowerNotify;
|
|
|
|
/*************************************************************************
|
|
* Friends
|
|
*/
|
|
friend class CMiniportDMusUARTStream;
|
|
friend NTSTATUS NTAPI
|
|
DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
|
|
friend NTSTATUS NTAPI
|
|
SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext);
|
|
friend VOID NTAPI
|
|
DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2);
|
|
friend NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest);
|
|
friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream);
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream
|
|
*****************************************************************************
|
|
* MPU-401 miniport stream. This object is associated with the pin and is
|
|
* created when the pin is instantiated. It inherits IMXF
|
|
* so it can expose this interface and CUnknown so it automatically gets
|
|
* reference counting and aggregation support.
|
|
*/
|
|
class CMiniportDMusUARTStream : public IMXF
|
|
{
|
|
private:
|
|
LONG m_Ref; // Reference Count
|
|
CMiniportDMusUART * m_pMiniport; // Parent.
|
|
REFERENCE_TIME m_SnapshotTimeStamp; // Current snapshot of miniport's input timestamp.
|
|
PUCHAR m_pPortBase; // Base port address.
|
|
BOOLEAN m_fCapture; // Whether this is capture.
|
|
long m_NumFailedMPUTries; // Deadman timeout for MPU hardware.
|
|
PAllocatorMXF m_AllocatorMXF; // source/sink for DMus structs
|
|
PMXF m_sinkMXF; // sink for DMus capture
|
|
PDMUS_KERNEL_EVENT m_DMKEvtQueue; // queue of waiting events
|
|
ULONG m_NumberOfRetries; // Number of consecutive times the h/w was busy/full
|
|
ULONG m_DMKEvtOffset; // offset into the event
|
|
KDPC m_Dpc; // DPC for timer
|
|
KTIMER m_TimerEvent; // timer
|
|
BOOL m_TimerQueued; // whether a timer has been set
|
|
KSPIN_LOCK m_DpcSpinLock; // protects the ConsumeEvents DPC
|
|
|
|
STDMETHODIMP_(NTSTATUS) SourceEvtsToPort();
|
|
STDMETHODIMP_(NTSTATUS) ConsumeEvents();
|
|
STDMETHODIMP_(NTSTATUS) PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt);
|
|
|
|
public:
|
|
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
|
|
|
|
STDMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
InterlockedIncrement(&m_Ref);
|
|
return m_Ref;
|
|
}
|
|
STDMETHODIMP_(ULONG) Release()
|
|
{
|
|
InterlockedDecrement(&m_Ref);
|
|
|
|
if (!m_Ref)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_Ref;
|
|
}
|
|
|
|
virtual ~CMiniportDMusUARTStream();
|
|
|
|
STDMETHODIMP_(NTSTATUS) Init
|
|
(
|
|
IN CMiniportDMusUART * pMiniport,
|
|
IN PUCHAR pPortBase,
|
|
IN BOOLEAN fCapture,
|
|
IN PAllocatorMXF allocatorMXF,
|
|
IN PMASTERCLOCK masterClock
|
|
);
|
|
|
|
NTSTATUS HandlePortParams
|
|
(
|
|
IN PPCPROPERTY_REQUEST Request
|
|
);
|
|
|
|
/*************************************************************************
|
|
* IMiniportStreamDMusUART methods
|
|
*/
|
|
IMP_IMXF;
|
|
|
|
STDMETHODIMP_(NTSTATUS) Write
|
|
(
|
|
IN PVOID BufferAddress,
|
|
IN ULONG BytesToWrite,
|
|
OUT PULONG BytesWritten
|
|
);
|
|
|
|
friend VOID NTAPI
|
|
DMusUARTTimerDPC
|
|
(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
friend NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST);
|
|
friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream);
|
|
};
|
|
|
|
|
|
|
|
#define STR_MODULENAME "DMusUART:Miniport: "
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
#define UartFifoOkForWrite(status) ((status & MPU401_DRR) == 0)
|
|
#define UartFifoOkForRead(status) ((status & MPU401_DSR) == 0)
|
|
|
|
typedef struct
|
|
{
|
|
CMiniportDMusUART *Miniport;
|
|
PUCHAR PortBase;
|
|
PVOID BufferAddress;
|
|
ULONG Length;
|
|
PULONG BytesRead;
|
|
}
|
|
SYNCWRITECONTEXT, *PSYNCWRITECONTEXT;
|
|
|
|
/*****************************************************************************
|
|
* 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
|
|
};
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
BOOLEAN TryMPU(IN PUCHAR PortBase);
|
|
NTSTATUS WriteMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value);
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
// make sure we're in UART mode
|
|
NTSTATUS ResetHardware(PUCHAR portBase)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return WriteMPU(portBase,COMMAND,MPU401_CMD_UART);
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
//
|
|
// We initialize the UART with interrupts suppressed so we don't
|
|
// try to service the chip prematurely.
|
|
//
|
|
NTSTATUS CMiniportDMusUART::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS ntStatus;
|
|
if (m_UseIRQ)
|
|
{
|
|
ntStatus = interruptSync->CallSynchronizedRoutine(InitMPU,PVOID(portBase));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = InitMPU(NULL,PVOID(portBase));
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
//
|
|
// Start the UART (this should trigger an interrupt).
|
|
//
|
|
ntStatus = ResetHardware(portBase);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("*** InitMPU returned with ntStatus 0x%08x ***", ntStatus);
|
|
}
|
|
|
|
m_fMPUInitialized = NT_SUCCESS(ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* InitMPU()
|
|
*****************************************************************************
|
|
* Synchronized routine to initialize the MPU401.
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
InitMPU
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
DPRINT("InitMPU");
|
|
if (!DynamicContext)
|
|
{
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
PUCHAR portBase = PUCHAR(DynamicContext);
|
|
UCHAR status;
|
|
ULONGLONG startTime;
|
|
BOOLEAN success;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Reset the card (puts it into "smart mode")
|
|
//
|
|
ntStatus = WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
|
|
|
|
// wait for the acknowledgement
|
|
// NOTE: When the Ack arrives, it will trigger an interrupt.
|
|
// Normally the DPC routine would read in the ack byte and we
|
|
// would never see it, however since we have the hardware locked (HwEnter),
|
|
// we can read the port before the DPC can and thus we receive the Ack.
|
|
startTime = PcGetTimeInterval(0);
|
|
success = FALSE;
|
|
while(PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
{
|
|
status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
|
|
|
|
if (UartFifoOkForRead(status)) // Is data waiting?
|
|
{
|
|
READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
|
|
success = TRUE; // don't need to do more
|
|
break;
|
|
}
|
|
KeStallExecutionProcessor(25); // microseconds
|
|
}
|
|
#if (DBG)
|
|
if (!success)
|
|
{
|
|
DPRINT("First attempt to reset the MPU didn't get ACKed.\n");
|
|
}
|
|
#endif // (DBG)
|
|
|
|
// NOTE: We cannot check the ACK byte because if the card was already in
|
|
// UART mode it will not send an ACK but it will reset.
|
|
|
|
// reset the card again
|
|
(void) WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
|
|
|
|
// wait for ack (again)
|
|
startTime = PcGetTimeInterval(0); // This might take a while
|
|
BYTE dataByte = 0;
|
|
success = FALSE;
|
|
while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
{
|
|
status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
|
|
if (UartFifoOkForRead(status)) // Is data waiting?
|
|
{
|
|
dataByte = READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
|
|
success = TRUE; // don't need to do more
|
|
break;
|
|
}
|
|
KeStallExecutionProcessor(25);
|
|
}
|
|
|
|
if ((0xFE != dataByte) || !success) // Did we succeed? If no second ACK, something is hosed
|
|
{
|
|
DPRINT("Second attempt to reset the MPU didn't get ACKed.\n");
|
|
DPRINT("Init Reset failure error. Ack = %X", ULONG(dataByte));
|
|
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::Write()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUARTStream::
|
|
Write
|
|
(
|
|
IN PVOID BufferAddress,
|
|
IN ULONG Length,
|
|
OUT PULONG BytesWritten
|
|
)
|
|
{
|
|
DPRINT("Write\n");
|
|
ASSERT(BytesWritten);
|
|
if (!BufferAddress)
|
|
{
|
|
Length = 0;
|
|
}
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
if (!m_fCapture)
|
|
{
|
|
PUCHAR pMidiData;
|
|
ULONG count;
|
|
|
|
count = 0;
|
|
pMidiData = PUCHAR(BufferAddress);
|
|
|
|
if (Length)
|
|
{
|
|
SYNCWRITECONTEXT context;
|
|
context.Miniport = (m_pMiniport);
|
|
context.PortBase = m_pPortBase;
|
|
context.BufferAddress = pMidiData;
|
|
context.Length = Length;
|
|
context.BytesRead = &count;
|
|
|
|
if (m_pMiniport->m_UseIRQ)
|
|
{
|
|
ntStatus = m_pMiniport->m_pInterruptSync->
|
|
CallSynchronizedRoutine(SynchronizedDMusMPUWrite,PVOID(&context));
|
|
}
|
|
else // !m_UseIRQ
|
|
{
|
|
ntStatus = SynchronizedDMusMPUWrite(NULL,PVOID(&context));
|
|
} // !m_UseIRQ
|
|
|
|
if (count == 0)
|
|
{
|
|
m_NumFailedMPUTries++;
|
|
if (m_NumFailedMPUTries >= 100)
|
|
{
|
|
ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
m_NumFailedMPUTries = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_NumFailedMPUTries = 0;
|
|
}
|
|
} // if we have data at all
|
|
*BytesWritten = count;
|
|
}
|
|
else // called write on the read stream
|
|
{
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* SynchronizedDMusMPUWrite()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
SynchronizedDMusMPUWrite
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID syncWriteContext
|
|
)
|
|
{
|
|
PSYNCWRITECONTEXT context;
|
|
context = (PSYNCWRITECONTEXT)syncWriteContext;
|
|
ASSERT(context->Miniport);
|
|
ASSERT(context->PortBase);
|
|
ASSERT(context->BufferAddress);
|
|
ASSERT(context->Length);
|
|
ASSERT(context->BytesRead);
|
|
|
|
PUCHAR pChar = PUCHAR(context->BufferAddress);
|
|
NTSTATUS ntStatus; // , readStatus
|
|
ntStatus = STATUS_SUCCESS;
|
|
//
|
|
// while we're not there yet, and
|
|
// while we don't have to wait on an aligned byte (including 0)
|
|
// (we never wait on a byte. Better to come back later)
|
|
/*readStatus = */ DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
while ( (*(context->BytesRead) < context->Length)
|
|
&& ( TryMPU(context->PortBase)
|
|
|| (*(context->BytesRead)%3)
|
|
) )
|
|
{
|
|
ntStatus = WriteMPU(context->PortBase,DATA,*pChar);
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
pChar++;
|
|
*(context->BytesRead) = *(context->BytesRead) + 1;
|
|
// readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
}
|
|
else
|
|
{
|
|
DPRINT("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus);
|
|
break;
|
|
}
|
|
}
|
|
/*readStatus = */ DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
|
|
return ntStatus;
|
|
}
|
|
|
|
#define kMPUPollTimeout 2
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* TryMPU()
|
|
*****************************************************************************
|
|
* See if the MPU401 is free.
|
|
*/
|
|
BOOLEAN
|
|
TryMPU
|
|
(
|
|
IN PUCHAR PortBase
|
|
)
|
|
{
|
|
BOOLEAN success;
|
|
USHORT numPolls;
|
|
UCHAR status;
|
|
|
|
DPRINT("TryMPU");
|
|
numPolls = 0;
|
|
|
|
while (numPolls < kMPUPollTimeout)
|
|
{
|
|
status = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
|
|
|
|
if (UartFifoOkForWrite(status)) // Is this a good time to write data?
|
|
{
|
|
break;
|
|
}
|
|
numPolls++;
|
|
}
|
|
if (numPolls >= kMPUPollTimeout)
|
|
{
|
|
success = FALSE;
|
|
DPRINT("TryMPU failed");
|
|
}
|
|
else
|
|
{
|
|
success = TRUE;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* WriteMPU()
|
|
*****************************************************************************
|
|
* Write a byte out to the MPU401.
|
|
*/
|
|
NTSTATUS
|
|
WriteMPU
|
|
(
|
|
IN PUCHAR PortBase,
|
|
IN BOOLEAN IsCommand,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
DPRINT("WriteMPU");
|
|
NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
|
|
|
|
if (!PortBase)
|
|
{
|
|
DPRINT("O: PortBase is zero\n");
|
|
return ntStatus;
|
|
}
|
|
PUCHAR deviceAddr = PortBase + MPU401_REG_DATA;
|
|
|
|
if (IsCommand)
|
|
{
|
|
deviceAddr = PortBase + MPU401_REG_COMMAND;
|
|
}
|
|
|
|
ULONGLONG startTime = PcGetTimeInterval(0);
|
|
|
|
while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
{
|
|
UCHAR status
|
|
= READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
|
|
|
|
if (UartFifoOkForWrite(status)) // Is this a good time to write data?
|
|
{ // yep (Jon comment)
|
|
WRITE_PORT_UCHAR(deviceAddr,Value);
|
|
DPRINT("WriteMPU emitted 0x%02x",Value);
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* SnapTimeStamp()
|
|
*****************************************************************************
|
|
*
|
|
* At synchronized execution to ISR, copy miniport's volatile m_InputTimeStamp
|
|
* to stream's m_SnapshotTimeStamp and zero m_InputTimeStamp.
|
|
*
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream)
|
|
{
|
|
CMiniportDMusUARTStream *pMPStream = (CMiniportDMusUARTStream *)pStream;
|
|
|
|
// cache the timestamp
|
|
pMPStream->m_SnapshotTimeStamp = pMPStream->m_pMiniport->m_InputTimeStamp;
|
|
|
|
// if the window is closed, zero the timestamp
|
|
if (pMPStream->m_pMiniport->m_MPUInputBufferHead ==
|
|
pMPStream->m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
pMPStream->m_pMiniport->m_InputTimeStamp = 0;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::SourceEvtsToPort()
|
|
*****************************************************************************
|
|
*
|
|
* Reads incoming MIDI data, feeds into DMus events.
|
|
* No need to touch the hardware, just read from our SW FIFO.
|
|
*
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUARTStream::SourceEvtsToPort()
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
DPRINT("SourceEvtsToPort");
|
|
|
|
if (m_fCapture)
|
|
{
|
|
ntStatus = STATUS_SUCCESS;
|
|
if (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
PDMUS_KERNEL_EVENT aDMKEvt,eventTail,eventHead = NULL;
|
|
|
|
while (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
(void) m_AllocatorMXF->GetMessage(&aDMKEvt);
|
|
if (!aDMKEvt)
|
|
{
|
|
DPRINT("SourceEvtsToPort can't allocate DMKEvt");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// put this event at the end of the list
|
|
if (!eventHead)
|
|
{
|
|
eventHead = aDMKEvt;
|
|
}
|
|
else
|
|
{
|
|
eventTail = eventHead;
|
|
while (eventTail->pNextEvt)
|
|
{
|
|
eventTail = eventTail->pNextEvt;
|
|
}
|
|
eventTail->pNextEvt = aDMKEvt;
|
|
}
|
|
// read all the bytes out of the buffer, into event(s)
|
|
for (aDMKEvt->cbEvent = 0; aDMKEvt->cbEvent < sizeof(PBYTE); aDMKEvt->cbEvent++)
|
|
{
|
|
if (m_pMiniport->m_MPUInputBufferHead == m_pMiniport->m_MPUInputBufferTail)
|
|
{
|
|
// _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort m_MPUInputBufferHead met m_MPUInputBufferTail, overrun"));
|
|
break;
|
|
}
|
|
aDMKEvt->uData.abData[aDMKEvt->cbEvent] = m_pMiniport->m_MPUInputBuffer[m_pMiniport->m_MPUInputBufferHead];
|
|
m_pMiniport->m_MPUInputBufferHead++;
|
|
if (m_pMiniport->m_MPUInputBufferHead >= kMPUInputBufferSize)
|
|
{
|
|
m_pMiniport->m_MPUInputBufferHead = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pMiniport->m_UseIRQ)
|
|
{
|
|
ntStatus = m_pMiniport->m_pInterruptSync->CallSynchronizedRoutine(SnapTimeStamp,PVOID(this));
|
|
}
|
|
else // !m_UseIRQ
|
|
{
|
|
ntStatus = SnapTimeStamp(NULL,PVOID(this));
|
|
} // !m_UseIRQ
|
|
aDMKEvt = eventHead;
|
|
while (aDMKEvt)
|
|
{
|
|
aDMKEvt->ullPresTime100ns = m_SnapshotTimeStamp;
|
|
aDMKEvt->usChannelGroup = 1;
|
|
aDMKEvt->usFlags = DMUS_KEF_EVENT_INCOMPLETE;
|
|
aDMKEvt = aDMKEvt->pNextEvt;
|
|
}
|
|
(void)m_sinkMXF->PutMessage(eventHead);
|
|
}
|
|
}
|
|
else // render stream
|
|
{
|
|
DPRINT("SourceEvtsToPort called on render stream");
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* DMusMPUInterruptServiceRoutine()
|
|
*****************************************************************************
|
|
* ISR.
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
DMusMPUInterruptServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
DPRINT("DMusMPUInterruptServiceRoutine");
|
|
ULONGLONG startTime;
|
|
|
|
ASSERT(DynamicContext);
|
|
|
|
NTSTATUS ntStatus;
|
|
BOOL newBytesAvailable;
|
|
CMiniportDMusUART *that;
|
|
NTSTATUS clockStatus;
|
|
|
|
that = (CMiniportDMusUART *) DynamicContext;
|
|
newBytesAvailable = FALSE;
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
UCHAR portStatus = 0xff;
|
|
|
|
//
|
|
// Read the MPU status byte.
|
|
//
|
|
if (that->m_pPortBase)
|
|
{
|
|
portStatus =
|
|
READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
|
|
|
|
//
|
|
// If there is outstanding work to do and there is a port-driver for
|
|
// the MPU miniport...
|
|
//
|
|
if (UartFifoOkForRead(portStatus) && that->m_pPort)
|
|
{
|
|
startTime = PcGetTimeInterval(0);
|
|
while ( (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
|
|
&& (UartFifoOkForRead(portStatus)) )
|
|
{
|
|
UCHAR uDest = READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_DATA);
|
|
if ( (that->m_KSStateInput == KSSTATE_RUN)
|
|
&& (that->m_NumCaptureStreams)
|
|
)
|
|
{
|
|
ULONG buffHead = that->m_MPUInputBufferHead;
|
|
if ( (that->m_MPUInputBufferTail + 1 == buffHead)
|
|
|| (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead))
|
|
{
|
|
DPRINT("*****MPU Input Buffer Overflow*****");
|
|
}
|
|
else
|
|
{
|
|
if (!that->m_InputTimeStamp)
|
|
{
|
|
clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp);
|
|
if (STATUS_SUCCESS != clockStatus)
|
|
{
|
|
DPRINT("GetTime failed for clock 0x%08x",that->m_MasterClock);
|
|
}
|
|
}
|
|
newBytesAvailable = TRUE;
|
|
// ...place the data in our FIFO...
|
|
that->m_MPUInputBuffer[that->m_MPUInputBufferTail] = uDest;
|
|
ASSERT(that->m_MPUInputBufferTail < kMPUInputBufferSize);
|
|
|
|
that->m_MPUInputBufferTail++;
|
|
if (that->m_MPUInputBufferTail >= kMPUInputBufferSize)
|
|
{
|
|
that->m_MPUInputBufferTail = 0;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Look for more MIDI data.
|
|
//
|
|
portStatus =
|
|
READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
|
|
} // either there's no data or we ran too long
|
|
if (newBytesAvailable)
|
|
{
|
|
//
|
|
// ...notify the MPU port driver that we have bytes.
|
|
//
|
|
that->m_pPort->Notify(that->m_pServiceGroup);
|
|
}
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUART::GetDescription()
|
|
*****************************************************************************
|
|
* Gets the topology.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUART::
|
|
GetDescription
|
|
(
|
|
OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(OutFilterDescriptor);
|
|
|
|
DPRINT("GetDescription");
|
|
|
|
*OutFilterDescriptor = &MiniportFilterDescriptor;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
NTSTATUS
|
|
NewMiniportDMusUART(
|
|
OUT PMINIPORT* OutMiniport,
|
|
IN REFCLSID ClassId)
|
|
{
|
|
CMiniportDMusUART * This;
|
|
NTSTATUS Status;
|
|
|
|
This= new(NonPagedPool, TAG_PORTCLASS) CMiniportDMusUART(NULL);
|
|
if (!This)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = This->QueryInterface(IID_IMiniport, (PVOID*)OutMiniport);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
delete This;
|
|
}
|
|
|
|
DPRINT("NewMiniportDMusUART %p Status %x\n", *OutMiniport, Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUART::ProcessResources()
|
|
*****************************************************************************
|
|
* Processes the resource list, setting up helper objects accordingly.
|
|
*/
|
|
NTSTATUS
|
|
CMiniportDMusUART::
|
|
ProcessResources
|
|
(
|
|
IN PRESOURCELIST ResourceList
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("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
|
|
DPRINT("Starting MPU401 Port 0x%lx", 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)
|
|
)
|
|
{
|
|
DPRINT("Unknown ResourceList configuration");
|
|
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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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::QueryInterface
|
|
(
|
|
REFIID Interface,
|
|
PVOID * Object
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* CMiniportDMusUART::~CMiniportDMusUART()
|
|
*****************************************************************************
|
|
* Destructor.
|
|
*/
|
|
CMiniportDMusUART::~CMiniportDMusUART(void)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("~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((PINTERRUPTSYNCROUTINE)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;
|
|
}
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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);
|
|
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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();
|
|
|
|
DPRINT("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))
|
|
{
|
|
DPRINT("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, 'wNcP') CMiniportDMusUARTStream();
|
|
|
|
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)
|
|
{
|
|
DPRINT("NewStream failed, too many capture streams");
|
|
}
|
|
else if (StreamType == DMUS_STREAM_MIDI_RENDER)
|
|
{
|
|
DPRINT("NewStream failed, too many render streams");
|
|
}
|
|
else
|
|
{
|
|
DPRINT("NewStream invalid stream type");
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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.
|
|
*/
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
STDMETHODIMP_(void)
|
|
CMiniportDMusUART::
|
|
PowerChangeNotify
|
|
(
|
|
IN POWER_STATE PowerState
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState);
|
|
|
|
switch (PowerState.DeviceState)
|
|
{
|
|
case PowerDeviceD0:
|
|
if (m_PowerState.DeviceState != PowerDeviceD0)
|
|
{
|
|
if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase)))
|
|
{
|
|
DPRINT("InitializeHardware failed when resuming");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
case PowerDeviceD3:
|
|
default:
|
|
break;
|
|
}
|
|
m_PowerState.DeviceState = PowerState.DeviceState;
|
|
} // PowerChangeNotify
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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::QueryInterface
|
|
(
|
|
REFIID Interface,
|
|
PVOID * Object
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::~CMiniportDMusUARTStream()
|
|
*****************************************************************************
|
|
* Destructs a stream.
|
|
*/
|
|
CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("~CMiniportDMusUARTStream");
|
|
|
|
KeCancelTimer(&m_TimerEvent);
|
|
|
|
if (m_DMKEvtQueue)
|
|
{
|
|
if (m_AllocatorMXF)
|
|
{
|
|
m_AllocatorMXF->PutMessage(m_DMKEvtQueue);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("~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();
|
|
}
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* 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);
|
|
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::SetState()
|
|
*****************************************************************************
|
|
* Sets the state of the channel.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CMiniportDMusUARTStream::
|
|
SetState
|
|
(
|
|
IN KSSTATE NewState
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DPRINT("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
|
|
{
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUART::Service()
|
|
*****************************************************************************
|
|
* DPC-mode service call from the port.
|
|
*/
|
|
STDMETHODIMP_(void)
|
|
CMiniportDMusUART::
|
|
Service
|
|
( void
|
|
)
|
|
{
|
|
DPRINT("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;
|
|
}
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::ConnectOutput()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
NTSTATUS
|
|
CMiniportDMusUARTStream::
|
|
ConnectOutput(PMXF sinkMXF)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (m_fCapture)
|
|
{
|
|
if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF))
|
|
{
|
|
DPRINT("ConnectOutput");
|
|
m_sinkMXF = sinkMXF;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("ConnectOutput failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT("ConnectOutput called on renderer; failed");
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* CMiniportDMusUARTStream::DisconnectOutput()
|
|
*****************************************************************************
|
|
* Writes outgoing MIDI data.
|
|
*/
|
|
NTSTATUS
|
|
CMiniportDMusUARTStream::
|
|
DisconnectOutput(PMXF sinkMXF)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (m_fCapture)
|
|
{
|
|
if ((m_sinkMXF == sinkMXF) || (!sinkMXF))
|
|
{
|
|
DPRINT("DisconnectOutput");
|
|
m_sinkMXF = m_AllocatorMXF;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("DisconnectOutput failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT("DisconnectOutput called on renderer; failed");
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
|
|
/*****************************************************************************
|
|
* 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)
|
|
{
|
|
DPRINT("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)
|
|
{
|
|
DPRINT("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
|
|
{
|
|
DPRINT("PutMessage to capture stream");
|
|
ASSERT(NULL == pDMKEvt);
|
|
|
|
SourceEvtsToPort();
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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)
|
|
{
|
|
DPRINT("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)
|
|
{
|
|
DPRINT("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d", m_DMKEvtOffset);
|
|
m_DMKEvtOffset = 0;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
|
|
}
|
|
if (!m_TimerQueued)
|
|
{
|
|
(void) ConsumeEvents();
|
|
}
|
|
}
|
|
else // capture
|
|
{
|
|
DPRINT("PutMessage to capture stream");
|
|
ASSERT(NULL == pDMKEvt);
|
|
|
|
SourceEvtsToPort();
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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
|
|
{
|
|
DPRINT("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;
|
|
DPRINT("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
|
|
{
|
|
DPRINT("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)
|
|
{
|
|
DPRINT("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);
|
|
|
|
DPRINT("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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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)
|
|
{
|
|
DPRINT("DMusUARTTimerDPC");
|
|
if (false == aStream->m_fCapture)
|
|
{
|
|
(void) aStream->ConsumeEvents();
|
|
}
|
|
// ignores return value!
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DirectMusic properties
|
|
****************************************************************************/
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
/*
|
|
* Properties concerning synthesizer functions.
|
|
*/
|
|
const WCHAR wszDescOut[] = L"DMusic MPU-401 Out ";
|
|
const WCHAR wszDescIn[] = L"DMusic MPU-401 In ";
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
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:
|
|
DPRINT("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:
|
|
DPRINT("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:
|
|
DPRINT("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:
|
|
DPRINT("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:
|
|
DPRINT("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
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
|