WDF驱动开发-中断处理(三)

使用中断唤醒设备

当设备转换为低功耗状态时,框架会断开连接 (或报告为非活动) 用于 I/O 处理的中断。 从在 Windows 8.1 上运行的 KMDF 1.13 和 UMDF 2.0 开始,WDF 驱动程序可以创建一个框架中断对象,该对象在设备转换为低功耗状态时保持活动状态,然后可用于唤醒设备并将其还原到完全处于 D0 状态。

如果要为芯片上的系统开发 WDF 驱动程序 (SoC) 平台,则可以使用此类中断唤醒不提供传统唤醒信号机制的设备。 若要使用此功能,设备必须具有通过 ACPI 公开的唤醒中断的硬件支持。 创建中断的驱动程序必须是设备的电源策略所有者。

当设备转换为低功耗状态时,框架不会断开已标识为支持唤醒的中断。 当设备中断时,框架在 IRQL = PASSIVE_LEVEL调用驱动程序的 EvtDeviceD0Entry 和 EvtInterruptIsr 回调例程。

如果驱动程序已创建被动 级中断对象 用于 I/O 处理,我们建议共享同一个中断对象以用于唤醒功能。 在此方案中,驱动程序的 EvtInterruptIsr 回调例程实现条件逻辑以执行 I/O 相关中断的处理以及唤醒处理。

但是,如果驱动程序使用的中断需要在设备的 IRQL (DIRQL) 进行处理,我们建议创建额外的框架中断对象以提供唤醒功能。

按照以下步骤在 KMDF 或 UMDF 驱动程序中创建支持唤醒的中断对象:

1. 调用 WdfDeviceAssignS0IdleSettings,通常来自 EvtDriverDeviceAdd,并在 IdleCaps 参数中指定 IdleCanWakeFromS0;

2. (可选)调用 WdfDeviceInitSetPowerPolicyEventCallbacks 以注册 支持系统唤醒中所述的事件回调函数;

3. 调用 WDF_INTERRUPT_CONFIG_INIT 以初始化 WDF_INTERRUPT_CONFIG 结构。 提供在被动级别调用的 EvtInterruptIsr 回调函数。 在配置结构中,将 PassiveHandling 和 CanWakeDevice 设置为 TRUE。 然后从驱动程序的 EvtDevicePrepareHardware 回调函数调用 WdfInterruptCreate 以创建框架中断对象;

4. 调用 WdfDeviceAssignSxWakeSettings 将设备配置为将系统从低功耗状态唤醒;

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
wakeSettings.DxState = PowerDeviceD3;
wakeSettings.UserControlOfWakeSettings = WakeDoNotAllowUserControl;
wakeSettings.Enabled = WdfTrue;

status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
    Trace(TRACE_LEVEL_ERROR,"WdfDeviceAssignSxWakeSettings failed %x\n", status);
    return status;
}

5. 当设备转换为低功耗状态时,框架不会为支持唤醒的中断调用 EvtInterruptDisable 。 如果驱动程序提供了 EvtDeviceArmWakeFromS0 ,框架会调用该框架;

6. 当设备发出唤醒中断信号时,框架会调用驱动程序的 EvtDeviceD0Entry 回调例程;

7. 如果驱动程序的 EvtDeviceD0Entry 回调返回成功,框架将在被动级别调用驱动程序的 EvtInterruptIsr 回调。 在中断处理程序返回之前,它必须在中断控制器中静音中断。 如果驱动程序从 EvtDeviceD0Entry 返回失败代码,框架将断开中断,并调用驱动程序的 EvtInterruptDisable 回调,如果驱动程序已提供;

8. 如果驱动程序提供了以下唤醒事件回调例程,则框架会调用以下唤醒事件回调例程:

EvtDeviceDisarmWakeFromS0
EvtDeviceDisarmWakeFromSx
EvtDeviceWakeFromS0Triggered
EvtDeviceWakeFromSxTriggered
9. 框架继续执行正常的通电回调序列;

可以在windbg中使用 !wdfkd.wdfinterrupt 调试器扩展来显示特定中断是否已配置为支持唤醒。

注意: 唤醒中断功能不能与 USB 选择性挂起结合使用。

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

注意仅适用于 Kernel-Mode Driver Framework (KMDF) 版本 1.13 及更早版本。

许多设备都有控制中断生成和屏蔽的硬件寄存器。 通常,此类设备的 KMDF 和 UMDF 驱动程序使用框架的内置中断支持。

但是,芯片上的系统上的简单设备 (SoC) 硬件平台可能没有用于中断的硬件寄存器。 因此,此类设备的驱动程序可能无法控制何时生成中断,也无法在硬件中屏蔽中断。 如果设备在连接后立即中断,并且驱动程序正在使用框架的中断支持,则有可能在框架完全初始化框架中断对象之前触发中断。 因此,KMDF 驱动程序必须直接调用 WDM 例程才能连接和断开中断。 由于 UMDF 驱动程序无法调用这些方法,因此不能为此类设备编写 UMDF 驱动程序。

在 SoC 硬件平台上,主动两个中断通常用于非常简单的设备,如硬件按钮。 当用户按下一键时,设备的中断信号线将从低到高或从高转换为低。 当用户松开一键时,中断线会向相反的方向转换。 配置为双活动中断输入的 GPIO 引脚在从低到高和高到低转换时生成中断,导致系统在这两种情况下调用外围设备驱动程序的中断服务例程 ISR。 但是,驱动程序不会收到转换是低到高还是高到低转换的指示。

若要区分从低到高以及从高到低的转换,驱动程序必须跟踪每个中断的状态。 为此,驱动程序可能会维护布尔中断状态值,当中断行状态较低时为 FALSE ,当行状态较高时为 TRUE 。

假设系统启动时行状态默认为低。 驱动程序在其 EvtDevicePrepareHardware 回调函数中将状态值初始化为 FALSE。 然后,每次调用驱动程序的 ISR指示状态发生更改时,驱动程序都会反转其 ISR 中的状态值。

如果系统启动时线路状态较高,则启用中断后会立即触发。 由于驱动程序直接调用 IoConnectInterruptEx 例程,而不是调用 WdfInterruptCreate,因此可确保它收到可能的即时中断。

此解决方案要求 GPIO 控制器支持硬件中的双主动中断,或者 GPIO 控制器的驱动程序在软件中模拟双主动中断。  

下面的代码示例演示外围设备的 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。

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值