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.         return input_register_handler(&evdev_handler);/*调 用input_register_handler() 函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如链接:*/ 

        参数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. {
  3.         .driver_info = 1 }, /* Matches all devices */ 
  4.         { },/* Terminating zero entry */ 
  5. }; 

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

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

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

        input_open_device()函数

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值