点击打开链接
将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围大多数用到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_REPORTABS_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_REPORTSYN_REPORT
只有SYNC,没有其它任何信息,系统就会认为此次事件为UP。
B协议使用了slot,还有一个新面孔TRACKING_ID.
点击(此处)折叠或打开
ABS_MT_SLOT 0ABS_MT_TRACKING_ID **ABS_MT_POSITION_X x[0]ABS_MT_POSITION_Y y[0]ABS_MT_SLOT 1ABS_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 0ABS_MT_TRACKING_ID -1SYN_REPORTABS_MT_SLOT 1ABS_MT_TRACKING_ID -1SYN_REPORT
这里上报的ABS_MT_TRACKING_ID为-1,也只有这里该值才可以小于零,收到该值,系统就会清除对应的ID。看似简单的两个协议内容到这里就分析完毕了。
看了上面的分析,明显可以看出B协议要由于A协议,但事实上并不如此简单。B协议需要硬件上的支持,ID值并不是随便赋值的,而是硬件上跟踪了点的轨迹;如果硬件上满足不了这个条件,那么采用B协议只能闹成笑话。另外,B协议的复杂性如果掌握不好往往会带来一些莫名其妙的问题,比如如果因为某些因素(同步等),在UP的时候少清除了一个slot的信息,那么下次单击的时候你也会惊奇地发现竟然有两个点(采用了B协议,slot已经保存了点信息,除非明确清除)。
希望本文对希望了解TP或者多点触摸功能的童鞋有所帮助。
参考于:/rutian/p/3341118.html
PS:多点触控协议相关知识
Multi-touch (MT) Protocol
-------------------------
Copyright(C) - Henrik Rydberg<rydberg@euromail.se>
简介
------------
为了发挥新近的多点触摸和多用户设备的强大功能,为多点触摸定义一种上报详细数据的方法(比如有多个物体直接接触到设备的表面),是非常有必要的。这篇文档描述了多点触摸协议(multi-touch,MT),是的内核驱动可以对多个随意数量的触控事件上报详细的数据信息。
基于硬件的能力,该协议被分为两种类型。对于只能处理匿名接触(type A)的设备,该协议描述了如何把所有的原始触摸数据发送给接收者。对于那些有能力跟踪并识别每个触摸点的设备(type B),该协议描述了如何把每个触摸点的单独更新通过事件slots发送给接受者。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本部分由DroidPhone 翻译:/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 项目: /code/mtdev/.
[4] 参看事件计算一节的内容。
[5] 参看手指跟踪一节的内容。
如果觉得《Android驱动之 Linux Input子系统之TP——A/B(Slot)协议》对你有帮助,请点赞、收藏,并留下你的观点哦!