這兩天一直忙著搞插入OTG死機的問題。有時候機器上面插入個U盤,鼠標,或者硬盤,如果這個時候沒有接充電器的話,有很大的概率機器直接死機了,而且必須插入充電器才能開機。實測電池電壓基本為0,原來是電池過流保護了。由於采購的電池保護點過低(1.5A的時候就掛了),只能想辦法把輸出電流降到最低了,嘗試過很多方法,調低背光亮度可以有效解決在正常使用中死機,但是插入OTG死機的問題一直沒有得到解決。
硬件部同事說在插入OTG的時候,充電芯片上仍然有500mA的電流輸入,奇怪,這個時候又不是USB充電狀態,怎麼會有電流呢?不管他,硬件做的事情,俺不懂。想辦法把這個電流去掉吧,查了下充電芯片的文檔,還真有防止OTG插入時回流的,設個寄存器,OK。
今天把OTG插上,接上串口,發現機器竟然不休眠了。問了下同事,說插入OTG就是不休眠的。於是看了下代碼,這裡就簡單分析下吧。
大家知道Android系統獨有的wakelock鎖,只要某個模塊還持有wakelock,系統就不能進入休眠。USB OTG 也是這麼干的。
在drivers/usb/otg/otg-wakelock.c中
wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
vbus_lock.name); //這裡初始化一個wakelock鎖
otgwl_nb.notifier_call = otgwl_otg_notifications;
ret = otg_register_notifier(otgwl_xceiv, &otgwl_nb);
//這裡定義一個通知鏈,當有USB EVENT發生時(比如USB 的插入,拔掉),其他內核模塊會調用該通知鏈
由於都是在中斷上下文處理的,otg_register_notifier實現為atomic_notifier_chain_register
看下otgwl_otg_notifications的實現吧
static int otgwl_otg_notifications(struct notifier_block *nb,
unsigned long event, void *unused)
{
otgwl_handle_event(event);
return NOTIFY_OK;
}
可以看出來,該通知鏈就是用來處理OTG(應該所有的USB事件)事件的。
static void otgwl_handle_event(unsigned long event)
{
unsigned long irqflags;
spin_lock_irqsave(&otgwl_spinlock, irqflags);
if (!enabled) {
otgwl_drop(&vbus_lock);
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
return;
}
switch (event) {
case USB_EVENT_VBUS:
case USB_EVENT_ID:
case USB_EVENT_ENUMERATED:
otgwl_hold(&vbus_lock); //如果是這三種類型,則進行wakelock加鎖操作,從而不會進入休眠了。USB_EVENT_ID就是接OTG了,因為接了OTG,USB的ID會接地,所以這樣命名。
break;
case USB_EVENT_NONE: //拔掉USB的時候,加一個超時鎖,2s後自動解鎖
case USB_EVENT_CHARGER:
otgwl_temporary_hold(&vbus_lock);
break;
default:
break;
}
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
}
再看看在什麼地方通知的該通知鏈
drivers/usb/otg/twl6030-usb.c中的USB中斷處理函數中
當插入或拔掉USB,會有一個中斷發生,在中斷處理函數中發通知
atomic_notifier_call_chain(&twl->otg.notifier,
status, &charger_type);
這樣就做到了當插入USB OTG的時候,系統不能進入休眠。還不知道為什麼這麼做。難道是怕插入U盤,拷貝東西的時候系統休眠從而導致一些問題?