關鍵詞:android 電容屏 tp 工作隊列 中斷 多點觸摸協議
平台資訊:
核心:linux2.6/linux3.0
系統:android/android4.0
平台:S5PV310(samsung exynos 4210)
作者:xubin341719(歡迎轉載,請註明作者)
參考網站:http://edsionte.com/techblog/archives/1582
這部分參考別人的多一點
android 電容屏(一):電容屏基本原理篇
android 電容屏(二):驅動調試之基本概念篇
android 電容屏(三):驅動調試之驅動程式分析篇
電容屏驅動調試先瞭解Linux電容屏驅動中幾個常用的概念:
中斷下半部-工作隊列;
input機制;
Linux與Android 多點觸摸協議。
一、中斷下半部-工作隊列
1、中斷
先看一下宋寶華先生的《linux裝置驅動開發詳解》裡面對中斷的描述吧。這本書個人感覺 寫的比較好,從開始學驅動到現在,還能從中得到不少知識。
裝置的中斷會打斷核心中進程的正常調度和運行,系統對更高吞吐率的追求勢必要求中斷服務程式儘可能地短小精悍。但是,這個良好的願望往往與現實並不吻合。在大多數真實的系統中,當中斷到來時,要完成的工作往往並不會是短小的,它可能要進行較大量的耗時處理。如描述了Linux核心的中斷處理機制。為了在中斷執行時間儘可能短和中斷處理需完成大量工作之間找到一個平衡點,Linux將中斷處理常式分解為兩個半部:頂半部(top
half)和底半部(bottom half)。頂半部完成儘可能少的比較緊急的功能,它往往只是簡單地讀取寄存器中的中斷狀態並清除中斷標誌後就進行“登記中斷”的工作。“登記中斷”意味著將底半部處理常式掛到該裝置的底半部執行隊列中去。這樣,頂半部執行的速度就會很快,可以服務更多的插斷要求。現在,中斷處理工作的重心就落在了底半部的頭上,它來完成中斷事件的絕大多數任務。底半部幾乎做了中斷處理常式所有的事情,而且可以被新的中斷打斷,這也是底半部和頂半部的最大不同,因為頂半部往往被設計成不可中斷。底半部則相對來說並不是非常緊急的,而且相對比較耗時,不在硬體中斷服務程式中執行。儘管頂半部、底半部的結合能夠改善系統的響應能力,但是,僵化地認為Linux裝置驅動中的中斷處理一定要分兩個半部則是不對的。如果中斷要處理的工作本身很少,則完全可以直接在頂半部全部完成。
其實上面這一段大致說明一個問題,那就是:中斷要儘可能耗時比較短,儘快恢複系統正常調試,所以把中斷觸發、中斷執行分開,也就是所說的“上半部分(中斷觸發)、底半部(中斷執行)”,其實就是我們後面說的中斷上下文。下半部分一般有tasklet、工作隊列實現,觸控螢幕中中斷實現以工作隊列形式實現的,所以我們今天重點講工作隊列。
2、為什麼還需要工作隊列?
工作隊列(work queue)是另外一種將中斷的部分工作推後的一種方式,它可以實現一些tasklet不能實現的工作,比如工作隊列機制可以睡眠。這種差異的本質原因是,在工作隊列機制中,將推後的工作交給一個稱之為工作者線程(worker thread)的核心線程去完成(單核下一般會交給預設的線程events/0)。因此,在該機制中,當核心在執行中斷的剩餘工作時就處在進程上下文(process
context)中。也就是說由工作隊列所執行的中斷代碼會表現出進程的一些特性,最典型的就是可以重新調度甚至睡眠。
對於tasklet機制(中斷處理常式也是如此),核心在執行時處於中斷上下文(interrupt context)中。而中斷上下文與進程毫無瓜葛,所以在中斷上下文中就不能睡眠。因此,選擇tasklet還是工作隊列來完成下半部分應該不難選擇。當推後的那部分中斷程式需要睡眠時,工作隊列毫無疑問是你的最佳選擇;否則,還是用tasklet吧。
3、中斷上下文
有上面那個圖可以看出上下兩部分吧,就是上下文吧,這個比較好理解。
看下別人比較專業的解釋:
在瞭解中斷上下文時,先來回顧另一個熟悉概念:進程上下文(這個中文翻譯真的不是很好理解,用“環境”比它好很多)。一般的進程運行在使用者態,如果這個進程進行了系統調用,那麼此時使用者空間中的程式就進入了核心空間,並且稱核心代表該進程運行於核心空間中。由於使用者空間和核心空間具有不同的地址映射,並且使用者空間的進程要傳遞很多變數、參數給核心,核心也要儲存使用者進程的一些寄存器、變數等,以便系統調用結束後回到使用者空間繼續執行。這樣就產生了進程上下文。
所謂的進程上下文,就是一個進程在執行的時候,CPU的所有寄存器中的值、進程的狀態以及堆棧中的內容。當核心需要切換到另一個進程時(環境切換),它需要儲存當前進程的所有狀態,即儲存當前進程的進程上下文,以便再次執行該進程時,能夠恢複切換時的狀態繼續執行。上述所說的工作隊列所要做的工作都交給工作者線程來處理,因此它可以表現出進程的一些特性,比如說可以睡眠等。 對於中斷而言,是硬體通過觸發訊號,導致核心調用中斷處理常式,進入核心空間。這個過程中,硬體的一些變數和參數也要傳遞給核心,核心通過這些參數進行中斷處理,中斷上下文就可以理解為硬體傳遞過來的這些參數和核心需要儲存的一些環境,主要是被中斷的進程的環境。因此處於中斷內容相關的tasklet不會有睡眠這樣的特性。
4、工作隊列的使用方法
核心中通過下述結構體來表示一個具體的工作:
struct work_struct { unsigned long pending;//這個工作是否正在等待處理 struct list_head entry;//連結所有工作的鏈表,形成工作隊列 void (*func)(void *);//處理函數 void *data;//傳遞給處理函數的參數 void *wq_data;//內部使用資料 struct timer_list timer;//延遲的工作隊列所用到的定時器 };
而這些工作(結構體)連結成的鏈表就是所謂的工作隊列。工作者線程會在被喚醒時執行鏈表上的所有工作,當一個工作被執行完畢後,相應的work_struct結構體也會被刪除。當這個工作鏈表上沒有工作時,背景工作執行緒就會休眠。
(1)、通過如下宏可以建立一個要推後的完成的工作:
DECLARE_WORK(name,void(*func)(void*),void*data);
(2)、也可以通過下述宏動態建立一個工作:
INIT_WORK(struct work_struct*work,void(*func)(void*),void *data);
與tasklet類似,每個工作都有具體的工作隊列處理函數,原型如下:
void work_handler(void *data)
將工作隊列機制對應到具體的中斷程式中,即那些被推後的工作將會在func所指向的那個工作隊列處理函數中被執行。
(3)、實現了工作隊列處理函數後,就需要schedule_work函數對這個工作進行調度,就像這樣:
schedule_work(&work);
這樣work會馬上就被調度,一旦背景工作執行緒被喚醒,這個工作就會被執行(因為其所在工作隊列會被執行)。
二、input子系統概述 可見文章基於 mini2440 電阻式觸控螢幕(三):Linux輸入子系統(InputSubsystem):
按鍵、滑鼠、觸控螢幕、電池資訊等,都是通過input子系統上報。
三、Linux與Android 多點觸摸協議
為了使用功能強大的多點觸控裝置,就需要一種方案去上報使用者層所需的詳細的手指觸摸資料。這個文檔所描述的多點觸控協議可以讓核心驅動程式向使用者層上報任意多指的資料資訊。
1、使用說明
單點觸摸資訊是以ABS承載並按一定順序發送,如BTN_TOUCH、ABS_X、ABS_Y、SYNC。而多點觸摸資訊則是以ABS_MT承載並按一定順序發送,如ABS_MT_POSITION_X、ABS_MT_POSITION_Y,然後通過調用input_mt_sync()產生一個 SYN_MT_REPORT event來標記一個點的結束,告訴接收方接收當前手指的資訊並準備接收其它手指的觸控資訊。最後調用
input_sync()函數上報觸摸資訊開始動作並告訴接收方開始接收下一系列多點觸摸資訊。
協議定義了一系列ABS_MT事件,這些事件被分為幾大類,充許只應用其中的一部份,多點觸摸最小的事件集中應包括ABS_MT_TOUCH_MAJOR、ABS_MT_POSITION_X和 ABS_MT_POSITION_Y,以此來實現多點觸摸。如果裝置支援ABS_MT_WIDTH_MAJOR這個事件,那麼此事件可以提供手指觸摸接觸面積大小。觸摸方向等資訊可以由ABS_MT_TOUCH_MINOR,
ABS_MT_WIDTH_MINOR and ABS_MT_ORIENTATION提供。ABS_MT_TOOL_TYPE提供觸摸裝置的類別,如手或是筆或是其它。最後有些裝置可能會支援ABS_MT_TRACKING_ID,用來支援硬體跟蹤多點資訊,即該點屬於哪一條線等。
下面是兩點觸摸支援的最小事件集序列:ABS_MT_TOUCH_MAJORABS_MT_POSITION_XABS_MT_POSITION_YSYN_MT_REPORT //上報第一個點ABS_MT_TOUCH_MAJORABS_MT_POSITION_XABS_MT_POSITION_YSYN_MT_REPORT //上報第二個點 ………… //完成多點上報SYN_REPORT //開始動作
2、Event原語
接觸”一詞用來描述一個物體直接碰到另一個物體的表面。
ABS_MT_TOUCH_MAJOR描述了主接觸面的長軸,它和X,Y同一個單位,如果一個面的解析度為X*Y,則ABS_MT_TOUCH_MAJOR的最大值為sqrt(X^2+Y^2)
ABS_MT_TOUCH_MINOR描述了接觸面的短軸,如果接觸面是圓形,它可以不用。 ABS_MT_WIDTH_MAJOR描述了接觸工具的長軸 ABS_MT_WIDTH_MINOR描述了接觸工具的短軸 ABS_MT_TOUCH_MAJOR := max(X, Y) ABS_MT_TOUCH_MINOR := min(X, Y) ABS_MT_ORIENTATION := bool(X > Y)
以上四個參數可以用來產生額外的觸摸資訊,ABS_MT_TOUCH_MAJOR/ABS_MT_WIDTH_MAJOR的比率可以用來描述壓力。
ABS_MT_ORIENTATION
ABS_MT_POSITION_X接觸面的中心點X座標
ABS_MT_POSITION_Y接觸面的中心點Y座標
ABS_MT_TOOL_TYPE描述接觸工具類型,很多核心驅動無法區分此參數如手指及筆,如果是這樣,該參數可以不用,協議目前支援MT_TOOL_FINGER和MT_TOOL_PEN兩種類型。
ABS_MT_BLOB_ID形狀集ID,集合幾個點以描述一個形狀,很多驅動沒有形狀屬性,此參數可以不用。ABS_MT_TRACKING_ID描述了從接觸開始到釋放的整個過程的集合,如果裝置不支援,此參數可是不用。
3、觸摸軌跡
僅有少數裝置可以明觸的標識真實的 trackingID,多數情況下 trackingID只能來標識一次觸摸動作的過程。
4、手勢
多點觸摸指定的應用是建立手勢動作, TOUCH和 WIDTH參數經常用來區別手指的壓力和手指間的距離,另外 MINOR類的參數可以用來區別裝置的接觸面的大小(點接觸還是面接觸),ORIENTATION可以產生旋轉事件。
5、在Linux核心支援的基礎上,Android在其2.0源碼中加入多點觸摸功能(android4.0中介層有所不同)
由此觸控螢幕在Android的frameworks被完全分為2種實現途徑:單點觸控螢幕的單點方式,多點觸控螢幕的單點和多點方式。
在Linux的input.h中,多點觸摸功能依賴於以下幾個主要的軟體位:
…… #define SYN_REPORT0 #define SYN_CONFIG1 #define SYN_MT_REPORT2 …… #define ABS_MT_TOUCH_MAJOR0x30 #define ABS_MT_TOUCH_MINOR0x31 #define ABS_MT_WIDTH_MAJOR0x32 #define ABS_MT_WIDTH_MINOR0x33 #define ABS_MT_ORIENTATION0x34 #define ABS_MT_POSITION_X0x35 #define ABS_MT_POSITION_Y0x36 #define ABS_MT_TOOL_TYPE0x37 #define ABS_MT_BLOB_ID0x38 ……
在Android中對應的軟體位定義在RawInputEvent.java中:
…… public class RawInputEvent { …… public static final int CLASS_TOUCHSCREEN_MT = 0x00000010; …… public static final int ABS_MT_TOUCH_MAJOR = 0x30; public static final int ABS_MT_TOUCH_MINOR = 0x31; public static final int ABS_MT_WIDTH_MAJOR = 0x32; public static final int ABS_MT_WIDTH_MINOR = 0x33; public static final int ABS_MT_ORIENTATION = 0x34; public static final int ABS_MT_POSITION_X = 0x35; public static final int ABS_MT_POSITION_Y = 0x36; public static final int ABS_MT_TOOL_TYPE = 0x37; public static final int ABS_MT_BLOB_ID = 0x38; …… public static final int SYN_REPORT = 0; public static final int SYN_CONFIG = 1; public static final int SYN_MT_REPORT = 2; ……
在Android中,多點觸摸的實現方法在具體的代碼實現中和單點是完全區分開的。在Android代碼的EventHub.cpp中,單點屏和多點屏由如下程式碼片段來判定:
int EventHub::open_device(const char *deviceName) { …… if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) && test_bit(ABS_MT_POSITION_X, abs_bitmask) && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT; //LOGI("It is a multi-touch screen!"); } //single-touch? else if (test_bit(BTN_TOUCH, key_bitmask) && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { device->classes |= CLASS_TOUCHSCREEN; //LOGI("It is a single-touch screen!"); } …… }
我們知道,在觸控螢幕驅動中,通常在probe函數中會調用input_set_abs_params給裝置的input_dev結構體初始化,這些input_dev的參數會在Android的EventHub.cpp中被讀取。如上可知,如果我們的觸控螢幕想被當成多點屏被處理,只需要在驅動中給input_dev額外增加以下幾個參數即可:
input_set_abs_params(mcs_data.input, ABS_MT_POSITION_X, pdata->abs_x_min, pdata->abs_x_max, 0, 0); input_set_abs_params(mcs_data.input, ABS_MT_POSITION_Y, pdata->abs_y_min, pdata->abs_y_max, 0, 0); input_set_abs_params(mcs_data.input, ABS_MT_TOUCH_MAJOR, 0, 15, 0, 0);//相當於單點屏的ABX_PRESSURE input_set_abs_params(mcs_data.input, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);//相當於單點屏的ABS_TOOL_WIDTH
由於多點觸摸技術需要採集到多個點,然後再一起處理這些點,所以在軟體實現中需要保證每一波點的準確性和完整性。因此,Linux核心提供了input_mt_sync(struct input_dev * input)函數。在每波的每個點上報後需要緊跟一句input_mt_sync(),當這波所有點上報後再使用input_sync()進行同步。
例如一波要上報3個點: …… input_mt_sync(input); …… input_mt_sync(input); …… input_mt_sync(input); input_sync(input); 註:即使是僅上報一個點的單時間點事件,也需要一次input_mt_sync。