Android休眠唤醒驱动流程分析(一)

****************************************************
作者:Sean
日期:2012-11-29
修改历史:

****************************************************

标准linux休眠过程:

power management notifiers are executed with PM_SUSPEND_PREPARE 

tasks are frozen

target system sleep state is announced to the platform-handling code

devices are suspended 

platform-specific global suspend preparation methods are executed 

non-boot CPUs are taken off-line

interrupts are disabled on the remaining (main) CPU 

late 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 necessary

early resume of devices is carried out (一般有

platform-specific global resume preparation methods are invoked

devices are woken up

tasks are thawed 

power management notifiers are executed with PM_POST_SUSPEND 

 

用户可以通过sys文件系统控制系统进入休眠一些BUS driver的动作进行)‏

interrupts are enabled on the main CPU 

 non-boot CPUs are enabled 

 

查看系统支持的休眠方式:

#cat /sys/power/state 

常见有standby(suspend to RAM)mem(suspend to RAM)disk(suspend to disk),只是standby耗电更多,返回到正常工作状态的时间更短。

通过 #echo mem /sys/power/state  让系统进入休眠。

#echo on> /sys/power/state 使系统唤醒

 

Android休眠与唤醒

android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:wakelock 、early_suspend与late_resume

wakelockAndroid的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制, 只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。

当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock: main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。

它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。

 

early_suspend:先与linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。

 

本文中,linux kernel版本为 linux-2.6.29android版本为 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.clinux_source/arch/xxx/plat-xxx/pm.c


Android 休眠过程如下:

 

当用户读写/sys/power/state时,linux_source/kernel/power/main.c中的state_store()函数会被调用。其中,androidearly_suspend会执行request_suspend_state(state); 而标准的linux休眠则执行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_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;

 

memchr(buf, '\n', n);

len buf n;

 

 

if (len == && !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)

#ifdef CONFIG_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()函数中。

static DECLARE_WORK(early_suspend_work, early_suspend);

void request_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-d-d d:d:d.lu 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(struct work_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: call handlers\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状态。

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_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) {

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, "

"%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(struct work_struct *work)

{

int ret;

int entry_event_num;

 

if (has_wake_lock(WAKE_LOCK_SUSPEND)) {

if (debug_mask DEBUG_SUSPEND)

pr_info("suspend: abort suspend\n");

return;

}

 

entry_event_num current_event_num;

sys_sync();

if (debug_mask DEBUG_SUSPEND)

pr_info("suspend: enter suspend\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: exit suspend, ret %d "

"(%d-d-d d:d:d.lu 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_suspend returned with no event\n");

wake_lock_timeout(&unknown_wakeup, HZ 2);

}

}

 

 

pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。

int pm_suspend(suspend_state_t state)

{

if (state PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

return enter_state(state);

return -EINVAL;

}

 

enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。

static int enter_state(suspend_state_t state)

{

int error;

 

if (!valid_state(state))

return -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 sleep\n", pm_states[state]);

error suspend_prepare();

if (error)

goto Unlock;

 

if (suspend_test(TEST_FREEZER))

goto Finish;

 

pr_debug("PM: Entering %s sleep\n", pm_states[state]);

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分配一个虚拟终端来输出信息,再广播一个系统进入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 some memory\n");

shrink_all_memory(FREE_PAGE_NUMBER free_pages);

if (nr_free_pages() FREE_PAGE_NUMBER) {

error -ENOMEM;

printk(KERN_ERR "PM: No enough 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值