input子系统学习笔记九 evdev输入事件驱动分析

【感谢终结者投递本文】

        evdev 输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev 输入事件驱动从底层接收事件信息,将其反映到 sys 文件系统中,用户程序通过对 sys 文件系统的操作,就能够达到处理事件的能力。下面先对 evdev 的初始化进行简要的分析。

evdev的初始化

        evdev 以模块的方式被组织在内核中,与其他模块一样,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识 evdev 的存在。

        evdev_init()初始化函数

        evdev 模块定义在/drivers/input/evdev.c 文件中,该模块的初始化函数是 evdev_init()。在初始化函数中注册了一个 evdev_handler 结构体,用来对一些通用的抽象事件进行统一处理,该函数的代码如下:

C/C++代码
  1. static int __init evdev_init(void)  
  2. {  
  3.         return input_register_handler(&evdev_handler);/*调 用input_register_handler() 函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如链接:*/  
  4. }  

        参数evdev_handler

        结构体定义如下:

C/C++代码
  1. static struct input_handler evdev_handler = {  
  2.         .event= evdev_event,  
  3.         .connect= evdev_connect,  
  4.         .disconnect = evdev_disconnect,  
  5.         .fops= &evdev_fops,  
  6.         .minor= EVDEV_MINOR_BASE,/*定义了 minor 为 EVDEV_MINOR_BASE(64) 。因为一个 handler 可以处理 32 个设备,所以 evdev_handler 所能处理的设备文件范围为(13,64)~(13,64+32) 中 13 是所有输入设备的主设备号。*/  
  7.         .name= "evdev",  
  8.         .id_table = evdev_ids,/*定义了 id_table 结构。回忆前面几节的内容,由 input_attach_handler()函数可知,input_dev 与 handler 匹配成功的关键,在于 handler 中的 blacklist 和 id_talbe.。Evdev_handler只定义了 id_table,其定义如链接: */  
  9. };  

        id_talbe

C/C++代码
  1. static const struct input_device_id evdev_ids[] = {  
  2.         .driver_info = 1 }, /* Matches all devices */  
  3.         { },/* Terminating zero entry */  
  4. };  

        evdev_ids 没有定义 flags,也没有定义匹配属性值。这个 evdev_ids 的意思就是:evdev_handler可以匹配所有 input_dev 设备,也就是所有的 input_dev 发出的事件,都可以由 evdev_handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用 handler->connect()函数

        evdev_connect()函数

        evdev_connect()函数主要用来连接input_dev 和 input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果。代码如下:

C/C++代码
  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)  
  2. {  
  3.         struct evdev *evdev;  
  4.         int minor;  
  5.         int error;  
  6.         /*第 07~13 行,for 循环中的 EVDEV_MINORS 定义为 32,表示 evdev_handler 所表示的 32 个设备文件。 evdev_talbe 是一个 struct evdev 类型的数组,struct evdev 是模块使用的封装结构,与具体的输入设备有关。第 08 行,这一段代码的在 evdev_talbe 找到为空的那一项,当找到为空的一项,便结束 for 循环。这时,minor 就是数组中第一项为空的序号。第 10 到13 行,如果没有空闲的表项,则退出。*/  
  7.         for (minor = 0; minor < EVDEV_MINORS; minor++)  
  8.         if (!evdev_table[minor])  
  9.                 break;  
  10.   
  11.         if (minor == EVDEV_MINORS) {  
  12.                 printk(KERN_ERR "evdev: no more free evdev devices\n");  
  13.                 return -ENFILE;  
  14.         }  
  15.   
  16.         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);/*第 14~16 行,分配一个 struct evdev 的空间,如果分配失败,则退出。*/  
  17.         if (!evdev)  
  18.                 return -ENOMEM;  
  19. /*第 17~20 行,对分配的 evdev 结构进行初始化,主要对链表、互斥锁和等待队列做必要的封装了一个 handle 结构,这个结构与 handler 是不同的。可以把 handle初始化。 evdev 中,在这个结构用来联系匹配成功的 handler 和 input 看成是 handler 和 input device 的信息集合体,device。*/  
  20.         INIT_LIST_HEAD(&evdev->client_list);  
  21.         spin_lock_init(&evdev->client_lock);  
  22.         mutex_init(&evdev->mutex);  
  23.         init_waitqueue_head(&evdev->wait);  
  24.         /*第 21 行,对 evdev 命一个名字,这个设备的名字形如 eventx, 如 event1、 event2 和 event3等。最大有 32 个设备,这个设备将在/dev/input/目录下显示。*/  
  25.         dev_set_name(&evdev->dev, "event%d", minor);  
  26.         evdev->exist = 1;  
  27.         /*第 23~27 行,对 evdev 进行必要的初始化。其中,主要对 handle 进行初始化,这些初始化的目的是使 input_dev 和 input_handler 联系起来。*/  
  28.         evdev->minor = minor;  
  29.   
  30.         evdev->handle.dev = input_get_device(dev);  
  31.         evdev->handle.name = dev_name(&evdev->dev);  
  32.         evdev->handle.handler = handler;  
  33.         evdev->handle.private = evdev;  
  34.         /*第 28~33 行,在设备驱动模型中注册一个 evdev->dev 的设备,并初始化一个 evdev->dev 的设备。这里,使 evdev->dev 所属的类指向 input_class。这样在/sysfs 中创建的设备目录就会在/sys/class/input/下显示。*/  
  35.         evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);  
  36.         evdev->dev.class = &input_class;  
  37.         evdev->dev.parent = &dev->dev;  
  38.         evdev->dev.release = evdev_free;  
  39.         device_initialize(&evdev->dev);  
  40.   
  41.         error = input_register_handle(&evdev->handle);/*第 34 行,调用 input_register_handle()函数注册一个 input_handle 结构体。*/  
  42.         if (error)  
  43.         goto err_free_evdev;  
  44.   
  45.         error = evdev_install_chrdev(evdev);/*第 37 行,注册 handle,如果成功,那么调用 evdev_install_chrdev 将 evdev_table 的 minor项指向 evdev.。*/  
  46.         if (error)  
  47.                 goto err_unregister_handle;  
  48.   
  49.         error = device_add(&evdev->dev);/*将 evdev->device 注册到 sysfs 文件系统中。*/  
  50.         /*第 41~50 行,进行一些必要的错误处理。*/  
  51.         if (error)  
  52.                 goto err_cleanup_evdev;  
  53.   
  54.         return 0;  
  55.   
  56.         err_cleanup_evdev:  
  57.         evdev_cleanup(evdev);  
  58.         err_unregister_handle:  
  59.         input_unregister_handle(&evdev->handle);  
  60.         err_free_evdev:  
  61.         put_device(&evdev->dev);  
  62.         return error;  
  63. }  

        evdev 设备的打开

        用户程序通过输入子系统创建的设备结点函数 open()、read()和 write()等,打开和读写输入设备。创建的设备结点显示在/dev/input/目录下,由 eventx 表示。

        evdev_open()函数

        对主设备号为 INPUT_MAJOR 的设备结点进行操作,会将操作集转换成 handler 的操作集。在 evdev_handler 中定义了一个 fops 集合,被赋值为 evdev_fops 的指针。evdev_fops 就是设备结点的操作集,其定义代码如下:

C/C++代码
  1. static const struct file_operations evdev_fops = {  
  2.         .owner= THIS_MODULE,  
  3.         .read= evdev_read,  
  4.         .write= evdev_write,  
  5.         .poll= evdev_poll,  
  6.         .open= evdev_open,  
  7.         .release= evdev_release,  
  8.         .unlocked_ioctl = evdev_ioctl,  
  9.         .fasync= evdev_fasync,  
  10.         .flush= evdev_flush  
  11. };  

        evdev_fops 结 构 体 是 一 个 file_operations 的 类 型 。 当 用 户 层 调 用 类 似 代 码open("/dev/input/event1" , O_RDONLY) 函 数 打 开 设 备 结 点 时 , 会 调 用 evdev_fops 中 的evdev_read()函数,该函数的代码如下:

C/C++代码
  1. static int evdev_open(struct inode *inode, struct file *file)  
  2. {  
  3.         struct evdev *evdev;  
  4.         struct evdev_client *client;  
  5.         int i = iminor(inode) - EVDEV_MINOR_BASE;/*iminor(inode) - EVDEV_MINOR_BASE 得到了在 evdev_table[]中的序号,赋给变量 i。*/  
  6.         int error;  
  7.   
  8.         if (i >= EVDEV_MINORS)  
  9.                 return -ENODEV;  
  10.         /*第 09~17 行,将数组 evdev_table[]中对应的 evdev 取出,并调用 get_device()增加引用计数。*/  
  11.         error = mutex_lock_interruptible(&evdev_table_mutex);  
  12.         if (error)  
  13.                 return error;  
  14.         evdev = evdev_table[i];  
  15.         if (evdev)  
  16.                 get_device(&evdev->dev);  
  17.         mutex_unlock(&evdev_table_mutex);  
  18.   
  19.         if (!evdev)  
  20.                 return -ENODEV;  
  21.         /*第 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) */  
  22.         client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);  
  23.         if (!client) {  
  24.                 error = -ENOMEM;  
  25.                 goto err_put_evdev;  
  26.         }  
  27.   
  28.         spin_lock_init(&client->buffer_lock);  
  29.         client->evdev = evdev;  
  30.         evdev_attach_client(evdev, client);  
  31.   
  32.         error = evdev_open_device(evdev);/* evdev_open_device()函数 */  
  33.         if (error)  
  34.                 goto err_free_client;  
  35.   
  36.         file->private_data = client;  
  37.         nonseekable_open(inode, file);  
  38.   
  39.         return 0;  
  40.   
  41.         err_free_client:  
  42.                 evdev_detach_client(evdev, client);  
  43.         kfree(client);  
  44.                 err_put_evdev:  
  45.         put_device(&evdev->dev);  
  46.         return error;  
  47. }   

        evdev_open_device()函数

        evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用 input_open_device()函数打开设备。 evdev_open_device()函数的代码如下:

C/C++代码
  1. static int evdev_open_device(struct evdev *evdev)  
  2. {  
  3.         int retval;  
  4.   
  5.         retval = mutex_lock_interruptible(&evdev->mutex);  
  6.         if (retval)  
  7.                 return retval;  
  8.   
  9.         if (!evdev->exist)/*判断该设备是否存在,如果不存在则返回设备不存在。*/  
  10.                 retval = -ENODEV;  
  11.         else if (!evdev->open++) {/*第 09~12 行,如果 evdev 是第一次打开,就会调用 input_open_device()打开 evdev 对应的handle;否则不做任何操作返回。*/  
  12.                 retval = input_open_device(&evdev->handle);  
  13.                 if (retval)  
  14.                         evdev->open--;  
  15.            }  
  16.   
  17.         mutex_unlock(&evdev->mutex);  
  18.         return retval;  
  19. }  

        input_open_device()函数

        在这个函数中,递增 handle 的打开计数。如果是第一次打开,则调用 input_dev 的 open()函数。

Java代码
  1. int input_open_device(struct input_handle *handle)  
  2. {  
  3.         struct input_dev *dev = handle->dev;  
  4.         int retval;  
  5.   
  6.         retval = mutex_lock_interruptible(&dev->mutex);  
  7.         if (retval)  
  8.                 return retval;  
  9.   
  10.         if (dev->going_away) {  
  11.                 retval = -ENODEV;  
  12.                 goto out;  
  13.         }  
  14.   
  15.         handle->open++;  
  16.   
  17.         if (!dev->users++ && dev->open)  
  18.                 retval = dev->open(dev);  
  19.   
  20.         if (retval) {  
  21.                 dev->users--;  
  22.                 if (!--handle->open) {  
  23.                         /* 
  24.                         * Make sure we are not delivering any more events 
  25.                         * through this handle 
  26.                         */  
  27.                         synchronize_rcu();  
  28.                 }  
  29.         }  
  30.   
  31.         out:  
  32.                 mutex_unlock(&dev->mutex);  
  33.         return retval;  
  34. }  

        至此,关于input子系统的内容已经全部结束!

原文 http://www.ourunix.org/post/298.html

你可能感兴趣的:(list,File,Module,table,Class,input)