目录
[五、connect 函数](#五、connect 函数)
Linux Version:linux-5.4.239
Linux input子系统是内核为统一管理所有输入设备(如键盘、鼠标、按键、触摸屏)而设计的通用驱动框架。它为驱动开发者提供标准接口,同时向上为用户空间应用程序提供统一的设备访问方式。
一、总体框架
例如,当手指触摸屏幕时会发送时产生一个中断,然后 CPU 通过 SPI 总 线读取触摸坐标数据,并放入一个buffer,由字符驱动管理这个buffer,而驱动的 read 接 口可以让用户直接读取坐标数据。Input子系统框架如图1所示。

图1.Input子系统整体框架
二、驱动框架
为了在硬件和软件之间建立明确的分工,input子系统采用三层结构:设备驱动层、Input核心层、事件处理层,具体如图2所示。

图2.Input驱动框架
-
驱动层:负责初始化硬件、处理中断、读取原始数据,并将这些原始数据转换成统一的内核事件格式,然后向核心层报告。
-
核心层:为驱动层和事件处理层提供注册、数据传输等相关API。
-
事件处理层 :实现了标准的文件操作接口,并在
/dev/input/目录下创建对应的设备文件(如event0),应用程序通过读写这些文件来获取输入事件。
三、核心数据结构
Input子系统的三个核心数据结构为:input_dev(驱动层)、input_handler(事件处理层)、input_handle(核心层)三者的关系如图3所示。

图3.Input子系统层级关系
**input_dev:**表示一个具体的输入设备(如键盘),由驱动分配、初始化并注册到核心层。
input_handler: 表示一种事件处理方式(如 evdev、joydev),负责将内核事件发送到用户空间接口(如 /dev/input/eventX)。
input_handle: 连接 input_dev 与 input_handler,记录两者之间的绑定关系,kernel可通过事件处理程序找到对应的设备,实现事件的分发。
3.1、input_dev
input_dev用来描述一个输入设备,其结构如下:
objectivec
/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
* @uniq: unique identification code for the device (if device has it)
* @id: id of the device (struct input_id)
* @propbit: bitmap of device properties and quirks
* @evbit: bitmap of types of events supported by the device (EV_KEY,
* EV_REL, etc.)
* @keybit: bitmap of keys/buttons this device has
* @relbit: bitmap of relative axes for the device
* @absbit: bitmap of absolute axes for the device
* @mscbit: bitmap of miscellaneous events supported by the device
* @ledbit: bitmap of leds present on the device
* @sndbit: bitmap of sound effects supported by the device
* @ffbit: bitmap of force feedback effects supported by the device
* @swbit: bitmap of switches present on the device
* @hint_events_per_packet: average number of events generated by the
* device in a packet (between EV_SYN/SYN_REPORT events). Used by
* event handlers to estimate size of the buffer needed to hold
* events.
* @keycodemax: size of keycode table
* @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device
* @getkeycode: optional legacy method to retrieve current keymap.
* @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used.
* The method is being called while holding event_lock and thus must
* not sleep
* @ff: force feedback structure associated with the device if device
* supports force feedback effects
* @poller: poller structure associated with the device if device is
* set up to use polling mode
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
* @rep: current values for autorepeat parameters (delay, rate)
* @mt: pointer to multitouch state
* @absinfo: array of &struct input_absinfo elements holding information
* about absolute axes (current value, min, max, flat, fuzz,
* resolution)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
* request an IRQ, submit URB, etc.)
* @close: this method is called when the very last user calls
* input_close_device().
* @flush: purges the device. Most commonly used to get rid of force
* feedback effects loaded into the device when disconnecting
* from it
* @event: event handler for events sent _to_ the device, like EV_LED
* or EV_SND. The device is expected to carry out the requested
* action (turn on a LED, play sound, etc.) The call is protected
* by @event_lock and must not sleep
* @grab: input handle that currently has the device grabbed (via
* EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
* recipient for all input events coming from the device
* @event_lock: this spinlock is taken when input core receives
* and processes a new event for the device (in input_event()).
* Code that accesses and/or modifies parameters of a device
* (such as keymap or absmin, absmax, absfuzz, etc.) after device
* has been registered with input core must take this lock.
* @mutex: serializes calls to open(), close() and flush() methods
* @users: stores number of users (input handlers) that opened this
* device. It is used by input_open_device() and input_close_device()
* to make sure that dev->open() is only called when the first
* user opens device and dev->close() is called when the very
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
* @node: used to place the device onto input_dev_list
* @num_vals: number of values queued in the current frame
* @max_vals: maximum number of values queued in a frame
* @vals: array of values queued in the current frame
* @devres_managed: indicates that devices is managed with devres framework
* and needs not be explicitly unregistered or freed.
* @timestamp: storage for a timestamp set by input_set_timestamp called
* by a driver
*/
struct input_dev {
const char *name; // 设备名称
const char *phys; // 物理路径(/dev/input/event0)
const char *uniq;
struct input_id id; // 设备ID
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //支持的事件类型(如EV_KEY、EV_SYN)
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//按键/按钮 (KEY_xxx, BTN_xxx)
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//相对轴 (REL_xxx)
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//绝对轴 (ABS_xxx)
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//杂项事件 (MSC_xxx)
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//LED 指示灯 (LED_xxx)
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//声音效果 (SND_xxx)
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//力反馈效果 (FF_xxx)
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//开关状态 (SW_xxx)
unsigned int hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff;
struct input_dev_poller *poller;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt *mt;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list;// 关联的input_handle链表(设备匹配的handler)
struct list_head node; // 用于挂载到内核的input_dev_list链表
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
ktime_t timestamp[INPUT_CLK_MAX];
};
关键字段说明:
**struct input_id id:**总线类型、供应商、产品、版本号
**unsigned long evbit:**支持的事件大类(EV_KEY, EV_REL, EV_ABS ...)
**unsigned long keybit:**支持的按键/按钮(KEY_A, BTN_LEFT ...)
**unsigned long relbit:**支持的相对轴(REL_X, REL_Y, REL_WHEEL ...)
**int (*event)(struct input_dev *dev, ...):**处理发往设备的事件(如设置LED)
input_dev 完整描述了设备的能力、状态以及内核连接关系。示例代码如下所示:
objectivec
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/input.h>
static struct input_dev *test_dev;
static void timer_function(struct timer_list *data);
DEFINE_TIMER(test_timer, timer_function);
static void timer_function(struct timer_list *data)
{
static int val;
val = val ? 0 : 1;
input_report_key(test_dev, KEY_1, val);
input_sync(test_dev);
mod_timer(&test_timer, jiffies + msecs_to_jiffies(1000));
}
static int __init test_init(void)
{
int ret;
test_dev = input_allocate_device();
test_dev->name = "test_key";
__set_bit(EV_KEY, test_dev->evbit);
__set_bit(EV_SYN, test_dev->evbit);
__set_bit(KEY_1, test_dev->keybit);
ret = input_register_device(test_dev);
if(ret < 0){
printk(KERN_INFO " input_register_device error \n");
goto input_err;
}
test_timer.expires = jiffies + msecs_to_jiffies(1000);
add_timer(&test_timer);
return 0;
input_err:
input_unregister_device(test_dev);
return -1;
}
static void __exit test_exit(void)
{
del_timer(&test_timer);
input_unregister_device(test_dev);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL v2");
这个示例代码会每隔 1 秒自动模拟一次按键 KEY_1 的按下和释放,在test_init函数中首先通过input_allocate_device函数分配一个 input_dev 结构体,然后再设置支持的事件类型和具体按键,最后调用input_register_device函数将其注册到kernel中,这样就实现了一个input_dev设备。
3.2、input_handler
input_handler 位于 input 子系统的事件处理层,负责将底层驱动上报的输入事件转换为用户空间可以获取的格式(例如通过 /dev/input/eventX 节点)。
内核已实现多个通用input_handler,如下:
evdev_handler:通用处理程序,处理几乎所有输入设备事件(键盘、鼠标等)。
joydev_handler:游戏手柄专用,专门处理游戏手柄、摇杆的输入。
mousedev_handler:鼠标专用,专门处理鼠标事件。
objectivec
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
* @events: event sequence handler. This method is being called by
* input core with interrupts disabled and dev->event_lock
* spinlock held and so it may not sleep
* @filter: similar to @event; separates normal event handlers from
* "filters".
* @match: called after comparing device's id with handler's id_table
* to perform fine-grained matching between device and handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process
* that "grabbed" a device releases it
* @legacy_minors: set to %true by drivers using legacy minor ranges
* @minor: beginning of range of 32 legacy minors for devices this driver
* can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*
* Input handlers attach to input devices and create input handles. There
* are likely several handlers attached to any given input device at the
* same time. All of them will get their copy of input event generated by
* the device.
*
* The very same structure is used to implement input filters. Input core
* allows filters to run first and will not pass event to regular handlers
* if any of the filters indicate that the event should be filtered (by
* returning %true from their filter() method).
*
* Note that input core serializes calls to connect() and disconnect()
* methods.
*/
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;
const char *name;
const struct input_device_id *id_table;
struct list_head h_list;
struct list_head node;
};
关键字段说明:
connect:若match成功,调用connect方法,将 handler 正式绑定到设备上。
event/events:常规 handler 通过此方法接收并处理一个事件。
id_table:指向一个input_device_id数组,用于快速匹配设备。
h_list:链接所有由此 handler 创建的input_handle(每个 handle 代表 handler 与一个设备之间的连接)。
node:将 handler 挂载到全局链表input_handler_list上
这里以evdev handler为例,代码如下:
objectivec
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}
module_init(evdev_init);
module_exit(evdev_exit);
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input driver event char devices");
MODULE_LICENSE("GPL");
3.3、input_handle
input_handle 是连接input_dev与input_handler的桥梁。表示某个 handler 通过这个 handle 与某个 device 进行交互,并接收来自该设备的事件。
objectivec
/**
* struct input_handle - links input device with an input handler
* @private: handler-specific data
* @open: counter showing whether the handle is 'open', i.e. should deliver
* events from its device
* @name: name given to the handle by handler that created it
* @dev: input device the handle is attached to
* @handler: handler that works with the device through this handle
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
*/
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
关键字段说明:
dev**:** 指向一个具体的input_dev
handler**:** 指向一个具体的input_handler
d_node**:** 将此 handle 挂入dev->h_list(input_dev侧链表)。
h_node**:** 将此 handle 挂入handler->h_list(input_handler侧链表)。
从设备出发 :顺着 dev->h_list 找到所有 handle,再从 handle 拿到 handler。
从事件处理测出发 :顺着 handler->h_list 找到所有 handle,再从 handle 拿到 dev。
input_dev和input_handler通过input_handle建立的并不是一对一的关系,而是多对多关系,即一个设备可被多个handler匹配(如触摸屏同时被evdev和tslib处理),而一个handler也可匹配多个设备(如evdev处理所有按键设备)。
四、匹配规则
在 Linux 输入子系统中,当一个输入设备input_dev被注册时,事件input_handler就会与之进行匹配操作,同理,当事件input_handler被注册时,就会去和input_dev进行匹配,当匹配成功就会创建一个input_handle。
input_handler register:
input_register_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_register_device:
input_register_device
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler
可以看到当驱动调用 input_register_device注册一个新的 input_dev时,系统会遍历全局的 input_handler_list,对每个 handler 尝试匹配。反之当注册一个新的 handler,则会遍历input_dev_list,让新 handler 与它们进行匹配。
input_attach_handler:
objectivec
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); // 当匹配成功会调用connect函数
if (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;
}
具体匹配规则如下:
objectivec
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
return false;
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}
return true;
}
EXPORT_SYMBOL(input_match_device_id);
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;
}
}
return NULL;
}
不同的事件处理方式不同,所以匹配规则也不相同,这里以evdev为例,evdev的匹配规则很直接**,** 它通过一个万能的 id_table,匹配所有输入设备,如下:
objectivec
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
因为没有设置任何硬件ID(bustype, vendor, product, version)和evbit相关的比较条件,根据input_match_device_id 函数的逻辑,所有约束条件都会被跳过,直接返回匹配成功。而evdev_handler也没有实现 match 函数,所以没有二次匹配。
五、connect 函数
connect 函数起到了类似设备驱动模型中 probe 函数的作用。在 input_handler 与input_dev匹配成功后,调用connect函数将两者真正的相互关联。以evdev为例,如下:
objectivec
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct 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);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}
evdev_connect函数的主要作用是为该设备创建并注册一个 evdev 实例,并在用户空间生成对应的 /dev/input/eventX 字符设备节点,从而让用户程序能够通过标准的 read、write 等接口读取输入事件。
填充并注册 input_handle:
evdev->handle.dev = input_get_device(dev); // 指向输入设备
evdev->handle.name = dev_name(&evdev->dev); // 名称如 "event0"
evdev->handle.handler = handler; // 指向 evdev_handler
evdev->handle.private = evdev; // handler 私有数据
error = input_register_handle(&evdev->handle); //将 handle 挂入设备链表
dev->h_list和处理器链表handler->h_list
初始化并添加字符设备:
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class; // 归属于 input 类,自动在 /sys/class/input 下创建
evdev->dev.parent = &dev->dev; // 父设备是输入设备
evdev->dev.release = evdev_free; // 释放函数
device_initialize(&evdev->dev); // 初始化设备结构
cdev_init(&evdev->cdev, &evdev_fops); // 初始化字符设备,绑定文件操作表 evdev_fops
error = cdev_device_add(&evdev->cdev, &evdev->dev);
cdev_device_add 将字符设备添加到内核,并注册对应的设备,成功后在 /dev/input/ 下生成 eventX 节点。evdev_fops 则包含了 open、read、write等标准文件操作集,供用户空间调用。如下:
objectivec
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,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.llseek = no_llseek,
};
六、evdev_client
当打开 /dev/input/eventX 的文件描述符evdev_open函数就会被调用,会为本次打开创建一个独立的事件缓冲区client,并增加handle->open次数,如下代码:
objectivec
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
struct evdev_client *client;
int error;
client = kvzalloc(struct_size(client, buffer, bufsize), GFP_KERNEL);
if (!client)
return -ENOMEM;
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
file->private_data = client;
stream_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kvfree(client);
return error;
}
evdev_open
evdev_open_device
input_open_device
handle->open++; // 记录有多少个 handler 实例,在
input_pass_values// 中会判断是否向该 handle 汇报事件
struct evdev_client *client结构体如下:
objectivec
struct evdev_client {
unsigned int head; // 环形缓冲区写指针
unsigned int tail; // 环形缓冲区读指针
unsigned int packet_head;
spinlock_t buffer_lock;
struct fasync_struct *fasync;
struct evdev *evdev; // 指向所属的 evdev 设备
struct list_head node; // 用于挂入 evdev->client_list 链表
enum input_clock_type clk_type;
bool revoked;
unsigned long *evmasks[EV_CNT];
unsigned int bufsize; // 环形缓冲区容量,能存放的 input_event 个数
struct input_event buffer[]; // 存放事件的环形缓冲区
};
这里的 struct input_event buffer会用来存放 input_dev 传递的数据,等待用户空间读取。
七、事件传输流程
7.1、input_dev向用户空间汇报事件
比如电脑键盘驱动收到一个Enter按下的事件要怎么汇报给核心层,然后再汇报给用户呢?如下函数:
objectivec
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input
* devices to report input events. See also input_inject_event().
*
* NOTE: input_event() may be safely used right after input device was
* allocated with input_allocate_device(), even before it is registered
* with input_register_device(), but the event will not reach any of the
* input handlers. Such early invocation of input_event() may be used
* to 'seed' initial state of a switch or initial position of absolute
* axis, etc.
*/
void input_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);
当按键按下时,驱动程序会调用input_event函数,将事件传递给input核心层,最终分发给所有绑定的 input_handler,进而传递给用户空间的应用程序。
input核心层到input_handler的流程如下:
// drivers/input/input.c
input_handle_event
input_pass_values
list_for_each_entry_rcu(handle, &dev->h_list, d_node) // 通过dev->h_list找到与之对应的 handle,并找到对应的 handler
if (handle->open) // 这个值会在 evdev_open 中被++
count = input_to_handler(handle, vals, count);
handler->events(handle, vals, count);
现在已经调用到evdev handler中的evdev_event函数,那怎么把这个数据传递给用户的呢?如下:
objectivec
// drivers/input/evdev.c
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = { { type, code, value } };
evdev_events(handle, vals, 1);
}
evdev_event
evdev_events(handle, vals, 1);
list_for_each_entry_rcu(client, &evdev->client_list, node) // 找到对应的client
evdev_pass_values
__pass_event
client->buffer[client->head++] = *event; // 将事件复制到buffer
可以看到最后是将事件数据input_event 放入指定的client buffer 中,供用户空间程序通过 read读取,代码如下:
evdev_read
evdev_fetch_next_event(client, &event))
*event = client->buffer[client->tail++]; // 将事件从client_buffer复制到event input_event_to_user(buffer + read, &event)
copy_to_user(buffer, event, sizeof(struct input_event)) //最后拷贝到用户空间
用户空间代码如下:
objectivec
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
int fd, ret;
struct input_event *event;
fd = open("/dev/input/event0", O_RDWR);
if(fd < 0) {
printf("ERROR: open failed!\n");
return -1;
}
event = (struct input_event*)malloc(sizeof(struct input_event));
while (1) {
ret = read(fd, event, sizeof(struct input_event));
if(ret < 0) {
printf("ERROR: read failed!\n");
return -1;
}
if (event->type == EV_KEY && event->code == KEY_1) {
if (event->value == 1) {
printf("KEY_1 is 1\n");
} else if (event->value == 0) {
printf("KEY_1 is 0\n");
} else {
break;
}
}
}
close(fd);
return 0;
}
7.2、用户空间向input子系统注入事件
用户空间程序可以通过 write 向 /dev/input/eventX 写入一个或多个 struct input_event,这些事件会被注入到input子系统中。如下:
evdev_write
input_event_from_user
copy_from_user(event, buffer, sizeof(struct input_event))
input_inject_event(&evdev->handle,event.type, event.code, event.value);
input_handle_event(dev, type, code, value);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS) {
input_pass_values
如果需要传给设备(INPUT_PASS_TO_DEVICE)且 dev->event 存在,则会调用驱动回调,即input_dev->event。如果需要传给 handlers(INPUT_PASS_TO_HANDLERS),将事件存入 dev->vals 缓冲区。调用 input_pass_values 将事件分发给所有已打开的 input_handle。
因此,注入的事件会像真实硬件事件一样被所有监听该输入设备的 handler 接收,用户空间的其他进程也可以 read 到这些注入的事件。例如下代码段:
objectivec
struct input_event ev;
ev.type = EV_KEY;
ev.code = KEY_A;
ev.value = 1; // 按下
write(fd, &ev, sizeof(ev));
ev.value = 0; // 释放
write(fd, &ev, sizeof(ev));
八、实验结果
实验代码是实现了一个简单的Linux内核输入设备驱动,在前面代码分析中已经给出了源码,它模拟了一个名为 test_key的按键设备,并通过内核定时器每秒自动上报 KEY_1 按键的按下/释放事件,代码已经在前面给出,下面直接运行。
执行结果如下:

原始数据如下:

加载模块后,系统会出现一个/dev/input/event0的input设备节点,每隔1秒,该设备会交替上报 KEY_1 的按下和释放事件,用户态程序可以读取到这些事件,效果等同于每隔1秒有人按下并释放了键盘上的数字键1。