android休眠与唤醒驱动流程分析
标准linux休眠过程:
-
powermanagement notifiers are executed with PM_SUSPEND_PREPARE
-
tasksare frozen
-
targetsystem sleep state is announced to the platform-handling code
-
devicesare suspended
-
platform-specificglobal suspend preparation methods are executed
-
non-bootCPUs are taken off-line
-
interruptsare disabled on the remaining (main) CPU
-
latesuspend of devices is carried out (一般有一些BUSdriver的动作进行)
-
platform-specificglobal methods are invoked to put the system to sleep
标准linux唤醒过程:
-
themain CPU is switched to the appropriate mode, if necessary
-
earlyresume of devices is carried out
(一般有一些BUSdriver的动作进行) -
interruptsare enabled on the main CPU
-
non-bootCPUs are enabled
-
platform-specificglobal resume preparation methods are invoked
-
devicesare woken up
-
tasksare thawed
-
powermanagement notifiers are executed with PM_POST_SUSPEND
用户可以通过sys文件系统控制系统进入休眠:
查看系统支持的休眠方式:
#cat/sys/power/state
常见有standby(suspendto RAM)、mem(suspend toRAM)和disk(suspend todisk),只是standby耗电更多,返回到正常工作状态的时间更短。
通过 #echomem > /sys/power/state
让系统进入休眠。
Android休眠与唤醒
android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:wakelock、early_suspend与late_resume。
wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。
当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,在android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock:main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。
它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。
early_suspend:先与linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。
本文中,linuxkernel版本为
linux-2.6.29,android版本为android 2.1
与android休眠唤醒主要相关的文件主要有:
-
linux_source/kernel/power/main.c
-
linux_source/kernel/power/earlysuspend.c
-
linux_source/kernel/power/wakelock.c
-
linux_source/kernel/power/process.c
-
linux_source/driver/base/power/main.c
-
linux_source/arch/xxx/mach-xxx/pm.c或linux_source/arch/xxx/plat-xxx/pm.c
Android休眠过程如下:
当用户读写/sys/power/state时,linux_source/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_suspend会执行request_suspend_state(state);而标准的linux休眠则执行error=
enter_state(state);
static ssize_t state_store(structkobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state =PM_SUSPEND_ON;
#else
suspend_state_t state =PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we are requestedto hibernate */
if (len == 4 &&!strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state];state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len ==strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX &&*s)
#ifdefCONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON ||valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
#endif
#endif
Exit:
return error ? error : n;
}
在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。
staticDECLARE_WORK(early_suspend_work, early_suspend);
voidrequest_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock,irqflags);
old_sleep = state &SUSPEND_REQUESTED;
if (debug_mask &DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("request_suspend_state:%s (%d->%d) at %lld "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n",
new_state != PM_SUSPEND_ON ?"sleep" : "wakeup",
requested_suspend_state,new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900, tm.tm_mon + 1,tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,ts.tv_nsec);
}
if (!old_sleep && new_state!= PM_SUSPEND_ON) {
state |= SUSPEND_REQUESTED;
queue_work(suspend_work_queue,&early_suspend_work);
} else if (old_sleep &&new_state == PM_SUSPEND_ON) {
state &= ~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue,&late_resume_work);§
}
requested_suspend_state =new_state;
spin_unlock_irqrestore(&state_lock,irqflags);
}
在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock。
static void early_suspend(structwork_struct *work)
{
struct early_suspend *pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if (state ==SUSPEND_REQUESTED)
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: callhandlers\n");
list_for_each_entry(pos,&early_suspend_handlers, link) {
if(pos->suspend != NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend:sync\n");
sys_sync();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if (state ==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
在wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。
staticDECLARE_WORK(suspend_work, suspend);
void wake_unlock(struct wake_lock*lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock,irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
#ifdef CONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock, 0);
#endif
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_unlock: %s\n",lock->name);
lock->flags &=~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link,&inactive_locks);
if (type == WAKE_LOCK_SUSPEND) {
longhas_lock = has_wake_lock_locked(type);
if (has_lock > 0) {
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock: %s,start expire timer, "
"%ld\n", lock->name,has_lock);
mod_timer(&expire_timer,jiffies + has_lock);
} else {
if (del_timer(&expire_timer))
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock: %s,stop expire "
"timer\n",lock->name);
if(has_lock == 0)
queue_work(suspend_work_queue,&suspend_work);
}
if (lock == &main_wake_lock) {
if (debug_mask &DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
#ifdef CONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。
static void suspend(structwork_struct *work)
{
int ret;
int entry_event_num;
if(has_wake_lock(WAKE_LOCK_SUSPEND)) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend: abortsuspend\n");
return;
}
entry_event_num =current_event_num;
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: entersuspend\n");
ret =pm_suspend(requested_suspend_state);
if (debug_mask &DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("suspend: exitsuspend, ret = %d "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n", ret,
tm.tm_year + 1900, tm.tm_mon + 1,tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,ts.tv_nsec);
}
if (current_event_num ==entry_event_num) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend: pm_suspendreturned with no event\n");
wake_lock_timeout(&unknown_wakeup,HZ / 2);
}