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