註冊和上報鍵值這裡就不在累贅了,本篇部落格主要要解決以下bug:
1.系統進入睡眠狀態後,如何通過tp喚醒系統。
2.如何解決快速雙擊時喚醒系統,長按時不喚醒系統。
要喚醒進入睡眠狀態的系統,首先要了解以下函式介面:
在了解函式界面前,講一下suspend和resume,
suspend:kernel會依次呼叫你註冊驅動裡的suspend函式,將各種外設都進入節電模式。最後cpu進入power down 模式。
resume:當用rtc或者gpio中的乙個將cpu從power down 模式喚醒。依次也會呼叫各個驅動裡的resume函式將外設喚醒,
進入正常工作狀態。
這些函式核心功能體現在它為底層的裝置驅動提供用於上報wakeup event的介面。
首先要定義乙個可以在睡眠狀態下喚醒cpu的gpio,用enable_irq_wake(irq);和disable_irq_wake(ts->irq);
disable_irq_wake(ts->irq);//禁止中斷喚醒功能
enable_irq_wake(ts->irq);//使能中斷喚醒功能
在進入suspend時,會將irq mask寫入到cpu。也就是告訴cpu哪些irq可以將其從睡眠中喚醒。
device_init_wakeup(&client->dev, 1);
用來設定乙個驅動可以被喚醒,函式中的can_wakeup為1時,表明乙個裝置可以被喚醒, 在裝置初始化的時候呼叫。
//設定dev的can_wakeup標誌,若是enable==1,同時呼叫device_wakeup_enable使能wakeup功能;
int device_init_wakeup(struct device *dev, bool enable)
...}// 裝置模型中的所有裝置都有兩個標誌來控制喚醒事件(可使得裝置或系統退出低功耗狀態)
static inline void device_set_wakeup_capable(struct device *dev, bool capable)
static inline int device_set_wakeup_enable(struct device *dev, bool enable)
can_wakeup為1時表明乙個裝置可以被喚醒,裝置驅動為了支援linux中的電源管理,有責任呼叫device_init_wakeup()來初始化can_wakeup。
而should_wakeup則是在裝置的電源狀態發生變化時被device_may_wakeup()用來測試,測試它該不該變化。
pm_stay_awake(&ts->client->dev);
中斷中,pm_stay_awake可以使的裝置不立即進入休眠,這個響應是非常快的。
當裝置有wakeup event正在處理時,需要呼叫該介面通知電源管理系統。
那處理完成後呢?driver要呼叫pm_relax通知電源管理系統。pm_relax釋放這個「鎖」,讓系統可以重新休眠。
pm_relax和pm_stay_awake成對出現,用於在event處理結束後通知電源管理系統。
以下提供乙個例子:
//想要具備喚醒系統的功能,就要從中斷上下功夫,如下:
#include ...
#include #include struct device * dev;
int ***_isr(int irq, void *dev_id)
int ***_probe(struct platform_device *pdev)
int __init ***_init(void)
module_initcall(***_init);
module_license("gpl");
這段**中對中斷做了兩個特殊的處理,乙個是在申請中斷時加上了irqf_no_suspend, 另乙個是irq_enable_wake(irq);
這兩個函式都可以賦予irq喚醒系統的能力,前者是在suspend過程中dpm_suspend_noirq()->suspend_devices_irq()時保留irqf_no_suspend型別的中斷響應,
而後者直接跟irq_chip打交道,把喚醒功能的設定交由irq_chip driver處理。從使用角度我覺得,irq_enable_wake()會是乙個更為保險且靈活的方法,
畢竟更為直接而且禁用喚醒功能方便,disable_irq_wake()即可。
需要在源**中新增如下:(改完之後,休眠狀態下測得的電流只是比正常休眠狀態下的電流多了tp的電流)
//suspend中新增
static int gsl_ts_suspend(struct device *dev)
//resume中新增
static int gsl_ts_resume(struct device *dev)
//在中斷上半部關中斷
static irqreturn_t gsl_ts_irq(int irq, void *dev_id)
//中斷執行完畢會開啟中斷
如果有中斷關閉就要在程式執行完畢後開啟中斷,如果邏輯出錯,會報如下錯誤:
warning:at /sda1/yzhao-work-1/qc706eu-s/msm8916_1605_444/kernel/kernel/irq/manage.c:529 irq_set_irq_wake+0x88/0xe8()
[ 8518.783716] —[ end trace 35ae10e4ba3033e2 ]—
一般是申請和釋放資源不匹配。
下面解決第二個bug,快速雙擊喚醒,長按不喚醒,這個可以有不同的邏輯實現,以下是我的邏輯,不正確的地方希望指出:
首先要熟悉上報座標的流程,這裡擷取有用的一段進行分析,中斷的下半部函式為,gsl_ts_xy_worker,其中有process_gslx680_data處理座標點,和上報座標點。
gsl螢幕支援多點觸控,一次按下時把多個點的座標值經過record_point計算成為乙個點,後用report_data上報。
函式中,有三個重要的變數,之前一直忽視了,卻起到了區分快速雙擊和長按的作用:
cinfo.finger_num----->記錄按下的手指數
id_state_old_flag[i]----->記錄上一次按下的狀態,是陣列,按下為1,抬起為0
id_state_flag[i]----->記錄此次按下的狀態,是陣列,沒按為0,按下為1
下面是這個函式的大致**,和加入的**,之後再分析區分思路
static void process_gslx680_data(struct gsl_ts *ts)
break;
case 1: //第二階段
if((cinfo.finger_num == 0) && (id_sign[1] ==0 ))
level = 2; //在level為1的基礎上,cinfo.finger_num == 0說明有手指頭抬起,但是可能沒有按下第二次
break;
case 2://第三階段
if((cinfo.finger_num == 1) && (id_sign[1] <= 8 ))
level = 3; //在level為2的基礎上,cinfo.finger_num再次為1,說明有手指頭又按下,完成一次雙擊觸控
break;
}if(level == 3) //level為3時候,說明完成一次雙擊觸控,可以喚醒系統
}...}
//喚醒系統
void open_lcd(struct gsl_ts *ts)
//一些全域性變數
static unsigned long old = 0;
extern unsigned long volatile jiffies;
static int level = 0;
static int gsl_halt_flag;
static int flag;
思路:
除錯的時候,根據log:pr_err(「xhlin^le=%d cinfo.finger_num=%d, id_state_old_flag[1]=%d, id_state_flag[1]=%d,id_sign[1]=%d; \n」,level,cinfo.finger_num,id_state_old_flag[1],id_state_flag[1],id_sign[1]);
發現快速雙擊的時候,cinfo.finger_num會由1,變為0,再變為1
長按的時候,cinfo.finger_num一直為1,且id_sign[1]的值不間斷變大
快速雙擊log
長按log
所以以此區別,來實現雙擊喚醒,每次喚醒後,和每次無效雙擊後,要把level清0
記一次tp喚醒函式異常導致的lcd喚醒慢
機器休眠後,按電源鍵喚醒,2s多螢幕才亮,檢視核心資訊,沒發現什麼報錯資訊。先檢查lcd的初始化 去掉多餘的延時,喚醒時間依然很長。繼續分析核心資訊,從按下電源鍵到開背光,用時2s多,發現tp喚醒時間較長。直接修改tp的i2c位址 或者拔掉tp 讓tp驅動不跑,喚醒時間就正常了。繼續分析tp的喚醒函...
告訴你最真實的TP公司
筆者自己把tp公司分為兩大類五小類 第一大類,硬tp公司。介紹 所謂硬tp公司就是以採購跟包銷為主要模式的tp公司。主要運營方法就是跟廠商承諾一年銷售多少交易額的貨物,然後廠商要給予低價供貨支援 銷售返點跟推廣廣告費支援。優勢 這樣從廠商的角度容易接受,因為羊毛出在羊身上,廠商會計算供貨的利潤跟廣告...
解決方案 duilib中禁止乙個視窗雙擊最大化
用duilib開發了乙個視窗,比如是登入視窗,那麼這個視窗的視窗的雙擊最大化就毫無意義,甚至帶來災難,我們就要明確禁止這樣的行為。我們應該明確,乙個視窗建立的時候就賦予了它一些屬性,那我們就首先看看如何建立視窗的,即create函式。例如,你定義了乙個視窗類叫loginwnd,你會在需要的時候這用進...