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