1. Linux输入子系统框架
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件驱动程序层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动程序层。它们各自的功能如下:
对于输入子系统设备驱动程序层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
对于输入子系统事件驱动程序层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。
Linux输入子系统的框架如下:
/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。
输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。
2. Linux输入设备驱动编程
编写符合输入子系统框架的设备驱动程序主要有以下四个步骤:
1.分配一个input_dev结构体,调用input_allocate_device()或者devm_input_allocate_device(struct input_dev*)实现;
2.设置input设备支持的事件类型,其支持的事件类型如下表;
Linux输入子系统支持的事件类型
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件
EV_REL 0x02 相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS 0x03 绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC 0x04 其它
EV_SW 0x05 开关
EV_LED 0x11 按键/设备灯
EV_SND 0x12 声音/警报
EV_REP 0x14 重复
EV_FF 0x15 力反馈
EV_PWR 0x16 电源
EV_FF_STATUS 0x17 力反馈状态
EV_MAX 0x1f 事件类型最大个数和提供位掩码支持
3.将输入设备注册到输入子系统中,调用input_register_device(struct input_dev*)函数实现;
4.硬件相关的代码:注册中断处理函数,比如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等),即上报输入事件。
Linux输入子系统提供了设备驱动层上报输入事件的函数,在include/linux/input.h中:
void input_event(struct input_dev* dev, unsigned int type, unsigned int code, int value); //上报指定的type、code的输入事件
void input_report_key(struct input_dev *dev, unsigned int code, int value); //上报按键事件
void input_report_rel(struct input_dev *dev, unsigned int code, int value); //上报相对坐标事件
void input_report_abs(struct input_dev *dev, unsigned int code, int value); //上报绝对坐标事件
当提交输入设备产生的输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:
void input_sync(struct input_dev *dev);