晒晒 early suspend 的源代码~
我对PM这块不太懂,不知道early suspend 干啥的,具体怎么干的。就知道应该是PM相关的,进入和退出休眠状态需要的代码,即suspend和resume。
1. early_suspend 数据结构:
struct early_suspend {
#ifdef CONFIG_HAS_EARLYSUSPEND //判断是否配置了 CONFIG_EARLY_SUSPEND 选项。
struct list_head link; //suspend_work_queue链表
int level; //suspend等级
void (*suspend)(struct early_suspend *h); //进入休眠
void (*resume)(struct early_suspend *h); //退出休眠
#endif
};
2. level,suspend分为3个等级
/* The early_suspend structure defines suspend and resume hooks to be called
* when the user visible sleep state of the system changes, and a level to
* control the order. They can be used to turn off the screen and input
* devices that are not used for wakeup.
* Suspend handlers are called in low to high level order, resume handlers are
* called in the opposite order. If, when calling register_early_suspend,
* the suspend handlers have already been called without a matching call to the
* resume handlers, the suspend handler will be called directly from
* register_early_suspend. This direct call can violate the normal level order.
*/
enum {
EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50, //level数值越小,suspend调用的越早。resume相反。
EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
};
3. 注册和注销 early_suspend 工作
void register_early_suspend(struct early_suspend *handler)
{
struct list_head *pos; //链表头
mutex_lock(&early_suspend_lock); //互斥锁 early_suspend_lock
list_for_each(pos, &early_suspend_handlers) { //iterate over a list
struct early_suspend *e;
e = list_entry(pos, struct early_suspend, link); //get the struct for this entry。
if (e->level > handler->level) //判断优先级
break;
}
list_add_tail(&handler->link, pos); //把early_suspend 加到list尾部。
if ((state & SUSPENDED) && handler->suspend)
handler->suspend(handler);
mutex_unlock(&early_suspend_lock);
}
EXPORT_SYMBOL(register_early_suspend);
void unregister_early_suspend(struct early_suspend *handler)
{
mutex_lock(&early_suspend_lock);
list_del(&handler->link);
mutex_unlock(&early_suspend_lock);
}
EXPORT_SYMBOL(unregister_early_suspend);
4. early_suspend 和 late_resume 具体实现:
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); //自旋锁 state_lock
if (state == SUSPEND_REQUESTED)
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);
if (abort) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("early_suspend: call handlers\n");
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\n");
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);
}
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\n", state);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: call handlers\n");
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\n");
abort:
mutex_unlock(&early_suspend_lock);
}
5. 读取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)\n",
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;
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);
}
suspend_state_t get_suspend_state(void)
{
return requested_suspend_state;
}