Linux input子系统分析之四 input handler驱动实例evdev分析

       在前面几章我们介绍了input子系统的实现,针对input子系统而言,主要就包括input handler、input handle、input device这三种逻辑抽象,而在input子系统中input handler与input device是多对多的联系(而在设备驱动模型中,一般设备与驱动时1对多的关联)。

本章我们将通过一个input handler实例,理解input handler驱动的实现流程。我们选用evdev进行

分析。

本章的主要内容如下:

一、input handler驱动实现流程说明

二、evdev驱动分析

 

一、input handler驱动实现流程说明

针对input handler驱动实现主要包括如下几个流程:

  1. 定义一个struct input_handler类型的全局变量,struct input_handler的定义如下;
    1. 需要实现event/events接口,这两个函数主要对input_device分发的event进行处理;
    2. connect、disconnect主要实现input device与input handler的关联,类似于设备驱动模型中的probe/remove。若该input_handler提供字符设备索引节点共应用程序访问,则可以在connect接口中进行字符设备的注册操作;而在disconnect接口中完成字符设备的注销操作(这和设备驱动模型中,我们在设备驱动的probe接口中实现字符设备是类似的,如我们可以在spi device driver的probe接口中实现字符设备的创建等)。
  2. filter则主要用于事件过滤(若input handler需要支持事件过滤,则可以实现该接口),该接口非必须;
  3. match则主要用于input子系统中更精细的匹配检测,在使用input_match_device完成input_device_id的匹配后,若input handler提供match接口,则调用match函数进行精细匹配检测,该接口非必须;
  4. 需要定义struct input_device_id用于说明该input handler可匹配哪些input device设备。
  5. 调用input_register_handler完成input handler的注册。

 

以上便是input handler的注册流程,其实input 子系统的这种操作,可以理解为简化版的设备驱动

模型,其input handler与input device的关联与解除关联,和设备驱动模型的操作类似。下面我们以一个实例说明input handler的实现流程。

 

 

evdev驱动分析

上面我们分析了input handler的注册流程,下面我们以evdev的驱动实现为例,介绍input handler

的实现流程。evdev是一个通用的input handler,其匹配所有的input device,并且向应用层提供对应的字符设备文件,其最多支持32个字符设备节点的注册。

下面我们按照上面的顺序进行分析说明

evdev的input handler定义

如下即为evdev对应的input handler的定义,其实现了如下内容:

  1. 实现event、events接口,即evdev_event、evdev_events;
  2. 实现connect、disconnect接口,即evdev_connect、evdev_disconnect接口,在evdev_connect接口中完成字符设备的注册,字符设备名称的前缀为event(/dev/input/eventX),同时调用input_register_handle完成input_handle的注册,从而完成input device与input handler的关联;
  3. evdev的id_tables中未设置,即会匹配任意input device,即任何注册到系统的input device都会和evdev handler进行关联。

 

Evdev 注册与注销

调用input_register_handler、input_unregister_handler完成evdev的注册与注销操作。

 

完成以上两步,即完成了input handler的实现。下面呢我们主要分析下evdev内部的实现以及对应字

符设备操作接口的实现。

evdev相关的数据结构

     evdev提供了两个数据结构,即struct evdev、struct evdev_client ,其中evdev是对evdev的抽象,其包含ev input handler、input dev、cdev,完成了evdev对应字符设备、input handler、input device的关联(借助input_handle)。而struct evdev_client 则对应一个文件描述符,可以理解为一个文件描述符对应的私有变量,当应用程序执行一次打开操作时,则为对应的字符设备文件描述符创建一个对应的struct evdev_client变量,下面是这两个数据结构的定义。

      针对每一个应用程序,若成功打开evdev对应的字符设备文件(即/dev/input/eventX),则会创建一个evdev_client类型的变量,该变量中包含存储事件的缓存(buffer),并作为文件描述符的私有变量存储。同时将该变量链接至evdev的client_list链表中,当input device有事件上报时,则将该事件广播到evdev->client_list链表上所有evdev_client的buffer中,这样每一个打开的文件描述符均可以读取到该事件(这种事件分发是广播的机制)。

 

 

如下是evdev、evdev_client数据结构间的关联图,此处包含了文件描述符、进程描述符、input device、input handle、input handler的关联图:

  1. 文件描述符通过私有变量指针private_data可完成与evedv_client的关联(前提是该文件描述符即为打开/dev/input/eventX文件的描述符,在其open接口中,即将file->private_data指向新创建的evdev_client,且会将evdev_client链接到evdev->client_list链表上);
  2. evdev通过input_handle,指向对应的input_handle,从而间接关联到input_handler、input_device;
  3. evedv包含cdev变量,而cdev变量注册到系统的cdev_map中,而cdev中包含文件操作的操作接口指针,针对evdev而言即是evdev_fops。

 

 

而在evdev handler的connect接口中则创建input_handle,并设置input_handle与

input_handler、input_dev的关联,同时完成input_handle的注册;然后创建cdev,并完成字符设备的注册。而在evdev_fops的open接口中,则创建evdev_client,并完成evdev_client与文件描述符、evdev的关联。

Evdev connect接口分析

 

Evdev connect 即为evdev handler的probe接口,当input device与input handler匹配成功后,即会调用connect接口进行input device、input handler的绑定操作。下面我们分析下evdev的connect接口的实现:

  1. 获取一个未被使用的evdev次设备号,主要是借助input_get_new_minor(获取失败则返回失败);
  2. 创建input_handle,并完成注册操作(完成与input_handler、input_dev的关联);
  3. 创建cdev类型的变量,并将该字符设备注册至系统中。

 

 

Evdev read分析

    本章不再对evdev的字符设备文件操作接口evdev_fops以及evdev handler的events/event做详细的说明,此处主要说明下evdev_fops->read接口读取流程,从而说明input device接收到event事件后,如何分发给input handler,input handler又是如何返回给应用程序的。

如下图所示,当应用程序打开文件/dev/input/eventX后,当执行read操作后,则通过sys_read接口,

最终调用evdev_read接口(关于文件系统以及字符设备文件相关的内容,请参考我之前写的文档)。

  1. evdev_read首先从evdev_client的buffer中读取事件,若buffer中没有事件,则sleep该读进程;
  2. Input device的input事件处理函数(一般是中断处理函数),接收到input事件后,则调用input_event将事件分发给该input_device关联的所有input_handler(必须执行input_sync才会执行真正的分发操作);
  3. 调用具体的input_handler的event/events接口,针对evdev handler而言,即为evdev_event、evdev_events接口,在evdev_events接口中,则会将event事件分发到所有的evdev_client的buffer中去,然后wakeup evdev的等待队列,唤醒evdev_read中sleep的进程或者唤醒poll函数中sleep的队列,从而上报poll事件(可配合使用epoll/select机制,支持I/O多路复用机制)

 

 

 

Input device与input handler的事件分发说明

在前面的分析中,我们可以发现,input_device当接收到event后,会将该事件分发给所有关联的input handler;而在evdev的event/events接口中,又会将event分发到所有关联文件描述符对应evdev_client的buffer中,这都可以理解为一种事件的广播机制(即类似于tcp/ip中的广播)。

那如果我只需要input device将event只发送到一个input handler,即实现event的单播发送呢?

这个input子系统确实实现了,在input_dev中提供了grab成员变量,完成input_dev与指定

input_handle的绑定,而在evdev的ioctl接口中,提供了EVIOCGRAB命令,其实现input_dev与指定

input_handle的绑定,同时也完成了evdev与指定evdev_client的绑定。至此以后,从input_dev接收的event,则只会发送给对应的文件描述符对应的evdev_client的buffer中,即完成事件的单播发送。

 

 

       本章主要接收input handler的实现流程,并以evdev为例进行说明。下一章我们接收input device的创建,并完成一个虚拟的input device驱动进行测试验证。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值