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
error= enter_state(state);§
#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);
}
}
在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。
int pm_suspend(suspend_state_tstate)
{
if (state > PM_SUSPEND_ON &&state <= PM_SUSPEND_MAX)
returnenter_state(state);
return -EINVAL;
}
在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM: Syncingfilesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM: Preparing systemfor %s sleep\n", pm_states[state]);
error =suspend_prepare();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %ssleep\n", pm_states[state]);
error =suspend_devices_and_enter(state);
Finish:
pr_debug("PM: Finishingwakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。
static int suspend_prepare(void)
{
int error;
unsigned int free_pages;
if (!suspend_ops ||!suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error =pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error =usermodehelper_disable();
if (error)
goto Finish;
if(suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
free_pages =global_page_state(NR_FREE_PAGES);
if (free_pages <FREE_PAGE_NUMBER) {
pr_debug("PM: free somememory\n");
shrink_all_memory(FREE_PAGE_NUMBER- free_pages);
if (nr_free_pages() <FREE_PAGE_NUMBER) {
error = -ENOMEM;
printk(KERN_ERR "PM: Noenough memory\n");
}
}
if (!error)
return 0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。
static int try_to_freeze_tasks(boolsig_only)
{
struct task_struct *g, *p;
unsigned long end_time;
unsigned int todo;
struct timeval start, end;
u64 elapsed_csecs64;
unsigned int elapsed_csecs;
unsigned int wakeup = 0;
do_gettimeofday(&start);
end_time = jiffies + TIMEOUT;
do {
todo = 0;
read_lock(&tasklist_lock);
do_each_thread(g,p) {
if (frozen(p) || !freezeable(p))
continue;
if(!freeze_task(p, sig_only))
continue;
/*
* Now that we've doneset_freeze_flag, don't
* perturb a task in TASK_STOPPEDor TASK_TRACED.
* It is "frozen enough". If the task does wake
* up, it will immediately calltry_to_freeze.
*/
if (!task_is_stopped_or_traced(p)&&
!freezer_should_skip(p))
todo++;
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
yield(); /* Yield is okay here*/
if (todo &&has_wake_lock(WAKE_LOCK_SUSPEND)) {
wakeup = 1;
break;
}
if (time_after(jiffies, end_time))
break;
} while (todo);
do_gettimeofday(&end);
elapsed_csecs64 =timeval_to_ns(&end) - timeval_to_ns(&start);
do_div(elapsed_csecs64,NSEC_PER_SEC / 100);
elapsed_csecs = elapsed_csecs64;
if (todo) {
/* This does not unfreezeprocesses that are already frozen
* (we have slightly ugly callingconvention in that respect,
* and caller must callthaw_processes() if something fails),
* but it cleans up leftoverPF_FREEZE requests.
*/
if(wakeup) {
printk("\n");
printk(KERN_ERR "Freezing of%s aborted\n",
sig_only ? "user space ": "tasks ");
}
else {
printk("\n");
printk(KERN_ERR "Freezing oftasks failed after %d.%02d seconds "
"(%d tasks refusing tofreeze):\n",
elapsed_csecs / 100,elapsed_csecs % 100, todo);
show_state();
}
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);
if (freezing(p) &&!freezer_should_skip(p))
printk(KERN_ERR " %s\n",p->comm);
cancel_freezing(p);
task_unlock(p);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
} else {
printk("(elapsed %d.%02dseconds) ", elapsed_csecs / 100,
elapsed_csecs % 100);
}
return todo ? -EBUSY : 0;
}
到现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。
回到enter_state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱动的suspend函数。
当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的PM操作(本文中粉红色的函数,依赖于具体的平台),以s3c6410为例,其注册在linux_source/arch/arm/plat-s3c64xx/pm.c中,只定义了suspend_ops->enter()函数。
static struct platform_suspend_opss3c6410_pm_ops = {
.enter = s3c6410_pm_enter,
.valid = suspend_valid_only_mem,
};
接下来,多CPU中的非启动CPU被关闭。
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin) {
error =suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Somedevices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare) {
error =suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if (suspend_test(TEST_PLATFORM))
goto Finish;
error =disable_nonboot_cpus();
if (!error &&!suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
接下来suspend_enter()被调用,该函数首先关闭IRQ,然后调用device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查,接下来休眠所有的系统设备和总线。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。
static intsuspend_enter(suspend_state_t state)
{
int error = 0;
device_pm_lock();
#ifdef CONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,"performance");
}
#endif /* CONFIG_CPU_FREQ */
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if ((error =device_power_down(PMSG_SUSPEND))){
printk(KERN_ERR "PM: Somedevices failed to power down\n");
goto Done;
}
error =sysdev_suspend(PMSG_SUSPEND);
if (!error) {
if (!suspend_test(TEST_CORE))
error =suspend_ops->enter(state);
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,governor_name);
}
#endif /* CONFIG_CPU_FREQ */
BUG_ON(irqs_disabled());
device_pm_unlock();
return error;
}
在suspend_pos->enter()所对应的函数中,代码最终停止在pm_cpu_sleep();处。
static ints3c6410_pm_enter(suspend_state_t state)
{
……
s3c6410_pm_do_save(gpio_save,ARRAY_SIZE(gpio_save));
s3c6410_pm_do_save(irq_save,ARRAY_SIZE(irq_save));
s3c6410_pm_do_save(core_save,ARRAY_SIZE(core_save));
s3c6410_pm_do_save(sromc_save,ARRAY_SIZE(sromc_save));
/* Clear WAKEUP_STAT register fornext wakeup -jc.lee */
/* If this register do not becleared, Wakeup will be failed */
__raw_writel(__raw_readl(S3C_WAKEUP_STAT),S3C_WAKEUP_STAT);
#ifdef CONFIG_MACH_SMDK6410
/* ALL sub block "ON"before enterring sleep mode - EVT0 bug*/
__raw_writel(0xffffff00,S3C_NORMAL_CFG);
/* Open all clock gate to entersleep mode - EVT0 bug*/
__raw_writel(0xffffffff,S3C_HCLK_GATE);
__raw_writel(0xffffffff,S3C_PCLK_GATE);
__raw_writel(0xffffffff,S3C_SCLK_GATE);
……
/* s3c6410_cpu_save will also actas our return point from when
* we resume as it saves its ownregister state, so use the return
* code to differentiate returnfrom save and return from sleep */
if(s3c6410_cpu_save(regs_save) == 0) {
flush_cache_all();
pm_cpu_sleep();
}
/* restorethe cpu state */
cpu_init();
__raw_writel(s3c_eint_mask_val,S3C_EINT_MASK);
/* restore the system state */
s3c6410_pm_do_restore_core(core_save,ARRAY_SIZE(core_save));
s3c6410_pm_do_restore(sromc_save,ARRAY_SIZE(sromc_save));
……
}
Android唤醒过程如下:
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,以s3c6410为例,即pm.c中的s3c6410_pm_enter()中的cpu_init(),然后执行suspend_enter()的sysdev_resume()函数,唤醒系统设备和总线,使能系统中断。
static intsuspend_enter(suspend_state_t state)
{
int error = 0;
device_pm_lock();
#ifdef CONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,"performance");
}
#endif /* CONFIG_CPU_FREQ */
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if ((error =device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "PM: Somedevices failed to power down\n");
goto Done;
}
error =sysdev_suspend(PMSG_SUSPEND);
if (!error) {
if (!suspend_test(TEST_CORE))
error =suspend_ops->enter(state); //suspend过程完成处
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,governor_name);
}
#endif /* CONFIG_CPU_FREQ */
BUG_ON(irqs_disabled());
device_pm_unlock();
return error;
}
然后回到suspend_devices_and_enter()函数中,使能休眠时候停止掉的非启动CPU,继续唤醒每个设备,使能终端。
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin) {
error =suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Somedevices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare) {
error =suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if (suspend_test(TEST_PLATFORM))
goto Finish;
error = disable_nonboot_cpus();
if (!error &&!suspend_test(TEST_CPUS))
suspend_enter(state); //suspend过程完成处
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM: Syncingfilesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM: Preparing systemfor %s sleep\n", pm_states[state]);
error = suspend_prepare();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %ssleep\n", pm_states[state]);
error =suspend_devices_and_enter(state); //suspend过程完成处
Finish:
pr_debug("PM: Finishingwakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。
static void suspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:
如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写”on”到/sys/power/state来调用lateresume(),执行点亮屏幕等操作。
用户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行lateResume。
当”on”被写入到/sys/power/state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。
staticDECLARE_WORK(late_resume_work, late_resume);
static void late_resume(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 == SUSPENDED)
state &= ~SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume: abort,state %d\n", state);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: callhandlers\n");
list_for_each_entry_reverse(pos,&early_suspend_handlers, link)
if(pos->resume != NULL)
pos->resume(pos);
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume:done\n");
abort:
mutex_unlock(&early_suspend_lock);
}
关于wake_lock
在上文中,已经介绍了wakelock机制,下面从代码的角度进行介绍。
wakelock有3种类型,常用为WAKE_LOCK_SUSPEND,作用是防止系统进入睡眠。其他类型不是很清楚。
enum {
WAKE_LOCK_SUSPEND, /* Preventsuspend */
WAKE_LOCK_IDLE, /* Prevent lowpower idle */
WAKE_LOCK_TYPE_COUNT
};
Wakelock有加锁和解锁2种操作,加锁有2种方式,第一种是永久加锁(wake_lock),这种锁必须手动的解锁;另一种是超时锁(wake_lock_timeout),这种锁在过去指定时间后,会自动解锁。
void wake_lock(struct wake_lock*lock)
{
wake_lock_internal(lock, 0, 0);
}
void wake_lock_timeout(structwake_lock *lock, long timeout)
{
wake_lock_internal(lock, timeout,1);
}
对于wakelock,timeout= has_timeout = 0;直接加锁后,然后退出;
static void wake_lock_internal(
struct wake_lock *lock, longtimeout, int has_timeout)
{
int type;
unsigned long irqflags;
long expire_in;
spin_lock_irqsave(&list_lock,irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
BUG_ON(type >=WAKE_LOCK_TYPE_COUNT);
BUG_ON(!(lock->flags &WAKE_LOCK_INITIALIZED));
#ifdef CONFIG_WAKELOCK_STAT
if (type == WAKE_LOCK_SUSPEND &&wait_for_wakeup) {
if (debug_mask & DEBUG_WAKEUP)
pr_info("wakeup wake lock:%s\n", lock->name);
wait_for_wakeup = 0;
lock->stat.wakeup_count++;
}
if ((lock->flags &WAKE_LOCK_AUTO_EXPIRE) &&
(long)(lock->expires -jiffies) <= 0) {
wake_unlock_stat_locked(lock, 0);
lock->stat.last_time =ktime_get();
}
#endif
if (!(lock->flags &WAKE_LOCK_ACTIVE)) {
lock->flags |=WAKE_LOCK_ACTIVE;
#ifdef CONFIG_WAKELOCK_STAT
lock->stat.last_time =ktime_get();
#endif
}
list_del(&lock->link);
if(has_timeout) {
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type%d, timeout %ld.%03lu\n",
lock->name, type, timeout /HZ,
(timeout % HZ) * MSEC_PER_SEC /HZ);
lock->expires= jiffies + timeout;
lock->flags |=WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link,&active_wake_locks[type]);
} else {
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type%d\n", lock->name, type);
lock->expires = LONG_MAX;
lock->flags &=~WAKE_LOCK_AUTO_EXPIRE;
list_add(&lock->link,&active_wake_locks[type]);
}
if (type == WAKE_LOCK_SUSPEND) {
current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
if (lock == &main_wake_lock)
update_sleep_wait_stats_locked(1);
else if(!wake_lock_active(&main_wake_lock))
update_sleep_wait_stats_locked(0);
#endif
if (has_timeout)
expire_in =has_wake_lock_locked(type);
else
expire_in = -1;
if (expire_in > 0) {
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_lock: %s,start expire timer, "
"%ld\n", lock->name,expire_in);
mod_timer(&expire_timer,jiffies + expire_in);
} else {
if (del_timer(&expire_timer))
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_lock: %s,stop expire timer\n",
lock->name);
if (expire_in == 0)
queue_work(suspend_work_queue,&suspend_work);
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
而对于wake_lock_timeout,在经过timeout时间后,才加锁。再判断当前持有wakelock时,启动另一个定时器,在expire_timer的回调函数中再次判断是否持有wakelock。
static voidexpire_wake_locks(unsigned long data)
{
long has_lock;
unsigned long irqflags;
if (debug_mask & DEBUG_EXPIRE)
pr_info("expire_wake_locks:start\n");
spin_lock_irqsave(&list_lock,irqflags);
if (debug_mask & DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
has_lock =has_wake_lock_locked(WAKE_LOCK_SUSPEND);
if (debug_mask & DEBUG_EXPIRE)
pr_info("expire_wake_locks:done, has_lock %ld\n", has_lock);
if (has_lock== 0)
queue_work(suspend_work_queue,&suspend_work);
spin_unlock_irqrestore(&list_lock,irqflags);
}
static DEFINE_TIMER(expire_timer,expire_wake_locks, 0, 0);
在wakelock中,有2个地方可以让系统从early_suspend进入suspend状态。分别是:
在wake_unlock中,解锁之后,若没有其他的wakelock,则进入suspend。
在超时锁的定时器超时后,定时器的回调函数,会判断有没有其他的wakelock,若没有,则进入suspend。