1 /*****************************************************************************
2  * miniport_dmus.cpp - UART miniport implementation
3  *****************************************************************************
4  * Copyright (c) 1997-2000 Microsoft Corporation.  All Rights Reserved.
5  *
6  *      Feb 98    MartinP   --  based on UART, began deltas for DirectMusic.
7  *
8  */
9 
10 #include "private.hpp"
11 
12 #ifndef YDEBUG
13 #define NDEBUG
14 #endif
15 
16 #include <debug.h>
17 
18 //  + for absolute / - for relative
19 
20 #define kOneMillisec (10 * 1000)
21 
22 //
23 // MPU401 ports
24 //
25 #define MPU401_REG_STATUS   0x01    // Status register
26 #define MPU401_DRR          0x40    // Output ready (for command or data)
27                                     // if this bit is set, the output FIFO is FULL
28 #define MPU401_DSR          0x80    // Input ready (for data)
29                                     // if this bit is set, the input FIFO is empty
30 
31 #define MPU401_REG_DATA     0x00    // Data in
32 #define MPU401_REG_COMMAND  0x01    // Commands
33 #define MPU401_CMD_RESET    0xFF    // Reset command
34 #define MPU401_CMD_UART     0x3F    // Switch to UART mod
35 
36 
37 /*****************************************************************************
38  * Prototypes
39  */
40 
41 NTSTATUS NTAPI InitMPU(IN PINTERRUPTSYNC InterruptSync,IN PVOID DynamicContext);
42 NTSTATUS ResetHardware(PUCHAR portBase);
43 NTSTATUS ValidatePropertyRequest(IN PPCPROPERTY_REQUEST pRequest, IN ULONG ulValueSize, IN BOOLEAN fValueRequired);
44 NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest);
45 NTSTATUS NTAPI DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
46 VOID NTAPI DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2);
47 NTSTATUS NTAPI SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext);
48 /*****************************************************************************
49  * Constants
50  */
51 
52 const BOOLEAN   COMMAND   = TRUE;
53 const BOOLEAN   DATA      = FALSE;
54 const ULONG      kMPUInputBufferSize = 128;
55 
56 
57 /*****************************************************************************
58  * Classes
59  */
60 
61 /*****************************************************************************
62  * CMiniportDMusUART
63  *****************************************************************************
64  * MPU-401 miniport.  This object is associated with the device and is
65  * created when the device is started.  The class inherits IMiniportDMus
66  * so it can expose this interface and CUnknown so it automatically gets
67  * reference counting and aggregation support.
68  */
69 class CMiniportDMusUART : public CUnknownImpl<IMiniportDMus, IMusicTechnology, IPowerNotify>
70 {
71 private:
72     KSSTATE         m_KSStateInput;         // Miniport state (RUN/PAUSE/ACQUIRE/STOP)
73     PPORTDMUS       m_pPort;                // Callback interface.
74     PUCHAR          m_pPortBase;            // Base port address.
75     PINTERRUPTSYNC  m_pInterruptSync;       // Interrupt synchronization object.
76     PSERVICEGROUP   m_pServiceGroup;        // Service group for capture.
77     PMASTERCLOCK    m_MasterClock;          // for input data
78     REFERENCE_TIME  m_InputTimeStamp;       // capture data timestamp
79     USHORT          m_NumRenderStreams;     // Num active render streams.
80     USHORT          m_NumCaptureStreams;    // Num active capture streams.
81     ULONG            m_MPUInputBufferHead;   // Index of the newest byte in the FIFO.
82     ULONG            m_MPUInputBufferTail;   // Index of the oldest empty space in the FIFO.
83     GUID            m_MusicFormatTechnology;
84     POWER_STATE     m_PowerState;           // Saved power state (D0 = full power, D3 = off)
85     BOOLEAN         m_fMPUInitialized;      // Is the MPU HW initialized.
86     BOOLEAN         m_UseIRQ;               // FALSE if no IRQ is used for MIDI.
87     UCHAR           m_MPUInputBuffer[kMPUInputBufferSize];  // Internal SW FIFO.
88 
89     /*************************************************************************
90      * CMiniportDMusUART methods
91      *
92      * These are private member functions used internally by the object.
93      * See MINIPORT.CPP for specific descriptions.
94      */
95     NTSTATUS ProcessResources
96     (
97         IN      PRESOURCELIST   ResourceList
98     );
99     NTSTATUS InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase);
100 
101 public:
102     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
103 
104     CMiniportDMusUART(IUnknown * Unknown){}
105     virtual ~CMiniportDMusUART();
106 
107     /*************************************************************************
108      * IMiniport methods
109      */
110     STDMETHODIMP_(NTSTATUS)
111     GetDescription
112     (   OUT     PPCFILTER_DESCRIPTOR *  OutFilterDescriptor
113     );
114     STDMETHODIMP_(NTSTATUS)
115     DataRangeIntersection
116     (   IN      ULONG           PinId
117     ,   IN      PKSDATARANGE    DataRange
118     ,   IN      PKSDATARANGE    MatchingDataRange
119     ,   IN      ULONG           OutputBufferLength
120     ,   OUT     PVOID           ResultantFormat
121     ,   OUT     PULONG          ResultantFormatLength
122     )
123     {
124         return STATUS_NOT_IMPLEMENTED;
125     }
126 
127     /*************************************************************************
128      * IMiniportDMus methods
129      */
130     STDMETHODIMP_(NTSTATUS) Init
131     (
132         IN      PUNKNOWN        UnknownAdapter,
133         IN      PRESOURCELIST   ResourceList,
134         IN      PPORTDMUS       Port,
135         OUT     PSERVICEGROUP * ServiceGroup
136     );
137     STDMETHODIMP_(NTSTATUS) NewStream
138     (
139         OUT     PMXF                  * Stream,
140         IN      PUNKNOWN                OuterUnknown    OPTIONAL,
141         IN      POOL_TYPE               PoolType,
142         IN      ULONG                   PinID,
143         IN      DMUS_STREAM_TYPE        StreamType,
144         IN      PKSDATAFORMAT           DataFormat,
145         OUT     PSERVICEGROUP         * ServiceGroup,
146         IN      PAllocatorMXF           AllocatorMXF,
147         IN      PMASTERCLOCK            MasterClock,
148         OUT     PULONGLONG              SchedulePreFetch
149     );
150     STDMETHODIMP_(void) Service
151     (   void
152     );
153 
154     /*************************************************************************
155      * IMusicTechnology methods
156      */
157     IMP_IMusicTechnology;
158 
159     /*************************************************************************
160      * IPowerNotify methods
161      */
162     IMP_IPowerNotify;
163 
164     /*************************************************************************
165      * Friends
166      */
167     friend class CMiniportDMusUARTStream;
168     friend NTSTATUS NTAPI
169         DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
170     friend NTSTATUS NTAPI
171         SynchronizedDMusMPUWrite(PINTERRUPTSYNC InterruptSync,PVOID syncWriteContext);
172     friend VOID NTAPI
173         DMusUARTTimerDPC(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2);
174     friend NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST PropertyRequest);
175     friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream);
176 };
177 
178 /*****************************************************************************
179  * CMiniportDMusUARTStream
180  *****************************************************************************
181  * MPU-401 miniport stream.  This object is associated with the pin and is
182  * created when the pin is instantiated.  It inherits IMXF
183  * so it can expose this interface and CUnknown so it automatically gets
184  * reference counting and aggregation support.
185  */
186 class CMiniportDMusUARTStream : public CUnknownImpl<IMXF>
187 {
188 private:
189     CMiniportDMusUART * m_pMiniport;            // Parent.
190     REFERENCE_TIME      m_SnapshotTimeStamp;    // Current snapshot of miniport's input timestamp.
191     PUCHAR              m_pPortBase;            // Base port address.
192     BOOLEAN             m_fCapture;             // Whether this is capture.
193     long                m_NumFailedMPUTries;    // Deadman timeout for MPU hardware.
194     PAllocatorMXF       m_AllocatorMXF;         // source/sink for DMus structs
195     PMXF                m_sinkMXF;              // sink for DMus capture
196     PDMUS_KERNEL_EVENT  m_DMKEvtQueue;          // queue of waiting events
197     ULONG               m_NumberOfRetries;      // Number of consecutive times the h/w was busy/full
198     ULONG               m_DMKEvtOffset;         // offset into the event
199     KDPC                m_Dpc;                  // DPC for timer
200     KTIMER              m_TimerEvent;           // timer
201     BOOL                m_TimerQueued;          // whether a timer has been set
202     KSPIN_LOCK          m_DpcSpinLock;          // protects the ConsumeEvents DPC
203 
204     STDMETHODIMP_(NTSTATUS) SourceEvtsToPort();
205     STDMETHODIMP_(NTSTATUS) ConsumeEvents();
206     STDMETHODIMP_(NTSTATUS) PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt);
207 
208 public:
209     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
210 
211     virtual ~CMiniportDMusUARTStream();
212 
213     STDMETHODIMP_(NTSTATUS) Init
214     (
215         IN      CMiniportDMusUART * pMiniport,
216         IN      PUCHAR              pPortBase,
217         IN      BOOLEAN             fCapture,
218         IN      PAllocatorMXF       allocatorMXF,
219         IN      PMASTERCLOCK        masterClock
220     );
221 
222     NTSTATUS HandlePortParams
223     (
224         IN      PPCPROPERTY_REQUEST     Request
225     );
226 
227     /*************************************************************************
228      * IMiniportStreamDMusUART methods
229      */
230     IMP_IMXF;
231 
232     STDMETHODIMP_(NTSTATUS) Write
233     (
234         IN      PVOID       BufferAddress,
235         IN      ULONG       BytesToWrite,
236         OUT     PULONG      BytesWritten
237     );
238 
239     friend VOID NTAPI
240     DMusUARTTimerDPC
241     (
242         IN      PKDPC   Dpc,
243         IN      PVOID   DeferredContext,
244         IN      PVOID   SystemArgument1,
245         IN      PVOID   SystemArgument2
246     );
247     friend NTSTATUS NTAPI PropertyHandler_Synth(IN PPCPROPERTY_REQUEST);
248     friend STDMETHODIMP_(NTSTATUS) SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream);
249 };
250 
251 
252 
253 #define STR_MODULENAME "DMusUART:Miniport: "
254 
255 #ifdef _MSC_VER
256 #pragma code_seg("PAGE")
257 #endif
258 
259 #define UartFifoOkForWrite(status)  ((status & MPU401_DRR) == 0)
260 #define UartFifoOkForRead(status)   ((status & MPU401_DSR) == 0)
261 
262 typedef struct
263 {
264     CMiniportDMusUART  *Miniport;
265     PUCHAR              PortBase;
266     PVOID               BufferAddress;
267     ULONG               Length;
268     PULONG              BytesRead;
269 }
270 SYNCWRITECONTEXT, *PSYNCWRITECONTEXT;
271 
272 /*****************************************************************************
273  * PinDataRangesStreamLegacy
274  * PinDataRangesStreamDMusic
275  *****************************************************************************
276  * Structures indicating range of valid format values for live pins.
277  */
278 static
279 KSDATARANGE_MUSIC PinDataRangesStreamLegacy =
280 {
281     {
282         {
283             sizeof(KSDATARANGE_MUSIC),
284             0,
285             0,
286             0,
287             {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)},
288             {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI)},
289             {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)}
290         }
291     },
292     {STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT)},
293     0,
294     0,
295     0xFFFF
296 };
297 static
298 KSDATARANGE_MUSIC PinDataRangesStreamDMusic =
299 {
300     {
301         {
302             sizeof(KSDATARANGE_MUSIC),
303             0,
304             0,
305             0,
306             {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)},
307             {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_DIRECTMUSIC)},
308             {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)}
309         }
310     },
311     {STATICGUIDOF(KSMUSIC_TECHNOLOGY_PORT)},
312     0,
313     0,
314     0xFFFF
315 };
316 
317 /*****************************************************************************
318  * PinDataRangePointersStreamLegacy
319  * PinDataRangePointersStreamDMusic
320  * PinDataRangePointersStreamCombined
321  *****************************************************************************
322  * List of pointers to structures indicating range of valid format values
323  * for live pins.
324  */
325 static
326 PKSDATARANGE PinDataRangePointersStreamLegacy[] =
327 {
328     PKSDATARANGE(&PinDataRangesStreamLegacy)
329 };
330 static
331 PKSDATARANGE PinDataRangePointersStreamDMusic[] =
332 {
333     PKSDATARANGE(&PinDataRangesStreamDMusic)
334 };
335 static
336 PKSDATARANGE PinDataRangePointersStreamCombined[] =
337 {
338     PKSDATARANGE(&PinDataRangesStreamLegacy)
339    ,PKSDATARANGE(&PinDataRangesStreamDMusic)
340 };
341 
342 /*****************************************************************************
343  * PinDataRangesBridge
344  *****************************************************************************
345  * Structures indicating range of valid format values for bridge pins.
346  */
347 static
348 KSDATARANGE PinDataRangesBridge[] =
349 {
350     {
351         {
352             sizeof(KSDATARANGE),
353             0,
354             0,
355             0,
356             {STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC)},
357             {STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI_BUS)},
358             {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)}
359         }
360    }
361 };
362 
363 /*****************************************************************************
364  * PinDataRangePointersBridge
365  *****************************************************************************
366  * List of pointers to structures indicating range of valid format values
367  * for bridge pins.
368  */
369 static
370 PKSDATARANGE PinDataRangePointersBridge[] =
371 {
372     &PinDataRangesBridge[0]
373 };
374 
375 /*****************************************************************************
376  * SynthProperties
377  *****************************************************************************
378  * List of properties in the Synth set.
379  */
380 static
381 PCPROPERTY_ITEM
382 SynthProperties[] =
383 {
384     // Global: S/Get synthesizer caps
385     {
386         &KSPROPSETID_Synth,
387         KSPROPERTY_SYNTH_CAPS,
388         KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
389         PropertyHandler_Synth
390     },
391     // Global: S/Get port parameters
392     {
393         &KSPROPSETID_Synth,
394         KSPROPERTY_SYNTH_PORTPARAMETERS,
395         KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
396         PropertyHandler_Synth
397     },
398     // Per stream: S/Get channel groups
399     {
400         &KSPROPSETID_Synth,
401         KSPROPERTY_SYNTH_CHANNELGROUPS,
402         KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
403         PropertyHandler_Synth
404     },
405     // Per stream: Get current latency time
406     {
407         &KSPROPSETID_Synth,
408         KSPROPERTY_SYNTH_LATENCYCLOCK,
409         KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
410         PropertyHandler_Synth
411     }
412 };
413 DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth,  SynthProperties);
414 DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSynth2, SynthProperties);
415 
416 #define kMaxNumCaptureStreams       1
417 #define kMaxNumLegacyRenderStreams  1
418 #define kMaxNumDMusicRenderStreams  1
419 
420 /*****************************************************************************
421  * MiniportPins
422  *****************************************************************************
423  * List of pins.
424  */
425 static
426 PCPIN_DESCRIPTOR MiniportPins[] =
427 {
428     {
429         kMaxNumLegacyRenderStreams,kMaxNumLegacyRenderStreams,0,    // InstanceCount
430         NULL,                                                       // AutomationTable
431         {                                                           // KsPinDescriptor
432             0,                                              // InterfacesCount
433             NULL,                                           // Interfaces
434             0,                                              // MediumsCount
435             NULL,                                           // Mediums
436             SIZEOF_ARRAY(PinDataRangePointersStreamLegacy), // DataRangesCount
437             PinDataRangePointersStreamLegacy,               // DataRanges
438             KSPIN_DATAFLOW_IN,                              // DataFlow
439             KSPIN_COMMUNICATION_SINK,                       // Communication
440             (GUID *) &KSCATEGORY_AUDIO,                     // Category
441             &KSAUDFNAME_MIDI,                               // Name
442             {0}                                               // Reserved
443         }
444     },
445     {
446         kMaxNumDMusicRenderStreams,kMaxNumDMusicRenderStreams,0,    // InstanceCount
447         NULL,                                                       // AutomationTable
448         {                                                           // KsPinDescriptor
449             0,                                              // InterfacesCount
450             NULL,                                           // Interfaces
451             0,                                              // MediumsCount
452             NULL,                                           // Mediums
453             SIZEOF_ARRAY(PinDataRangePointersStreamDMusic), // DataRangesCount
454             PinDataRangePointersStreamDMusic,               // DataRanges
455             KSPIN_DATAFLOW_IN,                              // DataFlow
456             KSPIN_COMMUNICATION_SINK,                       // Communication
457             (GUID *) &KSCATEGORY_AUDIO,                     // Category
458             &KSAUDFNAME_DMUSIC_MPU_OUT,                     // Name
459             {0}                                               // Reserved
460         }
461     },
462     {
463         0,0,0,                                      // InstanceCount
464         NULL,                                       // AutomationTable
465         {                                           // KsPinDescriptor
466             0,                                          // InterfacesCount
467             NULL,                                       // Interfaces
468             0,                                          // MediumsCount
469             NULL,                                       // Mediums
470             SIZEOF_ARRAY(PinDataRangePointersBridge),   // DataRangesCount
471             PinDataRangePointersBridge,                 // DataRanges
472             KSPIN_DATAFLOW_OUT,                         // DataFlow
473             KSPIN_COMMUNICATION_NONE,                   // Communication
474             (GUID *) &KSCATEGORY_AUDIO,                 // Category
475             NULL,                                       // Name
476             {0}                                           // Reserved
477         }
478     },
479     {
480         0,0,0,                                      // InstanceCount
481         NULL,                                       // AutomationTable
482         {                                           // KsPinDescriptor
483             0,                                          // InterfacesCount
484             NULL,                                       // Interfaces
485             0,                                          // MediumsCount
486             NULL,                                       // Mediums
487             SIZEOF_ARRAY(PinDataRangePointersBridge),   // DataRangesCount
488             PinDataRangePointersBridge,                 // DataRanges
489             KSPIN_DATAFLOW_IN,                          // DataFlow
490             KSPIN_COMMUNICATION_NONE,                   // Communication
491             (GUID *) &KSCATEGORY_AUDIO,                 // Category
492             NULL,                                       // Name
493             {0}                                           // Reserved
494         }
495     },
496     {
497         kMaxNumCaptureStreams,kMaxNumCaptureStreams,0,      // InstanceCount
498         NULL,                                               // AutomationTable
499         {                                                   // KsPinDescriptor
500             0,                                                // InterfacesCount
501             NULL,                                             // Interfaces
502             0,                                                // MediumsCount
503             NULL,                                             // Mediums
504             SIZEOF_ARRAY(PinDataRangePointersStreamCombined), // DataRangesCount
505             PinDataRangePointersStreamCombined,               // DataRanges
506             KSPIN_DATAFLOW_OUT,                               // DataFlow
507             KSPIN_COMMUNICATION_SINK,                         // Communication
508             (GUID *) &KSCATEGORY_AUDIO,                       // Category
509             &KSAUDFNAME_DMUSIC_MPU_IN,                        // Name
510             {0}                                                 // Reserved
511         }
512     }
513 };
514 
515 /*****************************************************************************
516  * MiniportNodes
517  *****************************************************************************
518  * List of nodes.
519  */
520 #define CONST_PCNODE_DESCRIPTOR(n)          { 0, NULL, &n, NULL }
521 #define CONST_PCNODE_DESCRIPTOR_AUTO(n,a)   { 0, &a, &n, NULL }
522 static
523 PCNODE_DESCRIPTOR MiniportNodes[] =
524 {
525       CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth)
526     , CONST_PCNODE_DESCRIPTOR_AUTO(KSNODETYPE_SYNTHESIZER, AutomationSynth2)
527 };
528 
529 /*****************************************************************************
530  * MiniportConnections
531  *****************************************************************************
532  * List of connections.
533  */
534 enum {
535       eSynthNode  = 0
536     , eInputNode
537 };
538 
539 enum {
540       eFilterInputPinLeg = 0,
541       eFilterInputPinDM,
542       eBridgeOutputPin,
543       eBridgeInputPin,
544       eFilterOutputPin
545 };
546 
547 static
548 PCCONNECTION_DESCRIPTOR MiniportConnections[] =
549 {  // From                                   To
550    // Node           pin                     Node           pin
551     { PCFILTER_NODE, eFilterInputPinLeg,     PCFILTER_NODE, eBridgeOutputPin }      // Legacy Stream in to synth.
552   , { PCFILTER_NODE, eFilterInputPinDM,      eSynthNode,    KSNODEPIN_STANDARD_IN } // DM Stream in to synth.
553   , { eSynthNode,    KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eBridgeOutputPin }      // Synth to bridge out.
554   , { PCFILTER_NODE, eBridgeInputPin,        eInputNode,    KSNODEPIN_STANDARD_IN } // Bridge in to input.
555   , { eInputNode,    KSNODEPIN_STANDARD_OUT, PCFILTER_NODE, eFilterOutputPin }      // Input to DM/Legacy Stream out.
556 };
557 
558 /*****************************************************************************
559  * MiniportCategories
560  *****************************************************************************
561  * List of categories.
562  */
563 static
564 GUID MiniportCategories[] =
565 {
566     {STATICGUIDOF(KSCATEGORY_AUDIO)},
567     {STATICGUIDOF(KSCATEGORY_RENDER)},
568     {STATICGUIDOF(KSCATEGORY_CAPTURE)}
569 };
570 
571 /*****************************************************************************
572  * MiniportFilterDescriptor
573  *****************************************************************************
574  * Complete miniport filter description.
575  */
576 static
577 PCFILTER_DESCRIPTOR MiniportFilterDescriptor =
578 {
579     0,                                  // Version
580     NULL,                               // AutomationTable
581     sizeof(PCPIN_DESCRIPTOR),           // PinSize
582     SIZEOF_ARRAY(MiniportPins),         // PinCount
583     MiniportPins,                       // Pins
584     sizeof(PCNODE_DESCRIPTOR),          // NodeSize
585     SIZEOF_ARRAY(MiniportNodes),        // NodeCount
586     MiniportNodes,                      // Nodes
587     SIZEOF_ARRAY(MiniportConnections),  // ConnectionCount
588     MiniportConnections,                // Connections
589     SIZEOF_ARRAY(MiniportCategories),   // CategoryCount
590     MiniportCategories                  // Categories
591 };
592 
593 #ifdef _MSC_VER
594 #pragma code_seg("PAGE")
595 #endif
596 
597 BOOLEAN  TryMPU(IN PUCHAR PortBase);
598 NTSTATUS WriteMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value);
599 
600 #ifdef _MSC_VER
601 #pragma code_seg("PAGE")
602 #endif
603 
604 //  make sure we're in UART mode
605 NTSTATUS ResetHardware(PUCHAR portBase)
606 {
607     PAGED_CODE();
608 
609     return WriteMPU(portBase,COMMAND,MPU401_CMD_UART);
610 }
611 
612 #ifdef _MSC_VER
613 #pragma code_seg("PAGE")
614 #endif
615 
616 //
617 // We initialize the UART with interrupts suppressed so we don't
618 // try to service the chip prematurely.
619 //
620 NTSTATUS CMiniportDMusUART::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
621 {
622     PAGED_CODE();
623 
624     NTSTATUS    ntStatus;
625     if (m_UseIRQ)
626     {
627         ntStatus = interruptSync->CallSynchronizedRoutine(InitMPU,PVOID(portBase));
628     }
629     else
630     {
631         ntStatus = InitMPU(NULL,PVOID(portBase));
632     }
633 
634     if (NT_SUCCESS(ntStatus))
635     {
636         //
637         // Start the UART (this should trigger an interrupt).
638         //
639         ntStatus = ResetHardware(portBase);
640     }
641     else
642     {
643         DPRINT("*** InitMPU returned with ntStatus 0x%08x ***", ntStatus);
644     }
645 
646     m_fMPUInitialized = NT_SUCCESS(ntStatus);
647 
648     return ntStatus;
649 }
650 
651 #ifdef _MSC_VER
652 #pragma code_seg()
653 #endif
654 
655 /*****************************************************************************
656  * InitMPU()
657  *****************************************************************************
658  * Synchronized routine to initialize the MPU401.
659  */
660 NTSTATUS
661 NTAPI
662 InitMPU
663 (
664     IN      PINTERRUPTSYNC  InterruptSync,
665     IN      PVOID           DynamicContext
666 )
667 {
668     DPRINT("InitMPU");
669     if (!DynamicContext)
670     {
671         return STATUS_INVALID_PARAMETER_2;
672     }
673 
674     PUCHAR      portBase = PUCHAR(DynamicContext);
675     UCHAR       status;
676     ULONGLONG   startTime;
677     BOOLEAN     success;
678     NTSTATUS    ntStatus = STATUS_SUCCESS;
679 
680     //
681     // Reset the card (puts it into "smart mode")
682     //
683     ntStatus = WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
684 
685     // wait for the acknowledgement
686     // NOTE: When the Ack arrives, it will trigger an interrupt.
687     //       Normally the DPC routine would read in the ack byte and we
688     //       would never see it, however since we have the hardware locked (HwEnter),
689     //       we can read the port before the DPC can and thus we receive the Ack.
690     startTime = PcGetTimeInterval(0);
691     success = FALSE;
692     while(PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
693     {
694         status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
695 
696         if (UartFifoOkForRead(status))                      // Is data waiting?
697         {
698             READ_PORT_UCHAR(portBase + MPU401_REG_DATA);    // yep.. read ACK
699             success = TRUE;                                 // don't need to do more
700             break;
701         }
702         KeStallExecutionProcessor(25);  //  microseconds
703     }
704 #if (DBG)
705     if (!success)
706     {
707         DPRINT("First attempt to reset the MPU didn't get ACKed.\n");
708     }
709 #endif  //  (DBG)
710 
711     // NOTE: We cannot check the ACK byte because if the card was already in
712     // UART mode it will not send an ACK but it will reset.
713 
714     // reset the card again
715     (void) WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
716 
717                                     // wait for ack (again)
718     startTime = PcGetTimeInterval(0); // This might take a while
719     BYTE dataByte = 0;
720     success = FALSE;
721     while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
722     {
723         status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
724         if (UartFifoOkForRead(status))                              // Is data waiting?
725         {
726             dataByte = READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
727             success = TRUE;                                         // don't need to do more
728             break;
729         }
730         KeStallExecutionProcessor(25);
731     }
732 
733     if ((0xFE != dataByte) || !success)   // Did we succeed? If no second ACK, something is hosed
734     {
735         DPRINT("Second attempt to reset the MPU didn't get ACKed.\n");
736         DPRINT("Init Reset failure error. Ack = %X", ULONG(dataByte));
737 
738         ntStatus = STATUS_IO_DEVICE_ERROR;
739     }
740 
741     return ntStatus;
742 }
743 
744 #ifdef _MSC_VER
745 #pragma code_seg()
746 #endif
747 
748 /*****************************************************************************
749  * CMiniportDMusUARTStream::Write()
750  *****************************************************************************
751  * Writes outgoing MIDI data.
752  */
753 STDMETHODIMP_(NTSTATUS)
754 CMiniportDMusUARTStream::
755 Write
756 (
757     IN      PVOID       BufferAddress,
758     IN      ULONG       Length,
759     OUT     PULONG      BytesWritten
760 )
761 {
762     DPRINT("Write\n");
763     ASSERT(BytesWritten);
764     if (!BufferAddress)
765     {
766         Length = 0;
767     }
768 
769     NTSTATUS ntStatus = STATUS_SUCCESS;
770 
771     if (!m_fCapture)
772     {
773         PUCHAR  pMidiData;
774         ULONG   count;
775 
776         count = 0;
777         pMidiData = PUCHAR(BufferAddress);
778 
779         if (Length)
780         {
781             SYNCWRITECONTEXT context;
782             context.Miniport        = (m_pMiniport);
783             context.PortBase        = m_pPortBase;
784             context.BufferAddress   = pMidiData;
785             context.Length          = Length;
786             context.BytesRead       = &count;
787 
788             if (m_pMiniport->m_UseIRQ)
789             {
790                 ntStatus = m_pMiniport->m_pInterruptSync->
791                                 CallSynchronizedRoutine(SynchronizedDMusMPUWrite,PVOID(&context));
792             }
793             else    //  !m_UseIRQ
794             {
795                 ntStatus = SynchronizedDMusMPUWrite(NULL,PVOID(&context));
796             }       //  !m_UseIRQ
797 
798             if (count == 0)
799             {
800                 m_NumFailedMPUTries++;
801                 if (m_NumFailedMPUTries >= 100)
802                 {
803                     ntStatus = STATUS_IO_DEVICE_ERROR;
804                     m_NumFailedMPUTries = 0;
805                 }
806             }
807             else
808             {
809                 m_NumFailedMPUTries = 0;
810             }
811         }           //  if we have data at all
812         *BytesWritten = count;
813     }
814     else    //  called write on the read stream
815     {
816         ntStatus = STATUS_INVALID_DEVICE_REQUEST;
817     }
818     return ntStatus;
819 }
820 
821 #ifdef _MSC_VER
822 #pragma code_seg()
823 #endif
824 
825 /*****************************************************************************
826  * SynchronizedDMusMPUWrite()
827  *****************************************************************************
828  * Writes outgoing MIDI data.
829  */
830 NTSTATUS
831 NTAPI
832 SynchronizedDMusMPUWrite
833 (
834     IN      PINTERRUPTSYNC  InterruptSync,
835     IN      PVOID           syncWriteContext
836 )
837 {
838     PSYNCWRITECONTEXT context;
839     context = (PSYNCWRITECONTEXT)syncWriteContext;
840     ASSERT(context->Miniport);
841     ASSERT(context->PortBase);
842     ASSERT(context->BufferAddress);
843     ASSERT(context->Length);
844     ASSERT(context->BytesRead);
845 
846     PUCHAR  pChar = PUCHAR(context->BufferAddress);
847     NTSTATUS ntStatus; // , readStatus
848     ntStatus = STATUS_SUCCESS;
849     //
850     // while we're not there yet, and
851     // while we don't have to wait on an aligned byte (including 0)
852     // (we never wait on a byte.  Better to come back later)
853     /*readStatus = */ DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
854     while (  (*(context->BytesRead) < context->Length)
855           && (  TryMPU(context->PortBase)
856              || (*(context->BytesRead)%3)
857           )  )
858     {
859         ntStatus = WriteMPU(context->PortBase,DATA,*pChar);
860         if (NT_SUCCESS(ntStatus))
861         {
862             pChar++;
863             *(context->BytesRead) = *(context->BytesRead) + 1;
864 //            readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
865         }
866         else
867         {
868             DPRINT("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus);
869             break;
870         }
871     }
872     /*readStatus = */ DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
873     return ntStatus;
874 }
875 
876 #define kMPUPollTimeout 2
877 
878 #ifdef _MSC_VER
879 #pragma code_seg()
880 #endif
881 
882 /*****************************************************************************
883  * TryMPU()
884  *****************************************************************************
885  * See if the MPU401 is free.
886  */
887 BOOLEAN
888 TryMPU
889 (
890     IN      PUCHAR      PortBase
891 )
892 {
893     BOOLEAN success;
894     USHORT  numPolls;
895     UCHAR   status;
896 
897     DPRINT("TryMPU");
898     numPolls = 0;
899 
900     while (numPolls < kMPUPollTimeout)
901     {
902         status = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
903 
904         if (UartFifoOkForWrite(status)) // Is this a good time to write data?
905         {
906             break;
907         }
908         numPolls++;
909     }
910     if (numPolls >= kMPUPollTimeout)
911     {
912         success = FALSE;
913         DPRINT("TryMPU failed");
914     }
915     else
916     {
917         success = TRUE;
918     }
919 
920     return success;
921 }
922 
923 #ifdef _MSC_VER
924 #pragma code_seg()
925 #endif
926 
927 /*****************************************************************************
928  * WriteMPU()
929  *****************************************************************************
930  * Write a byte out to the MPU401.
931  */
932 NTSTATUS
933 WriteMPU
934 (
935     IN      PUCHAR      PortBase,
936     IN      BOOLEAN     IsCommand,
937     IN      UCHAR       Value
938 )
939 {
940     DPRINT("WriteMPU");
941     NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
942 
943     if (!PortBase)
944     {
945         DPRINT("O: PortBase is zero\n");
946         return ntStatus;
947     }
948     PUCHAR deviceAddr = PortBase + MPU401_REG_DATA;
949 
950     if (IsCommand)
951     {
952         deviceAddr = PortBase + MPU401_REG_COMMAND;
953     }
954 
955     ULONGLONG startTime = PcGetTimeInterval(0);
956 
957     while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
958     {
959         UCHAR status
960         = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
961 
962         if (UartFifoOkForWrite(status)) // Is this a good time to write data?
963         {                               // yep (Jon comment)
964             WRITE_PORT_UCHAR(deviceAddr,Value);
965             DPRINT("WriteMPU emitted 0x%02x",Value);
966             ntStatus = STATUS_SUCCESS;
967             break;
968         }
969     }
970     return ntStatus;
971 }
972 
973 #ifdef _MSC_VER
974 #pragma code_seg()
975 #endif
976 
977 /*****************************************************************************
978  * SnapTimeStamp()
979  *****************************************************************************
980  *
981  * At synchronized execution to ISR, copy miniport's volatile m_InputTimeStamp
982  * to stream's m_SnapshotTimeStamp and zero m_InputTimeStamp.
983  *
984  */
985 STDMETHODIMP_(NTSTATUS)
986 SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream)
987 {
988     CMiniportDMusUARTStream *pMPStream = (CMiniportDMusUARTStream *)pStream;
989 
990     //  cache the timestamp
991     pMPStream->m_SnapshotTimeStamp = pMPStream->m_pMiniport->m_InputTimeStamp;
992 
993     //  if the window is closed, zero the timestamp
994     if (pMPStream->m_pMiniport->m_MPUInputBufferHead ==
995         pMPStream->m_pMiniport->m_MPUInputBufferTail)
996     {
997         pMPStream->m_pMiniport->m_InputTimeStamp = 0;
998     }
999 
1000     return STATUS_SUCCESS;
1001 }
1002 
1003 /*****************************************************************************
1004  * CMiniportDMusUARTStream::SourceEvtsToPort()
1005  *****************************************************************************
1006  *
1007  * Reads incoming MIDI data, feeds into DMus events.
1008  * No need to touch the hardware, just read from our SW FIFO.
1009  *
1010  */
1011 STDMETHODIMP_(NTSTATUS)
1012 CMiniportDMusUARTStream::SourceEvtsToPort()
1013 {
1014     NTSTATUS    ntStatus;
1015 
1016     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1017     DPRINT("SourceEvtsToPort");
1018 
1019     if (m_fCapture)
1020     {
1021         ntStatus = STATUS_SUCCESS;
1022         if (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
1023         {
1024             PDMUS_KERNEL_EVENT  aDMKEvt,eventTail,eventHead = NULL;
1025 
1026             while (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
1027             {
1028                 (void) m_AllocatorMXF->GetMessage(&aDMKEvt);
1029                 if (!aDMKEvt)
1030                 {
1031                     DPRINT("SourceEvtsToPort can't allocate DMKEvt");
1032                     return STATUS_INSUFFICIENT_RESOURCES;
1033                 }
1034 
1035                 //  put this event at the end of the list
1036                 if (!eventHead)
1037                 {
1038                     eventHead = aDMKEvt;
1039                 }
1040                 else
1041                 {
1042                     eventTail = eventHead;
1043                     while (eventTail->pNextEvt)
1044                     {
1045                         eventTail = eventTail->pNextEvt;
1046                     }
1047                     eventTail->pNextEvt = aDMKEvt;
1048                 }
1049                 //  read all the bytes out of the buffer, into event(s)
1050                 for (aDMKEvt->cbEvent = 0; aDMKEvt->cbEvent < sizeof(PBYTE); aDMKEvt->cbEvent++)
1051                 {
1052                     if (m_pMiniport->m_MPUInputBufferHead == m_pMiniport->m_MPUInputBufferTail)
1053                     {
1054 //                        _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort m_MPUInputBufferHead met m_MPUInputBufferTail, overrun"));
1055                         break;
1056                     }
1057                     aDMKEvt->uData.abData[aDMKEvt->cbEvent] = m_pMiniport->m_MPUInputBuffer[m_pMiniport->m_MPUInputBufferHead];
1058                     m_pMiniport->m_MPUInputBufferHead++;
1059                     if (m_pMiniport->m_MPUInputBufferHead >= kMPUInputBufferSize)
1060                     {
1061                         m_pMiniport->m_MPUInputBufferHead = 0;
1062                     }
1063                 }
1064             }
1065 
1066             if (m_pMiniport->m_UseIRQ)
1067             {
1068                 ntStatus = m_pMiniport->m_pInterruptSync->CallSynchronizedRoutine(SnapTimeStamp,PVOID(this));
1069             }
1070             else    //  !m_UseIRQ
1071             {
1072                 ntStatus = SnapTimeStamp(NULL,PVOID(this));
1073             }       //  !m_UseIRQ
1074             aDMKEvt = eventHead;
1075             while (aDMKEvt)
1076             {
1077                 aDMKEvt->ullPresTime100ns = m_SnapshotTimeStamp;
1078                 aDMKEvt->usChannelGroup = 1;
1079                 aDMKEvt->usFlags = DMUS_KEF_EVENT_INCOMPLETE;
1080                 aDMKEvt = aDMKEvt->pNextEvt;
1081             }
1082             (void)m_sinkMXF->PutMessage(eventHead);
1083         }
1084     }
1085     else    //  render stream
1086     {
1087         DPRINT("SourceEvtsToPort called on render stream");
1088         ntStatus = STATUS_INVALID_DEVICE_REQUEST;
1089     }
1090     return ntStatus;
1091 }
1092 
1093 #ifdef _MSC_VER
1094 #pragma code_seg()
1095 #endif
1096 
1097 /*****************************************************************************
1098  * DMusMPUInterruptServiceRoutine()
1099  *****************************************************************************
1100  * ISR.
1101  */
1102 NTSTATUS
1103 NTAPI
1104 DMusMPUInterruptServiceRoutine
1105 (
1106     IN      PINTERRUPTSYNC  InterruptSync,
1107     IN      PVOID           DynamicContext
1108 )
1109 {
1110     DPRINT("DMusMPUInterruptServiceRoutine");
1111     ULONGLONG   startTime;
1112 
1113     ASSERT(DynamicContext);
1114 
1115     NTSTATUS            ntStatus;
1116     BOOL                newBytesAvailable;
1117     CMiniportDMusUART   *that;
1118     NTSTATUS            clockStatus;
1119 
1120     that = (CMiniportDMusUART *) DynamicContext;
1121     newBytesAvailable = FALSE;
1122     ntStatus = STATUS_UNSUCCESSFUL;
1123 
1124     UCHAR portStatus = 0xff;
1125 
1126     //
1127     // Read the MPU status byte.
1128     //
1129     if (that->m_pPortBase)
1130     {
1131         portStatus =
1132             READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
1133 
1134         //
1135         // If there is outstanding work to do and there is a port-driver for
1136         // the MPU miniport...
1137         //
1138         if (UartFifoOkForRead(portStatus) && that->m_pPort)
1139         {
1140             startTime = PcGetTimeInterval(0);
1141             while ( (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
1142                 &&  (UartFifoOkForRead(portStatus)) )
1143             {
1144                 UCHAR uDest = READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_DATA);
1145                 if (    (that->m_KSStateInput == KSSTATE_RUN)
1146                     &&  (that->m_NumCaptureStreams)
1147                    )
1148                 {
1149                     ULONG    buffHead = that->m_MPUInputBufferHead;
1150                     if (   (that->m_MPUInputBufferTail + 1 == buffHead)
1151                         || (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead))
1152                     {
1153                         DPRINT("*****MPU Input Buffer Overflow*****");
1154                     }
1155                     else
1156                     {
1157                         if (!that->m_InputTimeStamp)
1158                         {
1159                             clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp);
1160                             if (STATUS_SUCCESS != clockStatus)
1161                             {
1162                                 DPRINT("GetTime failed for clock 0x%08x",that->m_MasterClock);
1163                             }
1164                         }
1165                         newBytesAvailable = TRUE;
1166                         //  ...place the data in our FIFO...
1167                         that->m_MPUInputBuffer[that->m_MPUInputBufferTail] = uDest;
1168                         ASSERT(that->m_MPUInputBufferTail < kMPUInputBufferSize);
1169 
1170                         that->m_MPUInputBufferTail++;
1171                         if (that->m_MPUInputBufferTail >= kMPUInputBufferSize)
1172                         {
1173                             that->m_MPUInputBufferTail = 0;
1174                         }
1175                     }
1176                 }
1177                 //
1178                 // Look for more MIDI data.
1179                 //
1180                 portStatus =
1181                     READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
1182             }   //  either there's no data or we ran too long
1183             if (newBytesAvailable)
1184             {
1185                 //
1186                 // ...notify the MPU port driver that we have bytes.
1187                 //
1188                 that->m_pPort->Notify(that->m_pServiceGroup);
1189             }
1190             ntStatus = STATUS_SUCCESS;
1191         }
1192     }
1193 
1194     return ntStatus;
1195 }
1196 
1197 /*****************************************************************************
1198  * CMiniportDMusUART::GetDescription()
1199  *****************************************************************************
1200  * Gets the topology.
1201  */
1202 STDMETHODIMP_(NTSTATUS)
1203 CMiniportDMusUART::
1204 GetDescription
1205 (
1206     OUT     PPCFILTER_DESCRIPTOR *  OutFilterDescriptor
1207 )
1208 {
1209     PAGED_CODE();
1210 
1211     ASSERT(OutFilterDescriptor);
1212 
1213     DPRINT("GetDescription");
1214 
1215     *OutFilterDescriptor = &MiniportFilterDescriptor;
1216 
1217     return STATUS_SUCCESS;
1218 }
1219 
1220 #ifdef _MSC_VER
1221 #pragma code_seg("PAGE")
1222 #endif
1223 
1224 NTSTATUS
1225 NewMiniportDMusUART(
1226     OUT PMINIPORT* OutMiniport,
1227     IN  REFCLSID ClassId)
1228 {
1229     CMiniportDMusUART * This;
1230     NTSTATUS Status;
1231 
1232     This= new(NonPagedPool, TAG_PORTCLASS) CMiniportDMusUART(NULL);
1233     if (!This)
1234         return STATUS_INSUFFICIENT_RESOURCES;
1235 
1236     Status = This->QueryInterface(IID_IMiniport, (PVOID*)OutMiniport);
1237 
1238     if (!NT_SUCCESS(Status))
1239     {
1240         delete This;
1241     }
1242 
1243     DPRINT("NewMiniportDMusUART %p Status %x\n", *OutMiniport, Status);
1244     return Status;
1245 }
1246 
1247 
1248 #ifdef _MSC_VER
1249 #pragma code_seg("PAGE")
1250 #endif
1251 
1252 /*****************************************************************************
1253  * CMiniportDMusUART::ProcessResources()
1254  *****************************************************************************
1255  * Processes the resource list, setting up helper objects accordingly.
1256  */
1257 NTSTATUS
1258 CMiniportDMusUART::
1259 ProcessResources
1260 (
1261     IN      PRESOURCELIST   ResourceList
1262 )
1263 {
1264     PAGED_CODE();
1265 
1266     DPRINT("ProcessResources");
1267 
1268     ASSERT(ResourceList);
1269     if (!ResourceList)
1270     {
1271         return STATUS_DEVICE_CONFIGURATION_ERROR;
1272     }
1273     //
1274     // Get counts for the types of resources.
1275     //
1276     ULONG   countIO     = ResourceList->NumberOfPorts();
1277     ULONG   countIRQ    = ResourceList->NumberOfInterrupts();
1278     ULONG   countDMA    = ResourceList->NumberOfDmas();
1279     ULONG   lengthIO    = ResourceList->FindTranslatedPort(0)->u.Port.Length;
1280 
1281 #if DBG
1282     DPRINT("Starting MPU401 Port 0x%lx", ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart);
1283 #endif
1284 
1285     NTSTATUS ntStatus = STATUS_SUCCESS;
1286 
1287     //
1288     // Make sure we have the expected number of resources.
1289     //
1290     if  (   (countIO != 1)
1291         ||  (countIRQ  > 1)
1292         ||  (countDMA != 0)
1293         ||  (lengthIO == 0)
1294         )
1295     {
1296         DPRINT("Unknown ResourceList configuration");
1297         ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
1298     }
1299 
1300     if (NT_SUCCESS(ntStatus))
1301     {
1302         //
1303         // Get the port address.
1304         //
1305         m_pPortBase =
1306             PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart);
1307 
1308         ntStatus = InitializeHardware(m_pInterruptSync,m_pPortBase);
1309     }
1310 
1311     return ntStatus;
1312 }
1313 
1314 #ifdef _MSC_VER
1315 #pragma code_seg("PAGE")
1316 #endif
1317 /*****************************************************************************
1318  * CMiniportDMusUART::NonDelegatingQueryInterface()
1319  *****************************************************************************
1320  * Obtains an interface.  This function works just like a COM QueryInterface
1321  * call and is used if the object is not being aggregated.
1322  */
1323 STDMETHODIMP_(NTSTATUS)
1324 CMiniportDMusUART::QueryInterface
1325 (
1326     REFIID  Interface,
1327     PVOID * Object
1328 )
1329 {
1330     PAGED_CODE();
1331 
1332     DPRINT("Miniport::NonDelegatingQueryInterface");
1333     ASSERT(Object);
1334 
1335     if (IsEqualGUIDAligned(Interface,IID_IUnknown))
1336     {
1337         *Object = PVOID(PUNKNOWN(PMINIPORTDMUS(this)));
1338     }
1339     else
1340     if (IsEqualGUIDAligned(Interface,IID_IMiniport))
1341     {
1342         *Object = PVOID(PMINIPORT(this));
1343     }
1344     else
1345     if (IsEqualGUIDAligned(Interface,IID_IMiniportDMus))
1346     {
1347         *Object = PVOID(PMINIPORTDMUS(this));
1348     }
1349     else
1350     if (IsEqualGUIDAligned(Interface,IID_IMusicTechnology))
1351     {
1352         *Object = PVOID(PMUSICTECHNOLOGY(this));
1353     }
1354     else
1355     if (IsEqualGUIDAligned(Interface,IID_IPowerNotify))
1356     {
1357         *Object = PVOID(PPOWERNOTIFY(this));
1358     }
1359     else
1360     {
1361         *Object = NULL;
1362     }
1363 
1364     if (*Object)
1365     {
1366         //
1367         // We reference the interface for the caller.
1368         //
1369         PUNKNOWN(*Object)->AddRef();
1370         return STATUS_SUCCESS;
1371     }
1372 
1373     return STATUS_INVALID_PARAMETER;
1374 }
1375 
1376 #ifdef _MSC_VER
1377 #pragma code_seg("PAGE")
1378 #endif
1379 /*****************************************************************************
1380  * CMiniportDMusUART::~CMiniportDMusUART()
1381  *****************************************************************************
1382  * Destructor.
1383  */
1384 CMiniportDMusUART::~CMiniportDMusUART(void)
1385 {
1386     PAGED_CODE();
1387 
1388     DPRINT("~CMiniportDMusUART");
1389 
1390     ASSERT(0 == m_NumCaptureStreams);
1391     ASSERT(0 == m_NumRenderStreams);
1392 
1393     //  reset the HW so we don't get anymore interrupts
1394     if (m_UseIRQ && m_pInterruptSync)
1395     {
1396         (void) m_pInterruptSync->CallSynchronizedRoutine((PINTERRUPTSYNCROUTINE)InitMPU,PVOID(m_pPortBase));
1397     }
1398     else
1399     {
1400         (void) InitMPU(NULL,PVOID(m_pPortBase));
1401     }
1402 
1403     if (m_pInterruptSync)
1404     {
1405         m_pInterruptSync->Release();
1406         m_pInterruptSync = NULL;
1407     }
1408     if (m_pServiceGroup)
1409     {
1410         m_pServiceGroup->Release();
1411         m_pServiceGroup = NULL;
1412     }
1413     if (m_pPort)
1414     {
1415         m_pPort->Release();
1416         m_pPort = NULL;
1417     }
1418 }
1419 
1420 #ifdef _MSC_VER
1421 #pragma code_seg("PAGE")
1422 #endif
1423 /*****************************************************************************
1424  * CMiniportDMusUART::Init()
1425  *****************************************************************************
1426  * Initializes a the miniport.
1427  */
1428 STDMETHODIMP_(NTSTATUS)
1429 CMiniportDMusUART::
1430 Init
1431 (
1432     IN      PUNKNOWN        UnknownInterruptSync    OPTIONAL,
1433     IN      PRESOURCELIST   ResourceList,
1434     IN      PPORTDMUS       Port_,
1435     OUT     PSERVICEGROUP * ServiceGroup
1436 )
1437 {
1438     PAGED_CODE();
1439 
1440     ASSERT(ResourceList);
1441     if (!ResourceList)
1442     {
1443         return STATUS_DEVICE_CONFIGURATION_ERROR;
1444     }
1445 
1446     ASSERT(Port_);
1447     ASSERT(ServiceGroup);
1448 
1449     DPRINT("Init");
1450 
1451     *ServiceGroup = NULL;
1452     m_pPortBase = 0;
1453     m_fMPUInitialized = FALSE;
1454 
1455     // This will remain unspecified if the miniport does not get any power
1456     // messages.
1457     //
1458     m_PowerState.DeviceState = PowerDeviceUnspecified;
1459 
1460     //
1461     // AddRef() is required because we are keeping this pointer.
1462     //
1463     m_pPort = Port_;
1464     m_pPort->AddRef();
1465 
1466     // Set dataformat.
1467     //
1468     if (IsEqualGUIDAligned(m_MusicFormatTechnology, GUID_NULL))
1469     {
1470         RtlCopyMemory(  &m_MusicFormatTechnology,
1471                         &KSMUSIC_TECHNOLOGY_PORT,
1472                         sizeof(GUID));
1473     }
1474     RtlCopyMemory(  &PinDataRangesStreamLegacy.Technology,
1475                     &m_MusicFormatTechnology,
1476                     sizeof(GUID));
1477     RtlCopyMemory(  &PinDataRangesStreamDMusic.Technology,
1478                     &m_MusicFormatTechnology,
1479                     sizeof(GUID));
1480 
1481     for (ULONG bufferCount = 0;bufferCount < kMPUInputBufferSize;bufferCount++)
1482     {
1483         m_MPUInputBuffer[bufferCount] = 0;
1484     }
1485     m_MPUInputBufferHead = 0;
1486     m_MPUInputBufferTail = 0;
1487     m_InputTimeStamp = 0;
1488     m_KSStateInput = KSSTATE_STOP;
1489 
1490     NTSTATUS ntStatus = STATUS_SUCCESS;
1491 
1492     m_NumRenderStreams = 0;
1493     m_NumCaptureStreams = 0;
1494 
1495     m_UseIRQ = TRUE;
1496     if (ResourceList->NumberOfInterrupts() == 0)
1497     {
1498         m_UseIRQ = FALSE;
1499     }
1500 
1501     ntStatus = PcNewServiceGroup(&m_pServiceGroup,NULL);
1502     if (NT_SUCCESS(ntStatus) && !m_pServiceGroup)   //  keep any error
1503     {
1504         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1505     }
1506 
1507     if (NT_SUCCESS(ntStatus))
1508     {
1509         *ServiceGroup = m_pServiceGroup;
1510         m_pServiceGroup->AddRef();
1511 
1512         //
1513         // Register the service group with the port early so the port is
1514         // prepared to handle interrupts.
1515         //
1516         m_pPort->RegisterServiceGroup(m_pServiceGroup);
1517     }
1518 
1519     if (NT_SUCCESS(ntStatus) && m_UseIRQ)
1520     {
1521         //
1522         //  Due to a bug in the InterruptSync design, we shouldn't share
1523         //  the interrupt sync object.  Whoever goes away first
1524         //  will disconnect it, and the other points off into nowhere.
1525         //
1526         //  Instead we generate our own interrupt sync object.
1527         //
1528         UnknownInterruptSync = NULL;
1529 
1530         if (UnknownInterruptSync)
1531         {
1532             ntStatus =
1533                 UnknownInterruptSync->QueryInterface
1534                 (
1535                     IID_IInterruptSync,
1536                     (PVOID *) &m_pInterruptSync
1537                 );
1538 
1539             if (!m_pInterruptSync && NT_SUCCESS(ntStatus))  //  keep any error
1540             {
1541                 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1542             }
1543             if (NT_SUCCESS(ntStatus))
1544             {                                                                           //  run this ISR first
1545                 ntStatus = m_pInterruptSync->
1546                     RegisterServiceRoutine(DMusMPUInterruptServiceRoutine,PVOID(this),TRUE);
1547             }
1548 
1549         }
1550         else
1551         {   // create our own interruptsync mechanism.
1552             ntStatus =
1553                 PcNewInterruptSync
1554                 (
1555                     &m_pInterruptSync,
1556                     NULL,
1557                     ResourceList,
1558                     0,                          // Resource Index
1559                     InterruptSyncModeNormal     // Run ISRs once until we get SUCCESS
1560                 );
1561 
1562             if (!m_pInterruptSync && NT_SUCCESS(ntStatus))    //  keep any error
1563             {
1564                 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1565             }
1566 
1567             if (NT_SUCCESS(ntStatus))
1568             {
1569                 ntStatus = m_pInterruptSync->RegisterServiceRoutine(
1570                     DMusMPUInterruptServiceRoutine,
1571                     PVOID(this),
1572                     TRUE);          //  run this ISR first
1573             }
1574             if (NT_SUCCESS(ntStatus))
1575             {
1576                 ntStatus = m_pInterruptSync->Connect();
1577             }
1578         }
1579     }
1580 
1581     if (NT_SUCCESS(ntStatus))
1582     {
1583         ntStatus = ProcessResources(ResourceList);
1584     }
1585 
1586     if (!NT_SUCCESS(ntStatus))
1587     {
1588         //
1589         // clean up our mess
1590         //
1591 
1592         // clean up the interrupt sync
1593         if( m_pInterruptSync )
1594         {
1595             m_pInterruptSync->Release();
1596             m_pInterruptSync = NULL;
1597         }
1598 
1599         // clean up the service group
1600         if( m_pServiceGroup )
1601         {
1602             m_pServiceGroup->Release();
1603             m_pServiceGroup = NULL;
1604         }
1605 
1606         // clean up the out param service group.
1607         if (*ServiceGroup)
1608         {
1609             (*ServiceGroup)->Release();
1610             (*ServiceGroup) = NULL;
1611         }
1612 
1613         // release the port
1614         m_pPort->Release();
1615         m_pPort = NULL;
1616     }
1617 
1618     return ntStatus;
1619 }
1620 
1621 #ifdef _MSC_VER
1622 #pragma code_seg("PAGE")
1623 #endif
1624 /*****************************************************************************
1625  * CMiniportDMusUART::NewStream()
1626  *****************************************************************************
1627  * Gets the topology.
1628  */
1629 STDMETHODIMP_(NTSTATUS)
1630 CMiniportDMusUART::
1631 NewStream
1632 (
1633     OUT     PMXF                  * MXF,
1634     IN      PUNKNOWN                OuterUnknown    OPTIONAL,
1635     IN      POOL_TYPE               PoolType,
1636     IN      ULONG                   PinID,
1637     IN      DMUS_STREAM_TYPE        StreamType,
1638     IN      PKSDATAFORMAT           DataFormat,
1639     OUT     PSERVICEGROUP         * ServiceGroup,
1640     IN      PAllocatorMXF           AllocatorMXF,
1641     IN      PMASTERCLOCK            MasterClock,
1642     OUT     PULONGLONG              SchedulePreFetch
1643 )
1644 {
1645     PAGED_CODE();
1646 
1647     DPRINT("NewStream");
1648     NTSTATUS ntStatus = STATUS_SUCCESS;
1649 
1650     // In 100 ns, we want stuff as soon as it comes in
1651     //
1652     *SchedulePreFetch = 0;
1653 
1654     // if we don't have any streams already open, get the hardware ready.
1655     if ((!m_NumCaptureStreams) && (!m_NumRenderStreams))
1656     {
1657         ntStatus = ResetHardware(m_pPortBase);
1658         if (!NT_SUCCESS(ntStatus))
1659         {
1660             DPRINT("CMiniportDMusUART::NewStream ResetHardware failed");
1661             return ntStatus;
1662         }
1663     }
1664 
1665     if  (   ((m_NumCaptureStreams < kMaxNumCaptureStreams)
1666             && (StreamType == DMUS_STREAM_MIDI_CAPTURE))
1667         ||  ((m_NumRenderStreams < kMaxNumLegacyRenderStreams + kMaxNumDMusicRenderStreams)
1668             && (StreamType == DMUS_STREAM_MIDI_RENDER))
1669         )
1670     {
1671         CMiniportDMusUARTStream *pStream =
1672             new(PoolType, 'wNcP') CMiniportDMusUARTStream();
1673 
1674         if (pStream)
1675         {
1676             pStream->AddRef();
1677 
1678             ntStatus =
1679                 pStream->Init(this,m_pPortBase,(StreamType == DMUS_STREAM_MIDI_CAPTURE),AllocatorMXF,MasterClock);
1680 
1681             if (NT_SUCCESS(ntStatus))
1682             {
1683                 *MXF = PMXF(pStream);
1684                 (*MXF)->AddRef();
1685 
1686                 if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
1687                 {
1688                     m_NumCaptureStreams++;
1689                     *ServiceGroup = m_pServiceGroup;
1690                     (*ServiceGroup)->AddRef();
1691                 }
1692                 else
1693                 {
1694                     m_NumRenderStreams++;
1695                     *ServiceGroup = NULL;
1696                 }
1697             }
1698 
1699             pStream->Release();
1700         }
1701         else
1702         {
1703             ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1704         }
1705     }
1706     else
1707     {
1708         ntStatus = STATUS_INVALID_DEVICE_REQUEST;
1709         if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
1710         {
1711             DPRINT("NewStream failed, too many capture streams");
1712         }
1713         else if (StreamType == DMUS_STREAM_MIDI_RENDER)
1714         {
1715             DPRINT("NewStream failed, too many render streams");
1716         }
1717         else
1718         {
1719             DPRINT("NewStream invalid stream type");
1720         }
1721     }
1722 
1723     return ntStatus;
1724 }
1725 
1726 #ifdef _MSC_VER
1727 #pragma code_seg("PAGE")
1728 #endif
1729 /*****************************************************************************
1730  * CMiniportDMusUART::SetTechnology()
1731  *****************************************************************************
1732  * Sets pindatarange technology.
1733  */
1734 STDMETHODIMP_(NTSTATUS)
1735 CMiniportDMusUART::
1736 SetTechnology
1737 (
1738     IN      const GUID *            Technology
1739 )
1740 {
1741     PAGED_CODE();
1742 
1743     NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
1744 
1745     // Fail if miniport has already been initialized.
1746     //
1747     if (NULL == m_pPort)
1748     {
1749         RtlCopyMemory(&m_MusicFormatTechnology, Technology, sizeof(GUID));
1750         ntStatus = STATUS_SUCCESS;
1751     }
1752 
1753     return ntStatus;
1754 } // SetTechnology
1755 
1756 /*****************************************************************************
1757  * CMiniportDMusUART::PowerChangeNotify()
1758  *****************************************************************************
1759  * Handle power state change for the miniport.
1760  */
1761 #ifdef _MSC_VER
1762 #pragma code_seg("PAGE")
1763 #endif
1764 
1765 STDMETHODIMP_(void)
1766 CMiniportDMusUART::
1767 PowerChangeNotify
1768 (
1769     IN      POWER_STATE             PowerState
1770 )
1771 {
1772     PAGED_CODE();
1773 
1774     DPRINT("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState);
1775 
1776     switch (PowerState.DeviceState)
1777     {
1778         case PowerDeviceD0:
1779             if (m_PowerState.DeviceState != PowerDeviceD0)
1780             {
1781                 if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase)))
1782                 {
1783                     DPRINT("InitializeHardware failed when resuming");
1784                 }
1785             }
1786             break;
1787 
1788         case PowerDeviceD1:
1789         case PowerDeviceD2:
1790         case PowerDeviceD3:
1791         default:
1792             break;
1793     }
1794     m_PowerState.DeviceState = PowerState.DeviceState;
1795 } // PowerChangeNotify
1796 
1797 #ifdef _MSC_VER
1798 #pragma code_seg("PAGE")
1799 #endif
1800 /*****************************************************************************
1801  * CMiniportDMusUARTStream::NonDelegatingQueryInterface()
1802  *****************************************************************************
1803  * Obtains an interface.  This function works just like a COM QueryInterface
1804  * call and is used if the object is not being aggregated.
1805  */
1806 STDMETHODIMP_(NTSTATUS)
1807 CMiniportDMusUARTStream::QueryInterface
1808 (
1809     REFIID  Interface,
1810     PVOID * Object
1811 )
1812 {
1813     PAGED_CODE();
1814 
1815     DPRINT("Stream::NonDelegatingQueryInterface");
1816     ASSERT(Object);
1817 
1818     if (IsEqualGUIDAligned(Interface,IID_IUnknown))
1819     {
1820         *Object = PVOID(PUNKNOWN(this));
1821     }
1822     else
1823     if (IsEqualGUIDAligned(Interface,IID_IMXF))
1824     {
1825         *Object = PVOID(PMXF(this));
1826     }
1827     else
1828     {
1829         *Object = NULL;
1830     }
1831 
1832     if (*Object)
1833     {
1834         //
1835         // We reference the interface for the caller.
1836         //
1837         PUNKNOWN(*Object)->AddRef();
1838         return STATUS_SUCCESS;
1839     }
1840 
1841     return STATUS_INVALID_PARAMETER;
1842 }
1843 
1844 #ifdef _MSC_VER
1845 #pragma code_seg("PAGE")
1846 #endif
1847 /*****************************************************************************
1848  * CMiniportDMusUARTStream::~CMiniportDMusUARTStream()
1849  *****************************************************************************
1850  * Destructs a stream.
1851  */
1852 CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void)
1853 {
1854     PAGED_CODE();
1855 
1856     DPRINT("~CMiniportDMusUARTStream");
1857 
1858     KeCancelTimer(&m_TimerEvent);
1859 
1860     if (m_DMKEvtQueue)
1861     {
1862         if (m_AllocatorMXF)
1863         {
1864             m_AllocatorMXF->PutMessage(m_DMKEvtQueue);
1865         }
1866         else
1867         {
1868             DPRINT("~CMiniportDMusUARTStream, no allocator, can't flush DMKEvts");
1869         }
1870         m_DMKEvtQueue = NULL;
1871     }
1872     if (m_AllocatorMXF)
1873     {
1874         m_AllocatorMXF->Release();
1875         m_AllocatorMXF = NULL;
1876     }
1877 
1878     if (m_pMiniport)
1879     {
1880         if (m_fCapture)
1881         {
1882             m_pMiniport->m_NumCaptureStreams--;
1883         }
1884         else
1885         {
1886             m_pMiniport->m_NumRenderStreams--;
1887         }
1888 
1889         m_pMiniport->Release();
1890     }
1891 }
1892 
1893 #ifdef _MSC_VER
1894 #pragma code_seg("PAGE")
1895 #endif
1896 /*****************************************************************************
1897  * CMiniportDMusUARTStream::Init()
1898  *****************************************************************************
1899  * Initializes a stream.
1900  */
1901 STDMETHODIMP_(NTSTATUS)
1902 CMiniportDMusUARTStream::
1903 Init
1904 (
1905     IN      CMiniportDMusUART * pMiniport,
1906     IN      PUCHAR              pPortBase,
1907     IN      BOOLEAN             fCapture,
1908     IN      PAllocatorMXF       allocatorMXF,
1909     IN      PMASTERCLOCK        masterClock
1910 )
1911 {
1912     PAGED_CODE();
1913 
1914     ASSERT(pMiniport);
1915     ASSERT(pPortBase);
1916 
1917     DPRINT("Init");
1918 
1919     m_NumFailedMPUTries = 0;
1920     m_TimerQueued = FALSE;
1921     KeInitializeSpinLock(&m_DpcSpinLock);
1922     m_pMiniport = pMiniport;
1923     m_pMiniport->AddRef();
1924 
1925     pMiniport->m_MasterClock = masterClock;
1926 
1927     m_pPortBase = pPortBase;
1928     m_fCapture = fCapture;
1929 
1930     m_SnapshotTimeStamp = 0;
1931     m_DMKEvtQueue = NULL;
1932     m_DMKEvtOffset = 0;
1933 
1934     m_NumberOfRetries = 0;
1935 
1936     if (allocatorMXF)
1937     {
1938         allocatorMXF->AddRef();
1939         m_AllocatorMXF = allocatorMXF;
1940         m_sinkMXF = m_AllocatorMXF;
1941     }
1942     else
1943     {
1944         return STATUS_INVALID_PARAMETER;
1945     }
1946 
1947     KeInitializeDpc
1948     (
1949         &m_Dpc,
1950         &::DMusUARTTimerDPC,
1951         PVOID(this)
1952     );
1953     KeInitializeTimer(&m_TimerEvent);
1954 
1955     return STATUS_SUCCESS;
1956 }
1957 
1958 #ifdef _MSC_VER
1959 #pragma code_seg("PAGE")
1960 #endif
1961 /*****************************************************************************
1962  * CMiniportDMusUARTStream::SetState()
1963  *****************************************************************************
1964  * Sets the state of the channel.
1965  */
1966 STDMETHODIMP_(NTSTATUS)
1967 CMiniportDMusUARTStream::
1968 SetState
1969 (
1970     IN      KSSTATE     NewState
1971 )
1972 {
1973     PAGED_CODE();
1974 
1975     DPRINT("SetState %d",NewState);
1976 
1977     if (NewState == KSSTATE_RUN)
1978     {
1979         if (m_pMiniport->m_fMPUInitialized)
1980         {
1981             LARGE_INTEGER timeDue100ns;
1982             timeDue100ns.QuadPart = 0;
1983             KeSetTimer(&m_TimerEvent,timeDue100ns,&m_Dpc);
1984         }
1985         else
1986         {
1987             DPRINT("CMiniportDMusUARTStream::SetState KSSTATE_RUN failed due to uninitialized MPU");
1988             return STATUS_INVALID_DEVICE_STATE;
1989         }
1990     }
1991 
1992     if (m_fCapture)
1993     {
1994         m_pMiniport->m_KSStateInput = NewState;
1995         if (NewState == KSSTATE_STOP)   //  STOPping
1996         {
1997             m_pMiniport->m_MPUInputBufferHead = 0;   // Previously read bytes are discarded.
1998             m_pMiniport->m_MPUInputBufferTail = 0;   // The entire FIFO is available.
1999         }
2000     }
2001     return STATUS_SUCCESS;
2002 }
2003 
2004 #ifdef _MSC_VER
2005 #pragma code_seg()
2006 #endif
2007 
2008 
2009 /*****************************************************************************
2010  * CMiniportDMusUART::Service()
2011  *****************************************************************************
2012  * DPC-mode service call from the port.
2013  */
2014 STDMETHODIMP_(void)
2015 CMiniportDMusUART::
2016 Service
2017 (   void
2018 )
2019 {
2020     DPRINT("Service");
2021     if (!m_NumCaptureStreams)
2022     {
2023         //  we should never get here....
2024         //  if we do, we must have read some trash,
2025         //  so just reset the input FIFO
2026         m_MPUInputBufferTail = m_MPUInputBufferHead = 0;
2027     }
2028 }
2029 
2030 #ifdef _MSC_VER
2031 #pragma code_seg("PAGE")
2032 #endif
2033 
2034 /*****************************************************************************
2035  * CMiniportDMusUARTStream::ConnectOutput()
2036  *****************************************************************************
2037  * Writes outgoing MIDI data.
2038  */
2039 NTSTATUS
2040 CMiniportDMusUARTStream::
2041 ConnectOutput(PMXF sinkMXF)
2042 {
2043     PAGED_CODE();
2044 
2045     if (m_fCapture)
2046     {
2047         if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF))
2048         {
2049             DPRINT("ConnectOutput");
2050             m_sinkMXF = sinkMXF;
2051             return STATUS_SUCCESS;
2052         }
2053         else
2054         {
2055             DPRINT("ConnectOutput failed");
2056         }
2057     }
2058     else
2059     {
2060         DPRINT("ConnectOutput called on renderer; failed");
2061     }
2062     return STATUS_UNSUCCESSFUL;
2063 }
2064 
2065 #ifdef _MSC_VER
2066 #pragma code_seg("PAGE")
2067 #endif
2068 
2069 /*****************************************************************************
2070  * CMiniportDMusUARTStream::DisconnectOutput()
2071  *****************************************************************************
2072  * Writes outgoing MIDI data.
2073  */
2074 NTSTATUS
2075 CMiniportDMusUARTStream::
2076 DisconnectOutput(PMXF sinkMXF)
2077 {
2078     PAGED_CODE();
2079 
2080     if (m_fCapture)
2081     {
2082         if ((m_sinkMXF == sinkMXF) || (!sinkMXF))
2083         {
2084             DPRINT("DisconnectOutput");
2085             m_sinkMXF = m_AllocatorMXF;
2086             return STATUS_SUCCESS;
2087         }
2088         else
2089         {
2090             DPRINT("DisconnectOutput failed");
2091         }
2092     }
2093     else
2094     {
2095         DPRINT("DisconnectOutput called on renderer; failed");
2096     }
2097     return STATUS_UNSUCCESSFUL;
2098 }
2099 
2100 #ifdef _MSC_VER
2101 #pragma code_seg()
2102 #endif
2103 
2104 
2105 /*****************************************************************************
2106  * CMiniportDMusUARTStream::PutMessageLocked()
2107  *****************************************************************************
2108  * Now that the spinlock is held, add this message to the queue.
2109  *
2110  * Writes an outgoing MIDI message.
2111  * We don't sort a new message into the queue -- we append it.
2112  * This is fine, since the sequencer feeds us sequenced data.
2113  * Timestamps will ascend by design.
2114  */
2115 NTSTATUS CMiniportDMusUARTStream::PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt)
2116 {
2117     NTSTATUS    ntStatus = STATUS_SUCCESS;
2118     PDMUS_KERNEL_EVENT  aDMKEvt;
2119 
2120     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2121 
2122     if (!m_fCapture)
2123     {
2124         DPRINT("PutMessage to render stream");
2125         if (pDMKEvt)
2126         {
2127             // m_DpcSpinLock already held
2128 
2129             if (m_DMKEvtQueue)
2130             {
2131                 aDMKEvt = m_DMKEvtQueue;            //  put pDMKEvt in event queue
2132 
2133                 while (aDMKEvt->pNextEvt)
2134                 {
2135                     aDMKEvt = aDMKEvt->pNextEvt;
2136                 }
2137                 aDMKEvt->pNextEvt = pDMKEvt;        //  here is end of queue
2138             }
2139             else                                    //  currently nothing in queue
2140             {
2141                 m_DMKEvtQueue = pDMKEvt;
2142                 if (m_DMKEvtOffset)
2143                 {
2144                     DPRINT("PutMessage  Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset);
2145                     m_DMKEvtOffset = 0;
2146                 }
2147             }
2148 
2149             // m_DpcSpinLock already held
2150         }
2151         if (!m_TimerQueued)
2152         {
2153             (void) ConsumeEvents();
2154         }
2155     }
2156     else    //  capture
2157     {
2158         DPRINT("PutMessage to capture stream");
2159         ASSERT(NULL == pDMKEvt);
2160 
2161         SourceEvtsToPort();
2162     }
2163     return ntStatus;
2164 }
2165 
2166 #ifdef _MSC_VER
2167 #pragma code_seg()
2168 #endif
2169 
2170 /*****************************************************************************
2171  * CMiniportDMusUARTStream::PutMessage()
2172  *****************************************************************************
2173  * Writes an outgoing MIDI message.
2174  * We don't sort a new message into the queue -- we append it.
2175  * This is fine, since the sequencer feeds us sequenced data.
2176  * Timestamps will ascend by design.
2177  */
2178 NTSTATUS CMiniportDMusUARTStream::PutMessage(PDMUS_KERNEL_EVENT pDMKEvt)
2179 {
2180     NTSTATUS            ntStatus = STATUS_SUCCESS;
2181     PDMUS_KERNEL_EVENT  aDMKEvt;
2182 
2183     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2184 
2185     if (!m_fCapture)
2186     {
2187         DPRINT("PutMessage to render stream");
2188         if (pDMKEvt)
2189         {
2190             KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
2191 
2192             if (m_DMKEvtQueue)
2193             {
2194                 aDMKEvt = m_DMKEvtQueue;            //  put pDMKEvt in event queue
2195 
2196                 while (aDMKEvt->pNextEvt)
2197                 {
2198                     aDMKEvt = aDMKEvt->pNextEvt;
2199                 }
2200                 aDMKEvt->pNextEvt = pDMKEvt;        //  here is end of queue
2201             }
2202             else                                    //  currently nothing in queue
2203             {
2204                 m_DMKEvtQueue = pDMKEvt;
2205                 if (m_DMKEvtOffset)
2206                 {
2207                     DPRINT("PutMessage  Nothing in the queue, but m_DMKEvtOffset == %d", m_DMKEvtOffset);
2208                     m_DMKEvtOffset = 0;
2209                 }
2210             }
2211 
2212             KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
2213         }
2214         if (!m_TimerQueued)
2215         {
2216             (void) ConsumeEvents();
2217         }
2218     }
2219     else    //  capture
2220     {
2221         DPRINT("PutMessage to capture stream");
2222         ASSERT(NULL == pDMKEvt);
2223 
2224         SourceEvtsToPort();
2225     }
2226     return ntStatus;
2227 }
2228 
2229 #ifdef _MSC_VER
2230 #pragma code_seg()
2231 #endif
2232 
2233 /*****************************************************************************
2234  * CMiniportDMusUARTStream::ConsumeEvents()
2235  *****************************************************************************
2236  * Attempts to empty the render message queue.
2237  * Called either from DPC timer or upon IRP submittal.
2238 //  TODO: support packages right
2239 //  process the package (actually, should do this above.
2240 //  treat the package as a list fragment that shouldn't be sorted.
2241 //  better yet, go through each event in the package, and when
2242 //  an event is exhausted, delete it and decrement m_offset.
2243  */
2244 NTSTATUS CMiniportDMusUARTStream::ConsumeEvents(void)
2245 {
2246     PDMUS_KERNEL_EVENT aDMKEvt;
2247 
2248     NTSTATUS        ntStatus = STATUS_SUCCESS;
2249     ULONG           bytesRemaining = 0,bytesWritten = 0;
2250     LARGE_INTEGER   aMillisecIn100ns;
2251 
2252     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2253     KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
2254 
2255     m_TimerQueued = FALSE;
2256     while (m_DMKEvtQueue)                   //  do we have anything to play at all?
2257     {
2258         aDMKEvt = m_DMKEvtQueue;                            //  event we try to play
2259         if (aDMKEvt->cbEvent)
2260         {
2261             bytesRemaining = aDMKEvt->cbEvent - m_DMKEvtOffset; //  number of bytes left in this evt
2262 
2263             ASSERT(bytesRemaining > 0);
2264             if (bytesRemaining <= 0)
2265             {
2266                 bytesRemaining = aDMKEvt->cbEvent;
2267             }
2268 
2269             if (aDMKEvt->cbEvent <= sizeof(PBYTE))                //  short message
2270             {
2271                 DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.abData[0], aDMKEvt->uData.abData[1], aDMKEvt->uData.abData[2], aDMKEvt->uData.abData[3]);
2272                 ntStatus = Write(aDMKEvt->uData.abData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
2273             }
2274             else if (PACKAGE_EVT(aDMKEvt))
2275             {
2276                 ASSERT(m_DMKEvtOffset == 0);
2277                 m_DMKEvtOffset = 0;
2278                 DPRINT("ConsumeEvents(Package)");
2279 
2280                 ntStatus = PutMessageLocked(aDMKEvt->uData.pPackageEvt);  // we already own the spinlock
2281 
2282                 // null this because we are about to throw it in the allocator
2283                 aDMKEvt->uData.pPackageEvt = NULL;
2284                 aDMKEvt->cbEvent = 0;
2285                 bytesWritten = bytesRemaining;
2286             }
2287             else                //  SysEx message
2288             {
2289                 DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.pbData[0], aDMKEvt->uData.pbData[1], aDMKEvt->uData.pbData[2], aDMKEvt->uData.pbData[3]);
2290 
2291                 ntStatus = Write(aDMKEvt->uData.pbData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
2292             }
2293         }   //  if (aDMKEvt->cbEvent)
2294         if (STATUS_SUCCESS != ntStatus)
2295         {
2296             DPRINT("ConsumeEvents: Write returned 0x%08x", ntStatus);
2297             bytesWritten = bytesRemaining;  //  just bail on this event and try next time
2298         }
2299 
2300         ASSERT(bytesWritten <= bytesRemaining);
2301         if (bytesWritten == bytesRemaining)
2302         {
2303             m_DMKEvtQueue = m_DMKEvtQueue->pNextEvt;
2304             aDMKEvt->pNextEvt = NULL;
2305 
2306             m_AllocatorMXF->PutMessage(aDMKEvt);    //  throw back in free pool
2307             m_DMKEvtOffset = 0;                     //  start fresh on next evt
2308             m_NumberOfRetries = 0;
2309         }           //  but wait ... there's more!
2310         else        //  our FIFO is full for now.
2311         {
2312             //  update our offset by that amount we did write
2313             m_DMKEvtOffset += bytesWritten;
2314             ASSERT(m_DMKEvtOffset < aDMKEvt->cbEvent);
2315 
2316             DPRINT("ConsumeEvents tried %d, wrote %d, at offset %d", bytesRemaining,bytesWritten,m_DMKEvtOffset);
2317             aMillisecIn100ns.QuadPart = -(kOneMillisec);    //  set timer, come back later
2318             m_TimerQueued = TRUE;
2319             m_NumberOfRetries++;
2320             ntStatus = KeSetTimer( &m_TimerEvent, aMillisecIn100ns, &m_Dpc );
2321             break;
2322         }   //  we didn't write it all
2323     }       //  go back, Jack, do it again (while m_DMKEvtQueue)
2324     KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
2325     return ntStatus;
2326 }
2327 
2328 #ifdef _MSC_VER
2329 #pragma code_seg()
2330 #endif
2331 
2332 /*****************************************************************************
2333  * CMiniportDMusUARTStream::HandlePortParams()
2334  *****************************************************************************
2335  * Writes an outgoing MIDI message.
2336  */
2337 NTSTATUS
2338 CMiniportDMusUARTStream::
2339 HandlePortParams
2340 (
2341     IN      PPCPROPERTY_REQUEST     pRequest
2342 )
2343 {
2344     PAGED_CODE();
2345 
2346     NTSTATUS ntStatus;
2347 
2348     if (pRequest->Verb & KSPROPERTY_TYPE_SET)
2349     {
2350         return STATUS_INVALID_DEVICE_REQUEST;
2351     }
2352 
2353     ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTH_PORTPARAMS), TRUE);
2354     if (NT_SUCCESS(ntStatus))
2355     {
2356         RtlCopyMemory(pRequest->Value, pRequest->Instance, sizeof(SYNTH_PORTPARAMS));
2357 
2358         PSYNTH_PORTPARAMS Params = (PSYNTH_PORTPARAMS)pRequest->Value;
2359 
2360         if (Params->ValidParams & ~SYNTH_PORTPARAMS_CHANNELGROUPS)
2361         {
2362             Params->ValidParams &= SYNTH_PORTPARAMS_CHANNELGROUPS;
2363         }
2364 
2365         if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS))
2366         {
2367             Params->ChannelGroups = 1;
2368         }
2369         else if (Params->ChannelGroups != 1)
2370         {
2371             Params->ChannelGroups = 1;
2372         }
2373 
2374         pRequest->ValueSize = sizeof(SYNTH_PORTPARAMS);
2375     }
2376 
2377     return ntStatus;
2378 }
2379 
2380 #ifdef _MSC_VER
2381 #pragma code_seg()
2382 #endif
2383 
2384 /*****************************************************************************
2385  * DMusTimerDPC()
2386  *****************************************************************************
2387  * The timer DPC callback. Thunks to a C++ member function.
2388  * This is called by the OS in response to the DirectMusic pin
2389  * wanting to wakeup later to process more DirectMusic stuff.
2390  */
2391 VOID
2392 NTAPI
2393 DMusUARTTimerDPC
2394 (
2395     IN  PKDPC   Dpc,
2396     IN  PVOID   DeferredContext,
2397     IN  PVOID   SystemArgument1,
2398     IN  PVOID   SystemArgument2
2399 )
2400 {
2401     ASSERT(DeferredContext);
2402 
2403     CMiniportDMusUARTStream *aStream;
2404     aStream = (CMiniportDMusUARTStream *) DeferredContext;
2405     if (aStream)
2406     {
2407         DPRINT("DMusUARTTimerDPC");
2408         if (false == aStream->m_fCapture)
2409         {
2410             (void) aStream->ConsumeEvents();
2411         }
2412         //  ignores return value!
2413     }
2414 }
2415 
2416 /*****************************************************************************
2417  * DirectMusic properties
2418  ****************************************************************************/
2419 
2420 #ifdef _MSC_VER
2421 #pragma code_seg()
2422 #endif
2423 
2424 /*
2425  *  Properties concerning synthesizer functions.
2426  */
2427 const WCHAR wszDescOut[] = L"DMusic MPU-401 Out ";
2428 const WCHAR wszDescIn[] = L"DMusic MPU-401 In ";
2429 
2430 NTSTATUS
2431 NTAPI
2432 PropertyHandler_Synth
2433 (
2434     IN      PPCPROPERTY_REQUEST     pRequest
2435 )
2436 {
2437     NTSTATUS    ntStatus;
2438 
2439     PAGED_CODE();
2440 
2441     if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
2442     {
2443         ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
2444         if (NT_SUCCESS(ntStatus))
2445         {
2446             // if return buffer can hold a ULONG, return the access flags
2447             PULONG AccessFlags = PULONG(pRequest->Value);
2448 
2449             *AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT;
2450             switch (pRequest->PropertyItem->Id)
2451             {
2452                 case KSPROPERTY_SYNTH_CAPS:
2453                 case KSPROPERTY_SYNTH_CHANNELGROUPS:
2454                     *AccessFlags |= KSPROPERTY_TYPE_GET;
2455             }
2456             switch (pRequest->PropertyItem->Id)
2457             {
2458                 case KSPROPERTY_SYNTH_CHANNELGROUPS:
2459                     *AccessFlags |= KSPROPERTY_TYPE_SET;
2460             }
2461             ntStatus = STATUS_SUCCESS;
2462             pRequest->ValueSize = sizeof(ULONG);
2463 
2464             switch (pRequest->PropertyItem->Id)
2465             {
2466                 case KSPROPERTY_SYNTH_PORTPARAMETERS:
2467                     if (pRequest->MinorTarget)
2468                     {
2469                         *AccessFlags |= KSPROPERTY_TYPE_GET;
2470                     }
2471                     else
2472                     {
2473                         pRequest->ValueSize = 0;
2474                         ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2475                     }
2476             }
2477         }
2478     }
2479     else
2480     {
2481         ntStatus = STATUS_SUCCESS;
2482         switch(pRequest->PropertyItem->Id)
2483         {
2484             case KSPROPERTY_SYNTH_CAPS:
2485                 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CAPS");
2486 
2487                 if (pRequest->Verb & KSPROPERTY_TYPE_SET)
2488                 {
2489                     ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2490                 }
2491 
2492                 if (NT_SUCCESS(ntStatus))
2493                 {
2494                     ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTHCAPS), TRUE);
2495 
2496                     if (NT_SUCCESS(ntStatus))
2497                     {
2498                         SYNTHCAPS *caps = (SYNTHCAPS*)pRequest->Value;
2499                         int increment;
2500                         RtlZeroMemory(caps, sizeof(SYNTHCAPS));
2501                         // XXX Different guids for different instances!
2502                         //
2503                         if (pRequest->Node == eSynthNode)
2504                         {
2505                             increment = sizeof(wszDescOut) - 2;
2506                             RtlCopyMemory( caps->Description,wszDescOut,increment);
2507                             caps->Guid           = CLSID_MiniportDriverDMusUART;
2508                         }
2509                         else
2510                         {
2511                             increment = sizeof(wszDescIn) - 2;
2512                             RtlCopyMemory( caps->Description,wszDescIn,increment);
2513                             caps->Guid           = CLSID_MiniportDriverDMusUARTCapture;
2514                         }
2515 
2516                         caps->Flags              = SYNTH_PC_EXTERNAL;
2517                         caps->MemorySize         = 0;
2518                         caps->MaxChannelGroups   = 1;
2519                         caps->MaxVoices          = 0xFFFFFFFF;
2520                         caps->MaxAudioChannels   = 0xFFFFFFFF;
2521 
2522                         caps->EffectFlags        = 0;
2523 
2524                         CMiniportDMusUART *aMiniport;
2525                         ASSERT(pRequest->MajorTarget);
2526                         aMiniport = (CMiniportDMusUART *)(PMINIPORTDMUS)(pRequest->MajorTarget);
2527                         WCHAR wszDesc2[16];
2528                         int cLen;
2529                         cLen = swprintf(wszDesc2,L"[%03x]\0",PtrToUlong(aMiniport->m_pPortBase));
2530 
2531                         cLen *= sizeof(WCHAR);
2532                         RtlCopyMemory((WCHAR *)((DWORD_PTR)(caps->Description) + increment),
2533                                        wszDesc2,
2534                                        cLen);
2535 
2536 
2537                         pRequest->ValueSize = sizeof(SYNTHCAPS);
2538                     }
2539                 }
2540 
2541                 break;
2542 
2543              case KSPROPERTY_SYNTH_PORTPARAMETERS:
2544                 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_PORTPARAMETERS");
2545     {
2546                 CMiniportDMusUARTStream *aStream;
2547 
2548                 aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
2549                 if (aStream)
2550                 {
2551                     ntStatus = aStream->HandlePortParams(pRequest);
2552                 }
2553                 else
2554                 {
2555                     ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2556                 }
2557                }
2558                break;
2559 
2560             case KSPROPERTY_SYNTH_CHANNELGROUPS:
2561                 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CHANNELGROUPS");
2562 
2563                 ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
2564                 if (NT_SUCCESS(ntStatus))
2565                 {
2566                     *(PULONG)(pRequest->Value) = 1;
2567                     pRequest->ValueSize = sizeof(ULONG);
2568                 }
2569                 break;
2570 
2571             case KSPROPERTY_SYNTH_LATENCYCLOCK:
2572                 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_LATENCYCLOCK");
2573 
2574                 if(pRequest->Verb & KSPROPERTY_TYPE_SET)
2575                 {
2576                     ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2577                 }
2578                 else
2579                 {
2580                     ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONGLONG), TRUE);
2581                     if(NT_SUCCESS(ntStatus))
2582                     {
2583                         REFERENCE_TIME rtLatency;
2584                         CMiniportDMusUARTStream *aStream;
2585 
2586                         aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
2587                         if(aStream == NULL)
2588                         {
2589                             ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2590                         }
2591                         else
2592                         {
2593                             aStream->m_pMiniport->m_MasterClock->GetTime(&rtLatency);
2594                             *((PULONGLONG)pRequest->Value) = rtLatency;
2595                             pRequest->ValueSize = sizeof(ULONGLONG);
2596                         }
2597                     }
2598                 }
2599                 break;
2600 
2601             default:
2602                 DPRINT("Unhandled property in PropertyHandler_Synth");
2603                 break;
2604         }
2605     }
2606     return ntStatus;
2607 }
2608 
2609 /*****************************************************************************
2610  * ValidatePropertyRequest()
2611  *****************************************************************************
2612  * Validates pRequest.
2613  *  Checks if the ValueSize is valid
2614  *  Checks if the Value is valid
2615  *
2616  *  This does not update pRequest->ValueSize if it returns NT_SUCCESS.
2617  *  Caller must set pRequest->ValueSize in case of NT_SUCCESS.
2618  */
2619 NTSTATUS ValidatePropertyRequest
2620 (
2621     IN      PPCPROPERTY_REQUEST     pRequest,
2622     IN      ULONG                   ulValueSize,
2623     IN      BOOLEAN                 fValueRequired
2624 )
2625 {
2626     NTSTATUS    ntStatus;
2627 
2628     if (pRequest->ValueSize >= ulValueSize)
2629     {
2630         if (fValueRequired && NULL == pRequest->Value)
2631         {
2632             ntStatus = STATUS_INVALID_PARAMETER;
2633         }
2634         else
2635         {
2636             ntStatus = STATUS_SUCCESS;
2637         }
2638     }
2639     else  if (0 == pRequest->ValueSize)
2640     {
2641         ntStatus = STATUS_BUFFER_OVERFLOW;
2642     }
2643     else
2644     {
2645         ntStatus = STATUS_BUFFER_TOO_SMALL;
2646     }
2647 
2648     if (STATUS_BUFFER_OVERFLOW == ntStatus)
2649     {
2650         pRequest->ValueSize = ulValueSize;
2651     }
2652     else
2653     {
2654         pRequest->ValueSize = 0;
2655     }
2656 
2657     return ntStatus;
2658 } // ValidatePropertyRequest
2659 
2660 #ifdef _MSC_VER
2661 #pragma code_seg()
2662 #endif
2663 
2664 
2665