今天又把input输入子系统这块重新看了一遍,下面我就对所理解的东西做一个总结,以便以后自己复习,也希望能给在这块还迷茫的同学给点帮助,下面是我针对所理解的input子系统所画的框架结构图:
如图所示,input子系统是把驱动分为了好几个部分来实现的,其中核心层和纯软件的部分是内核帮助我们做好的,那我们只需要做跟硬件相关的部分就可以了。
首先,我们来分析input子系统的核心层input.c这个文件,那我们从它的入口函数开始:
如图,核心层的init函数中只是注册了一个字符驱动,那我们接下来看看input_fops这个结构都实现了什么,我们发现这个结构里面只是实现了open这个函数,
那我们就有必要看看这个open函数里面都做了什么,
首先,在open函数中定义了一个input_handler结构体,如我最上面所画的input子系统框架图所示,在open函数中我们看到下面这句话
那这个input_table又是什么呢?
我们在input_register_handler()这个函数中看到了对input_table做了初始化,把这个数组的每项根据这个函数传入的hanlder参数做了初始化,
我们在evdev.c这个文件的入口函数中可以看到,只是调用了input_register_handler()这个函数来注册,
再来看看evdev_handler这个结构体:
这样的话我们就可以看到这个结构体正如input子系统框架结构图中所画的纯软件部分,它里面实现了event等等一系列函数,还有fops,那我们就可以理解了,上面的inut_table这个数组中正式存储着上面这个input_handler,
在open函数中,我们正式把上面的input_handler结构体中的fops赋给了file->f_op。
我们再来看看硬件相关的部分,我们用gpio_keys.c这个做为例子来说明:
在gpio_keys_probe()探测函数中,我们通过来初始化了一个input_dev结构体,接下来我们对这个结构体做了一系列的初始化:
最后我们调用input_register_device(input)向系统注册了这个input设备,下面我们就来分析一下这个注册函数是怎么实现的:
我们看到,函数里面把这个input_dev加入了一个链表里面,接着遍历input_handler_list这个链表,调用input_attach_handler来想handler建立连接,
我们再来看看input_match_device这个函数:
正是通过input_handler这个结构体中的id_table来比较进行匹配的,
这样我们就可以理解到,在注册input_dev或者input_handler时,会比较会比较input_dev和input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则会调用input_handler的connect函数来建立连接。
这里的connect就是input_handler的connect所指向的evdev_connect函数:里面定义了一个evdev结构体,我们来看看这个结构体
这个结构体里面定义了一个input_handle结构体,在connect函数中对这个结构体做了一系列的初始化:
接着调用来注册这个input_handle,这个就是input子系统框架图中连接input_handler和input_dev的核心,
在input_register_handle函数中有下面几句:
把这个input_handle添加到input_handler->h_list链表,和input_handle->dev->h_list链表中,至此input_dev和input_handler就建立起了连接。
那接下来我们看看应用层调用一系列函数后,驱动层是怎么工作的,假如应用层调用read函数,要读取一些数据,之前我们提到,file->f_op已经指向了input_handler的fops,所以read ——》file->f_op->evdev_read,在evdev_read函数中我们看到:
如上面的带面所示,如果当前没有数据可以返回,就会休眠,休眠被唤醒的条件就是当有数据可读时,那接下来,我们分析这个休眠是怎么被唤醒的:
在gpio_keys.c这个文件中我们看到了当有按键按下时就会在中断函数中进行处理,
我们看到input_event()这个函数,这个函数是用来想上层上报事件的,我们来看看它内部是如何实现的:
我们会看到其中对dev->h_list这个链表进行了遍历,
list_for_each_entry(handle,&dev->h_list,d_node);
if(handle->open)
handle->handler->event(handle,type,code,value);
我们看到是调用到了input_handler中的event()函数,我们再来看看这个函数实现了什么:
在event函数中我们看到了wake_up_interruptible(&evdev->wait);在这里唤醒了之前休眠的read,这样的话就可以读取按键按下的数据。
到这里,我们的input子系统根据几个例子就分析的差不多了,input子系统分为了三个部分input.c核心层,input_handler和input_dev,我们所要做的就是input_dev,其它两个部分是内核帮我们实现好的,那我们需要做什么呢:
1:分配一个input_dev结构体:
struct input_dev *input;
input = input_allocate_device();
2:对这个input_dev结构体的参数进行设置
3:注册input_dev
input_register_device(&input);
4:硬件相关的部分的代码;比如在中断服务程序中上报事件;
到这里,我们的input子系统就彻底分析完了。