电容触摸屏驱动其实就是一下几种 linux 驱动框架的组合:
① IIC 设备驱动,因为电容触摸 IC 基本都是 IIC 接口的,因此大框架就是 IIC 设备驱动。
② 通过中断引脚(INT)向 linux 内核上报触摸信息,因此需要用到 linux 中断驱动框架。坐标的上报在中断服务函数中完成。
③ 触摸屏的坐标信息、屏幕按下和抬起信息都属于 linux 的 input 子系统,因此向 linux 内核上报触摸屏坐标信息就得使用 input 子系统。只是,我们得按照 linux 内核规定的规则来上报坐标信息。
在上面的驱动框架组合中我们发现 I2C 驱动、中断驱动、input 子系统都已经学习了解过了,还没有
学习过 input 子系统下的多点电容触摸协议,这个就是本章学习的重点,linux 内核中有一份文档详细的讲
解了多点电容触摸屏协议,文档路径为:Documentation/input/multitouch-protocol.txt
MT 协议被分为两种类型,TypeA 和 TypeB,这两种类型的区别如下:
TypeA:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少!)。
Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息,FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力。
触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核,只有 ABS_MT 事件是用于多点触摸的,ABS_MT 事件定义在文件 linux/input.h 中,相关事件如下所示:
852 #define ABS_MT_SLOT 0x2f /* MT slot being modified */
853 #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
854 #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
855 #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
856 #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
857 #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
858 #define ABS_MT_POSITION_X 0x35 /* Center X touch position */
859 #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
860 #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
861 #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
862 #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
863 #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
864 #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
865 #define ABS_MT_TOOL_X 0x3c /* Center X tool position */
866 #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
在上面这些众多的 ABS_MT 事件中,我们最常用的就是 ABS_MT_SLOT 、 ABS_MT_POSITION_X 、ABS_MT_POSITION_Y 和 ABS_MT_TRACKING_ID 。其中 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 用来上报触摸点的 (X,Y) 坐标信息,ABS_MT_SLOT 用来上报触摸点 ID ,对于 Type B 类型的设备,需要用到
ABS_MT_TRACKING_ID 事件来区分触摸点。
对于 TypeA 类型的设备,通过 input_mt_sync()函数来隔离不同的触摸点数据信息,此函数原型如下所示:
void input_mt_sync(struct input_dev *dev)
此函数只要一个参数,类型为 input_dev,用于指定具体的 input_dev 设备。input_mt_sync()函数会触发 SYN_MT_REPORT 事件,此事件会通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据。
对于 Type B 类型的设备,上报触摸点信息的时候需要通过 input_mt_slot()函数区分是哪一个触摸点,
input_mt_slot()函数原型如下所示:
void input_mt_slot(struct input_dev *dev, int slot)
此函数有两个参数,第一个参数是 input_dev 设备,第二个参数 slot 用于指定当前上报的是哪个触摸点信息。input_mt_slot()函数会触发 ABS_MT_SLOT 事件,此事件会告诉接收者当前正在更新的是哪个触摸点(slot)的数据。
不管是哪个类型的设备,最终都要调用 input_sync()函数来标识多点触摸信息传输完成,告诉接收者处理之前累计的所有消息,并且准备好下一次接收。Type B 和 Type A 相比最大的区别就是 Type B 可以区分出触摸点, 因此可以减少发送到用户空间的数据。Type B 使用 slot 协议区分具体的触摸点,slot 需要用到 ABS_MT_TRACKING_ID 消息,这个 ID 需要硬件提供,或者通过原始数据计算出来。对于 TypeA 设备,内核驱动需要一次性将触摸屏上所有的触摸点信息全部上报,每个触摸点的信息在本次上报事件流中的顺序不重要,因为事件的过滤和手指(触摸点)跟踪是在内核空间处理的。
Type B 设备驱动需要给每个识别出来的触摸点分配一个 slot,后面使用这个 slot 来上报触摸点信息。可以通过 slot 的 ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。一个非负数的 ID 表示一个有效的触摸点,-1 这个 ID 表示未使用 slot。一个以前不存在的 ID 表示这是一个新加的触摸点,一个 ID 如果再也不存在了就表示删除了。
有些设备识别或追踪的触摸点信息要比他上报的多,这些设备驱动应该给硬件上报的每个触摸点分配一个 Type B 的 slot。一旦检测到某一个 slot 关联的触摸点 ID 发生了变化,驱动就应该改变这个 slot 的ABS_MT_TRACKING_ID,使这个 slot 失效。如果硬件设备追踪到了比他正在上报的还要多的触摸点,那么驱动程序应该发送 BTN_TOOL_*TAP 消息,并且调用 input_mt_report_pointer_emulation()函数,将此函数的第二个参数 use_count 设置为 false。