Input子系统驱动---学习记录
目录
应用层如何使用Input子系统
驱动框架流程
驱动应该要实现的功能
evdev.c分析
小结:
gpio_keys.c分析
应用层如何使用Input子系统
Input子系统是一个典型的字符设备驱动程序,所以会创建/dev/input/eventX(也可能是/dev/eventX)设备节点,应用层通过open/read/write/ioctl去访问硬件设备。
驱动框架流程
硬件驱动建立在platform总线上,设备树通过platform_device与硬件驱动的platform_driver匹配,最后调用platform_driver下的.probe。
然后在.probe创建input_dev结构体,然后调用input_register_device去注册设备。
另一边/drivers/input/evdev.c通过module_init在初始化函数内创建input_handler结构体然后调用input_register_handler。(这块代码已经实现了,我们并不需要自己实现)
如何匹配是由/drivers/input/input.c里的两个全局变量input_handler_list、input_dev_list实现的,如下图:
input_register_handler会把input_handler通过list_add_tail添加进input_handler_list链表,然后通过list_for_each_entry在input_dev_list链表做匹配。
input_register_device会把input_dev通过list_add_tail添加进input_dev_list链表,然后通过list_for_each_entry在input_handler_list链表里做匹配。
如果匹配成功,就会调用evdev.c下input_handler结构体的.connect函数。如下图:
我们会发现input_register_handler使用的input_handler会和input_register_device使用的input_dev形成类似platform_device与platform_driver的映射关系。
所以input子系统驱动的整个流程用图表示如下:
驱动应该要实现的功能
由于/drivers/input/evdev.c已经帮我们实现了input_handler,所以我们只需要实现input_dev部分。
而这部分我们大概猜测:
1.创建input_dev结构体
2.设置设备树硬件信息
3.设置硬件中断(通过硬件中断去获取数据,所以在input的设备树节点中一定是有interrupts或者gpios用于设置中断)
4.调用input_register_device.
下面通过gpio_keys.c和evdev.c为例说下驱动流程.
evdev.c分析
input_register_handler设置evdev_handler,input_register_handler内部会首先把evdev_handler加入到全局链表input_handler_list里,如下图:
然后调用
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);会去与input_dev_list这个全局链表查找匹配,而input_register_device设置的input_dev就是放进input_dev_list,如果匹配成功,就会调用input_attach_handler,从而调用evdev_handler里的.connect。
在evdev_connect中就会在/dev/input下创建eventX节点,应用层open/read/write/ioctl就会调用对应的file_operations函数,如下图:
当硬件驱动调用input_register_device里注册的中断函数触发中断就会调用input_event里的input_handle_event去传输数据,这样当应用层调用read时,就会进入等待,当硬件触发中断后,才会开始读取数据。
同样的应用层调用write时,调用input_handle_event写入数据。
小结:
evdev.c这个作用就是用来与所有的input设备进行匹配,并且实现了一套通用的file_operations函数,而我们只需要在建立的这套框架上实现input_dev这块的驱动即可。
gpio_keys.c分析
设置evdev.c里file_operations的open/close,这样当应用层调用open/close最终就会调用gpio_keys_open和gpio_keys_close,如下图:
通过gpio_keys_setup_key去设置中断,如果在设备树里用的是gpios表示中断,那么就用gpio_keys_gpio_isr设置,如果用的是interrupts表示中断,就用gpio_keys_irq_isr设置。如下图
在input_dev结构体中,evbit/keybit这两个参数是很重要的,如下图
所以,在gpio_keys_setup_key > input_set_capability里处理,而设备树中通过linux,code来设置,如下图
最后通过input_register_device注册input_dev。
当用户层调用read时,会进入等待,一直到按下按键,就会触发中断 gpio_keys_gpio_isr > gpio_keys_gpio_work_func > gpio_keys_gpio_report_event > input_event > input_handle_event,最后就会唤醒read里的等待,最终读取数据。