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 //
FxInterrupt(__in PFX_DRIVER_GLOBALS Globals)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
~FxInterrupt()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
CreateWakeInterruptMachine(VOID)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
InvokeWakeInterruptEvtIsr(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
WakeInterruptIsr(VOID)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
_CreateAndInit(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in CfxDevice * Device,__in_opt FxObject * Parent,__in PWDF_OBJECT_ATTRIBUTES Attributes,__in PWDF_INTERRUPT_CONFIG Configuration,__out FxInterrupt ** Interrupt)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
Initialize(__in CfxDevice * Device,__in FxObject * Parent,__in PWDF_INTERRUPT_CONFIG Configuration)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
InitializeWorker(__in FxObject * Parent,__in PWDF_INTERRUPT_CONFIG Configuration)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
Reset(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
DeleteObject(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
OnPostReleaseHardware(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
GetInfo(VOID)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
Dispose(VOID)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
AssignResources(__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans)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
RevokeResources(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
SetPolicy(__in WDF_INTERRUPT_POLICY Policy,__in WDF_INTERRUPT_PRIORITY Priority,__in PGROUP_AFFINITY TargetProcessorSet)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
Connect(__in ULONG NotifyFlags)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
Disconnect(__in ULONG NotifyFlags)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
ForceDisconnect(VOID)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
ForceReconnect(VOID)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
FlushAndRundown()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
InterruptEnableInvokeCallback(VOID)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
_InterruptEnableThunk(__in PVOID SyncContext)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
InterruptEnable(VOID)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
InterruptDisableInvokeCallback(VOID)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
_InterruptDisableThunk(__in PVOID SyncContext)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
InterruptDisable(VOID)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
QueueWorkItemForIsr(VOID)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
_InterruptWorkItemCallback(__in PVOID DeferredContext)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
FlushQueuedWorkitem(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
AcquireLock()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
TryToAcquireLock()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
ReleaseLock()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
_InterruptSynchronizeThunk(__in PVOID SyncContext)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
Synchronize(__in PFN_WDF_INTERRUPT_SYNCHRONIZE Callback,__in WDFCONTEXT Context)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