1. 使用者空間的介面
在kernel/power/main.c中,定義了一組sysfs的屬性檔案,其中一個定義是:
把這個宏展開後:
staticstruct kobj_attribute state_attr = { \ .attr ={ \ .name = "state", \ .mode = 0644, \ }, \ .show =state_show, \ .store =state_store, \}
我們再看看main.c的入口:
staticint __init pm_init(void){ ...... power_kobj =kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; return sysfs_create_group(power_kobj,&attr_group);}
顯然,該函數執行後,會在產生/sys/power目錄,該目錄下會建立一系列屬性檔案,其中一個就是/sys/power/state檔案。使用者空間向該檔案的寫入將會導致state_store被調用,讀取該檔案將會導致state_show函數被調用。
現在回到Android的HAL層中,查看一下代碼:hardware/libhardware_legacy/power/power.c:
//定義寫入/sys/power/state的命令字串staticconst char *off_state = "mem";staticconst char *on_state = "on";//開啟/sys/power/state等屬性檔案,儲存相應的檔案描述符staticintopen_file_descriptors(constchar * const paths[]){ int i; for (i=0; i<OUR_FD_COUNT; i++) { int fd = open(paths[i], O_RDWR); if (fd < 0) { fprintf(stderr, "fatal erroropening \"%s\"\n", paths[i]); g_error = errno; return -1; } g_fds[i] = fd; } g_error = 0; return 0;}
最終,使用者空間的電源管理系統會調用set_screen_state函數來觸發suspend的流程,該函數實際上就是往/sys/power/state檔案寫入"mem"或"on"命令字串。
intset_screen_state(inton){ ...... initialize_fds(); ...... char buf[32]; int len; if(on) len = snprintf(buf, sizeof(buf),"%s", on_state); else len = snprintf(buf, sizeof(buf),"%s", off_state); buf[sizeof(buf) - 1] = '\0'; len = write(g_fds[REQUEST_STATE], buf,len); ...... return 0;}
/********************************************************************************************/
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
/********************************************************************************************/2. 核心中資料結構和介面
與earlysuspend相關的資料結構和介面都在earlysuspend.h中進行了定義。
- early_suspend 結構
struct early_suspend {#ifdef CONFIG_HAS_EARLYSUSPEND structlist_head link; int level; void(*suspend)(struct early_suspend *h); void(*resume)(struct early_suspend *h);#endif};
希望執行early suspend的裝置,他的裝置驅動程式需要向電源管理系統註冊,該結構體用於向電源管理系統註冊earlysuspend/lateresume,當電源管理系統啟動suspend流程時,回呼函數suspend會被調用,相反,resume的最後階段,回呼函數resume會被調用,level欄位用於調整該結構體在註冊鏈表中的位置,suspend時,level的數值越小,回呼函數的被調用的時間越早,resume時則反過來。Android預先定義了3個level等級:
enum {EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,};
如果你想你的裝置在FB裝置被禁止之前執行他的early suspend回調,裝置驅動程式應該把level值設定為小於150的某個數值,然後向系統註冊early_suspend結構。註冊和反註冊函數是:
- void register_early_suspend(struct early_suspend *handler);
- void unregister_early_suspend(struct early_suspend *handler);
- early_suspend_handlers鏈表
所有註冊到系統中的early_suspend結構都會按level值按順序加入到全域鏈表early_suspend_handlers中。
3. 工作流程
首先,我們從kernel/power/wakelock.c中的初始化函數開始:
static int __init wakelocks_init(void){int ret;int i; ......for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)INIT_LIST_HEAD(&active_wake_locks[i]); ......wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");wake_lock(&main_wake_lock);wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups"); ......ret = platform_device_register(&power_device);ret = platform_driver_register(&power_driver); ......suspend_work_queue = create_singlethread_workqueue("suspend"); ......return 0;}
可以看到,顯示初始化active_wake_locks鏈表數組,然後初始化並且鎖住main_wake_lock,註冊平台裝置power_device,這些數組、鎖和power_device我們在後續文章再討論,這裡我們關注的最後一個動作:建立了一個工作隊列線程suspend_work_queue,該工作隊列是earlysuspend的核心所在。
系統啟動完成後,相關的驅動程式通過register_early_suspend()函數註冊了early suspend特性,等待一段時間後,如果沒有使用者活動(例如按鍵、觸控等操作),使用者空間的電源管理服務最終會調用第一節提到的set_screen_state()函數,透過sysfs,進而會調用到核心中的state_store():
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n){#ifdef CONFIG_SUSPEND#ifdef CONFIG_EARLYSUSPENDsuspend_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *p;int len;int error = -EINVAL;p = memchr(buf, '\n', 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_SUSPENDfor (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_EARLYSUSPENDif (state == PM_SUSPEND_ON || valid_state(state)) {error = 0;request_suspend_state(state);}#elseerror = enter_state(state);#endif#endif Exit:return error ? error : n;}
看到了沒,前一篇文章說過,suspend to disk做了特殊處理,這裡直接比較傳入的字串,而不是使用後續的pm_states數組,這裡我不關心suspend to disk,所以略過hibernate的分析。
緊接著,通過pm_states數組,根據命令字串查詢得到請求的狀態,預設情況下,Android的核心都會配置了CONFIG_EARLYSUSPEND,所以會調用request_suspend_state()函數,不過在調用該函數之前會先valid_state()一下,這給了平台相關的代碼一個機會確認該平台是否支援所請求的電源狀態。valid_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 (!old_sleep && new_state != PM_SUSPEND_ON) {state |= SUSPEND_REQUESTED;if (queue_work(suspend_work_queue, &early_suspend_work))pr_info("early_suspend_work is in queue already\n");} else if (old_sleep && new_state == PM_SUSPEND_ON) {state &= ~SUSPEND_REQUESTED;wake_lock(&main_wake_lock);if (!queue_work(suspend_work_queue,&late_resume_work))pr_info("late_resume_work is in queue already\n");}requested_suspend_state = new_state;spin_unlock_irqrestore(&state_lock, irqflags);}
還記得前面初始化時建立的工作隊列suspend_woek_queue嗎?根據之前的電源狀態和請求的狀態, request_suspend_state()只是簡單地向suspend_work_queue中加入early_suspend_work或者是late_resume_work並調度他們執行。early_suspend_work的工作函數是early_suspend():
staticvoid 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;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
......
}
......
list_for_each_entry(pos,&early_suspend_handlers, link) {
if (pos->suspend != NULL) {
if (debug_mask &DEBUG_SUSPEND)
printk(KERN_DEBUG"pos->suspend: %pF begin\n", pos->suspend);
pos->suspend(pos);
if (debug_mask &DEBUG_SUSPEND)
printk(KERN_DEBUG"pos->suspend: %pF finish\n", pos->suspend);
}
}
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);
}
終於看到啦,early_suspend()遍曆early_suspend_handlers鏈表,從中取出各個驅動程式註冊的early_suspend結構,然後調用它的suspend回呼函數。最後,釋放main_wake_lock鎖,至此整個earlysuspend的流程完成。下面的順序圖表清晰地表明了整個調用的過程:
圖3.1 early suspend調用流程
但是,這時整個系統只是處於所謂的idle狀態,cpu還在工作,後台進程也在工作中,那什麼時候系統會真正地進入睡眠狀態?注意到最後一句關鍵的調用了沒有:
wake_unlock(&main_wake_lock);
解鎖動作會觸發標準linux的suspend流程,這個過程就留給寫一篇文章討論吧。