在linux中,像TP、G-sensor、按键等模块都会用到input系统上报事件,下面我们看看input系统是如何实现事件上报的。
Linux对input子系统进行了高度的抽象,将input系统具体分成三层,input核心层、input事件处理层和input设备驱动层。input核心层(input-core)对input设备驱动层(input-device)和input事件处理层(input-handler)进行管理并进行消息转发。如下图:
- 设备驱动层input-device
input设备驱动层对应的不同的设备驱动,像TP、鼠标、键盘等设备驱动,驱动中会注册input系统,利用input系统上报事件,下面是一个简单的设备驱动注册input系统。
static int tiny210_init_input(void)
{
int err = 0;
printk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__);
struct inout_dev *input = input_allocate_device(); //申请input设备
if (!input) {
printk(KERN_ERR, "input_allocate_device(..) failed.\n");
return (-ENOMEM);
}
//填充input结构体数据
input->name = "tiny210-keys";
__set_bit(EV_KEY , input->evbit );
__set_bit(KEY_HOME, input->keybit);
__set_bit(KEY_MENU, input->keybit);
err = input_register_device(input); //注册一个input设备
if (err) {
printk(KERN_ERR, "input_register_device(..) = %d.\n", err);
input_free_device(input);
input = NULL;
return (-ENODEV);
}
printk(SF_LOG_LEVEL, "%s(..) leave.\n", __FUNCTION__);
return err;
}
//有中断或者按键事件上报按键事件,一般在中断处理函数中
key_code = KEY_HOME;
input_report_key(input, key_code, kevent->value);
input_sync(input);
设备驱动层使用主要是去注册一个input子系统,然后通过注册的系统,上报想要上报的事件。从上面可以看出,驱动中使用input一般分为4步,1、input_allocate_device申请input资源;2、填充input结构体,包括name、注册的事件类型(如按键事件、绝对事件等等);3、input_register_device(input)在内核注册一个input事件;4、当有按键或者TP报点产生时,同步上报按键事件,这一步一般会在中断处理函数中进行。
input_allocate_device和input_register_device都是input-core暴露出来的接口。
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(dev);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
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",
(unsigned long) atomic_inc_return(&input_no) - 1);
//设备驱动模型视图中增加一个input设备
error = device_add(&dev->dev);
if (error)
return error;
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) {
device_del(&dev->dev);
return error;
}
//链接到全局输入设备链表
list_add_tail(&dev->node, &input_dev_list);
//匹配全局事件处理handler链表,匹配支持该输入设备的handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
- 事件层input-handler
从上面的图中可以看出,不同的设备会对应有不同的handler,我们以支持TP的event-handler为例说明,event-handler代码在driver/input/evdev.c中。
static const struct input_device_id evdev_ids[] = { //设备ID
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event, //消息处理接口
.connect = evdev_connect, // input-core匹配到device后调用的接口
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //管理改输入操作的接口类
.minor = EVDEV_MINOR_BASE, //handler名称
.name = "evdev",
.id_table = evdev_ids, //设备id,标识支持的device型号
};
static int __init evdev_init(void)
{
//注册handler,input_register_handler是input-core提供的接口
return input_register_handler(&evdev_handler);
}
input_register_handler函数在/driver/input/input.c中,也是input-core暴露出来的接口:
static struct input_handler *input_table[8];
//初始化全局input-handler列表
static LIST_HEAD(input_handler_list);
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
//注册的handler添加到input_table中,input_table最多支持8种handler(例如按键、鼠标为不同hander),每种handler最多支持32个设备
input_table[handler->minor >> 5] = handler;
}
//添加到input-handler全局链表
list_add_tail(&handler->node, &input_handler_list);
//遍历input_dev_list全局设备链表,寻找handler支持的设备
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
- 核心层input-core
核心层input-core完成的工作包括:
1) 直接跟字符设备驱动框架交互,字符设备驱动框架根据主设备号来进行管理,而input-core则是依赖于次设备号来进行分类管理。Input子系统的所有输入设备的主设备号都是13,其对应input-core定义的structfile_operations input_fops.驱动架构层通过主设备号13获取到input_fops,之后的处理便交给input_fops进行。
2) 提供接口供事件处理层(input-handler)和输入设备(input-device)注册,并为输入设备找到匹配的事件处理者。
3) 将input-device产生的消息(如触屏座标和压力值)转发给input-handler,或者将input-handler的消息传递给input-device(如鼠标的闪灯命令)。
input-core代码主要在/driver/input/input.c中:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
static int __init input_init(void)
{
int err;
//sys/class/下面创建input节点
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
//dev下面创建设备节点
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("bus/input", NULL);
if (!proc_bus_input_dir)
return -ENOMEM;
// bus/input下面建立devices节点
entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_fileops);
if (!entry)
goto fail1;
// bus/input下面建立handlers节点
entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_fileops);
if (!entry)
goto fail2;
return 0;
fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}
除了在/dev下面简历主设备号为13的input设备节点,还会在设备模型/sys/class目录注册设备类,在/proc/bus/input目录产生设备信息,可以在对应的节点下查看支持的device和handler。
- input-core关联匹配input-device和input-handler
在上面的input_register_device和input_register_handler接口中,最后都会调用input_attach_handler接口来匹配输入事件与对应的handler。
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
//匹配handler和device的ID,event_handler默认处理所有的事件类型设备
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
//匹配成功后调用handler的connect接口,既evdev_connect
error = handler->connect(handler, dev, id);
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;
}
//每个handler支持的最多设备数
#define EVDEV_MINORS 32
//evdev_table的每一个元素对应一个input-device
static struct evdev *evdev_table[EVDEV_MINORS];
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
//从0开始找到一个未用的索引号
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
//次设备文件名,这里是我们能看到的最终生成的设备文件event0、event1等
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
//填充evdev结构体
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
//在设备驱动视图/sys/class/input和/sys/devices目录下产生eventX设备
//最终依赖event机制和mdev在/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);
//注册handler,关联input-handler和input-device
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
//将evdev放入evdev_table中
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}
evdev_table代表evdev_handler所管理的底层input-device(通过input-handle管理)和应用层已打开该设备的进程、同步的相关结构和消息队列(evdev_client记录)。
struct evdev {
int open;
int minor;
//管理handler对应的device
struct input_handle handle;
wait_queue_head_t wait;
//记录应用层已经open过eventX设备的进程、同步异步相关结构和消息池
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
bool exist;
};
input-handler与input-device是通过input-handle来关联的,在input_register_handle函数中,将input-handle中的d_node和h_node分别关联到input-device和input-handler中,这样通过input-handler可以快速找到input-device,通过input-device也可以快速找到input-handler。
至此,我们可以得到以下evdev-handler管理下的示意图:
- 应用层open、read操作
input-device注册成功后,会生成不同的节点,如下所示:
应用层如果要得到相应的数据,例如得到按键操作的键值,会去open、read节点,例如open(dev/input/event0),下面我们看看open过程,设备节点的注册是在input_init实现的,register_chrdev(INPUT_MAJOR, "input", &input_fops);open函数会调用input_open_file函数:
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
err = mutex_lock_interruptible(&input_mutex);
if (err)
return err;
/* No load-on-demand here? */
//iminor(inode)为次设备号,根据次设备号找到注册的input-handler
//input_table在input_register_handler时会填充
handler = input_table[iminor(inode) >> 5];
if (handler) //handler下面对应的fops
new_fops = fops_get(handler->fops);
mutex_unlock(&input_mutex);
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops || !new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
//执行对应的handler下面的open
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
}
input_open_file会通过查找不同的handler,找到对应的open函数,我们继续已evdev_handler为例。
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;
unsigned int bufsize;
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
//input_attach_handler时填充
evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
//根据当前进程产生client的名称
snprintf(client->name, sizeof(client->name), "%s-%d",
dev_name(&evdev->dev), task_tgid_vnr(current));
wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
//将代表打开该设备的进程相关的数据结构client和evdev绑定
client->evdev = evdev;
evdev_attach_client(evdev, client);
//执行input-device层的open
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
file->private_data = client;
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
wake_lock_destroy(&client->wake_lock);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
evdev_open_device会调用到设备驱动中的open,一般像TP、G-sensor注册input系统没有定义open接口。
上面open获得的fd句柄对应的file_operations是evdev_handler的evdev_fops,所以应用层read的时候最终会调用evdev_read接口。上面open过程中,我们看到evdev_client结构体,evdev_client中的buffer作为一个消息队列会保存input-core转发上来的底层消息:
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;
char name[28];
//异步通知上层等待进程
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
unsigned int bufsize;
//消息队列,input-core转发的底层触屏消息会放到这里
//上层应用进程也会在这里读取
struct input_event buffer[];
};
下面看看evdev_read函数:
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
//判断读取长度是否小於单个input-event的长度
if (count < input_event_size())
return -EINVAL;
//判断消息队列消息是否为空
if (!(file->f_flags & O_NONBLOCK)) {
//等待消息队列不为空事件
//阻塞在这,等待wait释放,消息更新
retval = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail || !evdev->exist);
if (retval)
return retval;
}
if (!evdev->exist)
return -ENODEV;
//将消息队列的消息取出并通过copy_to_user填到buffer中返回
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
if (retval == 0 && file->f_flags & O_NONBLOCK)
retval = -EAGAIN;
return retval;
}
当没有数据时,wait_event_interruptible阻塞,上层进程将进入睡眠。直到有数据更新时,唤醒进程,那什么时候会有数据更新了?我们在前面介绍input设备驱动的时候有说到input_report_key(input, key_code, kevent->value);和input_sync(input);数据就是在这里更新的。以TP为例,TP在产生中断后,会触发中断处理函数,调度到中断下部分后,会去读取TP的报点,然后通过input_report_abs和input_sync接口将数据更新到input-core中。
input_report_abs、input_report_key、input_sync都是input.h中暴露出来的接口,最后都是调用input-core层的input-event接口:
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);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case EV_ABS:
//检测input-device初始化声明是否支持该编码ABS_MAX
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);
break;
//非同步事件
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
//不是传递给input-core处理事件
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock();
//输入设备对应的input-handle,其handler就是evdev_handler
handle = rcu_dereference(dev->grab);
if (handle) //调用到evdev_event
handle->handler->event(handle, type, code, value);
……
}
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;
//填充输入消息结构体
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client) //将消息写入client结构体中的buffer中
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
//唤醒等待读取消息进程,input_sync函数调用下来就会唤醒进程
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);
}
wake_up_interruptible(&evdev->wait)调用后会唤醒执行在evdev_read中wait_event_interruptible等待读取消息的进程,继续下面的执行过程,从client的buffer中取出消息,并通过copy_to_user返回给应用程序。