linux input子系统分析--概述与数据结构
Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。输入设备一般包括键盘,鼠标,触摸屏等,在内核中都是以输入设备出现的。下面分析input输入子系统的结构,以及功能实现。
一. Input子系统结构与功能实现
1. Input子系统是分层结构的,总共分为三层: 硬件驱动层,子系统核心层,事件处理层。
(1)其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
2. 各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),Input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层-->子系统核心-->事件处理层-->用户空间
3. 以触摸屏为例说明输入子系统的工作流程:
注:mini2440的触摸屏驱动所用驱动层对应的模块文件为:s3c2410_ts.c,事件处理层对应的模块文件为 evdev.c
(1)s3c2410_ts模块初始化函数中将触摸屏注册到了输入子系统中,于此同时,注册函数在事件处理层链表中寻找事件处理器,这里找到的是evdev,并且将驱动与事件处理器挂载。并且在/dev/input中生成设备文件event0,以后我们访问这个文件就会找的我们的触摸屏驱动程序。
(2)应用程序打开设备文件/dev/input/event0,读取设备文件,调用evdev模块中read,如果没有事件进程就会睡眠。
(3)当触摸屏按下,驱动层通过子系统核心将事件(就是X,Y坐标),传给事件处理层也就是evdev,evdev唤醒睡眠的进程,将事件传给进程处理。
二.主要input通用数据结构
1.input_dev 这是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,成员比较多
(1)有以下几个数组:
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //事件支持的类型 // 下面是每种类型支持的编码 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按键 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //绝对坐标,其中触摸屏驱动使用的就是这个 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)];evbit[BITS_TO_LONGS(EV_CNT)]; 这个数组以位掩码的形式,代表了这个设备支持的事件的类型。设置方式如:
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat) { dev->absmin[axis] = min; dev->absmax[axis] = max; dev->absfuzz[axis] = fuzz; dev->absflat[axis] = flat; dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); //填充了absbit这个数组 }触摸屏驱动中是这样调用的
(2) struct input_id id 成员
这个是标识设备驱动特征的
struct input_id { __u16 bustype; //总线类型 __u16 vendor; //生产厂商 __u16 product; //产品类型 __u16 version; //版本 };如果需要特定的事件处理器来处理这个设备的话,这几个就非常重要,因为子系统核心是通过他们,将设备驱动与事件处理层联系起来的。但是因为触摸屏驱动所用的事件处理器为evdev,匹配所有,所有这个初始化
struct input_handle { void *private; //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。 int open; //打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的open方法间接设置 const char *name; struct input_dev *dev; //关联的input_dev结构 struct input_handler *handler; //关联的input_handler结构 struct list_head d_node; //input_handle通过d_node连接到了input_dev上的h_list链表上 struct list_head h_node; //input_handle通过h_node连接到了input_handler的h_list链表上 };4. 三个数据结构之间的关系
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。通过input_handle也可以找到input_dev和input_handler。