传递电源 IRP

必须将电源 IRP 一直向下传递到设备堆栈,以确保电源转换受到干净管理。 驱动程序处理 IRP,当 IRP 沿着设备堆栈向下传输时,该 IRP 会降低设备功率。 驱动程序处理 IRP,该 IRP 在 IoCompletion 例程中应用设备电源,因为 IRP 将备份设备堆栈。

下图显示了驱动程序在 Windows 7 和 Windows Vista 中将电源 IRP 向下传递设备堆栈所需的步骤。

说明在 Windows vista 中向下传递电源 irp 的示意图。

如上图所示,在 Windows 7 和 Windows Vista 中,驱动程序必须执行以下操作:

  1. 如果设置 IoCompletion 例程,则调用 IoCopyCurrentIrpStackLocationToNext;如果未设置 IoCompletion 例程,则调用 IoSkipCurrentIrpStackLocation

    这两个例程设置下一个较低驱动程序的 IRP 堆栈位置。 复制当前堆栈位置可确保在 IoCompletion 例程运行时将 IRP 堆栈指针设置为正确的位置。

    如果错误编写的驱动程序在调用 IoSkipCurrentIrpStackLocation 时出错,然后设置完成例程,则此驱动程序可能会覆盖其下级驱动程序设置的完成例程。

  2. 如果需要完整的例程,请调用 IoSetCompletionRoutine 来设置 IoCompletion 例程。

  3. 调用 IoCallDriver 将 IRP 传递给堆栈中下一个较低的驱动程序。

下图显示了驱动程序在 Windows Server 2003、Windows XP 和 Windows 2000 中将电源 IRP 传递到设备堆栈所需的步骤。

 (windows server 2003、windows xp 和 windows 2000) 传递电源 irp。

如上图所示,驱动程序必须执行以下操作:

  1. 根据驱动程序的类型,可能会调用 PoStartNextPowerIrp。 有关详细信息,请参阅 调用 PoStartNextPowerIrp

  2. 如果设置 IoCompletion 例程,则调用 IoCopyCurrentIrpStackLocationToNext;如果未设置 IoCompletion 例程,则调用 IoSkipCurrentIrpStackLocation

    这两个例程设置下一个较低驱动程序的 IRP 堆栈位置。 复制当前堆栈位置可确保在 IoCompletion 例程运行时将 IRP 堆栈指针设置为正确的位置。

  3. 调用 IoSetCompletionRoutine 以设置 IoCompletion 例程。 在 IoCompletion 例程中,大多数驱动程序 调用 PoStartNextPowerIrp 以指示它已准备好处理下一个电源 IRP。

  4. 调用 PoCallDriver 将 IRP 传递给堆栈中下一个较低的驱动程序。

    驱动程序必须使用 PoCallDriver,而不是 IoCallDriver (作为其他 IRP) ,以确保系统正确同步电源 IRP。 有关详细信息,请参阅 调用 IoCallDriver 与呼叫 PoCallDriver

请记住,可以在 IRQL = DISPATCH_LEVEL调用 IoCompletion 例程。 因此,如果在较低级别的驱动程序完成 IRP 后,驱动程序需要在 IRQL = PASSIVE_LEVEL 处进行其他处理,则驱动程序的完成例程应将工作项排入队列,然后返回STATUS_MORE_PROCESSING_REQUIRED。 工作线程必须完成 IRP。

在 Windows 98/Me 中,驱动程序必须在 IRQL = PASSIVE_LEVEL 完成电源 IRP。

请勿更改 Power IRP 中的函数代码

除了管理 IRP 处理的常规规则外, IRP_MJ_POWER IRP 还有以下特殊要求:接收电源 IRP 的驱动程序不得更改 IRP 中由电源管理器或更高级别驱动程序设置的任何 I/O 堆栈位置的主要和次要函数代码。 在 IRP 完成之前,电源管理器依赖于这些函数代码保持不变。 违反此规则可能会导致难以调试的问题。 例如,操作系统可能会停止响应或“挂起”。

处理 Power IRP 时不要阻止

驱动程序在处理电源 IRP 时不得造成长时间延迟。

向下传递电源 IRP 时,驱动程序应在 Windows 7 和 Windows Vista) 或 Windows Server 2003、Windows XP 和 Windows 2000) 中调用 IoCallDriver (或 PoCallDriver (后,尽快从其 DispatchPower 例程中返回。 驱动程序在返回之前不得等待内核事件或以其他方式延迟。 如果驱动程序无法在短时间内处理电源 IRP,则应返回STATUS_PENDING并将所有传入 IRP 排队,直到电源 IRP 完成。 (请注意,此行为不同于允许阻止的 PnP 和 DispatchPnP 例程的行为。)

如果驱动程序必须等待设备堆栈中其他驱动程序的电源操作,则它应从其 DispatchPower 例程返回STATUS_PENDING,并为电源 IRP 设置 IoCompletion 例程。 驱动程序可以执行 IoCompletion 例程中所需的任何任务,然后调用 PoStartNextPowerIrp (Windows Server 2003、Windows XP 和 Windows 2000,仅) 和 IoCompleteRequest

例如,设备的电源策略所有者通常在持有系统电源 IRP 时发送设备电源 IRP,以便设置适合所请求系统电源状态的设备电源状态。

在这种情况下,电源策略所有者应在系统电源 IRP 中设置 IoCompletion 例程,将系统电源 IRP 传递给下一个较低的驱动程序,并从其 DispatchPower 例程返回STATUS_PENDING。

IoCompletion 例程中,它会调用 PoRequestPowerIrp 来发送设备电源 IRP,并将指针传递到请求中的回调例程。 IoCompletion 例程应返回STATUS_MORE_PROCESSING_REQUIRED。

最后,驱动程序从回调例程传递系统 IRP。 驱动程序不得在其 DispatchPower 例程中等待内核事件,并向其当前正在处理的 IRP 发出 IoCompletion 例程的信号;可能会出现系统死锁。 有关详细信息,请参阅 处理设备电源策略所有者中的系统Set-Power IRP

在类似情况下,当系统进入睡眠状态时,电源策略所有者可能需要完成一些挂起的 I/O,然后才能发送设备 IRP 来关闭其设备。 驱动程序不应在 I/O 完成并在其 DispatchPower 例程中等待事件时发出信号,而应将工作项排队并从 DispatchPower 例程返回STATUS_PENDING。 在工作线程中,它会等待 I/O 完成,然后发送设备电源 IRP。 有关详细信息,请参阅 IoAllocateWorkItem