一、Linux Input 子系统框架
本文是基于kernel-3.18版本分析的。输入(Input)子系统是分层架构的,总共分为5 层,从上到下分别是:用户空间层(User Space)事件处理层(Event Handler)、输入子系统核心层(Input Core)、硬件驱动层(Input Driver) 、硬件设备层(Hardware)。
驱动根据CORE提供的接口,向上报告发生的按键动作。然后CORE根据驱动的类型,分派这个报告给对应的事件处理层进行处理。事件处理层把数据变化反应到设备模型的文件中(事件缓冲区)。并通知在这些设备模型文件上等待的进程。
input子系统框架:
(1) “硬件驱动层”负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,比如你的设备是触摸输入设备,还是鼠标输入设备,还是键盘输入设备,这些不同的设备,自然有不同的硬件操作,驱动工程师往往只需要完成这层的代码编写。
(2) “输入子系统核心层”是链接其他两层之间的纽带与桥梁,向下提供硬件驱动层的接口,向上提供事件处理层的接口。
(3) “事件处理层” 负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value), Input 子系统支持的所有事件都定义在 input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间。
二、Input 主要通用数据结构
2.1、input_dev
输入设备 input_dev,这是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构
[->input.h]
struct input_dev {
const char *name; //输入设备的名称
const char *phys; //输入设备节点名称
const char *uniq; //指定唯一的ID号,就像MAC地址一样
struct input_id id; //输入设备标识ID,用于和事件处理层进行匹配
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
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)]; //位图,记录设备支持的开关功能
unsigned int hint_events_per_packet;
unsigned int keycodemax; //设备支持的最大按键值个数
unsigned int keycodesize; //每个按键的字节大小
void *keycode; //指向按键池,即指向按键值数组首地址
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode); //修改按键值
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke); //获取按键值
struct ff_device *ff; //用于强制更新输入设备的部分内容
unsigned int repeat_key; //重复按键的键值
struct timer_list timer; //设置当有连击时的延时定时器
int rep[REP_CNT];
struct input_mt *mt;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //位图,按键的状态
unsigned long led[BITS_TO_LONGS(LED_CNT)]; //位图,led的状态
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; //位图,声音的状态
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //位图,开关的状态
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 __rcu *grab; //类似私有指针,可以直接访问到事件处理接口event
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list; //该链表头用于链接此设备所关联的input_handle
struct list_head node; //用于将此设备链接到input_dev_list(链接了所有注册到内核的事件处理器)
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
2.2、input_handler
input_handler 这是事件处理器的数据结构,代表一个事件处理器
[->input.h]
struct input_handler {
void *private;
/* 当事件处理器接收到来自Input设备传来的事件时调用的处理函数,
event、events用于处理事件 */
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
/* 比较 device's id with handler's id_table ,匹配device and handler*/
bool (*match)(struct input_handler *handler, struct input_dev *dev);
/* connect用于建立intput_handler和input_dev的联系,
当一个Input设备注册到内核的时候被调用,将输入设备与事件处理器联结起来 */
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
/* disconnect用于解除handler和device的联系 */
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor; //次设备号
const char *name; //次设备号
const struct input_device_id *id_table; //用于和device匹配 ,这个是事件处理器所支持的input设备
//这个链表用来链接他所支持的input_handle结构,input_dev与input_handler配对之后就会生成一个input_handle结构
struct list_head h_list;
//链接到input_handler_list,这个链表链接了所有注册到内核的事件处理器
struct list_head node;
};
2.3、input_handle
[->input.h]
struct input_handle {
/*
每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,
注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。
*/
void *private;
/*
打开标志,每个input_handle 打开后才能操作,
这个一般通过事件处理器的open方法间接设置
*/
int open;
const char *name;
/* 指向Input_dev结构实体 */
struct input_dev *dev;
/* 指向Input_Hander结构实体 */
struct input_handler *handler;
/* input_handle通过d_node连接到了input_dev上的h_list链表上 */
struct list_head d_node;
/* input_handle通过h_node连接到了input_handler的h_list链表上 */
struct list_head h_node;
};
2.4、三个数据结构之间的关系
input_dev: 是硬件驱动层,代表一个input设备。 input_handler: 是事件处理层,代表一个事件处理器。 input_handle: 属于核心层,代表一个配对的input_dev与input_handler
input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。注:(稍后详细分析)
[->input.c]
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
......
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
......
}
input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)注:(稍后详细分析)
[->input.c]
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error;
......
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
......
return 0;
}
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle在设备注册和事件处理器,注册的时候都要进行配对工作(input_match_device),配对后就会实现链接(handler->connect)通过input_handle也可以找到input_dev和input_handler。注:(稍后详细分析)
[->input.c]
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
......
error = handler->connect(handler, dev, id);
......
return error;
}
我们可以看到,input_device和input_handler中都有一个h_list,而input_handle拥有指向input_dev和input_handler的指针,也就是说input_handle是用来关联input_dev和input_handler的。 那么为什么一个input_device和input_handler中拥有的是h_list而不是一个handle呢?因为一个device可能对应多个handler,而一个handler也不能只处理一个device,比如说一个鼠标,它可以对应even handler,也可以对应mouse handler,因此当其注册时与系统中的handler进行匹配,就有可能产生两个实例,一个是evdev,另一个是mousedev,而任何一个实例中都只有一个handle。至于以何种方式来传递事件,就由用户程序打开哪个实例来决定。后面一个情况很容易理解,一个事件驱动不能只为一个甚至一种设备服务,系统中可能有多种设备都能使用这类handler,比如event handler就可以匹配所有的设备。在input子系统中,有8种事件驱动,每种事件驱动最多可以对应32个设备,因此dev实例总数最多可以达到256个。
三、Input 核心层(Input.c)
这一节主要介绍核心层的初始化,input_device、input_handle、input_handler之间的关系(稍后回头看更佳)。 总体概览图:
3.1、输入核心层:初始化
首先从驱动”入口函数”开始查看
[->input.c]
static int __init input_init(void)
{