Power Management

浏览:
字体:
发布时间:2013-12-09 23:24:06
来源:
Power Management
迫切的想知道power management 的流程。
对整个流程稍微整理了下


1、autosleep 流程
上层操作底层的/sys/power/autosleep 这个接口进行操作底层。在main .c 里面会创建一个autosleep 接口,对应main.c 里面的store 函数。
所对应的source code 在:/kernel/kernel/main.c 里面

static ssize_t autosleep_store(struct kobject *kobj,                   struct kobj_attribute *attr,                   const char *buf, size_t n){    suspend_state_t state = decode_state(buf, n);    int error;    if (state == PM_SUSPEND_ON        && strcmp(buf, off) && strcmp(buf, off))        return -EINVAL;    error = pm_autosleep_set_state(state);    old_state=state;    return error ? error : n;}



当底层接受到上层传递到的值进行一些列的操作,有很多的state 状态:
#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)
当底层接受到上层的state 之后,主要是调用pm_autosleep_set_state 这个函数进行power management 的管理。
它的实现如下:
int pm_autosleep_set_state(suspend_state_t state){#ifndef CONFIG_HIBERNATION    if (state >= PM_SUSPEND_MAX)        return -EINVAL;#endif    __pm_stay_awake(autosleep_ws);    mutex_lock(&autosleep_lock);    autosleep_state = state;    __pm_relax(autosleep_ws);    if (state > PM_SUSPEND_ON) {        pm_wakep_autosleep_enabled(true);        //Add a timer to trigger wakelock debug        pr_info([PM]unattended_timer: mod_timer (auto_sleep));        mod_timer(&unattended_timer, jiffies + msecs_to_jiffies(PM_UNATTENDED_TIMEOUT));        queue_up_suspend_work();    } else {        pm_wakep_autosleep_enabled(false);        //Add a timer to trigger wakelock debug        pr_info([PM]unattended_timer: del_timer (late_resume));        del_timer(&unattended_timer);    }    mutex_unlock(&autosleep_lock);    return 0;}


调用的流程:
1、 pm_wakep_autosleep_enabled 设置所注册的wake source 的auto sleep 全部设置成1,ws->autosleep_enabled = set;
2、调用queue_up_suspend_work(); 让设备进入suspend 状态。
static DECLARE_WORK(suspend_work, try_to_suspend);
调用try_to_suspend 函数。

pm_wakep_autosleep_enabled 这个函数还是很重要的,我们在下面的wake_lock 章节会详细讲解。


在底层里面的用autosleep_state 这个全部变量去保存目前的suspend 的状态。

if (autosleep_state >= PM_SUSPEND_MAX)
hibernate(); //进入冬眠模式
else
pm_suspend(autosleep_state); 进入suspend 的模式
我们主要看下pm_suspend 函数的实现:

int pm_suspend(suspend_state_t state){    int error;    if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)        return -EINVAL;    pm_suspend_marker(entry);    error = enter_state(state);    if (error) {        suspend_stats.fail++;        dpm_save_failed_errno(error);    } else {        suspend_stats.success++;    }    pm_suspend_marker(exit);    return error;}EXPORT_SYMBOL(pm_suspend);


然后调用enter_state(state);函数进入所要进入的状态。

主要调用下面两个函数:
error = suspend_prepare
分配console ,发送PM_SUSPEND_PREPARE这个notify 事件。最后还有的是调用suspend_freeze_processes 冻结所有的进程。
error = suspend_devices_and_enter(state);
函数实现如下:

int suspend_devices_and_enter(suspend_state_t state){    int error;    bool wakeup = false;    if (!suspend_ops)        return -ENOSYS;    trace_machine_suspend(state);    if (suspend_ops->begin) {//判断suspend_ops 里面有没有begin 函数,关于suspend_ops 的赋值,我们会单独有一个标题讲解        error = suspend_ops->begin(state);        if (error)            goto Close;    }    //Add a timer to trigger wakelock debug    pr_info([PM]unattended_timer: del_timer);    del_timer ( &unattended_timer );    suspend_console();//console 进行冬眠    suspend_test_start();    error = dpm_suspend_start(PMSG_SUSPEND);    if (error) {        printk(KERN_ERR PM: Some devices failed to suspend);        goto Recover_platform;    }    suspend_test_finish(suspend devices);    if (suspend_test(TEST_DEVICES))        goto Recover_platform;    do {        error = suspend_enter(state, &wakeup);    } while (!error && !wakeup        && suspend_ops->suspend_again && suspend_ops->suspend_again()); Resume_devices:    suspend_test_start();    dpm_resume_end(PMSG_RESUME);    suspend_test_finish(resume devices);    resume_console();    //Add a timer to trigger wakelock debug    pr_info([PM]unattended_timer: mod_timer);    mod_timer(&unattended_timer, jiffies + msecs_to_jiffies(PM_UNATTENDED_TIMEOUT)); Close:    if (suspend_ops->end)        suspend_ops->end();    trace_machine_suspend(PWR_EVENT_EXIT);    return error; Recover_platform:    if (suspend_ops->recover)        suspend_ops->recover();    goto Resume_devices;}



suspend_console(); //冻结终端
suspend_test_start//打出suspend 开始的时间

其中dpm_suspend_start 试下如下:
int dpm_suspend_start(pm_message_t state)
{
int error;

error = dpm_prepare(state); 为设备准备链表
if (error) {
suspend_stats.failed_prepare++;
dpm_save_failed_step(SUSPEND_PREPARE);
} else
error = dpm_suspend(state);将准备的设备进入suspend
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
在dpm_suspendl里面,我们会将while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next); dpm_list 里面所有的设备都遍历出来,
最终哦将遍历出来的设备添加到另外一个链表里面去:list_move_tail(&dev->power.entry, &dpm_prepared_list.至于dpm_list 是如何来的,我们在另外一个章节进行讲解。

接下来dpm_suspend(state); 进行suspend 设备。
dpm_suspend 的函数实现如下:

int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;

might_sleep();

mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
while (!list_empty(&dpm_prepared_list)) {
struct device *dev = to_device(dpm_prepared_list.prev);

get_device(dev);
mutex_unlock(&dpm_list_mtx);

error = device_suspend(dev);

mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, , error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &dpm_suspended_list);
put_device(dev);
if (async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
if (error) {
suspend_stats.failed_suspend++;
dpm_save_failed_step(SUSPEND_SUSPEND);
} else
dpm_show_time(starttime, state, NULL);
return error;
}
然后调用device_suspend 函数
-----》__device_suspend ------》if (dev->pm_domain) {
info = power domain ;
callback = pm_op(&dev->pm_domain->ops, state);
goto Run;
}

if (dev->type && dev->type->pm) {
info = type ;
callback = pm_op(dev->type->pm, state);
goto Run;
}

if (dev->class) {
if (dev->class->pm) {
info = class ;
callback = pm_op(dev->class->pm, state);
goto Run;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, legacy class );
error = legacy_suspend(dev, state, dev->class->suspend);
goto End;
}
}

if (dev->bus) {
if (dev->bus->pm) {
info = bus ;
callback = pm_op(dev->bus->pm, state);
} else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, legacy bus );
error = legacy_suspend(dev, state, dev->bus->suspend);
goto End;
}
}

调用pm_op函数调用设备、class、 bus 等的suspend函数,同样的resume 函数也是一样的流程。
dpm_suspend_start 函数执行完了之后。我们再次回到suspend_devices_and_enter 函数中来。



do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup
&& suspend_ops->suspend_again && suspend_ops->suspend_again());

这里是一个死循环。只有wakeup 为1 的时候,我们才会调出这个循环。
我们设备的suspend的时候有很多的操作,每一个操作都放在不同的链表里面,然后我们根据不同suspend的类型,进行不同的操作。
举例如下:
error = dpm_suspend_start(PMSG_SUSPEND);

error = dpm_prepare(state); 会调用到to_device(dpm_list.next); 这个链表 ,这个链表是我们在创建device 的时候,我们所有创建设备的power 控制的结点。遍历过dpm_list 的链表后,我们将设备放到dpm_prepared_list 链表里面,再进行dpm_prepared_list 这个链表的操作。下面的几种状态也是同样的效果,只是不同的操作罢了。

2、设备的注册
我们在注册一个设备的时候,通常会调用device_register 进行设备的注册。下面设备的流程,我们只care power management 部分,别的细节暂时不care 。

整个设备注册的流程:

return device_add(dev); ----》
device_pm_add(dev);-------》

void device_pm_add(struct device *dev){    pr_debug(PM: Adding info for %s:%s,         dev->bus ? dev->bus->name : No Bus, dev_name(dev));    mutex_lock(&dpm_list_mtx);    if (dev->parent && dev->parent->power.is_prepared)        dev_warn(dev, parent %s should not be sleeping,            dev_name(dev->parent));    list_add_tail(&dev->power.entry, &dpm_list);    dev_pm_qos_constraints_init(dev);    mutex_unlock(&dpm_list_mtx);}


上面的注册会将设备添加到dpm_list里面。 list_add_tail(&dev->power.entry, &dpm_list);
3、suspend_ops 的操作

在整个power management 里面经常会调用到suspend_ops 这个操作集合里面提供的interface 进行
suspend_ops 是存放在kerel/kernel/suspend.c 里面的全部变量。 suspend_set_ops 这个函数会为suspend_set_ops 这个变量进行赋值。
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
lock_system_sleep();
suspend_ops = ops;
unlock_system_sleep();
}

suspend_set_ops 是在/kernel/arch/arm/mach-msm/pm-8x60.c 里面。

static int __init msm_pm_init(void)
{
enum msm_pm_time_stats_id enable_stats[] = {
MSM_PM_STAT_IDLE_WFI,
MSM_PM_STAT_RETENTION,
MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_POWER_COLLAPSE,
MSM_PM_STAT_SUSPEND,
};
msm_pm_mode_sysfs_add();
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
suspend_set_ops(&msm_pm_ops);


msm_pm_ops 的实现如下:
static const struct platform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid = suspend_valid_only_mem,
.prepare_late = msm_suspend_prepare,
.wake = msm_suspend_wake,
};


看到没有,这个里面实现ops 里面的enter、valid 、prepare_late 、wake 函数。
这些函数在suspend 的时候都会调用到,如:
static int suspend_prepare(void)
{
int error;

if (!suspend_ops || !suspend_ops->enter)
return -EPERM;


这里就在调用enter 函数。

4.设备resume 的流程

当我们按下power key 之后,我们的设备会进入suspend ,整个流程如上面讲解suspend 的流程一样。
source code :


do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup
&& suspend_ops->suspend_again && suspend_ops->suspend_again());

suspend_enter 函数的实现:

static int suspend_enter(suspend_state_t state, bool *wakeup){    int error;    if (suspend_ops->prepare) {        error = suspend_ops->prepare();        if (error)            goto Platform_finish;    }    error = dpm_suspend_end(PMSG_SUSPEND);    if (error) {        printk(KERN_ERR PM: Some devices failed to power down);        goto Platform_finish;    }    if (suspend_ops->prepare_late) {        error = suspend_ops->prepare_late();        if (error)            goto Platform_wake;    }    if (suspend_test(TEST_PLATFORM))        goto Platform_wake;    error = disable_nonboot_cpus();    if (error || suspend_test(TEST_CPUS))        goto Enable_cpus;    arch_suspend_disable_irqs();    BUG_ON(!irqs_disabled());    error = syscore_suspend();    if (!error) {        *wakeup = pm_wakeup_pending();        if (!(suspend_test(TEST_CORE) || *wakeup)) {            error = suspend_ops->enter(state);            events_check_enabled = false;        }        syscore_resume();    }    arch_suspend_enable_irqs();    BUG_ON(irqs_disabled()); Enable_cpus:    enable_nonboot_cpus(); Platform_wake:    if (suspend_ops->wake)        suspend_ops->wake();    dpm_resume_start(PMSG_RESUME); Platform_finish:    if (suspend_ops->finish)        suspend_ops->finish();    return error;}




当我们进入suspend 的时候,我们最后会调用到suspend_ops->enter 函数里面,当执行到这个函数的时候,我们进入了要求的suspend 状态,只有当唤醒的时候才会返回。关于suspend_ops->enter 函数的实现,我们在讲解suspend_ops的时候里面有赋值(msm_pm_enter)。

其实我目前还没有明白为何进入msm_pm_enter 函数为何就停止在哪里?
设置CPU 的寄存器,让整个CPU 进入睡眠,进入睡眠之后,执行的code 就停止执行了, 就停止在那里了,当我们按下power button 的时候,CPU 又起来来,code 又继续执行了,所以就走下面的resume 流程。
power button 是硬件设计好了的wake source 源,硬件设计好了的。



当返回的时候,我们就会调用resume设备的 函数。

Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish(resume devices);
resume_console();

其实resume 的过程就和suspend 的流程差不多,也是从设备的链表里面遍历设备,最后调用设备的resume 函数。
因为下面执行的是goto 语句,所以依次就会执行:


syscore_resume();
}

arch_suspend_enable_irqs(); //打开中断
BUG_ON(irqs_disabled());

Enable_cpus:
enable_nonboot_cpus();//启动非启动CPU


Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();

dpm_resume_start(PMSG_RESUME);//Execute noirq and early device callbacks.

Platform_finish:
if (suspend_ops->finish)
就是整个resume 设备的流程。

5.wake_lock机制
基本原理如下:当启动一个应用程序的时候,它都可以申请一个wake_lock唤醒锁,每当申请成功之后都会在内核中注册一下(通知系统内核,现在已经有锁被申请),当应用程序在某种情况下释放wake_lock的时候,会注销之前所申请的wake_lock。特别要注意的是:只要是系统中有一个wake_lock的时候,系统此时都不能进行睡眠。
下面将讲解上层获得wake_lock 的整个过程。
frameworks/base/services/java/com/android/server/power/PowerManagerService.java        public void acquire() {            synchronized (this) {                mReferenceCount += 1;                if (mReferenceCount == 1) {                    if (DEBUG_SPEW) {                        Slog.d(TAG, Acquiring suspend blocker  + mName + .);                    }                    nativeAcquireSuspendBlocker(mName);                }            }        }


会调用nativeAcquireSuspendBlocker 这个 函数的实现是放在jni 里面的:
frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
ScopedUtfChars name(env, nameStr);
acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

最后调用acquire_wake_lock 进行申请wake_lock
enum {
PARTIAL_WAKE_LOCK = 1, // the cpu stays on, but the screen is off
FULL_WAKE_LOCK = 2 // the screen is also on
};
通过注释也可以看出这两种wake_lock 的区别。
我们继续trace code :
acquire_wake_lock的实现:
hardware/libhardware_legacy/power/power.c

int
acquire_wake_lock(int lock, const char* id)
{
initialize_fds();//获得我们设备结点的设备描述符号,并将我们获得的设备描述符号放到g_fds 这个数组里面。或者 /sys/power/wake_lock,/sys/power/wake_unlock,这两个设备结点的设备描述符号。

// ALOGI(acquire_wake_lock lock=%d id='%s' , lock, id);

if (g_error) return g_error;

int fd;

if (lock == PARTIAL_WAKE_LOCK) {
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];//获得wake_lock 的设备节点的设备描述符
}
else {
return EINVAL;
}

return write(fd, id, strlen(id));//我们获得对应的设备描述符后,我们直接操作描述符。

}
write(fd, id, strlen(id) 会调用到wake_lock 里面的store 函数。在下面的小章节会讲解到wakelock.c 这个文件。
5.1.wake_lock_store
当我们使用到wake_lock 设备节点的时候,最终调用到wake_lock_store 函数。
static ssize_t wake_lock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_lock(buf);
return error ? error : n;
}
pm_wake_lock函数的实现如下:

int pm_wake_lock(const char *buf){    const char *str = buf;    struct wakelock *wl;    u64 timeout_ns = 0;    size_t len;    int ret = 0;    while (*str && !isspace(*str))        str++;    len = str - buf;    if (!len)        return -EINVAL;    if (*str && *str != '') {        /* Find out if there's a valid timeout string appended. */        ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);        if (ret)            return -EINVAL;    }    mutex_lock(&wakelocks_lock);    wl = wakelock_lookup_add(buf, len, true);    if (IS_ERR(wl)) {        ret = PTR_ERR(wl);        goto out;    }    if (timeout_ns) {        u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;        do_div(timeout_ms, NSEC_PER_MSEC);        __pm_wakeup_event(&wl->ws, timeout_ms);    } else {        __pm_stay_awake(&wl->ws);    }    wakelocks_lru_most_recent(wl); out:    mutex_unlock(&wakelocks_lock);    return ret;}


上面的函数主要讲解wakelock_lookup_add
会调用到void wakeup_source_add(struct wakeup_source *ws)函数将我们申请的wake_lock 添加到
list_add_rcu(&ws->entry, &wakeup_sources); wakeup_sources 链表中去,我们在suspend 的时候会去遍历wakeup_sources 这个链表里面的wake source 的状态。
并根据 timeout_ns 的时间进行调用不同的函数,最终都是将对应的wake_source 设置为true. ws->active = true;

if (timeout_ns) { u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;

do_div(timeout_ms, NSEC_PER_MSEC);
__pm_wakeup_event(&wl->ws, timeout_ms);
} else {
__pm_stay_awake(&wl->ws);
}

现在我们再回到上面讲解的pm_wakep_autosleep_enabled 这个函数里面:
void pm_wakep_autosleep_enabled(bool set){    struct wakeup_source *ws;    ktime_t now = ktime_get();    rcu_read_lock();    list_for_each_entry_rcu(ws, &wakeup_sources, entry) {        spin_lock_irq(&ws->lock);        if (ws->autosleep_enabled != set) {            ws->autosleep_enabled = set;            if (ws->active) {                if (set)                    ws->start_prevent_time = now;                else                    update_prevent_sleep_time(ws, now);            }        }        spin_unlock_irq(&ws->lock);    }    rcu_read_unlock();}#endif /* CONFIG_PM_AUTOSLEEP *

/


上面在进入suspend 的时候会判断ws->active ,如果为true 就不会进入suspend .


>更多相关文章
24小时热门资讯
24小时回复排行
资讯 | QQ | 安全 | 编程 | 数据库 | 系统 | 网络 | 考试 | 站长 | 关于东联 | 安全雇佣 | 搞笑视频大全 | 微信学院 | 视频课程 |
关于我们 | 联系我们 | 广告服务 | 免责申明 | 作品发布 | 网站地图 | 官方微博 | 技术培训
Copyright © 2007 - 2024 Vm888.Com. All Rights Reserved
粤公网安备 44060402001498号 粤ICP备19097316号 请遵循相关法律法规
');})();