驱动程序收到的电源IRP的主功能码是IRP_MJ_POWER 以及四个次要代码:
IRP_MN_POWER_SEQUENCE
驱动程序将此 IRP 作为优化发送,以确定其设备是否实际进入了特定的电源状态。 对此 IRP 的支持是可选的。
若要发送此 IRP,驱动程序必须调用 IoAllocateIrp 来分配 IRP,并指定主要 IRP 代码 IRP_MJ_POWER 和次 IRP 代码 IRP_MN_POWER_SEQUENCE。 驱动程序必须调用 IoCallDriver 或 PoCallDriver ,才能将 IRP 传递到下一个较低的驱动程序。 电源管理器无法发送此 IRP。
此 IRP 的发送方必须在 IRQL <= DISPATCH_LEVEL 运行。
输入参数: 无
输出参数: Parameters.PowerSequence 指向具有以下成员 的POWER_SEQUENCE 结构:
- SequenceD1: 设备处于电源状态 D1 或更低状态的次数。
- SequenceD2:设备处于电源状态 D2 或更低状态的次数。
- SequenceD3:设备处于电源状态 D3 的次数。
I/O状态:驱动程序将 Irp->IoStatus.Status 设置为 STATUS_SUCCESS,以指示设备已进入请求的状态。
每次设备进入相应的电源状态或较低功率状态时,总线驱动程序都会递增 SequenceD1、 SequenceD2 和 SequenceD3 中的值。
总线驱动程序可以选择处理它:功能驱动和Filter驱动程序可以选择发送它。
对于需要很长时间才能更改状态的设备,此 IRP 提供了有用的优化。 每次设备更改其电源状态时,其总线驱动程序都会递增该电源状态的序列值。 总线驱动程序在启动时初始化序列值,并随后不断递增它们;无需将其重置为零。
设备策略所有者可以在关闭设备之前发送一次此 IRP 以获取序列值,并在将电源还原到设备时再次获取新值。 通过比较这两组值,驱动程序可以确定设备是否实际进入了低功率状态。 如果设备未断电,驱动程序可以在设备返回到 D0 状态时避免耗时的重新初始化。
例如,如果设备在达到 D2 状态时需要很长时间才能恢复电源,则驱动程序可以在将设备状态设置为 D2 或更低之前存储 SequenceD2 值。 稍后,当设备恢复电源时,驱动程序可以将新的 SequenceD2 值与其存储值进行比较,以确定设备状态是否实际低于 D2。 如果值匹配,则设备实际上没有进入电源状态 D2 或更低状态,驱动程序可以避免重新初始化设备。
IRP_MN_QUERY_POWER
电源管理器或设备电源策略所有者发送此 IRP,以确定它是否可以更改系统或设备电源状态,一般在准备进入睡眠状态之前使用这个IRP。 驱动程序必须调用 PoRequestPowerIrp 来分配和发送此 IRP。
电源管理器将此 IRP 在 IRQL = PASSIVE_LEVEL 发送到在 PDO 中设置DO_POWER_PAGABLE标志的设备堆栈。
如果设置了DO_POWER_INRUSH标志,电源管理器可以在 IRQL = DISPATCH_LEVEL 发送 IRP。 此类驱动程序不能直接或间接访问任何分页代码或数据。
输入参数:
Parameters.Power.Type: 指定要设置的电源状态类型,可以是 SystemPowerState 还是 DevicePowerState。
Parameters.Power.State: 指定电源状态本身,如下所示:
- 如果 Parameters.Power.Type 为 SystemPowerState,则该值是 SYSTEM_POWER_STATE 类型的枚举器。
- 如果 Parameters.Power.Type 为 DevicePowerState,则该值是 DEVICE_POWER_STATE 类型的枚举器。
Parameters.Power.ShutdownType:指定有关所请求转换的其他信息。 可能的值为 POWER_ACTION 类型的枚举器。
输出参数: 无
I/O状态:驱动程序将 Irp->IoStatus.Status 设置为 STATUS_SUCCESS,以指示设备已进入请求的状态。
IRP_MN_QUERY_POWER的参数与 IRP_MN_SET_POWER 的参数相同。 但是, IRP_MN_QUERY_POWER 查询系统或设备是否可以进入特定电源状态,而不是通知驱动程序电源状态不可撤销的更改。
驱动程序不得为了响应 IRP_MN_QUERY_POWER 请求而更改其设备的电源状态。
驱动程序在 Windows Server 2003、Windows XP 和 Windows 2000 上收到 IRP_MN_QUERY_POWER 请求后,驱动程序必须调用 PoStartNextPowerIrp,如 调用 PoStartNextPowerIrp 中所述。 从 Windows Vista 开始,不需要调用 PoStartNextPowerIrp ,此类调用不执行电源管理操作。
系统电源状态的IRP_MN_QUERY_POWER: 通常用于电源管理器发送此 IRP,以确保它可以在不中断工作(例如断开网络连接)的情况下更改系统电源状态。
电源管理器在发送 IRP_MN_SET_POWER 之前进行查询,以请求系统睡眠状态或正常系统关闭。 但是,在某些关键条件下 (例如用户按下关机按钮或电池掉电) ,电源管理器可能会发送 IRP_MN_SET_POWER 请求,而无需先发送查询电源请求。
当驱动程序收到系统电源查询 IRP 时,如果它不能支持任何对查询的系统状态有效的设备状态,它应该会使 IRP 失败。 否则,驱动程序应将 IRP 传递给下一个较低的驱动程序。 总线驱动程序完成 IRP。
从 Windows Vista 开始,过渡到系统睡眠状态被视为关键操作。 尽管驱动程序可能会使系统查询电源 IRP 失败,但电源管理器仍可能将系统电源状态更改为睡眠状态。 驱动程序收到系统查询电源 IRP 后,应始终为系统电源状态的后续更改做好准备。
当设备电源策略所有者收到系统电源查询 IRP 时,它应在 IRP 中设置 IoCompletion 例程,然后再将其传递。 在 IoCompletion 例程中,它应发送对查询的系统状态有效的设备状态的 IRP_MN_QUERY_POWER 。
当 IRP 指定 PowerSystemShutdown (S5) 时, Parameters.Power.ShutdownType 中的值会提供关闭的原因。 ShutdownType 告知驱动程序系统是 (PowerActionShutdownReset) 重置还是无限期关闭以稍后 (PowerActionShutdownOff) 重新启动。 对于大多数设备的驱动程序,差异是无关紧要的。 但是,对于某些设备(例如执行 DMA 的视频流设备),驱动程序可能会选择在系统重置时关闭其设备,从而停止任何正在进行的 I/O。
在 Microsoft Windows 2000 及更高系统上, ShutdownType 的值也可以是 PowerActionShutdown。 在这种情况下,驱动程序无法判断请求的关机类型,因此应继续进行重置。
如果驱动程序在系统电源状态 IRP_MN_QUERY_POWER 请求失败,电源管理器通常会通过发出 IRP_MN_SET_POWER IRP 来做出响应。 通常,此 IRP 将重申当前系统状态。 但是,驱动程序可能会收到查询状态或其他中间状态 的IRP_MN_SET_POWER 。 驱动程序应准备好处理这些情况。
设备电源状态的IRP_MN_QUERY_POWER:设备电源策略所有者将此 IRP 发送到其堆栈,以响应系统 IRP_MN_QUERY_POWER 请求。
如果驱动程序可以将其设备置于请求的设备状态,则会将 IoStatus.Status 设置为STATUS_SUCCESS并将 IRP 向下传递到下一个较低的驱动程序,依此类推,直到 IRP 到达总线驱动程序。 如果堆栈中的任何驱动程序必须使 IRP 失败,该驱动程序应通过调用 IoCompleteRequest 并返回失败状态立即完成 IRP。 IRP 失败的驱动程序不会在堆栈中进一步传递它。
通过返回STATUS_SUCCESS,驱动程序保证不会启动任何会更改其设置所请求电源状态的能力的操作。 驱动程序应将需要此类操作的任何 IRP 排队,直到完成将设备返回到可接受的电源状态的设置功率 IRP。
在 Windows 2000 及更高系统上,当 IRP 指定 PowerDeviceD1、 PowerDeviceD2 或 PowerDeviceD3 时,如果系统电源 IRP 处于活动状态, Parameters.Power.ShutdownType 中的值将提供有关当前系统电源 IRP 的信息。 在这种情况下, ShutdownType 中的值指示当前请求的系统电源状态;如果系统请求未完成,则为 PowerActionNone 。 在 Windows 98/Me 上,当 IRP 请求设备电源状态时,此字段始终包含 PowerActionNone 。
IRP_MN_SET_POWER
系统电源管理器或设备电源策略所有者可以发送此 IRP。
电源管理器发送此 IRP,以通知驱动程序系统电源状态发生更改。 如果驱动程序已注册其设备进行空闲检测,则电源管理器会发送此 IRP 来更改空闲设备的电源状态。
拥有电源策略的驱动程序发送此 IRP 来设置其设备的设备电源状态。 驱动程序必须调用 PoRequestPowerIrp 才能发送此 IRP。
电源管理器将此 IRP 在 IRQL = PASSIVE_LEVEL 发送到在 PDO 中设置DO_POWER_PAGABLE标志的设备堆栈。 此类堆栈中的驱动程序可以触摸分页代码或数据来完成请求。
如果设置了DO_POWER_INRUSH标志,电源管理器可以在 IRQL = DISPATCH_LEVEL 发送 IRP。 此类驱动程序不能直接或间接访问任何分页代码或数据。
输入参数
Parameters.Power.Type:成员指定要设置的电源状态的类型,即 SystemPowerState 或 DevicePowerState。
Parameters.Power.State 成员指定电源状态本身,如下所示:
- 如果 Parameters.Power.Type 为 SystemPowerState,则该值是 SYSTEM_POWER_STATE 类型的枚举器。
- 如果 Parameters.Power.Type 为 DevicePowerState,则该值是 DEVICE_POWER_STATE 类型的枚举器。
Parameters.Power.ShutdownType 成员指定有关所请求转换的其他信息。 此成员的可能值 POWER_ACTION 枚举值。
从 Windows Vista 开始, Parameters.Power.SystemPowerStateContext 成员是一个只读的、部分不透明的 SYSTEM_POWER_STATE_CONTEXT 结构,其中包含有关计算机以前系统电源状态的信息。 如果 Parameters.Power.Type 为 SystemPowerState , Parameters.Power.State 为 PowerSystemWorking,则此结构中的两个标志位指示快速启动或从休眠状态唤醒是否导致计算机进入 S0 (工作) 系统状态。
下表显示了IRP_MN_SET_POWER的内容。Parameters.Power。{State|ShutdownType} 以及每个系统电源转换SYSTEM_POWER_STATE_CONTEXT结构中的CurrentSystemState、TargetSystemState 和 EffectiveSystemState 位字段。 每行表示一个 IRP_MN_SET_POWER。
输出参数: Parameters.Power.SystemContext 保留供系统使用。
I/O状态:驱动程序将 Irp->IoStatus.Status 设置为 STATUS_SUCCESS,以指示设备已进入请求的状态。
注意:
- 驱动程序不得使设置系统电源状态的请求失败。
- 位于总线驱动程序上方的功能驱动和Filter驱动程序不得使设置设备电源状态的请求失败。 如果设备被删除或正在移除,总线驱动程序可能会使设备启动请求失败。
电源管理器或驱动程序可以请求 IRP_MN_SET_POWER IRP。 电源管理器出于以下原因之一发送此 IRP:
- 通知驱动程序系统电源状态发生更改
- 更改电源管理器正在为其执行空闲检测的设备电源状态
拥有设备电源策略的驱动程序发送 IRP_MN_SET_POWER 来更改其设备的电源状态。
在任何给定时间,系统只允许每个设备对象有一个此类 IRP 处于活动状态。
每个驱动程序都必须通过从 Windows Vista) 或 PoCallDriver (Windows XP) 开始调用 IoCallDriver 将每个电源 IRP 向下传递到下一个较低的驱动程序。 PoCallDriver 接口类似于 IoCallDriver 接口,只不过电源管理子系统可能会在将 IRP 传递给下一个驱动程序之前延迟 IRP。 例如,如果设备需要浪涌电流,因此必须使用另一台此类设备串行启动,则 PowerDeviceD0 请求可能会出现延迟。
驱动程序在 Windows XP 上收到 IRP_MN_SET_POWER 请求后,驱动程序必须调用 PoStartNextPowerIrp,如 调用 PoStartNextPowerIrp 中所述。 从 Windows Vista 开始,不需要调用 PoStartNextPowerIrp ,并且此类调用不执行电源管理操作。
系统电源状态: 只有系统电源管理器可以发送系统设置电源 IRP;驱动程序不得使设置系统电源状态的请求失败。
在发送 IRP_MN_SET_POWER 请求系统睡眠状态之前,电源管理器会尽可能发送 IRP_MN_QUERY_POWER 。 但是在某些情况下,例如用户按下关机按钮或电池过期) ,电源管理器可能会在不首先查询的情况下发出 IRP_MN_SET_POWER 。
IRP_MN_SET_POWER请求将发送到设备堆栈中的顶部驱动程序。 顶部驱动程序将 IRP 向下传递到下一个下级驱动程序,依此类推,直到 IRP 到达必须完成 IRP 的总线驱动程序。
除了传递它之外,Filetr驱动程序通常不需要对系统设置电源 IRP 执行操作。
但是,设备电源策略所有者在传递 IRP 之前设置 IoCompletion 例程。 在 IoCompletion 例程中,它发送设备电源 IRP 的IRP_MN_SET_POWER 请求。 有关详细信息,请参阅 在设备电源策略所有者中处理系统Set-Power IRP。
系统设置电源 IRP 通知驱动程序即将更改系统电源状态,驱动程序必须为此做好准备。 但是,驱动程序在收到设备电源状态的 IRP_MN_SET_POWER 之前,不应更改其设备的电源状态。
Parameters.Power.ShutdownType 中的值提供有关挂起操作的其他信息。 当 IRP 指定 PowerSystemShutdown (S5) 时,驱动程序可以确定系统是重置 powerActionShutdownReset () 还是无限期关闭电源以稍后 (PowerActionShutdownOff) 重新启动。 对于大多数设备的驱动程序,差异是无关紧要的。 但是,对于某些设备(如视频流设备),驱动程序可能会关闭设备电源,以便在系统重置时停止 I/O。
在 Windows 2000 及更高版本的操作系统上, ShutdownType 的值也可以是 PowerActionShutdown。 在这种情况下,驱动程序无法判断请求的关机类型,因此应继续进行重置。
设备电源状态: 位于总线驱动程序上方的功能驱动和Filter驱动程序不得使设置设备电源状态的请求失败。 如果设备被删除或正在移除,总线驱动程序可能会使设备启动请求失败。
在完成 IRP 之前,驱动程序必须将设备设置为请求的状态。
当 IRP 请求转换为较低功率状态时,驱动程序必须在 IRP 沿着设备堆栈向下移动时处理 IRP,从而保存驱动程序将设备还原到工作状态所需的任何上下文。 在总线驱动程序收到 IRP 后,驱动程序会:
- 保存驱动程序将设备还原到工作状态所需的任何上下文;
- 将设备设置为请求的电源状态;
- 调用 PoSetPowerState 以通知电源管理器;
- 调用 PoStartNextPowerIrp 以仅) 启动 Windows Server 2003、Windows XP 和 Windows 2000 (下一个电源 IRP);
- 完成设备电源 IRP;
驱动程序必须及时完成此 IRP。 通常,驱动程序应避免典型用户发现速度明显缓慢的任何延迟。 例如,驱动程序可能会延迟系统状态更改以刷新缓存的磁盘或网络数据,但不应使网络连接保持活动状态或格式化磁带。
在 Windows 2000 及更高版本的操作系统上,如果 IRP 指定 PowerDeviceD1、 PowerDeviceD2 或 PowerDeviceD3,并且系统集电源 IRP 处于活动状态,则 Parameters.Power.ShutdownType 中的值提供有关系统 IRP 的信息。
休眠路径上的设备的驱动程序应检查此值。 如果 IRP 请求 PowerDeviceD3 且 ShutdownType 为 PowerActionHibernate,则此类驱动程序应保存还原设备所需的任何上下文,但不应关闭设备电源;当计算机断电时,设备将进入 D3 状态。
确定何时关闭设备的驱动程序因设备类而异,确定何时启动设备的驱动程序几乎始终是访问设备寄存器的驱动程序。 在访问设备的硬件寄存器之前,驱动程序必须验证设备是否处于 D0 状态。 如果设备不处于 D0 状态,驱动程序必须调用 PoRequestPowerIrp 以发送 IRP 来启动设备。 除非设备处于 D0 状态,否则驱动程序无法访问其设备。
当驱动程序收到设备状态 D0 的设定功率 IRP 时,它会设置 IoCompletion 例程并将 IRP 传递给下一个较低的驱动程序。
当 IRP 到达总线驱动程序时,该驱动程序向设备设置 (或重置) 电源,并调用 PoSetPowerState 通知电源管理器设备的新电源状态。
总线驱动程序完成启动 IRP 后,功能驱动和Filter驱动程序在 IoCompletion 例程中处理 IRP,因为它将备份到设备堆栈。 在 IoCompletion 例程中,每个驱动程序还原或重新初始化其设备上下文,并执行任何其他必需的启动任务。
IRP_MN_WAIT_WAKE
拥有电源策略的驱动程序将此 IRP 定向到其 PDO,以使其设备能够唤醒以响应外部事件,例如传入电话呼叫。 驱动程序必须调用 PoRequestPowerIrp 才能发送此 IRP。
一般情况下,驱动程序应在确定应为其设备启用唤醒后立即发送此 IRP。 因此,大多数此类设备的驱动程序在开机后和完成IRP_MN_START_DEVICE请求之前发送 此 IRP。
但是,只要设备处于工作状态,驱动程序就可以发送 IRP, (PowerDeviceD0) 。 设备堆栈不得处于转换状态;也就是说,当任何其他电源 IRP 在其设备堆栈中处于活动状态时,驱动程序不应发送 IRP_MN_WAIT_WAKE 。
等待/唤醒 IRP 不会更改设备或系统的电源状态, 它只是启用来自设备的唤醒信号。 当唤醒信号到达时,策略所有者必须调用 PoRequestPowerIrp 以发送设置电源 IRP 以将其设备返回到 D0。
驱动程序必须以 IRQL = PASSIVE_LEVEL 运行才能发送此 IRP。 但是,IRP 可以在 IRQL = DISPATCH_LEVEL完成。
输入参数:Parameters.WaitWake.PowerState 包含应允许设备从中唤醒系统的最低 (最低功率) 系统电源状态。
输出参数: 无。
I/O状态:
驱动程序将 Irp-> IoStatus.Status设置为下列值之一:
- STATUS_PENDING: 驱动程序已收到 IRP,并正在等待设备发出唤醒信号。
- STATUS_INVALID_DEVICE_STATE: 设备处于低于设备DEVICE_CAPABILITIES结构中指定的 DeviceWake 状态,或者设备无法从 IRP 中传递的 SystemWake 状态唤醒系统。
- STATUS_NOT_SUPPORTED: 设备不支持唤醒。
- STATUS_DEVICE_BUSY: IRP_MN_WAIT_WAKE请求已挂起,必须先完成或取消请求,然后才能发出另一个IRP_MN_WAIT_WAKE请求。
- STATUS_SUCCESS: 设备已发出唤醒事件的信号。
- STATUS_CANCELLED:IRP 已取消。
如果驱动程序必须使此 IRP 失败,它将立即完成 IRP,并且不会将 IRP 传递给下一个较低的驱动程序。
驱动程序出于以下两个原因之一发送 IRP_MN_WAIT_WAKE :
- 使其设备能够唤醒睡眠系统以响应外部唤醒信号;
- 使设备能够从设备睡眠状态唤醒,以响应外部唤醒信号;
必须将 IRP 向下传递到设备的总线驱动程序,该设备调用 IoMarkIrpPending 并从其 DispatchPower 例程返回STATUS_PENDING。 IRP 一直处于挂起状态,直到出现唤醒信号或发送 IRP 的驱动程序取消它为止。
在任何给定时间,对于 PDO,只能保留一个等待/唤醒 IRP。 如果驱动程序已持有 PDO 的等待/唤醒 IRP,则必须使任何其他此类 IRP 失败并STATUS_DEVICE_BUSY。 枚举多个子 PDO 的驱动程序可以为每个此类 PDO 挂起一个等待/唤醒 IRP。
当 IRP 沿着设备堆栈向下移动时,每个驱动程序都会设置 一个 IoCompletion 例程。 当设备发出唤醒事件信号时,总线驱动程序为唤醒信号提供服务并完成 IRP,返回STATUS_SUCCESS。 然后,I/O 管理器调用下一个更高级别的驱动程序的 IoCompletion 例程,依此调用设备堆栈。
当驱动程序发送等待/唤醒 IRP 时,它应在 PoRequestPowerIrp 调用中指定回调例程。 在回调例程中,驱动程序通常为设备提供服务。 例如,设备的电源策略所有者必须调用 PoRequestPowerIrp 以发送设备状态 D0 的IRP_MN_SET_POWER 。
充当一个设备的总线驱动程序的驱动程序和父设备的策略所有者在收到来自Child PDO 的 IRP_MN_WAIT_WAKE 请求时请求父设备堆栈 的IRP_MN_WAIT_WAKE IRP。 如果驱动程序枚举多个Child PDO,则无论有多少Child PDO发送等待/唤醒请求,它都应仅为父级设备堆栈请求请求一个等待/唤醒 IRP。 相反,此类驱动程序应保留等待/唤醒 IRP 的内部计数,每次收到请求时递增计数,并在每次完成请求时递减计数。 如果在完成等待/唤醒 IRP 后计数为非零,驱动程序应将另一个等待/唤醒 IRP 发送到其设备堆栈,以“重新调整”自身进行唤醒。
若要取消 IRP_MN_WAIT_WAKE,驱动程序会调用 IoCancelIrp。 只有发起 IRP 的驱动程序才能取消它。 发生以下任一情况时,驱动程序会取消挂起 的IRP_MN_WAIT_WAKE :
- 驱动程序收到停止或删除设备的 PnP IRP;
- 系统将进入睡眠状态,设备唤醒信号不得唤醒它;