1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxInterrupt.hpp 8 9 Abstract: 10 11 This module implements a frameworks managed interrupt object 12 13 Author: 14 15 16 17 18 Environment: 19 20 Both kernel and user mode 21 22 Revision History: 23 24 25 --*/ 26 27 #ifndef _FXINTERRUPT_H_ 28 #define _FXINTERRUPT_H_ 29 30 #include "fxwakeinterruptstatemachine.hpp" 31 32 // 33 // We need two parameters for KeSynchronizeExecution when enabling 34 // and disabling interrupts, so we use this structure on the stack since its 35 // a synchronous call. 36 // 37 struct FxInterruptEnableParameters { 38 FxInterrupt* Interrupt; 39 NTSTATUS ReturnVal; 40 }; 41 42 typedef FxInterruptEnableParameters FxInterruptDisableParameters; 43 44 45 class FxInterrupt : public FxNonPagedObject { 46 47 friend FxPkgPnp; 48 49 private: 50 51 // 52 // User supplied configuration 53 // 54 WDF_TRI_STATE m_ShareVector; 55 56 // 57 // Kernel Interupt object 58 // 59 struct _KINTERRUPT* m_Interrupt; 60 61 // 62 // Kernel spinlock for Interrupt 63 // 64 MdLock* m_SpinLock; 65 66 KIRQL m_OldIrql; 67 volatile KIRQL m_SynchronizeIrql; 68 69 // 70 // Built in SpinLock/PassiveLock 71 // 72 MxLock m_BuiltInSpinLock; 73 74 // 75 // Passive-level interrupt handling. 76 // 77 FxWaitLock* m_WaitLock; 78 79 // 80 // DpcForIsr and WorkItemForIsr support. Note that a DPC is still 81 // needed even if the driver opts to use WorkItemForIsr when 82 // driver handles interrupts at DIRQL. 83 // 84 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 85 KDPC m_Dpc; 86 #endif 87 FxSystemWorkItem* m_SystemWorkItem; 88 89 // 90 // Automatic serialization: this is the callback lock for the object the DPC or 91 // work-item will synchronize with. 92 // 93 FxCallbackLock* m_CallbackLock; 94 95 // 96 // Set to TRUE when WDF is responsible for disposing the wait-lock. 97 // 98 BOOLEAN m_DisposeWaitLock; 99 100 // 101 // Value provided by driver. When TRUE we use IoReportActive/Inactive to 102 // do soft connect/disconnect on explicit power transitions. 103 // 104 BOOLEAN m_UseSoftDisconnect; 105 106 // 107 // Set to TRUE for passive-level interrupt handling. 108 // 109 BOOLEAN m_PassiveHandling; 110 111 // set to TRUE once the interrupt has been added to the pnp package's 112 // interrupt list 113 BOOLEAN m_AddedToList; 114 115 // 116 // Indicates whether the driver has forced a disconnect. If so, then 117 // we should stop automatically managing the connected state. 118 // 119 BOOLEAN m_Connected; 120 BOOLEAN m_ForceDisconnected; 121 122 // 123 // Indicates whether the m_EvtInterruptPostEnable succeeded or not. 124 // 125 BOOLEAN m_Enabled; 126 127 // 128 // Save floating point when the ISR runs 129 // 130 BOOLEAN m_FloatingSave; 131 132 // 133 // Set to TRUE if interrupt is created in the prepare hardware callback. 134 // 135 BOOLEAN m_CreatedInPrepareHardware; 136 137 // 138 // State machine to manage a wake capable interrupt 139 // 140 FxWakeInterruptMachine* m_WakeInterruptMachine; 141 142 143 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 144 // 145 // Set to true on successful connect or when driver reports active. 146 // (this field is mainly for aid in debugging) 147 // 148 BOOLEAN m_Active; 149 #endif 150 151 // 152 // Interrupt policy 153 // 154 BOOLEAN m_SetPolicy; 155 WDF_INTERRUPT_POLICY m_Policy; 156 WDF_INTERRUPT_PRIORITY m_Priority; 157 GROUP_AFFINITY m_Processors; 158 159 // 160 // Callbacks 161 // 162 PFN_WDF_INTERRUPT_ENABLE m_EvtInterruptEnable; 163 PFN_WDF_INTERRUPT_DISABLE m_EvtInterruptDisable; 164 165 PFN_WDF_INTERRUPT_ISR m_EvtInterruptIsr; 166 PFN_WDF_INTERRUPT_DPC m_EvtInterruptDpc; 167 PFN_WDF_INTERRUPT_WORKITEM m_EvtInterruptWorkItem; 168 169 #if ((FX_CORE_MODE)==(FX_CORE_USER_MODE)) 170 // 171 // Rd interrupt object 172 // 173 RD_INTERRUPT_CONTEXT m_RdInterruptContext; 174 175 // 176 // Each interrupt object has this structure which comprises an event and a 177 // wait structure. The wait struture is associted with interrupt's callback 178 // and the event, and is queued to threadpool. The callback is invoked when 179 // the event is set. 180 // 181 FxInterruptWaitblock* m_InterruptWaitblock; 182 183 // 184 // True if the interrupt callback can queue another interrupt wait. 185 // Set to true when interrupt is connected and false when interrupts 186 // callbacks and waits are flushed. 187 // 188 BOOLEAN m_CanQueue; 189 190 // 191 // UMDF's handling of interrupt is split in two parts: 192 // 1. framwork code- runs at passive always and therefore uses mode-agnostic 193 // code meant for passive-level handling, tracked through m_PassiveLevel 194 // field of interrupt object. 195 // 2. redirector code- does passive handling of all of level-triggered 196 // interrupt and DIRQL handing of all others (edge and msi). Driver 197 // doesn't have any choice in that. The PassiveHandling field in the 198 // interrupt config is always set for passive for UMDF (through UMDF's 199 // init function). 200 // 201 // This field stores the type of handling done by redirector as opposed to 202 // m_PassiveHandling which stores user's choice. 203 // 204 BOOLEAN m_PassiveHandlingByRedirector; 205 #endif 206 207 // 208 // PnP data about the interrupt. 209 // 210 WDF_INTERRUPT_INFO m_InterruptInfo; 211 212 // 213 // Weak ref to the translated resource interrupt descriptor. 214 // It is valid from prepare hardware callback to release hardware callback. 215 // 216 PCM_PARTIAL_RESOURCE_DESCRIPTOR m_CmTranslatedResource; 217 218 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 219 // 220 // Callback used to set m_Disconnecting, synchronized to running ISRs. 221 // Only runs if m_IsEdgeTriggeredNonMsiInterrupt is TRUE. 222 // 223 static 224 MdInterruptSynchronizeRoutineType _InterruptMarkDisconnecting; 225 226 // 227 // Backup KINTERRUPT pointer, captured from the KMDF ISR thunk. We need it 228 // because valid interrupts may arrive before IoConnectInterruptEx sets 229 // FxInterrupt.m_Interrupt. Non-NULL only if m_IsEdgeTriggeredNonMsiInterrupt is TRUE. 230 // 231 struct _KINTERRUPT* m_InterruptCaptured; 232 #endif 233 234 // 235 // Used to mark the interrupt disconnect window, and to discard interrupts 236 // that arrive within this window. Only set if m_IsEdgeTriggeredNonMsiInterrupt is TRUE. 237 // 238 BOOLEAN m_Disconnecting; 239 240 // 241 // Set if this is an Edge-Triggered non-MSI interrupt. These interrupts are 242 // stateful and it is important not to drop any around the connection window. 243 // 244 BOOLEAN m_IsEdgeTriggeredNonMsiInterrupt; 245 246 protected: 247 248 LIST_ENTRY m_PnpList; 249 250 public: 251 FxInterrupt( 252 __in PFX_DRIVER_GLOBALS FxDriverGlobals 253 ); 254 255 virtual 256 ~FxInterrupt( 257 VOID 258 ); 259 260 _Must_inspect_result_ 261 static 262 NTSTATUS 263 _CreateAndInit( 264 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 265 __in CfxDevice * Device, 266 __in_opt FxObject * Parent, 267 __in PWDF_OBJECT_ATTRIBUTES Attributes, 268 __in PWDF_INTERRUPT_CONFIG Configuration, 269 __out FxInterrupt ** Interrupt 270 ); 271 272 _Must_inspect_result_ 273 NTSTATUS 274 CreateWakeInterruptMachine( 275 VOID 276 ); 277 278 _Must_inspect_result_ 279 NTSTATUS 280 Initialize( 281 __in CfxDevice* Device, 282 __in FxObject* Parent, 283 __in PWDF_INTERRUPT_CONFIG Configuration 284 ); 285 286 _Must_inspect_result_ 287 NTSTATUS 288 InitializeWorker( 289 __in FxObject* Parent, 290 __in PWDF_INTERRUPT_CONFIG Configuration 291 ); 292 293 _Must_inspect_result_ 294 NTSTATUS 295 InitializeInternal( 296 __in FxObject* Parent, 297 __in PWDF_INTERRUPT_CONFIG Configuration 298 ); 299 300 virtual 301 BOOLEAN 302 Dispose( 303 VOID 304 ); 305 306 virtual 307 VOID 308 DeleteObject( 309 VOID 310 ); 311 312 VOID 313 OnPostReleaseHardware( 314 VOID 315 ); 316 317 VOID 318 DpcHandler( 319 __in_opt PVOID SystemArgument1, 320 __in_opt PVOID SystemArgument2 321 ); 322 323 BOOLEAN 324 QueueDpcForIsr( 325 VOID 326 ); 327 328 BOOLEAN 329 Synchronize( 330 __in PFN_WDF_INTERRUPT_SYNCHRONIZE Callback, 331 __in WDFCONTEXT Context 332 ); 333 334 struct _KINTERRUPT* 335 GetInterruptPtr( 336 VOID 337 ); 338 339 __inline 340 BOOLEAN IsWakeCapable(VOID)341 IsWakeCapable( 342 VOID 343 ) 344 { 345 return ((m_WakeInterruptMachine != NULL) ? TRUE:FALSE); 346 } 347 348 VOID SetActiveForWake(__in BOOLEAN ActiveForWake)349 SetActiveForWake( 350 __in BOOLEAN ActiveForWake 351 ) 352 { 353 m_WakeInterruptMachine->m_ActiveForWake = ActiveForWake; 354 } 355 356 BOOLEAN IsActiveForWake(VOID)357 IsActiveForWake( 358 VOID 359 ) 360 { 361 if ((m_WakeInterruptMachine != NULL) && 362 (m_WakeInterruptMachine->m_ActiveForWake)) { 363 return TRUE; 364 } else { 365 return FALSE; 366 } 367 } 368 369 VOID ProcessWakeInterruptEvent(__in FxWakeInterruptEvents Event)370 ProcessWakeInterruptEvent( 371 __in FxWakeInterruptEvents Event 372 ) 373 { 374 m_WakeInterruptMachine->ProcessEvent(Event); 375 } 376 377 378 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 379 380 VOID 381 ReportActive( 382 _In_ BOOLEAN Internal = FALSE 383 ); 384 385 VOID 386 ReportInactive( 387 _In_ BOOLEAN Internal = FALSE 388 ); 389 390 BOOLEAN IsSoftDisconnectCapable(VOID)391 IsSoftDisconnectCapable( 392 VOID 393 ) 394 { 395 if (m_UseSoftDisconnect && 396 FxLibraryGlobals.IoReportInterruptInactive != NULL && 397 m_Interrupt != NULL && 398 m_Connected) { 399 return TRUE; 400 } 401 else { 402 return FALSE; 403 } 404 } 405 406 #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE)) 407 408 BOOLEAN IsSoftDisconnectCapable(VOID)409 IsSoftDisconnectCapable( 410 VOID 411 ) 412 { 413 // 414 // Not implemented for UMDF 415 // 416 return FALSE; 417 } 418 419 VOID ReportActive(_In_ BOOLEAN Internal=FALSE)420 ReportActive( 421 _In_ BOOLEAN Internal = FALSE 422 ) 423 { 424 UNREFERENCED_PARAMETER(Internal); 425 // 426 // Not implemented for UMDF 427 // 428 } 429 430 VOID ReportInactive(_In_ BOOLEAN Internal=FALSE)431 ReportInactive( 432 _In_ BOOLEAN Internal = FALSE 433 ) 434 { 435 UNREFERENCED_PARAMETER(Internal); 436 // 437 // Not implemented for UMDF 438 // 439 } 440 441 #endif 442 443 VOID 444 WorkItemHandler( 445 VOID 446 ); 447 448 BOOLEAN 449 QueueWorkItemForIsr( 450 VOID 451 ); 452 453 __inline 454 BOOLEAN IsPassiveHandling(VOID)455 IsPassiveHandling( 456 VOID 457 ) 458 { 459 return m_PassiveHandling; 460 } 461 462 __inline 463 BOOLEAN IsPassiveConnect(VOID)464 IsPassiveConnect( 465 VOID 466 ) 467 { 468 // 469 // UMDF's handling of interrupt is split in two parts: 470 // 1. framework code that runs at passive always in host process and 471 // therefore uses mode-agnostic code meant for passive-level handling, 472 // tracked through m_PassiveHandling member. 473 // field of interrupt object. 474 // 2. redirector code that does passive handling of all of level-triggered 475 // interrupt and DIRQL handing of all others (edge and msi). Driver 476 // doesn't have any choice in that. The m_PassiveHandling field in the 477 // interrupt config is always set for passive for UMDF (through UMDF's 478 // init function). m_PasiveHandlingByRedirector member is present to 479 // this part of code. 480 // In summary, m_PassiveHandling and m_PasiveHandlingByRedirector 481 // effectively maintain how the interrupt is connected (passive or DIRQL), 482 // for KMDF and UMDF respectively. This routine tells how the 483 // interrupt is connnected by looking at these members. 484 // 485 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 486 return IsPassiveHandling(); 487 #else 488 return m_PassiveHandlingByRedirector; 489 #endif 490 } 491 492 __inline 493 BOOLEAN IsAutomaticSerialization(VOID)494 IsAutomaticSerialization( 495 VOID 496 ) 497 { 498 return m_CallbackLock != NULL ? TRUE : FALSE; 499 } 500 501 VOID 502 AcquireLock( 503 VOID 504 ); 505 506 BOOLEAN 507 TryToAcquireLock( 508 VOID 509 ); 510 511 VOID 512 ReleaseLock( 513 VOID 514 ); 515 516 CfxDevice* GetDevice(VOID)517 GetDevice( 518 VOID 519 ) 520 { 521 return m_Device; 522 } 523 524 PWDF_INTERRUPT_INFO 525 GetInfo( 526 VOID 527 ); 528 529 WDFINTERRUPT GetHandle(VOID)530 GetHandle( 531 VOID 532 ) 533 { 534 return (WDFINTERRUPT) GetObjectHandle(); 535 } 536 537 BOOLEAN IsSharedSpinLock(VOID)538 IsSharedSpinLock( 539 VOID 540 ) 541 { 542 return m_SpinLock != &m_BuiltInSpinLock.Get() ? TRUE : FALSE; 543 } 544 545 BOOLEAN IsSyncIrqlSet(VOID)546 IsSyncIrqlSet( 547 VOID 548 ) 549 { 550 return m_SynchronizeIrql != PASSIVE_LEVEL ? TRUE : FALSE; 551 } 552 553 KIRQL GetSyncIrql(VOID)554 GetSyncIrql( 555 VOID 556 ) 557 { 558 return m_SynchronizeIrql; 559 } 560 561 KIRQL GetResourceIrql(VOID)562 GetResourceIrql( 563 VOID 564 ) 565 { 566 return m_InterruptInfo.Irql; 567 } 568 569 BOOLEAN SharesLock(FxInterrupt * Interrupt)570 SharesLock( 571 FxInterrupt* Interrupt 572 ) 573 { 574 return m_SpinLock == Interrupt->m_SpinLock ? TRUE : FALSE; 575 } 576 577 private: 578 VOID 579 Reset( 580 VOID 581 ); 582 583 VOID 584 ResetInternal( 585 VOID 586 ); 587 588 VOID SetSyncIrql(KIRQL SyncIrql)589 SetSyncIrql( 590 KIRQL SyncIrql 591 ) 592 { 593 m_SynchronizeIrql = SyncIrql; 594 } 595 596 // 597 // Called from workitem to perform final flushing of any 598 // outstanding DPC's and dereferencing of objects. 599 // 600 VOID 601 FlushAndRundown( 602 VOID 603 ); 604 605 VOID 606 FlushAndRundownInternal( 607 VOID 608 ); 609 610 static 611 MdInterruptServiceRoutineType _InterruptThunk; 612 613 static 614 EVT_SYSTEMWORKITEM _InterruptWorkItemCallback; 615 616 static 617 MdInterruptSynchronizeRoutineType _InterruptSynchronizeThunk; 618 619 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 620 621 static 622 MdDeferredRoutineType _InterruptDpcThunk; 623 624 #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE)) 625 626 static 627 MX_WORKITEM_ROUTINE _InterruptWorkItemThunk; 628 629 VOID 630 ThreadpoolWaitCallback( 631 VOID 632 ); 633 634 VOID 635 QueueSingleWaitOnInterruptEvent( 636 VOID 637 ); 638 639 VOID 640 StartThreadpoolWaitQueue( 641 VOID 642 ); 643 644 VOID 645 StopAndFlushThreadpoolWaitQueue( 646 VOID 647 ); 648 649 #endif 650 651 // 652 // Helper functions to enable an interrupt. 653 // Sequence: 654 // (1) InterruptEnable 655 // (2) _InterruptEnableThunk 656 // (3) InterruptEnableInvokeCallback 657 // 658 NTSTATUS 659 InterruptEnable( 660 VOID 661 ); 662 663 static 664 MdInterruptSynchronizeRoutineType _InterruptEnableThunk; 665 666 667 NTSTATUS 668 InterruptEnableInvokeCallback( 669 VOID 670 ); 671 672 // 673 // Helper functions to disable an interrupt. 674 // Sequence: 675 // (1) InterruptDisable 676 // (2) _InterruptDisableThunk 677 // (3) InterruptDisableInvokeCallback 678 // 679 NTSTATUS 680 InterruptDisable( 681 VOID 682 ); 683 684 static 685 MdInterruptSynchronizeRoutineType _InterruptDisableThunk; 686 687 688 NTSTATUS 689 InterruptDisableInvokeCallback( 690 VOID 691 ); 692 public: 693 static 694 BOOLEAN _IsMessageInterrupt(__in USHORT ResourceFlags)695 _IsMessageInterrupt( 696 __in USHORT ResourceFlags 697 ) 698 { 699 if (ResourceFlags & CM_RESOURCE_INTERRUPT_MESSAGE) { 700 return TRUE; 701 } 702 else { 703 return FALSE; 704 } 705 } 706 707 static 708 BOOLEAN _IsWakeHintedInterrupt(__in USHORT ResourceFlags)709 _IsWakeHintedInterrupt( 710 __in USHORT ResourceFlags 711 ) 712 { 713 if (ResourceFlags & CM_RESOURCE_INTERRUPT_WAKE_HINT) { 714 return TRUE; 715 } 716 else { 717 return FALSE; 718 } 719 } 720 721 _Must_inspect_result_ 722 NTSTATUS 723 Connect( 724 __in ULONG NotifyFlags 725 ); 726 727 _Must_inspect_result_ 728 NTSTATUS 729 ConnectInternal( 730 VOID 731 ); 732 733 _Must_inspect_result_ 734 NTSTATUS 735 Disconnect( 736 __in ULONG NotifyFlags 737 ); 738 739 VOID 740 DisconnectInternal( 741 VOID 742 ); 743 744 _Must_inspect_result_ 745 NTSTATUS 746 ForceDisconnect( 747 VOID 748 ); 749 750 _Must_inspect_result_ 751 NTSTATUS 752 ForceReconnect( 753 VOID 754 ); 755 756 VOID 757 FilterResourceRequirements( 758 __inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor 759 ); 760 761 VOID 762 AssignResources( 763 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw, 764 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans 765 ); 766 767 PCM_PARTIAL_RESOURCE_DESCRIPTOR GetResources(VOID)768 GetResources( 769 VOID 770 ) 771 { 772 // Weak ref to the translated resource interrupt descriptor. 773 // It is valid from prepare hardware callback to release hardware callback. 774 return m_CmTranslatedResource; 775 } 776 777 VOID 778 AssignResourcesInternal( 779 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw, 780 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans, 781 __in PWDF_INTERRUPT_INFO InterruptConfig 782 ); 783 784 VOID 785 RevokeResources( 786 VOID 787 ); 788 789 VOID 790 RevokeResourcesInternal( 791 VOID 792 ); 793 794 VOID 795 SetPolicy( 796 __in WDF_INTERRUPT_POLICY Policy, 797 __in WDF_INTERRUPT_PRIORITY Priority, 798 __in PGROUP_AFFINITY TargetProcessorSet 799 ); 800 801 VOID 802 SetPolicyInternal( 803 __in WDF_INTERRUPT_POLICY Policy, 804 __in WDF_INTERRUPT_PRIORITY Priority, 805 __in PGROUP_AFFINITY TargetProcessorSet 806 ); 807 808 VOID 809 FlushQueuedDpcs( 810 VOID 811 ); 812 813 VOID 814 FlushQueuedWorkitem( 815 VOID 816 ); 817 818 VOID 819 InvokeWakeInterruptEvtIsr( 820 VOID 821 ); 822 823 BOOLEAN 824 WakeInterruptIsr( 825 VOID 826 ); 827 828 BOOLEAN IsLevelTriggered(__in ULONG Flags)829 IsLevelTriggered( 830 __in ULONG Flags 831 ) 832 { 833 return ((Flags & CM_RESOURCE_INTERRUPT_LEVEL_LATCHED_BITS) 834 == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE); 835 } 836 837 __inline 838 BOOLEAN QueueDeferredRoutineForIsr(VOID)839 QueueDeferredRoutineForIsr( 840 VOID 841 ) 842 { 843 // 844 // Queue DPC for KMDF and workitem for UMDF. Note that driver can either 845 // specify EvtInterruptDpc or EvtInterruptWorkItem, and therefore it can 846 // either call WdfInterruptQueueDpcForisr or WdfInterruptQueueWorkitemForIsr. 847 // 848 849 850 851 852 // 853 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 854 return QueueDpcForIsr(); 855 #else 856 return QueueWorkItemForIsr(); 857 #endif 858 } 859 860 }; 861 862 BOOLEAN 863 _SynchronizeExecution( 864 __in MdInterrupt Interrupt, 865 __in MdInterruptSynchronizeRoutine SynchronizeRoutine, 866 __in PVOID SynchronizeContext 867 ); 868 869 #endif // _FXINTERRUPT_H_ 870