1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxInterrupt.hpp
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 #ifndef _FXINTERRUPT_H_
28 #define _FXINTERRUPT_H_
29 
30 #include "fxwakeinterruptstatemachine.hpp"
31 
32 //
33 // We need two parameters for KeSynchronizeExecution when enabling
34 // and disabling interrupts, so we use this structure on the stack since its
35 // a synchronous call.
36 //
37 struct FxInterruptEnableParameters {
38     FxInterrupt*        Interrupt;
39     NTSTATUS            ReturnVal;
40 };
41 
42 typedef FxInterruptEnableParameters FxInterruptDisableParameters;
43 
44 
45 class FxInterrupt : public FxNonPagedObject {
46 
47     friend FxPkgPnp;
48 
49 private:
50 
51     //
52     // User supplied configuration
53     //
54     WDF_TRI_STATE                   m_ShareVector;
55 
56     //
57     // Kernel Interupt object
58     //
59     struct _KINTERRUPT*             m_Interrupt;
60 
61     //
62     // Kernel spinlock for Interrupt
63     //
64     MdLock*                         m_SpinLock;
65 
66     KIRQL                           m_OldIrql;
67     volatile KIRQL                  m_SynchronizeIrql;
68 
69     //
70     // Built in SpinLock/PassiveLock
71     //
72     MxLock                          m_BuiltInSpinLock;
73 
74     //
75     // Passive-level interrupt handling.
76     //
77     FxWaitLock*                     m_WaitLock;
78 
79     //
80     // DpcForIsr and WorkItemForIsr support. Note that a DPC is still
81     // needed even if the driver opts to use WorkItemForIsr when
82     // driver handles interrupts at DIRQL.
83     //
84 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
85     KDPC                            m_Dpc;
86 #endif
87     FxSystemWorkItem*               m_SystemWorkItem;
88 
89     //
90     // Automatic serialization: this is the callback lock for the object the DPC or
91     //       work-item will synchronize with.
92     //
93     FxCallbackLock*                 m_CallbackLock;
94 
95     //
96     // Set to TRUE when WDF is responsible for disposing the wait-lock.
97     //
98     BOOLEAN                         m_DisposeWaitLock;
99 
100     //
101     // Value provided by driver. When TRUE we use IoReportActive/Inactive to
102     // do soft connect/disconnect on explicit power transitions.
103     //
104     BOOLEAN                         m_UseSoftDisconnect;
105 
106     //
107     // Set to TRUE for passive-level interrupt handling.
108     //
109     BOOLEAN                         m_PassiveHandling;
110 
111     // set to TRUE once the interrupt has been added to the pnp package's
112     // interrupt list
113     BOOLEAN                         m_AddedToList;
114 
115     //
116     // Indicates whether the driver has forced a disconnect.  If so, then
117     // we should stop automatically managing the connected state.
118     //
119     BOOLEAN                         m_Connected;
120     BOOLEAN                         m_ForceDisconnected;
121 
122     //
123     // Indicates whether the m_EvtInterruptPostEnable succeeded or not.
124     //
125     BOOLEAN                         m_Enabled;
126 
127     //
128     // Save floating point when the ISR runs
129     //
130     BOOLEAN                         m_FloatingSave;
131 
132     //
133     // Set to TRUE if interrupt is created in the prepare hardware callback.
134     //
135     BOOLEAN                         m_CreatedInPrepareHardware;
136 
137     //
138     // State machine to manage a wake capable interrupt
139     //
140     FxWakeInterruptMachine*         m_WakeInterruptMachine;
141 
142 
143 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
144     //
145     // Set to true on successful connect or when driver reports active.
146     // (this field is mainly for aid in debugging)
147     //
148     BOOLEAN                         m_Active;
149 #endif
150 
151     //
152     // Interrupt policy
153     //
154     BOOLEAN                         m_SetPolicy;
155     WDF_INTERRUPT_POLICY            m_Policy;
156     WDF_INTERRUPT_PRIORITY          m_Priority;
157     GROUP_AFFINITY                  m_Processors;
158 
159     //
160     // Callbacks
161     //
162     PFN_WDF_INTERRUPT_ENABLE        m_EvtInterruptEnable;
163     PFN_WDF_INTERRUPT_DISABLE       m_EvtInterruptDisable;
164 
165     PFN_WDF_INTERRUPT_ISR           m_EvtInterruptIsr;
166     PFN_WDF_INTERRUPT_DPC           m_EvtInterruptDpc;
167     PFN_WDF_INTERRUPT_WORKITEM      m_EvtInterruptWorkItem;
168 
169 #if ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
170     //
171     // Rd interrupt object
172     //
173     RD_INTERRUPT_CONTEXT            m_RdInterruptContext;
174 
175     //
176     // Each interrupt object has this structure which comprises an event and a
177     // wait structure. The wait struture  is associted with interrupt's callback
178     // and the event, and is queued to threadpool. The callback is invoked when
179     // the event is set.
180     //
181     FxInterruptWaitblock* m_InterruptWaitblock;
182 
183     //
184     // True if the interrupt callback can queue another interrupt wait.
185     // Set to true when interrupt is connected and false when interrupts
186     // callbacks and waits are flushed.
187     //
188     BOOLEAN m_CanQueue;
189 
190     //
191     // UMDF's handling of interrupt is split in two parts:
192     // 1. framwork code- runs at passive always and therefore uses mode-agnostic
193     //    code meant for passive-level handling, tracked through m_PassiveLevel
194     //    field of interrupt object.
195     // 2. redirector code- does passive handling of all of level-triggered
196     //    interrupt and DIRQL handing of all others (edge and msi). Driver
197     //    doesn't have any choice in that. The PassiveHandling field in the
198     //    interrupt config is always set for passive for UMDF (through UMDF's
199     //    init function).
200     //
201     // This field stores the type of handling done by redirector as opposed to
202     // m_PassiveHandling which stores user's choice.
203     //
204     BOOLEAN m_PassiveHandlingByRedirector;
205 #endif
206 
207     //
208     // PnP data about the interrupt.
209     //
210     WDF_INTERRUPT_INFO              m_InterruptInfo;
211 
212     //
213     // Weak ref to the translated resource interrupt descriptor.
214     // It is valid from prepare hardware callback to release hardware callback.
215     //
216     PCM_PARTIAL_RESOURCE_DESCRIPTOR  m_CmTranslatedResource;
217 
218 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
219     //
220     // Callback used to set m_Disconnecting, synchronized to running ISRs.
221     // Only runs if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
222     //
223     static
224     MdInterruptSynchronizeRoutineType _InterruptMarkDisconnecting;
225 
226     //
227     // Backup KINTERRUPT pointer, captured from the KMDF ISR thunk. We need it
228     // because valid interrupts may arrive before IoConnectInterruptEx sets
229     // FxInterrupt.m_Interrupt. Non-NULL only if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
230     //
231     struct _KINTERRUPT* m_InterruptCaptured;
232 #endif
233 
234     //
235     // Used to mark the interrupt disconnect window, and to discard interrupts
236     // that arrive within this window. Only set if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
237     //
238     BOOLEAN m_Disconnecting;
239 
240     //
241     // Set if this is an Edge-Triggered non-MSI interrupt. These interrupts are
242     // stateful and it is important not to drop any around the connection window.
243     //
244     BOOLEAN m_IsEdgeTriggeredNonMsiInterrupt;
245 
246 protected:
247 
248     LIST_ENTRY  m_PnpList;
249 
250 public:
251     FxInterrupt(
252         __in PFX_DRIVER_GLOBALS FxDriverGlobals
253         );
254 
255     virtual
256     ~FxInterrupt(
257         VOID
258         );
259 
260     _Must_inspect_result_
261     static
262     NTSTATUS
263     _CreateAndInit(
264         __in PFX_DRIVER_GLOBALS FxDriverGlobals,
265         __in CfxDevice * Device,
266         __in_opt FxObject * Parent,
267         __in PWDF_OBJECT_ATTRIBUTES Attributes,
268         __in PWDF_INTERRUPT_CONFIG Configuration,
269         __out FxInterrupt ** Interrupt
270         );
271 
272     _Must_inspect_result_
273     NTSTATUS
274     CreateWakeInterruptMachine(
275         VOID
276         );
277 
278     _Must_inspect_result_
279     NTSTATUS
280     Initialize(
281         __in CfxDevice* Device,
282         __in FxObject*  Parent,
283         __in PWDF_INTERRUPT_CONFIG Configuration
284         );
285 
286     _Must_inspect_result_
287     NTSTATUS
288     InitializeWorker(
289         __in FxObject*  Parent,
290         __in PWDF_INTERRUPT_CONFIG Configuration
291         );
292 
293     _Must_inspect_result_
294     NTSTATUS
295     InitializeInternal(
296         __in FxObject*  Parent,
297         __in PWDF_INTERRUPT_CONFIG Configuration
298         );
299 
300     virtual
301     BOOLEAN
302     Dispose(
303         VOID
304         );
305 
306     virtual
307     VOID
308     DeleteObject(
309         VOID
310         );
311 
312     VOID
313     OnPostReleaseHardware(
314         VOID
315         );
316 
317     VOID
318     DpcHandler(
319         __in_opt PVOID SystemArgument1,
320         __in_opt PVOID SystemArgument2
321         );
322 
323     BOOLEAN
324     QueueDpcForIsr(
325         VOID
326         );
327 
328     BOOLEAN
329     Synchronize(
330         __in  PFN_WDF_INTERRUPT_SYNCHRONIZE Callback,
331         __in  WDFCONTEXT                    Context
332         );
333 
334     struct _KINTERRUPT*
335     GetInterruptPtr(
336         VOID
337         );
338 
339     __inline
340     BOOLEAN
341     IsWakeCapable(
342         VOID
343         )
344     {
345         return ((m_WakeInterruptMachine != NULL) ? TRUE:FALSE);
346     }
347 
348     VOID
349     SetActiveForWake(
350         __in BOOLEAN ActiveForWake
351         )
352     {
353         m_WakeInterruptMachine->m_ActiveForWake = ActiveForWake;
354     }
355 
356     BOOLEAN
357     IsActiveForWake(
358         VOID
359         )
360     {
361         if ((m_WakeInterruptMachine != NULL) &&
362             (m_WakeInterruptMachine->m_ActiveForWake)) {
363             return TRUE;
364         } else {
365             return FALSE;
366         }
367     }
368 
369     VOID
370     ProcessWakeInterruptEvent(
371         __in FxWakeInterruptEvents Event
372         )
373     {
374         m_WakeInterruptMachine->ProcessEvent(Event);
375     }
376 
377 
378 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
379 
380     VOID
381     ReportActive(
382         _In_ BOOLEAN Internal = FALSE
383         );
384 
385     VOID
386     ReportInactive(
387         _In_ BOOLEAN Internal = FALSE
388         );
389 
390     BOOLEAN
391     IsSoftDisconnectCapable(
392         VOID
393         )
394     {
395         if (m_UseSoftDisconnect &&
396             FxLibraryGlobals.IoReportInterruptInactive != NULL &&
397             m_Interrupt != NULL &&
398             m_Connected) {
399             return TRUE;
400         }
401         else {
402             return FALSE;
403         }
404     }
405 
406 #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
407 
408     BOOLEAN
409     IsSoftDisconnectCapable(
410         VOID
411         )
412     {
413         //
414         // Not implemented for UMDF
415         //
416         return FALSE;
417     }
418 
419     VOID
420     ReportActive(
421         _In_ BOOLEAN Internal = FALSE
422         )
423     {
424         UNREFERENCED_PARAMETER(Internal);
425         //
426         // Not implemented for UMDF
427         //
428     }
429 
430     VOID
431     ReportInactive(
432         _In_ BOOLEAN Internal = FALSE
433         )
434     {
435         UNREFERENCED_PARAMETER(Internal);
436         //
437         // Not implemented for UMDF
438         //
439     }
440 
441 #endif
442 
443     VOID
444     WorkItemHandler(
445         VOID
446         );
447 
448     BOOLEAN
449     QueueWorkItemForIsr(
450         VOID
451         );
452 
453     __inline
454     BOOLEAN
455     IsPassiveHandling(
456         VOID
457         )
458     {
459         return m_PassiveHandling;
460     }
461 
462     __inline
463     BOOLEAN
464     IsPassiveConnect(
465         VOID
466         )
467     {
468         //
469         // UMDF's handling of interrupt is split in two parts:
470         // 1. framework code that runs at passive always in host process and
471         //    therefore uses mode-agnostic code meant for passive-level handling,
472         //    tracked through m_PassiveHandling member.
473         //    field of interrupt object.
474         // 2. redirector code that does passive handling of all of level-triggered
475         //    interrupt and DIRQL handing of all others (edge and msi). Driver
476         //    doesn't have any choice in that. The m_PassiveHandling field in the
477         //    interrupt config is always set for passive for UMDF (through UMDF's
478         //    init function). m_PasiveHandlingByRedirector member is present to
479         //    this part of code.
480         // In summary, m_PassiveHandling and m_PasiveHandlingByRedirector
481         // effectively maintain how the interrupt is connected (passive or DIRQL),
482         // for KMDF and UMDF respectively. This routine tells how the
483         // interrupt is connnected by looking at these members.
484         //
485 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
486         return IsPassiveHandling();
487 #else
488         return m_PassiveHandlingByRedirector;
489 #endif
490     }
491 
492     __inline
493     BOOLEAN
494     IsAutomaticSerialization(
495         VOID
496         )
497     {
498         return m_CallbackLock != NULL ? TRUE : FALSE;
499     }
500 
501     VOID
502     AcquireLock(
503         VOID
504         );
505 
506     BOOLEAN
507     TryToAcquireLock(
508         VOID
509         );
510 
511     VOID
512     ReleaseLock(
513         VOID
514         );
515 
516     CfxDevice*
517     GetDevice(
518         VOID
519         )
520     {
521         return m_Device;
522     }
523 
524     PWDF_INTERRUPT_INFO
525     GetInfo(
526         VOID
527         );
528 
529     WDFINTERRUPT
530     GetHandle(
531         VOID
532         )
533     {
534         return (WDFINTERRUPT) GetObjectHandle();
535     }
536 
537     BOOLEAN
538     IsSharedSpinLock(
539         VOID
540         )
541     {
542         return m_SpinLock != &m_BuiltInSpinLock.Get() ? TRUE : FALSE;
543     }
544 
545     BOOLEAN
546     IsSyncIrqlSet(
547         VOID
548         )
549     {
550         return m_SynchronizeIrql != PASSIVE_LEVEL ? TRUE : FALSE;
551     }
552 
553     KIRQL
554     GetSyncIrql(
555         VOID
556         )
557     {
558         return m_SynchronizeIrql;
559     }
560 
561     KIRQL
562     GetResourceIrql(
563         VOID
564         )
565     {
566         return m_InterruptInfo.Irql;
567     }
568 
569     BOOLEAN
570     SharesLock(
571         FxInterrupt* Interrupt
572         )
573     {
574         return m_SpinLock == Interrupt->m_SpinLock ? TRUE : FALSE;
575     }
576 
577 private:
578     VOID
579     Reset(
580         VOID
581         );
582 
583     VOID
584     ResetInternal(
585         VOID
586         );
587 
588     VOID
589     SetSyncIrql(
590         KIRQL SyncIrql
591         )
592     {
593         m_SynchronizeIrql = SyncIrql;
594     }
595 
596     //
597     // Called from workitem to perform final flushing of any
598     // outstanding DPC's and dereferencing of objects.
599     //
600     VOID
601     FlushAndRundown(
602         VOID
603         );
604 
605     VOID
606     FlushAndRundownInternal(
607         VOID
608         );
609 
610     static
611     MdInterruptServiceRoutineType _InterruptThunk;
612 
613     static
614     EVT_SYSTEMWORKITEM _InterruptWorkItemCallback;
615 
616     static
617     MdInterruptSynchronizeRoutineType _InterruptSynchronizeThunk;
618 
619 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
620 
621     static
622     MdDeferredRoutineType _InterruptDpcThunk;
623 
624 #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
625 
626     static
627     MX_WORKITEM_ROUTINE _InterruptWorkItemThunk;
628 
629     VOID
630     ThreadpoolWaitCallback(
631         VOID
632         );
633 
634     VOID
635     QueueSingleWaitOnInterruptEvent(
636         VOID
637         );
638 
639     VOID
640     StartThreadpoolWaitQueue(
641         VOID
642         );
643 
644     VOID
645     StopAndFlushThreadpoolWaitQueue(
646         VOID
647         );
648 
649 #endif
650 
651     //
652     // Helper functions to enable an interrupt.
653     // Sequence:
654     //  (1) InterruptEnable
655     //  (2) _InterruptEnableThunk
656     //  (3) InterruptEnableInvokeCallback
657     //
658     NTSTATUS
659     InterruptEnable(
660         VOID
661         );
662 
663     static
664     MdInterruptSynchronizeRoutineType _InterruptEnableThunk;
665 
666 
667     NTSTATUS
668     InterruptEnableInvokeCallback(
669         VOID
670         );
671 
672     //
673     // Helper functions to disable an interrupt.
674     // Sequence:
675     //  (1) InterruptDisable
676     //  (2) _InterruptDisableThunk
677     //  (3) InterruptDisableInvokeCallback
678     //
679     NTSTATUS
680     InterruptDisable(
681         VOID
682         );
683 
684     static
685     MdInterruptSynchronizeRoutineType _InterruptDisableThunk;
686 
687 
688     NTSTATUS
689     InterruptDisableInvokeCallback(
690         VOID
691         );
692 public:
693     static
694     BOOLEAN
695     _IsMessageInterrupt(
696         __in USHORT ResourceFlags
697         )
698     {
699         if (ResourceFlags & CM_RESOURCE_INTERRUPT_MESSAGE) {
700             return TRUE;
701         }
702         else {
703             return FALSE;
704         }
705     }
706 
707     static
708     BOOLEAN
709     _IsWakeHintedInterrupt(
710         __in USHORT ResourceFlags
711         )
712     {
713         if (ResourceFlags & CM_RESOURCE_INTERRUPT_WAKE_HINT) {
714             return TRUE;
715         }
716         else {
717             return FALSE;
718         }
719     }
720 
721     _Must_inspect_result_
722     NTSTATUS
723     Connect(
724         __in ULONG NotifyFlags
725         );
726 
727     _Must_inspect_result_
728     NTSTATUS
729     ConnectInternal(
730         VOID
731         );
732 
733     _Must_inspect_result_
734     NTSTATUS
735     Disconnect(
736         __in ULONG NotifyFlags
737         );
738 
739     VOID
740     DisconnectInternal(
741         VOID
742         );
743 
744     _Must_inspect_result_
745     NTSTATUS
746     ForceDisconnect(
747         VOID
748         );
749 
750     _Must_inspect_result_
751     NTSTATUS
752     ForceReconnect(
753         VOID
754         );
755 
756     VOID
757     FilterResourceRequirements(
758         __inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor
759         );
760 
761     VOID
762     AssignResources(
763         __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,
764         __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans
765         );
766 
767     PCM_PARTIAL_RESOURCE_DESCRIPTOR
768     GetResources(
769         VOID
770         )
771     {
772         // Weak ref to the translated resource interrupt descriptor.
773         // It is valid from prepare hardware callback to release hardware callback.
774         return m_CmTranslatedResource;
775     }
776 
777     VOID
778     AssignResourcesInternal(
779         __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,
780         __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans,
781         __in PWDF_INTERRUPT_INFO InterruptConfig
782         );
783 
784     VOID
785     RevokeResources(
786         VOID
787         );
788 
789     VOID
790     RevokeResourcesInternal(
791         VOID
792         );
793 
794     VOID
795     SetPolicy(
796         __in WDF_INTERRUPT_POLICY   Policy,
797         __in WDF_INTERRUPT_PRIORITY Priority,
798         __in PGROUP_AFFINITY        TargetProcessorSet
799         );
800 
801     VOID
802     SetPolicyInternal(
803         __in WDF_INTERRUPT_POLICY   Policy,
804         __in WDF_INTERRUPT_PRIORITY Priority,
805         __in PGROUP_AFFINITY        TargetProcessorSet
806         );
807 
808     VOID
809     FlushQueuedDpcs(
810         VOID
811         );
812 
813     VOID
814     FlushQueuedWorkitem(
815         VOID
816         );
817 
818     VOID
819     InvokeWakeInterruptEvtIsr(
820         VOID
821         );
822 
823     BOOLEAN
824     WakeInterruptIsr(
825         VOID
826         );
827 
828     BOOLEAN
829     IsLevelTriggered(
830         __in ULONG Flags
831         )
832     {
833         return ((Flags & CM_RESOURCE_INTERRUPT_LEVEL_LATCHED_BITS)
834             == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE);
835     }
836 
837     __inline
838     BOOLEAN
839     QueueDeferredRoutineForIsr(
840         VOID
841         )
842     {
843     //
844     // Queue DPC for KMDF and workitem for UMDF. Note that driver can either
845     // specify EvtInterruptDpc or EvtInterruptWorkItem, and therefore it can
846     // either call WdfInterruptQueueDpcForisr or WdfInterruptQueueWorkitemForIsr.
847     //
848 
849 
850 
851 
852     //
853 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
854         return QueueDpcForIsr();
855 #else
856         return QueueWorkItemForIsr();
857 #endif
858      }
859 
860 };
861 
862 BOOLEAN
863 _SynchronizeExecution(
864     __in MdInterrupt  Interrupt,
865     __in MdInterruptSynchronizeRoutine  SynchronizeRoutine,
866     __in PVOID  SynchronizeContext
867     );
868 
869 #endif // _FXINTERRUPT_H_
870