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