linux 电源管理源码分析3,基于Android的Linux内核的电源管理:Early Suspend

3. 工作流程

首先,我们从kernel/power/wakelock.c中的初始化函数开始:

staticint__init wakelocks_init(void)

{

intret;

inti;

......

for(i = 0; i 

INIT_LIST_HEAD(&active_wake_locks[i]);

......

wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND,"main");

wake_lock(&main_wake_lock);

wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND,"unknown_wakeups");

......

ret = platform_device_register(&power_device);

ret = platform_driver_register(&power_driver);

......

suspend_work_queue = create_singlethread_workqueue("suspend");

......

return0;

}

可以看到,显示初始化active_wake_locks链表数组,然后初始化并且锁住main_wake_lock,注册平台设备power_device,这些数组、锁和power_device我们在后续文章在讨论,这里我们关注的最后一个动作:创建了一个工作队列线程suspend_work_queue,该工作队列是earlysuspend的核心所在。

系统启动完成后,相关的驱动程序通过register_early_suspend()函数注册了early suspend特性,等待一段时间后,如果没有用户活动(例如按键、触控等操作),用户空间的电源管理服务最终会调用第一节提到的set_screen_state()函数,透过sysfs,进而会调用到内核中的state_store():staticssize_t state_store(structkobject *kobj,structkobj_attribute *attr,

constchar*buf,size_tn)

{

#ifdef CONFIG_SUSPEND

#ifdef CONFIG_EARLYSUSPEND

suspend_state_t state = PM_SUSPEND_ON;

#else

suspend_state_t state = PM_SUSPEND_STANDBY;

#endif

constchar*const*s;

#endif

char*p;

intlen;

interror = -EINVAL;

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

len = p ? p - buf : n;

/* First, check if we are requested to hibernate */

if(len == 4 && !strncmp(buf,"disk", len)) {

error = hibernate();

gotoExit;

}

#ifdef CONFIG_SUSPEND

for(s = &pm_states[state]; state 

if(*s && len == strlen(*s) && !strncmp(buf, *s, len))

break;

}

if(state 

#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:

returnerror ? error : n;

}

看到了没,前一篇文章http://www.linuxidc.com/Linux/2011-07/39478.htm说过,suspend to disk做了特殊处理,这里直接比较传入的字符串,而不是使用后续的pm_states数组,这里我不关心suspendto disk,所以略过hibernate的分析。

紧接着,通过pm_states数组,根据命令字符串查询得到请求的状态,默认情况下,Android的内核都会配置了CONFIG_EARLYSUSPEND,所以会调用request_suspend_state()函数,不过在调用该函数之前会先valid_state()一下,��给了平台相关的代码一个机会确认该平台是否支持所请求的电源状态。

voidrequest_suspend_state(suspend_state_t new_state)

{

unsignedlongirqflags;

intold_sleep;

spin_lock_irqsave(&state_lock, irqflags);

old_sleep = state & SUSPEND_REQUESTED;

......

if(!old_sleep && new_state != PM_SUSPEND_ON) {

state |= SUSPEND_REQUESTED;

if(queue_work(suspend_work_queue, &early_suspend_work))

pr_info("early_suspend_work is in queue already\n");

}elseif(old_sleep && new_state == PM_SUSPEND_ON) {

state &= ~SUSPEND_REQUESTED;

wake_lock(&main_wake_lock);

if(!queue_work(suspend_work_queue,&late_resume_work))

pr_info("late_resume_work is in queue already\n");

}

requested_suspend_state = new_state;

spin_unlock_irqrestore(&state_lock, irqflags);

}

还记得前面初始化时建立的工作队列suspend_woek_queue吗?根据之前的电源状态和请求的状态, request_suspend_state()只是简单地向suspend_work_queue中加入early_suspend_work或者是late_resume_work并调度他们执行。early_suspend_work的工作函数是early_suspend():

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

......

}

......

list_for_each_entry(pos,&early_suspend_handlers, link) {

if (pos->suspend != NULL) {

if (debug_mask &DEBUG_SUSPEND)

printk(KERN_DEBUG"pos->suspend: %pF begin\n", pos->suspend);

pos->suspend(pos);

if (debug_mask &DEBUG_SUSPEND)

printk(KERN_DEBUG"pos->suspend: %pF finish\n", pos->suspend);

}

}

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);

}

终于看到啦,early_suspend()遍历early_suspend_handlers链表,从中取出各个驱动程序注册的early_suspend结构,然后调用它的suspend回调函数。最后,释放main_wake_lock锁,至此整个earlysuspend的流程完成。下面的序列图清晰地表明了整个调用的过程:

a1dfb9f08c690a40db4c79919f7fbd73.gif

图3.1  early suspend调用流程

但是,这时整个系统只是出于所谓的idle状态,cpu还在工作,后台进程也在工作中,那什么时候系统会真正地进入睡眠状态?注意到最后一句关键的调用了没有:

wake_unlock(&main_wake_lock);

解锁动作会触发标准linux的suspend流程,这个过程就留给写一篇文章讨论吧。0b1331709591d260c1c78e86d0c51c18.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值