將A/B協議這部分單獨拿出來說一方面是由於這部分內容是比較easy忽視的。周圍大多數用到input子系統的開發者也不甚理解。還有一方面是由於這部分知識一旦擴展到TP(觸摸屏Touch Panel)的多點觸摸就要與Middleware/Framework一起結合起來看才干全然掌握,復雜性所在。
這里的Middleware/Framework是針對android來說的,本人從事android這幾個層次的工作,所以就從android的角度來講講這部分內容,其他系統盡管代碼不同。但原理上是全然一樣的。
B協議又稱為slot協議,那么input子系統里面使用的slot是什么。A/B協議到底是怎樣划分的?
slot直譯為位置、槽,有兩層含義。一層是位置,還有一層是容器。在Input子系統中。它扮演的就是這兩個角色。
它產生於這樣一個背景:
假設從Device獲取的當前數據與上一個數據同樣,我們有必要再上報當前數據嗎?假設我們無論兩次數據是否一致都上報。那就是A協議;假設我們選擇不上報。那么既然須要比較。總須要把上一次數據存起來吧。slot就是做這個事情的,顯然這就是Slot(B)協議。
其實到這里,對TP不感興趣的童鞋能夠不繼續向下看了,了解了兩個協議的差別看或者寫一般模塊的代碼不會有問題了。須要注意的是。想要測試Device驅動的input部分是否正常的時候。假如使用的是B協議。input_report數據的時候要記得每次都要report不同的值,否則在HAL層是看不到數據不停上報的。由於前后兩個數據同樣的時候,B協議是不會上報到系統的。
另外。在上層測試數據上報頻率的時候。採用 數據總量/時間差 的方法,假設驅動採用的是B協議,測試結果也是不准確的。
以下要說的與TP的多點觸摸(MT Multi-touch)的功能關系密切,沒有興趣的能夠略過。由於這部分代碼不從事TP或者android的人是不會接觸到的。所以代碼就不貼出來了。有興趣的童鞋能夠單獨交流O(∩_∩)O~
我們都知道,在支持MT的手機上多指滑動的時候。多條手指滑動過的軌跡彼此是不相交的。這也是我們所期望的。但這個功能到底是怎樣實現的呢?看了上面的分析應該就知道,A/B兩種協議方式都能夠實現該功能。
A協議不會使用slot。多指處理中,它的報點序列例如以下(每一個序列都以input_report_***函數實現):
點擊(此處)折疊或打開
- ABS_MT_POSITION_X x[0]
- ABS_MT_POSITION_Y y[0]
- SYN_MT_REPORT
- ABS_MT_POSITION_X x[1]
- ABS_MT_POSITION_Y y[1]
- SYN_MT_REPORT
- …
- SYN_REPORT
上面的序列中須要說明的是系統以SYN_MT_REPORT為一個點的信息的結尾,以SYN_REPORT為一次事件的結尾。也就是說多指觸摸的時候。android的中間件部分每收到一次SYN_MT_REPORT就形成一個點信息,收到一個點之后並不會馬上處理,而是一個事件完畢之后才會處理,SYN_REPORT就是這個事件的標志。A協議比較簡單。我們也能夠發如今上面的序列中根本就沒有軌跡跟蹤的信息。有的僅僅是點坐標等信息,那么系統假設去推斷當前的多個點各屬於哪一條線呢?
我們假設前一次事件共同擁有5個點,本次觸摸也有5個點,系統會分別計算前一次5個點與本次5個點的距離。distance[prev_i, curr_j] (i=0,1,...,4; j=0,1,...4),這樣會產生總共5*5=25個數字。然后對這25個數字進行排序,android用的是堆排序。(我們在系統上假設用多指。一般最多也是雙值,也就是4個數據。這里採用了堆排序。不知是出於什么情況考慮。感覺換個方法可能更有用些。)以下的任務就是推斷哪些當前點與前一次的點近期,那么賦予它們同樣的id,應用收到這個信息后,就能夠知道當前點屬於哪條線了。
手抬起來的時候又用什么樣的序列來通知系統呢,
點擊(此處)折疊或打開
- SYN_MT_REPORT
- SYN_REPORT
僅僅有SYNC,沒有其他不論什么信息,系統就會覺得此次事件為UP。
B協議使用了slot,還有一個新面孔TRACKING_ID.
點擊(此處)折疊或打開
- ABS_MT_SLOT 0
- ABS_MT_TRACKING_ID **
- ABS_MT_POSITION_X x[0]
- ABS_MT_POSITION_Y y[0]
- ABS_MT_SLOT 1
- ABS_MT_TRACKING_ID **
- ABS_MT_POSITION_X x[1]
- ABS_MT_POSITION_Y y[1]
- SYN_REPORT
沒有SYN_MT_REPORT。那么它用什么來跟蹤當前點屬於哪一條線呢。用的就是ABS_MT_TRACKING_ID,當前序列中某點的ID值。假設與前一次序列中某點的ID值相等。那么他們就屬於同一條線。既然如此,那么android系統中還須要做排序等運算嗎?當然不須要。那么手指全部抬起的時候序列又是怎樣的呢?
點擊(此處)折疊或打開
- ABS_MT_SLOT 0
- ABS_MT_TRACKING_ID -1
- SYN_REPORT
- ABS_MT_SLOT 1
- ABS_MT_TRACKING_ID -1
- SYN_REPORT
這里上報的ABS_MT_TRACKING_ID為-1,也僅僅有這里該值才干夠小於零,收到該值,系統就會清除相應的ID。
看似簡單的兩個協議內容到這里就分析完畢了。
看了上面的分析,明顯能夠看出B協議要由於A協議。但其實並不如此簡單。
B協議須要硬件上的支持。ID值並非隨便賦值的,而是硬件上跟蹤了點的軌跡;假設硬件上滿足不了這個條件,那么採用B協議僅僅能鬧成笑話。另外。B協議的復雜性假設掌握不好往往會帶來一些莫名其妙的問題,比方假設由於某些因素(同步等)。在UP的時候少清除了一個slot的信息。那么下次單擊的時候你也會驚奇地發現居然有兩個點(採用了B協議。slot已經保存了點信息,除非明白清除)。
希望本文對希望了解TP或者多點觸摸功能的童鞋有所幫助。
參考於:http://www.cnblogs.com/rutian2013/p/3341118.html
PS:多點觸控協議相關知識
Multi-touch (MT) Protocol
-------------------------
Copyright(C) 2009-2010 Henrik Rydberg<rydberg@euromail.se>
簡單介紹
------------
為了發揮新近的多點觸摸和多用戶設備的強大功能,為多點觸摸定義一種上報具體數據的方法(比方有多個物體直接接觸到設備的表面),是非常有必要的。這篇文檔描寫敘述了多點觸摸協議(multi-touch。MT),是的內核驅動能夠對多個隨意數量的觸控事件上報具體的數據信息。
基於硬件的能力,該協議被分為兩種類型。
對於僅僅能處理匿名接觸(type A)的設備,該協議描寫敘述了怎樣把全部的原始觸摸數據發送給接收者。對於那些有能力跟蹤並識別每一個觸摸點的設備(type B)。該協議描寫敘述了怎樣把每一個觸摸點的單獨更新通過事件slots發送給接受者。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本部分由DroidPhone 翻譯:http://blog.csdn.net/droidphone
Kernel版本號:V3.7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
協議的使用
--------------
具體的觸控信息被按順序地切割為多個ABS_MT事件數據包進行發送。僅僅有ABS_MT事件信息被識別為觸控數據包的一部分,由於這些事件在當前的單點觸控(single-touch,ST)應用中是被忽略掉的,我們能夠在現有的驅動中基於ST協議之上來實現MT協議。
對於type A設備的驅動。在每一個數據包的結尾用input_mt_sync()對多個觸控包進行切割,這將會產生一個SYN_MT_REPORT事件,它通知接收者接受當前的觸控信息並准備接收下一個信息。
對於type B設備的驅動,在每一個數據包的開始,通過調用input_mt_slot()進行切割。同一時候帶入一個參數:slot。這會產生一個ABS_MT_SLOT事件。它通知接收者准備更新給定slot的信息。
.
兩種類型的驅動通常都通過調用input_sync()函數來標記一個多點觸摸數據傳送的結束,這通知接收者對從上一個EV_SYN/SYN_REPORT以來的全部累加事件作出響應。並准備接收新的一組事件/數據包。
無狀態的type A協議和有狀態的type B slot協議之間的主要差別是通過識別同樣接觸點來減低發送到用戶空間的數據量。
slot協議須要使用到ABS_MT_TRACKING_ID。它要不由硬件來提供。或者通過原始數據進行計算【5】。
對於type A設備。內核驅動應該依據設備表面上全部有效觸控進行列舉並生成事件。每一個觸控點數據包在這次事件流中的順序並不重要。事件過濾和手指跟蹤的工作留給用戶空間來實現【3】。
對於type B設備,內核驅動應該把每一個識別出的觸控和一個slot相關聯,並使用該slot來傳播觸摸狀態的改變。
通過改動關聯slot的ABS_MT_TRACKING_ID來達到對觸摸點的創建。替換和銷毀。一個非負數的跟蹤id被解釋為有效的觸摸,-1則代表一個不再使用的slot。一個之前沒有出現過的跟蹤id被覺得是一個新的接觸點,當一個跟蹤id不再出現時則覺得該接觸點已經被移除。由於僅僅有變化的部分被傳播。每一個被啟動的接觸點的狀態信息必須駐留在接收端。
每當接收到一個MT事件,僅僅需對當前slot的相關屬性進行一次簡單的更新就可以。
有些設備能夠識別和/或跟蹤比它能報告給驅動很多其他的接觸點,對於這樣的設備的驅動應該使得硬件上報的每一個接觸點關聯一個type B的slot。一旦識別到一個關聯了slot的接觸點發生了變化,驅動應該通過改變他的ABS_MT_TRACKING_ID使得該slot無效。假設硬件發出通知它跟蹤到了比眼下上報的還要多的接觸點,驅動應該使用BTN_TOOL_*TAP事件知會用戶空間此刻硬件跟蹤的總的接觸點數目已經改變。要完畢此工作。驅動應該顯式地發送BTN_TOOL_*TAP事件,並在調用input_mt_report_pointer_emulation()時把use_count參數設為false。驅動應該僅僅通告硬件所能報告的slots數量。用戶空間能夠通過注意到最大支持的BTN_TOOL_*TAP事件大於在ABS_MT_SLOT軸的absinfo中報告的type B的slots總數,來檢測驅動能否報告比slots數還多的觸控點。
Protocol Example A
------------------
對於一個兩點觸控的觸摸信息。type A設備的最小的事件序列看起來就像以下這樣:
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
實際上,在移動當中一個觸控點后的上報序列看起來是一樣的、全部存在觸控點的原始數據被發送。然后在它們之間用SYN_REPORT進行同步。
當第一個接觸點離開后,事件序列例如以下:
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
當第二個接觸點離開后。事件序列例如以下:
SYN_MT_REPORT
SYN_REPORT
假如驅動在ABS_MT事件之外上報一個BTN_TOUCH 或ABS_PRESSURE事件。最后一個SYN_MT_REPORT能夠省略掉。否則。最后的SYN_REPORT會被input核心層扔掉,結果就是一個0觸控點事件被傳到用戶空間中。
Protocol Example B
------------------
對於一個兩點觸控的觸摸信息,type B設備的最小的事件序列看起來就像以下這樣:
ABS_MT_SLOT 0
ABS_MT_TRACKING_ID 45
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT
id 45的觸控點在x方向移動后的事件序列例如以下:
ABS_MT_SLOT 0
ABS_MT_POSITION_X x[0]
SYN_REPORT
slot 0相應的接觸點離開后,相應的事件序列例如以下:
ABS_MT_TRACKING_ID -1
SYN_REPORT
上一個被改動的slot也是0,所以ABS_MT_SLOT被省略掉。這一消息移除了接觸點45相關聯的slot 0,於是接觸點45被銷毀。slot 0被釋放后能夠被還有一個接觸點重用。
最后,第二個接觸點離開后的時間序列例如以下:
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID -1
SYN_REPORT
事件的使用
-----------
一組ABS_MT事件集合按須要的特性被定義。這些事件被分成幾個組。以便同意僅僅實現當中的一部分。最小的集合由ABS_MT_POSITION_X和ABS_MT_POSITION_Y組成,用於跟蹤多點接觸。假設設備支持。ABS_MT_TOUCH_MAJOR和ABS_MT_WIDTH_MAJOR能夠用來提供接觸面積和相應的接觸工具。
TOUCH和WIDTH參數有一個幾何解釋。想象一下通過一個窗戶觀察一個人把一個手指按壓在對面的玻璃上。你會看到兩個區域,內圈的區域包括了手指實際和玻璃接觸的部分。而外圈則是手指的外輪廓。
接觸區域(a)就是ABS_MT_POSITION_X/Y,而手指輪廓區域(b) 的中心就是ABS_MT_TOOL_X/Y。接觸區域的直徑是ABS_MT_TOUCH_MAJOR。而手指輪廓的直徑就是ABS_MT_WIDTH_MAJOR。如今想象一下此人把手指向玻璃壓得更緊,接觸區域會添加,通常,ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR的比值。總是小於1的,它和接觸的壓力相關。只是對於基於壓力的設備。應該改為使用ABS_MT_PRESSURE來提供接觸的壓力值。
能夠檢測懸浮的設備能夠使用ABS_MT_DISTANCE來指出離表面的距離。
Linux MT Win8
__________ _______________________
/ \ | |
/ \ | |
/ ____ \ | |
/ / \ \ | |
\ \ a \ \ | a |
\ \____/ \ | |
\ \ | |
\ b \ | b |
\ \ | |
\ \ | |
\ \ | |
\ / | |
\ / | |
\ / | |
\__________/ |_______________________|
除了MAJOR參數之外。接觸和手指區域的橢圓外形還能夠添加一個MINOR參數,MAJOR和MINOR參數相當於橢圓的長軸和短軸。接觸區域的橢圓的方向能夠用ORIENTATION參數描寫敘述,而手指區域的橢圓方向能夠通過向量運算(a-b)來獲得。
對於type A設備。將來的規格可能會通過ABS_MT_BLOB_ID來描寫敘述接觸的外形。
ABS_MT_TOOL_TYPE能夠用來指出觸控工具是手指還是筆或者其他物體。最后,ABS_MT_TRACKING_ID應該一直用來跟蹤被識別的接觸點【5】。
在type B協議里,ABS_MT_TOOL_TYPE和ABS_MT_TRACKING_ID被隱藏在input核心層中進行處理。驅動程序應該改為調用input_mt_report_slot_state()。
事件的語義說明
---------------
ABS_MT_TOUCH_MAJOR
接觸區域的長軸的長度。該長度應該按接觸表面的單位提供。假設表面的分辨率是X-Y,則ABS_MT_TOUCH_MAJOR可能的最大值是sqrt(X^2 + Y^2),它的對角線【4】。
ABS_MT_TOUCH_MINOR
接觸區域短軸的表面單位長度。假設區域是圓形,該事件能夠忽略【4】。
ABS_MT_WIDTH_MAJOR
工具輪廓區域長軸的表面單位長度。這應該就是工具本省的大小。
接觸面和輪廓面的方向被假設是一樣的【4】。
ABS_MT_WIDTH_MINOR
工具輪廓區域短軸的表面單位長度。圓形的話能夠被忽略【4】。
上述4個值能夠被用來推導出接觸面的額外信息。ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR能夠被用作接近的壓力。手指和手掌有不用的特征寬度。
ABS_MT_PRESSURE
接觸區域的壓力值,能夠是隨意單位。
基於壓力的設備應該使用該事件而不是TOUCH和 WIDTH事件。也用於能夠報告空間信號強度的設備。
ABS_MT_DISTANCE
接觸物到接觸表面之間的表面單位距離。
距離為0表明已經接觸到了表面。
一個正值表示接觸物懸浮在表面之上。
ABS_MT_ORIENTATION
接觸橢圓區域的方向。該值描寫敘述了環繞觸摸中心做1/4順時針轉動的有符號數。沒有限定該數值的范圍,可是在橢圓和表面的Y方向對齊時應該返回0值,橢圓往左轉時應該返回負值。往右轉時應該返回正值。當和X軸全然對齊時,應該返回最大值。
接觸的橢圓默覺得是對稱點。對於能夠360度轉動的設備,須要報告超出最大范圍來指出轉動多於1/4圈。對於倒置的手指。應該上報最大范圍的兩倍。
假設接觸面是一個圓形,或者方向信息在內核驅動中不可用,此時方向參數能夠被忽略。假設設備能夠識別出兩個軸方向。有可能僅僅支持部分不連續的方向,在這樣的情況下,ABS_MT_ORIENTATION的范圍應該是[0,1]【4】。
ABS_MT_POSITION_X
接觸中心的X坐標。
ABS_MT_POSITION_Y
接觸中心的Y坐標
ABS_MT_TOOL_X
接觸工具輪廓中心的X坐標。假設設備不能區分接觸面和工具本身時。能夠忽略該事件。
ABS_MT_TOOL_Y
接觸工具輪廓中心的Y坐標。假設設備不能區分接觸面和工具本身時,能夠忽略該事件。
這4個位置值能夠用於從觸控工具位置中分離實際的接觸位置。
假設兩種位置信息都存在,那么觸控工具的長軸指向接觸點,否則。工具和接觸面的軸互相對其。
ABS_MT_TOOL_TYPE
接近工具的類型。非常多內核驅動程序不能區分不同的觸控工具,比方手指和筆。
這樣的情況下,該事件能夠被忽略。眼下的協議支持MT_TOOL_FINGER 和MT_TOOL_PEN【2】兩種類型。
對於type B設備。該事件由input核心層處理,驅動應該改為使用input_mt_report_slot_state()來上報。
ABS_MT_BLOB_ID
BLOB_ID事件把幾個數據包組合在一起來組成一個隨意的接觸形狀。
一些點組成的序列定義了一個多邊形的外形。
這是一個type A設備的底層匿名數據組合,不應該和上層的跟蹤ID相混淆。
多數type A設備沒有blog這一能力,所以驅動能夠放心地忽略該事件。
ABS_MT_TRACKING_ID
TRACKING_ID識別一個被啟動的觸控點的整個生命周期【5】。
TRACKING_ID的范圍應該足夠大,從而保證在足夠長的時間內都能夠維護一個唯一的值。
對於type B設備,該事件由input核心層處理,驅動程序應該改為使用input_mt_report_slot_state()來上報該事件。
事件的計算
-----------------
一堆不同的硬件不可避免地導致一些設備比還有一些更適合於MT協議。
為了簡單和統一地進行相應。本節給出一些方法來確定怎樣計算某些事件。
對於那些報告接觸形狀為矩形的設備,我們不能獲得帶符號的方向值,假設X和Y各自是接觸面矩形的兩個邊長,以下這些公式能夠最大可能地獲取最多的信息:
ABS_MT_TOUCH_MAJOR := max(X, Y)
ABS_MT_TOUCH_MINOR := min(X, Y)
ABS_MT_ORIENTATION := bool(X > Y)
ABS_MT_ORIENTATION的范圍應該被設為[0,1],以便設備能夠區分手指是沿着Y軸還是沿着X軸【1】。
對於有T和C坐標的win8設備。位置信息的相應關系是:
ABS_MT_POSITION_X := T_X
ABS_MT_POSITION_Y := T_Y
ABS_MT_TOOL_X := C_X
ABS_MT_TOOL_X := C_Y
非常不幸的是。沒有足夠的信息能夠確定觸摸橢圓和工具橢圓。所以僅僅能求助於近似算法。一種兼容早期使用方式的簡單的方案是:
ABS_MT_TOUCH_MAJOR := min(X, Y)
ABS_MT_TOUCH_MINOR := <not used>
ABS_MT_ORIENTATION := <not used>
ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C)
ABS_MT_WIDTH_MINOR := min(X, Y)
基本原理是:我們沒有關於觸摸橢圓的相關信息。所以改用它的內切圓形來近似它。觸控工具的橢圓應該與向量(T - C)對齊,所以它的直徑必須添加(T, C)之間的距離。最后。假設觸摸區域的直徑等於觸控工具的厚度,我們能夠得到上述公式。
手指跟蹤
---------------
跟蹤手指的處理過程,比如,為在表面上啟動的每一個觸控點分配一個唯一的trackingID。是一個歐幾里得偶匹配問題。在每一次的事件同步中,一組實際的觸控點要和上一個同步的一組觸控點進行匹配,完整的實現請參考【3】。
手勢
--------
在某些能夠產生手勢事件的特殊應用中,能夠使用TOUCH和WIDTH參數,比方,近似手指的壓力或者區分食指和拇指。利用額外的MINOR參數,能夠區分整個手指壓下還是僅僅是手指的一個點壓。利用ORIENTATION參數,我們能夠檢測手指的擰動。
注意事項
------------
為了和現有的應用兼容。一個手指數據包中報告的數據不能被識別為單點觸控事件。
對於type A設備,全部的手指數據都要略過輸入過濾算法,由於接下來的同一類型的事件指向的是不同的手指。
使用type A協議設備的樣例,請參考bcm5974的驅動。
對於type B協議的樣例,請參考hid-egalax的驅動。
[1] Also, the difference (TOOL_X -POSITION_X) can be used to model tilt.
[2] The list can of course be extended.
[3] mtdev 項目: http://bitmath.org/code/mtdev/.
[4] 參看事件計算一節的內容。
[5] 參看手指跟蹤一節的內容。