Windows CE的电源管理之二
OEM Adaptation Layer(OAL)是一层与硬件平台相关的代码,它在电源状态转换中扮演着重要的角色。首先必须实现的是以下几个与硬件相关的函数:
OEMInit:初次上电时(或在冷启后)被调用,一般在这个函数中处理一些重要的初始化工作,如初始化系统内存,建立调试环境,设置系统中断等;
OEMIdle:没有线程可调度运行时被内核调用,这时可将CPU置于低功耗状态,并保证其可以快速返回运行状态;
OEMPowerOff:系统进入休眠(Suspend)状态前被调用,它即是系统进入休眠状态前被调用的最后一个函数,也是在系统被唤醒后所执行的第一个函数(中断处理程序以外);
Interrupt Handler:一些用来唤醒系统的中断处理程序。当中断发生时,内核首先调用中断处理程序,其中一些中断是可以将系统从睡眠状态中唤醒的,但中断时间内能处理的事情非常少,在中断处理程序里大部分API也是不能被调用的。
高级的电源管理功能允许任何设备驱动将其中断作为系统的唤醒源,在版本Windows CE.NET 4.1以前,OAL掌管着所有的中断处理,这就意味着在生成内核镜像以后,我们就无法在系统中添加新的中断处理程序了。现在即使这个设备是在运行时才安装到系统中来的,比如PCMCIA或者CF接口的无线网卡,它可以通过调用API(LoadIntChainHandler和FreeIntChainHandler)实现中断处理程序的安装和卸载。在系统中添加了新的中断处理程序后,它就可以通过内核IOCTL代码(IOCTL_HAL_ENABLE_WAKE)实现其作为系统的唤醒源。
位于操作系统内核层的电源管理策略,也要为设备驱动及上层应用程序提供接口,通过PowerPolicyNotify向电源管理组件发送事件请求,设备及应用程序就可以参与到系统的电源管理策略中来。
2. 设备驱动的电源管理
我们可以为设备驱动添加电源管理功能,首先就是要在驱动程序中添加电源管理的IOCTL代码,然后通知电源管理器该驱动是支持电源管理的,最后在驱动中编码实现该设备支持的几种电源状态。
2.1 建立支持电源管理的设备驱动
为了建立一个能够对设备进行电源管理的驱动程序,我们必须首先建立一个支持non-COM-related设备接口的驱动程序。non-COM-related设备接口标明这个设备是支持电源管理的。可以用以下方式建立这种接口:
l 可以在注册表中,用激活设备所用的IClass值定义接口;
l 可以在驱动程序的Init函数中,设置注册表中的IClass值;
l 可以使用ActivateDeviceEx的参数REGINI设置IClass值;
l 可以在驱动程序中显示地调用AdvertiseInterface函数。
电源管理器通过IOCTL代码来和驱动通信。通常情况下,当一个驱动程序声明为支持电源管理时,驱动只需要在DeviceIoControl中实现电源的管理即可。下面是电源管理器用来与驱动通信的IOCTL代码:
l IOCTL_POWER_CAPABILITIES:代表电源管理器请求设备驱动返回设备支持的电源状态及相关特征;
l IOCTL_POWER_SET:请求驱动更新设备的电源状态;
l IOCTL_POWER_QUERY:电源管理器询问设备是否准备好进行状态切换;
l IOCTL_POWER_GET:请求驱动返回当前设备的电源状态;
l IOCTL_REGISTER_POWER_RELATIONSHIP:通知父设备注册所有它所控制的设备。
其中IOCTL_POWER_CAPABILITIES和IOCTL_POWER_SET是支持电源管理的设备驱动必须实现的。
2.2 IOCTL代码的实现
在设备自举的时候,设备驱动必须使设备处于D0状态,当电源管理器通过IOCTL_POWER_CAPABILITIES向设备发出查询时,设备驱动应该尽可能详细的报告该设备的电源管理能力,以便将自己纳入到系统的电源管理策略中去。在自举完成后,电源管理器可以根据管理策略,调用IOCTL_POWER_SET调整设备的电源状态。在设备自我管理电源的情况下,设备应该通过DevicePowerNotify函数请求系统改变它们的电源状态。在实现对IOCTL_POWER_SET支持时,设备驱动开发应该注意以下几点:
l 设备并不一定具备所有五种设备电源状态,但至少可以工作在D0状态;
l 电源管理器可能会要求设备进入任何设备电源状态,并不仅仅是设备声明支持的几个。
l 如果一个设备被要求进入一个它并不支持的电源状态,它就会进入另一个它支持的更高功耗的状态。例如,一个设备并不支持D2,它会被要求进入D1。
l 电源管理器可能会通过发出IOCTL_POWER_SET,使设备再次进入它已经处于的当前状态。在这种情况下,设备驱动程序简单的返回成功即可。
l 设备的电源状态不一定与系统的电源状态同步,因为它可能受到应用程序需求的限制。
2.3 休眠和唤醒的处理
支持电源管理的流设备驱动通过XXX_PowerDown和XXX_PowerUp接收系统休眠和唤醒的通知,这些通知在内核调用OEMPowerOff之前发出,并处于中断上下文中。
设备驱动的开发者必须清楚,在系统休眠期间,设备应该处于何种状态。并不是所有的设备在这个时候都应该强制被关闭,比如在音频设备可以不依赖处理器来播放音乐时,在系统休眠期间它的状态就应该交给电源管理器和应用程序来处理。而如果这个音频设备的播放需要处理器频繁的工作,在系统进入休眠状态时,驱动程序应该果断的关闭设备的电源,即使该设备由于应用程序的请求不处于D4状态。
另外,设备的D3状态值得特殊考虑,因为这个状态不仅仅与设备的功耗级别相关,处于D3状态的设备还被允许将系统从休眠状态中唤醒。所以当我们开发支持电源管理的设备驱动时需要注意以下几点:
l 支持唤醒系统的设备不应该主动通过DevicePowerNotify请求进入D3状态,因为一般情况下,只有当系统打算进入休眠状态时,电源管理器才将设备作为唤醒源启用。从代码的角度也应该避免这一点,因为驱动程序无法区分来自IOCTL_POWER_SET中对D3状态的请求是由它自己还是电源管理器发起的;
l 如果有必要,能唤醒系统的设备可以定义一样的D2和D3状态,而只有D3具有启动唤醒的功能;
l 支持D3的设备不一定具有将系统从休眠状态唤醒的功能,不能将系统唤醒的设备,但它又具有低功耗模式,是可以自己主动请求进入D3状态的;
l 如果不能唤醒系统的设备处于D3状态,在系统进入休眠状态时,它应该在XXX_PowerDown中将状态转换为D4,并且在XXX_PowerUp中恢复到D3状态;如果无法这么做,它就不应该支持D3状态,而是在请求进入D3时直接进入D4状态;
做到了以上几点,在系统进入休眠状态时,OEM和应用程序开发人员就可以请求将设备进入D3状态,而不必关心这个设备是否支持唤醒系统。
3. 应用程序的电源管理
电源管理组件提供了一组接口供应用程序参与到电源管理的活动中,应用程序可以通过RequestPowerNotifications函数请求电源管理器向其发送电源相关的通知,也可以通过SetPowerRequirement通知电源管理器将设备设置在特殊的电源状态下。这样,指定设备的电源状态就不会随系统电源状态的改变而改变。
3.1 电源通知机制
电源相关的通知通过消息队列传递给应用程序,通常应用程序新建一个消息队列,并通过RequestPowerNotifications将这个消息队列的句柄传递给电源管理器,同时创建一个线程侦听来自这个队列的消息。电源管理器定义了如下几种通知:
l PBT_RESUME:当系统从休眠状态被唤醒是产生;
l PBT_POWERSTATUSCHANGE:当系统接入或者断开外部电源时产生;
l PBT_TRANSITION:当电源管理器执行系统电源状态转换时发生;
l PBT_POWERINFOCHANGE:当电池信息更新时发生。
3.2 电源请求机制
电源请求机制为应用程序提供了强大的能力控制电源管理器调整设备的电源等级,与其他所有的电源设置相比,它具有很高的优先级。举例来说,假设有一个条形码阅读器连接在COM1端口,并且COM1只有在最高电源等级(D0)时才能驱动这个条形码阅读器。为了使其正常工作,应用程序将调用SetPowerRequirement把COM1指定D0状态。假设之后串口驱动自身决定降低一个电源等级,驱动调用DevicePowerNotify通知电源管理器它期望的设备电源状态,驱动程序的这个请求将不起作用,直到应用程序调用ReleasePowerRequirement为止。继续这个例子,假设这时的系统电源状态转换为低能耗等级,虽然与之相关的COM1电源等级为D3,由于应用程序的电源请求,COM1将继续维持在D0状态。
在调用SetPowerRequirement函数时,指定POWER_FORCE标志将强制设备不进入休眠状态,即使这时系统已处于休眠状态。
3.3 设置系统电源状态
在某些应用的场合下,应用程序可能需要改变系统的电源状态。OEM通过注册表定义了系统支持的电源状态, 应用程序可以通过GetSystemPowerState返回当前系统电源状态的名称,也可以通过SetSystemPowerState改变系统的电源状态。
* 原创文章,转载请注明出处