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