多点触摸技术A/B(Slot)协议
B协议又称为slot协议,slot直译为位置、槽,有两层含义,一层是位置,另一层是容器。在Input子系统中,它扮演的就是这两个角色。它产生于这样一个背景:
如果从Device获取的当前数据与上一个数据相同,我们有必要再上报当前数据吗?如果我们不管两次数据是否一致都上报,那就是A协议;如果我们选择不上报,那么既然需要比较,总需要把上一次数据存起来吧,slot就是做这个事情的,显然这就是Slot(B)协议。
A协议实现方式
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协议实现方式
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_TRACKING_ID -1
SYN_REPORT
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID -1
SYN_REPORT
这里上报的ABS_MT_TRACKING_ID为-1,也只有这里该值才可以小于零,收到该值,系统就会清除对应的ID。看似简单的两个协议内容到这里就分析完毕了。
1. #include <linux/input/mt.h>
1. //down
2. input_mt_slot(ts->input_dev, id);
3. //input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
4. input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
5. input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
6. input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
7. input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
ps:个人感觉注释的那一行是在初次手指摁下才会执行,移动的时候就不执行这句话了
//up
1. input_mt_slot(ts->input_dev, id);
2. //input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
3. input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
ps:同理,注释的这句应该是在slot为0的时候不执行
1. //init
2. //__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
3. input_mt_init_slots(ts->input_dev, 255);
上面两条ps是在本人看了内核文档介绍多触点以后的感觉,不一定对
对于type B设备的驱动,在每个数据包的开始,通过调用input_mt_slot()进行分割,同时带入一个参数:slot。这会产生一个ABS_MT_SLOT事件,它通知接收者准备更新给定slot的信息。.
总结
看了上面的分析,明显可以看出B协议要优于A协议,但事实上并不如此简单。B协议需要硬件上的支持,ID值并不是随便赋值的,而是硬件上跟踪了点的轨迹;另外,B协议的复杂性如果掌握不好往往会带来一些莫名其妙的问题,比如如果因为某些因素(同步等),在UP的时候少清除了一个slot的信息,那么下次单击的时候你也会惊奇地发现竟然有两个点(采用了B协议,slot已经保存了点信息,除非明确清除)。Ps:
事件报告完毕后,设备驱动需要使用input_sync函数告诉输入子系统一个完整的报告已经发送。
void input_sync(struct input_dev *dev)
{
input_event(dev,EV_SYN,SYN_REPORT,0);
}
这一点在鼠标移动处理中很重要,因为鼠标坐标的X分量和Y分量是分开传送的,需要利用input_sync函数来同步。