版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、android休眠与唤醒驱动流程分析标准linux休眠过程:power management notifiers are executed with PM_SUSPEND_PREPAREtasks are frozentarget system sleep state is announced to the platform-handling codedevices are suspendedplatform-specific global suspend preparation methods are executednon-boot CPUs are taken off-lineinter
2、rupts are disabled on the remaining (main) CPUlate suspend of devices is carried out (一般有一些 BUS driver 的动作进行)? platform-specific global methods are invoked to put the system to sleep标准linux唤醒过程:the main CPU is switched to the appropriate mode, if necessaryearly resume of devices is carried out ( 一般有
3、一些 BUS driver 的动作进行)?interrupts are enabled on the main CPUnon-boot CPUs are enabledplatform-specific global resume preparation methods are invokeddevices are woken uptasks are thawedpower management notifiers are executed with PM_POST_SUSPEND用户可以通过sys文件系统控制系统进入休眠:查看系统支持的休眠方式:#cat /sys/power/state常见
4、有 standby(suspend to RAM)、mem(suspend to RAM)和 disk(suspend to disk),只是 standby耗电更多,返回到正常工作状态的时间更短。通过 #echo mem > /sys/power/state 让系统进入休眠。Android休眠与唤醒android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出 的一套电源管理系统,其核心内容有: wakelock、early_suspend与late_resume。wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的
5、机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是 有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时 了,内核就会启动休眠的那套机制来进入休眠。当系统在启动完毕后,会自己去加一把名为“ main '的锁,而当系统有意愿去睡眠时则会 先去释放这把"main"锁,在 android中,在early_suspend的最后一步会去释放" main"锁 (wake_unlock: main )。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。它的缺点是,如果有某一应用获
6、锁而不释放或者因一直在执行某种操作而没时间来释放 的话,则会导致系统一直进入不了睡眠状态,功耗过大。early_suspend:先与linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操 作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上 会首先调用这些注册的函数。early_suspend函数,当系统要进入睡眠之前本文中,linux kernel 版本为 linux-2.6.29 , android 版本为 android 2.1与android休眠唤醒主要相关的文件主要有:linux_source
7、/kernel/power/main.clinux_source/kernel/power/earlysuspend.clinux_source/kernel/power/wakelock.clinux_source/kernel/power/process.clinux_source/driver/base/power/main.clinux_source/arch/xxx/mach-xxx/pm.c 或 linux_source/arch/xxx/plat-xxx/pm.cAndroid休眠过程如下:当用户读写 /sys/power/state 时,linux_source/kernel/
8、power/main.c 中的 state_store()函数会被 调用。其中, android 的 early_suspend 会执行 request_suspend_state(state);而标准的 linux 休眠 贝U执行 error = enter_state(state);static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)一#ifdef CONFIG_SUSPEND#ifdef CONFIG_EARL YSUSPENDsusp
9、end_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *p;int len;int error = -EINV AL;p = memchr(buf, 'n', n);len = p ? p - buf : n;/* First, check if we are requested to hibernate */if (len = 4 && !strncmp(buf, "
10、;disk", len) error = hibernate();goto Exit;#ifdef CONFIG_SUSPENDfor (s = &pm_statesstate; state < PM_SUSPEND_MAX; s+, state+) if (*s && len = strlen(*s) && !strncmp(buf, *s, len) break;if (state < PM_SUSPEND_MAX && *s)#ifdef CONFIG_EARL YSUSPENDif (state = PM_S
11、USPEND_ON | valid_state(state) error = 0;request_suspend_state(state);一 一#elseerror = enter_state(state);#endif在request_suspend_state(state网数中,会调用 early_suspend_work 的工作队歹U,从而进入 early_suspend()函数中。static DECLARE_WORK(early_suspend_work, early_suspend);void request_suspend_state(suspend_state_t new_s
12、tate)-一一 一unsigned long irqflags;int old_sleep;spin_lock_irqsave(&state_lock, irqflags);old_sleep"= state & SUSPEND_REQUESTED;if (debug_mask & DEBUG_UsEr_STA TE) struct timespec ts;struct rtc_time tm;getnstimeofday(&ts);rtc_time_to_tm(ts.tv_sec, &tm);pr_info("request_su
13、spend_state: %s (%d->%d) at %lld "一 ”(%d-%02M%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.t
14、v_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, &am
15、p;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(struct work_struct *work)一一struct early_suspend
16、 *pos;unsigned long irqflags;int abort = 0;mutex_lock(&early_suspend_lock);spin_lock_irqsave(&state_lock, irqflags);if (state = SUSPEND_REQUESTED)state |= SUSPENDED;elseabort = 1;spin_unlock_irqrestore(&state_lock, irqflags);if (abort) (if (debug_mask & DEBUG_SUSPEND)pr_info("ea
17、rly_suspend: abort, state %dn”, state);mutex_unlock(&early_suspend_lock);goto abort;if (debug_mask & DEBUG_SUSPEND)pr_info("early_suspend: call handlersn");list_for_each_entry(pos, &early_suspend_handlers, link) (if (pos->suspend != NULL) pos->suspend(pos);mutex_unlock(&a
18、mp;early_suspend_lock);if (debug_mask & DEBUG_SUSPEND) pr_info("early_suspend: syncn");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 u
19、nlock()中,删除链表中 wake_lock节点,判断当前是否存在wake_lock ,若wake_lock的数目为 0,则调用工作队列 suspend_work,进入suspend状态。static DECLARE_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_LOC
20、K_TYPE_MASK;#ifdef CONFIG_WAKELOCK_STA T wake_unlock_stat_locked(lock, 0);#endifif (debug_mask & DEBUG_WAKE_LOCK)pr_info("wake_unlock: %sn", lock->name);lock->flags &= (W AKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);list_del(&lock->link);list_add(&lock->link, &i
21、nactive_locks);if (type = WAKE_LOCK_SUSPEND) (long has_lock = has_wake_lock_locked(type);if (has_lock > 0) (if(debug_mask & DEBUG_EXPIRE)pr_info("wake_unlock: %s, start expire timer,""%ldn", lock->name, has_lock);mod_timer(&expire_timer, jiffies + has_lock); else (i
22、f (del_timer(&expire_timer)if(debug_mask"& DEBUG_EXPIRE)pr_info("wake_unlock: %s, stop expire ""timern", lock->name);if (has_lock = 0)queue_work(suspend_work_queue, &suspend_work);-一if (lock = &main_wake_lock) (if (debug_mask & DEBUG_SUSPEND)print_acti
23、ve_locks(WAKE_LOCK_SUSPEND);#ifdef CONFIG_WAKELOCK_STA Tupdate_sleep_wait_stats_locked(0);#endifspin_unlock_irqrestore(&list_lock, irqflags);-一在suspend()函数中,先判断当前是否有wake_lock ,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。static void suspend(struct work_struct *work)一int ret;int entry_event_num;if (has_wak
24、e_lock(W AKE_LOCK_SUSPEND) if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: abort suspendn");return;entry_event_num = current_event_num;sys_sync();if (dbug_mask & DEBUG_SUSPEND)pr_info("suspend: enter suspendn");ret = pm_suspend(requested_suspend_state);if (debug_mask &a
25、mp; DEBUG_EXIT_SUSPEND) struct timespec ts;struct rtc_time tm;getnstimeofday(&ts);rtc_time_to_tm(ts.tv_sec, &tm);pr_info("suspend: exit suspend, 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_se
26、c, ts.tv_nsec);-if (current_event_num = entry_event_num) if (debug_mask & DEBuG_SUSPEND)pr_info("suspend: pm_suspend returned with no eventn");wake_lock_timeout(&unknown_wakeup, HZ / 2);一一一在pm_suspend()函数中,enter_state()函数被调用,从而进入 标准linux休眠过程。int pm_suspend(suspend_state_t state) 一一
27、 if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)return enter_state(state);return -EINV AL;在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用 suspend_prepare(谦冻结进程,最后调用suspend_devices_and_enter()让夕卜设进入休眠。static int enter_state(suspend_state_t state)(int error;if (!valid_state(state)return
28、-ENODEV;if (!mutex_trylock(&pm_mutex)return -EBUSY; 一printk(KERN_INFO "PM: Syncing filesystems .");sys_sync();printk("done.n");pr_debug("PM: Preparing system for %s sleepn", pm_statesstate);error = suspend_prepare();if (error)goto Unlock;if (suspend_test(TEST_FREEZE
29、R)goto Finish;pr_debug("PM: Entering %s sleepn", pm_statesstate);error = suspend_devices_and_enter(state);Finish:pr_debug("PM: Finishing wakeup.n");suspend_finish();Unlock:mutex_unlock(&pm_mutex);return error;在suspend_prepare()函数中,先通过 pm_prepare_console();给 suspend分酉己个虚拟终端来 输
30、出信息,再广播一个系统进入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 Fi
31、nish;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 some memoryn");shrink_all_memory(FREE_PAGE_NUMBER - free_pages);if (n
32、r_free_pages() < FREE_PAGE_NUMBER) 一error = -ENOMEM;printk(KERN_ERR "PM: No enough memoryn");一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(阀数中调用了 free
33、ze_processes()函数,而 freeze_processes() 函数中又调用了 try_to_freeze_tasks ()来完成冻结任务。在冻结过程中,会判断当前进程是否有 wake_lock ,若有,则冻结失败,函数会放弃冻结。static int try_to_freeze_tasks(bool sig_only)- -struct task_struct *g, *p;unsigned long end_time;unsigned int todo;struct timeval start, end;u64 elapsed_csecs64;unsigned int elap
34、sed_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 done set_freeze_flag, don't* perturb a task
35、 in TASK_STOPPED or TASK_TRACED.* It is "frozen enough". If the task does wake* up, it will immediately call try_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
36、(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
37、) /* This does not unfreeze processes that are already frozen* (we have slightly ugly calling convention in that respect,* and caller must call thaw_processes() if something fails),* but it cleans up leftover PF_FREEZE requests.*/一if(wakeup) printk("n");printk(KERN_ERR "Freezing of %s
38、 abortedn",sig_only ? "user space ”: "tasks ");一else printk("n");printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds ""(%d tasks refusing to freeze):n",elapsed_csecs / 100, elapsed_csecs % 100, todo);show_state(); 一read_lock(&tasklist
39、_lock);do_each_thread(g, p) task_lock(p);if (freezing(p) && !freezer_should_skip(p) printk(KERN_ERR "%sn", p->comm);cancel_freezing(p);task_unlock(p); while_each_thread(g, p);read_unlock(&tasklist_lock); else printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100
40、,elapsed_csecs % 100);return todo ? -EBUSY : 0; 到现在,所有的进程(也包括 workqueue/kthread)都已经停止了,内核态进程有可能在停 止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设suspend。函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。回到enter state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数
41、让外设进入休眠。该函数中,首先休眠串口(之后不能再显示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()函数。
42、static struct platform_suspend_ops s3c6410_pm_ops = .enter= s3c6410_pm_enter,.valid= suspend_valid_only_mem,;一一一接下来,多CPU中的非启动CPU被关闭。int suspend_devices_and_enter(suspend_state_t state)-一一int error;if (!suspend_ops)return -ENOSYS;if (suspend_ops->begin) error = suspend_ops->begin(state);if (err
43、or)goto Close;suspend_console();suspend_test_start();error = device_suspend(PMSG_SUSPEND);if (error) printk(KERN_ERR "PM: Some devices failed to suspendn");goto Recover_platform;一suspend_test_finish("suspend devices");if (suspend_test(TEST_DEVICES)goto Recover_platform;if (suspen
44、d_ops->prepare) error = suspend_ops->prepare();if (error)goto Resume_devices;一if (suspend_test(TEST_PLA TFORM)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->fi
45、nish();Resume_devices:suspend_test_start();device_resume(PMSG_RESUME);suspend_test_finish("resume devices");resume_console();Close:if (suspend_ops->end)suspend_ops->end();return error;Recover_platform:if (suspend_ops->recover)suspend_ops->recover();goto Resume_devices;一接下来suspe
46、nd_enter(胰调用,该函数首先关闭IRQ,然后调用device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查,接下来休眠所有的系统设备和总线。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。static int suspend_enter(suspend_state_t state)一一一int error = 0;device_pm_lock();#ifdef cOnFG_CPU_FREQcpufreq_get
47、_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)
48、 printk(KERN_ERR "PM: Some devices failed to power downn");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();#ifdefCONFIG_CPU_FREQif(str
49、nicmp(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 int s3c6410_pm_enter(suspend_state_t state)一一一 s3c6410_
50、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 for next wakeup -jc.lee */* If this register do not be cleared, Wakeup
51、 will be failed */_raw_writel(_raw_readl(S3C_W AKEUP_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 enter sleep mode - EVT0 bug*/_raw_writel(0xffffffff, S3C_
52、HCLK_GATE);_raw_writel(0xffffffff, S3C_PCLK_GATE);_raw_writel(0xffffffff, S3C_SCLK_GATE);/* s3c6410_cpu_save will also act as our return point from when* we resume as it saves its own register state, so use the return* code to differentiate return from save and return from sleep */if (s3c6410_cpu_sa
53、ve(regs_save) = 0) flush_cache_all();pm_cpu_sleep(); 一 一/* restore the 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唤醒过程如
54、下:如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从 suspend完成的地方开始执行,以 s3c6410为例,即 pm.c 中的 s3c6410_pm_enter()中的 cpu_init(),然后执行 suspend_enter() 的sysdev_resume()函数,唤醒系统设备和总线,使能系统中断。static int suspend_enter(suspend_state_t state)一一一int error = 0;device_pm_lock();#ifdef CONFIG_CPU_FREQcpufreq_get_cpufreq_name(0);strcpy(gov
55、ernor_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:
56、Some devices failed to power downn");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();#ifdefCONFIG_CPU_FREQif(strnicmp(govern
57、or_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,继续唤醒每个设备,使能终端。int suspend_devices_and_enter(suspend_state_t state)-一一int error
58、;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: Some devices failed to suspendn");goto Recover_platform;一suspend_test_finish("suspend devices");if (suspend_test(TEST_DEVICES)goto Recover_platform;if
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论