本文是我学习时所写,非百分之百原创,望指出错误之处。
参考资料:
input子系统按键处理
INPUT输入子系统
在系统中会出现很多的input设备,比如:键盘、屏幕等等,这些物理设备都会统一的抽象为input设备。
用来抽象这些设备的数据结构是struct input_dev结构,该结构如下:
input输入子系统的架构如下图:
从中我们可以得知,input输入子系统分为上中下三个部分。
1、下层设备驱动层。系统中可以注册多个input输入设备,每个设备对应不同的物理硬件设备,比如:键盘input设备、鼠标input设备等等。
2、上层为处理程序(handlers),事件驱动层。不同的input设备在这一层都会有不同的handlers所对应,不同的handlers在在=应用层中的接口命名方式又不一样,例如:Mouse下的输入设备在应用层的接口是 /dev/input/mousen (n代表0、1、2…),Joystick下的输入设备在应用层的接口是 /dev/input/jsn(n代表0、1、2…),这个是在input输入子系统中实现的。
3、中间的输入核心层。从图中我们可以看到,输入核心层起到上下层中进行数据传输的作用,当下层发生任意输入事件是,核心层便被激活,然后将该事件传输给上层的一个或多个handlers中去。
input核心层分别为设备驱动层和事件驱动层提供了相关的API接口。
提供给设备驱动层的API
设备驱动层用struct input_dev结构来抽象一个硬件设备。
以下为struct input_dev的结构:
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //表示此input设备支持的事件
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 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 sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
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; //当前占有该设备的handle
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
struct list_head h_list;//用来挂接dev上连接的所有handle的一个链表头
struct list_head node;//作为一个链表节点将自己挂接到 全局input_dev_list 链表上
};
其中,成员unsigned long evbit[BITS_TO_LONGS(EV_CNT)],表示此input设备支持的事件,比如:EV_SYN(同步事件)、EV_KEY(按键事件)、EV_SW(开关事件)、EV_ABS(绝对坐标事件)、EV_REL(相对坐标事件)、EV_LED(LED灯事件)、EV_SND(声音事件)、EV_REP(重复按键事件)、EV_FF(受力事件)、EV_PWR(电源相关事件)。
成员unsigned long keybit[BITS_TO_LONGS(KEY_CNT)],表示
成员unsigned long absbit[BITS_TO_LONGS(ABS_CNT)],设置相应的位以支持某一类绝对值坐标。
核心层提供给设备驱动层的API主要有三个,如下所示:
struct input_dev *input_allocate_device(void);//申请一个input设备
int input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
/*函数input_set_capability,该函数用来设备准备注册的input设备所支持的上报事件的类型type,如EV_KEY;以及需要上报的事件的code,
如KEY_POWER*/
int input_register_device(struct input_dev *dev);//注册一个已经设置好了的input设备dev
我们在编写某个输入设备的驱动程序时,一般会先使用函数input_allocate_device申请一个input设备。然后使用input_set_capability函数对该input设备进行设置,例如支持哪类事件,具体事件的code等等。然后调用input_register_device函数将input设备注册到内核中。
#input_register_device注册函数中重要的调用关系如下:
int input_register_device(struct input_dev *dev) #input设备注册函数
__set_bit(EV_SYN, dev->evbit); #首先将该设备将EV_SYN置位,表示支持所有事件
init_timer(&dev->timer); #初始化一个定时器,该定时器在处理重复事件时发挥作用
device_add(&dev->dev); #将input_dev内置的struct device dev结构注册到Linux设备模型中去
list_add_tail(&dev->node, &input_dev_list);#将我们注册的dev加入到input_dev_list链表中
#input核心层维护两个全局链表,分别为input_dev_list和input_handler_list,用力啊存放所有的struct input_dev和struct input_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);#这句和上一句是一个循环,遍历input_handler_list上的handler,用于和我们现在的dev匹配。
#input_attach_handler匹配函数的调用关系如下:
input_attach_handler(struct input_dev *dev, struct input_handler*handler)
if (handler->blacklist&& input_match_device(handler->blacklist, dev))
return -ENODEV;
#以上连句表示先判断handler中的blacklist字段是否定义,定了则先对handler->blacklist和dev->id进行匹配
#handler->blacklist是struct input_device_id结构的数据,dev->id是struct input_id结构的数据
id = input_match_device(handler->id_table, dev);
#这一句将handler中的id_table字段和dev->id进行匹配,匹配成功,返回struct input_device_id类型数据指针
#handler->id_table也是struct input_device_id结构的数据
handler->connect(handler, dev, id);#当该input设备和某个handler匹配成功后会调用对应handler中的connect()函数
#connect()函数的作用就是将handler和input_dev使用struct input_handle结构连接起来
#并通过struct input_handle结构组成的链表维护所有的已经匹配成功的handler和input_dev
提供给设备驱动层的API还有如下几个:
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);
input_set_abs_params(input_dev, ABS_RX, 0, 23040, 0, 0);
/*函数input_set_abs_params,参数dev表示input设备,支持绝对值x坐标,并设置它在坐标系中的最大值和最小值,以及干扰值和平焊位置等。
就是通过设置absbit成员实现的。*/
static void input_handle_event(structinput_dev *dev,unsigned int type,unsigned int code, int value);
/*函数input_handle_event,该函数向核心层上报事件,参数dev是经过注册了的input设备指针,参数type表示事件类型,如:EV_KEY、EV_SYN等*/
在input_handle_event函数中,会判断发生的事件应该是继续往上发送给handler还是发送给设备的,如让 LED 灯点亮事件、蜂鸣器鸣叫事件等,这些事件就要发送给设备,需要调用struct input_dev结构中的event()函数;如果是发送给handler层处理,则调用 input_pass_event()函数。
提供给事件驱动层的API
提供给事件驱动层的API主要由以下两个:
int input_register_handler(struct input_handler *handler);//向核心层注册handler
int input_register_handle(struct input_handle *handle);//注册
以下为struct input_handler结构:
struct input_handler {
void *private;// 私有数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
//handler用于向上层上报输入事件的函数
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
//match 函数用来匹配handler 与 input_dev 设备
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
//当handler 与 input_dev 匹配成功之后调用该函数
void (*disconnect)(struct input_handle *handle);
//断开handler 与 input_dev 之间的连接
void (*start)(struct input_handle *handle);
const struct file_operations *fops;// 一个file_operations 指针,指向这个handler的操作函数
int minor;
//该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构
const char *name;//handler的名字
const struct input_device_id *id_table;//指向一个 input_device_id类型的数组,用来进行与input设备匹配时用到的信息
/*id_table会和struct input_dev结构中的id字段进行比较*/
const struct input_device_id *blacklist; //指向一个 input_device_id类型的数组,这个数组包含 handler 应该忽略的设备
struct list_head h_list;
//用来挂接handler上连接的所有handle的一个链表头
struct list_head node;
/*作为一个链表节点将自己挂接到 input_handler_list 链表上
(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)*/
};
想要向核心层注册handler时,就需要将以上结构体中,必要的字段进行填充,然后调用input_register_handler函数。
#input_register_handler函数的调用关系如下:
int input_register_handler(struct input_handler *handler)
list_add_tail(&handler->node,&input_handler_list);
#以上这句将 handler 加入全局的 input_handler_list 链表中,该链表包含了系统中所有的 input_handler
list_for_each_entry(dev,&input_dev_list, node)
input_attach_handler(dev, handler);
#以上两句是一个循环,之前介绍input_register_device时介绍过,遍历全局input_dev_list链表,与之和handler对比
input_register_handle函数在什么地方调用呢,在struct input_handler结构中,有一个函数connect,这个函数作用就是当handler和input_device匹配成功之后就调用该函数,将通过struct input_handle结构建立起关系,并向核心层注册这个struct input_handle结构。
struct input_handle的结构如下:
struct input_handle {
void *private;//handle 的私有数据
int open;// 这个用来做打开计数的
const char *name;// 该handle 的名字
struct input_dev *dev;// 用来指向该handle 绑定的input_dev 结构体
struct input_handler *handler;// 用来指向该handle 绑定的 handler 结构体
struct list_head d_node;// 作为一个链表节点挂接到与他绑定的input_dev->hlist 链表上
struct list_head h_node;// 作为一个链表节点挂接到与他绑定的handler->hlist 链表上
};
在input_register_handle函数中,将handle挂到对应input_dev的h_list链表中,使用d_node挂载;也会将handle挂到对应handler的h_list中,使用h_node挂载。
#input_register_handle函数调用关系如下
int input_register_handle(struct input_handle *handle);
list_add_tail_rcu(&handle->d_node,&dev->h_list);#将d_node挂载到h_list下
list_add_tail(&handle->h_node,&handler->h_list);#将h_ndde挂载到h_list下
if (handler->start)
handler->start(handle);
#以上两句表示,如果handler定义了start()函数,则执行该函数
通过以上步骤,便将handler和input_device关联起来了。
事件上报
之前我们说过input_handle_event函数是核心层提供的事件上报函数,那接下来就跟一下这个函数,如下:
#input_handle_event函数调用关系如下
input_handle_event(struct input_dev *dev,unsigned int type,unsigned int code, int value);
if ((disposition &INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev,type, code, value);
#以上两句表示,如果该事件需要传递给设备,则调用设备的event函数
if(disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
#以上两句表示,如果事件需要传递给handler处理,则调用input_pass_event函数处理
#input_pass_event函数调用关系如下
input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);
handle= rcu_dereference(dev->grab);
#以上一句表示,通过struct input_dev的grab字段获得与该input_dev占有的handle
if(handle)
handle->handler->event(handle,type, code, value);
else
list_for_each_entry_rcu(handle,&dev->h_list, d_node) #一般情况下走这里
if (handle->open)
handle->handler->event(handle,type,code, value);
#以上三局句组成循环,第一句遍历dev中的h_list,该链表是和该input_dev相关联的所有handle的集合
#如果引用计数非0,则调用handler中的event函数继续想上层传递事件
#如果引用计数为0,表示该handler没有被打开,用户进程没有使用该handler,所以不必向上层传递事件