1.??????用戶空間的接口
在kernel/power/main.c中,定義了一組sysfs的屬性文件,其中一個定義是:
power_attr(state);
把這個宏展開后:
[cpp]?view plain?copy
staticstruct?kobj_attribute?state_attr?=?{?\??
.attr?={????????????????????????????????\??
.name?=?"state",????\??
.mode?=?0644,???????????????????????????\??
},????????????????????????????????????????????\??
.show??????=state_show,???????????????????????????\??
.store???????=state_store,??????????????????\??
}??
我們再看看main.c的入口:
[cpp]?view plain?copy
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:
[cpp]?view plain?copy
//定義寫入/sys/power/state的命令字符串??
staticconst?char?*off_state?=?"mem";??
staticconst?char?*on_state?=?"on";??
//打開/sys/power/state等屬性文件,保存相應的文件描述符??
staticint??
open_file_descriptors(constchar?*?const?paths[])??
{??
int?i;??
for?(i=0;?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"命令字符串。
[cpp]?view plain?copy
int??
set_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 結構
[cpp]?view plain?copy
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等級:
[cpp]?view plain?copy
enum?{??
EARLY_SUSPEND_LEVEL_BLANK_SCREEN?=?50,??
EARLY_SUSPEND_LEVEL_STOP_DRAWING?=?100,??
EARLY_SUSPEND_LEVEL_DISABLE_FB?=?150,??
};??
[cpp]?view plain?copy
如果你想你的設備在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中的初始化函數開始:
[cpp]?view plain?copy
static?int?__init?wakelocks_init(void)??
{??
int?ret;??
int?i;??
......??
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);??
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():
[cpp]?view plain?copy
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',?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?
if?(*s?&&?len?==?strlen(*s)?&&?!strncmp(buf,?*s,?len))??
break;??
}??
if?(state?
#ifdef?CONFIG_EARLYSUSPEND??
if?(state?==?PM_SUSPEND_ON?||?valid_state(state))?{??
error?=?0;??
request_suspend_state(state);??
}??
#else??
error?=?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()的具體實現請參考內核代碼樹。
[cpp]?view plain?copy
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);
?
評論
查看更多