android休眠唤醒机制分析(二)—early_suspend-飞外

本文转自:http://blog.csdn.net/g_salamander/article/details/7982170

early_suspend是Android休眠流程的第一阶段即浅度休眠,不会受到wake_lock的阻止,一般用于关闭lcd、tp等设备为运行的应用节约电能。Android的PowerManagerService会根据用户的操作情况调整电源状态,如果需要休眠则会调用到HAL层的set_screen_state()接口,在set_screen_state()中会向/sys/power/state节点写入"mem"值让驱动层开始进入休眠流程。

一、休眠唤醒机制及其用户空间接口

Linux系统支持如下休眠唤醒等级


constchar*constpm_states[PM_SUSPEND_MAX]={#ifdefCONFIG_EARLYSUSPEND[PM_SUSPEND_ON]="on",#endif[PM_SUSPEND_STANDBY]="standby",[PM_SUSPEND_MEM]="mem",};

但在Android中一般只支持"on"和"mem",其中"on"为唤醒设备,"mem"为休眠设备。/sys/power/state节点的读写操作如下:


staticssize_tstate_show(structkobject*kobj,structkobj_attribute*attr,char*buf){char*s=buf;#ifdefCONFIG_SUSPENDinti;for(i=0;i PM_SUSPEND_MAX;i++){if(pm_states[i] valid_state(i))s+=sprintf(s,"%s",pm_states[i]);//打印系统支持的休眠等级}#endif#ifdefCONFIG_HIBERNATIONs+=sprintf(s,"%s","disk");#elseif(s!=buf)/*convertthelastspacetoanewline*/*(s-1)='';#endifreturn(s-buf);}staticssize_tstate_store(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tn){#ifdefCONFIG_SUSPEND#ifdefCONFIG_EARLYSUSPENDsuspend_state_tstate=PM_SUSPEND_ON;#elsesuspend_state_tstate=PM_SUSPEND_STANDBY;#endifconstchar*const*s;#endifchar*p;intlen;interror=-EINVAL;p=memchr(buf,'',n);len=p?p-buf:n;/*First,checkifwearerequestedtohibernate*/if(len==4 !strncmp(buf,"disk",len)){error=hibernate();gotoExit;}#ifdefCONFIG_SUSPENDfor(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_EARLYSUSPENDif(state==PM_SUSPEND_ON||valid_state(state)){error=0;request_suspend_state(state);//请求进入android的休眠流程}#elseerror=enter_state(state);//linux的标准休眠流程#endif#endifExit:returnerror?error:n;}power_attr(state);

其中state_show()为节点的读函数,主要打印出系统支持的休眠等级;state_store()为节点的写函数,根据参数请求休眠或者唤醒流程。节点的创建代码如下:


staticstructattribute*g[]={ state_attr.attr,//state节点#ifdefCONFIG_PM_TRACE pm_trace_attr.attr,#endif#ifdefined(CONFIG_PM_SLEEP) defined(CONFIG_PM_DEBUG) pm_test_attr.attr,//pm_test节点#endif#ifdefCONFIG_USER_WAKELOCK wake_lock_attr.attr,//wake_lock节点 wake_unlock_attr.attr,//wake_unlock节点#endifNULL,};staticstructattribute_groupattr_group={.attrs=g,};staticint__initpm_init(void){interror=pm_start_workqueue();if(error)returnerror;power_kobj=kobject_create_and_add("power",NULL);//创建power节点if(!power_kobj)return-ENOMEM;returnsysfs_create_group(power_kobj, attr_group);//创建一组属性节点}core_initcall(pm_init);

二、early_suspend 实现

1、early_suspend 定义、接口及其用法


EARLY_SUSPEND_LEVEL_BLANK_SCREEN=50,EARLY_SUSPEND_LEVEL_STOP_DRAWING=100,EARLY_SUSPEND_LEVEL_DISABLE_FB=150,};structearly_suspend{#ifdefCONFIG_HAS_EARLYSUSPENDstructlist_headlink;//链表节点intlevel;//优先等级void(*suspend)(structearly_suspend*h);void(*resume)(structearly_suspend*h);#endif};

可以看到early_suspend由两个函数指针、链表节点、优先等级组成;内核默认定义了3个优先等级,在suspend的时候先执行优先等级低的handler,在resume的时候则先执行等级高的handler,用户可以定义自己的优先等级;early_suspend向内核空间提供了2个接口用于注册和注销handler:


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

其中register_early_suspend()用于注册,unregister_early_suspend用于注销;一般early_suspend的使用方式如下:


ts- earlysuspend.suspend=sitronix_i2c_suspend_early;ts- earlysuspend.resume=sitronix_i2c_resume_late;ts- earlysuspend.level=EARLY_SUSPEND_LEVEL_BLANK_SCREEN;register_early_suspend( ts- earlysuspend);

设置好suspend和resume接口,定义优先等级,然后注册结构即可。

2、初始化信息

我们看一下early_suspend需要用到的一些数据:


staticDEFINE_MUTEX(early_suspend_lock);staticLIST_HEAD(early_suspend_handlers);//初始化浅度休眠链表//声明3个工作队列用于同步、浅度休眠和唤醒staticvoidearly_sys_sync(structwork_struct*work);staticvoidearly_suspend(structwork_struct*work);staticvoidlate_resume(structwork_struct*work);staticDECLARE_WORK(early_sys_sync_work,early_sys_sync);staticDECLARE_WORK(early_suspend_work,early_suspend);staticDECLARE_WORK(late_resume_work,late_resume);staticDEFINE_SPINLOCK(state_lock);enum{SUSPEND_REQUESTED=0x1,//当前正在请求浅度休眠SUSPENDED=0x2,//浅度休眠完成SUSPEND_REQUESTED_AND_SUSPENDED=SUSPEND_REQUESTED|SUSPENDED,};staticintstate;

初始化了一个链表early_suspend_handlers用于管理early_suspend,还定义读写链表用到的互斥体;另外还声明了3个工作队列,分别用于缓存同步、浅度休眠和唤醒;还声明了early_suspend操作的3个状态。
3、register_early_suspend 和unregister_early_suspend


//遍历浅度休眠链表list_for_each(pos, early_suspend_handlers){structearly_suspend*e;e=list_entry(pos,structearly_suspend,link);//判断当前节点的优先等级是否大于handler的优先等级//以此决定handler在链表中的顺序if(e- level handler- level)break;}//将handler加入当前节点之前,优先等级越低越靠前list_add_tail( handler- link,pos);if((state SUSPENDED) handler- suspend)handler- suspend(handler);mutex_unlock( early_suspend_lock);}EXPORT_SYMBOL(register_early_suspend);
注册的流程比较简单,首先遍历链表,依次比较每个节点的优先等级,如果遇到优先等级比新节点优先等级高则跳出,然后将新节点加入优先等级较高的节点前面,这样就确保了链表是优先等级低在前高在后的顺序;在将节点加入链表后查看当前状态是否为浅度休眠完成状态,如果是则执行handler的suspend函数。


voidunregister_early_suspend(structearly_suspend*handler){mutex_lock( early_suspend_lock);list_del( handler- link);mutex_unlock( early_suspend_lock);}EXPORT_SYMBOL(unregister_early_suspend);

前面我们看到用户空间在写/sys/power/state节点的时候会执行request_suspend_state()函数,该函数代码如下:


spin_lock_irqsave( state_lock,irqflags);old_sleep=state SUSPEND_REQUESTED;//打印当前状态if(debug_mask DEBUG_USER_STATE){structtimespects;structrtc_timetm;getnstimeofday( ts);rtc_time_to_tm(ts.tv_sec, tm);pr_info("request_suspend_state:%s(%d- %d)at%lld""(%d-%02d-%02d%02d:%02d:%02d.%09luUTC)",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;pr_info("sys_sync_work_queueearly_sys_sync_work.");//执行缓存同步与浅度休眠的工作队列queue_work(sys_sync_work_queue, early_sys_sync_work);queue_work(suspend_work_queue, early_suspend_work);}elseif(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);}
函数首先打印出当前状态变化的log,然后判断新状态,如果是休眠状态则置位SUSPEND_REQUESTED标志,然后将同步缓存、浅度休眠工作队列加入相应的内核线程执行;如果新状态是唤醒则首先将main_wake_lock激活,然后再将浅度唤醒工作队列加入内核线程执行;最后更新全局状态变量,因为提供了一个内核空间接口用于获取当前休眠唤醒状态:


structearly_suspend*pos;unsignedlongirqflags;intabort=0;mutex_lock( early_suspend_lock);spin_lock_irqsave( state_lock,irqflags);if(state==SUSPEND_REQUESTED)//判断当前状态是否在请求浅度休眠state|=SUSPENDED;//如果是则置位SUSPENDEDelseabort=1;spin_unlock_irqrestore( state_lock,irqflags);if(abort){//取消early_suspendif(debug_mask DEBUG_SUSPEND)pr_info("early_suspend:abort,state%d",state);mutex_unlock( early_suspend_lock);gotoabort;}if(debug_mask DEBUG_SUSPEND)pr_info("early_suspend:callhandlers");//遍历浅度休眠链表并执行其中所有suspend函数//执行顺序根据优先等级而定,等级越低越先执行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");/*Removesys_syncfromearly_suspend,anduseworkqueuetocompletesys_sync*///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);}
在suspend流程中首先判断当前状态是否为SUSPEND_REQUESTED,如果是则置位SUSPENDED标志,如果不是则取消suspend流程;然后遍历浅度休眠链表,从链表头部到尾部依次调用各节点的suspend()函数,执行完后判断当前状态是否为SUSPEND_REQUESTED_AND_SUSPENDED,如果是则释放main_wake_lock,当前系统中如果只存在main_wake_lock这个有效锁,则会在wake_unlock()里面启动深度休眠线程,如果还有其他其他wake_lock则保持当前状态。


structearly_suspend*pos;unsignedlongirqflags;intabort=0;mutex_lock( early_suspend_lock);spin_lock_irqsave( state_lock,irqflags);if(state==SUSPENDED)//清除浅度休眠完成标志state =~SUSPENDED;elseabort=1;spin_unlock_irqrestore( state_lock,irqflags);if(abort){if(debug_mask DEBUG_SUSPEND)pr_info("late_resume:abort,state%d",state);gotoabort;}if(debug_mask DEBUG_SUSPEND)pr_info("late_resume:callhandlers");//反向遍历浅度休眠链表并执行其中所有resume函数//执行顺序根据优先等级而定,等级越高越先执行list_for_each_entry_reverse(pos, early_suspend_handlers,link)if(pos- resume!=NULL)pos- resume(pos);if(debug_mask DEBUG_SUSPEND)pr_info("late_resume:done");abort:mutex_unlock( early_suspend_lock);}
在resume流程中同样首先判断当前状态是否为SUSPENDED,如果是则清除SUSPENDED标志,然后反向遍历浅度休眠链表,按照优先等级从高到低的顺序执行节点的resume()函数。