
上一篇文章写了《input子系统的的概念》。今天这篇文章主要分析input子系统的代码,input子系统作为一个字符设备存在kernel中,我们从input这个模块的入口函数开始讲起。
input子系统的核心代码:drivers\input\input.c。
《一》
input模块的入口函数:如图1-1,入口函数通过调用register_chrdev()注册一个字符设备。其中
① INPUT_MAJOR:主设备号:13(如图1-2,定义在include\linux\major.h下)。
② &input_fops:file_operations结构体(如图1-3)。会发现结构体只有open函数被注册(input_open_file()),跟我之前文章写的字符设备不一样,没有了read,write函数。怎么回事?

图1-1

图1-2

图1-3
《二》
file_operations结构体只有open函数,那么就看看open函数究竟做了什么?input_open_file()函数的实体如图2-1。其中
① iminor(inode):函数调用了函数MINOR(inode->i_rdev)(其中iminor函数原型如图2-2,通过获取子设备号左移5位后,获取挂载的input设备驱动的数组号,从而获得input设备驱动的input_handler。input_handler结构体,如图2-3所示,其中需要关乎结构体中的成员:event,connect,和file_operations。
② 如果handler存在,说明挂载了这个设备驱动。然后获取handler的成员fops赋值给new_fops(其中,在第①点中提到要关乎input_handler结构体成员file_operations)。
③ 将新的new_fops赋值给file->f_op,此时的input子系统的file_operations为新挂载的input设备的file_operations。
④ 调用新挂载的input设备的open函数。
⑤如果打开失败,input子系统的file_operations将使用回旧的file_operations,所以第③点,做了保存机制,保存了旧的file_operations。

图2-1

图2-2

图2-3
《三》
input_table[]数组从以上的代码中都没有赋值,那么他在哪里赋值的呢?在drivers\input\input.c中,input_table[]是静态全局变量,所以只需要在input.c中查找,可以发现在input_register_handler()函数中可以看到input_table[]有赋值,如图3-1。
① 判断input_handler驱动处理程序是否存在,不存在则将handler赋值给input_table[]中。
② 并将其添加到input_handler_list链表中。
③ 对每一个的input_dev,调用input_match_handler(),判断input_handler是否有支持input_dev。

图3-1
通过查找可以发现,如图3-2所示,有键盘设备,鼠标设备等通过input_register_handler()注册了设备处理函数。

图3-2
《四》
这里我们以evdev.c(事件设备)来讲解如何注册handler??
在evdev.c中入口函数中(图4-1)通过input_register_handler()函数,注册了一个结构体evdev_handler(图4-2).
① fops:注册了file_operations结构体(图4-3),似曾相识,跟我们之前的注册的字符设备的结构很像,文件的操作。
② minor:子设备号(evdev:64),用于上面说到input_table[]数组中。
③ id_table:用来和input_dev匹配(图4-4),从注释上可以获知,支持所有的输入设备。
④ event:从字面意思理解就是事件处理函数,下面将进一步讲解这个函数。
⑤connect:在通过input_register_handler()注册handler时,会调用input_match_handler()进行匹配(图3-1),如果匹配成功会调用,handler->connect(handler,dev, id)。

图4-1

图4-2

图4-3

图4-4
《五》
在上一篇文章中,有说到核心层对下提供设备驱动的编程接口,对上提供事件层的编程接口。在《三》和《四》中,我们写到事件层接口的实现,那么接下在讲解一下设备驱动的编程接口。

图5-1
图 5-1是上一篇文章写到的内容,我们将红框的文字转为图5-2所示。这样我们进一步了解input_dev和input_handler的关系。

图5-2
在drivers\input\input.c中,我们看到提供给input_dev的接口为input_register_device(),函数实体(图5-3)。通过input_register_device()函数注册一个驱动设备,然后加到input_handler_list链表中,对每一个的input_handler,调用input_match_device (),判断input_dev是否有支持input_handler。

图5-3
在图3-1中,注册handler的时候,对每一个的input_dev,调用input_match_device(),判断input_handler是否有支持input_dev。在图5-3,对每一个的input_handler,调用input_match_device (),判断input_dev是否有支持input_handler。
显然,你会发现跟平台总线很像,字符设备通过platform_match()函数设备和驱动进行匹配。而input子系统通过调用input_match_device ()函数将input_dev和input_handler进行匹配。
在平台总线上不管是注册设备先还是注册驱动,都可以。其实input子系统也一样,驱动跟handle的注册也是没有优先顺序的。

图5-4
《六》
Input子系统中注册input_dev和注册input_handler都调用了input_match_device()函数。那么我们来看看这个函数实现了什么?图6-1为input_match_device ()函数的实体。
在《四》中,我们以evdev.c(事件设备)。在图4-4中,我们可以看到input_device_id只注册了driver_info,所以我们前面四个if可以不解读。直接看看MATCH(),MATCH是一个宏,结构如图6-2,我们以图6-1中红框的evbit为例,可以将图6-2改写为图6-3,如果dev支持某一种事件类型,则会将dev->bit[0]中置1。
在图3-1中的input_match_device(handler->id_table,dev),传入的参数handler->id_table,我们看看evdev.c(事件设备)的id_table赋值了什么?可以看到图4-2和图4-4。handler->id_table->evbit[0]等成员全部都为0,所以0&任何数都为0,0 != 0不成立,所以不会跳出循环,返回id,匹配成功。

图6-1

图6-2

图6-3
我们看图3-1和图5-3,当匹配成功,则会调用handler的connect函数。
《七》
图7-1所示为evdev.c(事件设备)的connect()函数实体。dev和handler通过一个中间件hande连接起来。通过devfs_mk_cdev()函数创建设备文件。然后创建一个简单类。

图7-1
《八》
最后还有一个关键的函数接口input_event(),它用来接收应用层产生的事件。input_event()函数的实体如图8-1。红框部分可以看出,驱动input_dev和处理input_handler已经通过input_handler 的.connect函数建立起了连接,那么就调用input_handler的.event事件函数。

图8-1
下一篇:Linux input子系统的驱动开发