
硬件中断/轮询│▼设备驱动(Producer)—— drivers/input/*- 创建并注册:struct input_dev- 上报事件:input_report_*() + input_sync()│▼Input Core(撮合/转发)—— drivers/input/input.c- 维护设备/handler 列表- 匹配:input_match_device()- 绑定:handler->connect()- 转发:input_event()│▼事件处理层 Handler(Consumer)—— 以 evdev 为例- /dev/input/eventX(字符设备接口)- evdev_event()/evdev_read()/ioctl│▼用户空间:Xorg/Wayland(libinput) 或 直接 read()/libevdev
/dev/input/eventX)。先熟悉核心层最核心的几个数据结构,理解它们才能看懂流程:
// 输入设备核心结构体(定义在 include/linux/input.h)struct input_dev {const char *name; // 设备名称(如 "usb-mouse")const char *phys; // 物理路径(如 "usb-0000:00:14.0-1/input0")struct input_id id; // 设备ID(总线类型、厂商ID、产品ID等)unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // 支持的事件类型(EV_KEY/EV_REL/EV_ABS等)unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // 支持的按键unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // 支持的相对坐标(鼠标)unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // 支持的绝对坐标(触摸屏)struct list_head node; // 挂入核心层设备链表的节点struct input_handler *grab; // 独占该设备的handlerstruct mutex mutex; // 保护设备的互斥锁// 其他成员(事件队列、定时器、电源管理等)};// 输入事件处理者结构体(Handler)struct input_handler {void (*event)(struct input_handle *handle, unsignedint type, unsignedint code, int value); // 事件处理函数int (*connect)(struct input_handler *handler, struct input_dev *dev, conststruct input_device_id *id); // 匹配设备的函数void (*disconnect)(struct input_handle *handle); // 断开匹配的函数struct list_head node; // 挂入核心层handler链表的节点const struct input_device_id *id_table; // 支持的设备ID表struct list_head h_list; // 关联的input_handle链表};// 设备与Handler的连接结构体(核心层的“桥梁”)struct input_handle {struct input_dev *dev; // 关联的输入设备struct input_handler *handler; // 关联的Handlerstruct list_head d_node; // 挂入input_dev的h_liststruct list_head h_node; // 挂入input_handler的h_list};
Linux 输入子系统核心层的代码主要集中在 drivers/input/input.c 文件中,核心流程分为设备注册流程、事件上报流程、设备注销流程三大块。就是围绕这几个函数的解读的。
三、代表函数分类
// 注册输入设备intinput_register_device(struct input_dev *dev);// 注销输入设备voidinput_unregister_device(struct input_dev *dev);
// 注册事件处理器intinput_register_handler(struct input_handler *handler);// 注销事件处理器voidinput_unregister_handler(struct input_handler *handler);
把 handler 放入全局 handler 链表;
反向遍历现有设备,逐个尝试连接(同上)。
// 核心层事件上报函数voidinput_event(struct input_dev *dev, unsignedint type, unsignedint code, int value);
对 dev 绑定的每个 handle:调用 handle->handler->event(handle, ...)。
设备驱动层示例代码(伪代码)
struct input_dev *input_dev;input_dev = input_allocate_device(); // 分配input_dev// 设置设备属性input_dev->name = "my_input_device";input_dev->id.bustype = BUS_I2C;// 设置支持的事件类型set_bit(EV_KEY, input_dev->evbit);set_bit(KEY_A, input_dev->keybit);// 注册设备input_register_device(input_dev);
注册设备:input_register_device(struct input_dev *dev)
大概流程如下
input_register_device()list_for_each_entry(handler, &input_handler_list, node)input_attach_handler()input_match_device()handler->connect()
intinput_register_device(struct input_dev *dev){static atomic_t input_no = ATOMIC_INIT(0);struct input_devres *devres = NULL;struct input_handler *handler;unsigned int packet_size;const char *path;int error;if (dev->devres_managed) {devres = devres_alloc(devm_input_device_unregister,sizeof(struct input_devres), GFP_KERNEL);if (!devres)return -ENOMEM;devres->input = dev;}/* Every input device generates EV_SYN/SYN_REPORT events. */__set_bit(EV_SYN, dev->evbit);/* 将KEY_RESERVED位清零 */__clear_bit(KEY_RESERVED, dev->keybit);/* 确保dev->evbit中没有设置的位都被清零 */input_cleanse_bitmasks(dev);packet_size = input_estimate_events_per_packet(dev);......//初始化定时器init_timer(&dev->timer);//REP_DELAY和REP_PERIOD是关于重复按键的,如果没有设置就赋值为默认值if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {dev->timer.data = (long) dev;dev->timer.function = input_repeat_key;dev->rep[REP_DELAY] = 250;dev->rep[REP_PERIOD] = 33;}if (!dev->getkeycode)//获取键的扫描码dev->getkeycode = input_default_getkeycode;if (!dev->setkeycode)//设置键的扫描码dev->setkeycode = input_default_setkeycode;dev_set_name(&dev->dev, "input%ld",//设置内嵌dev的名字(unsigned long) atomic_inc_return(&input_no) - 1);error = device_add(&dev->dev);//将input_dev内嵌的dev注册到sysfsif (error)goto err_free_vals;path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);pr_info("%s as %s\n",dev->name ? dev->name : "Unspecified device",path ? path : "N/A");kfree(path);error = mutex_lock_interruptible(&input_mutex);//上锁if (error)goto err_device_del;list_add_tail(&dev->node, &input_dev_list);//将输入设备挂载到input_dev_list链表上list_for_each_entry(handler, &input_handler_list, node)input_attach_handler(dev, handler);//遍历事件处理驱动,如果找到匹配的事件处理驱动就将该设备依附上去,//生成一个handleinput_wakeup_procfs_readers();mutex_unlock(&input_mutex);..........}EXPORT_SYMBOL(input_register_device);
其中input_attach_handler(dev, handler);的函数是调用如下:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler){const struct input_device_id *id;int error;id = input_match_device(handler, dev);if (!id)return -ENODEV;error = handler->connect(handler, dev, id);// 匹配成功,调用connectif (error && error != -ENODEV)pr_err("failed to attach handler %s to device %s, error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);return error;}
其中handler->connect(handler, dev, id);如果匹配上就调用handler->connect函数指针,这是当事件驱动提供的连接函数,当handler和dev匹配上时被调用,会产生一个handle;
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);例如evdev_handler的connect函数,生成设备节点:
static struct input_handler evdev_handler = {// ....connect = evdev_connect,// ...};staticintevdev_connect(struct input_handler *handler, struct input_dev *dev, conststruct input_device_id *id){struct evdev *evdev;int minor;int dev_no;int error;minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);...............evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);evdev->dev = dev;evdev->handle.dev = dev;evdev->handle.handler = handler;evdev->handle.private = evdev;// 创建设备节点evdev->devno = input_alloc_device_id();dev_set_name(&evdev->dev->dev, "event%d", evdev->devno);cdev_init(&evdev->cdev, &evdev_fops);cdev_add(&evdev->cdev, MKDEV(INPUT_MAJOR, evdev->devno), 1);input_register_handle(&evdev->handle);return 0;}cdev_add(&evdev->cdev, MKDEV(INPUT_MAJOR, evdev->devno), 1);加入到了字符设备中。
核心层作用: 把 dev 放入全局设备链表;
遍历所有 input_handler,用 input_match_device() 按 id_table + 能力位图 匹配;
成功则调用 handler->connect(handler, dev, id),生成 input_handle 绑定二者。
Input Core(撮合/转发)—— drivers/input/input.c- 维护设备/handler 列表- 匹配:input_match_device()- 绑定:handler->connect()- 转发:input_event()

另外
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
|---id = input_match_device(handler, dev);调用如下

整个匹配过程事件驱动占主导,判断设备驱动是否符合事件驱动的要求,如果符合就返回匹配上的struct input_device_id结构体指针;
handler->id_table指针可能指向好几个struct input_device_id结构体,只要有一个匹配上就算匹配成功;
匹配中最重要的是事件类型bit位的检查,事情驱动中没有设置的bit位代表事件驱动不关心这个bit位,因为每个事件驱动对应不同类型的输入设备,所以支持的输入事件是有区别的;
staticinlinevoidinput_sync(struct input_dev *dev){input_event(dev, EV_SYN, SYN_REPORT, 0);}
而input_event 函数原型是
voidinput_event(struct input_dev *dev,unsigned int type, unsigned int code, int value){unsigned long flags;//判断设备驱动是否支持该输入事件if (is_event_supported(type, dev->evbit, EV_MAX)) {spin_lock_irqsave(&dev->event_lock, flags);input_handle_event(dev, type, code, value);spin_unlock_irqrestore(&dev->event_lock, flags);}}EXPORT_SYMBOL(input_event);
static void input_handle_event()函数
staticvoidinput_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

input_get_disposition()会根据事件的类型返回不同的值。整个函数会对传入的不同的输入事件进行判断和处理,最终是调用input_pass_values()函数将输入事件上报到事件驱动层;

和input_to_handler函数结合来看

事件处理层 Handler(Consumer)—— 以 evdev 为例- /dev/input/eventX(字符设备接口)- evdev_event()/evdev_read()/ioctl
通用事件处理驱动是万能匹配的,简单理解就是无论什么类型的输入设备都能和通用事件处理驱动匹配上,也就是每个输入设备都会有一个对应的/dev/input/eventX设备节点;
当应用程序执行open("/dev/input/eventX", O_RDWR)时等调用的时候,系统调用经过文件系统一系列操作后就会执行file_operations中的成员函数。
这些函数会从事件处理层到input core层再到驱动程序的input_dev对应的方法(例如open)依次执行下去。
代码位于kernel/drivers/input/evdev.c
static const struct file_operations evdev_fops = {.owner = THIS_MODULE,.read = evdev_read,.write = evdev_write,.poll = evdev_poll,.open = evdev_open,.release = evdev_release,.unlocked_ioctl = evdev_ioctl,#ifdefCONFIG_COMPAT.compat_ioctl = evdev_ioctl_compat,#endif.fasync = evdev_fasync,.flush = evdev_flush,.llseek = no_llseek,};
比如evdev_open函数代表注册一个client,此后准备接收数据。
staticintevdev_open(struct inode *inode, structfile *file){struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);unsigned int size = sizeof(struct evdev_client) +bufsize * sizeof(struct input_event);struct evdev_client *client;int error;client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);// 第二,给evdev_client结构分配内存空间。if (!client)client = vzalloc(size);if (!client)return -ENOMEM;client->bufsize = bufsize;spin_lock_init(&client->buffer_lock);snprintf(client->name, sizeof(client->name), "%s-%d",dev_name(&evdev->dev), task_tgid_vnr(current));client->evdev = evdev;// 第三,将client的evdev指向当前evdev,并且将client挂接到evdev的链表上evdev_attach_client(evdev, client);error = evdev_open_device(evdev); // 第四,调用evdev_open_device()if (error)goto err_free_client;file->private_data = client;nonseekable_open(inode, file);// 第五,设置文件的模式return 0;err_free_client:evdev_detach_client(evdev, client);kfree(client);return error;}
这块代码的解读:
1.struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); 得到 struct evdev *evdev 具体是这样子:
struct evdev {int open; // open,当用户open此设备时,open的值加1。struct input_handle handle; // 包括了匹配的 dev 与 handlerwait_queue_head_t wait; // 等待队列struct evdev_client __rcu *grab; // 可以为evdev实例指定一个struct evdev_client实例,这样在传递Input消息时就只会传递给这一个struct evdev_client实例,而不会传递给所有的struct evdev_client实例。每open一次就会生成一个struct evdev_client实例。struct list_head client_list; // 用来把所有的struct client_list实例连接在一起spinlock_t client_lock; /* protects client_list */struct mutex mutex; // 同步相关的锁struct device dev; // dev,用来嵌入到设备模型中。struct cdev cdev; //bool exist; // struct evdev被成功实例化后,exist的值就为true};
client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);struct evdev_client {unsigned int head;unsigned int tail;unsigned int packet_head; /* [future] position of the first element of next packet */spinlock_t buffer_lock; /* protects access to buffer, head and tail */struct wake_lock wake_lock;bool use_wake_lock;char name[28];struct fasync_struct *fasync;struct evdev *evdev;struct list_head node;int clkid;unsigned int bufsize;struct input_event buffer[];};
staticunsignedintevdev_compute_buffer_size(struct input_dev *dev){unsigned int n_events =max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS,EVDEV_MIN_BUFFER_SIZE);return roundup_pow_of_two(n_events);}
bufsize成员:表示当前输入设备的输入事件缓冲区最多可以存放事件的个数。
值得注意的是,输入事件缓冲区的大小不是静态的,这里采用的是基于柔型数组进行动态申请;
在计算bufsize的时候,进行向上对齐2的幂。这样子在判断的时候可以做一个小的优化。
client->evdev = evdev;// 第三,将client的evdev指向当前evdev,并且将client挂接到evdev的链表上evdev_attach_client(evdev, client);staticvoidevdev_attach_client(struct evdev *evdev,struct evdev_client *client){spin_lock(&evdev->client_lock);list_add_tail_rcu(&client->node, &evdev->client_list);spin_unlock(&evdev->client_lock);}

其具体函数
staticintevdev_open_device(struct evdev *evdev){int retval;retval = mutex_lock_interruptible(&evdev->mutex);if (retval)return retval;if (!evdev->exist)retval = -ENODEV;else if (!evdev->open++) {retval = input_open_device(&evdev->handle);if (retval)evdev->open--;}mutex_unlock(&evdev->mutex);return retval;}intinput_open_device(struct input_handle *handle){struct input_dev *dev = handle->dev;int retval;retval = mutex_lock_interruptible(&dev->mutex);if (retval)return retval;if (dev->going_away) {retval = -ENODEV;goto out;}handle->open++;if (!dev->users++ && dev->open)retval = dev->open(dev);if (retval) {dev->users--;if (!--handle->open) {/** Make sure we are not delivering any more events* through this handle*/synchronize_rcu();}}out:mutex_unlock(&dev->mutex);return retval;}EXPORT_SYMBOL(input_open_device);
最后发现input_open_device()的核心是对handle->open和dev->users成员自增,调用dev->open()。
另外的evdev_read,evdev_poll等函数可以自己追踪一下。
应用层中的open/close 机制:
第一次有人打开某个 handler 对应的设备文件(如 /dev/input/eventX) → input_open_device() 计数从 0→1 → 调 dev->open()(设备驱动提供,常用于上电/使能中断)。
最后一个关闭 → input_close_device() 计数从 1→0 → 调 dev->close()。
input_register_device()注册设备input_handler,匹配成功后调用connect()/dev/input/eventX设备节点input_report_*()上报事件input_event()将事件分发给所有注册的input_handlerread()从设备节点获取事件下篇文章可以讲解一下具体案例按键和触摸屏设备驱动。
谢谢阅读收藏!