您的位置:首页 > 移动开发 > Android开发

基于MTK android电源管理:early_suspend分析

2013-05-22 14:19 387 查看
Android共支持3种suspend的模式:

early_suspend、suspned、hibernation

1,earlysuspend是一种低功耗的状态,某些设备可以选择进入某种功耗较低的状态,比如LCD可以降低亮度或灭掉;

2,suspend是指除电源管理以外的其他外围模块以及cpu均不工作,只有内存保持自刷新的状态;

3,hibernation是指所有内存镜像都被写入磁盘中,然后系统关机,恢复后系统将能恢复到“关机”之前的状态.

power manager的suspend状态有4种,可从suspend.h中查看

typedef int __bitwise suspend_state_t;

#define PM_SUSPEND_ON ((__force suspend_state_t) 0)

#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1)

#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)

#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)

其中在suspend.c中只为前三种定义了可操作的宏:

const char *const pm_states[PM_SUSPEND_MAX] = {

#ifdef CONFIG_EARLYSUSPEND

[PM_SUSPEND_ON] = "on",

#endif

[PM_SUSPEND_STANDBY] = "standby",

[PM_SUSPEND_MEM] = "mem",

};

至于第四种应该就是hibernation功能,目前所知道的android设备支持的很少;

#define power_attr(_name) \

static struct kobj_attribute _name##_attr = { \

.attr = { \

.name = __stringify(_name), \

.mode = 0644, \

}, \

.show = _name##_show, \

.store = _name##_store, \

}

power_attr(state);

在power maneger main.c中定义了电源管理的接口属性

static int __init pm_init(void)

{

int error = pm_start_workqueue();

if (error)

return error;

hibernate_image_size_init();

hibernate_reserved_size_init();

power_kobj = kobject_create_and_add("power", NULL);

if (!power_kobj)

return -ENOMEM;

error = sysfs_create_group(power_kobj, &attr_group);

if (error)

return error;

return pm_autosleep_init();

}

将该属性文件注册到/sys下面,之后用户层可以通过/sys/power/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;

#ifdef CONFIG_MTK_HIBERNATION

state = decode_state(buf, n);

hib_log("entry (%d)\n", state);

#endif

if (no_suspend && len == 3 && !strncmp(buf, "mem", len))

{

printk("suspend disabled\n");

goto Exit;

}

#ifdef CONFIG_MTK_HIBERNATION

if (len == 8 && !strncmp(buf, "hibabort", len)) {

hib_log("abort hibernation...\n");

error = mtk_hibernate_abort();

goto Exit;

}

#endif

/* First, check if we are requested to hibernate */

if (len == 4 && !strncmp(buf, "disk", len)) {

#ifdef CONFIG_MTK_HIBERNATION

hib_log("trigger hibernation...\n");

#ifdef CONFIG_EARLYSUSPEND

if (PM_SUSPEND_ON == get_suspend_state()) {

hib_warn("\"on\" to \"disk\" (i.e., 0->4) is not supported !!!\n");

error = -EINVAL;

goto Exit;

}

#endif

if (!pre_hibernate()) {

error = 0;

error = mtk_hibernate();

}

#else // !CONFIG_MTK_HIBERNATION

error = hibernate();

#endif

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 = -EINVAL;

#else

error = enter_state(state);

#endif

}

#endif

Exit:

return error ? error : n;

}

CONFIG_MTK_HIBERNATION用来判断是否有开启hibernation状态

for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {

if (*s && len == strlen(*s) && !strncmp(buf, *s, len))

break;

}

用来侦测目前powwer manager的状态,是on还是mem,还是suspened.

valid_state(state):用来判断该android的power manager是否支持该state,之后再进入request_suspend_state.

如果是标准的linux内核就会走enter_state(state)

void request_suspend_state(suspend_state_t new_state)

{

unsigned long irqflags;

int old_sleep;

int wait_flag = 0;

spin_lock_irqsave(&state_lock, irqflags);

old_sleep = state & SUSPEND_REQUESTED;

if (earlysuspend_debug_mask & DEBUG_USER_STATE) {

struct timespec ts;

struct rtc_time tm;

getnstimeofday(&ts);

rtc_time_to_tm(ts.tv_sec, &tm);

pm_warn("%s (%d->%d) at %lld "

"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",

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;

pm_warn("sys_sync_work_queue early_sys_sync_work\n");

queue_work(sys_sync_work_queue, &early_sys_sync_work);

pm_warn("suspend_work_queue early_suspend_work\n");

queue_work(suspend_work_queue, &early_suspend_work);

} else if (old_sleep && new_state == PM_SUSPEND_ON) {

state &= ~SUSPEND_REQUESTED;

//wake_lock(&main_wake_lock);

///cun

if (queue_work(suspend_work_queue, &late_resume_work)) {

/*

* In order to synchronize the backlight turn on timing,egou

* block the thread and wait for fb driver late_resume()

* callback function is completed

*/

wait_flag = 1;

}

}

requested_suspend_state = new_state;

spin_unlock_irqrestore(&state_lock, irqflags);

if (wait_flag == 1) {

wait_for_completion(&fb_drv_ready);

pr_warn("wait done\n");

}

}

通过上面的两条判断语句根据不同的power manager状态将对应的suspend、resume操作函数加入对应的工作队列中去;

先来看看suspend的操作函数:

queue_work(suspend_work_queue, &early_suspend_work):调度执行指定的工作对列suspend_work_queue中的任务,early_suspend_work是指定的任务指针

DECLARE_WORK(early_suspend_work, early_suspend)静态的创建工作对列结构,其中early_suspend是队列中的任务所对应的function;

suspend_work_queue是在什么时候创建的呢?

static int __init org_wakelocks_init(void)

{

int ret;

wake_lock_init(&sys_sync_wake_lock, WAKE_LOCK_SUSPEND, "sys_sync");

sys_sync_work_queue = create_singlethread_workqueue("fs_sync");

if (sys_sync_work_queue == NULL) {

pr_err("[wakelocks_init] fs_sync workqueue create failed\n");

}

suspend_work_queue = create_singlethread_workqueue("suspend");

if (suspend_work_queue == NULL) {

ret = -ENOMEM;

goto err_suspend_work_queue;

}

return 0;

err_suspend_work_queue:

return ret;

}

整个suspend的核心就是在创建suspend_work_queue的工作队列中完成

static void early_suspend(struct work_struct *work)

{

struct early_suspend *pos;

unsigned long irqflags;

int abort = 0, count = 0;

pr_warn("@@@@@@@@@@@@@@@@@@@@@@@\n@@@__early_suspend__@@@\n@@@@@@@@@@@@@@@@@@@@@@@\n");

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) {

if (earlysuspend_debug_mask & DEBUG_SUSPEND)

pm_warn("abort, state %d\n", state);

mutex_unlock(&early_suspend_lock);

goto abort;

}

pr_warn("early_suspend_count = %d, forbid_id = 0x%x\n", early_suspend_count, forbid_id);

if (earlysuspend_debug_mask & DEBUG_SUSPEND)

pm_warn("call handlers\n");

list_for_each_entry(pos, &early_suspend_handlers, link) {

if (pos->suspend != NULL) {

if (!(forbid_id & (0x1 << count))) {

//if (earlysuspend_debug_mask & DEBUG_VERBOSE)

pr_warn("ES handlers %d: [%pf], level: %d\n", count, pos->suspend, pos->level);

pos->suspend(pos);

}

count++;

}

}

mutex_unlock(&early_suspend_lock);

/* Remove sys_sync from early_suspend, and use work queue to complete sys_sync */

abort:

if (state == SUSPEND_REQUESTED_AND_SUSPENDED) {

//wake_unlock(&main_wake_lock);

#ifdef CONFIG_MTK_HIBERNATION

suspend_state_t susp_state = get_suspend_state();

pm_warn("calling pm_autosleep_set_state() with parameter: %d\n", susp_state);

pm_autosleep_set_state(susp_state);

#else

pm_autosleep_set_state(PM_SUSPEND_MEM);

#endif

}

}

关键性的函数: list_for_each_entry(pos, &early_suspend_handlers, link),会遍历early_suspend_handlers

链表中的所有handler,然后回调所有加入该链表中的模块的suspend handle函数,最后会释放main lock,到此early_suspend结束,每个模块又是如何加入到该early_suspend_handlers链表中的呢

每个模块如果需要进入suspend,就需要将对应的处理函数加入到handles的链表中,register_early_suspend(&obj->early_drv),该函数就是用来将处理函数注册到handles链表中

obj->early_drv.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1,

obj->early_drv.suspend = bma220_early_suspend,

其中level是表示该结构在handlers链表中的位置,suspend时该level值越小那么在回调函数中执行的就越早,resume则反过来

android在earlysuspened.h中先定义了如下几个level值:

enum {

EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,

EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,

EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,

};

struct early_suspend {

#ifdef CONFIG_HAS_EARLYSUSPEND

struct list_head link;

int level;

void (*suspend)(struct early_suspend *h);

void (*resume)(struct early_suspend *h);

#endif

};

如果需要将suspend的顺序调到这其中之前的话,只需将level值改成比这个小就可以了

下面看看register_early_suspend这个函数的处理过程:

void register_early_suspend(struct early_suspend *handler)

{

struct list_head *pos;

mutex_lock(&early_suspend_lock);

list_for_each(pos, &early_suspend_handlers) {

struct early_suspend *e;

e = list_entry(pos, struct early_suspend, link);

if (e->level > handler->level)

break;

}

list_add_tail(&handler->link, pos);

early_suspend_count++;

if ((state & SUSPENDED) && handler->suspend)

handler->suspend(handler);

mutex_unlock(&early_suspend_lock);

}

可以到source code详细查看这段函数体的实现过程,大意如下;

通过扫描链表中的每一项来与将要注册的handle的level进行比较,将level小的这项加入到与之对比的链表项的后面

下面看看有关suspend的hal层代码;

const char * const NEW_PATHS[] = {

"/sys/power/wake_lock",

"/sys/power/wake_unlock",

"/sys/power/state"

};

首先定义了操作的接口,这些接口在kernel起来后就已注册好,任何一个要操作的接口,首先就需先打开:

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);

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;

}

用户空间的电源管理系统会通过调用set_screen_state来触发底层的suspend流程

int

set_screen_state(int on)

{

QEMU_FALLBACK(set_screen_state(on));

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

initialize_fds();

//LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime,

// systemTime(), strerror(g_error));

if (g_error) return g_error;

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);

if(len < 0) {

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

}

return 0;

}

该函数实质上是往接口中写men或on等字符串命令
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: