
沉淀、分享、成长,让自己和他人都能有所收获!😄
Linux 驱动课程火热更新中:放大招!瑞芯微底层驱动课程~

autosleep 基于wakeup source实现, 用于取代android wakelock中的自动休眠功能。
autosleep实现代码位于“kernel/power/autosleep.c”。
编译内核时, 必须配置CONFIG_PM_AUTOSLEEP选项才能enable autosleep。
autosleep在sysfs中提供了一个属性文件“/sys/power/autosleep”用以配置autosleep, 类似于“/sys/power/state”文件可以控制suspend到不同的状态, autosleep也可配置不同的状态:
向“/sys/power/autosleep”写入以上5个值的任意一个可将autosleep切换到指定的状态,一旦写入非“off”值, autosleep就开始运行
读取“/sys/power/autosleep”可以获得当前的状态, 特别的, 如果系统不支持autosleep, 读取该文件会输出”error”
“/sys/power/autosleep”的show()/store()方法由pm_autosleep_state()/pm_autosleep_set_state()来实现,在下面会详细解释
autosleep的初始化函数pm_autosleep_init()在PM的初始化时(kernel/power/main.c:pm_init)被调用:
int __init pm_autosleep_init(void)
{
autosleep_ws =wakeup_source_register("autosleep");
if(!autosleep_ws)
return-ENOMEM;
autosleep_wq =alloc_ordered_workqueue("autosleep",0);
if(autosleep_wq)
return0;
wakeup_source_unregister(autosleep_ws);
return-ENOMEM;
}
staticDECLARE_WORK(suspend_work, try_to_suspend);
voidqueue_up_suspend_work(void)
{
if(autosleep_state > PM_SUSPEND_ON)
queue_work(autosleep_wq,&suspend_work);
}
可以看到, queue_up_suspend_work()中会调度work, 运行try_to_suspend()来触发休眠, 那么什么时候会调用queue_up_suspend_work()呢? 是在“/sys/power/autosleep”的store()方法pm_autosleep_state()中调用的
pm_autosleep_state()的代码比较简单
intpm_autosleep_set_state(suspend_state_t state)
{
#ifndefCONFIG_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);
queue_up_suspend_work();
}else{
pm_wakep_autosleep_enabled(false);
}
mutex_unlock(&autosleep_lock);
return0;
}
staticvoidtry_to_suspend(structwork_struct*work)
{
unsignedint initial_count, final_count;
if(!pm_get_wakeup_count(&initial_count, true))
goto out;
mutex_lock(&autosleep_lock);
if(!pm_save_wakeup_count(initial_count)||
system_state != SYSTEM_RUNNING){
mutex_unlock(&autosleep_lock);
goto out;
}
if(autosleep_state == PM_SUSPEND_ON){
mutex_unlock(&autosleep_lock);
return;
}
if(autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state);
mutex_unlock(&autosleep_lock);
if(!pm_get_wakeup_count(&final_count, false))
goto out;
if(final_count == initial_count)
schedule_timeout_uninterruptible(HZ /2);
out:
queue_up_suspend_work();
}