首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >linux input子系统(2)《Rice linux 学习开发》

linux input子系统(2)《Rice linux 学习开发》

作者头像
Rice加饭
发布于 2022-05-09 21:15:41
发布于 2022-05-09 21:15:41
2.2K0
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式

上一篇文章写了《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子系统的驱动开发

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档