博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android的Linux内核的电源管理:Early Suspend
阅读量:4952 次
发布时间:2019-06-12

本文共 5962 字,大约阅读时间需要 19 分钟。

1. 用户空间的接口

        在kernel/power/main.c中,定义了一组sysfs的属性文件,其中一个定义是:
        power_attr(state);
        把这个宏展开后:

 
static struct kobj_attribute state_attr = { \.attr ={ \.name = "state", \.mode = 0644, \}, \.show =state_show, \.store =state_store, \}

 

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

static int __init pm_init(void){power_kobj =kobject_create_and_add("power", NULL);if (!power_kobj)return -ENOMEM;return sysfs_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的命令字符串static const char *off_state = "mem";static const char *on_state = "on";//打开/sys/power/state等属性文件,保存相应的文件描述符static intopen_file_descriptors(constchar * const paths[]){int i;for (i=0; i//最终,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。
intset_screen_state(inton){initialize_fds();char buf[32];int len;if(on)len = snprintf(buf, sizeof(buf),"%s", on_state);elselen = snprintf(buf, sizeof(buf),"%s", off_state);buf[sizeof(buf) - 1] = '\0';len = write(g_fds[REQUEST_STATE], buf,len);return 0;}

 

 

2. 内核中数据结构和接口

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

struct early_suspend {#ifdef CONFIG_HAS_EARLYSUSPENDstructlist_head link;int level;void(*suspend)(struct early_suspend *h);void(*resume)(struct early_suspend *h);#endif};

 

希望执行early suspend的设备,他的设备驱动程序需要向电源管理系统注册,该结构体用于向电源管理系统注册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设备被禁止之前执行他的early suspend回调,设备驱动程序应该把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中的初始化函数开始:

static int __init wakelocks_init(void){int ret;int i;......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");......return 0;}

 

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

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n){#ifdef CONFIG_SUSPEND#ifdef CONFIG_EARLYSUSPENDsuspend_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *p;int len;int error = -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();goto Exit;}#ifdef CONFIG_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)#ifdef CONFIG_EARLYSUSPENDif (state == PM_SUSPEND_ON || valid_state(state)) {error = 0;request_suspend_state(state);}#elseerror = enter_state(state);#endif#endifExit:return error ? error : n;}

 

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

void request_suspend_state(suspend_state_t new_state){unsigned long irqflags;int old_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");} else if (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;elseabort = 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的流程完成。下面的序列图清晰地表明了整个调用的过程:

 

 其它相关:

 

 

转载于:https://www.cnblogs.com/sdphome/archive/2011/11/07/2240371.html

你可能感兴趣的文章
C#jbox小节
查看>>
结构体指针释放的问题
查看>>
C#枚举Enum[轉]
查看>>
第三百五十七天 how can I 坚持
查看>>
【动态规划】流水作业调度问题与Johnson法则
查看>>
startActivityForResult不起作用
查看>>
Python&Selenium&Unittest&BeautifuReport 自动化测试并生成HTML自动化测试报告
查看>>
活现被翻转生命
查看>>
POJ 1228
查看>>
SwaggerUI+SpringMVC——构建RestFul API的可视化界面
查看>>
springmvc怎么在启动时自己执行一个线程
查看>>
流操作的规律
查看>>
Python基础学习15--异常的分类与处理
查看>>
javascript运算符的优先级
查看>>
React + Redux 入门(一):抛开 React 学 Redux
查看>>
13位时间戳和时间格式化转换,工具类
查看>>
vue router-link子级返回父级页面
查看>>
C# 通知机制 IObserver<T> 和 IObservable<T>
查看>>
Code of Conduct by jsFoundation
查看>>
div 只显示两行超出部分隐藏
查看>>