您的位置:首页 > 其它

Power Management

2013-12-03 21:53 169 查看
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 .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: