注册过程大致如下:
input_register_device -> input_attach_handler -> input_match_device -> connect
input_register_device对input_handler_list中的每一个handler尝试input_attach_handler
list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler);
input_attach_handler调用input_match_device,如果match,则调用该handler的connect
evdev默认match所有的input_dev设备,evdev_handler的connect为evdev_handler
它会分配一个evdev,初始化handle的dev和handler,将input_dev和input_handler绑定在一起,并将该handle分别挂到input_dev的h_list和input_handler的h_list上
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; ... // 寻找可用minor值 for (minor = 0; minor < EVDEV_MINORS; minor++) if (!evdev_table[minor]) break; // 分配evdev evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 初始化evdev INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = true; evdev->minor = minor; evdev->hw_ts_sec = -1; evdev->hw_ts_nsec = -1; // 初始化evdev->handle evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; // 初始化evdev->dev evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); // 将handle分别挂到input_dev的h_list和input_handler的h_list上 error = input_register_handle(&evdev->handle); // 保存evdev到evdev_table数组 error = evdev_install_chrdev(evdev); // 创建/dev/input/eventX文件 error = device_add(&evdev->dev); ... } int input_register_handle(struct input_handle *handle) { ... list_add_tail_rcu(&handle->d_node, &dev->h_list); list_add_tail_rcu(&handle->h_node, &handler->h_list); ... } // 保存evdev到evdev_table数组 static int evdev_install_chrdev(struct evdev *evdev) { evdev_table[evdev->minor] = evdev; return 0; }
最后在/dev/input/目录下创建出eventX设备
需要注意的是evdev和input_dev各自拥有自己的struct device dev成员,调用device_add创建eventX设备时使用的是evdev->dev
evdev->dev.parent = dev->dev
两个dev的class都是input_class
每个eventX都对应一对input_dev和input_handler(由input_handle绑定在一起),一个input_dev可以对应多个input_handler,一个input_handler也可以对应多个input_dev
注:与eventX对应的input_dev包含多个handler:sysrq、rfkill、kbd、evdev,evdev只是其中之一
<3>handler: c0817300, sysrq drivers/tty/sysrq.c
<3>handler: c084a7e0, rfkill net/rfkill/input.c
<3>handler: c081752c, kbd drivers/tty/vt/keyboard.c
<3>handler: c08215d4, evdev drivers/input/evdev.c
其他handler,如kbd在kbd_connect时并没有创建设备文件
直接写/dev/input/eventX,或者通过uinput创建完eventX设备后直接写/dev/uinput,
都能让eventX产生输入事件,进而Android通过EventHub从eventX中收到新的key或者motion事件
调用流程如下:
input_inject_event -> input_handle_event -> input_pass_event drivers/input/input.c void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
从dev的h_list中取出每一个handle,进而取出handle中的handler,调用handler->event
list_for_each_entry_rcu(handle, &dev->h_list, d_node) { if (!handle->open) continue; handler = handle->handler; if (!handler->filter) { if (filtered) break; handler->event(handle, type, code, value); } else if (handler->filter(handle, type, code, value)) filtered = true; }
对于eventX,handler为evdev_handler,event为evdev_event,
drivers/input/evdev.c list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_event(client, &event, time_mono, time_real);
对evdev上每一个evdev_client调用evdev_pass_event,注意,每次open eventX都会分配一个evdev_client挂在evdev的client_list上
evdev_pass_event将新event事件加入到evdev_client的buffer中,并唤醒buffer上所有读等待的任务
这样,每一个open eventX的进程都能从buffer中读出事件
从以上流程可以看到,对evdev创建的eventX设备write时,会导致所有的handler的event被调用,而不仅仅是evdev的evdev_event
每个eventX都对应一个evdev(input_device_registe调用evdev_connect产生),每次open eventX都会分配一个evdev_client挂在evdev的client_list上
event-injector-kmod是一个内核驱动demo,insmod后会注册一个/dev/input/eventX设备
假设我们创建出的设备为/dev/input/event2,在Android中可以通过以下脚本模拟调节音量键
$ cat event.sh sendevent /dev/input/event2 1 $1 1 sendevent /dev/input/event2 0 0 0 sendevent /dev/input/event2 1 $1 0 sendevent /dev/input/event2 0 0 0
执行adb shell后执行sh event.sh 114,就会让Android系统收到音量调节按键事件
也可以通过Android jni代码向eventX写入事件(需要root,先chmod 666 /dev/input/event2)
#include <linux/input.h> /* struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; */ void send_event(int fd, uint16_t type, uint16_t code, int32_t value) { debug("SendEvent call (%d,%d,%d,%d)", fd, type, code, value); if (fd <= fileno(stderr)) return; struct input_event event; int len; memset(&event, 0, sizeof(event)); gettimeofday(&event.time, NULL); // event (type, code, value) event.type = type; event.code = code; event.value = value; if (write(fd, &event, sizeof(event)) < 0) { debug("send_event error"); } // sync (0,0,0) event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; if (write(fd, &event, sizeof(event)) < 0) { debug("send_event error"); } } void inject() { int fd = open("/dev/input/event2", O_RDWR | O_NDELAY); send_event(fd, 1, 114, 1); // send volume-down key down event send_event(fd, 1, 114, 0); // send volume-down key up event close(fd); }
input子系统整体流程全面分析
Linux设备驱动剖析之Input(一)(二)(三)(四)
Linux输入子系统:输入设备编程指南 — input-programming.txt
Input Event Drivers (Virtual Mouse)
PDF格式:
linux input子系统整体流程分析
设备驱动程序实例_Button
设备驱动程序实例_VirtualMouse
From: http://www.pickbox.me/2014/09/04/linux-input%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%88%86%E6%9E%90%E4%B9%8Beventx%E8%AE%BE%E5%A4%87%E5%88%9B%E5%BB%BA%E5%92%8C%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92/