Synchronize with trunk revision 59636 (just before Alex's CreateProcess revamp).
[reactos.git] / drivers / wdm / audio / backpln / portcls / miniport_dmus.cpp
index 65d32ed..7fd55ae 100644 (file)
-/*
- * 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
+/*****************************************************************************
+ * 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"
 
-class CMiniportDMus : public IMiniportDMus
+//  + 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);
 
@@ -29,107 +116,2583 @@ public:
         }
         return m_Ref;
     }
-    IMP_IMiniportDMus;
-    CMiniportDMus(IUnknown *OuterUnknown){}
-    virtual ~CMiniportDMus(){}
 
-protected:
-    LONG 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);
 };
 
-// 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;
+    }
+
+    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 =
 {
-    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
+#ifdef _MSC_VER
+#pragma code_seg()
+#endif
+
+/*****************************************************************************
+ * SynchronizedDMusMPUWrite()
+ *****************************************************************************
+ * Writes outgoing MIDI data.
+ */
+NTSTATUS
 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
-    )
+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);
 
-    *OutMiniport = (PMINIPORT)This;
-    This->AddRef();
+        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 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
+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
+