-/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS Kernel Streaming
- * FILE: drivers/wdm/audio/backpln/portcls/miniport_dmus.cpp
- * PURPOSE: DirectMusic miniport
- * PROGRAMMER: Johannes Anderwald
+#include "private.hpp"
+
+// + 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
*/
-#include "private.hpp"
+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;
+
-class CMiniportDMus : public IMiniportDMus
+/*****************************************************************************
+ * 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);
}
return m_Ref;
}
- IMP_IMiniportDMus;
- CMiniportDMus(IUnknown *OuterUnknown){}
- virtual ~CMiniportDMus(){}
-protected:
- LONG m_Ref;
+ CMiniportDMusUART(IUnknown * Unknown){}
+ ~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
+ DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
+ friend NTSTATUS
+ 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);
};
-// IUnknown methods
+/*****************************************************************************
+ * 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
-NTSTATUS
-NTAPI
-CMiniportDMus::QueryInterface(
- IN REFIID refiid,
- OUT PVOID* Output)
+ 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;
+ }
+
+ ~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 PropertyHandler_Synth(IN PPCPROPERTY_REQUEST);
+ friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream);
+};
+
+/*****************************************************************************
+ * miniport.cpp - UART miniport implementation
+ *****************************************************************************
+ * Copyright (c) 1997-2000 Microsoft Corporation. All Rights Reserved.
+ *
+ * Feb 98 MartinP -- based on UART, began deltas for DirectMusic.
+ */
+
+#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 =
{
- if (IsEqualGUIDAligned(refiid, IID_IMiniportDMus))
{
- *Output = PVOID(PUNKNOWN(this));
- PUNKNOWN(*Output)->AddRef();
- return STATUS_SUCCESS;
+ {
+ 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
}
- return STATUS_UNSUCCESSFUL;
+};
+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);
}
-// IMiniport methods
+#ifdef _MSC_VER
+#pragma code_seg("PAGE")
+#endif
-NTSTATUS
-NTAPI
-CMiniportDMus::DataRangeIntersection(
- IN ULONG PinId,
- IN PKSDATARANGE DataRange,
- IN PKSDATARANGE MatchingDataRange,
- IN ULONG OutputBufferLength,
- OUT PVOID ResultantFormat OPTIONAL,
- OUT PULONG ResultantFormatLength)
+//
+// We initialize the UART with interrupts suppressed so we don't
+// try to service the chip prematurely.
+//
+NTSTATUS CMiniportDMusUART::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
{
- return STATUS_UNSUCCESSFUL;
+ 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
-CMiniportDMus::GetDescription(
- OUT PPCFILTER_DESCRIPTOR *Description)
+InitMPU
+(
+ IN PINTERRUPTSYNC InterruptSync,
+ IN PVOID DynamicContext
+)
{
- return STATUS_UNSUCCESSFUL;
+ 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;
}
-HRESULT
-NTAPI
-CMiniportDMus::Init(
- IN PUNKNOWN pUnknownAdapter,
- IN PRESOURCELIST pResourceList,
- IN PPORTDMUS pPort,
- OUT PSERVICEGROUP *ppServiceGroup
- )
+#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
+)
{
- return STATUS_UNSUCCESSFUL;
+ 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;
}
-HRESULT
-NTAPI
-CMiniportDMus::NewStream(
- OUT PMXF *ppMXF,
- IN PUNKNOWN pOuterUnknown OPTIONAL,
- IN POOL_TYPE PoolType,
- IN ULONG uPinId,
- IN DMUS_STREAM_TYPE StreamType,
- IN PKSDATAFORMAT pDataFormat,
- OUT PSERVICEGROUP *ppServiceGroup,
- IN PAllocatorMXF pAllocatorMXF,
- IN PMASTERCLOCK pMasterClock,
- OUT PULONGLONG puuSchedulePreFetch
- )
+#ifdef _MSC_VER
+#pragma code_seg()
+#endif
+
+/*****************************************************************************
+ * SynchronizedDMusMPUWrite()
+ *****************************************************************************
+ * Writes outgoing MIDI data.
+ */
+NTSTATUS
+SynchronizedDMusMPUWrite
+(
+ IN PINTERRUPTSYNC InterruptSync,
+ IN PVOID syncWriteContext
+)
{
- return STATUS_UNSUCCESSFUL;
+ 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;
}
-VOID
-NTAPI
-CMiniportDMus::Service()
+#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
-NewMiniportDMusUART(
- OUT PMINIPORT* OutMiniport,
- IN REFCLSID ClassId)
+WriteMPU
+(
+ IN PUCHAR PortBase,
+ IN BOOLEAN IsCommand,
+ IN UCHAR Value
+)
{
- CMiniportDMus * This;
+ DPRINT("WriteMPU");
+ NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
- This = new(NonPagedPool, TAG_PORTCLASS)CMiniportDMus(NULL);
- if (!This)
- return STATUS_INSUFFICIENT_RESOURCES;
+ 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;
- *OutMiniport = (PMINIPORT)This;
- This->AddRef();
+ // 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
+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;
+
+#ifdef 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 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;
+}
+
+#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) 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 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
+