1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 InterruptObject.cpp 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 28 --*/ 29 30 #include "pnppriv.hpp" 31 32 // Tracing support 33 extern "C" { 34 #if defined(EVENT_TRACING) 35 #include "InterruptObject.tmh" 36 #endif 37 } 38 39 // 40 // We need three parameters for KeSynchronizeExecution so we use this 41 // structure on the stack since its a synchronous call 42 // 43 struct FxInterruptSyncParameters { 44 FxInterrupt* Interrupt; 45 PFN_WDF_INTERRUPT_SYNCHRONIZE Callback; 46 WDFCONTEXT Context; 47 }; 48 49 // 50 // At this time we are unable to include wdf19.h in the share code, thus for 51 // now we simply cut and paste the needed structures. 52 // 53 typedef struct _WDF_INTERRUPT_CONFIG_V1_9 { 54 ULONG Size; 55 56 // 57 // If this interrupt is to be synchronized with other interrupt(s) assigned 58 // to the same WDFDEVICE, create a WDFSPINLOCK and assign it to each of the 59 // WDFINTERRUPTs config. 60 // 61 WDFSPINLOCK SpinLock; 62 63 WDF_TRI_STATE ShareVector; 64 65 BOOLEAN FloatingSave; 66 67 // 68 // Automatic Serialization of the DpcForIsr 69 // 70 BOOLEAN AutomaticSerialization; 71 72 // Event Callbacks 73 PFN_WDF_INTERRUPT_ISR EvtInterruptIsr; 74 75 PFN_WDF_INTERRUPT_DPC EvtInterruptDpc; 76 77 PFN_WDF_INTERRUPT_ENABLE EvtInterruptEnable; 78 79 PFN_WDF_INTERRUPT_DISABLE EvtInterruptDisable; 80 81 } WDF_INTERRUPT_CONFIG_V1_9, *PWDF_INTERRUPT_CONFIG_V1_9; 82 83 // 84 // The interrupt config structure has changed post win8-Beta. This is a 85 // temporary definition to allow beta drivers to load on post-beta builds. 86 // Note that size of win8-beta and win8-postbeta structure is different only on 87 // non-x64 platforms, but the fact that size is same on amd64 is harmless because 88 // the struture gets zero'out by init macro, and the default value of the new 89 // field is 0 on amd64. 90 // 91 typedef struct _WDF_INTERRUPT_CONFIG_V1_11_BETA { 92 ULONG Size; 93 94 // 95 // If this interrupt is to be synchronized with other interrupt(s) assigned 96 // to the same WDFDEVICE, create a WDFSPINLOCK and assign it to each of the 97 // WDFINTERRUPTs config. 98 // 99 WDFSPINLOCK SpinLock; 100 101 WDF_TRI_STATE ShareVector; 102 103 BOOLEAN FloatingSave; 104 105 // 106 // DIRQL handling: automatic serialization of the DpcForIsr/WaitItemForIsr. 107 // Passive-level handling: automatic serialization of all callbacks. 108 // 109 BOOLEAN AutomaticSerialization; 110 111 // 112 // Event Callbacks 113 // 114 PFN_WDF_INTERRUPT_ISR EvtInterruptIsr; 115 PFN_WDF_INTERRUPT_DPC EvtInterruptDpc; 116 PFN_WDF_INTERRUPT_ENABLE EvtInterruptEnable; 117 PFN_WDF_INTERRUPT_DISABLE EvtInterruptDisable; 118 PFN_WDF_INTERRUPT_WORKITEM EvtInterruptWorkItem; 119 120 // 121 // These fields are only used when interrupt is created in 122 // EvtDevicePrepareHardware callback. 123 // 124 PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptRaw; 125 PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptTranslated; 126 127 // 128 // Optional passive lock for handling interrupts at passive-level. 129 // 130 WDFWAITLOCK WaitLock; 131 132 // 133 // TRUE: handle interrupt at passive-level. 134 // FALSE: handle interrupt at DIRQL level. This is the default. 135 // 136 BOOLEAN PassiveHandling; 137 138 } WDF_INTERRUPT_CONFIG_V1_11_BETA, *PWDF_INTERRUPT_CONFIG_V1_11_BETA; 139 140 // 141 // Public constructors 142 // 143 FxInterrupt::FxInterrupt( 144 __in PFX_DRIVER_GLOBALS Globals 145 ) : 146 FxNonPagedObject(FX_TYPE_INTERRUPT, sizeof(FxInterrupt), Globals) 147 { 148 149 m_Interrupt = NULL; 150 151 m_OldIrql = PASSIVE_LEVEL; 152 153 m_EvtInterruptEnable = NULL; 154 m_EvtInterruptDisable = NULL; 155 156 m_PassiveHandling = FALSE; 157 158 m_EvtInterruptIsr = NULL; 159 m_EvtInterruptDpc = NULL; 160 m_EvtInterruptWorkItem = NULL; 161 162 m_CallbackLock = NULL; 163 m_WaitLock = NULL; 164 m_SystemWorkItem = NULL; 165 166 m_DisposeWaitLock = FALSE; 167 168 // 169 // We want platform specific behavior for soft disconnect to avoid any 170 // compat issues on existing platforms. In later versions (after 1.11) the 171 // platform differenciation could be removed. 172 // 173 #if defined(_ARM_) 174 m_UseSoftDisconnect = TRUE; 175 #else 176 m_UseSoftDisconnect = FALSE; 177 #endif 178 179 #if FX_IS_KERNEL_MODE 180 KeInitializeDpc(&m_Dpc, _InterruptDpcThunk, this); 181 182 m_Active = FALSE; 183 m_InterruptCaptured = NULL; 184 185 #elif FX_IS_USER_MODE 186 187 m_RdInterruptContext = NULL; 188 m_InterruptWaitblock = NULL; 189 m_CanQueue = FALSE; 190 m_PassiveHandlingByRedirector = FALSE; 191 #endif 192 193 m_Disconnecting = FALSE; 194 m_IsEdgeTriggeredNonMsiInterrupt = FALSE; 195 196 m_ShareVector = WdfUseDefault; 197 198 m_AddedToList = FALSE; 199 m_Connected = FALSE; 200 m_ForceDisconnected = FALSE; 201 m_Enabled = FALSE; 202 m_FloatingSave = FALSE; 203 204 m_WakeInterruptMachine = NULL; 205 206 // This field is init later on. 207 m_CreatedInPrepareHardware = FALSE; 208 209 WDF_INTERRUPT_INFO_INIT(&m_InterruptInfo); 210 m_CmTranslatedResource = NULL; 211 212 Reset(); 213 214 // This is set up by Initialize 215 m_SpinLock = NULL; 216 217 // 218 // MSI Support 219 // 220 m_InterruptInfo.MessageNumber = 0; 221 222 // 223 // WdfIrqPolicyOneCloseProcessor is a safe policy to use. It ensures that 224 // old devices continue to work without any change in their functionality. 225 // 226 m_Policy = WdfIrqPolicyOneCloseProcessor; 227 m_Priority = WdfIrqPriorityUndefined; 228 RtlZeroMemory(&m_Processors, sizeof(m_Processors)); 229 m_SetPolicy = FALSE; 230 231 InitializeListHead(&m_PnpList); 232 233 // 234 // Driver writer can only create WDFINTERRUPTs, not delete them 235 // 236 MarkNoDeleteDDI(ObjectDoNotLock); 237 MarkPassiveDispose(ObjectDoNotLock); 238 MarkDisposeOverride(ObjectDoNotLock); 239 } 240 241 FxInterrupt::~FxInterrupt() 242 { 243 244 // 245 // If this hits, its because someone destroyed the INTERRUPT by 246 // removing too many references by mistake without calling WdfObjectDelete 247 // 248 if( m_Interrupt != NULL ) { 249 DoTraceLevelMessage( 250 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 251 "Destroy Interrupt destroyed without calling WdfObjectDelete, or " 252 "by Framework processing DeviceRemove. Possible reference count problem?"); 253 FxVerifierDbgBreakPoint(GetDriverGlobals()); 254 } 255 256 if (m_Device != NULL) { 257 DoTraceLevelMessage( 258 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 259 "Destroy Interrupt destroyed without calling WdfObjectDelete, or " 260 "by Framework processing DeviceRemove. Possible reference count problem?"); 261 FxVerifierDbgBreakPoint(GetDriverGlobals()); 262 } 263 } 264 265 _Must_inspect_result_ 266 NTSTATUS 267 FxInterrupt::CreateWakeInterruptMachine( 268 VOID 269 ) 270 /*++ 271 272 Routine Description: 273 This routine is used to create a state machine that 274 is used to manage a wake capable interrupt machine 275 276 Arguments: 277 278 Return Value: 279 None 280 281 --*/ 282 { 283 NTSTATUS status; 284 FxWakeInterruptMachine * fxWakeInterruptMachine = NULL; 285 286 ASSERT(NULL == m_WakeInterruptMachine); 287 288 fxWakeInterruptMachine = new (m_Device->m_PkgPnp->GetDriverGlobals()) 289 FxWakeInterruptMachine(this); 290 291 if (NULL == fxWakeInterruptMachine) { 292 status = STATUS_INSUFFICIENT_RESOURCES; 293 DoTraceLevelMessage( 294 GetDriverGlobals(), 295 TRACE_LEVEL_ERROR, TRACINGPNP, 296 "WDFDEVICE 0x%p failed to allocate FxWakeInterruptMachine. %!STATUS!.", 297 m_Device, status); 298 goto exit; 299 } 300 301 status = fxWakeInterruptMachine->Initialize(m_Device->m_PkgPnp->GetDriverGlobals()); 302 if (!NT_SUCCESS(status)) { 303 DoTraceLevelMessage( 304 GetDriverGlobals(), 305 TRACE_LEVEL_ERROR, TRACINGPNP, 306 "WDFDEVICE 0x%p failed to initialize FxWakeInterruptMachine. %!STATUS!.", 307 m_Device, status); 308 goto exit; 309 } 310 311 status = fxWakeInterruptMachine->Init( 312 m_Device->m_PkgPnp, 313 FxWakeInterruptMachine::_ProcessEventInner, 314 fxWakeInterruptMachine 315 ); 316 if (!NT_SUCCESS(status)) { 317 DoTraceLevelMessage( 318 GetDriverGlobals(), 319 TRACE_LEVEL_ERROR, TRACINGPNP, 320 "WDFDEVICE 0x%p failed to init FxWakeInterruptMachine. %!STATUS!.", 321 m_Device, status); 322 goto exit; 323 } 324 325 m_WakeInterruptMachine = fxWakeInterruptMachine; 326 327 fxWakeInterruptMachine->m_IsrEvent.Initialize(SynchronizationEvent,FALSE); 328 329 m_Device->m_PkgPnp->WakeInterruptCreated(); 330 331 DoTraceLevelMessage( 332 GetDriverGlobals(), 333 TRACE_LEVEL_VERBOSE, TRACINGPNP, 334 "WDFDEVICE 0x%p created wake interrupt", 335 m_Device); 336 337 exit: 338 if (!NT_SUCCESS(status)) { 339 if (NULL != fxWakeInterruptMachine) { 340 delete fxWakeInterruptMachine; 341 } 342 } 343 return status; 344 } 345 346 VOID 347 FxInterrupt::InvokeWakeInterruptEvtIsr( 348 VOID 349 /*++ 350 351 Routine Description: 352 This routine is called by the interrupt wake machine to invoke 353 the Evt for the ISR 354 355 Arguments: 356 357 Return Value: 358 None 359 360 --*/ 361 ) 362 { 363 ASSERT(m_PassiveHandling); 364 ASSERT(m_WakeInterruptMachine != NULL); 365 366 // 367 // Acquire our internal passive-lock after the kernel acquired its own 368 // passive-lock and before invoking the callback. 369 // 370 AcquireLock(); 371 372 m_WakeInterruptMachine->m_Claimed = m_EvtInterruptIsr( 373 GetHandle(), 374 m_InterruptInfo.MessageNumber); 375 ReleaseLock(); 376 } 377 378 BOOLEAN 379 FxInterrupt::WakeInterruptIsr( 380 VOID 381 ) 382 /*++ 383 384 Routine Description: 385 This is the ISR for a wake interrupt. This queues an event into the 386 state machine. State machine will take care of waking the device if 387 the device is in Dx and then invoking the driver callback for the 388 interrupt. 389 390 Arguments: 391 392 Return Value: 393 None 394 395 --*/ 396 { 397 ASSERT(m_WakeInterruptMachine != NULL); 398 399 // 400 // Queue an event in the state machine 401 // 402 m_WakeInterruptMachine->ProcessEvent(WakeInterruptEventIsr); 403 404 GetDriverGlobals()->WaitForSignal(m_WakeInterruptMachine->m_IsrEvent.GetSelfPointer(), 405 "Wake Interrupt ISR is stuck waiting for the device" 406 "to power back up and driver calllback to be processed", 407 GetHandle(), 408 GetDriverGlobals()->DbgWaitForWakeInterruptIsrTimeoutInSec, 409 WaitSignalBreakUnderVerifier|WaitSignalBreakUnderDebugger); 410 411 // 412 // State machine stores the return value of the callback in the 413 // m_Claimed member variable 414 // 415 return m_WakeInterruptMachine->m_Claimed; 416 } 417 418 419 _Must_inspect_result_ 420 NTSTATUS 421 FxInterrupt::_CreateAndInit( 422 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 423 __in CfxDevice * Device, 424 __in_opt FxObject * Parent, 425 __in PWDF_OBJECT_ATTRIBUTES Attributes, 426 __in PWDF_INTERRUPT_CONFIG Configuration, 427 __out FxInterrupt ** Interrupt 428 ) 429 { 430 FxInterrupt * pFxInterrupt; 431 NTSTATUS status; 432 433 pFxInterrupt = new (FxDriverGlobals, Attributes) 434 FxInterrupt(FxDriverGlobals); 435 436 if (pFxInterrupt == NULL) { 437 status = STATUS_INSUFFICIENT_RESOURCES; 438 439 DoTraceLevelMessage( 440 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 441 "not enough memory to allocate WDFINTERRUPT for WDFDEVICE %p, " 442 "%!STATUS!", Device, status); 443 444 return status; 445 } 446 447 if (NULL == Parent) { 448 Parent = Device; 449 } 450 451 status = pFxInterrupt->Initialize(Device, Parent, Configuration); 452 453 if (NT_SUCCESS(status)) { 454 status = pFxInterrupt->Commit(Attributes, NULL, Parent); 455 if (!NT_SUCCESS(status)) { 456 DoTraceLevelMessage( 457 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 458 "FxInterrupt Commit failed %!STATUS!", status); 459 } 460 } 461 462 if (NT_SUCCESS(status)) { 463 *Interrupt = pFxInterrupt; 464 } 465 else { 466 pFxInterrupt->DeleteFromFailedCreate(); 467 return status; 468 } 469 470 if (Configuration->CanWakeDevice) { 471 status = pFxInterrupt->CreateWakeInterruptMachine(); 472 } 473 474 if (!NT_SUCCESS(status)) { 475 pFxInterrupt->DeleteFromFailedCreate(); 476 } 477 478 return status; 479 } 480 481 _Must_inspect_result_ 482 NTSTATUS 483 FxInterrupt::Initialize( 484 __in CfxDevice* Device, 485 __in FxObject* Parent, 486 __in PWDF_INTERRUPT_CONFIG Configuration 487 ) 488 { 489 NTSTATUS status; 490 FxPkgPnp* pkgPnp; 491 492 PCM_PARTIAL_RESOURCE_DESCRIPTOR cmDescRaw; 493 PCM_PARTIAL_RESOURCE_DESCRIPTOR cmDescTrans; 494 495 // 496 // DIRQL handling: FxInterrupt can be parented by, and optionally 497 // serialize its DpcForIsr or WorItemForIsr with an FxDevice or 498 // FxIoQueue. 499 // 500 // Passive handling: FxInterrupt can be parented by, and optionally 501 // serialize its WorItemForIsr with an FxDevice or FxIoQueue. 502 503 // 504 // Add a reference to the device object we are associated with. We will be 505 // notified in Dispose to release this reference. 506 // 507 Device->ADDREF(this); 508 509 // 510 // It is important to store the Device only after taking the reference 511 // because Dispose checks for m_Device != NULL to release the reference. If 512 // assign it here and then take the reference later, we can return failure 513 // in between the assignment and reference and then release a reference that 514 // was not taken in Dispose. 515 // 516 m_Device = Device; 517 pkgPnp = m_Device->m_PkgPnp; 518 519 // 520 // NOTE: Since Dispose always releases this reference, we *must* take this 521 // reference, so that even if we return error, the reference is 522 // still there to be removed on cleanup. 523 // 524 ADDREF((PVOID)_InterruptThunk); 525 526 // 527 // Values always supplied by the caller 528 // 529 m_ShareVector = Configuration->ShareVector; 530 m_FloatingSave = Configuration->FloatingSave; 531 532 m_EvtInterruptEnable = Configuration->EvtInterruptEnable; 533 m_EvtInterruptDisable = Configuration->EvtInterruptDisable; 534 535 // 536 // Do further initialization 537 // 538 status = InitializeWorker(Parent, Configuration); 539 if (!NT_SUCCESS(status)) { 540 return status; 541 } 542 543 // 544 // Update the message number used for MSI support 545 // 546 m_InterruptInfo.MessageNumber = pkgPnp->m_InterruptObjectCount; 547 548 // 549 // This logic is executed when the driver creates interrupts in its 550 // EvtDevicePrepareHardware callback. 551 // 552 if (Configuration->InterruptRaw != NULL) { 553 554 ASSERT(Configuration->InterruptTranslated != NULL); 555 ASSERT(m_Device->GetDevicePnpState() != WdfDevStatePnpInit); 556 557 m_CreatedInPrepareHardware = TRUE; 558 559 // 560 // Get the real resource descriptors not the copies. 561 // 562 cmDescRaw = &(CONTAINING_RECORD(Configuration->InterruptRaw, 563 FxResourceCm, 564 m_DescriptorClone))->m_Descriptor; 565 566 cmDescTrans = &(CONTAINING_RECORD(Configuration->InterruptTranslated, 567 FxResourceCm, 568 m_DescriptorClone))->m_Descriptor; 569 // 570 // Assign resources to this interrupt. 571 // 572 FxInterrupt::AssignResources(cmDescRaw, cmDescTrans); 573 } 574 575 // Add this interrupt to the list of them being kept in the PnP package. 576 pkgPnp->AddInterruptObject(this); 577 578 m_AddedToList = TRUE; 579 580 return STATUS_SUCCESS; 581 } 582 583 _Must_inspect_result_ 584 NTSTATUS 585 FxInterrupt::InitializeWorker( 586 __in FxObject* Parent, 587 __in PWDF_INTERRUPT_CONFIG Configuration 588 ) 589 { 590 FxObject* tmpObject; 591 IFxHasCallbacks* callbacks; 592 NTSTATUS status; 593 CfxDeviceBase* deviceBase; 594 BOOLEAN passiveCallbacks; 595 596 const PFX_DRIVER_GLOBALS fxDriverGlobals = GetDriverGlobals(); 597 const WDFTYPE parentType = Parent->GetType(); 598 599 // 600 // Init interrupt's callbacks. 601 // 602 m_EvtInterruptIsr = Configuration->EvtInterruptIsr; 603 m_EvtInterruptDpc = Configuration->EvtInterruptDpc; 604 m_EvtInterruptWorkItem = Configuration->EvtInterruptWorkItem; 605 606 // 607 // Init soft disconnect configuration 608 // 609 switch (Configuration->ReportInactiveOnPowerDown) { 610 case WdfTrue: 611 m_UseSoftDisconnect = TRUE; 612 break; 613 614 case WdfFalse: 615 m_UseSoftDisconnect = FALSE; 616 break; 617 618 case WdfUseDefault: 619 default: 620 // 621 // Leave the driver's value alone. 622 // 623 break; 624 } 625 626 // 627 // TRUE if passive-level interrupt handling is enabled for this interrupt. 628 // 629 m_PassiveHandling = Configuration->PassiveHandling; 630 631 // 632 // If the caller specified a spinlock we use it. 633 // 634 if (Configuration->SpinLock != NULL) { 635 FxSpinLock* pFxSpinLock; 636 637 FxObjectHandleGetPtr(GetDriverGlobals(), 638 Configuration->SpinLock, 639 FX_TYPE_SPIN_LOCK, 640 (PVOID*)&pFxSpinLock); 641 642 pFxSpinLock->SetInterruptSpinLock(); 643 644 m_SpinLock = pFxSpinLock->GetLock(); 645 } 646 else if (m_PassiveHandling == FALSE) { 647 // 648 // If the caller does not specify a spinlock, and this is 649 // a DIRQL interrupt we use a built in one. 650 // Originally this logic was added to allow Acquire/Release Lock 651 // to work on W2K platforms that does not support 652 // KeAcquireInterruptSpinLock. 653 // 654 m_SpinLock = &m_BuiltInSpinLock.Get(); 655 } 656 657 // 658 // Automatic serialization: the DPC or work-item is synchronized with 659 // the parent's callback lock. 660 // 661 662 // 663 // Get the first ancestor that implements callbacks. 664 // 665 deviceBase = FxDeviceBase::_SearchForDevice(Parent, &callbacks); 666 667 // 668 // Validate parent: 669 // - the specified device (from API) must be one of the ancestors. 670 // - the parent can only be a queue or a device. 671 // 672 if (m_DeviceBase == NULL || deviceBase != m_DeviceBase || 673 (parentType != FX_TYPE_QUEUE && parentType != FX_TYPE_DEVICE)) { 674 status = STATUS_INVALID_PARAMETER; 675 DoTraceLevelMessage( 676 fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 677 "The specified object 0x%p is not a valid parent for a " 678 "WDFINTERRUPT, WDF_INTERRUPT_CONFIG structure 0x%p passed, " 679 "%!STATUS!", Parent->GetObjectHandle(), Configuration, status); 680 681 return status; 682 } 683 684 // 685 // If automatic-serialization is off, m_CallbackLock is NULL. 686 // else if parent doesn't use locking, m_CallbackLock is NULL. 687 // else if work-item ISR callback set, m_CallbackLock is a passive lock. 688 // else m_CallbackLock is a spin-lock. 689 // 690 // Note: logic retrieves the parent's callback lock when automatic 691 // serialization is on even if work-item or DPC callbacks are not set, 692 // this is not to break the existing behavior/validation. 693 // 694 if (Configuration->EvtInterruptWorkItem != NULL) { 695 passiveCallbacks = TRUE; 696 } 697 else if (Configuration->EvtInterruptDpc != NULL) { 698 passiveCallbacks = FALSE; 699 } 700 else if (m_PassiveHandling) { 701 passiveCallbacks = TRUE; 702 } 703 else { 704 passiveCallbacks = FALSE; 705 } 706 707 status = _GetEffectiveLock( Parent, 708 callbacks, // IFxHasCallbacks* 709 Configuration->AutomaticSerialization, 710 passiveCallbacks, 711 &m_CallbackLock, 712 &tmpObject // No reference count is taken. 713 ); 714 if (!NT_SUCCESS(status)) { 715 // 716 // We should never incur this error. 717 // 718 ASSERT(status != STATUS_WDF_INCOMPATIBLE_EXECUTION_LEVEL); 719 return status; 720 } 721 722 // 723 // If the parent is a queue, the queue inherits the deletion constraints of the 724 // interrupt object, i.e., driver cannot manually delete the queue. 725 // 726 if (FX_TYPE_QUEUE == parentType) { 727 ((FxIoQueue*)Parent)->SetInterruptQueue(); 728 } 729 730 // 731 // Passive-level handling: init wait-lock. 732 // 733 if (m_PassiveHandling) { 734 ASSERT(NULL == m_SpinLock); 735 736 // 737 //If the caller specified a waitlock, we use it. 738 // 739 if (Configuration->WaitLock != NULL) { 740 ASSERT(m_PassiveHandling); 741 FxObjectHandleGetPtr(GetDriverGlobals(), 742 Configuration->WaitLock, 743 FX_TYPE_WAIT_LOCK, 744 (PVOID*)&m_WaitLock); 745 } 746 747 // 748 // Use a default lock if none was specified. 749 // 750 if (NULL == m_WaitLock) { 751 WDFWAITLOCK waitLock = NULL; 752 WDF_OBJECT_ATTRIBUTES attributes; 753 754 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 755 756 status = FxWaitLock::_Create( 757 fxDriverGlobals, 758 &attributes, 759 NULL, 760 FALSE, 761 &waitLock); 762 763 if (!NT_SUCCESS(status)) { 764 DoTraceLevelMessage(fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 765 "Could not allocate waitlock, %!STATUS!", 766 status); 767 return status; 768 } 769 770 FxObjectHandleGetPtr(fxDriverGlobals, 771 waitLock, 772 FX_TYPE_WAIT_LOCK, 773 (PVOID*)&m_WaitLock); 774 // 775 // Explicitly dispose this wait-lock object. 776 // 777 m_DisposeWaitLock = TRUE; 778 } 779 } 780 781 // 782 // If needed, initialize the interrupt's workitem. 783 // Alloacte workitem if driver specified EvtInterruptWorkitem. 784 // In addition, for Umdf, allocate workitem if driver alternatively 785 // specified EvtInterruptDpc. Note that driver is not allwed to specify both. 786 // 787 if (m_EvtInterruptWorkItem != NULL || 788 (FxLibraryGlobals.IsUserModeFramework && m_EvtInterruptDpc != NULL)) { 789 status = FxSystemWorkItem::_Create( 790 fxDriverGlobals, 791 m_Device->GetDeviceObject(), 792 &m_SystemWorkItem); 793 794 if (!NT_SUCCESS(status)) { 795 DoTraceLevelMessage(fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 796 "Could not allocate workitem, %!STATUS!", 797 status); 798 return status; 799 } 800 } 801 802 // 803 // Do mode-apecific initialization 804 // 805 status = InitializeInternal(Parent, Configuration); 806 if (!NT_SUCCESS(status)) { 807 return status; 808 } 809 810 return STATUS_SUCCESS; 811 } 812 813 VOID 814 FxInterrupt::Reset( 815 VOID 816 ) 817 /*++ 818 819 Routine Description: 820 Resets the interrupt info and synchronize irql for the interrupt. The pnp 821 state machine will call this function every time new resources are assigned 822 to the device. 823 824 Arguments: 825 None 826 827 Return Value: 828 None 829 830 --*/ 831 { 832 // 833 // Other values in m_InterruptInfo survive a reset, so RtlZeroMemory is not 834 // an option. Manually set the fields that need resetting. 835 // 836 m_InterruptInfo.TargetProcessorSet = 0; 837 m_InterruptInfo.Group = 0; 838 m_InterruptInfo.Irql = PASSIVE_LEVEL; 839 m_InterruptInfo.ShareDisposition = 0; 840 m_InterruptInfo.Mode = LevelSensitive; 841 m_InterruptInfo.Vector = 0; 842 843 m_SynchronizeIrql = PASSIVE_LEVEL; 844 845 // 846 // Do mode-specific reset. 847 // For KMDF, it's a no-op. 848 // For UMDF, a message is sent to reflector to reset the interrupt info. 849 // 850 ResetInternal(); 851 } 852 853 // 854 // This is an API call from the device driver to delete this INTERRUPT. 855 // 856 VOID 857 FxInterrupt::DeleteObject( 858 VOID 859 ) 860 { 861 if (m_AddedToList) { 862 // 863 // Pop this off of PnP's list of interrupts. 864 // 865 m_Device->m_PkgPnp->RemoveInterruptObject(this); 866 } 867 868 if (m_CmTranslatedResource != NULL) { 869 // 870 // This can happen if driver explicitly deletes the interrupt in its 871 // release hardware callback. 872 // 873 RevokeResources(); 874 } 875 876 if (m_WakeInterruptMachine) { 877 delete m_WakeInterruptMachine; 878 m_WakeInterruptMachine = NULL; 879 } 880 881 // 882 // Use the base FxObject's DeleteObject implementation which will Dispose us 883 // 884 FxNonPagedObject::DeleteObject(); // __super call 885 } 886 887 // 888 // Called by the PnP package after the driver's release hardware callback. 889 // 890 VOID 891 FxInterrupt::OnPostReleaseHardware( 892 VOID 893 ) 894 { 895 if (m_CreatedInPrepareHardware) { 896 // Delete this interrupt. 897 DeleteObject(); 898 } 899 } 900 901 PWDF_INTERRUPT_INFO 902 FxInterrupt::GetInfo( 903 VOID 904 ) 905 { 906 return &m_InterruptInfo; 907 } 908 909 // 910 // Called from the parent when the parent is being removed. 911 // 912 // Must ensure that any races with Delete are handled properly 913 // 914 BOOLEAN 915 FxInterrupt::Dispose( 916 VOID 917 ) 918 { 919 // MarkPassiveDispose() in Initialize ensures this 920 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 921 922 FlushAndRundown(); 923 924 return TRUE; 925 } 926 927 VOID 928 FxInterrupt::AssignResources( 929 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw, 930 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans 931 ) 932 /*++ 933 934 Routine Description: 935 936 This function allows an interrupt object to know what resources have been 937 assigned to it. It will be called as part of IRP_MN_START_DEVICE. 938 939 Arguments: 940 941 CmDescRaw - A CmResourceDescriptor that describes raw interrupt resources 942 943 CmDescTrans - A CmResourceDescriptor that describes translated interrupt 944 resources 945 946 Return Value: 947 948 VOID 949 950 --*/ 951 { 952 if (CmDescTrans->u.Interrupt.Group > 0 && 953 FxIsProcessorGroupSupported() == FALSE) { 954 // 955 // This should never happen. 956 // 957 FxVerifierDbgBreakPoint(GetDriverGlobals()); 958 } 959 960 #if FX_IS_USER_MODE 961 // 962 // For UMDF, see if this is level-triggered interrupt in which case we need 963 // reflector to handle it at passive level. Also, level-triggered 964 // support is present only on Win8 and newer. Note that, for KMDF, driver 965 // would have provided the choice of handling at passive level so we know 966 // that early on for KMDF, however for UMDF, driver can't specify the choice 967 // and UMDF figures out whether to handle at passive or not by looking at 968 // the interrupt type in resources. 969 // 970 if (IsLevelTriggered(CmDescTrans->Flags) && 971 FxIsPassiveLevelInterruptSupported()) { 972 m_PassiveHandlingByRedirector = TRUE; 973 } 974 #endif 975 976 if (IsPassiveConnect() && _IsMessageInterrupt(CmDescTrans->Flags)) { 977 DoTraceLevelMessage( 978 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 979 "Driver cannot specify PassiveHandling for MSI interrupts."); 980 FxVerifierDbgBreakPoint(GetDriverGlobals()); 981 // IoConnectInterruptEx will fail later on. 982 } 983 984 m_InterruptInfo.Group = CmDescTrans->u.Interrupt.Group; 985 m_InterruptInfo.TargetProcessorSet = CmDescTrans->u.Interrupt.Affinity; 986 m_InterruptInfo.ShareDisposition = CmDescTrans->ShareDisposition; 987 m_InterruptInfo.Mode = 988 CmDescTrans->Flags & CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive; 989 990 // 991 // Interrupt's IRQL. 992 // 993 m_InterruptInfo.Irql = (KIRQL)CmDescTrans->u.Interrupt.Level; 994 995 if (IsPassiveConnect()) { 996 m_InterruptInfo.Irql = PASSIVE_LEVEL; 997 } 998 999 // 1000 // Note if this is an MSI interrupt 1001 // 1002 m_InterruptInfo.MessageSignaled = _IsMessageInterrupt(CmDescTrans->Flags); 1003 1004 // 1005 // Edge-triggered interrupts that are ActiveBoth are made stateful by the OS 1006 // (GPIO buttons driver) to track buttons' press/release state. This is because 1007 // the driver may not have the ability to read the state directly from the hardware. 1008 // 1009 // There is no way to identify ActiveBoth interrupts since KINTERRUPT_POLARITY is 1010 // not exposed to client drivers, so we decided to apply this logic to all 1011 // edge-triggered non-MSI interrupts. 1012 // 1013 m_IsEdgeTriggeredNonMsiInterrupt = (m_InterruptInfo.Mode == Latched && 1014 m_InterruptInfo.MessageSignaled == FALSE); 1015 1016 if (m_InterruptInfo.MessageSignaled && 1017 CmDescRaw->u.MessageInterrupt.Raw.MessageCount > 1) { 1018 // 1019 // This is an assignment for a multi-message PCI 2.2-style resource. 1020 // Thus the vector and message data have to be deduced. 1021 // 1022 m_InterruptInfo.Vector = CmDescTrans->u.Interrupt.Vector + m_InterruptInfo.MessageNumber; 1023 1024 m_Device->SetDeviceTelemetryInfoFlags(DeviceInfoMsi22MultiMessageInterrupt); 1025 } 1026 else { 1027 // 1028 // This is an assignment for a single interrupt, either line-based or 1029 // PCI 2.2 single-message MSI, or PCI 3.0 MSI-X-style resource. 1030 // 1031 m_InterruptInfo.Vector = CmDescTrans->u.Interrupt.Vector; 1032 1033 if (m_InterruptInfo.MessageSignaled) { 1034 m_Device->SetDeviceTelemetryInfoFlags(DeviceInfoMsiXOrSingleMsi22Interrupt); 1035 } 1036 else { 1037 if (IsLevelTriggered(CmDescTrans->Flags)) { 1038 m_Device->SetDeviceTelemetryInfoFlags(DeviceInfoLineBasedLevelTriggeredInterrupt); 1039 } 1040 else { 1041 m_Device->SetDeviceTelemetryInfoFlags(DeviceInfoLineBasedEdgeTriggeredInterrupt); 1042 } 1043 } 1044 } 1045 1046 if (IsPassiveConnect()) { 1047 m_Device->SetDeviceTelemetryInfoFlags(DeviceInfoPassiveLevelInterrupt); 1048 } 1049 1050 // 1051 // Do mode-specific work. For KMDF, it's a no-op. 1052 // For UMDF, send a sync message to Reflector to assign resources. 1053 // 1054 AssignResourcesInternal(CmDescRaw, CmDescTrans, &m_InterruptInfo); 1055 1056 // 1057 // Weak ref to the translated resource interrupt descriptor. 1058 // It is valid from prepare hardware callback to release hardware callback. 1059 // 1060 m_CmTranslatedResource = CmDescTrans; 1061 1062 DoTraceLevelMessage( 1063 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP, 1064 "Is MSI? %d, MSI-ID %d, AffinityPolicy %!WDF_INTERRUPT_POLICY!, " 1065 "Priority %!WDF_INTERRUPT_PRIORITY!, Group %d, Affinity 0x%I64x, " 1066 "Irql 0x%x, Vector 0x%x\n", 1067 m_InterruptInfo.MessageSignaled, 1068 m_InterruptInfo.MessageNumber, 1069 m_Policy, 1070 m_Priority, 1071 m_InterruptInfo.Group, 1072 (ULONGLONG)m_InterruptInfo.TargetProcessorSet, 1073 m_InterruptInfo.Irql, 1074 m_InterruptInfo.Vector); 1075 1076 } 1077 1078 VOID 1079 FxInterrupt::RevokeResources( 1080 VOID 1081 ) 1082 /*++ 1083 1084 Routine Description: 1085 1086 This function tells an interrupt object that it no longer owns any resources. 1087 1088 Arguments: 1089 1090 none 1091 1092 Return Value: 1093 1094 VOID 1095 1096 --*/ 1097 { 1098 ULONG messageNumber; 1099 1100 // 1101 // The message # doesn't change, so we must preserve it. 1102 // 1103 messageNumber = m_InterruptInfo.MessageNumber; 1104 1105 // 1106 // This will zero out all the fields and init the size (as the structure 1107 // can be resued again say after a rebalance). 1108 // 1109 WDF_INTERRUPT_INFO_INIT(&m_InterruptInfo); 1110 1111 m_InterruptInfo.MessageNumber = messageNumber; 1112 1113 // 1114 // Used by interrupts created during 'EvtDevicePrepareHardware' callback. 1115 // 1116 m_CmTranslatedResource = NULL; 1117 1118 // 1119 // Do mode-specific work. For KMDF, it's a no-op. 1120 // For UMDF, send a sync message to Reflector. 1121 // 1122 RevokeResourcesInternal(); 1123 } 1124 1125 VOID 1126 FxInterrupt::SetPolicy( 1127 __in WDF_INTERRUPT_POLICY Policy, 1128 __in WDF_INTERRUPT_PRIORITY Priority, 1129 __in PGROUP_AFFINITY TargetProcessorSet 1130 ) 1131 /*++ 1132 1133 Routine Description: 1134 1135 This function fills in the policy member variables. These values will be 1136 used in IRP_MN_FILTER_RESOURCE_REQUIREMENTS. 1137 1138 Arguments: 1139 1140 Policy - Strategy for assigning target processors 1141 1142 Priority - DIRQL preference 1143 1144 TargetProcessorSet - Processors which should receive this interrupt, if 1145 the policy is "SpecifyProcessors." 1146 1147 Return Value: 1148 1149 VOID 1150 1151 --*/ 1152 { 1153 // 1154 // We cannot apply policy for interrupts created during prepare hardware. 1155 // 1156 if (m_CreatedInPrepareHardware) { 1157 DoTraceLevelMessage( 1158 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 1159 "You cannot apply policy at this stage for WDFINTERRUPT 0x%p " 1160 "For interrupts created in EvtDevicePrepareHardware you must use " 1161 "EvtDeviceFilter APIs or use a pre-process routine to handle the " 1162 "IRP_MN_FILTER_RESOURCE_REQUIREMENT, %!STATUS!", 1163 GetHandle(), STATUS_INVALID_DEVICE_REQUEST); 1164 FxVerifierDbgBreakPoint(GetDriverGlobals()); 1165 } 1166 1167 m_Policy = Policy; 1168 m_Priority = Priority; 1169 m_Processors = *TargetProcessorSet; 1170 1171 // 1172 // Make sure OS supports processor groups, default to group 0 otherwise. 1173 // 1174 if (FxIsProcessorGroupSupported() == FALSE) { 1175 m_Processors.Group = 0; 1176 } 1177 1178 m_SetPolicy = TRUE; 1179 1180 // 1181 // Do mode-specific work. This function does nothing for KMDF. 1182 // It sends a message to reflector for UMDF. 1183 // 1184 SetPolicyInternal(Policy, Priority, TargetProcessorSet); 1185 } 1186 1187 _Must_inspect_result_ 1188 NTSTATUS 1189 FxInterrupt::Connect( 1190 __in ULONG NotifyFlags 1191 ) 1192 /*++ 1193 1194 Routine Description: 1195 1196 This function is the external interface for connecting the interrupt. It 1197 calls the PnP manager to connect the interrupt (only if the operation is 1198 not occurring in a non power pageable state). Then it calls 1199 EvtInterruptEnable at DIRQL and EvtInterruptPostEnable at PASSIVE_LEVEL. 1200 1201 Arguments: 1202 NotifyFlags - combination of values from the enum NotifyResourcesFlags 1203 1204 Return Value: 1205 1206 NTSTATUS 1207 1208 --*/ 1209 { 1210 PFX_DRIVER_GLOBALS pFxDriverGlobals; 1211 NTSTATUS status; 1212 1213 pFxDriverGlobals = GetDriverGlobals(); 1214 1215 if ((NotifyFlags & NotifyResourcesExplicitPowerup) && 1216 IsActiveForWake()) { 1217 // 1218 // If an interrupt is marked as wakeable and the device has been set to 1219 // wake via a driver-owned ISR, leave the interrupt connected. 1220 // 1221 SetActiveForWake(FALSE); 1222 1223 return STATUS_SUCCESS; 1224 } 1225 1226 // 1227 // See if we need to just do soft connect. We do soft connect on explicit 1228 // power up if driver opted-in for that. 1229 // 1230 if (IsSoftDisconnectCapable() && 1231 (NotifyFlags & NotifyResourcesExplicitPowerup)){ 1232 1233 ReportActive(TRUE); 1234 1235 status = STATUS_SUCCESS; 1236 goto Enable; 1237 } 1238 1239 // 1240 // We should either be disconnected or being asked to connect in the NP path 1241 // 1242 ASSERT(m_Connected == FALSE || (NotifyFlags & NotifyResourcesNP)); 1243 1244 if (m_ForceDisconnected) { 1245 return STATUS_SUCCESS; 1246 } 1247 1248 // 1249 // Check to see if this interrupt object was actually assigned any 1250 // resources. If it wasn't, then don't attempt to connect. A WDFINTERRUPT 1251 // object won't be assigned any resources if the underlying device wasn't 1252 // granted any by the PnP manager. 1253 // 1254 if (m_InterruptInfo.Vector == 0) { 1255 return STATUS_SUCCESS; 1256 } 1257 1258 // 1259 // If we are in an NP path, the interrupt remained connected while the device 1260 // went into Dx so there is no need to reconnect it. 1261 // 1262 if ((NotifyFlags & NotifyResourcesNP) == 0) { 1263 1264 ASSERT(m_Interrupt == NULL); 1265 ASSERT(m_SynchronizeIrql != PASSIVE_LEVEL || m_PassiveHandling); 1266 1267 // 1268 // Call pnp manager to connect the interrupt. For KMDF, we call 1269 // kernel DDI, whereas for UMDF, we send a sync message to redirector. 1270 // 1271 status = ConnectInternal(); 1272 1273 if (!NT_SUCCESS(status)) { 1274 m_Interrupt = NULL; 1275 1276 DoTraceLevelMessage( 1277 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 1278 "IoConnectInterrupt(Ex) Failed," 1279 " SpinLock 0x%p," 1280 " Vector 0x%x," 1281 " IRQL 0x%x," 1282 " Synchronize IRQL 0x%x," 1283 " Mode 0x%x," 1284 " ShareVector %s," 1285 " ProcessorGroup %d," 1286 " ProcessorEnableMask 0x%I64x," 1287 " FloatingSave %s," 1288 " %!STATUS!", 1289 m_SpinLock, 1290 m_InterruptInfo.Vector, 1291 m_InterruptInfo.Irql, 1292 m_SynchronizeIrql, 1293 m_InterruptInfo.Mode, 1294 m_InterruptInfo.ShareDisposition == 1295 CmResourceShareShared ? "True" : "False", 1296 m_InterruptInfo.Group, 1297 (ULONGLONG)m_InterruptInfo.TargetProcessorSet, 1298 m_FloatingSave ? "True" : "False", 1299 status 1300 ); 1301 1302 return status; 1303 } 1304 1305 m_Connected = TRUE; 1306 1307 #if FX_IS_KERNEL_MODE 1308 m_Active = TRUE; 1309 #endif 1310 1311 } 1312 else { 1313 ASSERT(m_Connected); 1314 ASSERT(m_Interrupt != NULL); 1315 } 1316 1317 Enable: 1318 1319 // 1320 // Enable the interrupt at the hardware. 1321 // 1322 status = InterruptEnable(); 1323 if (!NT_SUCCESS(status)) { 1324 DoTraceLevelMessage( 1325 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 1326 "EvtInterruptEnable WDFDEVICE %p, WDFINTERRUPT %p, PKINTERRUPT %p " 1327 "returned %!STATUS!", m_Device->GetHandle(), GetHandle(), 1328 m_Interrupt, status); 1329 1330 return status; 1331 } 1332 1333 m_Enabled = TRUE; 1334 1335 return status; 1336 } 1337 1338 _Must_inspect_result_ 1339 NTSTATUS 1340 FxInterrupt::Disconnect( 1341 __in ULONG NotifyFlags 1342 ) 1343 /*++ 1344 1345 Routine Description: 1346 1347 This function is the external interface for disconnecting the interrupt. It 1348 calls the Io manager to disconnect. Then it calls EvtInterruptPreDisable at 1349 PASSIVE_LEVEL and EvtInterruptDisable at DIRQL. 1350 1351 Arguments: 1352 1353 Surprise - Indicates that we are disconnecting due to a surprise-remove, 1354 which means that we shouldn't do anything that touches hardware. 1355 1356 Return Value: 1357 1358 NTSTATUS 1359 1360 --*/ 1361 { 1362 PFX_DRIVER_GLOBALS pFxDriverGlobals; 1363 NTSTATUS status, finalStatus; 1364 1365 finalStatus = STATUS_SUCCESS; 1366 pFxDriverGlobals = GetDriverGlobals(); 1367 1368 // 1369 // Check to see if this interrupt object was actually assigned any 1370 // resources. If it wasn't, then don't attempt to disconnect. A 1371 // WDFINTERRUPT object won't be assigned any resources if the underlying 1372 // device wasn't granted any by the PnP manager. 1373 // 1374 1375 if (m_InterruptInfo.Vector == 0) { 1376 return STATUS_SUCCESS; 1377 } 1378 1379 if (IsWakeCapable() && 1380 (NotifyFlags & NotifyResourcesArmedForWake)) { 1381 // 1382 // If an interrupt is marked as wakeable and the device has been set to 1383 // wake via a driver-owned ISR, leave the interrupt connected. 1384 // 1385 ASSERT(NotifyFlags & NotifyResourcesExplicitPowerDown); 1386 1387 SetActiveForWake(TRUE); 1388 1389 return STATUS_SUCCESS; 1390 } 1391 1392 // 1393 // This takes care of the power-up failure path for interrupt that doesnt 1394 // support soft disconnect. The interrupt has already been hard 1395 // disconnected in power-up failure path. 1396 // 1397 if ((NotifyFlags & NotifyResourcesDisconnectInactive) && 1398 (IsSoftDisconnectCapable() == FALSE) && 1399 (IsActiveForWake() == FALSE)) { 1400 // 1401 // We got here to disconnect an inactive interrupt. But if 1402 // this interrupt is not Soft Disconnect capable then it was 1403 // never made inactive in the first place so nothing to do here. 1404 // It should already be hard disconnected by now. 1405 // 1406 ASSERT(NotifyFlags & NotifyResourcesForceDisconnect); 1407 1408 return STATUS_SUCCESS; 1409 } 1410 1411 1412 if (m_Connected == FALSE) { 1413 // 1414 // No way we can be not connect and enabled 1415 // 1416 ASSERT(m_Enabled == FALSE); 1417 1418 // 1419 // if m_Connected is FALSE because the driver forcefully disconnected 1420 // the interrupt we still want to disconnect the actual interrupt object 1421 // if the caller wants to force disconnect (e.g., the device is being 1422 // removed) 1423 // 1424 if (m_Interrupt != NULL && 1425 (NotifyFlags & NotifyResourcesForceDisconnect)) { 1426 // 1427 // If the driver lets the state machine handle the interrupt state 1428 // we should never get here so make sure the driver forced the issue. 1429 // 1430 ASSERT(m_ForceDisconnected); 1431 1432 goto Disconnect; 1433 } 1434 1435 return STATUS_SUCCESS; 1436 } 1437 1438 if (m_Enabled && ((NotifyFlags & NotifyResourcesSurpriseRemoved) == 0)) { 1439 1440 // 1441 // 1442 // For wake capable interrupts it is possible to enter this path 1443 // with NotifyResourcesDisconnectInactive flag if the device fails 1444 // to power up after the interrupt was left connected during Dx 1445 // 1446 if (IsWakeCapable() == FALSE) { 1447 ASSERT((NotifyFlags & NotifyResourcesDisconnectInactive) == 0); 1448 } 1449 1450 // 1451 // Disable the interrupt at the hardware. 1452 // 1453 status = InterruptDisable(); 1454 1455 m_Enabled = FALSE; 1456 1457 if (!NT_SUCCESS(status)) { 1458 // 1459 // Even upon failure we continue because we don't want to leave 1460 // the interrupt connected when we tear down the stack. 1461 // 1462 DoTraceLevelMessage( 1463 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, 1464 "EvtInterruptDisable WDFDEVICE %p, WDFINTERRUPT %p, " 1465 "PKINTERRUPT %p returned %!STATUS!", 1466 m_Device->GetHandle(), 1467 GetHandle(), m_Interrupt, status); 1468 1469 // 1470 // Overwrite the previous value. Not a big deal since both are 1471 // errors. 1472 // 1473 finalStatus = status; 1474 } 1475 } 1476 1477 #if FX_IS_KERNEL_MODE 1478 // 1479 // Some edge-triggered interrupts may fire before the connection process is 1480 // finished and m_Interrupt is set. To accomodate them, we save the KINTERRUPT 1481 // in _InterruptThunk to m_InterruptCaptured which serves as a backup for 1482 // m_Interrupt. Now we need to NULL m_InterruptCaptured and ensure that 1483 // _InterruptThunk will not re-capture it. 1484 // 1485 if (m_IsEdgeTriggeredNonMsiInterrupt == TRUE) { 1486 // 1487 // Synchronize the setting of m_Disconnecting with any running ISRs. 1488 // No new ISR callbacks will run after _SynchronizeExecution returns, 1489 // until m_Disconnecting is set to FALSE again. 1490 // 1491 if (m_Interrupt != NULL) { 1492 _SynchronizeExecution(m_Interrupt, _InterruptMarkDisconnecting, this); 1493 } 1494 1495 // 1496 // Because m_Disconnecting was set, we know the ISR 1497 // will not re-capture the KINTERRUPT again. 1498 // 1499 m_InterruptCaptured = NULL; 1500 } 1501 #endif 1502 1503 // 1504 // Now flush queued callbacks so that we know that nobody is still trying to 1505 // synchronize against this interrupt. For KMDF this will flush DPCs and 1506 // for UMDF this will send a message to reflector to flush queued DPCs. 1507 // 1508 FlushQueuedDpcs(); 1509 1510 #if FX_IS_KERNEL_MODE 1511 // 1512 // Rundown the workitem if present (passive-level interrupt support or KMDF). 1513 // Not needed for UMDF since reflector doesn't use workitem for isr. 1514 // 1515 FlushQueuedWorkitem(); 1516 1517 #endif 1518 1519 // 1520 // See if we need to just do soft disconnect. Soft disconnect is done only 1521 // during explicit power down. 1522 // 1523 if (IsSoftDisconnectCapable() && 1524 (NotifyFlags & NotifyResourcesExplicitPowerDown)) { 1525 ReportInactive(TRUE); 1526 1527 goto Exit; 1528 } 1529 1530 // 1531 // In the NP path we disable the interrupt but do not disconnect the 1532 // interrupt. (That is b/c IoDisconnectInterrupt is a pagable function and 1533 // calling it could cause paging I/O on this device which will be unserviceable 1534 // because it is in Dx. 1535 // 1536 if (NotifyFlags & NotifyResourcesNP) { 1537 // 1538 // If we are in the NP path, force disconnect should not be set. Force 1539 // disconnect is setup during a query remove/stop power down. 1540 // 1541 ASSERT((NotifyFlags & NotifyResourcesForceDisconnect) == 0); 1542 1543 goto Exit; 1544 } 1545 1546 Disconnect: 1547 // 1548 // Disconnect the interrupt. For KMDF, this calls the kernel DDI, and for 1549 // UMDF, sends a sync message to reflector. 1550 // 1551 DisconnectInternal(); 1552 1553 if (IsActiveForWake()) { 1554 // 1555 // Since the interrupt has been disconnected, it not longer active 1556 // for wake 1557 // 1558 SetActiveForWake(FALSE); 1559 } 1560 1561 m_Connected = FALSE; 1562 1563 #if FX_IS_KERNEL_MODE 1564 m_Active = FALSE; 1565 #endif 1566 1567 Exit: 1568 m_Disconnecting = FALSE; 1569 1570 return finalStatus; 1571 } 1572 1573 1574 _Must_inspect_result_ 1575 NTSTATUS 1576 FxInterrupt::ForceDisconnect( 1577 VOID 1578 ) 1579 { 1580 ULONG flags; 1581 1582 // 1583 // Since we have no context or coordination of power state when these calls 1584 // are made, our only recourse is to see if the device is power pagable or 1585 // not and use that as the basis for our flags. 1586 // 1587 if (m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) { 1588 flags = NotifyResourcesNoFlags; 1589 } 1590 else { 1591 flags = NotifyResourcesNP; 1592 } 1593 1594 // 1595 // Log the event because the driver is not allow the state machine to handle 1596 // the interrupt's state. 1597 // 1598 DoTraceLevelMessage( 1599 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP, 1600 "Force disconnect called on WDFDEVICE %p, WDFINTERRUPT %p, PKINTERRUPT %p", 1601 m_Device->GetHandle(), GetHandle(), m_Interrupt); 1602 1603 m_ForceDisconnected = TRUE; 1604 1605 return Disconnect(flags); 1606 } 1607 1608 _Must_inspect_result_ 1609 NTSTATUS 1610 FxInterrupt::ForceReconnect( 1611 VOID 1612 ) 1613 { 1614 ULONG flags; 1615 1616 // 1617 // Since we have no context or coordination of power state when these calls 1618 // are made, our only recourse is to see if the device is power pagable or 1619 // not and use that as the basis for our flags. 1620 // 1621 if (m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) { 1622 flags = NotifyResourcesNoFlags; 1623 } 1624 else { 1625 flags = NotifyResourcesNP; 1626 } 1627 1628 // 1629 // Log the event because the driver is not allow the state machine to handle 1630 // the interrupt's state. 1631 // 1632 DoTraceLevelMessage( 1633 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP, 1634 "Force connect called on WDFDEVICE %p, WDFINTERRUPT %p, PKINTERRUPT %p", 1635 m_Device->GetHandle(), GetHandle(), m_Interrupt); 1636 1637 m_ForceDisconnected = FALSE; 1638 1639 return Connect(flags); 1640 } 1641 1642 // 1643 // Called by the system work item to finish the rundown 1644 // 1645 VOID 1646 FxInterrupt::FlushAndRundown() 1647 { 1648 FxObject* pObject; 1649 1650 // 1651 // This called at PASSIVE_LEVEL which is required for 1652 // IoDisconnectInterrupt and KeFlushQueuedDpcs 1653 // 1654 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 1655 1656 // 1657 // If we have the KeFlushQueuedDpcs function call it 1658 // to ensure the DPC routine is no longer running before 1659 // we release the final reference to memory and the framework objects 1660 // 1661 FlushQueuedDpcs(); 1662 1663 // 1664 // Do mode-specific work. 1665 // 1666 FlushAndRundownInternal(); 1667 1668 // 1669 // Release the reference taken in FxInterrupt::Initialize 1670 // 1671 if (m_Device != NULL) { 1672 pObject = m_Device; 1673 m_Device = NULL; 1674 1675 pObject->RELEASE(this); 1676 } 1677 1678 // 1679 // Release the reference taken in FxInterrupt::Initialize 1680 // 1681 RELEASE((PVOID)_InterruptThunk); 1682 } 1683 1684 // 1685 // Enable interrupts 1686 // 1687 NTSTATUS 1688 FxInterrupt::InterruptEnableInvokeCallback( 1689 VOID 1690 ) 1691 { 1692 NTSTATUS status; 1693 1694 if (m_PassiveHandling) { 1695 // 1696 // Passive-level interrupt handling: acquire our internal passive-lock 1697 // after the kernel acquired its own passive-lock and before invoking 1698 // the callback. 1699 // 1700 AcquireLock(); 1701 status = m_EvtInterruptEnable(GetHandle(), 1702 m_Device->GetHandle()); 1703 ReleaseLock(); 1704 } 1705 else { 1706 // 1707 // DIRQL interrupt handling: invoke the callback. 1708 // 1709 status = m_EvtInterruptEnable(GetHandle(), 1710 m_Device->GetHandle()); 1711 } 1712 1713 return status; 1714 } 1715 1716 BOOLEAN 1717 FxInterrupt::_InterruptEnableThunk( 1718 __in PVOID SyncContext 1719 ) 1720 { 1721 FxInterruptEnableParameters* p; 1722 1723 p = (FxInterruptEnableParameters*) SyncContext; 1724 1725 p->ReturnVal = p->Interrupt->InterruptEnableInvokeCallback(); 1726 1727 return TRUE; 1728 } 1729 1730 NTSTATUS 1731 FxInterrupt::InterruptEnable( 1732 VOID 1733 ) 1734 { 1735 FxInterruptEnableParameters params; 1736 1737 params.Interrupt = this; 1738 params.ReturnVal = STATUS_SUCCESS; 1739 1740 if (m_EvtInterruptEnable) { 1741 _SynchronizeExecution(m_Interrupt, _InterruptEnableThunk, ¶ms); 1742 } 1743 1744 return params.ReturnVal; 1745 } 1746 1747 // 1748 // Disable interrupts 1749 // 1750 NTSTATUS 1751 FxInterrupt::InterruptDisableInvokeCallback( 1752 VOID 1753 ) 1754 { 1755 NTSTATUS status; 1756 1757 if (m_PassiveHandling) { 1758 // 1759 // Passive-level interrupt handling: acquire our internal passive-lock 1760 // after the kernel acquired its own passive-lock and before invoking 1761 // the callback. 1762 // 1763 AcquireLock(); 1764 status = m_EvtInterruptDisable(GetHandle(), 1765 m_Device->GetHandle()); 1766 ReleaseLock(); 1767 } 1768 else { 1769 // 1770 // DIRQL interrupt handling: invoke the callback. 1771 // 1772 status = m_EvtInterruptDisable(GetHandle(), 1773 m_Device->GetHandle()); 1774 } 1775 1776 return status; 1777 } 1778 1779 1780 BOOLEAN 1781 FxInterrupt::_InterruptDisableThunk( 1782 __in PVOID SyncContext 1783 ) 1784 { 1785 FxInterruptDisableParameters* p; 1786 1787 p = (FxInterruptDisableParameters*) SyncContext; 1788 1789 p->ReturnVal = p->Interrupt->InterruptDisableInvokeCallback(); 1790 1791 return TRUE; 1792 } 1793 1794 NTSTATUS 1795 FxInterrupt::InterruptDisable( 1796 VOID 1797 ) 1798 { 1799 FxInterruptDisableParameters params; 1800 1801 params.Interrupt = this; 1802 params.ReturnVal = STATUS_SUCCESS; 1803 1804 if (m_EvtInterruptDisable != NULL) { 1805 _SynchronizeExecution(m_Interrupt, _InterruptDisableThunk, ¶ms); 1806 } 1807 1808 return params.ReturnVal; 1809 } 1810 1811 BOOLEAN 1812 FxInterrupt::QueueWorkItemForIsr( 1813 VOID 1814 ) 1815 { 1816 BOOLEAN queued; 1817 1818 // 1819 // Using this function is optional, 1820 // but the caller better have registered a handler 1821 // 1822 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 1823 ASSERT(m_EvtInterruptWorkItem != NULL); 1824 #else 1825 ASSERT(m_EvtInterruptWorkItem != NULL || m_EvtInterruptDpc != NULL); 1826 #endif 1827 1828 if (Mx::MxGetCurrentIrql() > DISPATCH_LEVEL) { 1829 // 1830 // Note: if the caller runs at DIRQL, the function returns the result 1831 // of KeInsertQueueDpc() and not that of WorkItem.TryToQueue(). 1832 // The latter result is lost. Docs should clarify this behavior. 1833 // 1834 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) 1835 queued = Mx::MxInsertQueueDpc(&m_Dpc, this, NULL); 1836 #else 1837 queued = FALSE; 1838 FX_VERIFY(INTERNAL, TRAPMSG("Not expected")); 1839 #endif 1840 } 1841 else { 1842 queued = m_SystemWorkItem->TryToEnqueue(_InterruptWorkItemCallback, this); 1843 } 1844 1845 return queued; 1846 } 1847 1848 VOID 1849 FxInterrupt::_InterruptWorkItemCallback( 1850 __in PVOID DeferredContext 1851 ) 1852 /*++ 1853 1854 Routine Description: 1855 Thunk used to invoke EvtInterruptWorkItem at passive-level 1856 1857 --*/ 1858 { 1859 ASSERT(DeferredContext != NULL); 1860 ((FxInterrupt*)DeferredContext)->WorkItemHandler(); 1861 } 1862 1863 VOID 1864 FxInterrupt::FlushQueuedWorkitem( 1865 VOID 1866 ) 1867 { 1868 if (m_SystemWorkItem != NULL) { 1869 m_SystemWorkItem->WaitForExit(); 1870 } 1871 } 1872 1873 #pragma prefast(push) 1874 #pragma prefast(disable:__WARNING_UNEXPECTED_IRQL_CHANGE, "Used unannotated pointers previously") 1875 1876 VOID 1877 FxInterrupt::AcquireLock( 1878 ) 1879 { 1880 if (FALSE == m_PassiveHandling) { 1881 struct _KINTERRUPT* kinterrupt = GetInterruptPtr(); 1882 1883 // 1884 // DIRQL interrupt handling. 1885 // 1886 ASSERTMSG("Can't synchronize when the interrupt isn't connected: ", 1887 kinterrupt != NULL); 1888 1889 if (NULL != kinterrupt) { 1890 m_OldIrql = Mx::MxAcquireInterruptSpinLock(kinterrupt); 1891 } 1892 } 1893 else { 1894 // 1895 // Passive-level interrupt handling when automatic serialization is off. 1896 // 1897 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 1898 ASSERT(m_WaitLock != NULL); 1899 m_WaitLock->AcquireLock(GetDriverGlobals(), NULL); 1900 } 1901 } 1902 #pragma prefast(pop) 1903 1904 BOOLEAN 1905 FxInterrupt::TryToAcquireLock( 1906 ) 1907 { 1908 LONGLONG timeout = 0; 1909 1910 ASSERTMSG("TryToAcquireLock is only available for passive-level " 1911 "interrupt handling: ", m_PassiveHandling); 1912 1913 if (FALSE == m_PassiveHandling) { 1914 return FALSE; 1915 } 1916 1917 ASSERT(m_WaitLock != NULL); 1918 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 1919 1920 // 1921 // Passive-level interrupt handling. Automatic serialization is off. 1922 // 1923 return FxWaitLockInternal::IsLockAcquired( 1924 m_WaitLock->AcquireLock(GetDriverGlobals(), &timeout) 1925 ); 1926 } 1927 1928 #pragma prefast(push) 1929 #pragma prefast(disable:__WARNING_UNEXPECTED_IRQL_CHANGE, "Used unannotated pointers previously") 1930 1931 VOID 1932 FxInterrupt::ReleaseLock( 1933 ) 1934 { 1935 if (FALSE == m_PassiveHandling) { 1936 struct _KINTERRUPT* kinterrupt = GetInterruptPtr(); 1937 1938 // 1939 // DIRQL interrupt handling. 1940 // 1941 ASSERTMSG("Can't synchronize when the interrupt isn't connected: ", 1942 kinterrupt != NULL); 1943 1944 if (NULL != kinterrupt) { 1945 #pragma prefast(suppress:__WARNING_CALLER_FAILING_TO_HOLD, "Unable to annotate ReleaseLock for this case."); 1946 Mx::MxReleaseInterruptSpinLock(kinterrupt, m_OldIrql); 1947 } 1948 } 1949 else { 1950 // 1951 // Passive-level interrupt handling when automatic serialization is off. 1952 // 1953 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 1954 ASSERT(m_WaitLock != NULL); 1955 #pragma prefast(suppress:__WARNING_CALLER_FAILING_TO_HOLD, "Unable to annotate ReleaseLock for this case."); 1956 m_WaitLock->ReleaseLock(GetDriverGlobals()); 1957 } 1958 } 1959 #pragma prefast(pop) 1960 1961 BOOLEAN 1962 FxInterrupt::_InterruptSynchronizeThunk( 1963 __in PVOID SyncContext 1964 ) 1965 { 1966 FxInterruptSyncParameters* p; 1967 BOOLEAN result; 1968 1969 p = (FxInterruptSyncParameters*) SyncContext; 1970 1971 if (p->Interrupt->m_PassiveHandling) { 1972 ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL); 1973 // 1974 // Passive-level interrupt handling: acquire our internal passive-lock 1975 // after the kernel acquired its own passive-lock and before invoking 1976 // the callback. 1977 // 1978 p->Interrupt->AcquireLock(); 1979 result = p->Callback(p->Interrupt->GetHandle(), p->Context); 1980 p->Interrupt->ReleaseLock(); 1981 } 1982 else { 1983 result = p->Callback(p->Interrupt->GetHandle(), p->Context); 1984 } 1985 1986 return result; 1987 } 1988 1989 BOOLEAN 1990 FxInterrupt::Synchronize( 1991 __in PFN_WDF_INTERRUPT_SYNCHRONIZE Callback, 1992 __in WDFCONTEXT Context 1993 ) 1994 { 1995 struct _KINTERRUPT* kinterrupt; 1996 FxInterruptSyncParameters params; 1997 1998 params.Interrupt = this; 1999 params.Callback = Callback; 2000 params.Context = Context; 2001 2002 kinterrupt = GetInterruptPtr(); 2003 2004 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 2005 ASSERTMSG("Can't synchronize when the interrupt isn't connected: ", 2006 kinterrupt != NULL); 2007 #endif 2008 2009 return _SynchronizeExecution(kinterrupt, 2010 _InterruptSynchronizeThunk, 2011 ¶ms); 2012 } 2013 2014