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