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