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