Window XP驱动开发(二十四) 电源管理

转载自: http://blog.csdn.net/xxxluozhen/article/details/5023703

 

 

 一、电源管理

1、WDM电源管理模型

在Windows 2000和Windows 98中,操作系统接管了大部分电源管理工作。当然,这是因为只有操作系统才能真正了解电源管理的内部过程。例如,系统BIOS负责的电源管理不能区分应用程序使用的屏幕和屏幕保护程序使用的屏幕之间的区别。但操作系统可以区分开这种不同,从而确定是否可以关闭显示器。

作为计算机全局电源策略,操作系统支持一些用户接口元素,用户可以通过这些接口元素控制最终的电源管理策略。这些用户接口元素包括控制面板、开始菜单上的命令、控制设备唤醒特征的API。通过向设备发送IRP,内核的电源管理部件实现了操作系统的电源策略。WDM驱动程序主要是作为响应这些IRP的被动角色。

 

2、 WDM驱动程序的角色

(1)设备的某个驱动程序需要充当设备电源策略的管理者。通常都是由功能驱动程序充当这个角色。电源管理器可以改变整个系统的电源状态。功能驱动程序接收电源管理器发来的IRP(系统IRP),作为设备电源策略的管理者,功能驱动程序用设备理解的术语翻译这些IRP并引发新的IRP(设备IRP)。

(2)当响应设备IRP时,功能驱动程序需要关心设备专有的细节。设备硬件会有自己的上下文信息,不应该在设备处于低电源期间丢失这些信息。例如,键盘驱动程序会保存锁定键(如CAPS-LOCK、NUM-LOCK、SCROLL-LOCK)、LED等信息。功能驱动程序有责任保存并恢复这些上下文信息。

(3)某些设备带有唤醒特征,当外部事件发生时,这些设备可以唤醒系统;功能驱动程序应与用户协同工作以确保唤醒特征在需要时有效。

(4)许多功能驱动程序还管理含有大量IRP的队列(设备读写IRP),因此当设备电源状态转变时需要停止或释放这些队列

处于设备堆栈底端的总线驱动程序有责任控制设备的电流和执行任何与设备唤醒特征相关的电气步骤。过滤器驱动程序通常作为电源管理IRP通过的管道,它们用专用的协议向下层驱动程序传递电源管理请求

3、设备电源状态与系统电源状态

(1)WDM模型使用与ACPI(Advanced Configuration and Power Interface)规范(见http://www.teleport.com/~acpi/spec.htm)相同的术语来描述电源状态。在D0状态中,设备处于全供电状态。在D3状态中,设备处于无供电(或最小限度的电流)状态。中间的D1和D2状态指出设备的两个不同睡眠状态。随着设备从D0状态变化到D3状态,设备将消耗越来越少的电力,同时需要保留的当前状态上下文信息也越来越少。而设备再转变回D0状态的延迟期则相应增加。
(2)Microsoft规定了不同类型设备的类专用电源需求。这个需求规范可以在http://www.microsoft.com/hwdev/specs/PMref/找到。例如,这个规范要求每个设备至少要支持D0和D3两个状态。输入设备(键盘、鼠标等)还应该支持D1状态。Modem设备需要另外支持D2状态。设备类上的这些不同规定可能来源于设备的用途和工业上的实践。
操作系统不直接处理设备的电源状态,由设备驱动程序专门处理

(3)系统使用一组与ACPI设备状态类似的系统电源状态来控制电源。Working状态是全供电状态,计算机可以实现全部功能。程序仅能在Working状态下执行。其它系统电源状态对应更小的电力需求配置,在这些系统电源状态中,计算机不能执行任何指令。Shutdown状态就是电源关闭状态。Hibernate状态是另一种Shutdown状态,它把计算机的整个状态都记录到硬盘上,因此在电源恢复供电时可以使计算机快速恢复到记录前的状态。在Hibernate和Working状态之间是三个有不同电力消耗级别的中间状态。

4、 电源状态转换

(1)系统初始化后即进入Working状态。大部分设备也以D0状态启动,但某些设备的驱动程序会在设备启动时使设备进入低电源消耗状态。在系统启动并正常运行后,这些设备的驱动程序才使设备进入一个稳定的状态,在这个状态中,系统电源处于Working状态,而设备处于的状态取决于具体活动和设备自身的能力。
(2)用户的活动或外部事件会导致电源状态的改变。一个常见的电源状态转换情景是用户在开始菜单上选择“关闭系统”中的“standby”选项,使计算机进入等待状态。在响应这个命令过程中,电源管理器首先向每个驱动程序发送带有IRP_MN_QUERY_POWER副功能码的IRP_MJ_POWER请求以询问设备能否接受即将到来的电源关闭请求。如果所有驱动程序都同意,电源管理器将发送第二个带有IRP_MN_SET_POWER副功能码的电源管理IRP,然后驱动程序把其设备置入低电源状态以响应这个IRP。如果有任何一个驱动程序否决了这个查询,电源管理器仍旧发出这个IRP_MN_SET_POWER请求,但它用原来的电源级别换成了请求的电源级别。

所以在我们的功能驱动中的IRP_MJ_POWER的派遣函数中要去case这几个电源的请求。eg:

NTSTATUS  USB2COM_ProcessPowerIrp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
{
	 PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION deviceExtension;
    BOOLEAN fGoingToD0 = FALSE;
    POWER_STATE sysPowerState, desiredDevicePowerState;
    KEVENT event;

    USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_ProcessPowerIrp() IRP_MJ_POWER\n"));

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation (Irp);
    USB2COM_IncrementIoCount(DeviceObject);

    switch (irpStack->MinorFunction) 
	{
    case IRP_MN_WAIT_WAKE:
		 break;

    case IRP_MN_SET_POWER:
		{
			switch (irpStack->Parameters.Power.Type) 
			{
            case SystemPowerState:
				break;
			case DevicePowerState:
				break;
			}
		}
        break;
	case IRP_MN_QUERY_POWER:        
        break; 
    default:
		break;
	}
}



(3)系统并不总是发送IRP_MN_QUERY_POWER请求。某些事件(如电池电力将要耗尽)必须被无条件接受,并且操作系统也不再发出查询请求。如果查询发出后,并且驱动程序也接受了请求的电源状态,那么驱动程序将不再启动任何会妨碍未来电源状态设置请求的操作。例如,磁带机驱动程序在使一个进入低电源状态的查询请求成功返回前先确保当前没有执行备份操作。另外,该驱动程序还拒绝任何后来的备份命令,除非是另一个电源状态设置请求。

5、 POWER_STATUS

该结构体在电源管理中占据很重要的地位,其主要进行细微的描述电源的状态和剩余时间等方面的信息,该结构体描述如下;

typedef struct _POWER_STATUS 
{    
	BYTE   PS_AC_Line_Status;    
	BYTE   PS_Battery_Status;    
	BYTE   PS_Battery_Flag;    
	BYTE   PS_Battery_Life_Percentage;    
	WORD   PS_Battery_Life_Time;
}   POWER_STATUS;

Ø       PS_AC_Line_Status描述电源AC输入线的状态。

Ø       PS_Battery_Status描述电池的状态

Ø       PS_Battery_Status描述电池先前的状态或者是改变时的状态。

Ø       PS_Battery_Life_Percentage描述电池还剩余多少电,以百分计

Ø       PS_Battery_Life_Time描述电池还能支持多少时间,以秒钟计。

6、 IRP_MN_QUERY_CAPABILITIES

通常,我们总是把设备设置成与设备的当前活动、设备的唤醒特征、设备的能力、以及迫近的系统状态,相一致的最低的电源状态。PnP管理器在启动设备后(也可能是其它时间)的很短时间内向设备发出查询设备能力请求——IRP_MN_QUERY_CAPABILITIES请求,该请求的参数是一个DEVICE_CAPABILITIES结构,该结构体将描述系统的电源状态信息,DEVICE_CAPABILITIES结构体描述:

typedef struct _DEVICE_CAPABILITIES {
    USHORT Size;
    USHORT Version;  // the version documented here is version 1
    ULONG DeviceD1:1;
    ULONG DeviceD2:1;
    ULONG LockSupported:1;
    ULONG EjectSupported:1; // Ejectable in S0
    ULONG Removable:1;
    ULONG DockDevice:1;
    ULONG UniqueID:1;
    ULONG SilentInstall:1;
    ULONG RawDeviceOK:1;
    ULONG SurpriseRemovalOK:1;
    ULONG WakeFromD0:1;
    ULONG WakeFromD1:1;
    ULONG WakeFromD2:1;
    ULONG WakeFromD3:1;
    ULONG HardwareDisabled:1;
    ULONG NonDynamic:1;
    ULONG WarmEjectSupported:1;
    ULONG NoDisplayInUI:1;
    ULONG Reserved:14;

    ULONG Address;
    ULONG UINumber;

    DEVICE_POWER_STATE DeviceState[POWER_SYSTEM_MAXIMUM];
    SYSTEM_POWER_STATE SystemWake;
    DEVICE_POWER_STATE DeviceWake;
    ULONG D1Latency;
    ULONG D2Latency;
    ULONG D3Latency;
} DEVICE_CAPABILITIES, *PDEVICE_CAPABILITIES;

Ø       DeviceState域为与每个系统状态对应的可能存在的设备最高级状态数组

Ø       SystemWake域为最低级的系统电源状态,在该状态中设备可以生成唤醒信号。PowerSystemUnspecified域指出设备不能唤醒系统。

Ø       DeviceWake域为最低级的设备电源状态,在该状态中设备可以生成唤醒信号。PowerDeviceUnspecified指出设备不能生成唤醒信号

Ø       D1Latency域为设备从D1切换到D0状态所需的最长时间(100微秒单位)。

Ø       D2Latency域为设备从D2切换到D0状态所需的最长时间(100微秒单位)。

Ø       D3Latency域为设备从D3切换到D0状态所需的最长时间(100微秒单位)。

Ø       WakeFromD0、WakeFromD1、WakeFromD2和WakeFromD3标志,指出当设备处于指定的状态中时,设备的系统唤醒特征是否是可操作的

7、 IRP_MJ_POWER

在WDM驱动当中,驱动程序响应IRP_MJ_POWER例程表示支持电源管理。电源状态描述保存在IO_STACK_LOCATION的Parameters联合中。

eg:

case IRP_MN_SET_POWER:
        {
        USB2COM_KdPrint( DBGLVL_MEDIUM,("USB2COM_ProcessPowerIrp() Enter IRP_MN_SET_POWER\n"));
        switch (irpStack->Parameters.Power.Type) {
            case SystemPowerState:

 

该联合提供了响应的四种电源状态,如下表3-3-2所示。表3-3-2  IO_STACK_LOCATION的Parameters.Power子结构中的各个域


在管理器和驱动沟通使用了IRP_MJ_POWER类型的I/O请求包,该功能附带了四个辅助码:

(1)IRP_MN_QUERY_POWER确定预期的电源状态改变是否安全

(2)IRP_MN_SET_POWER命令驱动程序改变电源状态;

(3)IRP_MN_WAIT_WAKE 命令总线驱动程序使用唤醒特征、使功能驱动程序能了解唤醒信号何时发生;

(4)IRP_MN_POWER_SEQUENCE 为上下文保存和恢复提供优化
对于整个操作系统而言,所有驱动程序,包括过滤器驱动程序和功能驱动程序通常都向其下面的驱动程序传递电源管理请求,除IRP_MN_QUERY_POWER请求外。

在功能驱动(例如、USB驱动),系统控制程序如何把电源管理请求传递到低级驱动程序有特殊的规则。三种可能的处理过程:

第一,在释放一个电源管理请求的控制之前,你必须调用PoStartNextPowerIrp,即使你以错误状态完成该IRP,也要这样做。做这个调用的原因是,电源管理器自己需要维持一个电源管理请求队列,所以必须通知它确实可以出队一个请求,以及向设备发送下一个请求。除了调用PoStartNextPowerIrp,你还必须调用专用例程PoCallDriver(代替IoCallDriver)来向下层驱动程序发送请求

8、 IRP_MN_SET_POWER与IRP_MN_QUERY_POWER

PNP管理器发出IRP_MN_SET_POWER后,例程函数可以设置系统和设备电源状态。在DDK中SystemPowerState和DevicePowerState为系统和设备电源状态。在PNP中是如何区分那种类型的电源状态呢?DDK中I/O参数Parameters.Power.Type标识电源状态类型。如果Parameters.Power.Type为SystemPowerState则重新设置的是系统电源状态,反之亦然。
IRP_MN_QUERY_POWER请求发生在IRP_MN_SET_POWER之前,主要用于确定是否具备修改电源状态的能力

9、 IRP_MN_POWER_SEQUENCE

当PNP管理器发出IRP_MN_POWER_SEQUENCE请求,主要用于指出设备是否已经进入制定设电源状态
IRP_MN_POWER_SEQUENCE请求制定三种电源状态,SequenceD1、SequenceD2、SequenceD2。该请求后电源状态保存在Parameters.PowerSequence当中。

10、  IRP_MN_WAIT_WAKE

当PNP管理器发出IRP_MN_WAIT_WAKE请求,指定设备是否具有硬件唤醒特征,这允许它们在外部事件发生时可以唤醒沉睡中的系统。当系统产生唤醒请求时,会调用PoRequestPowerIrp函数发出IRP_MN_WAIT_WAKE请求,其实其他IRP_MN_SET_POWER、IRP_MN_QUERY_POWER等请求也都是由该函数发起,但IRP_MN_POWER_SEQUENCE除外,请求后由USB总线处理。PoRequestPowerIrp函数描述如下;

 

NTKERNELAPI
NTSTATUS
PoRequestPowerIrp (
    IN PDEVICE_OBJECT DeviceObject,
    IN UCHAR MinorFunction,
    IN POWER_STATE PowerState,
    IN PREQUEST_POWER_COMPLETE CompletionFunction,
    IN PVOID Context,
    OUT PIRP *Irp OPTIONAL
    );

Ø       MinorFunction 于指定为IRP_MN_QUERY_POWER,IRP_MN_SET_POWER和IRP_MN_WAIT_WAKE之一。

Ø       PowerState域特地为IRP_MN_SET_POWER和IRP_MN_QUERY_POWER准备,指出需要请求的电源状态

Ø       CompletionFunction域为完成用例函数

Ø       Context域为需要向用例函数传递的参数。

11、 在AddDevice中设置的标志

设备对象中的两个标志位,控制电源管理的各个方面。当你在AddDevice函数中调用了IoCreateDevice例程之后,这两个位都将被置0,你可以根据具体环境设置这两个位。

表3-3-3. DEVICE_OBJECT中的电源管理标志

eg:

deviceObject->Flags |= DO_POWER_PAGABLE;

(1)如果IRP_MJ_POWER请求的派遣函数必须运行在PASSIVE_LEVEL级别,你需要设置DO_POWER_PAGABLE标志。该标志的名称含有相关的含义,因为只有在PASSIVE_LEVEL级上才允许分页操作。如果你把该标志设置为0,则电源管理器可以在DISPATCH_LEVEL级上向你发送电源管理请求。实际上,在当前发行的Windows 2000中总应该这样做。
(2)如果你的设备在上电时需要大电流,设置DO_POWER_INRUSH标志,这可以使多个有这样要求的设备不同时上电。另外,电源管理器会在DISPATCH_LEVEL级上向inrush设备发送电源管理请求,这意味着你不能同时设置DO_POWER_PAGABLE标志。
如果设备的ASL描述中这样指定,那么系统的ACPI过滤器驱动程序将在PDO中自动设置INRUSH标志,这样系统会正确地顺序化有INRUSH标志设备的电源供应,因此你并不需要在自己的设备对象中设置这个标志。但是,如果系统不能自动确定你的设备是否需要inrush对待,你需要自己设置这个标志。
设备的所有设备对象中的PAGABLE和INRUSH标志设置都要一致。如果PDO设置了PAGABLE标志,则其它设备对象也应该设置PAGABLE标志。否则将发生代码为DRIVER_POWER_STATE_FAILURE的bug check(一个PAGABLE设备在一个非PAGABLE设备之上是合法的,但相反则不行)。

如果设备对象设置了INRUSH标志,那么它自己和任何它下面的低级设备对象都不应该设置PAGABLE标志,否则将发生代码为INTERNAL_POWER_ERROR的bug check
如果你正写一个磁盘驱动程序,不要忘了为响应设备分页文件的PnP通知,你可以随时在分页和非分页状态间改变。

12、 设备电源管理流程

设备电源管理代码编写相当复杂,如果处理不当将会带来很严重的错误。在DDK环境下,支持NT和WDM两种驱动模式,电源管理代码需要自己编写进行处理。在微软新推出的WDK环境下,新推出的WDF驱动模式很好的解决了这个问题。微软认为电源管理应该模块化,处理方式基本一致,而且技术不过关的工程师很难将其处理完美,因为微软提供了WDK选择支持电源管理的功能,帮助工程师完成电源管理。但是、我们的USB  驱动采用的是WDM模式。

 

 

 

 

 

 

你可能感兴趣的:(Hibernate,Microsoft,XP,query,extension,DDK)