[CMAKE]
[reactos.git] / 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 NTAPI
185 DMusMPUInterruptServiceRoutine(PINTERRUPTSYNC InterruptSync,PVOID DynamicContext);
186 friend NTSTATUS NTAPI
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 NTAPI 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 NTAPI
696 InitMPU
697 (
698 IN PINTERRUPTSYNC InterruptSync,
699 IN PVOID DynamicContext
700 )
701 {
702 DPRINT("InitMPU");
703 if (!DynamicContext)
704 {
705 return STATUS_INVALID_PARAMETER_2;
706 }
707
708 PUCHAR portBase = PUCHAR(DynamicContext);
709 UCHAR status;
710 ULONGLONG startTime;
711 BOOLEAN success;
712 NTSTATUS ntStatus = STATUS_SUCCESS;
713
714 //
715 // Reset the card (puts it into "smart mode")
716 //
717 ntStatus = WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
718
719 // wait for the acknowledgement
720 // NOTE: When the Ack arrives, it will trigger an interrupt.
721 // Normally the DPC routine would read in the ack byte and we
722 // would never see it, however since we have the hardware locked (HwEnter),
723 // we can read the port before the DPC can and thus we receive the Ack.
724 startTime = PcGetTimeInterval(0);
725 success = FALSE;
726 while(PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
727 {
728 status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
729
730 if (UartFifoOkForRead(status)) // Is data waiting?
731 {
732 READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
733 success = TRUE; // don't need to do more
734 break;
735 }
736 KeStallExecutionProcessor(25); // microseconds
737 }
738 #if (DBG)
739 if (!success)
740 {
741 DPRINT("First attempt to reset the MPU didn't get ACKed.\n");
742 }
743 #endif // (DBG)
744
745 // NOTE: We cannot check the ACK byte because if the card was already in
746 // UART mode it will not send an ACK but it will reset.
747
748 // reset the card again
749 (void) WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
750
751 // wait for ack (again)
752 startTime = PcGetTimeInterval(0); // This might take a while
753 BYTE dataByte = 0;
754 success = FALSE;
755 while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
756 {
757 status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
758 if (UartFifoOkForRead(status)) // Is data waiting?
759 {
760 dataByte = READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
761 success = TRUE; // don't need to do more
762 break;
763 }
764 KeStallExecutionProcessor(25);
765 }
766
767 if ((0xFE != dataByte) || !success) // Did we succeed? If no second ACK, something is hosed
768 {
769 DPRINT("Second attempt to reset the MPU didn't get ACKed.\n");
770 DPRINT("Init Reset failure error. Ack = %X", ULONG(dataByte));
771
772 ntStatus = STATUS_IO_DEVICE_ERROR;
773 }
774
775 return ntStatus;
776 }
777
778 #ifdef _MSC_VER
779 #pragma code_seg()
780 #endif
781
782 /*****************************************************************************
783 * CMiniportDMusUARTStream::Write()
784 *****************************************************************************
785 * Writes outgoing MIDI data.
786 */
787 STDMETHODIMP_(NTSTATUS)
788 CMiniportDMusUARTStream::
789 Write
790 (
791 IN PVOID BufferAddress,
792 IN ULONG Length,
793 OUT PULONG BytesWritten
794 )
795 {
796 DPRINT("Write\n");
797 ASSERT(BytesWritten);
798 if (!BufferAddress)
799 {
800 Length = 0;
801 }
802
803 NTSTATUS ntStatus = STATUS_SUCCESS;
804
805 if (!m_fCapture)
806 {
807 PUCHAR pMidiData;
808 ULONG count;
809
810 count = 0;
811 pMidiData = PUCHAR(BufferAddress);
812
813 if (Length)
814 {
815 SYNCWRITECONTEXT context;
816 context.Miniport = (m_pMiniport);
817 context.PortBase = m_pPortBase;
818 context.BufferAddress = pMidiData;
819 context.Length = Length;
820 context.BytesRead = &count;
821
822 if (m_pMiniport->m_UseIRQ)
823 {
824 ntStatus = m_pMiniport->m_pInterruptSync->
825 CallSynchronizedRoutine(SynchronizedDMusMPUWrite,PVOID(&context));
826 }
827 else // !m_UseIRQ
828 {
829 ntStatus = SynchronizedDMusMPUWrite(NULL,PVOID(&context));
830 } // !m_UseIRQ
831
832 if (count == 0)
833 {
834 m_NumFailedMPUTries++;
835 if (m_NumFailedMPUTries >= 100)
836 {
837 ntStatus = STATUS_IO_DEVICE_ERROR;
838 m_NumFailedMPUTries = 0;
839 }
840 }
841 else
842 {
843 m_NumFailedMPUTries = 0;
844 }
845 } // if we have data at all
846 *BytesWritten = count;
847 }
848 else // called write on the read stream
849 {
850 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
851 }
852 return ntStatus;
853 }
854
855 #ifdef _MSC_VER
856 #pragma code_seg()
857 #endif
858
859 /*****************************************************************************
860 * SynchronizedDMusMPUWrite()
861 *****************************************************************************
862 * Writes outgoing MIDI data.
863 */
864 NTSTATUS
865 NTAPI
866 SynchronizedDMusMPUWrite
867 (
868 IN PINTERRUPTSYNC InterruptSync,
869 IN PVOID syncWriteContext
870 )
871 {
872 PSYNCWRITECONTEXT context;
873 context = (PSYNCWRITECONTEXT)syncWriteContext;
874 ASSERT(context->Miniport);
875 ASSERT(context->PortBase);
876 ASSERT(context->BufferAddress);
877 ASSERT(context->Length);
878 ASSERT(context->BytesRead);
879
880 PUCHAR pChar = PUCHAR(context->BufferAddress);
881 NTSTATUS ntStatus,readStatus;
882 ntStatus = STATUS_SUCCESS;
883 //
884 // while we're not there yet, and
885 // while we don't have to wait on an aligned byte (including 0)
886 // (we never wait on a byte. Better to come back later)
887 readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
888 while ( (*(context->BytesRead) < context->Length)
889 && ( TryMPU(context->PortBase)
890 || (*(context->BytesRead)%3)
891 ) )
892 {
893 ntStatus = WriteMPU(context->PortBase,DATA,*pChar);
894 if (NT_SUCCESS(ntStatus))
895 {
896 pChar++;
897 *(context->BytesRead) = *(context->BytesRead) + 1;
898 // readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
899 }
900 else
901 {
902 DPRINT("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus);
903 break;
904 }
905 }
906 readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
907 return ntStatus;
908 }
909
910 #define kMPUPollTimeout 2
911
912 #ifdef _MSC_VER
913 #pragma code_seg()
914 #endif
915
916 /*****************************************************************************
917 * TryMPU()
918 *****************************************************************************
919 * See if the MPU401 is free.
920 */
921 BOOLEAN
922 TryMPU
923 (
924 IN PUCHAR PortBase
925 )
926 {
927 BOOLEAN success;
928 USHORT numPolls;
929 UCHAR status;
930
931 DPRINT("TryMPU");
932 numPolls = 0;
933
934 while (numPolls < kMPUPollTimeout)
935 {
936 status = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
937
938 if (UartFifoOkForWrite(status)) // Is this a good time to write data?
939 {
940 break;
941 }
942 numPolls++;
943 }
944 if (numPolls >= kMPUPollTimeout)
945 {
946 success = FALSE;
947 DPRINT("TryMPU failed");
948 }
949 else
950 {
951 success = TRUE;
952 }
953
954 return success;
955 }
956
957 #ifdef _MSC_VER
958 #pragma code_seg()
959 #endif
960
961 /*****************************************************************************
962 * WriteMPU()
963 *****************************************************************************
964 * Write a byte out to the MPU401.
965 */
966 NTSTATUS
967 WriteMPU
968 (
969 IN PUCHAR PortBase,
970 IN BOOLEAN IsCommand,
971 IN UCHAR Value
972 )
973 {
974 DPRINT("WriteMPU");
975 NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
976
977 if (!PortBase)
978 {
979 DPRINT("O: PortBase is zero\n");
980 return ntStatus;
981 }
982 PUCHAR deviceAddr = PortBase + MPU401_REG_DATA;
983
984 if (IsCommand)
985 {
986 deviceAddr = PortBase + MPU401_REG_COMMAND;
987 }
988
989 ULONGLONG startTime = PcGetTimeInterval(0);
990
991 while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
992 {
993 UCHAR status
994 = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
995
996 if (UartFifoOkForWrite(status)) // Is this a good time to write data?
997 { // yep (Jon comment)
998 WRITE_PORT_UCHAR(deviceAddr,Value);
999 DPRINT("WriteMPU emitted 0x%02x",Value);
1000 ntStatus = STATUS_SUCCESS;
1001 break;
1002 }
1003 }
1004 return ntStatus;
1005 }
1006
1007 #ifdef _MSC_VER
1008 #pragma code_seg()
1009 #endif
1010
1011 /*****************************************************************************
1012 * SnapTimeStamp()
1013 *****************************************************************************
1014 *
1015 * At synchronized execution to ISR, copy miniport's volatile m_InputTimeStamp
1016 * to stream's m_SnapshotTimeStamp and zero m_InputTimeStamp.
1017 *
1018 */
1019 STDMETHODIMP_(NTSTATUS)
1020 SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream)
1021 {
1022 CMiniportDMusUARTStream *pMPStream = (CMiniportDMusUARTStream *)pStream;
1023
1024 // cache the timestamp
1025 pMPStream->m_SnapshotTimeStamp = pMPStream->m_pMiniport->m_InputTimeStamp;
1026
1027 // if the window is closed, zero the timestamp
1028 if (pMPStream->m_pMiniport->m_MPUInputBufferHead ==
1029 pMPStream->m_pMiniport->m_MPUInputBufferTail)
1030 {
1031 pMPStream->m_pMiniport->m_InputTimeStamp = 0;
1032 }
1033
1034 return STATUS_SUCCESS;
1035 }
1036
1037 /*****************************************************************************
1038 * CMiniportDMusUARTStream::SourceEvtsToPort()
1039 *****************************************************************************
1040 *
1041 * Reads incoming MIDI data, feeds into DMus events.
1042 * No need to touch the hardware, just read from our SW FIFO.
1043 *
1044 */
1045 STDMETHODIMP_(NTSTATUS)
1046 CMiniportDMusUARTStream::SourceEvtsToPort()
1047 {
1048 NTSTATUS ntStatus;
1049
1050 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1051 DPRINT("SourceEvtsToPort");
1052
1053 if (m_fCapture)
1054 {
1055 ntStatus = STATUS_SUCCESS;
1056 if (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
1057 {
1058 PDMUS_KERNEL_EVENT aDMKEvt,eventTail,eventHead = NULL;
1059
1060 while (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
1061 {
1062 (void) m_AllocatorMXF->GetMessage(&aDMKEvt);
1063 if (!aDMKEvt)
1064 {
1065 DPRINT("SourceEvtsToPort can't allocate DMKEvt");
1066 return STATUS_INSUFFICIENT_RESOURCES;
1067 }
1068
1069 // put this event at the end of the list
1070 if (!eventHead)
1071 {
1072 eventHead = aDMKEvt;
1073 }
1074 else
1075 {
1076 eventTail = eventHead;
1077 while (eventTail->pNextEvt)
1078 {
1079 eventTail = eventTail->pNextEvt;
1080 }
1081 eventTail->pNextEvt = aDMKEvt;
1082 }
1083 // read all the bytes out of the buffer, into event(s)
1084 for (aDMKEvt->cbEvent = 0; aDMKEvt->cbEvent < sizeof(PBYTE); aDMKEvt->cbEvent++)
1085 {
1086 if (m_pMiniport->m_MPUInputBufferHead == m_pMiniport->m_MPUInputBufferTail)
1087 {
1088 // _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort m_MPUInputBufferHead met m_MPUInputBufferTail, overrun"));
1089 break;
1090 }
1091 aDMKEvt->uData.abData[aDMKEvt->cbEvent] = m_pMiniport->m_MPUInputBuffer[m_pMiniport->m_MPUInputBufferHead];
1092 m_pMiniport->m_MPUInputBufferHead++;
1093 if (m_pMiniport->m_MPUInputBufferHead >= kMPUInputBufferSize)
1094 {
1095 m_pMiniport->m_MPUInputBufferHead = 0;
1096 }
1097 }
1098 }
1099
1100 if (m_pMiniport->m_UseIRQ)
1101 {
1102 ntStatus = m_pMiniport->m_pInterruptSync->CallSynchronizedRoutine(SnapTimeStamp,PVOID(this));
1103 }
1104 else // !m_UseIRQ
1105 {
1106 ntStatus = SnapTimeStamp(NULL,PVOID(this));
1107 } // !m_UseIRQ
1108 aDMKEvt = eventHead;
1109 while (aDMKEvt)
1110 {
1111 aDMKEvt->ullPresTime100ns = m_SnapshotTimeStamp;
1112 aDMKEvt->usChannelGroup = 1;
1113 aDMKEvt->usFlags = DMUS_KEF_EVENT_INCOMPLETE;
1114 aDMKEvt = aDMKEvt->pNextEvt;
1115 }
1116 (void)m_sinkMXF->PutMessage(eventHead);
1117 }
1118 }
1119 else // render stream
1120 {
1121 DPRINT("SourceEvtsToPort called on render stream");
1122 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
1123 }
1124 return ntStatus;
1125 }
1126
1127 #ifdef _MSC_VER
1128 #pragma code_seg()
1129 #endif
1130
1131 /*****************************************************************************
1132 * DMusMPUInterruptServiceRoutine()
1133 *****************************************************************************
1134 * ISR.
1135 */
1136 NTSTATUS
1137 NTAPI
1138 DMusMPUInterruptServiceRoutine
1139 (
1140 IN PINTERRUPTSYNC InterruptSync,
1141 IN PVOID DynamicContext
1142 )
1143 {
1144 DPRINT("DMusMPUInterruptServiceRoutine");
1145 ULONGLONG startTime;
1146
1147 ASSERT(DynamicContext);
1148
1149 NTSTATUS ntStatus;
1150 BOOL newBytesAvailable;
1151 CMiniportDMusUART *that;
1152 NTSTATUS clockStatus;
1153
1154 that = (CMiniportDMusUART *) DynamicContext;
1155 newBytesAvailable = FALSE;
1156 ntStatus = STATUS_UNSUCCESSFUL;
1157
1158 UCHAR portStatus = 0xff;
1159
1160 //
1161 // Read the MPU status byte.
1162 //
1163 if (that->m_pPortBase)
1164 {
1165 portStatus =
1166 READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
1167
1168 //
1169 // If there is outstanding work to do and there is a port-driver for
1170 // the MPU miniport...
1171 //
1172 if (UartFifoOkForRead(portStatus) && that->m_pPort)
1173 {
1174 startTime = PcGetTimeInterval(0);
1175 while ( (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
1176 && (UartFifoOkForRead(portStatus)) )
1177 {
1178 UCHAR uDest = READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_DATA);
1179 if ( (that->m_KSStateInput == KSSTATE_RUN)
1180 && (that->m_NumCaptureStreams)
1181 )
1182 {
1183 ULONG buffHead = that->m_MPUInputBufferHead;
1184 if ( (that->m_MPUInputBufferTail + 1 == buffHead)
1185 || (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead))
1186 {
1187 DPRINT("*****MPU Input Buffer Overflow*****");
1188 }
1189 else
1190 {
1191 if (!that->m_InputTimeStamp)
1192 {
1193 clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp);
1194 if (STATUS_SUCCESS != clockStatus)
1195 {
1196 DPRINT("GetTime failed for clock 0x%08x",that->m_MasterClock);
1197 }
1198 }
1199 newBytesAvailable = TRUE;
1200 // ...place the data in our FIFO...
1201 that->m_MPUInputBuffer[that->m_MPUInputBufferTail] = uDest;
1202 ASSERT(that->m_MPUInputBufferTail < kMPUInputBufferSize);
1203
1204 that->m_MPUInputBufferTail++;
1205 if (that->m_MPUInputBufferTail >= kMPUInputBufferSize)
1206 {
1207 that->m_MPUInputBufferTail = 0;
1208 }
1209 }
1210 }
1211 //
1212 // Look for more MIDI data.
1213 //
1214 portStatus =
1215 READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
1216 } // either there's no data or we ran too long
1217 if (newBytesAvailable)
1218 {
1219 //
1220 // ...notify the MPU port driver that we have bytes.
1221 //
1222 that->m_pPort->Notify(that->m_pServiceGroup);
1223 }
1224 ntStatus = STATUS_SUCCESS;
1225 }
1226 }
1227
1228 return ntStatus;
1229 }
1230
1231 /*****************************************************************************
1232 * CMiniportDMusUART::GetDescription()
1233 *****************************************************************************
1234 * Gets the topology.
1235 */
1236 STDMETHODIMP_(NTSTATUS)
1237 CMiniportDMusUART::
1238 GetDescription
1239 (
1240 OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
1241 )
1242 {
1243 PAGED_CODE();
1244
1245 ASSERT(OutFilterDescriptor);
1246
1247 DPRINT("GetDescription");
1248
1249 *OutFilterDescriptor = &MiniportFilterDescriptor;
1250
1251 return STATUS_SUCCESS;
1252 }
1253
1254 #ifdef _MSC_VER
1255 #pragma code_seg("PAGE")
1256 #endif
1257
1258 NTSTATUS
1259 NewMiniportDMusUART(
1260 OUT PMINIPORT* OutMiniport,
1261 IN REFCLSID ClassId)
1262 {
1263 CMiniportDMusUART * This;
1264 NTSTATUS Status;
1265
1266 This= new(NonPagedPool, TAG_PORTCLASS) CMiniportDMusUART(NULL);
1267 if (!This)
1268 return STATUS_INSUFFICIENT_RESOURCES;
1269
1270 Status = This->QueryInterface(IID_IMiniport, (PVOID*)OutMiniport);
1271
1272 if (!NT_SUCCESS(Status))
1273 {
1274 delete This;
1275 }
1276
1277 DPRINT("NewMiniportDMusUART %p Status %x\n", *OutMiniport, Status);
1278 return Status;
1279 }
1280
1281
1282 #ifdef _MSC_VER
1283 #pragma code_seg("PAGE")
1284 #endif
1285
1286 /*****************************************************************************
1287 * CMiniportDMusUART::ProcessResources()
1288 *****************************************************************************
1289 * Processes the resource list, setting up helper objects accordingly.
1290 */
1291 NTSTATUS
1292 CMiniportDMusUART::
1293 ProcessResources
1294 (
1295 IN PRESOURCELIST ResourceList
1296 )
1297 {
1298 PAGED_CODE();
1299
1300 DPRINT("ProcessResources");
1301
1302 ASSERT(ResourceList);
1303 if (!ResourceList)
1304 {
1305 return STATUS_DEVICE_CONFIGURATION_ERROR;
1306 }
1307 //
1308 // Get counts for the types of resources.
1309 //
1310 ULONG countIO = ResourceList->NumberOfPorts();
1311 ULONG countIRQ = ResourceList->NumberOfInterrupts();
1312 ULONG countDMA = ResourceList->NumberOfDmas();
1313 ULONG lengthIO = ResourceList->FindTranslatedPort(0)->u.Port.Length;
1314
1315 #ifdef DBG
1316 DPRINT("Starting MPU401 Port 0x%lx", ResourceList->FindTranslatedPort(0)->u.Port.Start.LowPart);
1317 #endif
1318
1319 NTSTATUS ntStatus = STATUS_SUCCESS;
1320
1321 //
1322 // Make sure we have the expected number of resources.
1323 //
1324 if ( (countIO != 1)
1325 || (countIRQ > 1)
1326 || (countDMA != 0)
1327 || (lengthIO == 0)
1328 )
1329 {
1330 DPRINT("Unknown ResourceList configuraton");
1331 ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
1332 }
1333
1334 if (NT_SUCCESS(ntStatus))
1335 {
1336 //
1337 // Get the port address.
1338 //
1339 m_pPortBase =
1340 PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart);
1341
1342 ntStatus = InitializeHardware(m_pInterruptSync,m_pPortBase);
1343 }
1344
1345 return ntStatus;
1346 }
1347
1348 #ifdef _MSC_VER
1349 #pragma code_seg("PAGE")
1350 #endif
1351 /*****************************************************************************
1352 * CMiniportDMusUART::NonDelegatingQueryInterface()
1353 *****************************************************************************
1354 * Obtains an interface. This function works just like a COM QueryInterface
1355 * call and is used if the object is not being aggregated.
1356 */
1357 STDMETHODIMP_(NTSTATUS)
1358 CMiniportDMusUART::QueryInterface
1359 (
1360 REFIID Interface,
1361 PVOID * Object
1362 )
1363 {
1364 PAGED_CODE();
1365
1366 DPRINT("Miniport::NonDelegatingQueryInterface");
1367 ASSERT(Object);
1368
1369 if (IsEqualGUIDAligned(Interface,IID_IUnknown))
1370 {
1371 *Object = PVOID(PUNKNOWN(PMINIPORTDMUS(this)));
1372 }
1373 else
1374 if (IsEqualGUIDAligned(Interface,IID_IMiniport))
1375 {
1376 *Object = PVOID(PMINIPORT(this));
1377 }
1378 else
1379 if (IsEqualGUIDAligned(Interface,IID_IMiniportDMus))
1380 {
1381 *Object = PVOID(PMINIPORTDMUS(this));
1382 }
1383 else
1384 if (IsEqualGUIDAligned(Interface,IID_IMusicTechnology))
1385 {
1386 *Object = PVOID(PMUSICTECHNOLOGY(this));
1387 }
1388 else
1389 if (IsEqualGUIDAligned(Interface,IID_IPowerNotify))
1390 {
1391 *Object = PVOID(PPOWERNOTIFY(this));
1392 }
1393 else
1394 {
1395 *Object = NULL;
1396 }
1397
1398 if (*Object)
1399 {
1400 //
1401 // We reference the interface for the caller.
1402 //
1403 PUNKNOWN(*Object)->AddRef();
1404 return STATUS_SUCCESS;
1405 }
1406
1407 return STATUS_INVALID_PARAMETER;
1408 }
1409
1410 #ifdef _MSC_VER
1411 #pragma code_seg("PAGE")
1412 #endif
1413 /*****************************************************************************
1414 * CMiniportDMusUART::~CMiniportDMusUART()
1415 *****************************************************************************
1416 * Destructor.
1417 */
1418 CMiniportDMusUART::~CMiniportDMusUART(void)
1419 {
1420 PAGED_CODE();
1421
1422 DPRINT("~CMiniportDMusUART");
1423
1424 ASSERT(0 == m_NumCaptureStreams);
1425 ASSERT(0 == m_NumRenderStreams);
1426
1427 // reset the HW so we don't get anymore interrupts
1428 if (m_UseIRQ && m_pInterruptSync)
1429 {
1430 (void) m_pInterruptSync->CallSynchronizedRoutine((PINTERRUPTSYNCROUTINE)InitMPU,PVOID(m_pPortBase));
1431 }
1432 else
1433 {
1434 (void) InitMPU(NULL,PVOID(m_pPortBase));
1435 }
1436
1437 if (m_pInterruptSync)
1438 {
1439 m_pInterruptSync->Release();
1440 m_pInterruptSync = NULL;
1441 }
1442 if (m_pServiceGroup)
1443 {
1444 m_pServiceGroup->Release();
1445 m_pServiceGroup = NULL;
1446 }
1447 if (m_pPort)
1448 {
1449 m_pPort->Release();
1450 m_pPort = NULL;
1451 }
1452 }
1453
1454 #ifdef _MSC_VER
1455 #pragma code_seg("PAGE")
1456 #endif
1457 /*****************************************************************************
1458 * CMiniportDMusUART::Init()
1459 *****************************************************************************
1460 * Initializes a the miniport.
1461 */
1462 STDMETHODIMP_(NTSTATUS)
1463 CMiniportDMusUART::
1464 Init
1465 (
1466 IN PUNKNOWN UnknownInterruptSync OPTIONAL,
1467 IN PRESOURCELIST ResourceList,
1468 IN PPORTDMUS Port_,
1469 OUT PSERVICEGROUP * ServiceGroup
1470 )
1471 {
1472 PAGED_CODE();
1473
1474 ASSERT(ResourceList);
1475 if (!ResourceList)
1476 {
1477 return STATUS_DEVICE_CONFIGURATION_ERROR;
1478 }
1479
1480 ASSERT(Port_);
1481 ASSERT(ServiceGroup);
1482
1483 DPRINT("Init");
1484
1485 *ServiceGroup = NULL;
1486 m_pPortBase = 0;
1487 m_fMPUInitialized = FALSE;
1488
1489 // This will remain unspecified if the miniport does not get any power
1490 // messages.
1491 //
1492 m_PowerState.DeviceState = PowerDeviceUnspecified;
1493
1494 //
1495 // AddRef() is required because we are keeping this pointer.
1496 //
1497 m_pPort = Port_;
1498 m_pPort->AddRef();
1499
1500 // Set dataformat.
1501 //
1502 if (IsEqualGUIDAligned(m_MusicFormatTechnology, GUID_NULL))
1503 {
1504 RtlCopyMemory( &m_MusicFormatTechnology,
1505 &KSMUSIC_TECHNOLOGY_PORT,
1506 sizeof(GUID));
1507 }
1508 RtlCopyMemory( &PinDataRangesStreamLegacy.Technology,
1509 &m_MusicFormatTechnology,
1510 sizeof(GUID));
1511 RtlCopyMemory( &PinDataRangesStreamDMusic.Technology,
1512 &m_MusicFormatTechnology,
1513 sizeof(GUID));
1514
1515 for (ULONG bufferCount = 0;bufferCount < kMPUInputBufferSize;bufferCount++)
1516 {
1517 m_MPUInputBuffer[bufferCount] = 0;
1518 }
1519 m_MPUInputBufferHead = 0;
1520 m_MPUInputBufferTail = 0;
1521 m_InputTimeStamp = 0;
1522 m_KSStateInput = KSSTATE_STOP;
1523
1524 NTSTATUS ntStatus = STATUS_SUCCESS;
1525
1526 m_NumRenderStreams = 0;
1527 m_NumCaptureStreams = 0;
1528
1529 m_UseIRQ = TRUE;
1530 if (ResourceList->NumberOfInterrupts() == 0)
1531 {
1532 m_UseIRQ = FALSE;
1533 }
1534
1535 ntStatus = PcNewServiceGroup(&m_pServiceGroup,NULL);
1536 if (NT_SUCCESS(ntStatus) && !m_pServiceGroup) // keep any error
1537 {
1538 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1539 }
1540
1541 if (NT_SUCCESS(ntStatus))
1542 {
1543 *ServiceGroup = m_pServiceGroup;
1544 m_pServiceGroup->AddRef();
1545
1546 //
1547 // Register the service group with the port early so the port is
1548 // prepared to handle interrupts.
1549 //
1550 m_pPort->RegisterServiceGroup(m_pServiceGroup);
1551 }
1552
1553 if (NT_SUCCESS(ntStatus) && m_UseIRQ)
1554 {
1555 //
1556 // Due to a bug in the InterruptSync design, we shouldn't share
1557 // the interrupt sync object. Whoever goes away first
1558 // will disconnect it, and the other points off into nowhere.
1559 //
1560 // Instead we generate our own interrupt sync object.
1561 //
1562 UnknownInterruptSync = NULL;
1563
1564 if (UnknownInterruptSync)
1565 {
1566 ntStatus =
1567 UnknownInterruptSync->QueryInterface
1568 (
1569 IID_IInterruptSync,
1570 (PVOID *) &m_pInterruptSync
1571 );
1572
1573 if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error
1574 {
1575 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1576 }
1577 if (NT_SUCCESS(ntStatus))
1578 { // run this ISR first
1579 ntStatus = m_pInterruptSync->
1580 RegisterServiceRoutine(DMusMPUInterruptServiceRoutine,PVOID(this),TRUE);
1581 }
1582
1583 }
1584 else
1585 { // create our own interruptsync mechanism.
1586 ntStatus =
1587 PcNewInterruptSync
1588 (
1589 &m_pInterruptSync,
1590 NULL,
1591 ResourceList,
1592 0, // Resource Index
1593 InterruptSyncModeNormal // Run ISRs once until we get SUCCESS
1594 );
1595
1596 if (!m_pInterruptSync && NT_SUCCESS(ntStatus)) // keep any error
1597 {
1598 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1599 }
1600
1601 if (NT_SUCCESS(ntStatus))
1602 {
1603 ntStatus = m_pInterruptSync->RegisterServiceRoutine(
1604 DMusMPUInterruptServiceRoutine,
1605 PVOID(this),
1606 TRUE); // run this ISR first
1607 }
1608 if (NT_SUCCESS(ntStatus))
1609 {
1610 ntStatus = m_pInterruptSync->Connect();
1611 }
1612 }
1613 }
1614
1615 if (NT_SUCCESS(ntStatus))
1616 {
1617 ntStatus = ProcessResources(ResourceList);
1618 }
1619
1620 if (!NT_SUCCESS(ntStatus))
1621 {
1622 //
1623 // clean up our mess
1624 //
1625
1626 // clean up the interrupt sync
1627 if( m_pInterruptSync )
1628 {
1629 m_pInterruptSync->Release();
1630 m_pInterruptSync = NULL;
1631 }
1632
1633 // clean up the service group
1634 if( m_pServiceGroup )
1635 {
1636 m_pServiceGroup->Release();
1637 m_pServiceGroup = NULL;
1638 }
1639
1640 // clean up the out param service group.
1641 if (*ServiceGroup)
1642 {
1643 (*ServiceGroup)->Release();
1644 (*ServiceGroup) = NULL;
1645 }
1646
1647 // release the port
1648 m_pPort->Release();
1649 m_pPort = NULL;
1650 }
1651
1652 return ntStatus;
1653 }
1654
1655 #ifdef _MSC_VER
1656 #pragma code_seg("PAGE")
1657 #endif
1658 /*****************************************************************************
1659 * CMiniportDMusUART::NewStream()
1660 *****************************************************************************
1661 * Gets the topology.
1662 */
1663 STDMETHODIMP_(NTSTATUS)
1664 CMiniportDMusUART::
1665 NewStream
1666 (
1667 OUT PMXF * MXF,
1668 IN PUNKNOWN OuterUnknown OPTIONAL,
1669 IN POOL_TYPE PoolType,
1670 IN ULONG PinID,
1671 IN DMUS_STREAM_TYPE StreamType,
1672 IN PKSDATAFORMAT DataFormat,
1673 OUT PSERVICEGROUP * ServiceGroup,
1674 IN PAllocatorMXF AllocatorMXF,
1675 IN PMASTERCLOCK MasterClock,
1676 OUT PULONGLONG SchedulePreFetch
1677 )
1678 {
1679 PAGED_CODE();
1680
1681 DPRINT("NewStream");
1682 NTSTATUS ntStatus = STATUS_SUCCESS;
1683
1684 // In 100 ns, we want stuff as soon as it comes in
1685 //
1686 *SchedulePreFetch = 0;
1687
1688 // if we don't have any streams already open, get the hardware ready.
1689 if ((!m_NumCaptureStreams) && (!m_NumRenderStreams))
1690 {
1691 ntStatus = ResetHardware(m_pPortBase);
1692 if (!NT_SUCCESS(ntStatus))
1693 {
1694 DPRINT("CMiniportDMusUART::NewStream ResetHardware failed");
1695 return ntStatus;
1696 }
1697 }
1698
1699 if ( ((m_NumCaptureStreams < kMaxNumCaptureStreams)
1700 && (StreamType == DMUS_STREAM_MIDI_CAPTURE))
1701 || ((m_NumRenderStreams < kMaxNumLegacyRenderStreams + kMaxNumDMusicRenderStreams)
1702 && (StreamType == DMUS_STREAM_MIDI_RENDER))
1703 )
1704 {
1705 CMiniportDMusUARTStream *pStream =
1706 new(PoolType) CMiniportDMusUARTStream();
1707
1708 if (pStream)
1709 {
1710 pStream->AddRef();
1711
1712 ntStatus =
1713 pStream->Init(this,m_pPortBase,(StreamType == DMUS_STREAM_MIDI_CAPTURE),AllocatorMXF,MasterClock);
1714
1715 if (NT_SUCCESS(ntStatus))
1716 {
1717 *MXF = PMXF(pStream);
1718 (*MXF)->AddRef();
1719
1720 if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
1721 {
1722 m_NumCaptureStreams++;
1723 *ServiceGroup = m_pServiceGroup;
1724 (*ServiceGroup)->AddRef();
1725 }
1726 else
1727 {
1728 m_NumRenderStreams++;
1729 *ServiceGroup = NULL;
1730 }
1731 }
1732
1733 pStream->Release();
1734 }
1735 else
1736 {
1737 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
1738 }
1739 }
1740 else
1741 {
1742 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
1743 if (StreamType == DMUS_STREAM_MIDI_CAPTURE)
1744 {
1745 DPRINT("NewStream failed, too many capture streams");
1746 }
1747 else if (StreamType == DMUS_STREAM_MIDI_RENDER)
1748 {
1749 DPRINT("NewStream failed, too many render streams");
1750 }
1751 else
1752 {
1753 DPRINT("NewStream invalid stream type");
1754 }
1755 }
1756
1757 return ntStatus;
1758 }
1759
1760 #ifdef _MSC_VER
1761 #pragma code_seg("PAGE")
1762 #endif
1763 /*****************************************************************************
1764 * CMiniportDMusUART::SetTechnology()
1765 *****************************************************************************
1766 * Sets pindatarange technology.
1767 */
1768 STDMETHODIMP_(NTSTATUS)
1769 CMiniportDMusUART::
1770 SetTechnology
1771 (
1772 IN const GUID * Technology
1773 )
1774 {
1775 PAGED_CODE();
1776
1777 NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
1778
1779 // Fail if miniport has already been initialized.
1780 //
1781 if (NULL == m_pPort)
1782 {
1783 RtlCopyMemory(&m_MusicFormatTechnology, Technology, sizeof(GUID));
1784 ntStatus = STATUS_SUCCESS;
1785 }
1786
1787 return ntStatus;
1788 } // SetTechnology
1789
1790 /*****************************************************************************
1791 * CMiniportDMusUART::PowerChangeNotify()
1792 *****************************************************************************
1793 * Handle power state change for the miniport.
1794 */
1795 #ifdef _MSC_VER
1796 #pragma code_seg("PAGE")
1797 #endif
1798
1799 STDMETHODIMP_(void)
1800 CMiniportDMusUART::
1801 PowerChangeNotify
1802 (
1803 IN POWER_STATE PowerState
1804 )
1805 {
1806 PAGED_CODE();
1807
1808 DPRINT("CMiniportDMusUART::PoweChangeNotify D%d", PowerState.DeviceState);
1809
1810 switch (PowerState.DeviceState)
1811 {
1812 case PowerDeviceD0:
1813 if (m_PowerState.DeviceState != PowerDeviceD0)
1814 {
1815 if (!NT_SUCCESS(InitializeHardware(m_pInterruptSync,m_pPortBase)))
1816 {
1817 DPRINT("InitializeHardware failed when resuming");
1818 }
1819 }
1820 break;
1821
1822 case PowerDeviceD1:
1823 case PowerDeviceD2:
1824 case PowerDeviceD3:
1825 default:
1826 break;
1827 }
1828 m_PowerState.DeviceState = PowerState.DeviceState;
1829 } // PowerChangeNotify
1830
1831 #ifdef _MSC_VER
1832 #pragma code_seg("PAGE")
1833 #endif
1834 /*****************************************************************************
1835 * CMiniportDMusUARTStream::NonDelegatingQueryInterface()
1836 *****************************************************************************
1837 * Obtains an interface. This function works just like a COM QueryInterface
1838 * call and is used if the object is not being aggregated.
1839 */
1840 STDMETHODIMP_(NTSTATUS)
1841 CMiniportDMusUARTStream::QueryInterface
1842 (
1843 REFIID Interface,
1844 PVOID * Object
1845 )
1846 {
1847 PAGED_CODE();
1848
1849 DPRINT("Stream::NonDelegatingQueryInterface");
1850 ASSERT(Object);
1851
1852 if (IsEqualGUIDAligned(Interface,IID_IUnknown))
1853 {
1854 *Object = PVOID(PUNKNOWN(this));
1855 }
1856 else
1857 if (IsEqualGUIDAligned(Interface,IID_IMXF))
1858 {
1859 *Object = PVOID(PMXF(this));
1860 }
1861 else
1862 {
1863 *Object = NULL;
1864 }
1865
1866 if (*Object)
1867 {
1868 //
1869 // We reference the interface for the caller.
1870 //
1871 PUNKNOWN(*Object)->AddRef();
1872 return STATUS_SUCCESS;
1873 }
1874
1875 return STATUS_INVALID_PARAMETER;
1876 }
1877
1878 #ifdef _MSC_VER
1879 #pragma code_seg("PAGE")
1880 #endif
1881 /*****************************************************************************
1882 * CMiniportDMusUARTStream::~CMiniportDMusUARTStream()
1883 *****************************************************************************
1884 * Destructs a stream.
1885 */
1886 CMiniportDMusUARTStream::~CMiniportDMusUARTStream(void)
1887 {
1888 PAGED_CODE();
1889
1890 DPRINT("~CMiniportDMusUARTStream");
1891
1892 KeCancelTimer(&m_TimerEvent);
1893
1894 if (m_DMKEvtQueue)
1895 {
1896 if (m_AllocatorMXF)
1897 {
1898 m_AllocatorMXF->PutMessage(m_DMKEvtQueue);
1899 }
1900 else
1901 {
1902 DPRINT("~CMiniportDMusUARTStream, no allocator, can't flush DMKEvts");
1903 }
1904 m_DMKEvtQueue = NULL;
1905 }
1906 if (m_AllocatorMXF)
1907 {
1908 m_AllocatorMXF->Release();
1909 m_AllocatorMXF = NULL;
1910 }
1911
1912 if (m_pMiniport)
1913 {
1914 if (m_fCapture)
1915 {
1916 m_pMiniport->m_NumCaptureStreams--;
1917 }
1918 else
1919 {
1920 m_pMiniport->m_NumRenderStreams--;
1921 }
1922
1923 m_pMiniport->Release();
1924 }
1925 }
1926
1927 #ifdef _MSC_VER
1928 #pragma code_seg("PAGE")
1929 #endif
1930 /*****************************************************************************
1931 * CMiniportDMusUARTStream::Init()
1932 *****************************************************************************
1933 * Initializes a stream.
1934 */
1935 STDMETHODIMP_(NTSTATUS)
1936 CMiniportDMusUARTStream::
1937 Init
1938 (
1939 IN CMiniportDMusUART * pMiniport,
1940 IN PUCHAR pPortBase,
1941 IN BOOLEAN fCapture,
1942 IN PAllocatorMXF allocatorMXF,
1943 IN PMASTERCLOCK masterClock
1944 )
1945 {
1946 PAGED_CODE();
1947
1948 ASSERT(pMiniport);
1949 ASSERT(pPortBase);
1950
1951 DPRINT("Init");
1952
1953 m_NumFailedMPUTries = 0;
1954 m_TimerQueued = FALSE;
1955 KeInitializeSpinLock(&m_DpcSpinLock);
1956 m_pMiniport = pMiniport;
1957 m_pMiniport->AddRef();
1958
1959 pMiniport->m_MasterClock = masterClock;
1960
1961 m_pPortBase = pPortBase;
1962 m_fCapture = fCapture;
1963
1964 m_SnapshotTimeStamp = 0;
1965 m_DMKEvtQueue = NULL;
1966 m_DMKEvtOffset = 0;
1967
1968 m_NumberOfRetries = 0;
1969
1970 if (allocatorMXF)
1971 {
1972 allocatorMXF->AddRef();
1973 m_AllocatorMXF = allocatorMXF;
1974 m_sinkMXF = m_AllocatorMXF;
1975 }
1976 else
1977 {
1978 return STATUS_INVALID_PARAMETER;
1979 }
1980
1981 KeInitializeDpc
1982 (
1983 &m_Dpc,
1984 &::DMusUARTTimerDPC,
1985 PVOID(this)
1986 );
1987 KeInitializeTimer(&m_TimerEvent);
1988
1989 return STATUS_SUCCESS;
1990 }
1991
1992 #ifdef _MSC_VER
1993 #pragma code_seg("PAGE")
1994 #endif
1995 /*****************************************************************************
1996 * CMiniportDMusUARTStream::SetState()
1997 *****************************************************************************
1998 * Sets the state of the channel.
1999 */
2000 STDMETHODIMP_(NTSTATUS)
2001 CMiniportDMusUARTStream::
2002 SetState
2003 (
2004 IN KSSTATE NewState
2005 )
2006 {
2007 PAGED_CODE();
2008
2009 DPRINT("SetState %d",NewState);
2010
2011 if (NewState == KSSTATE_RUN)
2012 {
2013 if (m_pMiniport->m_fMPUInitialized)
2014 {
2015 LARGE_INTEGER timeDue100ns;
2016 timeDue100ns.QuadPart = 0;
2017 KeSetTimer(&m_TimerEvent,timeDue100ns,&m_Dpc);
2018 }
2019 else
2020 {
2021 DPRINT("CMiniportDMusUARTStream::SetState KSSTATE_RUN failed due to uninitialized MPU");
2022 return STATUS_INVALID_DEVICE_STATE;
2023 }
2024 }
2025
2026 if (m_fCapture)
2027 {
2028 m_pMiniport->m_KSStateInput = NewState;
2029 if (NewState == KSSTATE_STOP) // STOPping
2030 {
2031 m_pMiniport->m_MPUInputBufferHead = 0; // Previously read bytes are discarded.
2032 m_pMiniport->m_MPUInputBufferTail = 0; // The entire FIFO is available.
2033 }
2034 }
2035 return STATUS_SUCCESS;
2036 }
2037
2038 #ifdef _MSC_VER
2039 #pragma code_seg()
2040 #endif
2041
2042
2043 /*****************************************************************************
2044 * CMiniportDMusUART::Service()
2045 *****************************************************************************
2046 * DPC-mode service call from the port.
2047 */
2048 STDMETHODIMP_(void)
2049 CMiniportDMusUART::
2050 Service
2051 ( void
2052 )
2053 {
2054 DPRINT("Service");
2055 if (!m_NumCaptureStreams)
2056 {
2057 // we should never get here....
2058 // if we do, we must have read some trash,
2059 // so just reset the input FIFO
2060 m_MPUInputBufferTail = m_MPUInputBufferHead = 0;
2061 }
2062 }
2063
2064 #ifdef _MSC_VER
2065 #pragma code_seg("PAGE")
2066 #endif
2067
2068 /*****************************************************************************
2069 * CMiniportDMusUARTStream::ConnectOutput()
2070 *****************************************************************************
2071 * Writes outgoing MIDI data.
2072 */
2073 NTSTATUS
2074 CMiniportDMusUARTStream::
2075 ConnectOutput(PMXF sinkMXF)
2076 {
2077 PAGED_CODE();
2078
2079 if (m_fCapture)
2080 {
2081 if ((sinkMXF) && (m_sinkMXF == m_AllocatorMXF))
2082 {
2083 DPRINT("ConnectOutput");
2084 m_sinkMXF = sinkMXF;
2085 return STATUS_SUCCESS;
2086 }
2087 else
2088 {
2089 DPRINT("ConnectOutput failed");
2090 }
2091 }
2092 else
2093 {
2094 DPRINT("ConnectOutput called on renderer; failed");
2095 }
2096 return STATUS_UNSUCCESSFUL;
2097 }
2098
2099 #ifdef _MSC_VER
2100 #pragma code_seg("PAGE")
2101 #endif
2102
2103 /*****************************************************************************
2104 * CMiniportDMusUARTStream::DisconnectOutput()
2105 *****************************************************************************
2106 * Writes outgoing MIDI data.
2107 */
2108 NTSTATUS
2109 CMiniportDMusUARTStream::
2110 DisconnectOutput(PMXF sinkMXF)
2111 {
2112 PAGED_CODE();
2113
2114 if (m_fCapture)
2115 {
2116 if ((m_sinkMXF == sinkMXF) || (!sinkMXF))
2117 {
2118 DPRINT("DisconnectOutput");
2119 m_sinkMXF = m_AllocatorMXF;
2120 return STATUS_SUCCESS;
2121 }
2122 else
2123 {
2124 DPRINT("DisconnectOutput failed");
2125 }
2126 }
2127 else
2128 {
2129 DPRINT("DisconnectOutput called on renderer; failed");
2130 }
2131 return STATUS_UNSUCCESSFUL;
2132 }
2133
2134 #ifdef _MSC_VER
2135 #pragma code_seg()
2136 #endif
2137
2138
2139 /*****************************************************************************
2140 * CMiniportDMusUARTStream::PutMessageLocked()
2141 *****************************************************************************
2142 * Now that the spinlock is held, add this message to the queue.
2143 *
2144 * Writes an outgoing MIDI message.
2145 * We don't sort a new message into the queue -- we append it.
2146 * This is fine, since the sequencer feeds us sequenced data.
2147 * Timestamps will ascend by design.
2148 */
2149 NTSTATUS CMiniportDMusUARTStream::PutMessageLocked(PDMUS_KERNEL_EVENT pDMKEvt)
2150 {
2151 NTSTATUS ntStatus = STATUS_SUCCESS;
2152 PDMUS_KERNEL_EVENT aDMKEvt;
2153
2154 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2155
2156 if (!m_fCapture)
2157 {
2158 DPRINT("PutMessage to render stream");
2159 if (pDMKEvt)
2160 {
2161 // m_DpcSpinLock already held
2162
2163 if (m_DMKEvtQueue)
2164 {
2165 aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue
2166
2167 while (aDMKEvt->pNextEvt)
2168 {
2169 aDMKEvt = aDMKEvt->pNextEvt;
2170 }
2171 aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue
2172 }
2173 else // currently nothing in queue
2174 {
2175 m_DMKEvtQueue = pDMKEvt;
2176 if (m_DMKEvtOffset)
2177 {
2178 DPRINT("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d",m_DMKEvtOffset);
2179 m_DMKEvtOffset = 0;
2180 }
2181 }
2182
2183 // m_DpcSpinLock already held
2184 }
2185 if (!m_TimerQueued)
2186 {
2187 (void) ConsumeEvents();
2188 }
2189 }
2190 else // capture
2191 {
2192 DPRINT("PutMessage to capture stream");
2193 ASSERT(NULL == pDMKEvt);
2194
2195 SourceEvtsToPort();
2196 }
2197 return ntStatus;
2198 }
2199
2200 #ifdef _MSC_VER
2201 #pragma code_seg()
2202 #endif
2203
2204 /*****************************************************************************
2205 * CMiniportDMusUARTStream::PutMessage()
2206 *****************************************************************************
2207 * Writes an outgoing MIDI message.
2208 * We don't sort a new message into the queue -- we append it.
2209 * This is fine, since the sequencer feeds us sequenced data.
2210 * Timestamps will ascend by design.
2211 */
2212 NTSTATUS CMiniportDMusUARTStream::PutMessage(PDMUS_KERNEL_EVENT pDMKEvt)
2213 {
2214 NTSTATUS ntStatus = STATUS_SUCCESS;
2215 PDMUS_KERNEL_EVENT aDMKEvt;
2216
2217 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2218
2219 if (!m_fCapture)
2220 {
2221 DPRINT("PutMessage to render stream");
2222 if (pDMKEvt)
2223 {
2224 KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
2225
2226 if (m_DMKEvtQueue)
2227 {
2228 aDMKEvt = m_DMKEvtQueue; // put pDMKEvt in event queue
2229
2230 while (aDMKEvt->pNextEvt)
2231 {
2232 aDMKEvt = aDMKEvt->pNextEvt;
2233 }
2234 aDMKEvt->pNextEvt = pDMKEvt; // here is end of queue
2235 }
2236 else // currently nothing in queue
2237 {
2238 m_DMKEvtQueue = pDMKEvt;
2239 if (m_DMKEvtOffset)
2240 {
2241 DPRINT("PutMessage Nothing in the queue, but m_DMKEvtOffset == %d", m_DMKEvtOffset);
2242 m_DMKEvtOffset = 0;
2243 }
2244 }
2245
2246 KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
2247 }
2248 if (!m_TimerQueued)
2249 {
2250 (void) ConsumeEvents();
2251 }
2252 }
2253 else // capture
2254 {
2255 DPRINT("PutMessage to capture stream");
2256 ASSERT(NULL == pDMKEvt);
2257
2258 SourceEvtsToPort();
2259 }
2260 return ntStatus;
2261 }
2262
2263 #ifdef _MSC_VER
2264 #pragma code_seg()
2265 #endif
2266
2267 /*****************************************************************************
2268 * CMiniportDMusUARTStream::ConsumeEvents()
2269 *****************************************************************************
2270 * Attempts to empty the render message queue.
2271 * Called either from DPC timer or upon IRP submittal.
2272 // TODO: support packages right
2273 // process the package (actually, should do this above.
2274 // treat the package as a list fragment that shouldn't be sorted.
2275 // better yet, go through each event in the package, and when
2276 // an event is exhausted, delete it and decrement m_offset.
2277 */
2278 NTSTATUS CMiniportDMusUARTStream::ConsumeEvents(void)
2279 {
2280 PDMUS_KERNEL_EVENT aDMKEvt;
2281
2282 NTSTATUS ntStatus = STATUS_SUCCESS;
2283 ULONG bytesRemaining = 0,bytesWritten = 0;
2284 LARGE_INTEGER aMillisecIn100ns;
2285
2286 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
2287 KeAcquireSpinLockAtDpcLevel(&m_DpcSpinLock);
2288
2289 m_TimerQueued = FALSE;
2290 while (m_DMKEvtQueue) // do we have anything to play at all?
2291 {
2292 aDMKEvt = m_DMKEvtQueue; // event we try to play
2293 if (aDMKEvt->cbEvent)
2294 {
2295 bytesRemaining = aDMKEvt->cbEvent - m_DMKEvtOffset; // number of bytes left in this evt
2296
2297 ASSERT(bytesRemaining > 0);
2298 if (bytesRemaining <= 0)
2299 {
2300 bytesRemaining = aDMKEvt->cbEvent;
2301 }
2302
2303 if (aDMKEvt->cbEvent <= sizeof(PBYTE)) // short message
2304 {
2305 DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.abData[0], aDMKEvt->uData.abData[1], aDMKEvt->uData.abData[2], aDMKEvt->uData.abData[3]);
2306 ntStatus = Write(aDMKEvt->uData.abData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
2307 }
2308 else if (PACKAGE_EVT(aDMKEvt))
2309 {
2310 ASSERT(m_DMKEvtOffset == 0);
2311 m_DMKEvtOffset = 0;
2312 DPRINT("ConsumeEvents(Package)");
2313
2314 ntStatus = PutMessageLocked(aDMKEvt->uData.pPackageEvt); // we already own the spinlock
2315
2316 // null this because we are about to throw it in the allocator
2317 aDMKEvt->uData.pPackageEvt = NULL;
2318 aDMKEvt->cbEvent = 0;
2319 bytesWritten = bytesRemaining;
2320 }
2321 else // SysEx message
2322 {
2323 DPRINT("ConsumeEvents(%02x%02x%02x%02x)", aDMKEvt->uData.pbData[0], aDMKEvt->uData.pbData[1], aDMKEvt->uData.pbData[2], aDMKEvt->uData.pbData[3]);
2324
2325 ntStatus = Write(aDMKEvt->uData.pbData + m_DMKEvtOffset,bytesRemaining,&bytesWritten);
2326 }
2327 } // if (aDMKEvt->cbEvent)
2328 if (STATUS_SUCCESS != ntStatus)
2329 {
2330 DPRINT("ConsumeEvents: Write returned 0x%08x", ntStatus);
2331 bytesWritten = bytesRemaining; // just bail on this event and try next time
2332 }
2333
2334 ASSERT(bytesWritten <= bytesRemaining);
2335 if (bytesWritten == bytesRemaining)
2336 {
2337 m_DMKEvtQueue = m_DMKEvtQueue->pNextEvt;
2338 aDMKEvt->pNextEvt = NULL;
2339
2340 m_AllocatorMXF->PutMessage(aDMKEvt); // throw back in free pool
2341 m_DMKEvtOffset = 0; // start fresh on next evt
2342 m_NumberOfRetries = 0;
2343 } // but wait ... there's more!
2344 else // our FIFO is full for now.
2345 {
2346 // update our offset by that amount we did write
2347 m_DMKEvtOffset += bytesWritten;
2348 ASSERT(m_DMKEvtOffset < aDMKEvt->cbEvent);
2349
2350 DPRINT("ConsumeEvents tried %d, wrote %d, at offset %d", bytesRemaining,bytesWritten,m_DMKEvtOffset);
2351 aMillisecIn100ns.QuadPart = -(kOneMillisec); // set timer, come back later
2352 m_TimerQueued = TRUE;
2353 m_NumberOfRetries++;
2354 ntStatus = KeSetTimer( &m_TimerEvent, aMillisecIn100ns, &m_Dpc );
2355 break;
2356 } // we didn't write it all
2357 } // go back, Jack, do it again (while m_DMKEvtQueue)
2358 KeReleaseSpinLockFromDpcLevel(&m_DpcSpinLock);
2359 return ntStatus;
2360 }
2361
2362 #ifdef _MSC_VER
2363 #pragma code_seg()
2364 #endif
2365
2366 /*****************************************************************************
2367 * CMiniportDMusUARTStream::HandlePortParams()
2368 *****************************************************************************
2369 * Writes an outgoing MIDI message.
2370 */
2371 NTSTATUS
2372 CMiniportDMusUARTStream::
2373 HandlePortParams
2374 (
2375 IN PPCPROPERTY_REQUEST pRequest
2376 )
2377 {
2378 PAGED_CODE();
2379
2380 NTSTATUS ntStatus;
2381
2382 if (pRequest->Verb & KSPROPERTY_TYPE_SET)
2383 {
2384 return STATUS_INVALID_DEVICE_REQUEST;
2385 }
2386
2387 ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTH_PORTPARAMS), TRUE);
2388 if (NT_SUCCESS(ntStatus))
2389 {
2390 RtlCopyMemory(pRequest->Value, pRequest->Instance, sizeof(SYNTH_PORTPARAMS));
2391
2392 PSYNTH_PORTPARAMS Params = (PSYNTH_PORTPARAMS)pRequest->Value;
2393
2394 if (Params->ValidParams & ~SYNTH_PORTPARAMS_CHANNELGROUPS)
2395 {
2396 Params->ValidParams &= SYNTH_PORTPARAMS_CHANNELGROUPS;
2397 }
2398
2399 if (!(Params->ValidParams & SYNTH_PORTPARAMS_CHANNELGROUPS))
2400 {
2401 Params->ChannelGroups = 1;
2402 }
2403 else if (Params->ChannelGroups != 1)
2404 {
2405 Params->ChannelGroups = 1;
2406 }
2407
2408 pRequest->ValueSize = sizeof(SYNTH_PORTPARAMS);
2409 }
2410
2411 return ntStatus;
2412 }
2413
2414 #ifdef _MSC_VER
2415 #pragma code_seg()
2416 #endif
2417
2418 /*****************************************************************************
2419 * DMusTimerDPC()
2420 *****************************************************************************
2421 * The timer DPC callback. Thunks to a C++ member function.
2422 * This is called by the OS in response to the DirectMusic pin
2423 * wanting to wakeup later to process more DirectMusic stuff.
2424 */
2425 VOID
2426 NTAPI
2427 DMusUARTTimerDPC
2428 (
2429 IN PKDPC Dpc,
2430 IN PVOID DeferredContext,
2431 IN PVOID SystemArgument1,
2432 IN PVOID SystemArgument2
2433 )
2434 {
2435 ASSERT(DeferredContext);
2436
2437 CMiniportDMusUARTStream *aStream;
2438 aStream = (CMiniportDMusUARTStream *) DeferredContext;
2439 if (aStream)
2440 {
2441 DPRINT("DMusUARTTimerDPC");
2442 if (false == aStream->m_fCapture)
2443 {
2444 (void) aStream->ConsumeEvents();
2445 }
2446 // ignores return value!
2447 }
2448 }
2449
2450 /*****************************************************************************
2451 * DirectMusic properties
2452 ****************************************************************************/
2453
2454 #ifdef _MSC_VER
2455 #pragma code_seg()
2456 #endif
2457
2458 /*
2459 * Properties concerning synthesizer functions.
2460 */
2461 const WCHAR wszDescOut[] = L"DMusic MPU-401 Out ";
2462 const WCHAR wszDescIn[] = L"DMusic MPU-401 In ";
2463
2464 NTSTATUS
2465 NTAPI
2466 PropertyHandler_Synth
2467 (
2468 IN PPCPROPERTY_REQUEST pRequest
2469 )
2470 {
2471 NTSTATUS ntStatus;
2472
2473 PAGED_CODE();
2474
2475 if (pRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
2476 {
2477 ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
2478 if (NT_SUCCESS(ntStatus))
2479 {
2480 // if return buffer can hold a ULONG, return the access flags
2481 PULONG AccessFlags = PULONG(pRequest->Value);
2482
2483 *AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT;
2484 switch (pRequest->PropertyItem->Id)
2485 {
2486 case KSPROPERTY_SYNTH_CAPS:
2487 case KSPROPERTY_SYNTH_CHANNELGROUPS:
2488 *AccessFlags |= KSPROPERTY_TYPE_GET;
2489 }
2490 switch (pRequest->PropertyItem->Id)
2491 {
2492 case KSPROPERTY_SYNTH_CHANNELGROUPS:
2493 *AccessFlags |= KSPROPERTY_TYPE_SET;
2494 }
2495 ntStatus = STATUS_SUCCESS;
2496 pRequest->ValueSize = sizeof(ULONG);
2497
2498 switch (pRequest->PropertyItem->Id)
2499 {
2500 case KSPROPERTY_SYNTH_PORTPARAMETERS:
2501 if (pRequest->MinorTarget)
2502 {
2503 *AccessFlags |= KSPROPERTY_TYPE_GET;
2504 }
2505 else
2506 {
2507 pRequest->ValueSize = 0;
2508 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2509 }
2510 }
2511 }
2512 }
2513 else
2514 {
2515 ntStatus = STATUS_SUCCESS;
2516 switch(pRequest->PropertyItem->Id)
2517 {
2518 case KSPROPERTY_SYNTH_CAPS:
2519 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CAPS");
2520
2521 if (pRequest->Verb & KSPROPERTY_TYPE_SET)
2522 {
2523 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2524 }
2525
2526 if (NT_SUCCESS(ntStatus))
2527 {
2528 ntStatus = ValidatePropertyRequest(pRequest, sizeof(SYNTHCAPS), TRUE);
2529
2530 if (NT_SUCCESS(ntStatus))
2531 {
2532 SYNTHCAPS *caps = (SYNTHCAPS*)pRequest->Value;
2533 int increment;
2534 RtlZeroMemory(caps, sizeof(SYNTHCAPS));
2535 // XXX Different guids for different instances!
2536 //
2537 if (pRequest->Node == eSynthNode)
2538 {
2539 increment = sizeof(wszDescOut) - 2;
2540 RtlCopyMemory( caps->Description,wszDescOut,increment);
2541 caps->Guid = CLSID_MiniportDriverDMusUART;
2542 }
2543 else
2544 {
2545 increment = sizeof(wszDescIn) - 2;
2546 RtlCopyMemory( caps->Description,wszDescIn,increment);
2547 caps->Guid = CLSID_MiniportDriverDMusUARTCapture;
2548 }
2549
2550 caps->Flags = SYNTH_PC_EXTERNAL;
2551 caps->MemorySize = 0;
2552 caps->MaxChannelGroups = 1;
2553 caps->MaxVoices = 0xFFFFFFFF;
2554 caps->MaxAudioChannels = 0xFFFFFFFF;
2555
2556 caps->EffectFlags = 0;
2557
2558 CMiniportDMusUART *aMiniport;
2559 ASSERT(pRequest->MajorTarget);
2560 aMiniport = (CMiniportDMusUART *)(PMINIPORTDMUS)(pRequest->MajorTarget);
2561 WCHAR wszDesc2[16];
2562 int cLen;
2563 cLen = swprintf(wszDesc2,L"[%03x]\0",PtrToUlong(aMiniport->m_pPortBase));
2564
2565 cLen *= sizeof(WCHAR);
2566 RtlCopyMemory((WCHAR *)((DWORD_PTR)(caps->Description) + increment),
2567 wszDesc2,
2568 cLen);
2569
2570
2571 pRequest->ValueSize = sizeof(SYNTHCAPS);
2572 }
2573 }
2574
2575 break;
2576
2577 case KSPROPERTY_SYNTH_PORTPARAMETERS:
2578 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_PORTPARAMETERS");
2579 {
2580 CMiniportDMusUARTStream *aStream;
2581
2582 aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
2583 if (aStream)
2584 {
2585 ntStatus = aStream->HandlePortParams(pRequest);
2586 }
2587 else
2588 {
2589 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2590 }
2591 }
2592 break;
2593
2594 case KSPROPERTY_SYNTH_CHANNELGROUPS:
2595 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_CHANNELGROUPS");
2596
2597 ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONG), TRUE);
2598 if (NT_SUCCESS(ntStatus))
2599 {
2600 *(PULONG)(pRequest->Value) = 1;
2601 pRequest->ValueSize = sizeof(ULONG);
2602 }
2603 break;
2604
2605 case KSPROPERTY_SYNTH_LATENCYCLOCK:
2606 DPRINT("PropertyHandler_Synth:KSPROPERTY_SYNTH_LATENCYCLOCK");
2607
2608 if(pRequest->Verb & KSPROPERTY_TYPE_SET)
2609 {
2610 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2611 }
2612 else
2613 {
2614 ntStatus = ValidatePropertyRequest(pRequest, sizeof(ULONGLONG), TRUE);
2615 if(NT_SUCCESS(ntStatus))
2616 {
2617 REFERENCE_TIME rtLatency;
2618 CMiniportDMusUARTStream *aStream;
2619
2620 aStream = (CMiniportDMusUARTStream*)(pRequest->MinorTarget);
2621 if(aStream == NULL)
2622 {
2623 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
2624 }
2625 else
2626 {
2627 aStream->m_pMiniport->m_MasterClock->GetTime(&rtLatency);
2628 *((PULONGLONG)pRequest->Value) = rtLatency;
2629 pRequest->ValueSize = sizeof(ULONGLONG);
2630 }
2631 }
2632 }
2633 break;
2634
2635 default:
2636 DPRINT("Unhandled property in PropertyHandler_Synth");
2637 break;
2638 }
2639 }
2640 return ntStatus;
2641 }
2642
2643 /*****************************************************************************
2644 * ValidatePropertyRequest()
2645 *****************************************************************************
2646 * Validates pRequest.
2647 * Checks if the ValueSize is valid
2648 * Checks if the Value is valid
2649 *
2650 * This does not update pRequest->ValueSize if it returns NT_SUCCESS.
2651 * Caller must set pRequest->ValueSize in case of NT_SUCCESS.
2652 */
2653 NTSTATUS ValidatePropertyRequest
2654 (
2655 IN PPCPROPERTY_REQUEST pRequest,
2656 IN ULONG ulValueSize,
2657 IN BOOLEAN fValueRequired
2658 )
2659 {
2660 NTSTATUS ntStatus;
2661
2662 if (pRequest->ValueSize >= ulValueSize)
2663 {
2664 if (fValueRequired && NULL == pRequest->Value)
2665 {
2666 ntStatus = STATUS_INVALID_PARAMETER;
2667 }
2668 else
2669 {
2670 ntStatus = STATUS_SUCCESS;
2671 }
2672 }
2673 else if (0 == pRequest->ValueSize)
2674 {
2675 ntStatus = STATUS_BUFFER_OVERFLOW;
2676 }
2677 else
2678 {
2679 ntStatus = STATUS_BUFFER_TOO_SMALL;
2680 }
2681
2682 if (STATUS_BUFFER_OVERFLOW == ntStatus)
2683 {
2684 pRequest->ValueSize = ulValueSize;
2685 }
2686 else
2687 {
2688 pRequest->ValueSize = 0;
2689 }
2690
2691 return ntStatus;
2692 } // ValidatePropertyRequest
2693
2694 #ifdef _MSC_VER
2695 #pragma code_seg()
2696 #endif
2697
2698