android 休眠唤醒驱动流程分析,Android4.0.4休眠唤醒机制分析(基于MSM8260)
当手机满足一定的条件时,会进入休眠状态。从手机进入休眠到唤醒,主要分为三个阶段:
early suspend
suspend
late resume
early suspend执行在休眠前需要完成的一些工作,late resume完成在退出休眠后进行的一些扫尾工作。early suspend与late resume执行的操作是一一对应的。
early_suspend的结构
需要early suspend和late resume执行的操作,通过函数register_early_suspend注册,unregister_early_suspend取消注册。
early_suspend的结构体如下:
struct early_suspend {
struct list_head link;
int level;//节点的级别,控制执行顺序
void (*suspend)(struct early_suspend *h);//early_suspend执行的函数
void (*resume)(struct early_suspend *h);//late_resume执行的函数
};
register_early_suspend(struct early_suspend *handler)
{
struct list_head *pos;
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);
if ((state & SUSPENDED) && handler->suspend)
handler->suspend(handler);
}
注册early_suspend变量,就是在链表early_suspend_handlers中增加该节点,level小的节点排前面。所有early_suspend注册结束后,early_suspend_handlers就是按照节点的level从小到大排列的链表。
early suspend的执行
在文件系统中有一个虚拟的文件:/sys/power/state
向该文件写入字符串“mem”,则启动进入休眠的过程。
向该文件写入字符串“on”,则手机退出休眠状态。
其中Constchar*const pm_states[PM_SUSPEND_MAX] = {
[PM_SUSPEND_ON]="on",
...................
[PM_SUSPEND_MEM]= "mem",
};
向state文件写入字符串,对应函数的操作如下:
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state = PM_SUSPEND_ON;//初始化值为“on”=0
const char * const *s;
char *p;
int len;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;//取出输入字符串的长度
for (s = &pm_states[state]; state
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;//找到对应的输入状态
}
if (state
if (state == PM_SUSPEND_ON || valid_state(state)) {//如果状态为“on”或者“mem”
request_suspend_state(state);//调用该函数
}
}
——————————————————————————————————————————————————————————————————————————————
/*回调函数的注册与实现*/
static struct platform_suspend_opsmsm_pm_ops= {
.enter = msm_pm_enter,
.valid = suspend_valid_only_mem,
};
static int __init msm_pm_init(void)
suspend_set_ops(&msm_pm_ops);
suspend_ops = ops;
其中static const struct platform_suspend_ops *suspend_ops;是本文件的全局变量
bool valid_state(suspend_state_t state)
{
//调用上面注册valid函数
returnsuspend_ops&&suspend_ops->valid &&suspend_ops->valid(state);
}
上面注册的valid函数如下:
int suspend_valid_only_mem(suspend_state_t state)
{
return state ==PM_SUSPEND_MEM;
}
这些枚举变量定义如下:
#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)
还有一个函数是msm_pm_enter
static int msm_pm_enter(suspend_state_t state)
{
................................
rs_limits = msm_rpmrs_lowest_limits(false,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE, -1, -1);
...........................
if (rs_limits) {
ret = msm_rpmrs_enter_sleep(msm_pm_max_sleep_time, rs_limits, false, true);
if (!ret) {
int collapsed = msm_pm_power_collapse(false);//进入休眠,等待中断
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
collapsed = msm_pm_l2x0_power_collapse();
msm_pm_collapse();
ENTRY(msm_pm_collapse)//idle-v7.S(arch\arm\mach-msm)
....................
Wfi//休眠在此
............
msm_rpmrs_exit_sleep(rs_limits, false, true, collapsed);//退出休眠
}
}
enter_exit:
if (MSM_PM_DEBUG_SUSPEND & msm_pm_debug_mask)
pr_info("%s: return\n", __func__);
return 0;
}
其中wfi指令定义如下:
#define wfi()__asm__ __volatile__ ("wfi" : : : "memory")
WFI(Wait For Interrupt)指令,讓ARM等待外部中斷例如:IRQ或FIQ,對產品端而言就是手機的按鍵或是透過Real-Time Clock的中斷,喚醒處理器,恢復正常的執行.
WFI 会暂时将执行中断挂起,直至发生以下事件后再恢复执行:
发生 IRQ中断,不考虑CPSR I位
发生 FIQ中断,不考虑CPSR F位
发生不精确的数据中止,除非被 CPSR A位屏蔽
出现调试进入请求,无论是否启用调试
——————————————————————————————————————————————————————————————————————————————
接着上面的函数讲request_suspend_state()
enum {
SUSPEND_REQUESTED = 0x1,
SUSPENDED = 0x2,
SUSPEND_REQUESTED_AND_SUSPENDED=SUSPEND_REQUESTED| SUSPENDED,
};
static int state;// state全局变量
void request_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
old_sleep = state & SUSPEND_REQUESTED; //保存旧的suspend状态
//如果旧的状态不为SUSPEND_REQUESTED,并且新请求的状态为“mem”
if (!old_sleep && new_state != PM_SUSPEND_ON) {
state |= SUSPEND_REQUESTED;//把state的状态置为SUSPEND_REQUESTED
queue_work(suspend_work_queue, &early_suspend_work);//进入early suspend的处理
} else if (old_sleep && new_state == PM_SUSPEND_ON) {
//如果旧的状态为SUSPEND_REQUESTED,并且新请求的状态为“on”
state &= ~SUSPEND_REQUESTED;//清除SUSPEND_REQUESTED标记
wake_lock(&main_wake_lock);//锁定main_wake_lock,进入late resume处理流程
queue_work(suspend_work_queue, &late_resume_work);
}
requested_suspend_state= new_state;//保存请求的状态
}
下面看下early_suspend_work,late_resume_work的注册处理Wakelock.c (kernel\power)
static DEFINE_SPINLOCK(list_lock);
static LIST_HEAD(inactive_locks);
static struct list_headactive_wake_locks[WAKE_LOCK_TYPE_COUNT];
struct workqueue_struct *suspend_work_queue;
struct wake_lockmain_wake_lock;
suspend_state_trequested_suspend_state= PM_SUSPEND_MEM;
static int __init wakelocks_init(void)
{
for (i = 0; i
INIT_LIST_HEAD(&active_wake_locks[i]);
wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
wake_lock(&main_wake_lock);
..................
suspend_work_queue= create_singlethread_workqueue("suspend");
}
下面看下两个work的声明
static DECLARE_WORK(early_suspend_work, early_suspend);
static DECLARE_WORK(late_resume_work, late_resume);
处理函数如下
static void early_suspend(struct work_struct *work)
{
struct early_suspend *pos;
if (state == SUSPEND_REQUESTED)
state |= SUSPENDED;//如果为request,则继续置位suspend
else
abort = 1;
if (abort) {
goto abort;
}
suspend_sys_sync_queue();
abort:
if (state == SUSPEND_REQUESTED_AND_SUSPENDED)//如果状态为3
wake_unlock(&main_wake_lock);//解锁main_wake_lock
}
下面是该函数的实现:suspend_sys_sync_queue
static DECLARE_WORK(suspend_sys_sync_work,suspend_sys_sync);
void suspend_sys_sync_queue(void)
{
ret = queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work);
if (ret)
suspend_sys_sync_count++;//该work还没执行完
}
static voidsuspend_sys_sync(struct work_struct *work)
{
pr_info("PM: Syncing filesystems...\n");
sys_sync();//具体做同步文件系统的操作
pr_info("sync done.\n");
suspend_sys_sync_count--;//执行完毕,减一
}
static void late_resume(struct work_struct *work)
{
if (state == SUSPENDED)
state &= ~SUSPENDED;//把全局变量状态state取反
pr_info("late_resume: done\n");
}
下面进入wakelock的操作:
一个线程运行时,如果要防止手机进入休眠,则需要定义一个wake_lock,并且锁定该wake_lock。当允许睡眠时,解锁该wake_lock。以alarm设置的wake_lock (kernel\drivers\rtc\ alarm.c)为例:
定义:
static struct wake_lock alarm_rtc_wake_lock;
初始化
wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");
阻止手机进入睡眠时,锁定该wake_lock:
wake_lock(&alarm_rtc_wake_lock);
wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
允许手机进入睡眠时,解除锁定:
wake_unlock(&alarm_rtc_wake_lock);
wake_lock结构体的定义如下:(用于统计的字段没有列出)
struct wake_lock {
struct list_head link;//加入链表时用
int flags;//wake_lock的标志
const char *name;//名称
unsigned long expires;//超时记录
};
其中,flags各个bit的定义如下:
#define WAKE_LOCK_TYPE_MASK (0x0f)
#define WAKE_LOCK_INITIALIZED (1U /初始化时设置
#define WAKE_LOCK_ACTIVE (1U /锁定时设置
#define WAKE_LOCK_AUTO_EXPIRE (1U /定时锁定时设置
#define WAKE_LOCK_PREVENTING_SUSPEND (1U <
wake_lock的类型定义如下:
enum {
WAKE_LOCK_SUSPEND, /* Prevent suspend */
WAKE_LOCK_IDLE, /* Prevent low power idle */
WAKE_LOCK_TYPE_COUNT
};
void wake_lock_init(struct wake_lock *lock, int type, const char *name)
{
lock->name = name;
lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
INIT_LIST_HEAD(&lock->link);
list_add(&lock->link, &inactive_locks);
}
初始化时,把该wake_lock放入不活动的链表inactive_locks。
static LIST_HEAD(inactive_locks);
以下时刻会把一个wake_lock放入inactive_locks链表:
初始化wake_lock_init
解锁wake_lock
定时锁定超时expire_wake_lock
以下两个函数执行锁定wake_lock的操作:
void wake_lock(struct wake_lock *lock)
{
wake_lock_internal(lock, 0, 0);
}
void wake_lock_timeout(struct wake_lock *lock, long timeout)
{
wake_lock_internal(lock, timeout, 1);
}
wake_lock是硬锁定,只有通过wake_unlock才能解锁。
wake_lock_timeout则只锁定一段时间,超时后,自动解除锁定。
两个函数都调用wake_lock_internal。
wake_lock_internal的主要操作:
static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout)
{
type = lock->flags & WAKE_LOCK_TYPE_MASK; // WAKE_LOCK_SUSPEND = 0
lock->flags |= WAKE_LOCK_ACTIVE;
list_del(&lock->link);//从inactive_locks链表中删除
if (has_timeout) {
lock->expires = jiffies + timeout;
lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link, &active_wake_locks[type]);
}
else {
lock->expires = LONG_MAX;
lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
list_add(&lock->link, &active_wake_locks[type]);
}
if (type == WAKE_LOCK_SUSPEND) { //如果类型为WAKE_LOCK_SUSPEND
current_event_num++;
if (has_timeout)
expire_in = has_wake_lock_locked(type);//检查活动的wake_locks链表
else
expire_in = -1;
if (expire_in > 0) {
mod_timer(&expire_timer, jiffies + expire_in);//超时时间没到期
} else {
del_timer(&expire_timer)//没有超时
if (expire_in == 0)//没有锁定的wakelock,则suspend_work压入工作队列
queue_work(suspend_work_queue, &suspend_work);
}
}
锁定wake_lock时,把该wake_lock从inactive_locks链表中删除,然后加入active_wake_locks[type]链表,其定义如下:
static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
enum {
WAKE_LOCK_SUSPEND, /* Prevent suspend */
WAKE_LOCK_IDLE, /* Prevent low power idle */
WAKE_LOCK_TYPE_COUNT
};
void wake_unlock(struct wake_lock *lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock, irqflags);
type = lock->flags & WAKE_LOCK_TYPE_MASK;
lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link, &inactive_locks);
if (type == WAKE_LOCK_SUSPEND) {
long has_lock = has_wake_lock_locked(type);//检查活动的wake_locks链表
if (has_lock > 0) {
mod_timer(&expire_timer, jiffies + has_lock);
} else {
del_timer(&expire_timer)
if (has_lock == 0)
queue_work(suspend_work_queue, &suspend_work);
}
}
}
解锁一个wake_lock时,先设置其flag,然后把该wake_lock从活动的wake_locks链表中删除,加入非活动的wake_locks链表中。
上面两个函数同时都调到has_wake_lock_locked函数,来检查活动的wake_locks链表
static long has_wake_lock_locked(int type) {
long max_timeout = 0;
list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
long timeout = lock->expires - jiffies;
if (timeout <= 0)
expire_wake_lock(lock);
else if (timeout > max_timeout)
max_timeout = timeout;
} else
return -1;
}
return max_timeout;
}
has_wake_lock_locked返回三类数值:
大于0,活动的wake_lock中没有硬锁定的wake_lock,有定时锁定的wake_lock,返回值为最大的超时时间与当前时间的差。这时会启动定时器expire_timer,该定时器超时后执行函数expire_wake_locks,这时会再次检查是否有活动的wake_lock,如果没有了,则把suspend_work压入工作队列。
等于0,活动的wake_lock中没有硬锁定的wake_lock,定时锁定的wake_lock都已超时,解除了锁定(expire_wake_lock)。此时已没有任何锁定的wake_lock,可以执行休眠的操作了,把suspend_work压入工作队列。
小于0,-1,活动的wake_lock中有硬锁定的wake_lock。此时只能等待调用wake_unlock解除对应wake_lock的锁定。
static DECLARE_WORK(suspend_work, suspend);//所有的wakelock都解锁后,执行该函数
static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0); //超时后,执行该函数
static void expire_wake_locks(unsigned long data)
{
long has_lock;
has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);//检查有没有锁定?
if (has_lock == 0)
queue_work(suspend_work_queue, &suspend_work);
}
无论通过哪种途径,只要最后检测到没有锁定的wake_lock,则把suspend对应的work放入工作队列suspend_work_queue。
工作队列suspend_work_queue专门处理休眠唤醒相关的操作,在文件Wakelock.c (kernel\power)中定义:
struct workqueue_struct *suspend_work_queue;
在函数wakelocks_init中初始化:
suspend_work_queue = create_singlethread_workqueue("suspend");
static void suspend(struct work_struct *work)
{
suspend_sys_sync_queue();//同步文件系统
pr_info("suspend: enter suspend\n");
ret = pm_suspend(requested_suspend_state);
}
跟踪调用关系:
int pm_suspend(suspend_state_t state)
enter_state(state);
suspend_sys_sync_queue(); //同步文件系统
suspend_prepare();//(1)
suspend_devices_and_enter(state);//(2)
suspend_finish();//(3)与suspend_prepare函数的后半部分对比,完全相同。
挂起用户空间进程
——————————————————————————————————————
(1)suspend_prepare();
static int suspend_prepare(void)
{
pm_prepare_console();
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error = usermodehelper_disable();
if (error)
goto Finish;
error = suspend_freeze_processes();// 让各个进程进入休眠,可能某些进程不能进入休眠,// 则返回失败
if (!error)//如果顺利,正常进入休眠,从这里返回,否则,唤醒进程
return 0;
suspend_thaw_processes();// 后半部分恢复的操作,与suspend_finish的操作相同
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
其中suspend_freeze_processes();
int freeze_processes(void)//从串口打出来的log就是从该函数输出
{
printk("Freezing user space processes ... ");
error = try_to_freeze_tasks(true);
printk("done.\n");
error = suspend_sys_sync_wait();
printk("Freezing remaining freezable tasks ... ");
error = try_to_freeze_tasks(false);
printk("done.");
}
其中suspend_thaw_processes();
void thaw_processes(void)
{
printk("Restarting tasks ... ");
................
printk("done.\n");
}
——————————————————————————————————
设备的挂起,唤醒过程
——————————————————————————————————
(2)suspend_devices_and_enter()
int suspend_devices_and_enter(suspend_state_t state)
{
...............
suspend_console(); // 禁止控制台打印,此后,串口没有打印输出,此时会输出log://"Suspending console(s) (use no_console_suspend to debug)
..........
error = dpm_suspend_start(PMSG_SUSPEND);(2-1)
.....................
error = suspend_enter(state);(2-2)//休眠于此
Resume_devices:
dpm_resume_end(PMSG_RESUME);(2-3)
resume_console();
}
(2-1)dpm_suspend_start()//进入休眠
int dpm_suspend_start(pm_message_t state)
{
dpm_prepare(state);// 逐个执行每个device注册的prepare函数
dpm_suspend(state);// 逐个执行每个device注册的suspend函数
}
(2-2)suspend_enter(state);
suspend_ops->enter(state);//该函数已在前面分析过(msm_pm_enter),设备休眠在此
//唤醒后,继续向下执行
(2-3)dpm_resume_end()//退出休眠
void dpm_resume_end(pm_message_t state)
{
dpm_resume(state);// 逐个执行每个device的resume函数
dpm_complete(state);// 逐个执行每个device的complete函数
}
android 休眠唤醒驱动流程分析,Android4.0.4休眠唤醒机制分析(基于MSM8260)相关推荐
- android移植wifi驱动流程porting
android载入wifi驱动流程 wifi_load_driver check_wifi_chip_type_string get_wifi_device_id save_wifi_chip_typ ...
- 华为荣耀android是什么系统,华为荣耀Android4.0系统正式发布
查看原图 腾讯数码讯(水蓝)华为荣耀Honor手机自发布以来便有着超高的关注度,尤其是最近CDMA版本的亮相更是引人瞩目.不过,现在这款手机真正吸引人的则是最新Android 4.0 商用版的正式发布 ...
- android智能云电视,率先升级Android4.0 TCL3D智能云电视独领技术风潮
自去年底全新Android4.0操作系统发布,就以其超炫的UI界面及大幅提升的流畅体验迅速俘获了千万时尚人士的"芳心",搭载该系统更成为了高端智能产品的标志之一.权威彩电专家指出, ...
- android休眠唤醒驱动流程分析【转】
转自:http://blog.csdn.net/hanmengaidudu/article/details/11777501 标准linux休眠过程: l power managemen ...
- android 休眠唤醒驱动流程分析,Android 电源管理——gotosleep和userActivity关注
一.Android power management应用层分析 Android提供了android.os.PowerManager类,该类用于控制设备的电源状态的切换. 该类对外有三个接口函数: 1. ...
- android gallery2源码分析,Android4.2.2 Gallery2源码分析(8)——假装的Activity
两周的奋战,终于对Gallery的结构有了个大致的了解.Gallery是一个很好的Android系统知识的学习源码,不仅仅因为它设计到了多线程,布局优化,Opengl的结合等等内容.还有两点必须说明的 ...
- android高仿股票源码,从0到1开发 股票分析APP(附源码)
前言:再给自己挖个坑吧. 我想写个什么东西呢? 一:可以浏览当下相关资讯,以及大盘指数实时更新. 二:添加自选股票,可以查看该股票的走势图,相关资讯以及基本数据. 三:通过 server 端定义相关指 ...
- Android中级第十一讲之MotionEvent的分发、拦截机制分析
最近在研究事件分发机制,一方面在看内核剖析,一方面找测试小例子,最终找到,内容如下,--下载地址 咱们就借这个小例子来讲一些问题 一开始只关注了onTouch事件,应用于Window层,用于对操作事件 ...
- [网络安全自学篇] 七十.WannaCry勒索病毒复现及分析(三)蠕虫传播机制分析及IDA和OD逆向
这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了宏病毒相关知识,包括宏病毒基础原理.防御措施.自发邮件及APT28样本分 ...
最新文章
- 使用 AFNetworking 进行 XML 和 JSON 数据请求
- 数据结构之二叉搜索树/二叉查找数/有序二叉树/排序二叉树
- 解决fragment replace 重叠现象
- 移动端html游戏开发,GitHub - PromeYang/GameBuilder: GameBuilder 是移动端轻量HTML5游戏快速开发框架,主要应用于活动推广。...
- hive olap 数据仓库_数据仓库系统的实现和使用(含OLAP重点讲解)
- mysql索引篇之覆盖索引、联合索引、索引下推
- RPC远程过程调用之Hessian 基于HTTP
- 这个开源组织里的项目都是精品
- 玩游戏提示计算机性能过低,如果玩游戏的fps低怎么办?八个问题和九个解决方案...
- redis怎么连接mysql数据库_Golang连接Redis数据库的方法
- 我的docker随笔3:实现加速器,加快拉取镜像速度
- arm linux输出到lcd,求助 armlinux中实现lcd显示
- Kubeflow:连接云计算和机器学习的“桥梁”
- php 非常简单的导入sql文件
- 如何删除电脑里的android驱动程序,驱动安装失败 如何手动清除旧驱动程序
- 使用JS访问本地数据库
- 用LED驱动框架注册led设备的示例代码
- java floor(),Java floor()用法及代码示例
- eNSP模拟简单网络环境
- HSB/HSV/HSL区别