输入子系统(一)

1、框架
之前写的驱动程序都是使用自己的框架,也只有我们自己知道在测试时该打开什么设备,那么,内核中提供了一套框架,将这些输入设备都进行了统一和规范化,这就是今天要来介绍的输入子系统
打开内核source insight工程,在drivers目录下找到input.c,这是输入子系统的最开始的文件,从这里进行分析
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
从上面这张图可以看到,input.c在它的入口函数里先注册了一个名为inout的类,之后注册了字符设备,字符设备的主设备号INPUT_MAJOR是13,设备名也是input,之前我们写过的字符设备中,要注册类,然后需要在类下去注册设备,但是这里只是注册了类,并没有发现设备,那么它肯定是在别处注册的,后面碰到了再说,然后去查看字符设备的file_ops结构体,发现这个结构和我们之前写的有点不太一样,这个file_operations结构体当中只有一个open函数,没有其他的函数了,是有点奇怪,点开open函数,看看里面都做了什么
在这里插入图片描述
从open函数的代码可以看出,它做了这么几件事

1、从 "input_table" 数组里根据打开的文件次设备号取出了一个 "input_handler" 结构
2、定义了一个新的file_operations结构体,从第一步中的返回的input_handler结构体中获取新的
   file_operations结构       "new_fops = fops_get(handler->fops)"
3、把这个新的fops结构体赋给打开的文件f_op指针,然后使用新的fops结构体中的open函数去打开设备
	"file->f_op = new_fops;  
	 err = new_fops->open(inode, file);"

对input.c的open函数做了简单分析之后,有几个问题就产生了
1、一开始从Input_table里获取数组元素,那么这个数组里是些什么内容?这个数组是怎么来的?
2、返回的元素input_handler里面又是些什么内容?
好,我们接着往下分析,寻找这些问题的答案,找一个输入子系统的例子来进行分析,evdev.c
在这里插入图片描述
在这里插入图片描述
evdev.c的入口函数里通过input_register_handler来将上面的input_handler结构注册进内核当中,在input_handler结构体中已经定义好了file_fops操作函数集合,所以,前面可以直接使用input_table数组中的元素的fops,来看看这个注册函数都做了什么事情

在这里插入图片描述
看到这个函数其实就已经很明了了,程序使用input_register_handler函数注册一个input_handler结构进内核当中,这个函数首先根据input_handler结构体中的次设备号为input_table数组下标,然后把input_handler结构体放入input_table当中,然后把input_handler链入handler的链表当中,最后是一个for循环,通过名字可以看出,它是去遍历input_device的链表,然后使用input_attach_handler函数进行两者的匹配,看来,不止是有input_handler结构体,还有一个input_device结构体,我们再来找一找input_device在哪里,它又是怎么回事?
在这里插入图片描述
在gpio_keys.c中找到了注册input_device的地方,进去看一下它都做了些什么?
在这里插入图片描述
在进行了一系列的处理之后,可以看出注册input_device其实和注册input_handler是一样的,在这里它也是把input_device结构放入一个链表,然后去遍历input_handler链表进行匹配,看到这里我们大致可以明白输入子系统的一个架构

1、"input_handler"表示软件层面上的结构,使用"input_register_handler"注册一个input_handler结构进内核,注册函数首先会根据
	input_handler结构体中的次设备号把这个input_handler结构放入input_table中的对应的位置
	然后把input_handler链入一个链表,最后遍历input_decice链表,使用"input_attach_handler"进行匹配
2、"input_device"对应一个物理设备,使用"input_register_device"注册一个input_device结构进内核,注册函数也会把这个
 	input_device结构体放入一个链表,然后去遍历input_handler链表,调用"input_attach_handler"函数进行匹配
 那么,比较重要的就是这个匹配函数了,我们接下来看看这个匹配函数里都做了些什么事情

在这里插入图片描述
从input_attach_handler函数的代码中可以看出,首先调用input_match_device函数根据handler中的id_table和device进行匹配,若是匹配成功则调用handler里的connect函数,我们再返回去看一下evdev这个结构体中的connect函数都做了什么?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在evdev的connect函数里首先定义了一个evdev的结构,进行赋值之后就把evdev放入了evdev_table数组中去了,这个结构体中有一个比较重要的成员input_handle(注意,不是handler),然后把匹配成功的input_handler和inut_device都放入了这个结构体中,这个结构体我们等会再说,然后,在connect函数里使用class_device_create注册设备,我们最开始说没有注册设备,看到了没,就是在这里注册了设备,然后使用input_register_handle函数把evdev结构体中的input_hande注册进内核,接下来看一下这个函数的工作
在这里插入图片描述
可以看到,在input_register_handle函数里把input_handle结构体中的d_node指针指向了input_device结构体中的h_list,把Input_handle结构体中的h_node指向了inout_handler结构体中的h_list,使三者之间进行关联
2、总结
ok.说了这么多,接下来总结一下前面说的内容
在这里插入图片描述

1、在输入子系统input.c核心层里的入口函数里注册了字符设备,输入子系统的字符设备主设备号是13,这里只有注册类并没有注册类下的设备
 在应用程序打开输入子系统时会调用到刚才注册的字符设备中的fops中的open函数,这个fops结构体中只有open函数
 在open函数里,根据打开的字符设备次设备号,从Input_table数组里取出一项,调用它的fops->open函数
2、那么,这个input_table数组中的数组项是由谁来构造的呢,在以evdev.c为例我们进行了分析,在这个文件的入口函数里注册了一个
 input_handler结构,把它放入了input_table中的对应项,同时链入链表,对input_device链表进行遍历查找,如果根据
 input_handler的id_table能够成功匹配一个input_device,就会调用evdev结构中的connect函数,会生成一个input_handle结构,
 把三者进行关联,同样的,在每注册一个input_device的同时,也会进行上面的操作,所以,不管先注册handler还是先注册device,
 它都会去对应的链表中去匹配  

在这里插入图片描述

1、在匹配的时候,使用input_handler的id_table进行匹配,若是成功匹配到了一个input_device就会生成一个input_handle结构
2、input_table结构中有几个比较重要的成员
		handler:input_handler结构的指针
		device: input_device结构的指针
		h_list: input_handler的链表
		d_list: input_device的链表
3、匹配成功之后,传入connect函数中就有handler结构和device结构,然后在该函数里把这两个结构赋给input_handle中的对应指针
4、然后把input_handler结构体中的h_list指向input_handle中的h_list,把input_device结构中的h_list指向了input_handle结构中的d_list指针
这样一来,重要从Input_table中取出一个input_handler结构,就可以从Input_handler中的h_list找到input_handle,然后从input_handle中的
d_list找到input_device,从而实现两者的关联。

3、读数据
这个时候,如果应用程序来读数据的话,先从Input_table数组获取一个有效的数组项之后,就可以调用input_handler结构体中的read函数了,还是以evdev为例子进行简单分析一下

在这里插入图片描述
从程序里可以简单看出来,如果应用程序是非阻塞的读取,并且此时环形缓冲区中没有数据的话就立即返回,如果是以阻塞式的方式来读取数据的话,调用wait_event_interruptible函数让进程就行休眠,在这里休眠,肯定是在别的地方进行唤醒,回忆一下之前写的中断程序,应用程序再read函数里进行休眠,在按键按下发生中断之后,在中断服务程序里唤醒休眠的进程读取数据,那么,同样的这里休眠的进程肯定是在真实的物理设备发生中断之后唤醒的休眠进程
在这里插入图片描述
在input_handler结构中有一个event函数,驱动程序就是在这个函数里唤醒休眠的进程的,那么。这个函数是如何被调用的呢,在gpio_keys中注册了中断,对应我们的按键的话,当按下按键之后,发生中断就代表有数据可以读取了,
在这里插入图片描述
在中断函数里调用硬件的input_device中的event函数,进入这个函数中去看看它又做了什么
在这里插入图片描述
在这个函数的最后,可以看到是一个循环,循环内核中的Input_handle链表,如果input_handle已经被打开了就调用该handle中的input_handler中的event函数,就回到了上面的分析过程中,在input_handler的event函数中唤醒休眠的进程,好了,到这里对内核中的输入子系统已经有了一个简单的认识了,在下一节中来编写程序。

发布了33 篇原创文章 · 获赞 2 · 访问量 1016

猜你喜欢

转载自blog.csdn.net/weixin_41791581/article/details/103537228
今日推荐