1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxInterruptApi.cpp
8 
9 Abstract:
10 
11     This implements the WDFINTERRUPT API's
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 #include "pnppriv.hpp"
28 
29 extern "C" {
30 // #include "FxInterruptApi.tmh"
31 }
32 
33 //
34 // At this time we are unable to include wdf19.h in the share code, thus for
35 // now we simply cut and paste the needed structures.
36 //
37 typedef struct _WDF_INTERRUPT_CONFIG_V1_9 {
38     ULONG              Size;
39 
40     //
41     // If this interrupt is to be synchronized with other interrupt(s) assigned
42     // to the same WDFDEVICE, create a WDFSPINLOCK and assign it to each of the
43     // WDFINTERRUPTs config.
44     //
45     WDFSPINLOCK        SpinLock;
46 
47     WDF_TRI_STATE      ShareVector;
48 
49     BOOLEAN            FloatingSave;
50 
51     //
52     // Automatic Serialization of the DpcForIsr
53     //
54     BOOLEAN            AutomaticSerialization;
55 
56     // Event Callbacks
57     PFN_WDF_INTERRUPT_ISR         EvtInterruptIsr;
58 
59     PFN_WDF_INTERRUPT_DPC         EvtInterruptDpc;
60 
61     PFN_WDF_INTERRUPT_ENABLE      EvtInterruptEnable;
62 
63     PFN_WDF_INTERRUPT_DISABLE     EvtInterruptDisable;
64 
65 } WDF_INTERRUPT_CONFIG_V1_9, *PWDF_INTERRUPT_CONFIG_V1_9;
66 
67 //
68 // The interrupt config structure has changed post win8-Beta. This is a
69 // temporary definition to allow beta drivers to load on post-beta builds.
70 // Note that size of win8-beta and win8-postbeta structure is different only on
71 // non-x64 platforms, but the fact that size is same on amd64 is harmless because
72 // the struture gets zero'out by init macro, and the default value of the new
73 // field is 0 on amd64.
74 //
75 typedef struct _WDF_INTERRUPT_CONFIG_V1_11_BETA {
76     ULONG              Size;
77 
78     //
79     // If this interrupt is to be synchronized with other interrupt(s) assigned
80     // to the same WDFDEVICE, create a WDFSPINLOCK and assign it to each of the
81     // WDFINTERRUPTs config.
82     //
83     WDFSPINLOCK                     SpinLock;
84 
85     WDF_TRI_STATE                   ShareVector;
86 
87     BOOLEAN                         FloatingSave;
88 
89     //
90     // DIRQL handling: automatic serialization of the DpcForIsr/WaitItemForIsr.
91     // Passive-level handling: automatic serialization of all callbacks.
92     //
93     BOOLEAN                         AutomaticSerialization;
94 
95     //
96     // Event Callbacks
97     //
98     PFN_WDF_INTERRUPT_ISR           EvtInterruptIsr;
99     PFN_WDF_INTERRUPT_DPC           EvtInterruptDpc;
100     PFN_WDF_INTERRUPT_ENABLE        EvtInterruptEnable;
101     PFN_WDF_INTERRUPT_DISABLE       EvtInterruptDisable;
102     PFN_WDF_INTERRUPT_WORKITEM      EvtInterruptWorkItem;
103 
104     //
105     // These fields are only used when interrupt is created in
106     // EvtDevicePrepareHardware callback.
107     //
108     PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptRaw;
109     PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptTranslated;
110 
111     //
112     // Optional passive lock for handling interrupts at passive-level.
113     //
114     WDFWAITLOCK                     WaitLock;
115 
116     //
117     // TRUE: handle interrupt at passive-level.
118     // FALSE: handle interrupt at DIRQL level. This is the default.
119     //
120     BOOLEAN                         PassiveHandling;
121 
122 } WDF_INTERRUPT_CONFIG_V1_11_BETA, *PWDF_INTERRUPT_CONFIG_V1_11_BETA;
123 
124 //
125 // Interrupt Configuration Structure
126 //
127 typedef struct _WDF_INTERRUPT_CONFIG_V1_11 {
128     ULONG              Size;
129 
130     //
131     // If this interrupt is to be synchronized with other interrupt(s) assigned
132     // to the same WDFDEVICE, create a WDFSPINLOCK and assign it to each of the
133     // WDFINTERRUPTs config.
134     //
135     WDFSPINLOCK                     SpinLock;
136 
137     WDF_TRI_STATE                   ShareVector;
138 
139     BOOLEAN                         FloatingSave;
140 
141     //
142     // DIRQL handling: automatic serialization of the DpcForIsr/WaitItemForIsr.
143     // Passive-level handling: automatic serialization of all callbacks.
144     //
145     BOOLEAN                         AutomaticSerialization;
146 
147     //
148     // Event Callbacks
149     //
150     PFN_WDF_INTERRUPT_ISR           EvtInterruptIsr;
151     PFN_WDF_INTERRUPT_DPC           EvtInterruptDpc;
152     PFN_WDF_INTERRUPT_ENABLE        EvtInterruptEnable;
153     PFN_WDF_INTERRUPT_DISABLE       EvtInterruptDisable;
154     PFN_WDF_INTERRUPT_WORKITEM      EvtInterruptWorkItem;
155 
156     //
157     // These fields are only used when interrupt is created in
158     // EvtDevicePrepareHardware callback.
159     //
160     PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptRaw;
161     PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptTranslated;
162 
163     //
164     // Optional passive lock for handling interrupts at passive-level.
165     //
166     WDFWAITLOCK                     WaitLock;
167 
168     //
169     // TRUE: handle interrupt at passive-level.
170     // FALSE: handle interrupt at DIRQL level. This is the default.
171     //
172     BOOLEAN                         PassiveHandling;
173 
174     //
175     // TRUE: Interrupt is reported inactive on explicit power down
176     //       instead of disconnecting it.
177     // FALSE: Interrupt is disconnected instead of reporting inactive
178     //        on explicit power down.
179     // DEFAULT: Framework decides the right value.
180     //
181     WDF_TRI_STATE                   ReportInactiveOnPowerDown;
182 
183 } WDF_INTERRUPT_CONFIG_V1_11, *PWDF_INTERRUPT_CONFIG_V1_11;
184 
185 //
186 // extern "C" the entire file
187 //
188 extern "C" {
189 
190 _Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)191 __drv_maxIRQL(DISPATCH_LEVEL)
192 NTSTATUS
193 STDCALL
194 WDFEXPORT(WdfInterruptCreate)(
195     __in
196     PWDF_DRIVER_GLOBALS DriverGlobals,
197     __in
198     WDFDEVICE Device,
199     __in
200     PWDF_INTERRUPT_CONFIG Configuration,
201     __in_opt
202     PWDF_OBJECT_ATTRIBUTES Attributes,
203     __out
204     WDFINTERRUPT* Interrupt
205     )
206 
207 /*++
208 
209 Routine Description:
210 
211     Create an Interrupt object along with its associated DpcForIsr.
212 
213 Arguments:
214 
215     Object      - Handle to a WDFDEVICE or WDFQUEUE to associate the interrupt with.
216 
217     pConfiguration - WDF_INTERRUPT_CONFIG structure.
218 
219     pAttributes - Optional WDF_OBJECT_ATTRIBUTES to request a context
220                   memory allocation, and a DestroyCallback.
221 
222     pInterrupt  - Pointer to location to return the resulting WDFINTERRUPT handle.
223 
224 Returns:
225 
226     STATUS_SUCCESS - A WDFINTERRUPT handle has been created.
227 
228 --*/
229 
230 {
231     DDI_ENTRY();
232 
233     PFX_DRIVER_GLOBALS      pFxDriverGlobals;
234     FxInterrupt*            pFxInterrupt;
235     FxDevice*               pDevice;
236     FxObject*               pParent;
237     NTSTATUS                status;
238     WDF_DEVICE_PNP_STATE    devicePnpState;
239     ULONG                   expectedConfigSize;
240     WDF_INTERRUPT_CONFIG    intConfig;
241 
242     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
243                                    Device,
244                                    FX_TYPE_DEVICE,
245                                    (PVOID*)&pDevice,
246                                    &pFxDriverGlobals);
247 
248     FxPointerNotNull(pFxDriverGlobals, Configuration);
249     FxPointerNotNull(pFxDriverGlobals, Interrupt);
250 
251     if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,13)) {
252         expectedConfigSize = sizeof(WDF_INTERRUPT_CONFIG);
253     } else if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,11)) {
254         expectedConfigSize = sizeof(WDF_INTERRUPT_CONFIG_V1_11);
255     } else {
256         expectedConfigSize = sizeof(WDF_INTERRUPT_CONFIG_V1_9);
257     }
258 
259 
260     //
261     // We could check if Configuration->Size != expectedConfigSize, but
262     // logic below allows me to installing old WDF drivers v1.11 on new WDF.
263     //
264     if (Configuration->Size != sizeof(WDF_INTERRUPT_CONFIG) &&
265         Configuration->Size != sizeof(WDF_INTERRUPT_CONFIG_V1_11) &&
266         Configuration->Size != sizeof(WDF_INTERRUPT_CONFIG_V1_11_BETA) &&
267         Configuration->Size != sizeof(WDF_INTERRUPT_CONFIG_V1_9)) {
268 
269         DoTraceLevelMessage(
270             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
271             "WDF_INTERRUPT_CONFIG size 0x%x incorrect, expected 0x%x",
272             Configuration->Size, expectedConfigSize);
273 
274         return STATUS_INFO_LENGTH_MISMATCH;
275     }
276 
277     //
278     // Normalize WDF_INTERRUPT_CONFIG structure.
279     //
280     if (Configuration->Size < sizeof(WDF_INTERRUPT_CONFIG)) {
281         //
282         // Init new fields to default values.
283         //
284         WDF_INTERRUPT_CONFIG_INIT(&intConfig,
285                                   Configuration->EvtInterruptIsr,
286                                   Configuration->EvtInterruptDpc);
287         //
288         // Copy over existing fields and readjust the struct size.
289         //
290         RtlCopyMemory(&intConfig, Configuration, Configuration->Size);
291         intConfig.Size = sizeof(intConfig);
292 
293         //
294         // Use new config structure from now on.
295         //
296         Configuration = &intConfig;
297     }
298 
299     //
300     // Parameter validation.
301     //
302 
303 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
304     //
305     // Ensure access to interrupts is allowed
306     //
307     FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO),
308         CHECK(ERROR_STRING_HW_ACCESS_NOT_ALLOWED,
309         (pDevice->IsInterruptAccessAllowed() == TRUE)),
310         DriverGlobals->DriverName);
311 
312     //
313     // PassiveHandling member must be set to TRUE. INIT macro does that for
314     // UMDF. This check ensures the field is not overriden by caller.
315     //
316     FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO),
317         CHECK("PassiveHandling not set to TRUE in WDF_INTERRUPT_CONFIG structure",
318         (Configuration->PassiveHandling == TRUE)),
319         DriverGlobals->DriverName);
320 #endif
321 
322     if (Configuration->EvtInterruptIsr == NULL) {
323         DoTraceLevelMessage(
324             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
325             "NULL EvtInterruptIsr in WDF_INTERRUPT_CONFIG structure 0x%p"
326             " passed", Configuration);
327         return STATUS_INVALID_PARAMETER;
328     }
329 
330     //
331     // Driver can specify a parent only in the AutomaticSerialization case.
332     //
333     status = FxValidateObjectAttributes(
334                         pFxDriverGlobals,
335                         Attributes,
336                         Configuration->AutomaticSerialization ?
337                             FX_VALIDATE_OPTION_NONE_SPECIFIED :
338                             FX_VALIDATE_OPTION_PARENT_NOT_ALLOWED);
339     if (!NT_SUCCESS(status)) {
340         return status;
341     }
342 
343     if (Attributes != NULL && Attributes->ParentObject != NULL) {
344         FxObjectHandleGetPtr(pFxDriverGlobals,
345                              Attributes->ParentObject,
346                              FX_TYPE_OBJECT,
347                             (PVOID*)&pParent);
348     }
349     else {
350         pParent = (FxObject*)pDevice;
351     }
352 
353     devicePnpState = pDevice->GetDevicePnpState();
354 
355     if (devicePnpState != WdfDevStatePnpInit &&
356         0x0 == (pDevice->GetCallbackFlags() &
357                     FXDEVICE_CALLBACK_IN_PREPARE_HARDWARE)) {
358 
359         status = STATUS_INVALID_DEVICE_STATE;
360 
361         DoTraceLevelMessage(
362           pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
363           "WDFINTERRUPTs can only be created during: "
364           "(1) EvtDriverDeviceAdd when the WDFDEVICE %p is initially created, or "
365           "(2) EvtDevicePrepareHardware, %!STATUS!", Device, status);
366 
367         return status;
368     }
369 
370     //
371     // Interrupts created in EvtDriverDeviceAdd must not specify any
372     // CM resources b/c driver doesn't known them yet.
373     //
374     if (devicePnpState == WdfDevStatePnpInit) {
375 
376         if (Configuration->InterruptRaw != NULL ||
377             Configuration->InterruptTranslated != NULL) {
378 
379             status = STATUS_INVALID_PARAMETER;
380 
381             DoTraceLevelMessage(
382                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
383                 "Not NULL InterruptRaw or InterruptTranslated in "
384                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
385                 Configuration, status);
386 
387             return status;
388         }
389 
390         //
391         // Wake Interrupts can not be created in AddDevice as it is
392         // not known if they are actually marked as wake capable
393         //
394         if (Configuration->CanWakeDevice) {
395             status = STATUS_INVALID_PARAMETER;
396 
397             DoTraceLevelMessage(
398                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
399                 "CanWakeDevice set in WDF_INTERRUPT_CONFIG structure  0x%p"
400                 "during EvtDriverDeviceAdd, %!STATUS!",
401                 Configuration, status);
402 
403             return status;
404         }
405     }
406 
407     //
408     // Checks for interrupt created in EvtDevicePrepareHardware.
409     //
410     if (devicePnpState != WdfDevStatePnpInit) {
411         //
412         // CM resources must be specified.
413         //
414         if (Configuration->InterruptRaw == NULL ||
415             Configuration->InterruptTranslated == NULL) {
416 
417             status = STATUS_INVALID_DEVICE_STATE;
418 
419             DoTraceLevelMessage(
420                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
421                 "NULL InterruptRaw or InterruptTranslated in "
422                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
423                 Configuration, status);
424 
425             return status;
426         }
427 
428         //
429         // Share vector must be default.
430         //
431         if (Configuration->ShareVector != WdfUseDefault) {
432             status = STATUS_INVALID_DEVICE_STATE;
433 
434             DoTraceLevelMessage(
435                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
436                 "Driver cannot specify ShareVector different from "
437                 "WdfUseDefault in EvtDevicePrepareHardware callback,"
438                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
439                 Configuration, status);
440 
441             return status;
442         }
443 
444     }
445 
446     if (Configuration->CanWakeDevice) {
447         if (FxInterrupt::_IsWakeHintedInterrupt(
448             Configuration->InterruptTranslated->Flags) == FALSE) {
449 
450             status = STATUS_INVALID_PARAMETER;
451             DoTraceLevelMessage(
452                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
453                 "Driver cannot specify an interrupt as CanWakeDevice if "
454                 "the CM_RESOURCE_INTERRUPT_WAKE_HINT is not present."
455                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
456                 Configuration, status);
457 
458             return status;
459         }
460 
461         //
462         // Driver is allowed to create wake interrupt only if it
463         // is the power policy owner so that the we can wake the
464         // stack when the interrupt fires
465         //
466         if (pDevice->m_PkgPnp->IsPowerPolicyOwner() == FALSE) {
467             status = STATUS_INVALID_PARAMETER;
468             DoTraceLevelMessage(
469                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
470                 "Driver cannot specify an interrupt as CanWakeDevice if "
471                 "it is not power policy powner. WDF_INTERRUPT_CONFIG "
472                 "structure 0x%p passed, %!STATUS!",
473                 Configuration, status);
474 
475             return status;
476         }
477 
478         //
479         // Declaring an interrupt as wake capable allows it to
480         // take ownership of interrupt from ACPI. It does not
481         // make sense for a PDO
482         //
483         if (pDevice->IsPdo()) {
484             status = STATUS_INVALID_PARAMETER;
485             DoTraceLevelMessage(
486                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
487                 "Driver cannot specify an interrupt as CanWakeDevice for a PDO "
488                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
489                 Configuration, status);
490 
491             return status;
492         }
493     }
494 
495     if (Configuration->EvtInterruptDpc != NULL &&
496         Configuration->EvtInterruptWorkItem != NULL) {
497         status = STATUS_INVALID_PARAMETER;
498         DoTraceLevelMessage(
499             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
500             "Driver can provide either EvtInterruptDpc or "
501             "EvtInterruptWorkItem callback but not both. "
502             "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
503             Configuration, status);
504 
505         return status;
506     }
507 
508     if (Configuration->PassiveHandling == FALSE) {
509         //
510         // DIRQL handling validations.
511         //
512         if (Configuration->WaitLock) {
513             status = STATUS_INVALID_PARAMETER;
514             DoTraceLevelMessage(
515                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
516                 "Driver cannot specify WaitLock when handling interrupts at "
517                 "DIRQL, WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
518                 Configuration, status);
519 
520             return status;
521         }
522         //
523         // Wake interrupts should be passive level so that the stack
524         // can be sychronously bring the device back to D0 form within
525         // the ISR
526         //
527         if (Configuration->CanWakeDevice) {
528             status = STATUS_INVALID_PARAMETER;
529 
530             DoTraceLevelMessage(
531                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
532                 "CanWakeDevice set in WDF_INTERRUPT_CONFIG structure for an"
533                 "interrupt not marked as passive 0x%p, %!STATUS!",
534                 Configuration, status);
535 
536             return status;
537         }
538     }
539     else {
540         //
541         // Passive-level handling validations.
542         //
543         if (FxIsPassiveLevelInterruptSupported() == FALSE) {
544             status = STATUS_NOT_SUPPORTED;
545             DoTraceLevelMessage(
546                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
547                 "The current version of Windows does not support the handling "
548                 "of interrupts at passive-level, WDF_INTERRUPT_CONFIG "
549                 "structure 0x%p passed, %!STATUS!", Configuration, status);
550 
551             return status;
552         }
553 
554         if (Configuration->SpinLock) {
555             status = STATUS_INVALID_PARAMETER;
556             DoTraceLevelMessage(
557                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
558                 "Driver cannot specify SpinLock when handling interrupts at "
559                 "passive-level, WDF_INTERRUPT_CONFIG structure 0x%p passed, "
560                 "%!STATUS!", Configuration, status);
561 
562             return status;
563         }
564 
565 
566        //
567        // For UMDF reflector decides whether to handle the interrupt
568        // at passive or DIRQL. Driver has no choice. Therefore this check
569        // is applicable only for KMDF.
570        //
571 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
572         if (Configuration->InterruptTranslated != NULL &&
573             FxInterrupt::_IsMessageInterrupt(
574                 Configuration->InterruptTranslated->Flags)) {
575             status = STATUS_INVALID_PARAMETER;
576             DoTraceLevelMessage(
577                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
578                 "Driver cannot specify PassiveHandling for MSI interrupts, "
579                 "WDF_INTERRUPT_CONFIG structure 0x%p passed, %!STATUS!",
580                 Configuration, status);
581 
582             return status;
583         }
584 #endif
585     }
586 
587     //
588     // Make sure the specified resources are valid.
589     // Do this check only under verifier b/c if the driver has a lot of
590     // interrupts/resources, this verification can slow down power up.
591     // Note that if InterruptRaw is != NULL, it implies that
592     // InterruptTranslated is != NULL from the checks above.
593     //
594     if (pFxDriverGlobals->FxVerifierOn) {
595         if (Configuration->InterruptRaw != NULL ) {
596             status = pDevice->m_PkgPnp->ValidateInterruptResourceCm(
597                                         Configuration->InterruptRaw,
598                                         Configuration->InterruptTranslated,
599                                         Configuration);
600             if (!NT_SUCCESS(status)) {
601                 return status;
602             }
603         }
604     }
605 
606     status = FxInterrupt::_CreateAndInit(
607             pFxDriverGlobals,
608             pDevice,
609             pParent,
610             Attributes,
611             Configuration,
612             &pFxInterrupt);
613 
614     if (NT_SUCCESS(status)) {
615         *Interrupt = (WDFINTERRUPT) pFxInterrupt->GetObjectHandle();
616     }
617 
618     return status;
619 }
620 
621 BOOLEAN
622 STDCALL
WDFEXPORT(WdfInterruptQueueDpcForIsr)623 WDFEXPORT(WdfInterruptQueueDpcForIsr)(
624     __in
625     PWDF_DRIVER_GLOBALS DriverGlobals,
626     __in
627     WDFINTERRUPT Interrupt
628     )
629 
630 /*++
631 
632 Routine Description:
633 
634     Queue the DPC for the ISR
635 
636 Arguments:
637 
638     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
639 
640 Returns:
641 
642     TRUE - If the DPC has been enqueued.
643 
644     FALSE - If the DPC has already been enqueued.
645 
646 --*/
647 
648 {
649     DDI_ENTRY();
650 
651     FxInterrupt* pFxInterrupt;
652 
653     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
654                          Interrupt,
655                          FX_TYPE_INTERRUPT,
656                          (PVOID*)&pFxInterrupt);
657 
658     return pFxInterrupt->QueueDeferredRoutineForIsr();
659 }
660 
661 BOOLEAN
662 STDCALL
WDFEXPORT(WdfInterruptQueueWorkItemForIsr)663 WDFEXPORT(WdfInterruptQueueWorkItemForIsr)(
664     __in
665     PWDF_DRIVER_GLOBALS DriverGlobals,
666     __in
667     WDFINTERRUPT Interrupt
668     )
669 
670 /*++
671 
672 Routine Description:
673 
674     Queue the interrupt's work-item for the ISR.
675 
676 Arguments:
677 
678     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
679 
680 Returns:
681 
682     TRUE - If the work-item has been enqueued.
683 
684     FALSE - If the work-item has already been enqueued.
685 
686 
687 --*/
688 
689 {
690     DDI_ENTRY();
691 
692     FxInterrupt*    pFxInterrupt;
693 
694     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
695                          Interrupt,
696                          FX_TYPE_INTERRUPT,
697                          (PVOID*)&pFxInterrupt);
698 
699     return pFxInterrupt->QueueWorkItemForIsr();
700 }
701 
__drv_maxIRQL(DISPATCH_LEVEL)702 __drv_maxIRQL(DISPATCH_LEVEL)
703 BOOLEAN
704 STDCALL
705 WDFEXPORT(WdfInterruptSynchronize)(
706     __in
707     PWDF_DRIVER_GLOBALS DriverGlobals,
708     __in
709     WDFINTERRUPT Interrupt,
710     __in
711     PFN_WDF_INTERRUPT_SYNCHRONIZE Callback,
712     __in
713     WDFCONTEXT Context
714     )
715 
716 /*++
717 
718 Routine Description:
719 
720     Synchronize execution with the interrupt handler
721 
722 Arguments:
723 
724     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
725 
726     Callback     - PWDF_INTERRUPT_SYNCHRONIZE callback function to invoke
727 
728     Context      - Context to pass to the PFN_WDF_INTERRUPT_SYNCHRONIZE callback
729 
730 Returns:
731 
732     BOOLEAN result from user PFN_WDF_INTERRUPT_SYNCHRONIZE callback
733 
734 --*/
735 
736 {
737     DDI_ENTRY();
738 
739     FxInterrupt*    pFxInterrupt;
740     NTSTATUS        status;
741 
742     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
743                          Interrupt,
744                          FX_TYPE_INTERRUPT,
745                          (PVOID*)&pFxInterrupt);
746 
747     if (pFxInterrupt->IsPassiveHandling()) {
748         status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
749                                           PASSIVE_LEVEL);
750         if (!NT_SUCCESS(status)) {
751             return FALSE;
752         }
753     }
754 
755     FxPointerNotNull(pFxInterrupt->GetDriverGlobals(), Callback);
756 
757     return pFxInterrupt->Synchronize(Callback, Context);
758 }
759 
__drv_maxIRQL(DISPATCH_LEVEL)760 __drv_maxIRQL(DISPATCH_LEVEL)
761 VOID
762 STDCALL
763 WDFEXPORT(WdfInterruptAcquireLock)(
764     __in
765     PWDF_DRIVER_GLOBALS DriverGlobals,
766     __in
767     _Requires_lock_not_held_(_Curr_)
768     _Acquires_lock_(_Curr_)
769     WDFINTERRUPT Interrupt
770     )
771 
772 /*++
773 
774 Routine Description:
775 
776     Begin synchronization to the interrupts IRQL level
777 
778 Arguments:
779 
780     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
781 
782 Returns:
783 
784 --*/
785 
786 {
787     DDI_ENTRY();
788 
789     FxInterrupt*    pFxInterrupt;
790     NTSTATUS        status;
791 
792     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
793                          Interrupt,
794                          FX_TYPE_INTERRUPT,
795                          (PVOID*)&pFxInterrupt);
796 
797     if (pFxInterrupt->IsPassiveHandling()) {
798         status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
799                                           PASSIVE_LEVEL);
800         if (!NT_SUCCESS(status)) {
801             return;
802         }
803     }
804 
805     pFxInterrupt->AcquireLock();
806 }
807 
808 __drv_maxIRQL(DISPATCH_LEVEL + 1)
809 VOID
810 STDCALL
WDFEXPORT(WdfInterruptReleaseLock)811 WDFEXPORT(WdfInterruptReleaseLock)(
812     __in
813     PWDF_DRIVER_GLOBALS DriverGlobals,
814     __in
815     _Requires_lock_held_(_Curr_)
816     _Releases_lock_(_Curr_)
817     WDFINTERRUPT Interrupt
818     )
819 
820 /*++
821 
822 Routine Description:
823 
824     End synchronization to the interrupts IRQL level
825 
826 Arguments:
827 
828     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
829 
830 Returns:
831 
832 --*/
833 
834 {
835     DDI_ENTRY();
836 
837     FxInterrupt*    pFxInterrupt;
838     NTSTATUS        status;
839 
840     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
841                          Interrupt,
842                          FX_TYPE_INTERRUPT,
843                          (PVOID*)&pFxInterrupt);
844 
845     if (pFxInterrupt->IsPassiveHandling()) {
846         status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
847                                           PASSIVE_LEVEL);
848         if (!NT_SUCCESS(status)) {
849             return;
850         }
851     }
852 
853     pFxInterrupt->ReleaseLock();
854 }
855 
__drv_maxIRQL(PASSIVE_LEVEL)856 __drv_maxIRQL(PASSIVE_LEVEL)
857 VOID
858 STDCALL
859 WDFEXPORT(WdfInterruptEnable)(
860     __in
861     PWDF_DRIVER_GLOBALS DriverGlobals,
862     __in
863     WDFINTERRUPT Interrupt
864     )
865 
866 /*++
867 
868 Routine Description:
869 
870     Request that the interrupt be enabled in the hardware.
871 
872 Arguments:
873 
874     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
875 
876 Returns:
877 
878 --*/
879 
880 {
881     DDI_ENTRY();
882 
883     FxInterrupt* pFxInterrupt;
884     NTSTATUS status;
885 
886     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
887                          Interrupt,
888                          FX_TYPE_INTERRUPT,
889                          (PVOID*)&pFxInterrupt);
890 
891     status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
892                                       PASSIVE_LEVEL);
893     if (!NT_SUCCESS(status)) {
894         return;
895     }
896 
897     //
898     // Okay to ignore error status. Called function prints error messages.
899     //
900     (VOID) pFxInterrupt->ForceReconnect();
901 }
902 
__drv_maxIRQL(PASSIVE_LEVEL)903 __drv_maxIRQL(PASSIVE_LEVEL)
904 VOID
905 STDCALL
906 WDFEXPORT(WdfInterruptDisable)(
907     __in
908     PWDF_DRIVER_GLOBALS DriverGlobals,
909     __in
910     WDFINTERRUPT Interrupt
911     )
912 
913 /*++
914 
915 Routine Description:
916 
917     Request that the interrupt be disabled in the hardware.
918 
919 Arguments:
920 
921     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
922 
923 Returns:
924 
925 --*/
926 
927 {
928     DDI_ENTRY();
929 
930     FxInterrupt* pFxInterrupt;
931     NTSTATUS status;
932 
933     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
934                          Interrupt,
935                          FX_TYPE_INTERRUPT,
936                          (PVOID*)&pFxInterrupt);
937 
938     status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
939                                       PASSIVE_LEVEL);
940     if (!NT_SUCCESS(status)) {
941         return;
942     }
943 
944     //
945     // Okay to ignore error status. Called function prints error messages.
946     //
947     (VOID) pFxInterrupt->ForceDisconnect();
948 }
949 
950 _Must_inspect_result_
951 struct _KINTERRUPT*
952 STDCALL
WDFEXPORT(WdfInterruptWdmGetInterrupt)953 WDFEXPORT(WdfInterruptWdmGetInterrupt)(
954     __in
955     PWDF_DRIVER_GLOBALS DriverGlobals,
956     __in
957     WDFINTERRUPT Interrupt
958     )
959 
960 /*++
961 
962 Routine Description:
963 
964     Return the WDM _KINTERRUPT*
965 
966 Arguments:
967 
968     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
969 
970 Returns:
971 
972 --*/
973 
974 {
975     DDI_ENTRY();
976 
977     FxInterrupt* pFxInterrupt;
978 
979     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
980                          Interrupt,
981                          FX_TYPE_INTERRUPT,
982                          (PVOID*)&pFxInterrupt);
983 
984     return pFxInterrupt->GetInterruptPtr();
985 }
986 
__drv_maxIRQL(DISPATCH_LEVEL)987 __drv_maxIRQL(DISPATCH_LEVEL)
988 VOID
989 STDCALL
990 WDFEXPORT(WdfInterruptGetInfo)(
991     __in
992     PWDF_DRIVER_GLOBALS DriverGlobals,
993     __in
994     WDFINTERRUPT Interrupt,
995     __out
996     PWDF_INTERRUPT_INFO    Info
997     )
998 
999 /*++
1000 
1001 Routine Description:
1002 
1003     Return the WDF_INTERRUPT_INFO that describes this
1004     particular Message Signaled Interrupt MessageID.
1005 
1006 Arguments:
1007 
1008     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1009 
1010 Returns:
1011     Nothing
1012 
1013 --*/
1014 {
1015     DDI_ENTRY();
1016 
1017     PFX_DRIVER_GLOBALS  pFxDriverGlobals    = NULL;
1018     FxInterrupt*        pFxInterrupt        = NULL;
1019     ULONG               size                = 0;
1020 
1021     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1022                                    Interrupt,
1023                                    FX_TYPE_INTERRUPT,
1024                                    (PVOID*)&pFxInterrupt,
1025                                    &pFxDriverGlobals);
1026 
1027     FxPointerNotNull(pFxDriverGlobals, Info);
1028 
1029     if (sizeof(WDF_INTERRUPT_INFO_V1_7) == Info->Size) {
1030         size = sizeof(WDF_INTERRUPT_INFO_V1_7);
1031     }
1032     else if (sizeof(WDF_INTERRUPT_INFO) == Info->Size) {
1033         size = sizeof(WDF_INTERRUPT_INFO);
1034     }
1035     else {
1036         DoTraceLevelMessage(
1037             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1038             "WDF_INTERRUPT_INFO %p Size %d invalid, expected %d",
1039             Interrupt, Info->Size, sizeof(WDF_INTERRUPT_INFO));
1040         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1041         return;
1042     }
1043 
1044     RtlCopyMemory(Info, pFxInterrupt->GetInfo(), size);
1045 
1046     Info->Size = size;
1047 }
1048 
1049 WDFDEVICE
1050 STDCALL
WDFEXPORT(WdfInterruptGetDevice)1051 WDFEXPORT(WdfInterruptGetDevice)(
1052     __in
1053     PWDF_DRIVER_GLOBALS DriverGlobals,
1054     __in
1055     WDFINTERRUPT Interrupt
1056     )
1057 
1058 /*++
1059 
1060 Routine Description:
1061 
1062     Get the device that the interrupt is related to.
1063 
1064 Arguments:
1065 
1066     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1067 
1068 Returns:
1069 
1070     WDFDEVICE
1071 
1072 --*/
1073 
1074 {
1075     DDI_ENTRY();
1076 
1077     FxInterrupt* pFxInterrupt;
1078 
1079     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
1080                          Interrupt,
1081                          FX_TYPE_INTERRUPT,
1082                          (PVOID*)&pFxInterrupt);
1083 
1084     return pFxInterrupt->GetDevice()->GetHandle();
1085 }
1086 
__drv_maxIRQL(DISPATCH_LEVEL)1087 __drv_maxIRQL(DISPATCH_LEVEL)
1088 VOID
1089 STDCALL
1090 WDFEXPORT(WdfInterruptSetPolicy)(
1091     __in
1092     PWDF_DRIVER_GLOBALS DriverGlobals,
1093     __in
1094     WDFINTERRUPT Interrupt,
1095     __in
1096     WDF_INTERRUPT_POLICY Policy,
1097     __in
1098     WDF_INTERRUPT_PRIORITY Priority,
1099     __in
1100     KAFFINITY TargetProcessorSet
1101     )
1102 
1103 /*++
1104 
1105 Routine Description:
1106 
1107     Interrupts have attributes that a driver might want to influence.  This
1108     routine tells the Framework to tell the PnP manager what the driver would
1109     prefer.
1110 
1111 Arguments:
1112 
1113     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1114 
1115 Returns:
1116 
1117     NTSTATUS
1118 
1119 --*/
1120 
1121 {
1122     DDI_ENTRY();
1123 
1124     PFX_DRIVER_GLOBALS  pFxDriverGlobals = NULL;
1125     FxInterrupt*        pFxInterrupt     = NULL;
1126     GROUP_AFFINITY      processorSet;
1127 
1128     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1129                                    Interrupt,
1130                                    FX_TYPE_INTERRUPT,
1131                                    (PVOID*)&pFxInterrupt,
1132                                    &pFxDriverGlobals);
1133 
1134     if (Policy < WdfIrqPolicyMachineDefault ||
1135         Policy > WdfIrqPolicySpreadMessagesAcrossAllProcessors) {
1136         DoTraceLevelMessage(
1137             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1138             "Policy %d is out of range", Policy);
1139         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1140         return;
1141     }
1142 
1143     if (Priority < WdfIrqPriorityLow || Priority > WdfIrqPriorityHigh) {
1144         DoTraceLevelMessage(
1145             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1146             "Priority %d is out of range", Priority);
1147         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1148         return;
1149     }
1150 
1151     RtlZeroMemory(&processorSet, sizeof(processorSet));
1152     processorSet.Mask = TargetProcessorSet;
1153 
1154     pFxInterrupt->SetPolicy(Policy, Priority, &processorSet);
1155 }
1156 
__drv_maxIRQL(DISPATCH_LEVEL)1157 __drv_maxIRQL(DISPATCH_LEVEL)
1158 VOID
1159 STDCALL
1160 WDFEXPORT(WdfInterruptSetExtendedPolicy)(
1161     __in
1162     PWDF_DRIVER_GLOBALS DriverGlobals,
1163     __in
1164     WDFINTERRUPT Interrupt,
1165     __in
1166     PWDF_INTERRUPT_EXTENDED_POLICY PolicyAndGroup
1167     )
1168 
1169 /*++
1170 
1171 Routine Description:
1172 
1173     Interrupts have attributes that a driver might want to influence.  This
1174     routine tells the Framework to tell the PnP manager what the driver would
1175     prefer.
1176 
1177 Arguments:
1178 
1179     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1180 
1181     PolicyAndGroup - Driver preference for policy, priority and group affinity.
1182 
1183 Returns:
1184 
1185     NTSTATUS
1186 
1187 --*/
1188 
1189 {
1190     DDI_ENTRY();
1191 
1192     PFX_DRIVER_GLOBALS      pFxDriverGlobals    = NULL;
1193     FxInterrupt*            pFxInterrupt        = NULL;
1194     PGROUP_AFFINITY         processorSet        = NULL;
1195     WDF_INTERRUPT_POLICY    policy;
1196     WDF_INTERRUPT_PRIORITY  priority;
1197 
1198     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1199                                    Interrupt,
1200                                    FX_TYPE_INTERRUPT,
1201                                    (PVOID*)&pFxInterrupt,
1202                                    &pFxDriverGlobals);
1203 
1204     if (PolicyAndGroup->Size != sizeof(WDF_INTERRUPT_EXTENDED_POLICY)) {
1205         DoTraceLevelMessage(
1206             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1207             "WDF_INTERRUPT_EXTENDED_POLICY %p Size %d invalid, expected %d",
1208             PolicyAndGroup,
1209             PolicyAndGroup->Size,
1210             sizeof(WDF_INTERRUPT_EXTENDED_POLICY));
1211         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1212         return;
1213     }
1214 
1215     policy = PolicyAndGroup->Policy;
1216     priority = PolicyAndGroup->Priority;
1217     processorSet = &PolicyAndGroup->TargetProcessorSetAndGroup;
1218 
1219     if (policy < WdfIrqPolicyMachineDefault ||
1220         policy > WdfIrqPolicySpreadMessagesAcrossAllProcessors) {
1221         DoTraceLevelMessage(
1222             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1223             "Policy %d is out of range", policy);
1224         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1225         return;
1226     }
1227 
1228     if (priority < WdfIrqPriorityLow || priority > WdfIrqPriorityHigh) {
1229         DoTraceLevelMessage(
1230             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1231             "Priority %d is out of range", priority);
1232         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1233         return;
1234     }
1235 
1236     if (processorSet->Reserved[0] != 0 || processorSet->Reserved[1] != 0 ||
1237         processorSet->Reserved[2] != 0) {
1238         DoTraceLevelMessage(
1239             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1240             "TargetProcessorSet's reserved fields are not zero");
1241         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1242         return;
1243     }
1244 
1245     pFxInterrupt->SetPolicy(policy, priority, processorSet);
1246 }
1247 
1248 _Must_inspect_result_
_IRQL_requires_max_(PASSIVE_LEVEL)1249 _IRQL_requires_max_(PASSIVE_LEVEL)
1250 _Post_satisfies_(return == 1 || return == 0)
1251 BOOLEAN
1252 STDCALL
1253 WDFEXPORT(WdfInterruptTryToAcquireLock)(
1254     __in
1255     PWDF_DRIVER_GLOBALS DriverGlobals,
1256     __in
1257     _Requires_lock_not_held_(_Curr_)
1258     _When_(return!=0, _Acquires_lock_(_Curr_))
1259     WDFINTERRUPT Interrupt
1260     )
1261 
1262 /*++
1263 
1264 Routine Description:
1265 
1266     Try to acquire the interrupt's passive-lock.
1267 
1268 Arguments:
1269 
1270     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1271 
1272 Returns:
1273 
1274     TRUE:  function was able to successfully acquire the lock.
1275 
1276     FALSE: function was unable to acquire the lock.
1277 
1278 --*/
1279 
1280 {
1281     DDI_ENTRY();
1282 
1283     FxInterrupt*    pFxInterrupt;
1284     NTSTATUS        status;
1285 
1286     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
1287                          Interrupt,
1288                          FX_TYPE_INTERRUPT,
1289                          (PVOID*)&pFxInterrupt);
1290 
1291     if (pFxInterrupt->GetDriverGlobals()->FxVerifierOn) {
1292         if (pFxInterrupt->IsPassiveHandling() == FALSE) {
1293             DoTraceLevelMessage(
1294                 pFxInterrupt->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1295                 "WDFINTERRUPT %p is handled at DIRQL. This API is not "
1296                 "available for DIQRL interrupt handling.",
1297                 Interrupt);
1298             FxVerifierDbgBreakPoint(pFxInterrupt->GetDriverGlobals());
1299             return FALSE;
1300         }
1301 
1302         status = FxVerifierCheckIrqlLevel(pFxInterrupt->GetDriverGlobals(),
1303                                           PASSIVE_LEVEL);
1304         if (!NT_SUCCESS(status)) {
1305             return FALSE;
1306         }
1307     }
1308 
1309     return pFxInterrupt->TryToAcquireLock();
1310 }
1311 
__drv_maxIRQL(DISPATCH_LEVEL)1312 __drv_maxIRQL(DISPATCH_LEVEL)
1313 VOID
1314 STDCALL
1315 WDFEXPORT(WdfInterruptReportActive)(
1316     _In_
1317     PWDF_DRIVER_GLOBALS DriverGlobals,
1318     _In_
1319     WDFINTERRUPT Interrupt
1320     )
1321 
1322 /*++
1323 
1324 Routine Description:
1325 
1326     This DDI informs the system that the interrupt is active and expecting
1327     interrupt requests on the associated lines. This is typically called after
1328     having reported the lines as inactive via WdfInterruptReportInactive.
1329     The DDI doesn't do anything if this DDI is called before
1330     WdfInterruptReportInactive while the interrupt is connected.
1331 
1332 Arguments:
1333 
1334     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1335 
1336 Returns:
1337 
1338     VOID
1339 
1340 --*/
1341 {
1342     DDI_ENTRY();
1343 
1344     PFX_DRIVER_GLOBALS  pFxDriverGlobals = NULL;
1345     FxInterrupt*        pFxInterrupt     = NULL;
1346 
1347     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1348                                    Interrupt,
1349                                    FX_TYPE_INTERRUPT,
1350                                    (PVOID*)&pFxInterrupt,
1351                                    &pFxDriverGlobals);
1352 
1353     pFxInterrupt->ReportActive();
1354 
1355     return;
1356 }
1357 
__drv_maxIRQL(DISPATCH_LEVEL)1358 __drv_maxIRQL(DISPATCH_LEVEL)
1359 VOID
1360 STDCALL
1361 WDFEXPORT(WdfInterruptReportInactive)(
1362     _In_
1363     PWDF_DRIVER_GLOBALS DriverGlobals,
1364     _In_
1365     WDFINTERRUPT Interrupt
1366     )
1367 
1368 /*++
1369 
1370 Routine Description:
1371 
1372     This DDI informs the system that the the interrupt is no longer active
1373     and is not expecting interrupt requests on the associated lines.
1374 
1375 Arguments:
1376 
1377     Interrupt - Handle to WDFINTERUPT object created with WdfInterruptCreate.
1378 
1379 Returns:
1380 
1381     VOID
1382 
1383 --*/
1384 {
1385     DDI_ENTRY();
1386 
1387     PFX_DRIVER_GLOBALS  pFxDriverGlobals = NULL;
1388     FxInterrupt*        pFxInterrupt     = NULL;
1389 
1390     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1391                                    Interrupt,
1392                                    FX_TYPE_INTERRUPT,
1393                                    (PVOID*)&pFxInterrupt,
1394                                    &pFxDriverGlobals);
1395 
1396     pFxInterrupt->ReportInactive();
1397 
1398     return;
1399 }
1400 
1401 } // extern "C"
1402