取消等待/唤醒 IRP

只有发送等待/唤醒 IRP 的驱动程序才能取消该 IRP。

在以下情况下,驱动程序可能需要取消挂起的等待/唤醒 IRP:

  • 驱动程序接收设备的 PnP IRP_MN_STOP_DEVICEIRP_MN_QUERY_REMOVE_DEVICEIRP_MN_REMOVE_DEVICEIRP_MN_SURPRISE_REMOVAL 请求。 重启设备后,驱动程序应重新发出等待/唤醒 IRP (PoRequestPowerIrp) 。

  • 系统将进入睡眠状态,但不应启用设备来唤醒系统。

    例如,USB 集线器驱动程序可能会在设备启动时发送 IRP_MN_WAIT_WAKE 请求,以防稍后将其输入设备之一置于睡眠状态。 当系统处于工作状态时,来自设备的唤醒信号会将设备返回到工作状态 (但对系统电源状态) 没有影响。 当系统准备关闭时,如果不应允许设备唤醒系统,USB 集线器驱动程序会取消此 IRP。

  • 系统进入设备无法唤醒的睡眠状态。 也就是说,它进入的电源低于其DEVICE_CAPABILITIES结构中指定的 SystemWake 值。

  • 设备进入电源状态,无法响应唤醒信号。 也就是说,它进入的电源低于其DEVICE_CAPABILITIES结构中指定的 DeviceWake 值。

若要取消等待/唤醒 IRP,发送 IRP 的驱动程序调用 IoCancelIrp,并将指针传递到之前驱动程序调用 PoRequestPowerIrp 时返回的 IRP。

驱动程序不得取消它未发送的等待/唤醒 IRP。

取消等待/唤醒 IRP 的例程

许多函数和总线驱动程序应为等待/唤醒 IRP 设置 取消 例程;以下类型的驱动程序必须设置此类例程:

  • 将设备设置更改为启用或禁用唤醒的驱动程序。

  • IRP_MN_WAIT_WAKE 请求发送到父设备的驱动程序的驱动程序。

取消例程允许驱动程序为其设备禁用唤醒,并清理与挂起的等待/唤醒 IRP 相关的任何数据。 请求父设备的等待/唤醒 IRP 的驱动程序也可以取消这些 IRP。

在其等待/唤醒 取消 例程中,驱动程序应执行以下步骤:

  1. 调用 IoSetCancelRoutine 将 IRP 的 Cancel 例程重置为 NULL

  2. 调用 IoReleaseCancelSpinLock,传递 IRP 中指定的 CancelIRQL 以释放 IRP 的取消旋转锁。

  3. 重置设备扩展中的任何相关字段。 例如,当等待/唤醒 IRP 处于挂起状态时,大多数驱动程序会设置一个标志,并在设备扩展中保留指向 IRP 的指针。

    请注意,驱动程序在取消另一个此类 IRP 时可能会收到等待/唤醒 IRP。 驱动程序必须检查,以查看它是否已具有受旋转锁保护的 IRP (或其等效) 。 如果是这样,驱动程序必须仔细同步其处理,以确保取消正确的 IRP。 有关在 取消 例程中使用旋转锁的详细信息,请参阅 取消 IRP

  4. 更改任何所需的设备设置。 例如,调制解调器驱动程序会禁用设备的唤醒设置。

  5. Irp-IoStatus.Status> 设置为 STATUS_CANCELLED。

  6. 调用 IoCompleteRequest 以完成等待/唤醒 IRP,并指定IO_NO_INCREMENT。

  7. 如果驱动程序以前为父设备请求了相关 IRP_MN_WAIT_WAKE ,则驱动程序应从其 取消 例程中取消该 IRP。 驱动程序必须先释放取消旋转锁,然后才能取消父级的 IRP。

    例如,充当设备的总线驱动程序并为其父级拥有电源策略驱动程序的驱动程序应取消先前发送给其父级的相关等待/唤醒 IRP。 调用 IoCancelIrp 会在设备堆栈中调用父级的 Cancel 例程,依此调用。