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