您的位置:首页 > 运维架构 > Linux

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

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

把这个宏展开后:


staticstructkobj_attributestate_attr={\
.attr={\
.name="state",\
.mode=0644,\
},\
.show=state_show,\
.store=state_store,\
}


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

staticint__initpm_init(void){
power_kobj=kobject_create_and_add("power",NULL);
if(!power_kobj)
return-ENOMEM;
returnsysfs_create_group(power_kobj,&attr_group);
}


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

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

//定义写入/sys/power/state的命令字符串

staticconstchar*off_state="mem";
staticconstchar*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"命令字符串。
<prelang="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;
}


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

structearly_suspend{

#ifdefCONFIG_HAS_EARLYSUSPEND

structlist_headlink;
intlevel;
void(*suspend)(structearly_suspend*h);
void(*resume)(structearly_suspend*h);

#endif
};


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

enum{
EARLY_SUSPEND_LEVEL_BLANK_SCREEN=50,
EARLY_SUSPEND_LEVEL_STOP_DRAWING=100,
EARLY_SUSPEND_LEVEL_DISABLE_FB=150,
};


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

voidregister_early_suspend(structearly_suspend*handler);
voidunregister_early_suspend(structearly_suspend*handler);


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

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

staticint__initwakelocks_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;
}


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

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

staticssize_tstate_store(structkobject*kobj,structkobj_attribute*attr,
constchar*buf,size_tn)
{
#ifdefCONFIG_SUSPEND
#ifdefCONFIG_EARLYSUSPEND
suspend_state_tstate=PM_SUSPEND_ON;
#else
suspend_state_tstate=PM_SUSPEND_STANDBY;
#endif
constchar*const*s;
#endif
char*p;
intlen;
interror=-EINVAL;

p=memchr(buf,'\n',n);
len=p?p-buf:n;

/*First,checkifwearerequestedtohibernate*/
if(len==4&&!strncmp(buf,"disk",len)){
error=hibernate();
gotoExit;
}

#ifdefCONFIG_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
error=enter_state(state);
#endif
#endif

Exit:
returnerror?error:n;
}


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

voidrequest_suspend_state(suspend_state_tnew_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_workisinqueuealready\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_workisinqueuealready\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():

staticvoidearly_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:%pFbegin\n",pos->suspend);
pos->suspend(pos);

if(debug_mask&DEBUG_SUSPEND)
printk(KERN_DEBUG"pos->suspend:%pFfinish\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的流程完成。下面的序列图清晰地表明了整个调用的过程:

其它相关:

http://blog.sina.com.cn/s/blog_55465b470100n9yx.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: