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