WM/WINCE代码研读系列之 Power Management(1)

本文对Power Management这部分代码的研究是基于Wince5.0的(注:在最新的Wince 6.0上对电源管理的架构做了较大改变)。
这部分的代码在/PUBLIC/COMMON/OAK/DRIVERS/PM下,在OS中以PM.dll的形式存在。


一、PowerManagement Architecture

在/PUBLIC/COMMON/OAK/DRIVERS/PM下的代码有两套电源管理机制:
一种是Minimal的电源管理架构,在/PUBLIC/COMMON/OAK/DRIVERS/PM/PMSTUBS/下,用SYSGEN_PMSTUBS环境变量去使能这个架构;在这种架构下只支持消息接口类的电源管理API即PmRequestPowerNotifications和PmStopPowerNotifications,且PmSetSystemPowerState里面只做了Suspended/Resuming的简单处理。
另一种是Full的电源管理架构,在/PUBLIC/COMMON/OAK/DRIVERS/PM/下的MDD和PDD目录,用SYSGEN_PM环境变量去使能这个架构;在这种架构下支持所有类型的电源管理API。在Full的电源管理架构中,又分两个子类DEFAULT和PDA。在用SYSGEN_PM环境变量启用这个架构后默认使用DEFAULT,用SYSGEN_PM_PDA可以使能PDA这个子类。
在Windows Mobile 6(wince 5.0核心)中,无论是Windows Mobile 6 Professional/ Windows Mobile 6 Classic (在/PUBLIC/WPC/OAK/MISC/wpc.bat)还是Windows Mobile 6 Standard(在/PUBLIC/SMARTFON/OAK/MISC/smartfon.bat)都设置了SYSGEN_PM = 1,SYSGEN_PM_PDA = 1;
下面我将详细讨论PDA子类。


系统电源状态共有九种:
On,UserIdle,BacklightOff,ScreenOff,Unattended,Resuming,Suspended,ColdReboot,Reboot.
ColdReboot,Reboot这两个状态不能在电源管理的状态机中自由迁移,它们只能通过调用SetSystemPowerState来进入。

此外,Windows Mobile 6 Professional和Windows Mobile 6 Classic (即PPC)在/PUBLIC/WPC/OAK/FILES/下的project.reg里面做了如下设置

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power/Timeouts]

"ACUserIdle"=dword:0                        ; in seconds

"BattUserIdle"=dword:0                      ; in seconds

"BattSuspendTimeout"=dword:3c       ; in seconds

这样就把自动超时进入UserIdle状态的方式给屏蔽了,无法在PPC里面自动超时迁移到UserIdle这个电源状态。

Windows Mobile 6 Standard(即SmartPhone)在/PUBLIC/SMARTFON/OAK/FILES/下的project.reg里面做了如下设置

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power/Timeouts]

"ACSuspendTimeout"=dword:0         ; in seconds

"BattSuspendTimeout"=dword:0       ; in seconds

这样就把自动超时进入Suspended状态的方式给屏蔽了,无法在SmartPhone里面自动超时迁移到Suspended这个电源状态。

根据文档:Windows Mobile 6 Professional和Windows Mobile 6 Classic (即PPC) 不支持UserIdle这个状态,Windows Mobile 6 Standard(即SmartPhone)不支持Unattended,Resuming,Suspended三个状态。


影响系统电源状态变迁的事件共有以下十一种:

Wake source event:
这是OEM自己定义的唤醒事件,它们都是特定选择的一些硬件中断(例如USB线或是充电器的插入拔出中断,Baseband的中断,键盘中断,闹钟中断等)。在PPC中这些唤醒事件用来把系统从Suspended状态变到Resuming状态。在SmartPhone中没有Suspended状态,相应的低功耗模式是进入OEMIdle()。在有些特定硬件平台中,为了降低功耗往往在OEMIdle()里将CPU配置进低功耗模式或者Stop掉。例如PXA27X,PXA3XX的SmartPhone平台下,在OEMIdle()里让CPU进Standby模式,把一些硬件中断配置为Wake source event以唤醒CPU并退出OEMIdle()。在PPC里进入Suspended状态或是关机都会调用OEMPowerOff(),但在SmartPhone里OEMPowerOff()只在关机时被调用。

On/off event:
Power Button被按下。这个事件只在PPC里有效。
在Resuming,Unattended,ScreenOff状态下,按下Power Button将系统迁移到On状态。在On,BacklightOff,UserIdle状态下,按下Power Button将系统迁移到Unattended状态。

On event:
Application Buttons被按下。这个事件也只在PPC里有效。
在On,BacklightOff,UserIdle,Resuming,Unattended,ScreenOff状态下,按下Application Buttons都会将系统迁移到On状态。

Enter unattended:
调用PowerPolicyNotify(PPN_UNATTENDEDMODE,TRUE)让系统进入unattended模式。这时候m_dwUnattendedModeRef加一。
Leave unattended:
调用PowerPolicyNotify(PPN_UNATTENDEDMODE,FALSE)让系统离开unattended模式。这时候m_dwUnattendedModeRef加一。
任何状态要迁移到Suspended状态肯定会先迁移unattended状态。如果m_dwUnattendedModeRef为0则直接进入Suspended状态。不为0时会将SystemIdleTimeout定时器复位,在SystemIdle Timeout后才会进入Suspended状态。

下面接下来是四个Timeout事件:

15-second timeout(即ResumingSuspendTimeout):
15S超时其实是代码中的ResumingSuspendTimeout,这是专门用于Resuming状态的。
当系统从Suspended唤醒到Resuming状态时,如果15s内没有人系统迁移出这状态,则系统电源状态就会变到Unattended状态。
这个15s的时间是系统默认的(在PUBLIC/COMMON/OAK/DRIVERS/PM/PDD/PDA/下的pwstates.h定义的),但它也是可以被改变的。
修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("Batt
ResumingSuspendTimeout")的值可以改变没有使用外部电源时的超时时间。修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("ACResumingSuspendTimeout")的值可以改变使用外部电源时的超时时间。

SystemIdle Timeout(即SuspendTimeout):
系统自动进入Suspended状态的超时时间,用户可以通过控制面板去改变它。在On,BacklightOff,UserIdle,ScreenOff状态下SystemIdle Timeout会让系统进入Unattended状态,在Unattended状态下SystemIdle Timeout会让系统进入Suspended状态。
这个SystemIdle Timeout时间也是可以通过修改注册表去改变的。这个时间分两个,在不使用外部电源时是m_dwBattSuspendTimeout,使用外部电源时是m_dwACSuspendTimeout。
从代码上看,m_dwBattSuspendTimeout的默认值是300s,m_dwACSuspendTimeout的默认值是600s.然后在/PUBLIC/WPC/OAK/FILES/下(适用于PPC)或是/PUBLIC/SMARTFON/OAK/FILES/(适用于SmartPhone)的project.reg里对此又作了设置修改。修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattSuspendTimeout")的值可以改变m_dwBattSuspendTimeout。修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("ACSuspendTimeout")的值可以改变m_dwACSuspendTimeout。

Backlight Timeout:
系统自动进入BacklightOff状态的超时时间。在On状态下Backlight Timeout会让系统进入BacklightOff状态,在BacklightOff,UserIdle,ScreenOff,Unattended状态下会屏蔽掉Backlight Timeout。
同样这个时间分两个:在不使用外部电源时是m_dwBattBacklightTimeout,默认是30s,使用外部电源时是m_dwACBacklightTimeout,默认是60s。用户同样可以通过控制面板去改变它。
这个Backlight Timeout时间也是可以通过修改注册表去改变的。
修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattBacklightTimeout")的值和TEXT("HKEY_CURRENT_USER//ControlPanel//Backlight")下面的_T("BatteryTimeout")的值中的任何一个都可以改变m_dwBattBacklightTimeout。
修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("ACBacklightTimeout")的值和TEXT("HKEY_CURRENT_USER//ControlPanel//Backlight")下面的_T("ACTimeout")的值中的任何一个都可以改变m_dwACBacklightTimeout。

注意的是:为了避免冲突,我们修改Backlight Timeout时,上面的两组注册表值最好只用一组,就是要么用TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattBacklightTimeout")和_T("ACBacklightTimeout")要么就用TEXT("HKEY_CURRENT_USER//ControlPanel//Backlight")下面的_T("BatteryTimeout")和_T("ACTimeout")。
如果两组同时用的话,根据pwstates.cpp文件里PowerStateManager::PlatformLoadTimeouts里的代码(先读取TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattBacklightTimeout")和_T("ACBacklightTimeout")后读取TEXT("HKEY_CURRENT_USER//ControlPanel//Backlight")下面的_T("BatteryTimeout")和_T("ACTimeout")),会以后一组的注册表值为实际值。

User Timeout:
这个超时机制只在Windows Mobile 6 Standard设备中用。在Windows Mobile 6 Professional 和Windows Mobile 6 Classic 设备没有UserIdle这个状态,也就没有使用这个超时机制。
在On,BacklightOff状态下UserTimeout会让系统进入UserIdle状态,在UserIdle,ScreenOff,Unattended状态下会屏蔽掉User Timeout。
在Windows Mobile 6 Standard设备中,这个时间也分两个m_dwBattUserIdleTimeout和m_dwACUserIdleTimeout。
修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattUserIdle")的值和TEXT("HKEY_CURRENT_USER//ControlPanel//Power")下面的_T("Display")的值中的任何一个都可以改变m_dwBattUserIdleTimeout。
修改TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("ACUserIdle")的值和TEXT("HKEY_CURRENT_USER//ControlPanel//Power")下面的_T("Display")的值中的任何一个都可以改变m_dwACUserIdleTimeout。

和Backlight Timeout一样,不要同时使用TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//Timeouts")下面的_T("BattUserIdle")和_T("ACUserIdle")与TEXT("HKEY_CURRENT_USER//ControlPanel//Power")下面的_T("Display")这两组值,否则会以后一组的注册表值为实际值。

User Active事件:
当有任何用户操作时激活此事件。
在BacklightOff,UserIdle状态下User Active事件会让系统进入On状态,在On,ScreenOff,Unattended状态下对User Active事件不做处理。
在HKEY_LOCAL_MACHINE/System/GWE/ActivityEvent下的键值就是当GWES发现有任何用户操作时要发送的事件--PowerManager/ActivityTimer/UserActivity。在pmtimer.cpp文件里的ActivityTimersThreadProc()函数里根据在超时时间内是否有PowerManager/ActivityTimer/UserActivity事件判断当前是UserActivity状态还是UserInactivity状态,并且分别再发PowerManager/UserActivity_Active和PowerManager/UserActivity_Inactive事件。
对应的ActivityTimers的注册表在TEXT("HKEY_LOCAL_MACHINE//SYSTEM//CurrentControlSet//Control//Power//ActivityTimers")下。在PPC里这项注册表下只有_T("UserActivity")这个子项,这个子项下只有_T("TimeoutMs")=100值。也就是说从UserActivity状态转换到UserInactivity状态的超时时间是100ms,这样实际上PM模块就会忽略掉这里的PowerManager/UserActivity_Inactive事件,只会在GWE发PowerManager/ActivityTimer/UserActivity事件时通知PM当前是User Active事件。

SystemIdleTimerReset:
复位SystemIdleTimer,调用SystemIdleTimerReset()函数会发送_T("PowerManager/SystemIdleTimerReset")事件。此时PM在pwstates.cpp文件的PowerState::WaitForEvent()函数里会调用m_pPwrStateMgr->ResetSystemIdleTimeTimeout(TRUE);platEvent = SystemActivity;这样将m_dwCurSuspendTimeout复位并且platEvent = SystemActivity时系统电源状态不做迁移。

Power Manager API(SetSystemPowerState):
这里的PowerManager API特指能直接改变系统电源状态的SetSystemPowerState。
SetSystemPowerState的调用路径是PmSetSystemPowerState->PlatformSendSystemPowerState->g_pPowerStateManager->SendSystemPowerState.
先判断要设置的系统电源状态是否可以被SetSystemPowerState直接设置(调用AppsCanRequestState),其中On,UserIdle,Suspended,ScreenOff,ColdReboot,Reboot可以被设置,BacklightOff,Resuming,Unattended不可以被设置。其次依次调用pNewPowerState->EnterState();pNewPowerState = SetSystemState(pNewPowerState );m_pCurPowerState = pNewPowerState;SetEvent(m_hSystemApiCalled);将函数传入的状态设置为当前系统电源状态。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值