下面是有关输入子系统的相关介绍,希望对你有所帮助!
目录
1. 输入子系统
1.1 什么是输入设备
输入设备(也称为 input 设备),常见的输入设备有鼠标、键盘、触摸屏、 遥控器、电脑画图板等,用户通过输入设备与系统进行交互。
在Linux的内核中,对输入设备的使用,实际上运用了3大块来管理,他们分别是所谓的输入设备驱动层、输入子系统核心层,以及事件触发层。他们各自的工作分别是:
1. 输入设备驱动层:
每一种设备都有其特定的驱动程序,他们被妥当地装载到操作系统的设备模型框架内,封装硬件所提供的功能,向上提供规定的接口。
2. 核心层:
此处将收集由设备驱动层发来的数据,整合之后触发某一事件。
3. 事件触发层:
这一层是我们需要关注的,我们可以通过在用户空间读取相应设备的节点文件来获知某设备的某一个动作。
1.2 input 子系统
输入设备种类非常多,每种设备上报的数据类型又不一样,那么 Linux 系统如何管理呢? Linux 系统为了统一管理这些输入设备,实现了一套能够兼容所有输入设备的框架,那么这个框架就是 input 子系统。驱动开发人员基于 input 子系统开发输入设备的驱动程序, input 子系统可以屏蔽硬件的差异,向应用层提供一套统一的接口。
基于 input 子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点(设备文件), 设备节点名称通常为 eventX(X 表示一个数字编号 0、 1、 2、 3 等),譬如/dev/input/event0、 /dev/input/event1、/dev/input/event2 等,通过读取这些设备节点可以获取输入设备上报的数据。
1.3 读取数据的流程
假设触摸屏设备对应的设备节点为/dev/input/event0,那么数据读取流程如下:
- 应用程序打开/dev/input/event0 设备文件;
- 应用程序发起读操作(譬如调用 read),如果没有数据可读则会进入休眠(阻塞 I/O 情况下);
- 当有数据可读时,应用程序会被唤醒,读操作获取到数据返回;
- 应用程序对读取到的数据进行解析。
1.4 数据流向
以触摸屏为例,当手指在屏幕上滑动的时候,数据流大致是这样的:
驱动层中的触摸屏驱动会源源不断地产生触摸屏相关数据,并向上递送给内核输入子系统,输入子系统进一步将这些信息规整为统一的结构体,并借助事件触发层发往对应的设备节点,至此,应用程序即可从这些设备节点读取相关信息。
2.输入信息结构体
首先我们要知道,应用程序打开输入设备对应的设备文件,向其发起读操作,那么这个读操作获取到的是什么样的数据呢?
其实每一次 read 操作获取的都是一个 struct input_event 结构体类型数据, 该结构体定义在头文件中,它的定义如下:
liuth@liuth:/usr/include/linux$ cat input.h -n
8 #ifndef _INPUT_H
9 #define _INPUT_H
......
23 struct input_event {
24 struct timeval time;
25 __u16 type;
26 __u16 code;
27 __s32 value;
28 };
......
2.1 time
time表示输入事件发生的时间戳,精确到微秒。内核会记 录每个上报的事件其发生的时间,并通过变量 time 返回给应用程序。
2.2 type
type 用于描述发生了哪一种类型的事件(对事件的分类), Linux 系统所支持的输入事件类 型如下所示:
/** Event types*/
#define EV_SYN 0x00 //同步类事件,用于同步事件,有些事件可能会在时间和空间上产生延续,比如持续按住一个按键
//为了更好地管理这些持续的事件,EV_SYN用以将他们分割成一个个的小的数据包。
#define EV_KEY 0x01 //按键类事件, 用以描述键盘,按键或者类似键盘的设备的状态变化。
#define EV_REL 0x02 //相对位移类事件,比如鼠标的移动,滚轮的转动等。
#define EV_ABS 0x03 //绝对位移类事件,绝对位移,比如触摸屏上的坐标值。
#define EV_MSC 0x04 //其它杂类事件,不能匹配现有的类型,这相当于当前暂不识别的事件
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
2.3 code
code 表示该类事件中的哪一个具体事件, 每一种事件类型都包含多种不同的事件, 譬如一个键盘上通常有很多按键, 譬如字母 A、 B、 C、 D 或者数字 1、 2、 3、 4 等, 而 code 变量则告知应用程序是哪一个按键发生了输入事件。
2.4 value
内核每次上报事件都会向应用层发送一个数据 value, 对 value 值的解释随着 code 的变化而 变化。
绝对位移事件
触摸屏设备是一种绝对位移设备,它能够产生绝对位移事件; 譬如对于触摸屏来说,一个触摸点所包含的信息可能有多种,譬如触摸点的 X 轴坐标、 Y 轴坐标、 Z 轴坐标、按压力大小以及接触面积等, 所以 code 变量告知应用程序当前上报的是触摸点的哪一种信息(X 坐标还是 Y 坐标、亦或者其它),绝对位移事件如下:
#define ABS_X 0x00 //X 轴
#define ABS_Y 0x01 //Y 轴
#define ABS_Z 0x02 //Z 轴
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
....
3. 数据同步
同步事件类型 EV_SYN,用于实现同步操作、告知接收者本轮上报的数据已经完整。应用程序读取输入设备上报的数据时,一次 read 操作只能读取一个 struct input_event 类型数据,譬如对于触摸屏来说,一个触摸点的信息包含了 X 坐标、 Y 坐标以及其它信息, 对于这样情况,应用程序需要执行多次 read 操作才能把一个触摸点的信息全部读取出来, 这样才能得到触摸点的完整信息。
那么应用程序如何得知本轮已经读取到完整的数据了呢?
其实这就是通过同步事件来实现的, 内核将本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、 可以进行同步了。
所有的输入设备都需要上报同步事件, 上报的同步事件通常是 SYN_REPORT, 而 value 值通常为 0。
如果喜欢请不吝给予三连支持!