【输入子系统04】input_handler之evdev
发布日期:2021-06-29 14:51:14 浏览次数:3 分类:技术文章

本文共 7652 字,大约阅读时间需要 25 分钟。

【输入子系统04】input_handler之evdev

evdev

/kernel/drivers/input下众多事件处理器handler其中的一个,可以看下源码/kernel/drivers/input/evdev.c中的模块init:

static int __init evdev_init(void){
return input_register_handler(&evdev_handler);}

这个初始化就是往input核心中注册一个input_handler类型的evdev_handler,调用的是input.c提供的接口,input_handler结构前文有介绍,看下evdev_handler的赋值:

static struct input_handler evdev_handler = {
.event = evdev_event, .connect = evdev_connect, .disconnect = evdev_disconnect, .fops = &evdev_fops, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids,};

赋值各个函数指针!

input_register_handler

可以看到上面的evdev handler 就是调用这个接口注册到input核心中的,同样evdev.c同目录下也还有其它的handler,有兴趣可以看看它们的init函数,都是会调用到这个接口去注册的.

/** * input_register_handler - register a new input handler * @handler: handler to be registered * * This function registers a new input handler (interface) for input * devices in the system and attaches it to all input devices that * are compatible with the handler. */int input_register_handler(struct input_handler *handler){
struct input_dev *dev; int retval; retval = mutex_lock_interruptible(&input_mutex); if (retval) return retval; INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY; goto out; } input_table[handler->minor >> 5] = handler; //给input.c定义的全局handler 数组赋值,evdev handler的次设备号为64,这里除以32,赋值在input_table[2] } list_add_tail(&handler->node, &input_handler_list); //添加进handler 链表 list_for_each_entry(dev, &input_dev_list, node) //同样遍历input_dev这个链表,依次调用下面的input_attach_handler去匹配input_dev,这个跟input_dev注册的时候的情形类似 input_attach_handler(dev, handler); input_wakeup_procfs_readers(); out: mutex_unlock(&input_mutex); return retval;}

input核心中保存的handler数组:

static struct input_handler *input_table[8];

这是保存注册到input核心中的handler数组,因为在之前input注册的时候注册的字符设备主设备号为13.字符设备的次设备号为0~255,可以有256个设备,

这里后面会看到一个handler可以connect处理32个input设备,所以input体系中,最多拥有8个handler

这个匹配过程和上一篇中的过程是一样的,最后匹配上的话会调用匹配上的handler 中connect指针指向的函数.

另外可以注意的是evdev是匹配所有设备的,因为:

static const struct input_device_id evdev_ids[] = {
{
.driver_info = 1 }, /* Matches all devices */ {
}, /* Terminating zero entry */};

如果没有特定的handler添加进handler链表,那么在匹配的时候,只要有这个evdev的handler,最后都会匹配到evdev,这个具体可以去看看上篇的匹配过程.

我这边调试的是usb触摸屏,所以用的是evdev的handler,下面看下evdev的connect.

evdev_connect

注册的evdev_handler中connect指向的函数为evdev_connect:

/* * Create new evdev device. Note that input core serializes calls * to connect and disconnect so we don't need to lock evdev_table here. */static int evdev_connect(struct input_handler *handler, struct input_dev *dev,             const struct input_device_id *id){
struct evdev *evdev; int minor; int error; for (minor = 0; minor < EVDEV_MINORS; minor++) if (!evdev_table[minor]) break; if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n"); return -ENFILE; } // 可以看到这里evdev handler匹配连接好的设备都以evdev 类型存在这个evdev_table数组的,这个数组大小为32个,这就是我上面说到的,为什么只有8个handler //这里是判断evdev的32个位置中是否有空 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //为上面定义的*evdev分配内存空间 if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list); //以下都是对这个 evdev的初始化了 spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); dev_set_name(&evdev->dev, "event%d", minor); //给这个evdev命名 evdev->exist = true; evdev->minor = minor; // 以minor为索引赋值 evdev->handle.dev = input_get_device(dev); //evdev中的handle变量的初始化 ,后面分析这个handle ,这里面保存的就是已经匹配成功的input_dev 和 handler evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); //把这个evdev中初始化好的handle 注册到input核心中去,代表一个匹配成功的组合 error = evdev_install_chrdev(evdev); //把这个初始化好的evdev添加到上面说到过的evdev_table数组,以minor索引序号 error = device_add(&evdev->dev); //把这个device 添加到/sys/class/input/下面,所以我们可以看到/dev/input下面看到:event0~31 字样字符设备文件,这就是在上面命名的 return 0; err_cleanup_evdev: evdev_cleanup(evdev); err_unregister_handle: input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); return error;}

evdev

这里的evdev变量的结构如下:

struct evdev{
int open; //打开标志 int minor; //次设备号 struct input_handle handle; //包含的handle wait_queue_head_t wait; //等待队列 struct evdev_client __rcu *grab; //强制绑定的evdev_client结构 struct list_head client_list; //evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备 spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; bool exist;};

关于这个结构变量我的理解是抽象出来一个设备,代表一个input_dev与其匹配好的handler的组合(handle),可以看作提供给事件处理层的一个封装.

input_handle

这个代表一个匹配成功的input dev和 handler组合,定义在input.h中,每个evdev中包含一个input_handle,并且注册到input核心中:

/** * struct input_handle - links input device with an input handler * @private: handler-specific data * @open: counter showing whether the handle is 'open', i.e. should deliver *    events from its device * @name: name given to the handle by handler that created it * @dev: input device the handle is attached to * @handler: handler that works with the device through this handle * @d_node: used to put the handle on device's list of attached handles * @h_node: used to put the handle on handler's list of handles from which *    it gets events */struct input_handle {
void *private; //指向上面封装的evdev int open; const char *name; struct input_dev *dev; //input 设备 struct input_handler *handler; // 一个input的handler struct list_head d_node; //链表结构 struct list_head h_node;};

input_register_handle:

看看这个handle的注册,不要和handler搞混淆了,这不是一个概念~

/** * input_register_handle - register a new input handle * @handle: handle to register * * This function puts a new input handle onto device's * and handler's lists so that events can flow through * it once it is opened using input_open_device(). * * This function is supposed to be called from handler's * connect() method. */int input_register_handle(struct input_handle *handle){
struct input_handler *handler = handle->handler; struct input_dev *dev = handle->dev; //取出两个成员 ... /* * Filters go to the head of the list, normal handlers * to the tail. */ if (handler->filter) list_add_rcu(&handle->d_node, &dev->h_list); else list_add_tail_rcu(&handle->d_node, &dev->h_list); //把这个handle的d_node 加到对应input_dev的h_list链表里面 ... list_add_tail_rcu(&handle->h_node, &handler->h_list); //把这个handle的h_node 加到对应input_handler的h_list链表里面 ... }

这个注册是把handle 本身的链表加入到它自己的input_dev 以及 input_handler的h_list链表中,这样以后就可以通过h_list遍历到这个handle,

这样就实现了三者的绑定联系.

另外在evdev中还有个结构:

struct evdev_client {
unsigned int head; //buffer数组的索引头 unsigned int tail; //buffer数组的索引尾 unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct wake_lock wake_lock; bool use_wake_lock; char name[28]; struct fasync_struct *fasync; //异步通知函数 struct evdev *evdev; //包含一个evdev变量 struct list_head node; //链表 unsigned int bufsize; struct input_event buffer[]; //input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码(code),值(value)};

这个结构会在evdev被打开的时候 创建,这里关于evdev的初始以及在input系统中承接作用暂时介绍到这里,

————————————————

原文链接:https://blog.csdn.net/jscese/article/details/42238377

转载地址:https://ciellee.blog.csdn.net/article/details/104946173 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:《奔跑吧-Linux内核》--- 不服,来跑个分吧
下一篇:【输入子系统03】输入子系统核心

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月24日 21时48分25秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章