Power Management
Power Management
迫切的想知道power management 的流程。
对整个流程稍微整理了下
1、autosleep 流程
上层操作底层的/sys/power/autosleep 这个接口进行操作底层。在main .c 里面会创建一个autosleep 接口,对应main.c 里面的store 函数。
所对应的source code 在:/kernel/kernel/main.c 里面
当底层接受到上层传递到的值进行一些列的操作,有很多的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 的管理。
它的实现如下:
调用的流程:
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 函数的实现:
然后调用enter_state(state);函数进入所要进入的状态。
主要调用下面两个函数:
error = suspend_prepare
分配console ,发送PM_SUSPEND_PREPARE这个notify 事件。最后还有的是调用suspend_freeze_processes 冻结所有的进程。
error = suspend_devices_and_enter(state);
函数实现如下:
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);-------》
上面的注册会将设备添加到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 函数的实现:
当我们进入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 的整个过程。
会调用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函数的实现如下:
上面的函数主要讲解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 这个函数里面:
/
上面在进入suspend 的时候会判断ws->active ,如果为true 就不会进入suspend .
迫切的想知道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 .
>更多相关文章
- 09-29如何通过wrap malloc定位C/C++程序的内存泄漏
- 02-25打车软件大战升级,补贴还能维持多久?
- 12-23BMP文件右旋90度[c语言]
- 12-23寻找直方图中面积最大的矩形(C语言版)
- 12-23[ndk,2]ndk开发案例和错误处理
- 12-23[ndk,1]ndk开发,C语言入门讲解
- 12-23C语言连续存储实现队列机制
- 12-23Objective-c 数据类型
首页推荐
佛山市东联科技有限公司一直秉承“一切以用户价值为依归
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 12-05亚马逊推出新一代基础模型 任意模态生成大模
- 12-05OpenAI拓展欧洲业务 将在苏黎世设立办公室
- 12-05微软质疑美国联邦贸易委员会泄露信息 督促其
- 12-05联交所取消宝宝树上市地位 宝宝树:不会对公
- 12-04企业微信致歉:文档打开异常已完成修复
相关文章
24小时热门资讯
24小时回复排行
热门推荐
最新资讯
操作系统
黑客防御