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

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_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上
每个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.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/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值