我是靠谱客的博主 酷炫猎豹,最近开发中收集的这篇文章主要介绍linux的input子系统上报函数input_event的底层原理前言一、如何使用input子系统?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

最近在学习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子系统?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部