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