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);
}
}
在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。