输入事件层目前系统帮我们区分了三种,
通用各种类型输入类设备的evdev,主流,也是将来大方向
mousedev,joydev。
我们学习就以evdev来学习,将来使用通用性也更高。
一、首先是框架性的调用输入核心层实现的input_register_handler和input_unregister_handler来注册evdev层。
static struct input_handler evdev_handler = { //事件驱动核心结构体
.event = evdev_event, //打包数据,并上报事件(发送一个事件)
.events = evdev_events, //发送多个事件
.connect = evdev_connect, //找到匹配 dev 后进行连接
.disconnect = evdev_disconnect, //断开连接时使用
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE, //次设备号基数 64
.name = "evdev", //处理程序的名称,在/proc/bus/input/handlers 中显示
.id_table = evdev_ids, //匹配规则,evdev是所有都可以匹配
};
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);
evdev有个重要的数据结构,当输入设备和事件驱动匹配的时候,就会在connect函数创建dvdev设备,最后注册成字符设备开放到用户空间。
struct evdev { // evdev 相当于一个桥梁,链接 input_dev 和 handler
int open; //打开引用计数
struct input_handle handle; //关联的input_handle
wait_queue_head_t wait; //等待队列
struct evdev_client __rcu *grab;
struct list_head client_list; //evdev_client 链表,说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; //dev 是 evdev 的父设备
struct cdev cdev; //cdev字符设备
bool exist; //判断此 evdev 释放存在
};
二、接口实现
1.首先我们看一下connect函数,它在注册handler和dev时,如果匹配上了就会调用。(具体可以查看我的输入子系统学习三)
/*
* 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) //创建新的 evdev 设备。输入内核有序连接和断开
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); //申请未使用的minor
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //申请 sizeof(struct evdev) 的空间
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list); //初始化 client_list 链表
spin_lock_init(&evdev->client_lock); //初始化 client_lock 自旋锁
mutex_init(&evdev->mutex); //初始化 evdev 的互斥量
init_waitqueue_head(&evdev->wait); //初始化 evdev 的等待队列
evdev->exist = true; //此 evdev 为存在
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; //判断此 evdev 设备申请的是 32 个中的哪一个
dev_set_name(&evdev->dev, "event%d", dev_no); //设置此 evdev->dev 的名字
evdev->handle.dev = input_get_device(dev); //增加 evdev->dev 的引用计数
evdev->handle.name = dev_name(&evdev->dev); //设置名字 event0,1,2,3....
evdev->handle.handler = handler; //指向匹配到的 handler
evdev->handle.private = evdev; //指向自己 evdev
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); //由输入设备的主设备号13和此设备号合成一个设备号
evdev->dev.class = &input_class; //设置 evdev 的类为 input_class
evdev->dev.parent = &dev->dev; //输入设备 input_dev 是 evdev 是的父设备
evdev->dev.release = evdev_free; //释放 evdev 资源
device_initialize(&evdev->dev); //内部 device 初始化
error = input_register_handle(&evdev->handle); //注册 handle
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops); //用 evdev_fops 初始化一个 cdev 字符设备
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); //添加一个字符设备到系统中
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev); //真正的创建设备层次和attribute在sysfs中
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);
err_free_minor:
input_free_minor(minor);
return error;
}
2.disconnect,和connect相反
static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private; //handle 的私有数据就是 handle 本身 evdev
device_del(&evdev->dev);
evdev_cleanup(evdev);
input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle);
put_device(&evdev->dev);
}
3.对一个设备驱动层发送过来的事件打包
/*
* Pass incoming events to all connected clients. 传递来到的事件给所有连接的客户端
*/
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private; //此处的私有数据就是 evdev 本身
struct evdev_client *client;
ktime_t ev_time[EV_CLK_MAX];
ev_time[EV_CLK_MONO] = ktime_get(); //设置对应的时间
ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
TK_OFFS_BOOT);
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client) //如果该evdev有个专用的client,那么就将事件发给它如果该evdev
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time); //否则,发给 evdev 客户链表上的所有客户
rcu_read_unlock();
}
/*
* 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);
}
发送数据给client函数
static void __pass_event(struct evdev_client *client,
const struct input_event *event) //使用异步通知函数通知上层
{
client->buffer[client->head++] = *event; //把 *event 放在 struct evdev_client 的缓冲区中
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail].time = event->time;
client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
}
if (event->type == EV_SYN && event->code == SYN_REPORT) { //事件的类型是同步类型,事件的编码是只上报一次
client->packet_head = client->head; //下一次从缓冲区的第一个位置取数据
kill_fasync(&client->fasync, SIGIO, POLL_IN); //发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数
}
}
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time) //把事件发给 evdev_client,唤醒等待队列
{
struct evdev *evdev = client->evdev; //获取此客户端要访问的 evdev 设备
const struct input_value *v;
struct input_event event;
bool wakeup = false; //默认不唤醒
if (client->revoked) //如果保留了,直接返回
return;
event.time = ktime_to_timeval(ev_time[client->clk_type]); //根据客户端的时间类型,获取对应的时间
/* Interrupts are disabled, just acquire the lock. */ //中断被关闭,获取锁
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) { //循环发送
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event); //使用异步通知函数通知上层
if (v->type == EV_SYN && v->code == SYN_REPORT) //事件的类型是同步类型,事件的编码是只上报一次,此时要唤醒等待队列
wakeup = true;
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait); //唤醒 evdev 的等待队列
}
4.应用层的调用接口file_operation
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,
.flush = evdev_flush,
.llseek = no_llseek,
};
操作函数比较多,我们主要分析连个最常用的,open,和read
当我们使用open打开时,会创建 evdev_client 和 ecdev字符设备建立联系,通过evdev的handle找到对应的device,打开此设备。
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); //由 inode->i_cdev 成员获取 evdev 设备
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); //申请 size 大小的内存
if (!client)
client = vzalloc(size);
if (!client)
return -ENOMEM;
client->bufsize = bufsize; //设置事件缓冲区的大小
spin_lock_init(&client->buffer_lock);
client->evdev = evdev; //此次打开字符设备所使用的 evdev
evdev_attach_client(evdev, client); //把此 dvdev 附着在 evdev_client
error = evdev_open_device(evdev); //打开此 evdev 字符设备
if (error)
goto err_free_client;
file->private_data = client; //file 的私有数据设置为 evdev_client
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kvfree(client);
return error;
}
int input_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); //.open = input_proc_devices_open, 打开输入设备
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;
}
对应应用层的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;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size())
return -EINVAL;
for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV;
/* 当client缓冲区无数据;evdev不存在;文件非阻塞打开,那个read直接返回错误 */
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*
* count == 0 is special - no IO is done but we check
* for error conditions (see above).
*/
if (count == 0)
break;
/* 当要读取的数据大于struct input_event且client里面的buffer有数据,则把数据拷贝到用户空间*/
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + read, &event)) /* 对copy_to_user的封装 */
return -EFAULT;
read += input_event_size();
}
if (read)
break;
if (!(file->f_flags & O_NONBLOCK)) { //以阻塞的方式等待事件的发生
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}