【感谢终结者投递本文】
evdev 输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev 输入事件驱动从底层接收事件信息,将其反映到 sys 文件系统中,用户程序通过对 sys 文件系统的操作,就能够达到处理事件的能力。下面先对 evdev 的初始化进行简要的分析。
evdev 以模块的方式被组织在内核中,与其他模块一样,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识 evdev 的存在。
evdev_init()初始化函数
evdev 模块定义在/drivers/input/evdev.c 文件中,该模块的初始化函数是 evdev_init()。在初始化函数中注册了一个 evdev_handler 结构体,用来对一些通用的抽象事件进行统一处理,该函数的代码如下:
- static int __init evdev_init(void)
- {
- return input_register_handler(&evdev_handler);/*调 用input_register_handler() 函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_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,/*定义了 minor 为 EVDEV_MINOR_BASE(64) 。因为一个 handler 可以处理 32 个设备,所以 evdev_handler 所能处理的设备文件范围为(13,64)~(13,64+32) 中 13 是所有输入设备的主设备号。*/
- .name= "evdev",
- .id_table = evdev_ids,/*定义了 id_table 结构。回忆前面几节的内容,由 input_attach_handler()函数可知,input_dev 与 handler 匹配成功的关键,在于 handler 中的 blacklist 和 id_talbe.。Evdev_handler只定义了 id_table,其定义如链接: */
- };
id_talbe
- static const struct input_device_id evdev_ids[] = {
- {
- .driver_info = 1 }, /* Matches all devices */
- { },/* Terminating zero entry */
- };
evdev_ids 没有定义 flags,也没有定义匹配属性值。这个 evdev_ids 的意思就是:evdev_handler可以匹配所有 input_dev 设备,也就是所有的 input_dev 发出的事件,都可以由 evdev_handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用 handler->connect()函数
evdev_connect()函数
evdev_connect()函数主要用来连接input_dev 和 input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果。代码如下:
- 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;
- /*第 07~13 行,for 循环中的 EVDEV_MINORS 定义为 32,表示 evdev_handler 所表示的 32 个设备文件。 evdev_talbe 是一个 struct evdev 类型的数组,struct evdev 是模块使用的封装结构,与具体的输入设备有关。第 08 行,这一段代码的在 evdev_talbe 找到为空的那一项,当找到为空的一项,便结束 for 循环。这时,minor 就是数组中第一项为空的序号。第 10 到13 行,如果没有空闲的表项,则退出。*/
- for (minor = 0; minor < EVDEV_MINORS; minor++)
- if (!evdev_table[minor])
- break;
- if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
- return -ENFILE;
- }
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);/*第 14~16 行,分配一个 struct evdev 的空间,如果分配失败,则退出。*/
- if (!evdev)
- return -ENOMEM;
- /*第 17~20 行,对分配的 evdev 结构进行初始化,主要对链表、互斥锁和等待队列做必要的封装了一个 handle 结构,这个结构与 handler 是不同的。可以把 handle初始化。 evdev 中,在这个结构用来联系匹配成功的 handler 和 input 看成是 handler 和 input device 的信息集合体,device。*/
- INIT_LIST_HEAD(&evdev->client_list);
- spin_lock_init(&evdev->client_lock);
- mutex_init(&evdev->mutex);
- init_waitqueue_head(&evdev->wait);
- /*第 21 行,对 evdev 命一个名字,这个设备的名字形如 eventx, 如 event1、 event2 和 event3等。最大有 32 个设备,这个设备将在/dev/input/目录下显示。*/
- dev_set_name(&evdev->dev, "event%d", minor);
- evdev->exist = 1;
- /*第 23~27 行,对 evdev 进行必要的初始化。其中,主要对 handle 进行初始化,这些初始化的目的是使 input_dev 和 input_handler 联系起来。*/
- evdev->minor = minor;
- evdev->handle.dev = input_get_device(dev);
- evdev->handle.name = dev_name(&evdev->dev);
- evdev->handle.handler = handler;
- evdev->handle.private = evdev;
- /*第 28~33 行,在设备驱动模型中注册一个 evdev->dev 的设备,并初始化一个 evdev->dev 的设备。这里,使 evdev->dev 所属的类指向 input_class。这样在/sysfs 中创建的设备目录就会在/sys/class/input/下显示。*/
- 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);/*第 34 行,调用 input_register_handle()函数注册一个 input_handle 结构体。*/
- if (error)
- goto err_free_evdev;
- error = evdev_install_chrdev(evdev);/*第 37 行,注册 handle,如果成功,那么调用 evdev_install_chrdev 将 evdev_table 的 minor项指向 evdev.。*/
- if (error)
- goto err_unregister_handle;
- error = device_add(&evdev->dev);/*将 evdev->device 注册到 sysfs 文件系统中。*/
- /*第 41~50 行,进行一些必要的错误处理。*/
- if (error)
- goto err_cleanup_evdev;
- 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 设备的打开
用户程序通过输入子系统创建的设备结点函数 open()、read()和 write()等,打开和读写输入设备。创建的设备结点显示在/dev/input/目录下,由 eventx 表示。
evdev_open()函数
对主设备号为 INPUT_MAJOR 的设备结点进行操作,会将操作集转换成 handler 的操作集。在 evdev_handler 中定义了一个 fops 集合,被赋值为 evdev_fops 的指针。evdev_fops 就是设备结点的操作集,其定义代码如下:
- static const struct file_operations evdev_fops = {
- .owner= THIS_MODULE,
- .read= evdev_read,
- .write= evdev_write,
- .poll= evdev_poll,
- .open= evdev_open,
- .release= evdev_release,
- .unlocked_ioctl = evdev_ioctl,
- .fasync= evdev_fasync,
- .flush= evdev_flush
- };
evdev_fops 结 构 体 是 一 个 file_operations 的 类 型 。 当 用 户 层 调 用 类 似 代 码open("/dev/input/event1" , O_RDONLY) 函 数 打 开 设 备 结 点 时 , 会 调 用 evdev_fops 中 的evdev_read()函数,该函数的代码如下:
- static int evdev_open(struct inode *inode, struct file *file)
- {
- struct evdev *evdev;
- struct evdev_client *client;
- int i = iminor(inode) - EVDEV_MINOR_BASE;/*iminor(inode) - EVDEV_MINOR_BASE 得到了在 evdev_table[]中的序号,赋给变量 i。*/
- int error;
- if (i >= EVDEV_MINORS)
- return -ENODEV;
- /*第 09~17 行,将数组 evdev_table[]中对应的 evdev 取出,并调用 get_device()增加引用计数。*/
- error = mutex_lock_interruptible(&evdev_table_mutex);
- if (error)
- return error;
- evdev = evdev_table[i];
- if (evdev)
- get_device(&evdev->dev);
- mutex_unlock(&evdev_table_mutex);
- if (!evdev)
- return -ENODEV;
- /*第 18~30 行,分配并初始化一个 client 结构体,并将它和 evdev 关联起来。 关联的内容是,将 client->evdev 指 向 它 所 表 示 的 evdev , 调 用 evdev_attach_client() 将 client 挂 到evdev->client_list 上。第 29 行,将 client 赋给 file 的 private_data。在 evdev 中,这个操作集就是 evdev_fops,对应的 open()函数如下:static int evder_open(struct inode *inode, struct file *file) */
- client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
- if (!client) {
- error = -ENOMEM;
- goto err_put_evdev;
- }
- spin_lock_init(&client->buffer_lock);
- client->evdev = evdev;
- evdev_attach_client(evdev, client);
- error = evdev_open_device(evdev);/* evdev_open_device()函数 */
- if (error)
- goto err_free_client;
- file->private_data = client;
- nonseekable_open(inode, file);
- return 0;
- err_free_client:
- evdev_detach_client(evdev, client);
- kfree(client);
- err_put_evdev:
- put_device(&evdev->dev);
- return error;
- }
evdev_open_device()函数
evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用 input_open_device()函数打开设备。 evdev_open_device()函数的代码如下:
- static int evdev_open_device(struct evdev *evdev)
- {
- int retval;
- retval = mutex_lock_interruptible(&evdev->mutex);
- if (retval)
- return retval;
- if (!evdev->exist)/*判断该设备是否存在,如果不存在则返回设备不存在。*/
- retval = -ENODEV;
- else if (!evdev->open++) {/*第 09~12 行,如果 evdev 是第一次打开,就会调用 input_open_device()打开 evdev 对应的handle;否则不做任何操作返回。*/
- retval = input_open_device(&evdev->handle);
- if (retval)
- evdev->open--;
- }
- mutex_unlock(&evdev->mutex);
- return retval;
- }
input_open_device()函数
在这个函数中,递增 handle 的打开计数。如果是第一次打开,则调用 input_dev 的 open()函数。
- int input_open_device(struct input_handle *handle)
- {
- struct input_dev *dev = handle->dev;
- int retval;
- retval = mutex_lock_interruptible(&dev->mutex);
- if (retval)
- return retval;
- if (dev->going_away) {
- retval = -ENODEV;
- goto out;
- }
- handle->open++;
- if (!dev->users++ && dev->open)
- retval = dev->open(dev);
- if (retval) {
- dev->users--;
- if (!--handle->open) {
- /*
- * Make sure we are not delivering any more events
- * through this handle
- */
- synchronize_rcu();
- }
- }
- out:
- mutex_unlock(&dev->mutex);
- return retval;
- }
至此,关于input子系统的内容已经全部结束!