Android的Linux内核的电源管理:Early Suspend

来源:互联网 发布:淘宝扫码让别人登录 编辑:程序博客网 时间:2024/05/13 14:23

1. 用户空间的接口
        在kernel/power/main.c中,定义了一组sysfs的属性文件,其中一个定义是:
        power_attr(state);

        把这个宏展开后:

?
  
?
<span style="color: #0000ff;">staticstruct kobj_attribute state_attr = { \
.attr ={ \
.name = "state", \
.mode = 0644, \
}, \
.show =state_show, \
.store =state_store, \
}
</span>

 

我们再看看main.c的入口:

?
<span style="color: #0000ff;">staticint __init pm_init(void){
power_kobj =kobject_create_and_add("power", NULL);
if(!power_kobj)
return-ENOMEM;
returnsysfs_create_group(power_kobj,&attr_group);
}
</span>

 

显然,该函数执行后,会在生成/sys/power目录,该目录下会建立一系列属性文件,其中一个是/sys/power/state文件。用户空间向该文件的写入将会导致state_store被调用,读取该文件将会导致state_show函数被调用。

        现在回到Android的HAL层中,查看一下代码:hardware/libhardware_legacy/power/power.c:

?
<span style="color: #0000ff;">//定义写入/sys/power/state的命令字符串
 
staticconst char *off_state = "mem";
staticconst char *on_state = "on";
 
//打开/sys/power/state等属性文件,保存相应的文件描述符
staticint
open_file_descriptors(constchar * constpaths[]){
 
inti;
for(i=0; i
 
//最终,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。
<pre lang="c"line="1">int
set_screen_state(inton){
 
initialize_fds();
charbuf[32];
intlen;
if(on)
len = snprintf(buf, sizeof(buf),"%s", on_state);
else
len = snprintf(buf, sizeof(buf),"%s", off_state);
buf[sizeof(buf) - 1] = '\0';
len = write(g_fds[REQUEST_STATE], buf,len);
return0;
}
</span>

 

 

2. 内核中数据结构和接口
        与earlysuspend相关的数据结构和接口都在earlysuspend.h中进行了定义。
        - early_suspend 结构

?
<span style="color: #0000ff;">structearly_suspend {
 
#ifdef CONFIG_HAS_EARLYSUSPEND
 
structlist_head link;
intlevel;
void(*suspend)(structearly_suspend *h);
void(*resume)(structearly_suspend *h);
 
#endif
};
</span>

 

希望执行early suspend的设备,他的设备驱动程序需要向电源管理系统注册,该结构体用于向电源管理系统注册earlysuspend/lateresume,当电源管理系统启动suspend流程时,回调函数suspend会被调用,相反,resume的最后阶段,回调函数resume会被调用,level字段用于调整该结构体在注册链表中的位置,suspend时,level的数值越小,回调函数的被调用的时间越早,resume时则反过来。Android预先定义了3个level等级:

?
<span style="color: #0000ff;">enum{
EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
};
</span>

 

 如果你想你的设备在FB设备被禁止之前执行他的early suspend回调,设备驱动程序应该把level值设定为小于150的某个数值,然后向系统注册early_suspend结构。注册和反注册函数是:

?
<span style="color: #0000ff;">voidregister_early_suspend(structearly_suspend *handler);
voidunregister_early_suspend(structearly_suspend *handler);
</span>

 

 early_suspend_handlers链表
        所有注册到系统中的early_suspend结构都会按level值按顺序加入到全局链表early_suspend_handlers中。

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

?
<span style="color: #0000ff;">staticint __init wakelocks_init(void)
{
intret;
inti;
......
for(i = 0; i < ARRAY_SIZE(active_wake_locks); 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;
}
</span>

 

可以看到,显示初始化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():

?
<span style="color: #0000ff;">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 < 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:
returnerror ? error : n;
}
</span>

 

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

?
<span style="color: #0000ff;">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);
}
</span>

 

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

?
<span style="color: #0000ff;">staticvoid early_suspend(structwork_struct *work){
 
structearly_suspend *pos;
unsignedlongirqflags;
intabort = 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);
}
</span>

 

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



源出处:http://www.eoeandroid.com/thread-99123-1-1.html

原创粉丝点击