天天看点

Android驱动之 Linux Input子系统之TP——A/B(Slot)协议

​​点击打开链接​​

将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_***函数实现):

点击(此处)折叠或打开

  1. ABS_MT_POSITION_X x[0]
  2. ABS_MT_POSITION_Y y[0]
  3. SYN_MT_REPORT
  4. ABS_MT_POSITION_X x[1]
  5. ABS_MT_POSITION_Y y[1]
  6. 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,应用收到这个信息后,就能够知道当前点属于哪条线了。

       手抬起来的时候又用什么样的序列来通知系统呢,

       仅仅有SYNC,没有其他不论什么信息,系统就会觉得此次事件为UP。

       B协议使用了slot,还有一个新面孔TRACKING_ID.

  1. ABS_MT_SLOT 0
  2. ABS_MT_TRACKING_ID **
  3. ABS_MT_SLOT 1

      没有SYN_MT_REPORT。那么它用什么来跟踪当前点属于哪一条线呢。用的就是ABS_MT_TRACKING_ID,当前序列中某点的ID值。假设与前一次序列中某点的ID值相等。那么他们就属于同一条线。既然如此,那么android系统中还须要做排序等运算吗?当然不须要。那么手指全部抬起的时候序列又是怎样的呢?      

  1. ABS_MT_TRACKING_ID -1

  这里上报的ABS_MT_TRACKING_ID为-1,也仅仅有这里该值才干够小于零,收到该值,系统就会清除相应的ID。

看似简单的两个协议内容到这里就分析完毕了。

      看了上面的分析,明显能够看出B协议要由于A协议。但其实并不如此简单。

B协议须要硬件上的支持。ID值并非随便赋值的,而是硬件上跟踪了点的轨迹;假设硬件上满足不了这个条件,那么採用B协议仅仅能闹成笑话。另外。B协议的复杂性假设掌握不好往往会带来一些莫名其妙的问题,比方假设由于某些因素(同步等)。在UP的时候少清除了一个slot的信息。那么下次单击的时候你也会惊奇地发现居然有两个点(採用了B协议。slot已经保存了点信息,除非明白清除)。

      希望本文对希望了解TP或者多点触摸功能的童鞋有所帮助。

PS:多点触控协议相关知识

Multi-touch (MT) Protocol

-------------------------

         Copyright(C) 2009-2010 Henrik Rydberg<[email protected]>

 简单介绍

------------

    为了发挥新近的多点触摸和多用户设备的强大功能,为多点触摸定义一种上报具体数据的方法(比方有多个物体直接接触到设备的表面),是非常有必要的。这篇文档描写叙述了多点触摸协议(multi-touch。MT),是的内核驱动能够对多个随意数量的触控事件上报具体的数据信息。

    基于硬件的能力,该协议被分为两种类型。

对于仅仅能处理匿名接触(type A)的设备,该协议描写叙述了怎样把全部的原始触摸数据发送给接收者。对于那些有能力跟踪并识别每一个触摸点的设备(type B)。该协议描写叙述了怎样把每一个触摸点的单独更新通过事件slots发送给接受者。

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

本部分由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_REPORT

     实际上,在移动当中一个触控点后的上报序列看起来是一样的、全部存在触控点的原始数据被发送。然后在它们之间用SYN_REPORT进行同步。

     当第一个接触点离开后,事件序列例如以下:

     ABS_MT_POSITION_X x[1]

     ABS_MT_POSITION_Y y[1]

     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_SLOT 1

    ABS_MT_TRACKING_ID 46

    id 45的触控点在x方向移动后的事件序列例如以下:

    ABS_MT_SLOT 0

    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

事件的使用

-----------

    一组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] 參看手指跟踪一节的内容。