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, &params);
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, &params);
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                                  &params);
2012 }
2013 
2014