reactos/sdk/lib/drivers/wdf/shared/inc/private/common/fxinterrupt.hpp

870 lines
18 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxInterrupt.hpp
Abstract:
This module implements a frameworks managed interrupt object
Author:
Environment:
Both kernel and user mode
Revision History:
--*/
#ifndef _FXINTERRUPT_H_
#define _FXINTERRUPT_H_
#include "fxwakeinterruptstatemachine.hpp"
//
// We need two parameters for KeSynchronizeExecution when enabling
// and disabling interrupts, so we use this structure on the stack since its
// a synchronous call.
//
struct FxInterruptEnableParameters {
FxInterrupt* Interrupt;
NTSTATUS ReturnVal;
};
typedef FxInterruptEnableParameters FxInterruptDisableParameters;
class FxInterrupt : public FxNonPagedObject {
friend FxPkgPnp;
private:
//
// User supplied configuration
//
WDF_TRI_STATE m_ShareVector;
//
// Kernel Interupt object
//
struct _KINTERRUPT* m_Interrupt;
//
// Kernel spinlock for Interrupt
//
MdLock* m_SpinLock;
KIRQL m_OldIrql;
volatile KIRQL m_SynchronizeIrql;
//
// Built in SpinLock/PassiveLock
//
MxLock m_BuiltInSpinLock;
//
// Passive-level interrupt handling.
//
FxWaitLock* m_WaitLock;
//
// DpcForIsr and WorkItemForIsr support. Note that a DPC is still
// needed even if the driver opts to use WorkItemForIsr when
// driver handles interrupts at DIRQL.
//
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
KDPC m_Dpc;
#endif
FxSystemWorkItem* m_SystemWorkItem;
//
// Automatic serialization: this is the callback lock for the object the DPC or
// work-item will synchronize with.
//
FxCallbackLock* m_CallbackLock;
//
// Set to TRUE when WDF is responsible for disposing the wait-lock.
//
BOOLEAN m_DisposeWaitLock;
//
// Value provided by driver. When TRUE we use IoReportActive/Inactive to
// do soft connect/disconnect on explicit power transitions.
//
BOOLEAN m_UseSoftDisconnect;
//
// Set to TRUE for passive-level interrupt handling.
//
BOOLEAN m_PassiveHandling;
// set to TRUE once the interrupt has been added to the pnp package's
// interrupt list
BOOLEAN m_AddedToList;
//
// Indicates whether the driver has forced a disconnect. If so, then
// we should stop automatically managing the connected state.
//
BOOLEAN m_Connected;
BOOLEAN m_ForceDisconnected;
//
// Indicates whether the m_EvtInterruptPostEnable succeeded or not.
//
BOOLEAN m_Enabled;
//
// Save floating point when the ISR runs
//
BOOLEAN m_FloatingSave;
//
// Set to TRUE if interrupt is created in the prepare hardware callback.
//
BOOLEAN m_CreatedInPrepareHardware;
//
// State machine to manage a wake capable interrupt
//
FxWakeInterruptMachine* m_WakeInterruptMachine;
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
//
// Set to true on successful connect or when driver reports active.
// (this field is mainly for aid in debugging)
//
BOOLEAN m_Active;
#endif
//
// Interrupt policy
//
BOOLEAN m_SetPolicy;
WDF_INTERRUPT_POLICY m_Policy;
WDF_INTERRUPT_PRIORITY m_Priority;
GROUP_AFFINITY m_Processors;
//
// Callbacks
//
PFN_WDF_INTERRUPT_ENABLE m_EvtInterruptEnable;
PFN_WDF_INTERRUPT_DISABLE m_EvtInterruptDisable;
PFN_WDF_INTERRUPT_ISR m_EvtInterruptIsr;
PFN_WDF_INTERRUPT_DPC m_EvtInterruptDpc;
PFN_WDF_INTERRUPT_WORKITEM m_EvtInterruptWorkItem;
#if ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
//
// Rd interrupt object
//
RD_INTERRUPT_CONTEXT m_RdInterruptContext;
//
// Each interrupt object has this structure which comprises an event and a
// wait structure. The wait struture is associted with interrupt's callback
// and the event, and is queued to threadpool. The callback is invoked when
// the event is set.
//
FxInterruptWaitblock* m_InterruptWaitblock;
//
// True if the interrupt callback can queue another interrupt wait.
// Set to true when interrupt is connected and false when interrupts
// callbacks and waits are flushed.
//
BOOLEAN m_CanQueue;
//
// UMDF's handling of interrupt is split in two parts:
// 1. framwork code- runs at passive always and therefore uses mode-agnostic
// code meant for passive-level handling, tracked through m_PassiveLevel
// field of interrupt object.
// 2. redirector code- does passive handling of all of level-triggered
// interrupt and DIRQL handing of all others (edge and msi). Driver
// doesn't have any choice in that. The PassiveHandling field in the
// interrupt config is always set for passive for UMDF (through UMDF's
// init function).
//
// This field stores the type of handling done by redirector as opposed to
// m_PassiveHandling which stores user's choice.
//
BOOLEAN m_PassiveHandlingByRedirector;
#endif
//
// PnP data about the interrupt.
//
WDF_INTERRUPT_INFO m_InterruptInfo;
//
// Weak ref to the translated resource interrupt descriptor.
// It is valid from prepare hardware callback to release hardware callback.
//
PCM_PARTIAL_RESOURCE_DESCRIPTOR m_CmTranslatedResource;
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
//
// Callback used to set m_Disconnecting, synchronized to running ISRs.
// Only runs if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
//
static
MdInterruptSynchronizeRoutineType _InterruptMarkDisconnecting;
//
// Backup KINTERRUPT pointer, captured from the KMDF ISR thunk. We need it
// because valid interrupts may arrive before IoConnectInterruptEx sets
// FxInterrupt.m_Interrupt. Non-NULL only if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
//
struct _KINTERRUPT* m_InterruptCaptured;
#endif
//
// Used to mark the interrupt disconnect window, and to discard interrupts
// that arrive within this window. Only set if m_IsEdgeTriggeredNonMsiInterrupt is TRUE.
//
BOOLEAN m_Disconnecting;
//
// Set if this is an Edge-Triggered non-MSI interrupt. These interrupts are
// stateful and it is important not to drop any around the connection window.
//
BOOLEAN m_IsEdgeTriggeredNonMsiInterrupt;
protected:
LIST_ENTRY m_PnpList;
public:
FxInterrupt(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
);
virtual
~FxInterrupt(
VOID
);
_Must_inspect_result_
static
NTSTATUS
_CreateAndInit(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in CfxDevice * Device,
__in_opt FxObject * Parent,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__in PWDF_INTERRUPT_CONFIG Configuration,
__out FxInterrupt ** Interrupt
);
_Must_inspect_result_
NTSTATUS
CreateWakeInterruptMachine(
VOID
);
_Must_inspect_result_
NTSTATUS
Initialize(
__in CfxDevice* Device,
__in FxObject* Parent,
__in PWDF_INTERRUPT_CONFIG Configuration
);
_Must_inspect_result_
NTSTATUS
InitializeWorker(
__in FxObject* Parent,
__in PWDF_INTERRUPT_CONFIG Configuration
);
_Must_inspect_result_
NTSTATUS
InitializeInternal(
__in FxObject* Parent,
__in PWDF_INTERRUPT_CONFIG Configuration
);
virtual
BOOLEAN
Dispose(
VOID
);
virtual
VOID
DeleteObject(
VOID
);
VOID
OnPostReleaseHardware(
VOID
);
VOID
DpcHandler(
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2
);
BOOLEAN
QueueDpcForIsr(
VOID
);
BOOLEAN
Synchronize(
__in PFN_WDF_INTERRUPT_SYNCHRONIZE Callback,
__in WDFCONTEXT Context
);
struct _KINTERRUPT*
GetInterruptPtr(
VOID
);
__inline
BOOLEAN
IsWakeCapable(
VOID
)
{
return ((m_WakeInterruptMachine != NULL) ? TRUE:FALSE);
}
VOID
SetActiveForWake(
__in BOOLEAN ActiveForWake
)
{
m_WakeInterruptMachine->m_ActiveForWake = ActiveForWake;
}
BOOLEAN
IsActiveForWake(
VOID
)
{
if ((m_WakeInterruptMachine != NULL) &&
(m_WakeInterruptMachine->m_ActiveForWake)) {
return TRUE;
} else {
return FALSE;
}
}
VOID
ProcessWakeInterruptEvent(
__in FxWakeInterruptEvents Event
)
{
m_WakeInterruptMachine->ProcessEvent(Event);
}
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
VOID
ReportActive(
_In_ BOOLEAN Internal = FALSE
);
VOID
ReportInactive(
_In_ BOOLEAN Internal = FALSE
);
BOOLEAN
IsSoftDisconnectCapable(
VOID
)
{
if (m_UseSoftDisconnect &&
FxLibraryGlobals.IoReportInterruptInactive != NULL &&
m_Interrupt != NULL &&
m_Connected) {
return TRUE;
}
else {
return FALSE;
}
}
#elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
BOOLEAN
IsSoftDisconnectCapable(
VOID
)
{
//
// Not implemented for UMDF
//
return FALSE;
}
VOID
ReportActive(
_In_ BOOLEAN Internal = FALSE
)
{
UNREFERENCED_PARAMETER(Internal);
//
// Not implemented for UMDF
//
}
VOID
ReportInactive(
_In_ BOOLEAN Internal = FALSE
)
{
UNREFERENCED_PARAMETER(Internal);
//
// Not implemented for UMDF
//
}
#endif
VOID
WorkItemHandler(
VOID
);
BOOLEAN
QueueWorkItemForIsr(
VOID
);
__inline
BOOLEAN
IsPassiveHandling(
VOID
)
{
return m_PassiveHandling;
}
__inline
BOOLEAN
IsPassiveConnect(
VOID
)
{
//
// UMDF's handling of interrupt is split in two parts:
// 1. framework code that runs at passive always in host process and
// therefore uses mode-agnostic code meant for passive-level handling,
// tracked through m_PassiveHandling member.
// field of interrupt object.
// 2. redirector code that does passive handling of all of level-triggered
// interrupt and DIRQL handing of all others (edge and msi). Driver
// doesn't have any choice in that. The m_PassiveHandling field in the
// interrupt config is always set for passive for UMDF (through UMDF's
// init function). m_PasiveHandlingByRedirector member is present to
// this part of code.
// In summary, m_PassiveHandling and m_PasiveHandlingByRedirector
// effectively maintain how the interrupt is connected (passive or DIRQL),
// for KMDF and UMDF respectively. This routine tells how the
// interrupt is connnected by looking at these members.
//
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return IsPassiveHandling();
#else
return m_PassiveHandlingByRedirector;
#endif
}
__inline
BOOLEAN
IsAutomaticSerialization(
VOID
)
{
return m_CallbackLock != NULL ? TRUE : FALSE;
}
VOID
AcquireLock(
VOID
);
BOOLEAN
TryToAcquireLock(
VOID
);
VOID
ReleaseLock(
VOID
);
CfxDevice*
GetDevice(
VOID
)
{
return m_Device;
}
PWDF_INTERRUPT_INFO
GetInfo(
VOID
);
WDFINTERRUPT
GetHandle(
VOID
)
{
return (WDFINTERRUPT) GetObjectHandle();
}
BOOLEAN
IsSharedSpinLock(
VOID
)
{
return m_SpinLock != &m_BuiltInSpinLock.Get() ? TRUE : FALSE;
}
BOOLEAN
IsSyncIrqlSet(
VOID
)
{
return m_SynchronizeIrql != PASSIVE_LEVEL ? TRUE : FALSE;
}
KIRQL
GetSyncIrql(
VOID
)
{
return m_SynchronizeIrql;
}
KIRQL
GetResourceIrql(
VOID
)
{
return m_InterruptInfo.Irql;
}
BOOLEAN
SharesLock(
FxInterrupt* Interrupt
)
{
return m_SpinLock == Interrupt->m_SpinLock ? TRUE : FALSE;
}
private:
VOID
Reset(
VOID
);
VOID
ResetInternal(
VOID
);
VOID
SetSyncIrql(
KIRQL SyncIrql
)
{
m_SynchronizeIrql = SyncIrql;
}
//
// Called from workitem to perform final flushing of any
// outstanding DPC's and dereferencing of objects.
//
VOID
FlushAndRundown(
VOID
);
VOID
FlushAndRundownInternal(
VOID
);
static
MdInterruptServiceRoutineType _InterruptThunk;
static
EVT_SYSTEMWORKITEM _InterruptWorkItemCallback;
static
MdInterruptSynchronizeRoutineType _InterruptSynchronizeThunk;
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
static
MdDeferredRoutineType _InterruptDpcThunk;
#elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
static
MX_WORKITEM_ROUTINE _InterruptWorkItemThunk;
VOID
ThreadpoolWaitCallback(
VOID
);
VOID
QueueSingleWaitOnInterruptEvent(
VOID
);
VOID
StartThreadpoolWaitQueue(
VOID
);
VOID
StopAndFlushThreadpoolWaitQueue(
VOID
);
#endif
//
// Helper functions to enable an interrupt.
// Sequence:
// (1) InterruptEnable
// (2) _InterruptEnableThunk
// (3) InterruptEnableInvokeCallback
//
NTSTATUS
InterruptEnable(
VOID
);
static
MdInterruptSynchronizeRoutineType _InterruptEnableThunk;
NTSTATUS
InterruptEnableInvokeCallback(
VOID
);
//
// Helper functions to disable an interrupt.
// Sequence:
// (1) InterruptDisable
// (2) _InterruptDisableThunk
// (3) InterruptDisableInvokeCallback
//
NTSTATUS
InterruptDisable(
VOID
);
static
MdInterruptSynchronizeRoutineType _InterruptDisableThunk;
NTSTATUS
InterruptDisableInvokeCallback(
VOID
);
public:
static
BOOLEAN
_IsMessageInterrupt(
__in USHORT ResourceFlags
)
{
if (ResourceFlags & CM_RESOURCE_INTERRUPT_MESSAGE) {
return TRUE;
}
else {
return FALSE;
}
}
static
BOOLEAN
_IsWakeHintedInterrupt(
__in USHORT ResourceFlags
)
{
if (ResourceFlags & CM_RESOURCE_INTERRUPT_WAKE_HINT) {
return TRUE;
}
else {
return FALSE;
}
}
_Must_inspect_result_
NTSTATUS
Connect(
__in ULONG NotifyFlags
);
_Must_inspect_result_
NTSTATUS
ConnectInternal(
VOID
);
_Must_inspect_result_
NTSTATUS
Disconnect(
__in ULONG NotifyFlags
);
VOID
DisconnectInternal(
VOID
);
_Must_inspect_result_
NTSTATUS
ForceDisconnect(
VOID
);
_Must_inspect_result_
NTSTATUS
ForceReconnect(
VOID
);
VOID
FilterResourceRequirements(
__inout PIO_RESOURCE_DESCRIPTOR IoResourceDescriptor
);
VOID
AssignResources(
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans
);
PCM_PARTIAL_RESOURCE_DESCRIPTOR
GetResources(
VOID
)
{
// Weak ref to the translated resource interrupt descriptor.
// It is valid from prepare hardware callback to release hardware callback.
return m_CmTranslatedResource;
}
VOID
AssignResourcesInternal(
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescRaw,
__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescTrans,
__in PWDF_INTERRUPT_INFO InterruptConfig
);
VOID
RevokeResources(
VOID
);
VOID
RevokeResourcesInternal(
VOID
);
VOID
SetPolicy(
__in WDF_INTERRUPT_POLICY Policy,
__in WDF_INTERRUPT_PRIORITY Priority,
__in PGROUP_AFFINITY TargetProcessorSet
);
VOID
SetPolicyInternal(
__in WDF_INTERRUPT_POLICY Policy,
__in WDF_INTERRUPT_PRIORITY Priority,
__in PGROUP_AFFINITY TargetProcessorSet
);
VOID
FlushQueuedDpcs(
VOID
);
VOID
FlushQueuedWorkitem(
VOID
);
VOID
InvokeWakeInterruptEvtIsr(
VOID
);
BOOLEAN
WakeInterruptIsr(
VOID
);
BOOLEAN
IsLevelTriggered(
__in ULONG Flags
)
{
return ((Flags & CM_RESOURCE_INTERRUPT_LEVEL_LATCHED_BITS)
== CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE);
}
__inline
BOOLEAN
QueueDeferredRoutineForIsr(
VOID
)
{
//
// Queue DPC for KMDF and workitem for UMDF. Note that driver can either
// specify EvtInterruptDpc or EvtInterruptWorkItem, and therefore it can
// either call WdfInterruptQueueDpcForisr or WdfInterruptQueueWorkitemForIsr.
//
//
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
return QueueDpcForIsr();
#else
return QueueWorkItemForIsr();
#endif
}
};
BOOLEAN
_SynchronizeExecution(
__in MdInterrupt Interrupt,
__in MdInterruptSynchronizeRoutine SynchronizeRoutine,
__in PVOID SynchronizeContext
);
#endif // _FXINTERRUPT_H_