1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 InterruptObjectUm.cpp 8 9 Abstract: 10 11 This module implements a frameworks managed interrupt object 12 13 Author: 14 15 Environment: 16 17 User mode only 18 19 Revision History: 20 21 22 23 --*/ 24 25 #include "fxmin.hpp" 26 #include "FxInterruptThreadpoolUm.hpp" 27 28 extern "C" { 29 #include "InterruptObjectUm.tmh" 30 } 31 32 #define STRSAFE_LIB 33 #include <strsafe.h> 34 35 _Must_inspect_result_ 36 NTSTATUS 37 FxInterrupt::InitializeInternal( 38 __in FxObject* Parent, 39 __in PWDF_INTERRUPT_CONFIG Configuration 40 ) 41 { 42 IWudfDeviceStack *deviceStack; 43 HRESULT hr; 44 NTSTATUS status = STATUS_SUCCESS; 45 FxInterruptThreadpool* pool = NULL; 46 CM_SHARE_DISPOSITION shareVector; 47 48 UNREFERENCED_PARAMETER(Parent); 49 50 deviceStack = m_Device->GetDeviceStack(); 51 52 switch (m_ShareVector) { 53 case WdfTrue: 54 // 55 // Override the bus driver's value, explicitly sharing this interrupt. 56 // 57 shareVector = CmResourceShareShared; 58 break; 59 60 case WdfFalse: 61 // 62 // Override the bus driver's value, explicitly claiming this interrupt 63 // as non-shared. 64 // 65 shareVector = CmResourceShareDeviceExclusive; 66 break; 67 68 case WdfUseDefault: 69 default: 70 // 71 // Leave the bus driver's value alone. 72 // 73 shareVector = CmResourceShareUndetermined; 74 break; 75 } 76 77 // 78 // Create a thread pool if not already created. An interrupt is created in 79 // one of the pnp callbacks (OnAddDevice, OnPrepareHarwdare etc) so there is 80 // no race in getting and setting the theradpool pointer. 81 // 82 pool = m_Device->GetInterruptThreadpool(); 83 if (pool == NULL) { 84 hr = FxInterruptThreadpool::_CreateAndInit(GetDriverGlobals(), 85 &pool); 86 if (FAILED(hr)) 87 { 88 goto exit; 89 } 90 91 m_Device->SetInterruptThreadpool(pool); 92 } 93 94 // 95 // create an instance of interruypt wait block 96 // 97 hr = FxInterruptWaitblock::_CreateAndInit(pool, 98 this, 99 FxInterrupt::_InterruptThunk, 100 &m_InterruptWaitblock); 101 if (FAILED(hr)) { 102 DoTraceLevelMessage(GetDriverGlobals(), 103 TRACE_LEVEL_ERROR, TRACINGPNP, 104 "Waitblock creation failed for CWdfInterrupt object" 105 " %!hresult!", hr); 106 goto exit; 107 } 108 109 // 110 // Send an IOCTL to redirector to create and initialize an interrupt object 111 // 112 deviceStack = m_Device->GetDeviceStack(); 113 114 hr = deviceStack->InitializeInterrupt((WUDF_INTERRUPT_CONTEXT) this, 115 m_InterruptWaitblock->GetEventHandle(), 116 shareVector, 117 &m_RdInterruptContext 118 ); 119 120 if (SUCCEEDED(hr)) 121 { 122 status = STATUS_SUCCESS; 123 } 124 else 125 { 126 PUMDF_VERSION_DATA driverVersion = deviceStack->GetMinDriverVersion(); 127 128 BOOL preserveCompat = 129 deviceStack->ShouldPreserveIrpCompletionStatusCompatibility(); 130 131 status = CHostFxUtil::NtStatusFromHr( 132 hr, 133 driverVersion->MajorNumber, 134 driverVersion->MinorNumber, 135 preserveCompat 136 ); 137 } 138 139 if (!NT_SUCCESS(status)) { 140 DoTraceLevelMessage(GetDriverGlobals(), 141 TRACE_LEVEL_ERROR, TRACINGPNP, 142 "failed to initialize interrupt " 143 "%!STATUS!", status); 144 145 goto exit; 146 } 147 148 exit: 149 150 // 151 // Dispose will do cleanup. No need to cleanup here. 152 // 153 154 return status; 155 } 156 157 NTSTATUS 158 FxInterrupt::ConnectInternal( 159 VOID 160 ) 161 { 162 HRESULT hr; 163 NTSTATUS status; 164 IWudfDeviceStack2 *deviceStack; 165 PFX_DRIVER_GLOBALS pFxDriverGlobals; 166 BOOLEAN isRdConnectingOrConnected = FALSE; 167 168 pFxDriverGlobals = GetDriverGlobals(); 169 deviceStack = m_Device->GetDeviceStack2(); 170 171 // 172 // reset the interrupt event to non-signaled state to start with a 173 // clean slate. 174 // 175 m_InterruptWaitblock->ResetEvent(); 176 177 // 178 // Open the queue and enqueue an interrupt event wait to the threadpool 179 // before connecting the interrupt in the reflector. This minimizes the 180 // processing delay for interrupts that fire as soon as they are connected, 181 // like GPIO button devices that have no means to explicitly enable and 182 // disable interrupts at the hardware level. 183 // 184 StartThreadpoolWaitQueue(); 185 186 // 187 // Tell the PnP Manager to connect the interrupt. Send a message to 188 // redirector to do so. When ConnectInterrupt returns a failure code, 189 // we use isRdConnectingOrConnected to check if the failure was due 190 // to an already connected or currently connecting interrupt. 191 // 192 hr = deviceStack->ConnectInterrupt(m_RdInterruptContext, 193 &isRdConnectingOrConnected); 194 if (FAILED(hr)) 195 { 196 if (isRdConnectingOrConnected) { 197 // 198 // The connect call failed because we asked the Reflector to connect 199 // an already connected or currently connecting interrupt. Perhaps the 200 // client made a redundant call to WdfInterruptEnable. In this case, 201 // we want to keep the threadpool active so that we continue to receive 202 // and acknowledge interrupts - otherwise RdIsrPassiveLevel may time out. 203 // 204 DoTraceLevelMessage(pFxDriverGlobals, 205 TRACE_LEVEL_ERROR, TRACINGPNP, 206 "Multiple connection attempts for !WDFINTERRUPT 0x%p", 207 GetHandle()); 208 209 if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(2, 19)) { 210 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), 211 CHECK("Multiple interrupt connection attempts", FALSE), 212 pFxDriverGlobals->Public.DriverName); 213 } 214 } 215 else { 216 // 217 // Connecting the interrupt in the reflector failed, which means 218 // that IoConnectInterruptEx either failed or was not called at all. 219 // All we need to do in this case is revert the actions done by 220 // StartThreadpoolWaitQueue above, which are closing the queue 221 // and removing the enqueued interrupt event wait. 222 // 223 StopAndFlushThreadpoolWaitQueue(); 224 } 225 226 PUMDF_VERSION_DATA driverVersion = deviceStack->GetMinDriverVersion(); 227 BOOL preserveCompat = 228 deviceStack->ShouldPreserveIrpCompletionStatusCompatibility(); 229 230 status = CHostFxUtil::NtStatusFromHr( 231 hr, 232 driverVersion->MajorNumber, 233 driverVersion->MinorNumber, 234 preserveCompat 235 ); 236 237 DoTraceLevelMessage(pFxDriverGlobals, 238 TRACE_LEVEL_ERROR, TRACINGPNP, 239 "Connect message to reflector returned failure " 240 "%!hresult!", hr); 241 242 return status; 243 } 244 245 status = STATUS_SUCCESS; 246 247 return status; 248 } 249 250 VOID 251 FxInterrupt::DisconnectInternal( 252 VOID 253 ) 254 { 255 IWudfDeviceStack *deviceStack; 256 HRESULT hr; 257 InterruptControlType controlType; 258 259 // 260 // Tell the PnP Manager to disconnect the interrupt. 261 // Send a message to redirector to do so. 262 // 263 deviceStack = m_Device->GetDeviceStack(); 264 265 controlType = InterruptControlTypeDisconnect; 266 hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType); 267 if (FAILED(hr)) 268 { 269 DoTraceLevelMessage(GetDriverGlobals(), 270 TRACE_LEVEL_ERROR, TRACINGPNP, 271 "Disconnect message to reflector returned failure " 272 "%!hresult!", hr); 273 274 FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("Disconnect message to reflector returned failure. "), 275 GetDriverGlobals()->Public.DriverName); 276 } 277 278 // 279 // Now that interrupt has been disconnected, flush the threadpool. Note that 280 // we need to do this after disconnect because if we did it before disconnect, 281 // we might drop any spurious interrupts that were generated after 282 // the driver disabled interrupt generation in its Disable callback, 283 // and after the DPCs were flushed. Fx can't drop spurious interrupt 284 // because if the interrupt is level-triggered then refelctor would be waiting 285 // for acknowledgement. The fact that disconnect command to reflector has 286 // returned to fx guarantees that there are no more interrupts pending in 287 // reflector. 288 // 289 StopAndFlushThreadpoolWaitQueue(); 290 291 // 292 // There might still be WorkItemForIsr running as a result of 293 // the handling of spurious interrupt by driver, so we need to flush the 294 // workitem as well. 295 // 296 FlushQueuedWorkitem(); 297 298 return; 299 } 300 301 VOID 302 FxInterrupt::SetPolicyInternal( 303 __in WDF_INTERRUPT_POLICY Policy, 304 __in WDF_INTERRUPT_PRIORITY Priority, 305 __in PGROUP_AFFINITY TargetProcessorSet 306 ) 307 { 308 IWudfDeviceStack *deviceStack; 309 HRESULT hr; 310 311 deviceStack = m_Device->GetDeviceStack(); 312 313 // 314 // Tell reflector to set the policy of interrupt. 315 // 316 hr = deviceStack->SetInterruptPolicy(m_RdInterruptContext, 317 Policy, 318 Priority, 319 TargetProcessorSet); 320 if (FAILED(hr)) 321 { 322 DoTraceLevelMessage(GetDriverGlobals(), 323 TRACE_LEVEL_ERROR, TRACINGPNP, 324 "SetPolicy message to reflector returned failure " 325 "%!hresult!", hr); 326 } 327 328 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName); 329 330 return; 331 } 332 333 VOID 334 FxInterrupt::FilterResourceRequirements( 335 __inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor 336 ) 337 /*++ 338 339 Routine Description: 340 341 This function allows an interrupt object to change the 342 IoResourceRequirementsList that the PnP Manager sends during 343 IRP_MN_FILTER_RESOURCE_REQUIREMENTS. This function takes a single 344 IoResourceDescriptor and applies default or user specified policy. 345 346 Arguments: 347 348 IoResourceDescriptor - Pointer to descriptor that matches this interrupt object 349 350 Return Value: 351 352 VOID 353 354 --*/ 355 { 356 UNREFERENCED_PARAMETER(IoResourceDescriptor); 357 358 ASSERTMSG("Not implemented for UMDF\n", FALSE); 359 } 360 361 VOID 362 FxInterrupt::ResetInternal( 363 VOID 364 ) 365 { 366 IWudfDeviceStack *deviceStack; 367 InterruptControlType controlType; 368 HRESULT hr; 369 370 if (m_RdInterruptContext == NULL) { 371 // 372 // Reflector hasn't yet created a partner interrupt object so nothing 373 // to do. 374 // 375 return; 376 } 377 378 // 379 // Send a message to redirector to reset interrupt info. 380 // 381 deviceStack = m_Device->GetDeviceStack(); 382 383 controlType = InterruptControlTypeResetInterruptInfo; 384 hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType); 385 if (FAILED(hr)) 386 { 387 DoTraceLevelMessage(GetDriverGlobals(), 388 TRACE_LEVEL_ERROR, TRACINGPNP, 389 "ResetInterruptInfo message to reflector returned failure " 390 "%!hresult!", hr); 391 } 392 393 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName); 394 395 return; 396 } 397 398 VOID 399 FxInterrupt::RevokeResourcesInternal( 400 VOID 401 ) 402 { 403 IWudfDeviceStack *deviceStack; 404 InterruptControlType controlType; 405 HRESULT hr; 406 407 if (m_RdInterruptContext == NULL) { 408 // 409 // Reflector hasn't yet created a partner interrupt object so nothing 410 // to do. 411 // 412 return; 413 } 414 415 // 416 // Send a message to redirector to revoke interrupt resources. 417 // 418 deviceStack = m_Device->GetDeviceStack(); 419 420 controlType = InterruptControlTypeRevokeResources; 421 hr = deviceStack->ControlInterrupt(m_RdInterruptContext, controlType); 422 if (FAILED(hr)) 423 { 424 DoTraceLevelMessage(GetDriverGlobals(), 425 TRACE_LEVEL_ERROR, TRACINGPNP, 426 "RevokeResources message to reflector returned failure " 427 "%!hresult!", hr); 428 } 429 430 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), GetDriverGlobals()->Public.DriverName); 431 432 return; 433 } 434 435 VOID 436 FxInterrupt::FlushQueuedDpcs( 437 VOID 438 ) 439 { 440 IWudfDeviceStack *deviceStack; 441 HRESULT hr; 442 443 // 444 // Send a message to redirector to flush DPCs. 445 // 446 deviceStack = m_Device->GetDeviceStack(); 447 hr = deviceStack->ControlInterrupt(m_RdInterruptContext, 448 InterruptControlTypeFlushQueuedDpcs); 449 if (FAILED(hr)) 450 { 451 DoTraceLevelMessage(GetDriverGlobals(), 452 TRACE_LEVEL_ERROR, TRACINGPNP, 453 "FlushQueuedDpcs message to reflector returned failure " 454 "%!hresult!", hr); 455 456 FX_VERIFY_WITH_NAME(INTERNAL, 457 TRAPMSG("FlushQueuedDpcs message to reflector returned failure"), 458 GetDriverGlobals()->Public.DriverName); 459 } 460 461 return; 462 } 463 464 VOID 465 FxInterrupt::AssignResourcesInternal( 466 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw, 467 __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans, 468 __in PWDF_INTERRUPT_INFO InterruptInfo 469 ) 470 { 471 IWudfDeviceStack *deviceStack; 472 HRESULT hr; 473 474 // 475 // level-triggered interrupt handling is supported only on win8 and newer. 476 // 477 if (IsLevelTriggered(CmDescTrans->Flags) && 478 FxIsPassiveLevelInterruptSupported() == FALSE) { 479 hr = E_INVALIDARG; 480 DoTraceLevelMessage(GetDriverGlobals(), 481 TRACE_LEVEL_ERROR, TRACINGPNP, 482 "Failed to assign interrupt resource to interrupt object" 483 "because interrupt resource is for level-triggered interrupt" 484 "which is not supported on this platform. See the docs for info on" 485 "supported platforms. %!hresult!\n", hr); 486 487 FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), TRAPMSG( 488 "Failed to assign interrupt resource to interrupt object" 489 "because interrupt resource is for level-triggered interrupt" 490 "which is not supported on this platform. See the docs for info on" 491 "supported platforms."), 492 GetDriverGlobals()->Public.DriverName); 493 } 494 495 // 496 // Sharing is only supported for level-triggered interrupts. We allow 497 // shared latched interrupts in order to accomodate incorrect device 498 // firmwares that mistakenly declare their exclusive resources as shared. 499 // Genuinely shared edge-triggered interrupts will cause a deadlock 500 // because of how the OS handles non-passive latched interrupts with 501 // multiple registered handlers. See RdInterrupt::AssignResources 502 // for details. 503 // 504 if (IsLevelTriggered(CmDescTrans->Flags) == FALSE && 505 CmDescTrans->ShareDisposition != CmResourceShareDeviceExclusive) { 506 507 DoTraceLevelMessage(GetDriverGlobals(), 508 TRACE_LEVEL_WARNING, TRACINGPNP, 509 "The resource descriptor indicates that this is a shared " 510 "edge-triggered interrupt. UMDF only supports sharing of " 511 "level-triggered interrupts. Please check if your device " 512 "firmware mistakenly declares this resource as shared " 513 "instead of device exclusive. If the resource is in fact " 514 "shared, then UMDF does not support this device.\n"); 515 } 516 517 // 518 // Tell the PnP Manager to assign resources to the interrupt. 519 // Send a message to redirector to do so. 520 // 521 deviceStack = m_Device->GetDeviceStack(); 522 523 hr = deviceStack->AssignInterruptResources(m_RdInterruptContext, 524 CmDescRaw, 525 CmDescTrans, 526 InterruptInfo, 527 m_PassiveHandlingByRedirector); 528 if (FAILED(hr)) 529 { 530 DoTraceLevelMessage(GetDriverGlobals(), 531 TRACE_LEVEL_ERROR, TRACINGPNP, 532 "AssignResources message to reflector returned failure " 533 "%!hresult!\n", hr); 534 } 535 536 FX_VERIFY_WITH_NAME(INTERNAL, CHECK_HR(hr), 537 GetDriverGlobals()->Public.DriverName); 538 539 } 540 541 VOID 542 FxInterrupt::ThreadpoolWaitCallback( 543 VOID 544 ) 545 { 546 BOOLEAN claimed; 547 548 // 549 // ETW event for performance measurement 550 // 551 EventWriteEVENT_UMDF_FX_INTERRUPT_NOTIFICATION_RECEIVED( 552 m_InterruptInfo.MessageNumber 553 ); 554 555 // 556 // Invoke the ISR callback under interrupt lock. 557 // 558 if (IsWakeCapable()) { 559 // 560 // if it is a wake capable interrupt, we will hand it off 561 // to the state machine so that it can power up the device 562 // if required and then invoke the ISR callback 563 // 564 claimed = WakeInterruptIsr(); 565 } else { 566 AcquireLock(); 567 claimed = m_EvtInterruptIsr(GetHandle(), 568 m_InterruptInfo.MessageNumber 569 ); 570 ReleaseLock(); 571 } 572 573 // 574 // Queue another wait. MSDN: You must re-register the event with the 575 // wait object before signaling it each time to trigger the wait callback. 576 // 577 if (m_CanQueue) { 578 QueueSingleWaitOnInterruptEvent(); 579 } 580 581 // 582 // Return acknowledgement to reflector if it's handled at passive level 583 // by reflector. 584 // 585 if (m_PassiveHandlingByRedirector) { 586 IWudfDeviceStack *deviceStack; 587 HRESULT hr; 588 589 deviceStack = m_Device->GetDeviceStack(); 590 591 hr = deviceStack->AcknowledgeInterrupt(m_RdInterruptContext, claimed); 592 593 if (FAILED(hr)) { 594 DoTraceLevelMessage(GetDriverGlobals(), 595 TRACE_LEVEL_ERROR, TRACINGPNP, 596 "AcknowledgeInterrupt message to reflector returned " 597 "failure. Check UMDF log for failure reason. %!hresult!", hr); 598 599 FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("AcknowledgeInterrupt message to " 600 "reflector returned failure. Check UMDF log for failure reason. "), 601 GetDriverGlobals()->Public.DriverName); 602 } 603 } 604 605 return; 606 } 607 608 VOID 609 FxInterrupt::QueueSingleWaitOnInterruptEvent( 610 VOID 611 ) 612 { 613 m_InterruptWaitblock->SetThreadpoolWait(); 614 } 615 616 VOID 617 FxInterrupt::StartThreadpoolWaitQueue( 618 VOID 619 ) 620 { 621 m_CanQueue = TRUE; 622 623 QueueSingleWaitOnInterruptEvent(); 624 } 625 626 VOID 627 FxInterrupt::StopAndFlushThreadpoolWaitQueue( 628 VOID 629 ) 630 { 631 // 632 // We need to stop the threadpool wait queue and accomplish the following: 633 // 1) Prevent any new waits from being queued. 634 // 2) Removed any waits already queued. 635 // 3) Wait for interrupt isr callback to complete. 636 // 637 638 // 639 // Prevent any more enquing now that interrupt has been disconnected. 640 // 641 m_CanQueue = FALSE; 642 643 // 644 // wait for isr callback 645 // 646 m_InterruptWaitblock->WaitForOutstandingCallbackToComplete(); 647 648 // 649 // remove any waits already queued 650 // 651 m_InterruptWaitblock->ClearThreadpoolWait(); 652 653 // 654 // wait for callback. This additional wait for callback is needed to 655 // handle the follwoing race: 656 // - CanQueue is set to false in this thread 657 // - Callback is executing at statement after CanQueue check so it did not 658 // see false. 659 // - this thread waits for callback 660 // - callback thread queues a wait and returns 661 // - the wait earlier queued is satisfied and callback runs 662 // - this thread clears the queue (there is nothing to clear) but there is 663 // still a callback runnning and this thread needs to wait. 664 // 665 m_InterruptWaitblock->WaitForOutstandingCallbackToComplete(); 666 } 667 668 VOID 669 CALLBACK 670 FxInterrupt::_InterruptThunk( 671 PTP_CALLBACK_INSTANCE Instance, 672 PVOID Parameter, 673 PTP_WAIT Wait, 674 TP_WAIT_RESULT WaitResult 675 ) 676 { 677 FxInterrupt* fxInterrupt = (FxInterrupt*) Parameter; 678 679 UNREFERENCED_PARAMETER(Instance); 680 UNREFERENCED_PARAMETER(Wait); 681 UNREFERENCED_PARAMETER(WaitResult); 682 683 fxInterrupt->ThreadpoolWaitCallback(); 684 685 return; 686 } 687 688 VOID 689 FxInterrupt::FlushAndRundownInternal( 690 VOID 691 ) 692 { 693 // 694 // flush the threadpool callbacks 695 // 696 StopAndFlushThreadpoolWaitQueue(); 697 698 // 699 // Rundown the workitem. 700 // 701 if (m_SystemWorkItem != NULL) { 702 m_SystemWorkItem->DeleteObject(); 703 m_SystemWorkItem = NULL; 704 } 705 706 // 707 // If present, delete the default passive-lock. 708 // 709 if (m_DisposeWaitLock) { 710 ASSERT(m_WaitLock != NULL); 711 m_WaitLock->DeleteObject(); 712 m_WaitLock = NULL; 713 m_DisposeWaitLock = FALSE; 714 } 715 716 // 717 // waitblock destructor will ensure event and waitblock cleanup. 718 // 719 if (m_InterruptWaitblock != NULL) { 720 delete m_InterruptWaitblock; 721 m_InterruptWaitblock = NULL; 722 } 723 724 // 725 // No need to explicitly delete the COM wrapper (CWdfInterrupt). 726 // The COM wrapper will get deleted in fxInterrupt's destroy callback when 727 // the object tree reference taken during creation will be released. 728 // 729 } 730 731 BOOLEAN 732 FxInterrupt::QueueDpcForIsr( 733 VOID 734 ) 735 { 736 FX_VERIFY_WITH_NAME(INTERNAL, TRAPMSG("Not implemented"), 737 GetDriverGlobals()->Public.DriverName); 738 return FALSE; 739 } 740 741 VOID 742 FxInterrupt::WorkItemHandler( 743 VOID 744 ) 745 { 746 // 747 // For UMDF, we allow drivers to call WdfInterruptQueueDpcdForIsr, and 748 // internally we queue a workitem and invoke EvtInterruptDpc. Only 749 // one of the callbacks EvtInterruptDpc or EvtInterruptWorkitem is 750 // allowed. 751 // 752 ASSERT(m_EvtInterruptWorkItem != NULL || m_EvtInterruptDpc != NULL); 753 ASSERT((m_EvtInterruptWorkItem != NULL && m_EvtInterruptDpc != NULL) == FALSE); 754 755 FX_TRACK_DRIVER(GetDriverGlobals()); 756 757 // 758 // Call the drivers registered WorkItemForIsr event callback 759 // 760 if (m_CallbackLock != NULL) { 761 KIRQL irql = 0; 762 763 m_CallbackLock->Lock(&irql); 764 if (m_EvtInterruptWorkItem != NULL) { 765 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle()); 766 } 767 else { 768 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle()); 769 } 770 m_CallbackLock->Unlock(irql); 771 } 772 else { 773 if (m_EvtInterruptWorkItem != NULL) { 774 m_EvtInterruptWorkItem(GetHandle(), m_Device->GetHandle()); 775 } 776 else { 777 m_EvtInterruptDpc(GetHandle(), m_Device->GetHandle()); 778 } 779 } 780 781 return; 782 } 783 784 BOOLEAN 785 _SynchronizeExecution( 786 __in MdInterrupt Interrupt, 787 __in MdInterruptSynchronizeRoutine SynchronizeRoutine, 788 __in PVOID SynchronizeContext 789 ) 790 { 791 FxInterruptEnableParameters* pParams; 792 BOOLEAN isPassive; 793 794 UNREFERENCED_PARAMETER(Interrupt); 795 796 pParams = (FxInterruptEnableParameters*) SynchronizeContext; 797 isPassive = pParams->Interrupt->IsPassiveHandling(); 798 FX_VERIFY(INTERNAL, CHECK("Must be Passive Interrupt", isPassive)); 799 800 // 801 // The internal synchronize routine will call the routine under lock 802 // 803 return SynchronizeRoutine(SynchronizeContext); 804 } 805