处理同时处于活动状态的中断
注意 本主题仅适用于 Kernel-Mode Driver Framework (KMDF) 版本 1.13 及更早版本。
许多设备都有控制中断生成和屏蔽的硬件寄存器。 通常,此类设备的 KMDF 和 UMDF 驱动程序使用框架的内置中断支持。
但是,芯片上的系统上的简单设备 (SoC) 硬件平台可能没有用于中断的硬件寄存器。 因此,此类设备的驱动程序可能无法控制何时生成中断,也无法在硬件中屏蔽中断。 如果设备在连接后立即中断,并且驱动程序正在使用框架的中断支持,则有可能在框架完全初始化框架中断对象之前触发中断。 因此,KMDF 驱动程序必须直接调用 WDM 例程才能连接和断开中断。 由于 UMDF 驱动程序无法调用这些方法,因此不能为此类设备编写 UMDF 驱动程序。
本主题介绍 KMDF 驱动程序如何处理此类设备的中断。
在 SoC 硬件平台上,主动两个中断通常用于非常简单的设备,如硬件按钮。 当用户按下一键时,设备的中断信号线将从低到高或从高转换为低。 当用户松开一键时,中断线会向相反的方向转换。 配置为双活动中断输入的 GPIO 引脚在从低到高和高到低转换时生成中断,导致系统在这两种情况下调用外围设备驱动程序的中断服务例程 (ISR) 。 但是,驱动程序不会收到转换是低到高还是高到低转换的指示。
若要区分从低到高以及从高到低的转换,驱动程序必须跟踪每个中断的状态。 为此,驱动程序可能会维护布尔中断状态值,当中断行状态较低时为 FALSE ,当行状态较高时为 TRUE 。
假设系统启动时行状态默认为低。 驱动程序在其 EvtDevicePrepareHardware 回调函数中将状态值初始化为 FALSE。 然后,每次调用驱动程序的 ISR(指示状态发生更改)时,驱动程序都会反转其 ISR 中的状态值。
如果系统启动时线路状态较高,则启用中断后会立即触发。 由于驱动程序直接调用 IoConnectInterruptEx 例程,而不是调用 WdfInterruptCreate,因此可确保它收到可能的即时中断。
此解决方案要求 GPIO 控制器支持硬件中的双主动中断,或者 GPIO 控制器的驱动程序在软件中模拟双主动中断。 有关模拟两个主动中断的信息,请参阅 CONTROLLER_ATTRIBUTE_FLAGS 结构的 EmulateActiveBoth 成员的说明。
下面的代码示例演示外围设备的 KMDF 驱动程序如何跟踪中断极性。
typedef struct _INTERRUPT_CONTEXT INTERRUPT_CONTEXT, *PINTERRUPT_CONTEXT;
typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;
struct _INTERRUPT_CONTEXT
{
BOOLEAN State;
PDEVICE_CONTEXT DeviceContext;
};
struct _DEVICE_CONTEXT
{
PKINTERRUPT Interrupt;
INTERRUPT_CONTEXT InterruptContext;
PDEVICE_OBJECT PhysicalDeviceObject;
KSPIN_LOCK SpinLock;
};
...
BOOLEAN
YourInterruptIsr(
__in PKINTERRUPT Interrupt,
__in PVOID ServiceContext
)
{
PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ServiceContext;
PDEVICE_CONTEXT DeviceContext = InterruptContext->DeviceContext;
//
// Flip the state.
//
InterruptContext->State = !InterruptContext->State;
IoRequestDpc(DeviceContext->PhysicalDeviceObject, DeviceContext->PhysicalDeviceObject->CurrentIrp, InterruptContext);
}
VOID
YourInterruptDpc(
__in PKDPC Dpc,
__in PDEVICE_OBJECT DeviceObject,
__inout PIRP Irp,
__in_opt PVOID ContextPointer
)
{
PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ContextPointer;
...
}
NTSTATUS
EvtDriverDeviceAdd(
__in WDFDRIVER Driver,
__in PWDFDEVICE_INIT DeviceInit
)
{
WDFDEVICE Device;
PDEVICE_CONTEXT DeviceContext;
...
DeviceContext->Interrupt = NULL;
DeviceContext->PhysicalDeviceObject = WdfDeviceWdmGetPhysicalDevice(Device);
KeInitializeSpinLock(&DeviceContext->SpinLock);
IoInitializeDpcRequest(DeviceContext->PhysicalDeviceObject, YourInterruptDpc);
}
NTSTATUS
EvtDevicePrepareHardware(
__in WDFDEVICE Device,
__in WDFCMRESLIST ResourcesRaw,
__in WDFCMRESLIST ResourcesTranslated
)
{
PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);
for (ULONG i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);
if (descriptor->Type == CmResourceTypeInterrupt)
{
IO_CONNECT_INTERRUPT_PARAMETERS params;
RtlZeroMemory(¶ms, sizeof(params));
params.Version = CONNECT_FULLY_SPECIFIED;
params.FullySpecified.PhysicalDeviceObject = DeviceContext->PhysicalDeviceObject;
params.FullySpecified.InterruptObject = &DeviceContext->Interrupt;
params.FullySpecified.ServiceRoutine = YourInterruptIsr;
params.FullySpecified.ServiceContext = (PVOID)&DeviceContext->InterruptContext;
params.FullySpecified.SpinLock = &DeviceContext->SpinLock;
params.FullySpecified.Vector = descriptor->u.Interrupt.Vector;
params.FullySpecified.Irql = (KIRQL)descriptor->u.Interrupt.Level;
params.FullySpecified.SynchronizeIrql = (KIRQL)descriptor->u.Interrupt.Level;
params.FullySpecified.InterruptMode = (descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
params.FullySpecified.ProcessorEnableMask = descriptor->u.Interrupt.Affinity;
params.FullySpecified.ShareVector = descriptor->ShareDisposition;
//
// Default state is low.
//
DeviceContext->InterruptContext.State = 0;
DeviceContext->InterruptContext.DeviceContext = DeviceContext;
return IoConnectInterruptEx(¶ms);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
EvtDeviceReleaseHardware(
__in WDFDEVICE Device,
__in WDFCMRESLIST ResourcesTranslated
)
{
PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);
if (NULL != DeviceContext->Interrupt)
{
IO_DISCONNECT_INTERRUPT_PARAMETERS params;
params.Version = CONNECT_FULLY_SPECIFIED;
params.ConnectionContext.InterruptObject = DeviceContext->Interrupt;
IoDisconnectInterruptEx(¶ms);
}
return STATUS_SUCCESS;
}
在前面的代码示例中,驱动程序的 EvtDriverDeviceAdd 回调函数配置设备上下文,然后调用 IoInitializeDpcRequest 来注册 DpcForIsr 例程。
驱动程序的 InterruptService 例程反转中断状态值,然后调用 IoRequestDpc 将 DPC 排队。
在其 EvtDevicePrepareHardware 回调函数中,驱动程序将状态值初始化为 FALSE ,然后调用 IoConnectInterruptEx。 在其 EvtDeviceReleaseHardware 回调函数中,驱动程序调用 IoDisconnectInterruptEx 以取消注册其 ISR。