zoukankan      html  css  js  c++  java
  • early_suspend【转】

    android 休眠唤醒机制分析(二) — early_suspend

    early_suspend是Android休眠流程的第一阶段即浅度休眠,不会受到wake_lock的阻止,一般用于关闭lcd、tp等设备为运行的应用节约电能。Android的PowerManagerService会根据用户的操作情况调整电源状态,如果需要休眠则会调用到HAL层的set_screen_state()接口,在set_screen_state()中会向/sys/power/state节点写入"mem"值让驱动层开始进入休眠流程。

    一、休眠唤醒机制及其用户空间接口

    Linux系统支持如下休眠唤醒等级

    const char *const pm_states[PM_SUSPEND_MAX] = {
    #ifdef CONFIG_EARLYSUSPEND
    	[PM_SUSPEND_ON]		= "on",
    #endif
    	[PM_SUSPEND_STANDBY]	= "standby",
    	[PM_SUSPEND_MEM]	= "mem",
    };
    

    但在Android中一般只支持"on"和"mem",其中"on"为唤醒设备,"mem"为休眠设备。/sys/power/state节点的读写操作如下:

    static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
    			  char *buf)
    {
    	char *s = buf;
    #ifdef CONFIG_SUSPEND
    	int i;
     
    	for (i = 0; i < PM_SUSPEND_MAX; i++) {
    		if (pm_states[i] && valid_state(i))
    			s += sprintf(s,"%s ", pm_states[i]);  // 打印系统支持的休眠等级
    	}
    #endif
    #ifdef CONFIG_HIBERNATION
    	s += sprintf(s, "%s
    ", "disk");
    #else
    	if (s != buf)
    		/* convert the last space to a newline */
    		*(s-1) = '
    ';
    #endif
    	return (s - buf);
    }
     
    static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
    			   const char *buf, size_t n)
    {
    #ifdef CONFIG_SUSPEND
    #ifdef CONFIG_EARLYSUSPEND
    	suspend_state_t state = PM_SUSPEND_ON;
    #else
    	suspend_state_t state = PM_SUSPEND_STANDBY;
    #endif
    	const char * const *s;
    #endif
    	char *p;
    	int len;
    	int error = -EINVAL;
     
    	p = memchr(buf, '
    ', n);
    	len = p ? p - buf : n;
     
    	/* First, check if we are requested to hibernate */
    	if (len == 4 && !strncmp(buf, "disk", len)) {
    		error = hibernate();
      goto Exit;
    	}
     
    #ifdef CONFIG_SUSPEND
    	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
    		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
    			break;
    	}
    	if (state < PM_SUSPEND_MAX && *s)
    #ifdef CONFIG_EARLYSUSPEND
    		if (state == PM_SUSPEND_ON || valid_state(state)) {
    			error = 0;
    			request_suspend_state(state);  // 请求进入android的休眠流程
    		}
    #else
    		error = enter_state(state);  // linux的标准休眠流程
    #endif
    #endif
     
     Exit:
    	return error ? error : n;
    }
     
    power_attr(state);
    

    其中state_show()为节点的读函数,主要打印出系统支持的休眠等级;state_store()为节点的写函数,根据参数请求休眠或者唤醒流程。节点的创建代码如下:

    static struct attribute * g[] = {
    	&state_attr.attr,        // state节点
    #ifdef CONFIG_PM_TRACE
    	&pm_trace_attr.attr,
    #endif
    #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
    	&pm_test_attr.attr,      // pm_test节点
    #endif
    #ifdef CONFIG_USER_WAKELOCK
    	&wake_lock_attr.attr,    // wake_lock节点
    	&wake_unlock_attr.attr,  // wake_unlock节点
    #endif
    	NULL,
    };
     
    static struct attribute_group attr_group = {
    	.attrs = g,
    };
     
    static int __init pm_init(void)
    {
    	int error = pm_start_workqueue();
    	if (error)
    		return error;
    	power_kobj = kobject_create_and_add("power", NULL);  // 创建power节点
    	if (!power_kobj)
    		return -ENOMEM;
    	return sysfs_create_group(power_kobj, &attr_group);  // 创建一组属性节点
    }
     
    core_initcall(pm_init);
    
    

    二、early_suspend 实现

    1、early_suspend 定义、接口及其用法

    enum {
    	EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
    	EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
    	EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
    };
    struct early_suspend {
    #ifdef CONFIG_HAS_EARLYSUSPEND
    	struct list_head link;  // 链表节点
    	int level;              // 优先等级
    	void (*suspend)(struct early_suspend *h);
    	void (*resume)(struct early_suspend *h);
    #endif
    };
    

    可以看到early_suspend由两个函数指针、链表节点、优先等级组成;内核默认定义了3个优先等级,在suspend的时候先执行优先等级低的handler,在resume的时候则先执行等级高的handler,用户可以定义自己的优先等级;early_suspend向内核空间提供了2个接口用于注册和注销handler:

    void register_early_suspend(struct early_suspend *handler);
    void unregister_early_suspend(struct early_suspend *handler);
    

    其中register_early_suspend()用于注册,unregister_early_suspend用于注销;一般early_suspend的使用方式如下:

    ts->earlysuspend.suspend = sitronix_i2c_suspend_early;
    ts->earlysuspend.resume = sitronix_i2c_resume_late;
    ts->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
    register_early_suspend(&ts->earlysuspend);
    

    设置好suspendresume接口,定义优先等级,然后注册结构即可。

    2、初始化信息

    我们看一下early_suspend需要用到的一些数据:

    static DEFINE_MUTEX(early_suspend_lock);
    static LIST_HEAD(early_suspend_handlers);  // 初始化浅度休眠链表
    // 声明3个工作队列用于同步、浅度休眠和唤醒
    static void early_sys_sync(struct work_struct *work);
    static void early_suspend(struct work_struct *work);
    static void late_resume(struct work_struct *work);
    static DECLARE_WORK(early_sys_sync_work,early_sys_sync);
    static DECLARE_WORK(early_suspend_work, early_suspend);
    static DECLARE_WORK(late_resume_work, late_resume);
    static DEFINE_SPINLOCK(state_lock);
    enum {
    	SUSPEND_REQUESTED = 0x1,  // 当前正在请求浅度休眠
    	SUSPENDED = 0x2,          // 浅度休眠完成
    	SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
    };
    static int state;
    
    

    初始化了一个链表early_suspend_handlers用于管理early_suspend,还定义读写链表用到的互斥体;另外还声明了3个工作队列,分别用于缓存同步、浅度休眠和唤醒;还声明了early_suspend操作的3个状态。

    3、register_early_suspend 和 unregister_early_suspend

    void register_early_suspend(struct early_suspend *handler)
    {
    	struct list_head *pos;
     
    	mutex_lock(&early_suspend_lock);
    	// 遍历浅度休眠链表
    	list_for_each(pos, &early_suspend_handlers) {
    		struct early_suspend *e;
    		e = list_entry(pos, struct early_suspend, link);
    		// 判断当前节点的优先等级是否大于handler的优先等级
    		// 以此决定handler在链表中的顺序
    		if (e->level > handler->level)
    			break;
    	}
    	// 将handler加入当前节点之前,优先等级越低越靠前
    	list_add_tail(&handler->link, pos);
    	if ((state & SUSPENDED) && handler->suspend)
    		handler->suspend(handler);
    	mutex_unlock(&early_suspend_lock);
    }
    EXPORT_SYMBOL(register_early_suspend);
    

    注册的流程比较简单,首先遍历链表,依次比较每个节点的优先等级,如果遇到优先等级比新节点优先等级高则跳出,然后将新节点加入优先等级较高的节点前面,这样就确保了链表是优先等级低在前高在后的顺序;在将节点加入链表后查看当前状态是否为浅度休眠完成状态,如果是则执行handler的suspend函数。

    4、request_suspend_state

    前面我们看到用户空间在写/sys/power/state节点的时候会执行request_suspend_state()函数,该函数代码如下:

    void request_suspend_state(suspend_state_t new_state)
    {
    	unsigned long irqflags;
    	int old_sleep;
     
    	spin_lock_irqsave(&state_lock, irqflags);
    	old_sleep = state & SUSPEND_REQUESTED;
    	// 打印当前状态
    	if (debug_mask & DEBUG_USER_STATE) {
    		struct timespec ts;
    		struct rtc_time tm;
    		getnstimeofday(&ts);
    		rtc_time_to_tm(ts.tv_sec, &tm);
    		pr_info("request_suspend_state: %s (%d->%d) at %lld "
    			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)
    ",
    			new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
    			requested_suspend_state, new_state,
    			ktime_to_ns(ktime_get()),
    			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
    			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
    	}
    	// 如果新状态是休眠状态
    	if (!old_sleep && new_state != PM_SUSPEND_ON) {
    		state |= SUSPEND_REQUESTED;
    		pr_info("sys_sync_work_queue early_sys_sync_work.
    ");
    		// 执行缓存同步与浅度休眠的工作队列
    		queue_work(sys_sync_work_queue, &early_sys_sync_work);
    		queue_work(suspend_work_queue, &early_suspend_work);
    	} else if (old_sleep && new_state == PM_SUSPEND_ON) {
    	// 如果新状态是唤醒状态
    		state &= ~SUSPEND_REQUESTED;
    		// 激活内核锁
    		wake_lock(&main_wake_lock);
    		// 执行浅度唤醒的工作队列
    		queue_work(suspend_work_queue, &late_resume_work);
    	}
    	// 更新全局状态
    	requested_suspend_state = new_state;
    	spin_unlock_irqrestore(&state_lock, irqflags);
    }
    
    

    函数首先打印出当前状态变化的log,然后判断新状态,如果是休眠状态则置位SUSPEND_REQUESTED标志,然后将同步缓存、浅度休眠工作队列加入相应的内核线程执行;如果新状态是唤醒则首先将main_wake_lock激活,然后再将浅度唤醒工作队列加入内核线程执行;最后更新全局状态变量,因为提供了一个内核空间接口用于获取当前休眠唤醒状态:

    // 返回系统状态值
    suspend_state_t get_suspend_state(void)
    {
    	return requested_suspend_state;
    }
    

    5、early_suspend_work、late_resume_work 和 early_sys_sync

    static void early_suspend(struct work_struct *work)
    {
    	struct early_suspend *pos;
    	unsigned long irqflags;
    	int abort = 0;
     
    	mutex_lock(&early_suspend_lock);
    	spin_lock_irqsave(&state_lock, irqflags);
    	if (state == SUSPEND_REQUESTED)  // 判断当前状态是否在请求浅度休眠
    		state |= SUSPENDED;      // 如果是则置位SUSPENDED
    	else
    		abort = 1;
    	spin_unlock_irqrestore(&state_lock, irqflags);
     
    	if (abort) {  // 取消early_suspend
    		if (debug_mask & DEBUG_SUSPEND)
    			pr_info("early_suspend: abort, state %d
    ", state);
    		mutex_unlock(&early_suspend_lock);
    		goto abort;
    	}
     
    	if (debug_mask & DEBUG_SUSPEND)
    		pr_info("early_suspend: call handlers
    ");
    	// 遍历浅度休眠链表并执行其中所有suspend函数
    	// 执行顺序根据优先等级而定,等级越低越先执行
    	list_for_each_entry(pos, &early_suspend_handlers, link) {
    		if (pos->suspend != NULL)
    			pos->suspend(pos);
    	}
    	mutex_unlock(&early_suspend_lock);
     
    	if (debug_mask & DEBUG_SUSPEND)
    		pr_info("early_suspend: sync
    ");
     
    	/* Remove sys_sync from early_suspend, and use work queue to complete sys_sync */
    	//sys_sync();
    abort:
    	spin_lock_irqsave(&state_lock, irqflags);
    	if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
    		wake_unlock(&main_wake_lock);
    	spin_unlock_irqrestore(&state_lock, irqflags);
    }
    
    

    在suspend流程中首先判断当前状态是否为SUSPEND_REQUESTED,如果是则置位SUSPENDED标志,如果不是则取消suspend流程;然后遍历浅度休眠链表,从链表头部到尾部依次调用各节点的suspend()函数,执行完后判断当前状态是否为SUSPEND_REQUESTED_AND_SUSPENDED,如果是则释放main_wake_lock,当前系统中如果只存在main_wake_lock这个有效锁,则会在wake_unlock()里面启动深度休眠线程,如果还有其他其他wake_lock则保持当前状态。

    static void late_resume(struct work_struct *work)
    {
    	struct early_suspend *pos;
    	unsigned long irqflags;
    	int abort = 0;
     
    	mutex_lock(&early_suspend_lock);
    	spin_lock_irqsave(&state_lock, irqflags);
    	if (state == SUSPENDED)  // 清除浅度休眠完成标志
    		state &= ~SUSPENDED;
    	else
    		abort = 1;
    	spin_unlock_irqrestore(&state_lock, irqflags);
     
    	if (abort) {
    		if (debug_mask & DEBUG_SUSPEND)
    			pr_info("late_resume: abort, state %d
    ", state);
    		goto abort;
    	}
    	if (debug_mask & DEBUG_SUSPEND)
    		pr_info("late_resume: call handlers
    ");
    	// 反向遍历浅度休眠链表并执行其中所有resume函数
    	// 执行顺序根据优先等级而定,等级越高越先执行
    	list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
    		if (pos->resume != NULL)
    			pos->resume(pos);
    	if (debug_mask & DEBUG_SUSPEND)
    		pr_info("late_resume: done
    ");
    abort:
    	mutex_unlock(&early_suspend_lock);
    }
    
    

    在resume流程中同样首先判断当前状态是否为SUSPENDED,如果是则清除SUSPENDED标志,然后反向遍历浅度休眠链表,按照优先等级从高到低的顺序执行节点的resume()函数。

    static void early_sys_sync(struct work_struct *work)
    {
    	wake_lock(&sys_sync_wake_lock);
    	sys_sync();
    	wake_unlock(&sys_sync_wake_lock);
    }
    
    

    内核专门为缓存同步建立了一个线程,同时还创建了sys_sync_wake_lock防止在同步缓存时系统进入深度休眠。

  • 相关阅读:
    HDU 2844 Coins(多重背包)
    HDU 4540 威威猫系列故事——打地鼠(DP)
    Codeforces Round #236 (Div. 2)
    FZU 2140 Forever 0.5
    HDU 1171 Big Event in HDU(DP)
    HDU 1160 FatMouse's Speed(DP)
    ZOJ 3490 String Successor
    ZOJ 3609 Modular Inverse
    ZOJ 3603 Draw Something Cheat
    ZOJ 3705 Applications
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/9279648.html
Copyright © 2011-2022 走看看