Linux input 驱动
driver/input/
核心是input.c
其架构类似于hid驱动,向下对接一个设备,向上可以有多个出口。
下层设备驱动(例如hidinput)通过 input_register_device / input_unregister_device 注册 / 注销设备。
input_register_device
|-device_add // 不会引起某probe,仅生成sysfs
|-list_add_tail(&dev->node, &input_dev_list)
|-list_for_each_entry(handler, &input_handler_list, node)
|-input_attach_handler
这里有两个关键链表:
input_dev_list 下层设备的链表。
input_handler_list handler的链表,例如evdev。
handler就是向上层的出口。当一个设备新增,遍历已有的handler,并尝试与之“attach”,通过调用input_attach_handler函数。
input_attach_handler
|-input_match_device
|-handler->connect
遍历所有已注册的handler,并调用其connect函数。
handler是上层各“出口”调用 input_register_handle注册的。
下面以evdev为例:
evdev.c
这是一个小模块,只有1千多行。
其module_init / module_exit中注册/注销handler,通过input_register_handler / input_unregister_handler函数。
input_register_handler 函数是input.c中实现的。它注册一个input_handler结构体。
input_register_handler
|-list_add_tail(&handler->node, &input_handler_list)
|-list_for_each_entry(dev, &input_dev_list, node)
|-input_attach_handler
注意看那两个全局链表input_handler_list和input_dev_list,是不是似曾相识!
前面已经介绍过input_attach_handler,现在回到handler->connect。
evdev.c的connect函数是evdev_connect。
evdev_connect
|-input_get_new_minor
|-input_register_handle
|-cdev_add // 注册字符设备
|-device_add // 生成字符设备
注意input_register_handle 与 input_register_handlers的区别,前者将注册的是input_handle结构体挂在设备的链表上。后者注册的是input_handler结构体,挂在input_handler_list链表上。区别在哪里?evdev_hadler结构体只有一个实例,无论有多少个设备,它代表了evdev这个驱动。而input_handle是被evdev结构体包含的,代表的是一个设备在evdev上的出口,是每个设备都各有一个的。
evdev为每个底层设备添加一个字符设备。字符设备的fops是evdev_fops。
evdev_open
|-evdev_attach_client
|-evdev_open_device
|-input_open_device
|-dev->open
向各client推送数据:
evdev_events
|-evdev->client_list
|-evdev_pass_values
其它:
/proc/bus/input/device
/proc/bus/input/handlers