Android Linux Kernel 电源管理 Early Suspend(转)

来源:互联网 发布:灯光编程教学视频 编辑:程序博客网 时间:2024/05/01 20:26

原帖地址:http://www.eoeandroid.com/thread-99123-1-1.html

十分感谢这位大神的讲解,觉得讲解的不错,就转来,作为今后的参考;

里面对其进行了一些补充,以及加入了自己的理解;



1.用户空间的接口:

在kernel\drivers\power\main.c中,定义了一组sysfs属性文件,其中一个定义是:

power_attr(pm_async);

展开后:



我们在来看看main.c的入口:


     显然,该函数执行后,会在生成/sys/power目录,该目录下会建立一系列属性文件,

     其中一个是/sys/power/state文件。用户空间向该文件的写入将会导致state_store被调用,

     读取该文件将会导致state_show函数被调用。

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


该文件源码比较简单,下面列举重点片段:

       enum {

    ACQUIRE_PARTIAL_WAKE_LOCK = 0,

    RELEASE_WAKE_LOCK,

    REQUEST_STATE,

    OUR_FD_COUNT

};

const char * const OLD_PATHS[] = {
    "/sys/android_power/acquire_partial_wake_lock",
    "/sys/android_power/release_wake_lock",
    "/sys/android_power/request_state"

};

const char * const NEW_PATHS[] = {

    "/sys/power/wake_lock",

    "/sys/power/wake_unlock",

    "/sys/power/state"

};

static int g_initialized = 0;

static int g_fds[OUR_FD_COUNT];

static const char *off_state = "mem";

static const char *on_state = "on";

 

static int  open_file_descriptors(const char * const paths[])

{

    int i;

    for (i=0; i<OUR_FD_COUNT; i++) {

        int fd = open(paths[i], O_RDWR);

//最终,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。

        if (fd < 0) {

            fprintf(stderr, "fatal error opening /"%s/"/n", paths[i]);

            g_error = errno;

            return -1;

        }

        g_fds[i] = fd;

    }

 

    g_error = 0;

    return 0;

}

 

static inline void  initialize_fds(void)

{

    if (g_initialized == 0) {

        if(open_file_descriptors(NEW_PATHS) < 0) {

            open_file_descriptors(OLD_PATHS);

            on_state = "wake";

            off_state = "standby";

        }

        g_initialized = 1;

    }

}

 

int  acquire_wake_lock(int lock, const char* id)

{

    initialize_fds();

    if (g_error) return g_error;

    int fd;

 

    if (lock == PARTIAL_WAKE_LOCK) {   // 上层传下来的lock type

        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];

    }

    else {

        return EINVAL;

    }

 

    return write(fd, id, strlen(id));

}

 

int  release_wake_lock(const char* id)

{

    initialize_fds();

 

//    LOGI("release_wake_lock id='%s'/n", id);

 

    if (g_error) return g_error;

 

    ssize_t len = write(g_fds[RELEASE_WAKE_LOCK], id, strlen(id));

    return len >= 0;

}

 

int set_screen_state(int on)

{

    QEMU_FALLBACK(set_screen_state(on));

    LOGI("*** set_screen_state %d", on);

 

    initialize_fds();

    if (g_error) return g_error;

 

    char buf[32];

    int len;

    if(on)

        len = sprintf(buf, on_state);

    else

        len = sprintf(buf, off_state);

    len = write(g_fds[REQUEST_STATE], buf, len);

    if(len < 0) {

        LOGE("Failed setting last user activity: g_error=%d/n", g_error);

    }

    return 0;

}


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

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


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


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


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

  3. 工作流程

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

想了解wake lock 可以看我下面转载的:


http://blog.csdn.net/yuanjungogogo/article/details/8478688


。。。。。。 (显示初始化active_wake_locks链表数组,然后初始化并且锁住main_wake_lock,注册平台设备power_device)

,。。。。。(各种code)


最后一个动作:创建了一个工作队列线程suspend_work_queue,

该工作队列是earlysuspend的核心所在。

系统启动完成后,相关的驱动程序通过register_early_suspend()函数注册了early suspend特性,

等待一段时间后,如果没有用户活动(例如按键、触控等操作),

用户空间的电源管理服务最终会调用第一节提到的set_screen_state()函数,

透过sysfs,进而会调用到内核中的state_store():


 紧接着,通过pm_states数组,

根据命令字符串查询得到请求的状态,默认情况下,Android的内核都会配置了CONFIG_EARLYSUSPEND,

所以会调用request_suspend_state()函数,不过在调用该函数之前会先valid_state()

一下,这给了平台相关的代码一个机会确认该平台是否支持所请求的电源状态。

request_suspend_state()函数:



  还记得前面初始化时建立的工作队列suspend_woek_queue吗?

根据之前的电源状态和请求的状态

 request_suspend_state()只是简单地向suspend_work_queue中加入early_suspend_work或者是late_resume_work并调度他们执行。early_suspend_work的工作函数是early_suspend():



 终于看到啦,early_suspend()遍历early_suspend_handlers链表,从中取出各个驱动程序注册的early_suspend结构,然后调用它的suspend回调函数。最后,释放main_wake_lock锁,至此整个earlysuspend的流程完成。下面的序列图清晰地表明了整个调用的过程:

但是,这时整个系统只是处于所谓的idle状态,cpu还在工作,后台进程也在工作中,那什么时候系统会真正地进入睡眠状态?注意到最后一句关键的调用了没有:

wake_unlock(&main_wake_lock);



原创粉丝点击