Linux Input子系统分析之eventX设备创建和事件传递

来源:互联网 发布:物格而后知至,知至而后 编辑:程序博客网 时间:2024/04/29 20:59

Linux Input子系统整体架构

Linux Input子系统的架构图

The input subsystem

 

注册eventX设备

注册过程大致如下:
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和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注入和接收输入事件

直接写/dev/input/eventX,或者通过uinput创建完eventX设备后直接写/dev/uinput,
都能让eventX产生输入事件,进而Android通过EventHub从eventX中收到新的key或者motion事件

调用流程如下:

input_inject_event -> input_handle_event -> input_pass_eventdrivers/input/input.cvoid 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.clist_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上
每个eventX都对应一个evdev(input_device_registe调用evdev_connect产生),每次open eventX都会分配一个evdev_client挂在evdev的client_list上

Input Kernel Driver Example

event-injector-kmod是一个内核驱动demo,insmod后会注册一个/dev/input/eventX设备

假设我们创建出的设备为/dev/input/event2,在Android中可以通过以下脚本模拟调节音量键

$ cat event.shsendevent /dev/input/event2 1 $1 1sendevent /dev/input/event2 0 0 0sendevent /dev/input/event2 1 $1 0sendevent /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/


0 0