input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl、gpio 子系统
统一管理外部输入设备
- 按键
- 键盘
- 鼠标
- 触摸屏
对应的用户空间接口
- /dev/input/event0/1/2/...
- /dev/input/mouse0/1/2/...
- /dev/input/sj0/1/2/...
- ...
input子系统软件框架
input子系统的核心
一个输入事件,如手指触摸,键盘按键按下,横竖屏转动等等通过1. input driver -> 2.Input core -> 3.Event handler -> userspace 到达用户空间传给应用程序
1. 设备驱动层:对应 input_dev结构体 负责读取原始数据,如触摸屏的驱动,g_sensor的驱动等。
调用核心层input.c提供的接口,注册输入设备、上报事件
2.输入子系统核心层(input core):其对下提供了设备驱动的接口,对上提供了事件处理层的编程接口。创建了input class。
输入核心层input.c提供了注册设备驱动以及事件驱动的API接口。
维护着事件驱动的链表(input_handler_list)以及设备驱动的链表(input_dev_list)!
3. 事件处理层(event handler):对应input_handler结构体 负责将事件上报,将键值、坐标等数据上报的对应的设备节点,也就是上报给应用程序。(Linux中在用户空间将所有的设备都当初文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下会生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)
- 通用事件处理器(drivers/input/evdev.c)
- 鼠标事件处理器(drivers/input/mousedev.c)
- 摇杆事件处理器(drivers/input/joydev.c)等 (常使用的是通用事件处理器)对应input_handler,负责创建设备节点,负责和应用层进行数据交互以及上报
设备驱动与事件驱动通过id_table进行match,match之后通过connect函数创建事件驱动,事件驱动负责创建设备节点,提供open,close 等文件操作函数包括/dev/input/eventX,或者mouse等,然后通过input_handle进行绑定。维护着client(上层用户)的链表,将数据传递给用户层以及每一个打开文件的client
在核心层 也就是input.c 文件中有如下代码:
input.c 编译进了内核,只要系统启动就会运行input.c中的程序,就会执行到下面的代码,在/dev/目录下创建 一个input目录 主设备号为13
struct class input_class{
.name = "input",
.devnode = input_devnode,
};
……
static int __init input_init(void){
int err;
err = class_register(&input_class);
if(err){
pr_err(unable to register input_dev clsaa!\n);
return err;
}
err = input_proc_init();
if(err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
#define INPUT_MAJOR 13
可见核心层向内核注册了一个主设备号为13的驱动程序,但没有创建设备节点。也没有注册cdev结构体到内核中,也就是没有绑定cdev和指定的次设备号范围,也可以说没有绑定主设备号为13的设备节点和对应的驱动程序
input 核心层(也就是 drivers/input/input.c 这个文件)会向 Linux 内核注册一个字符设备驱程序对应主设备号为13 !
(也就是说在input.c中 就已经调用register_chrdev_region()在chrdevs数组中 添加了chrdevs[13]这个数组成员了)
如下图所示:
输入事件是标准的,对于所有输入设备都应该是可以用的,要实现的是输入设备驱动程序。输入设备驱动程序可以选择合适的输入事件驱动程序,通过输入核心以及输入事件驱动程序,向用户层输入数据
通俗讲解:
输入核心:输入核心提供了注册设备驱动以及事件驱动的API,维护着事件驱动的链表以及设备驱动的链表
设备驱动 :对应input_dev 负责实际的设备数据读取,通过input core 将数据传递到事件驱动程序,上报给用户层
事件驱动:对应input_handler,负责创建设备节点,负责和应用层进行数据交互以及上报
设备驱动与事件驱动通过id_table进行match,match之后通过connect函数创建事件驱动,事件驱动负责创建设备节点,提供open,close 等文件操作函数包括/dev/input/eventX,或者mouse等,然后通过input_handle进行绑定。维护着client(上层用户)的链表,将数据传递给用户层以及每一个打开文件的client
input_dev和input_handler 以及input_handle
1.input_dev
类似于platform_device 用来描述输入设备
Input_dev的定义如下,主要包括如下几个部分:
- evbit、keybit、relbit、absbit等变量标识该input device的类型、支持事件的code类型等;
- keycode相关的参数及函数指针等;
- *** open、event、close、flush为操作接口,如针对event则处理input_handler发送的事件,一般input设备而言,都是input_dev将接收的事件发送给input_handler,而针对led灯这类设备,需要处理input_handler发送的event,用于进行led灯的控制;***(也就是说一般来说是 input_dev上报事件给Input_handler然后调用 input_handler中的函数接口 但是比如LED灯控制就是特殊情况 需要input_handler给 input_dev 发送event 也就是使用Input_dev的event接口)
- users、going_away为该input device的打开及关联相关的引用计数及状态;
- num_vals、max_vals、vals、hint_events_per_packet则表示该input_dev单次分发事件时可发送的最大事件数、当前事件数、存储待分发事件的缓存等内容;
- 针对grab变量。其实现input_device与input_handle的1对1绑定操作,若执行了该操作,则所有该input_device产生的事件,则只分发给其绑定的input_handle对应的input_handler,而不再分发给其他的input_handler。若需要执行该绑定操作,则通过EVIOCGRAB ioctl命令即可设置。
struct input_dev {
void *private; //输入设备私有指针,一般指向用于描述设备驱动层的设备结构
const char *name; //提供给用户的输入设备的名称
const char *phys; //提供给编程者的设备节点的名称
const char *uniq; //指定唯一的ID号,就像MAC地址一样
struct input_id id; //输入设备标识ID,用于和事件处理层进行匹配
unsigned long evbit[NBITS(EV_MAX)]; //位图,记录设备支持的事件类型
unsigned long keybit[NBITS(KEY_MAX)]; //位图,记录设备支持的按键类型
unsigned long relbit[NBITS(REL_MAX)]; //位图,记录设备支持的相对坐标
unsigned long absbit[NBITS(ABS_MAX)]; //位图,记录设备支持的绝对坐标
unsigned long mscbit[NBITS(MSC_MAX)]; //位图,记录设备支持的其他功能
unsigned long ledbit[NBITS(LED_MAX)]; //位图,记录设备支持的指示灯
unsigned long sndbit[NBITS(SND_MAX)]; //位图,记录设备支持的声音或警报
unsigned long ffbit[NBITS(FF_MAX)]; //位图,记录设备支持的作用力功能
unsigned long swbit[NBITS(SW_MAX)]; //位图,记录设备支持的开关功能
unsigned int keycodemax; //设备支持的最大按键值个数
unsigned int keycodesize; //每个按键的字节大小
void *keycode; //指向按键池,即指向按键值数组首地址
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode); //修改按键值
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode); //获取按键值
struct ff_device *ff; //用于强制更新输入设备的部分内容
unsigned int repeat_key; //重复按键的键值
struct timer_list timer; //设置当有连击时的延时定时器
int state; //设备状态
int sync; //同步事件完成标识,为1说明事件同步完成
int abs[ABS_MAX + 1]; //记录坐标的值
int rep[REP_MAX + 1]; //记录重复按键的参数值
unsigned long key[NBITS(KEY_MAX)]; //位图,按键的状态
unsigned long led[NBITS(LED_MAX)]; //位图,led的状态
unsigned long snd[NBITS(SND_MAX)]; //位图,声音的状态
unsigned long sw[NBITS(SW_MAX)]; //位图,开关的状态
int absmax[ABS_MAX + 1]; //位图,记录坐标的最大值
int absmin[ABS_MAX + 1]; //位图,记录坐标的最小值
int absfuzz[ABS_MAX + 1]; //位图,记录坐标的分辨率
int absflat[ABS_MAX + 1]; //位图,记录坐标的基准值
int (*open)(struct input_dev *dev); //输入设备打开函数
void (*close)(struct input_dev *dev); //输入设备关闭函数
int (*flush)(struct input_dev *dev, struct file *file); //输入设备断开后刷新函数
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); //事件处理
struct input_handle *grab; //类似私有指针,可以直接访问到事件处理接口event
struct mutex mutex; //用于open、close函数的连续访问互斥
unsigned int users; //设备使用计数
struct class_device cdev; //输入设备的类信息
union { //设备结构体
struct device *parent;
} dev;
struct list_head h_list; 用来挂接input_dev 设备连接的所有handle 的一个链表头
struct list_head node; //input_dev链表
};
1.h_list成员
struct list_head h_list 是一个链表头,这个链表头对应的链表上挂载了所有和该input_dev结构体相对应的input_handle结构体(这个链表是用来保存所有和这个input_dev结构体对应的input_handle结构体的,每个节点都对应一个input_handle结构体),input_handle结构体是如何挂载在这个链表上的呢?通过input_handle的d_node成员(它是一个struct list_head d_node)作为链表的节点挂载在链表上
2.node成员
内核链表是将链表嵌入数据结构!list_head node成员 是该input_dev结构体在input_dev_list中对应的链表节点
有个问题:input_dev中的 open、close函数有什么用?
2.input_handler
*****(通常来说不需要我们创建input_handler,我们一般使用内核自带的input_handler,比如evdev_handler:)evdev_handler是最常用的input_handler ,它可以对应所有的 input_dev ,也就是说可以对应所有的输入设备,所以在我们后面的编写输入设备驱动时 不需要创建input_handler 只需要创建注册input_dev和上报事件给evdev_handler就可以了。我们只需要使用evdev.c中实现的evdev_handler就足够了,也就是说我们创建的input_dev会和 evdev_handler 匹配 成功后调用其中的evdev_connect函数!
类似于platform_driver 描述事件
- Input_handler可以理解为input_device的方法/驱动,下面我们详细说明;
- event、events接口用于接