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

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:

  1. //定义写入/sys/power/state的命令字符串
     
    static const char *off_state = "mem";
     
    static const char *on_state = "on";
     
    //打开/sys/power/state等属性文件,保存相应的文件描述符
     
    static int
     
    open_file_descriptors(const char * const paths[])
     
    {
     
        int i;
     
        for (i=0; i
    最终,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem""on"命令字符串。
  2. int set_screen_state(inton)
     
    {
     
        ......
     
        initialize_fds();
     
        ......
     
        char buf[32];
     
        int len;
     
        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);
     
        ......
     
        return 0;
     
    }
    2. 内核中数据结构和接口

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

- early_suspend 结构

struct early_suspend {
     
    #ifdef CONFIG_HAS_EARLYSUSPEND
     
             structlist_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_EARLYSUSPEND
        suspend_state_t state = PM_SUSPEND_ON;
    #else
        suspend_state_t state = PM_SUSPEND_STANDBY;
    #endif
        const char * const *s;
    #endif
        char *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_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:
        return error ? error : n;
    }
看到了没,前一篇文章说过,suspend to disk做了特殊处理,这里直接比较传入的字符串,而不是使用后续的pm_states数组,这里我不关心suspend to disk,所以略过hibernate的分析。

紧接着,通过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;
     
            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的流程完成。下面的序列图清晰地表明了整个调用的过程:



但是,这时整个系统只是处于所谓的idle状态,cpu还在工作,后台进程也在工作中,那什么时候系统会真正地进入睡眠状态?注意到最后一句关键的调用了没有:
wake_unlock(&main_wake_lock);
解锁动作会触发标准linux的suspend流程,这个过程就留给写一篇文章讨论吧。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值