1、简介:
输入设备本质上还是字符设备,只是在此基础上套上了 input 框架。
input子系统分为设备驱动层、核心层和事件处理层。
1)、设备驱动层:负责和底层的硬件设备打交道,将底层硬件设备对用户输入的响应转换为标准的输入事件 ,发送给核心层。
事实上,设备驱动层只关心如何驱动硬并获得硬件数据(eg:按下的按键的数据),然后直接调用核心层提供的接口函数即可。
2)、核心层:负责对设备驱动层提供规范和接口,设备驱动层通过调用核心层提供的接口,然后核心层就会自动将数据发送给事件处理层。
事实上,核心层和具体的硬件无关,起到一个承上启下的作用。
3)、事件处理层:提供read\write等函数,实现和用户空间的数据交互。
2、实现流程
2.1说明:
input子系统的主设备号是固定的:13号。因此,我们不需要自己写字符设备部分的框架,无需注册字符设备。所以,我们在驱动程序中,只需要进行对应的设备的初始化,并注册设备即可。因为,以按键为例,不同平台做对应的io是不一样,这一部分,内核是无法帮我们写好的。也就是说,我们要做的就是在input子系统的框架下,按照input子系统框架完善驱动,是具体的输入设备能运行在input的框架下。
2.2 实现流程
要理清input的工作流程,我们要先了解4个结构体和2个链表:
结构体:
①、input_dev:代表一个具体的输入设备
②、input_handler:代表事件处理层的一个事件处理器。
③、input_handl:是input_dev和input_handler之间的纽带。我将其比作是设备-总线-驱动中的总线
④、input_envent:输入设备的数据会赞数存放在input_envent结构体中,最终通过handler中的event()或者events()函数将input_envent结构体中的数据发给用户。
链表:
①、input_dev_list:存放每个注册成功的设备。
②、input_handler_list:存放设备对应的事件处理方法。
设备驱动层的操作:
1)在input子系统中,规定用input_dev结构体来代表一个input设备(一种设备对应一个)。在这个结构体里面,有一个存放次设备id的成员变量和大量位图形式的成员变量:
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */
通过这些成员变量,我们可以指定任意一个输入设备的属性。
我们要做的就是,为设备申请一个input_dev结构体,并使用__set_bit函数对其成员进行初始化。
/* 初始化 input_dev,设置产生哪些事件 */
__set_bit(EV_KEY, keyinputdev.inputdev->evbit);
/*按键事件 */
__set_bit(KEY_0, keyinputdev.inputdev->keybit);
2)、注册该输入设备,实际上就是在向内核中,添加这个结构体。添加到链表input_dev_list。
调用匹配函数,遍历input_handler_list(代表事件处理层的一个事件处理器),进行设备和设备处理函数的匹配方法。
事件处理层的操作:
1)、事件处理层也执行类似的操作。向内核注册input_handler结构体,并将他放到数组input_table里面(暂时还未放到链表input_handler_list里面)。
2)、当应用程序打开设备节点的时候,会根据设备节点的设备号,在数组input_table里面找到对应的处理方法的函数。
3)、将找到的事件处理方法(对应的input_handler结构体)加入到链表input_handler_list里面。调用匹配函数,遍历input_dev_list(存放每个注册成功的设备),进行设备和设备处理函数的匹配方法。
注:
一旦设备驱动层的input_device和事件处理层的input_handler匹配成功,input子系统就会新建一个input_handl结构体,用来连接input_dev和input_handler。
具体的实现思想是:
将新建的input_handl分别挂靠在input_dev和input_handler的_list链表上去。这样一个input_dev就可以根据它的它的input_handle链表找到它的事件处理方法input_handler,同样input_handler也可以根据它的input_handle链表找到它能处理的设备input_dev(我将input_handle比作是设备-总线-驱动中的总线)。
3、事件的上报
当硬件的状态发生改变时,一般会引起一个中断,这个中断通过input_event函数将数据向核心层上报,这将导致事件处理层input_bander中的event()函数被调用。那么,用户就可以通过read/write进行设备的读写。
4、总结:
总的来说,对于input子系统,需要我们做的就是:
1)、在设备树中,配置好输入设备的节点信息。
2)、在驱动程序中,通过of函数,从设备树中,获取到节点的信息。初始化并注册input_dev结构体。
3)、使用input核心层提供的api函数上报从硬件设备获得到的数据。
剩下的操作,都是由input子系统来完成。
4)、对于用户端,通过访问设备节点,实现对数据的处理。