处理同时处于活动状态的中断

注意 本主题仅适用于 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(&params, 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(&params);
                              }
               }

               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(&params);
               }

               return STATUS_SUCCESS;
}

在前面的代码示例中,驱动程序的 EvtDriverDeviceAdd 回调函数配置设备上下文,然后调用 IoInitializeDpcRequest 来注册 DpcForIsr 例程。

驱动程序的 InterruptService 例程反转中断状态值,然后调用 IoRequestDpc 将 DPC 排队。

在其 EvtDevicePrepareHardware 回调函数中,驱动程序将状态值初始化为 FALSE ,然后调用 IoConnectInterruptEx。 在其 EvtDeviceReleaseHardware 回调函数中,驱动程序调用 IoDisconnectInterruptEx 以取消注册其 ISR。