1 Input 概述
1.1 系统整体框图
1.2 Input 子系统框图
设备驱动层
主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层
核心层
为设备驱动层提供了规范和接口. 设备驱动层只要关心如何驱动硬件并获得硬件数据, 然后调用核心层的接口, 核心层会自动把数据提交给事件处理层.
事件处理层
是用户编程的接口(设备节点), 并处理驱动层提交的数据
1.3 数据关系框图
input_dev
- 是硬件驱动层,代表一个input设备
- 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作
- Input_dev可以通过Input_handle找到Input_handler
Input_handler
- 是事件处理层,代表一个事件处理器(如 evdev mousedev等)
- 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作
- 事件处理器一般内核自带,一般不需要我们来写
- Input_handler可以通过Input_handle找到Input_dev
Input_handle
- 属于核心层,代表一个配对的input设备与input事件处理器
- input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。
- 通过input_handle也可以找到input_dev和input_handler
一个device可能对应多个handler,而一个handler也不能只处理一个device
比如说一个鼠标,它可以对应evdev_handler,也可以对应mouse_handler,因此当其注册时与系统中的handler进行匹配,就有可能产生两个实例,一个是evdev,另一个是mousedev,而任何一个实例中都只有一个handle,至于以何种方式来传递事件,就由用户程序打开哪个实例来决定
2 设备驱动层
2.1 概述
纯硬件操作层, 包含不同硬件接口处理. 对于每种不同的具体硬件操作都对应着不同的Input_dev结构体.
2.2 数据结构
2.2.1 input_dev
1 | struct input_dev { |
Input_dev通过全局变量input_dev_list链接在一起. 设备注册的时候实现这个操作.
2.2.2 input_id
1 | struct input_id { |
如果需要特定的事件处理器来处理这个设备的话,这几个参数非常重要, 因为子系统核心时通过他们将设备驱动与事件处理层联系起来的. 但因为触摸屏所用的处理器为evdev, 匹配所有,所以这个初始化也无关紧要了.
2.3 输入设备驱动注册
- 输入设备驱动最终的目的即使能够与事件处理层的事件驱动相互匹配. 但是在drivers/input目录下有evdev.c事件驱动、mousedev.c事件驱动、joydev.c事件驱动等等. 我们的输入设备产生的事件最终上报给谁, 然后让事件驱动再去处理呢?
- evdev.c、mousedev.c 等是根据硬件输入设备的处理方式不同而抽象出来不同的事件处理接口帮助上层去调用的. 而我们写的设备驱动程序只不过是完成了硬件寄存器中的数据读写, 但提交给用户的事件必须是经过事件处理层的封装和同步才能够完成的.
3 核心层
3.1 概述
主要功能
- 注册主设备号
- 对于swi进入的open函数进行第一层处理, 并通过此设备号选择handler进入第二次open,也就是真正的open所在的file_operation, 并返回该file_operation的fd
- 提供input_register_device 和 input_register_handler函数分别用于注册device和handler
3.2 数据结构
3.2.1 input_handle
1 | struct input_handle { |
3.3 函数调用
3.3.1 input_register_device
1 | int input_register_device(struct input_dev *dev) |
主要功能:
- 进一步初始化输入设备
- 将自己的device结构添加到linux设备模型当中,将input_dev添加到input_dev_list链表中
- 需要合适的handler与Input_handler配对,配对的核心函数Input_attach_handler
3.3.1.1 input_attach_handler
1 | static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) |
3.3.1.2 input_match_device
1 | struct input_device_id *input_match_device(struct input_handler *handler, |
- 首先看id->driver_info有没有设置, 如果设置了说明匹配所有的id, evdev就是这个样的handler
- 然后依据id->flag来比较内容, 如果比较成功后则比较所支持事件的类型,只有所有位都匹配才成功返回
- 主要是比较Input_dev中的id和handler支持的id, 这个存放在handler的id_table中
3.3.2 input_register_handle
1 | int input_register_handle(struct input_handle *handle) |
3.3.3 input_register_handler
1 | int input_register_handler(struct input_handler *handler) |
4 事件处理层
4.1 概述
handler层是纯软件层, 包含不同的解决方案,如键盘、鼠标、游戏手柄等,但是没有涉及到硬件方面的操作。对于不同的解决方案,都包含一个名为Input_handler的结构体。
4.2 数据结构
4.2.1 Input_handler
1 | struct input_handler { |
4.3 事件处理器(evdev)
4.3.1 evdev设备结构
1 | struct evdev { |
- evdev结构体在配对成功后生成,由handler->connect生成, 对应设备文件为/sys/class/Input/event(n)
- 如触摸屏驱动的event0,这个设备是用户空间要访问的设备,可以理解它是一个虚拟设备,因为没有对应的硬件,但是通过handle->dev 就可以找到input_dev结构,而它对应着触摸屏,设备文件为/sys/class/input/input0。这个设备结构生成之后保存在evdev_table中,索引值是minor
4.3.2 evdev用户端设备结构
1 | struct evdev_client { |
- 这个结构在进程打开event0设备时候调用evdev的open函数, 在open中创建设个结构,并初始化. 在关闭设备文件的时候释放这个结构
4.3.3 evdev_handler结构体
1 | static struct input_handler evdev_handler = { |
4.3.4 数据结构关系图
4.3.5 connect函数
1 | static int evdev_connect(struct input_handler *handler, struct input_dev *dev, |
5 A/B(slot)协议
5.1 A协议
5.1.1 描述
- 如果不管当前坐标点和上一坐标点数据是否一致都上报, 称为A协议
5.1.2 上报流程
5.1.2.1 按下
1 | ABS_MT_POSITION_X x[0] |
- 是以SYN_MT_REPORT作为一个点的结尾
- 是以SYN_REPORT为一次事件的结尾
- 多指触摸的时候,native层每收到一次SYN_MT_REPORT就形成一个点的信息,收到一个点之后并不会立即处理,而是一个事件完成之后才会处理, SYN_REPORT就是这个事件的标志.
- A协议中有的只是点的坐标信息,那么系统如何去判断当前的多点属于哪一条线呢?
- 假设一次事件共有5个点, 本次触摸也有5个点.
- 系统会分别计算一次前5个点和当前5个点的距离, 这样就产生了 5x5=25个数据.
- 对这个25个数字进行排序, 判断当前点与哪一次的点最近,那么赋予它们相同的id, 就可以知道当前点属于哪条线了.
5.1.2.2 抬起
1 | SYN_MT_REPORT |
- 只有SYNC, 没有任何信息,系统就会认为此次事件为UP
5.2 B(slot)协议
5.2.1 描述
- 如果当前坐标点和上一坐标点数据一致,则该点不上报, 称为B(slot)协议
- slot直译为位置、槽,两层含义:一层是位置, 另一层是容器
- B协议使用了slot, 还有一个新的面孔TRACKING_ID
5.2.2 上报流程
5.2.2.1 按下
1 | ABS_MT_SLOT 0 |
- 相同的ABS_MT_TRACKING_ID点,它们属于同一条线
5.2.2.2 抬起
1 | ABS_MT_SLOT 0 |
- 点上报的时候 ABS_MT_TRACKING_ID 为-1, 系统会清除对应的ID.