Linux Input子系统整体架构

注册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包含多个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 3>3>3>3>其他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上
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