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