基于Android的Linux内核的电源管理:Early Suspend
1.用户空间的接口
在kernel/power/main.c中,定义了一组sysfs的属性文件,其中一个定义是:
- power_attr(state);
把这个宏展开后:
[cpp] view plain copy
- staticstructkobj_attributestate_attr={\
- .attr={\
- .name="state",\
- .mode=0644,\
- },\
- .show=state_show,\
- .store=state_store,\
- }
[cpp] view plain copy
我们再看看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:
[cpp] view plain copy
- //定义写入/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<OUR_FD_COUNT;i++){
- intfd=open(paths[i],O_RDWR);
- if(fd<0){
- fprintf(stderr,"fatalerroropening\"%s\"\n",paths[i]);
- g_error=errno;
- return-1;
- }
- g_fds[i]=fd;
- }
- g_error=0;
- return0;
- }
最终,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。
[cpp] view plain copy
- 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;
- }
/********************************************************************************************/
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/********************************************************************************************/
2.内核中数据结构和接口
与earlysuspend相关的数据结构和接口都在earlysuspend.h中进行了定义。
- early_suspend 结构
[cpp] view plain copy
- structearly_suspend{
- #ifdefCONFIG_HAS_EARLYSUSPEND
- structlist_headlink;
- intlevel;
- void(*suspend)(structearly_suspend*h);
- void(*resume)(structearly_suspend*h);
- #endif
- };
希望执行early suspend的设备,他的设备驱动程序需要向电源管理系统注册,该结构体用于向电源管理系统注册earlysuspend/lateresume,当电源管理系统启动suspend流程时,回调函数suspend会被调用,相反,resume的最后阶段,回调函数resume会被调用,level字段用于调整该结构体在注册链表中的位置,suspend时,level的数值越小,回调函数的被调用的时间越早,resume时则反过来。Android预先定义了3个level等级:
[cpp] view plain copy
- enum{
- EARLY_SUSPEND_LEVEL_BLANK_SCREEN=50,
- EARLY_SUSPEND_LEVEL_STOP_DRAWING=100,
- EARLY_SUSPEND_LEVEL_DISABLE_FB=150,
- };
[cpp] view plain copy
- 如果你想你的设备在FB设备被禁止之前执行他的earlysuspend回调,设备驱动程序应该把level值设定为小于150的某个数值,然后向系统注册early_suspend结构。注册和反注册函数是:
- void register_early_suspend(struct early_suspend *handler);
- void unregister_early_suspend(struct early_suspend *handler);
- early_suspend_handlers链表
所有注册到系统中的early_suspend结构都会按level值按顺序加入到全局链表early_suspend_handlers中。
3.工作流程
首先,我们从kernel/power/wakelock.c中的初始化函数开始:
[cpp] view plain copy
- 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()函数注册了early suspend特性,等待一段时间后,如果没有用户活动(例如按键、触控等操作),用户空间的电源管理服务最终会调用第一节提到的set_screen_state()函数,透过sysfs,进而会调用到内核中的state_store():
[cpp] view plain copy
- 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;
- }
看到了没,前一篇文章说过,suspend to disk做了特殊处理,这里直接比较传入的字符串,而不是使用后续的pm_states数组,这里我不关心suspend to disk,所以略过hibernate的分析。紧接着,通过pm_states数组,根据命令字符串查询得到请求的状态,默认情况下,Android的内核都会配置了CONFIG_EARLYSUSPEND,所以会调用request_suspend_state()函数,不过在调用该函数之前会先valid_state()一下,这给了平台相关的代码一个机会确认该平台是否支持所请求的电源状态。valid_state()的具体实现请参考内核代码树。
[cpp] view plain copy
- 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():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的流程完成。下面的序列图清晰地表明了整个调用的过程:
图3.1 early suspend调用流程
但是,这时整个系统只是处于所谓的idle状态,cpu还在工作,后台进程也在工作中,那什么时候系统会真正地进入睡眠状态?注意到最后一句关键的调用了没有:
wake_unlock(&main_wake_lock);
解锁动作会触发标准linux的suspend流程,这个过程就留给写一篇文章讨论吧。
更多相关文章
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- Android(安卓)播放本地视频
- Android用户登录数据存储的三种方式
- React Native项目组织结构介绍
- Android(安卓)file 文件重命名删除详解
- 实用技巧 - 收藏集 - 掘金