概述
前言
最近在学习input子系统驱动的编写的时候,用imx6ull开发板上的按键作为输入设备进行注册。实现过程中没什么问题,但是在面试的时候被问到了input_event函数实现的底层原理,所以总结了一下,作为笔记,供大家参考吧。一、如何使用input子系统?
在使用 input 子系统的时候我们只需要注册一个 input 设备即可, input_dev 结构体表示 input设备,此结构体定义在 include/linux/input.h 文件中
步骤大致分为以下几步:
在编写 input 设备驱动的时候我们需要先申请一个 input_dev 结构体变量,使用input_allocate_device 函数来申请一个 input_dev,此函数原型如下所示:
struct input_dev *input_allocate_device(void)
如果要注销的 input 设备的话需要使用 input_free_device 函数来释放掉前面申请到的
input_dev,input_free_device 函数原型如下:
void input_free_device(struct input_dev *dev)
申请好一个 input_dev 以后就需要初始化这个 input_dev,需要初始化的内容主要为事件类型(evbit)和事件值(keybit)这两种。input_dev 初始化完成以后就需要向 Linux 内核注册 input_dev了,需要用到 input_register_device 函数,此函数原型如下:
int input_register_device(struct input_dev *dev)
函数参数和返回值含义如下:
dev:要注册的 input_dev 。
返回值:0,input_dev 注册成功;负值,input_dev 注册失败。
同样的,注销 input 驱动的时候也需要使用 input_unregister_device 函数来注销掉前面注册的 input_dev,input_unregister_device 函数原型如下:
void input_unregister_device(struct input_dev *dev)
函数参数和返回值含义如下:
dev:要注销的 input_dev 。
返回值:无。
综上所述,input_dev 注册过程如下:
1. 使用 input_allocate_device 函数申请一个 input_dev。
2. 初始化 input_dev 的事件类型以及事件值。
3. 使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
4. 卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev,然后使用 input_free_device 函数释放掉前面申请的 input_dev。
回归正题:input_event函数
当我们向 Linux 内核注册好 input_dev 以后还不能高枕无忧的使用 input 设备, input 设备都是具有输入功能的,但是具体是什么样的输入值 Linux 内核是不知道的,我们需要获取到具体的输入值,或者说是输入事件,然后将输入事件上报给 Linux 内核。比如按键,我们需要在按键中断处理函数,或者消抖定时器中断函数中将按键值上报给 Linux 内核,这样 Linux 内核才能获取到正确的输入值。不同的事件,其上报事件的 API 函数不同,我们依次来看一下一些常用的事件上报 API 函数。
同样的还有一些其他的事件上报函数,拿按键的上报函数举例:
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
可以看到,其实这些各模块的上报函数,也是调用底层的input_event函数,那么到底这个input_event又是怎么实现的呢?
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
判断是否是注册时的event类型,驱动probe时注册input_dev时设置了能响应的event类型
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags); //得到自选锁
input_handle_event(dev, type, code, value); /*这句是关键 进一步处理传上来的这个 event */
spin_unlock_irqrestore(&dev->event_lock, flags);//释放锁
}
}
往下看关于input_handle_event(dev, type, code, value);做了什么,他是如何上报数据的:
我们上报的数据是type,code,value,那么这些数据是存在哪里的呢,其实是暂存在一个struct input_value的结构体中的,并由其结构体指针vals指向,所以在后续的处理中,这个指针一直传递到最终上报的函数handler中的event()或者events。下面是内核中具体实现过程:
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
int disposition;
disposition = input_get_disposition(dev, type, code, value);//初始为不做处理
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (!dev->vals)
return;
// 如果将数据交给input handler去处理
if (disposition & INPUT_PASS_TO_HANDLERS) {
struct input_value *v;
if (disposition & INPUT_SLOT) {//这里可能是触摸类设备数据存储
//将input device获取到数据暂存到input value
v = &dev->vals[dev->num_vals++];
v->type = EV_ABS;
v->code = ABS_MT_SLOT;
v->value = dev->mt->slot;
}
v = &dev->vals[dev->num_vals++];
v->type = type;
v->code = code;
v->value = value;
}
if (disposition & INPUT_FLUSH) {
if (dev->num_vals >= 2)
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
} else if (dev->num_vals >= dev->max_vals - 2) {
dev->vals[dev->num_vals++] = input_value_sync;
input_pass_values(dev, dev->vals, dev->num_vals);//更深一步调用 ,最终都是 调用到 event(**)方法
dev->num_vals = 0;
}
}
下面是input_pass_values
static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
struct input_handle *handle;
struct input_value *v;
if (!count)
return;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle) {
count = input_to_handler(handle, vals, count);
} else {
// 从input device中获取到input handle
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
count = input_to_handler(handle, vals, count);
}
rcu_read_unlock();
add_input_randomness(vals->type, vals->code, vals->value);
/* trigger auto repeat for key events */
for (v = vals; v != vals + count; v++) {
if (v->type == EV_KEY && v->value != 2) {
if (v->value)
input_start_autorepeat(dev, v->code);
else
input_stop_autorepeat(dev);
}
}
}
最后
以上就是酷炫猎豹为你收集整理的linux的input子系统上报函数input_event的底层原理前言一、如何使用input子系统?的全部内容,希望文章能够帮你解决linux的input子系统上报函数input_event的底层原理前言一、如何使用input子系统?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复