Power Management

来源:互联网 发布:社会支持网络理论案例 编辑:程序博客网 时间:2024/05/20 00:13
Power Management
迫切的想知道power management 的流程。
对整个流程稍微整理了下
参考这个链接http://blog.csdn.net/suntao222/article/details/8162938
还有这个链接;http://blog.csdn.net/tommy_wxie/article/details/7208633


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\n"))        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)\n");        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)\n");        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\n");    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\n");        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\n");    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\n",         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\n",            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\n");        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'\n", 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 != '\n') {        /* 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 .


原创粉丝点击