USB UMDF 驱动程序中的选择性挂起

本主题介绍 UMDF 函数驱动程序如何支持 USB 选择性挂起。

重要的 API

UMDF 函数驱动程序可以通过以下两种方式之一支持 USB 选择性挂起:

  • 通过声明电源策略所有权并处理设备空闲关闭和恢复。
  • 依靠 Microsoft 提供的 WinUSB.sys 驱动程序来处理选择性挂起。 安装 UMDF USB 驱动程序期间,WinUSB.sys 作为内核模式设备堆栈的一部分进行安装。 WinUSB.sys 实现用于暂停和恢复 USB 设备操作的基础机制。

这两种方法只需要少量代码。 WDK 中提供的 IdleWake 示例演示如何在 UMDF USB 驱动程序中支持选择性挂起。 可以在 %WinDDK%\BuildNumber\Src\Usb\OsrUsbFx2\ UMDF\Fx2_Driver\IdleWake 中找到此示例。 文件夹包含示例的 PPO 和非 PPO 版本。

支持选择性挂起的 UMDF 驱动程序必须遵循以下准则:

  • UMDF 驱动程序可以声明其设备堆栈的电源策略所有权,但不需要这样做。 默认情况下,基础 WinUSB.sys 驱动程序拥有电源策略。
  • 支持选择性挂起且是 PPO 的 UMDF 驱动程序可以使用电源管理的队列或非电源管理的队列。 支持选择性挂起但不是 PPO 的 UMDF 驱动程序不得使用电源管理的队列。

UMDF USB 驱动程序中的电源策略所有权

默认情况下,WinUSB.sys 是包含 UMDF USB 驱动程序的设备堆栈的 PPO。 从 WDF 1.9 开始,基于 UMDF 的 USB 驱动程序可以声明电源策略所有权。 由于每个设备堆栈中只有一个驱动程序可以是 PPO,因此作为 PPO 的 UMDF USB 驱动程序必须在 WinUSB.sys 中显式禁用电源策略所有权。

在 UMDF USB 驱动程序中声明电源策略所有权

  1. 调用 IWDFDeviceInitialize::SetPowerPolicyOwnership 并传递 TRUE,通常是从驱动程序回调对象上的 IDriverEntry::OnDeviceAdd 方法传递。 例如:

    FxDeviceInit->SetPowerPolicyOwnership(TRUE);
    
  2. 在 WinUSB 中禁用电源策略所有权。 在驱动程序的 INF 文件中,包含 AddReg 指令,该指令将注册表中的 WinUsbPowerPolicyOwnershipDisabled 值设置为非零值。 AddReg 指令必须出现在 DDInstall.HW 节中。 例如:

    [MyDriver_Install.NT.hw]
    AddReg=MyDriver_AddReg
    
    [MyDriver_AddReg]
    HKR,,"WinUsbPowerPolicyOwnershipDisabled",0x00010001,1
    

支持选择性挂起并使用低于 1.9 的 WDF 版本生成的 UMDF USB 驱动程序不得声明电源策略所有权。 在这些早期版本的 WDF 中,仅当 WinUSB.sys 为 PPO 时,USB 选择性挂起才能正常工作。

UMDF USB 驱动程序中的 I/O 队列

对于支持选择性挂起的 UMDF 驱动程序,UMDF 驱动程序是否为其设备拥有电源策略决定了它可以使用的 I/O 队列的类型。 支持选择性挂起和 PDO 的 UMDF 驱动程序可以使用电源托管或非电源管理的队列。 支持选择性挂起但不是 PPO 的 UMDF USB 驱动程序不应使用任何电源管理的 I/O 队列。

如果在设备挂起时,电源管理队列的 I/O 请求到达,则框架不会显示该请求,除非驱动程序为 PPO,如 USB 驱动程序中的选择性挂起图像中所示。 如果 UMDF 驱动程序不是设备的 PPO,则框架无法代表设备启动。 因此,请求仍停滞在电源管理的队列中。 请求永远不会到达 WinUSB,因此 WinUSB 无法启动设备。 因此,设备堆栈可能会停止。

如果队列不受电源管理,则框架会向 UMDF 驱动程序提供 I/O 请求,即使设备关闭也是如此。 UMDF 驱动程序格式化请求,并按照通常的方式将请求在设备堆栈中向下转发到默认 I/O 目标。 不需要特殊代码。 当请求到达 PPO (WinUSB.sys) 时,WinUSB.sys 启动设备并执行所需的 I/O 操作。

%WinDDK%\BuildNumber\Src\Usb\OsrUsbFx2\umdf\Fx2_Driver\IdleWake 中的示例驱动程序定义在生成非 PPO 版本的驱动程序时_NOT_POWER_POLICY_OWNER_常量。 当驱动程序为读取和写入请求创建队列时,它会确定是否通过检查常量来创建电源管理的队列。

为了创建队列,驱动程序调用驱动程序定义的 CMyQueue::Initialize 方法,该方法采用以下三个参数:

  • DispatchType,一个WDF_IO_QUEUE_DISPATCH_TYPE枚举值,指示队列如何调度请求。
  • 默认值,指示队列是否为默认队列的布尔值。
  • PowerManaged,一个布尔值,指示队列是否受电源管理。

以下代码片段演示驱动程序在创建读写队列过程中对 CMyQueue::Initialize 方法的调用:

#if defined(_NOT_POWER_POLICY_OWNER_)
    powerManaged = false;
#else
    powerManaged = true;
#endif  
hr = __super::Initialize(WdfIoQueueDispatchParallel,
                         true,
                         powerManaged,
                         );

CMyQueue::Initialize 然后调用 IWDFDevice::CreateIoQueue 来创建队列,如下所示:

hr = m_FxDevice->CreateIoQueue(
                               callback,
                               Default,
                               DispatchType,
                               PowerManaged,
                               FALSE,
                               &fxQueue
                               );

此代码序列生成一个并行调度请求的默认队列。 如果驱动程序是 PPO,则队列受电源管理,如果驱动程序不是 PPO,则队列不受电源管理。

支持 UMDF PPO 中的 USB 选择性挂起

若要支持选择性挂起,作为其设备堆栈的 PPO 的 UMDF USB 驱动程序必须执行以下操作:

  1. 声明设备堆栈的电源策略所有权,通常在其驱动程序回调对象上的 IDriverEntry::OnDeviceAdd 方法中,如前所述。
  2. 通过在框架设备对象上调用 IWDFDevice2::AssignS0IdleSettings 方法来启用选择性挂起。

从 PPO 启用 USB 选择性挂起

  • 通常从设备回调对象的 OnPrepareHardware 方法调用 IWDFDevice2::AssignS0IdleSettings。 将参数设置为 AssignS0IdleSettings,如下所示:
    • IdleCapsIdleUsbSelectiveSuspend
    • DxState 到框架将空闲设备转换到的设备睡眠状态。 对于 USB 选择性挂起,请指定 PowerDeviceMaximum,指示框架应使用总线驱动程序指定的值。
    • IdleTimeout 为在框架将其转换为 DxState 之前设备必须处于空闲状态的毫秒数。
    • UserControlOfIdleSettingsIdleAllowUserControl (如果驱动程序允许用户管理空闲设置),否则为 IdleDoNotAllowUserControl
    • 已启用WdfUseDefault 以默认启用选择性挂起,但允许用户设置替代默认值。

以下示例演示IdleWake_PPO驱动程序如何在其内部 CMyDevice::SetPowerManagement 方法中调用此方法:

hr = m_FxDevice->AssignS0IdleSettings( IdleUsbSelectiveSuspend,
                                PowerDeviceMaximum,
                                IDLE_TIMEOUT_IN_MSEC,
                                IdleAllowUserControl,
                                WdfUseDefault);                                                                                                   

如果设备硬件可以生成唤醒信号,则 UMDF 驱动程序还可以支持来自 S1、S2 或 S3 的系统唤醒。 有关详细信息,请参阅 UMDF 驱动程序中的系统唤醒

支持非 PPO UMDF 驱动程序中的 USB 选择性挂起

非 PPO 的 UMDF 函数驱动程序可以使用基础 WinUSB.sys 驱动程序的功能支持选择性挂起。 UMDF 驱动程序必须通知 WinUSB 设备和驱动程序支持选择性挂起,并且必须在 INF 文件中或通过在 USB 目标设备对象上设置电源策略来启用选择性挂起。

如果 UMDF 函数驱动程序启用选择性挂起,则基础 WinUSB.sys 驱动程序将确定设备何时处于空闲状态。 WinUSB 在没有挂起的传输或中断或批量终结点上唯一挂起的传输为 IN 传输时,WinUSB 会启动空闲超时计数器。 默认情况下,空闲超时为 5 秒,但 UMDF 驱动程序可以更改此默认值。

当 WinUSB.sys 确定设备处于空闲状态时,它会发送请求,将设备挂起内核模式设备堆栈。 总线驱动程序会根据需要更改硬件的状态。 如果端口上的所有设备功能都已挂起,该端口将进入 USB 选择性挂起状态。

如果 I/O 请求在设备挂起时到达 WinUSB.sys,WinUSB.sys 恢复设备操作(如果设备必须通电才能为请求提供服务)。 当系统保留在 S0 中时,UMDF 驱动程序不需要任何代码即可恢复设备。 如果设备硬件可以生成唤醒信号,则 UMDF 驱动程序还可以支持来自 S1、S2 或 S3 的系统唤醒。 有关详细信息,请参阅 UMDF 驱动程序中的系统唤醒

非 PPO 的 UMDF 驱动程序可以通过执行以下两个步骤来支持选择性挂起:

  1. 通知 WinUSB.sys 设备和驱动程序支持选择性挂起。
  2. 启用 USB 选择性挂起。

此外,驱动程序还可以选择:

  • 设置设备的超时值。
  • 允许用户启用或禁用选择性挂起。

有关如何在非 PPO 的 UMDF USB 函数驱动程序中实现 USB 选择性挂起的示例,请参阅 WDK 中的 Fx2_Driver 示例。 此示例位于 %WinDDK%\BuildNumber\Src\Usb\OsrUsbFx2\Umdf\Fx2_Driver\ IdleWake_Non-PPO

通知 WinUSB 选择性挂起支持

若要通知 WinUSB.sys 设备可以支持 USB 选择性挂起,设备 INF 必须将 DeviceIdleEnabled 值添加到设备的硬件密钥,并将该值设置为 1。 以下示例演示Fx2_Driver示例如何在 WUDFOsrUsbFx2_IdleWakeNon-PPO.Inx 文件中添加和设置此值:

[OsrUsb_Device_AddReg]
...
HKR,,"DeviceIdleEnabled",0x00010001,1

启用 USB 选择性挂起

UMDF USB 驱动程序可以在运行时或在 INF 中安装期间启用 USB 选择性挂起。

  • 为了在运行时启用支持,函数驱动程序调用 IWDFUsbTargetDevice::SetPowerPolicy 并将 PolicyType 参数设置为 AUTO_SUSPEND,Value 参数设置为 TRUE 或 1。 以下示例演示 Fx2_Driver 示例如何在 DeviceNonPpo.cpp 文件中启用选择性挂起:

    BOOL AutoSuspend = TRUE;
    hr = m_pIUsbTargetDevice->SetPowerPolicy( AUTO_SUSPEND,
                                              sizeof(BOOL),
                                             (PVOID) &AutoSuspend );
    
  • 为了在安装过程中启用支持,INF 包含一个 AddReg 指令,该指令将 DefaultIdleState 值添加到设备的硬件密钥,并将值设置为 1。 例如:

    HKR,,"DefaultIdleState",0x00010001,1
    

设置空闲超时值

默认情况下,如果没有任何传输挂起,或者唯一挂起的传输是中断或批量终结点上的 IN 传输,WinUSB 会在 5 秒后暂停设备。 UMDF 驱动程序可以在安装 INF 时或在运行时更改此空闲超时值。

  • 为了在安装时设置空闲超时,INF 包含一个 AddReg 指令,该指令将 DefaultIdleTimeout 值添加到设备的硬件密钥,并将该值设置为超时间隔(以毫秒为单位)。 以下示例将超时设置为 7 秒:

    HKR,,"DefaultIdleTimeout",0x00010001,7000
    
  • 若要在运行时设置空闲超时,驱动程序调用 IWDFUsbTargetDevice::SetPowerPolicy,PolicyType 设置为 SUSPEND_DELAY,Value 设置为空闲超时值(以毫秒为单位)。 在 Device.cpp 文件的以下示例中,Fx2_Driver示例将超时设置为 10 秒:

    HRESULT hr;
    ULONG value;
    value = 10 * 1000;
    hr = m_pIUsbTargetDevice->SetPowerPolicy( SUSPEND_DELAY,
                                              sizeof(ULONG),
                                             (PVOID) &value );
    

提供 USB 选择性挂起的用户控制**

使用 WinUSB 选择性挂起支持的 UMDF USB 驱动程序可以选择性地允许用户启用或禁用选择性挂起。 为此,请在 INF 中包含 AddReg 指令,该指令将 UserSetDeviceIdleEnabled 值添加到设备的硬件密钥,并将值设置为 1。 下面显示了要用于 AddReg 指令的字符串:

HKR,,"UserSetDeviceIdleEnabled",0x00010001,1

如果设置了 UserSetDeviceIdleEnabled,则设备的“属性”对话框包含一个“电源管理”选项卡,允许用户启用或禁用 USB 选择性挂起。

UMDF 驱动程序中的系统唤醒

在 UMDF 驱动程序中,对系统唤醒的支持独立于对选择性挂起的支持。 UMDF USB 驱动程序可以同时支持系统唤醒和选择性挂起、系统唤醒和选择性挂起,或者系统唤醒或选择性挂起。 支持系统唤醒的设备可以将系统从睡眠状态唤醒 (S1、S2 或 S3) 。

UMDF USB PPO 驱动程序可以通过为框架的驱动程序对象提供唤醒信息来支持系统唤醒。 当外部事件触发系统唤醒时,框架会将设备返回到工作状态。

USB 非 PPO 驱动程序可以使用 WinUSB.sys 驱动程序实现的系统唤醒支持。

在作为 PPO 的 UMDF USB 驱动程序中支持系统唤醒**

使用以下参数在框架的设备对象上调用 IWDFDevice2::AssignSxWakeSettings 方法:

  • DxState 为系统进入可唤醒 Sx 状态时设备转换到的电源状态。 对于 USB 设备,请指定 PowerDeviceMaximum 以使用总线驱动程序指定的值。
  • 如果驱动程序允许用户管理唤醒设置,则 UserControlOfWakeSettingsWakeAllowUserControl;如果驱动程序允许用户管理唤醒设置,则为 WakeDoNotAllowUserControl。
  • 启用WdfUseDefault 以默认启用唤醒,但允许用户的设置替代默认值。

以下示例演示IdleWake_PPO驱动程序如何在其内部 CMyDevice::SetPowerManagement 方法中调用此方法:

hr = m_FxDevice->AssignSxWakeSettings( PowerDeviceMaximum,
                                       WakeAllowUserControl,
                                       WdfUseDefault);

在非 PPO 驱动程序中通过 WinUSB 启用系统唤醒**

若要通过 WinUSB 启用系统唤醒,驱动程序的 INF 会将注册表值 SystemWakeEnabled 添加到设备的硬件密钥,并将其设置为 1。 IdleWake_Non-PPO 示例启用系统唤醒,如下所示:

[OsrUsb_Device_AddReg]
...
HKR,,"SystemWakeEnabled",0x00010001,1

通过设置此值,驱动程序将启用系统唤醒并允许用户控制设备唤醒系统的能力。 在 设备管理器 中,设备的电源管理设置属性页包括一个检查框,用户可以使用该框启用或禁用系统唤醒。