输入事件层目前系统帮我们区分了三种,
通用各种类型输入类设备的evdev,主流,也是将来大方向
mousedev,joydev。
我们学习就以evdev来学习,将来使用通用性也更高。
一、首先是框架性的调用输入核心层实现的input_register_handler和input_unregister_handler来注册evdev层。
static struct input_handler evdev_handler = {
.event = evdev_event, /* 打包数据,并上报事件 */
.connect = evdev_connect, /* 和dev匹配后做响应连接 */
.disconnect = evdev_disconnect, /* exit使用,卸载使用 */
.fops = &evdev_fops, /* 提供具体的read,write,sync之类的操作 */
.minor = EVDEV_MINOR_BASE, /* 64,evdev的minor base */
.name = "evdev",
.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有个重要的数据结构
/*evdev结构体再配对成功的时候生成,由handler->connect生成*/
struct evdev {
int open; //打开引用计数
int minor; //次设备号(实际的-EVDEV_MINOR_BASE)
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;
bool exist;
};
二、接口实现
1.首先我们看一下connect函数,它在注册handler和dev时,如果匹配上了就会调用。(具体可以查看我的输入子系统学习三)
/* 在endev里面定义的全局变量,它可以存放32的evdev指针 */
static struct evdev *evdev_table[EVDEV_MINORS];
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here.
*/
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;
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor]) /* 从小到大找到一个新的未使用的minor */
break;
/* 最多只有32个evdev设备,这里面minor是相对EVDEV_MINOR_BASE来说的,实际的次设备号应该加上EVDEV_MINOR_BASE */
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
/* 申请空间 */
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
/* 初始化新申请的evdev */
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait); /* 注意这里初始化了一个等待队列,将来肯定会有evdev被放入等待队列和唤醒等待队列操作 */
/*evdev的名字是从0开始增加 */
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); /* input_dev绑定到 evdev里面的handle里 */
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler; /* 绑定handler到handle里 */
evdev->handle.private = evdev; /* handle的私有数据就是evdev本身 */
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class; /* evdev里面的设备属于input_class,这样endev所有设备才会出现在sysfs下的input文件夹下面 */
evdev->dev.parent = &dev->dev; /* 父节点是设备的dev */
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev); /* 初始化evdev里面的dev通用数据 */
error = input_register_handle(&evdev->handle); /* 注册handle,核心层有分析过,把handler和dev通过链表增加到handler里面的handle里面的链表节点 */
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev); /* 把该evdev添加到前面定义的evdev_table表中 */
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev); /* 真正的创建设备层次和attribute在sysfs中,学习完驱动学习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);
return error;
}
2.disconnect,和connect相反
static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private; /* 在connect把evdev本身绑定在handle里面的private */
device_del(&evdev->dev);
evdev_cleanup(evdev); /* 对应于evdev_install_chrdev */
input_unregister_handle(handle);
put_device(&evdev->dev);
}
3.对一个设备驱动层发送过来的事件打包
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private; /* 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();
/* 如果该evdev有个专用的client,那么就将事件发给它如果该evdev不存在专用的client,那个就把该事件发送给evdev上client_list链表上所有的client */
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event); /* 打包数据 */
rcu_read_unlock();
/* 唤醒等到队列上的进程,注意这里是唤醒,肯定有别的地方定义(connect)和让它睡眠(read),注意我会标记的 */
wake_up_interruptible(&evdev->wait);
}
发送数据给client函数
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
/*
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
wake_lock_timeout(&client->wake_lock, 5 * HZ);
client->buffer[client->head++] = *event; /* 把键值写入client缓冲队列中,方便app层读(app层可能一次读多个输入信息) */
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
if (event->type == EV_SYN)
kill_fasync(&client->fasync, SIGIO, POLL_IN); /* 发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数*/
}
4.键值的缓存,方便read,ioctl函数操作。5.应用层的调用接口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
};
操作函数比较多,我们主要分析连个最常用的,open,和read
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE; /* 如果是event0,对于evdev设备来说,次设备号当然是0 */
int error;
/* 如果大于32,说明超出了evdev能够容纳的最大输入设备个数 */
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
/* 由于evdev中能容纳32个输入设备,因此通过设备号event0中的0定位到是要处理的是哪一个输入设备,evdev_table中的内容在输入设备驱动注册时通过evdev_connect填充 */
evdev = evdev_table[i];
/* 判断是否设备接口存在 */
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
/* 存在则分配evdev中的client来处理event*,同一个文件打开n次要分配n个client */
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
/* 初始化上面新分配的client */
spin_lock_init(&client->buffer_lock);
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 = evdev; /* client和具体的event绑定 */
evdev_attach_client(evdev, client); /* 把clinet添加到evdev的client_list链表末尾 */
error = evdev_open_device(evdev); /* 将evdev设置为打开状态,下面分析 */
if (error)
goto err_free_client;
file->private_data = client; /* 将文件的私有数据指针指向该client */
nonseekable_open(inode, file); /* 给该inode设置不能使用seek的属性(指定位置读写) */
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
open_device
static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
/* evdev->exist在connevt里面职位1的 */
if (!evdev->exist)
retval = -ENODEV;
/* 第一次打开的话要打开设备文件,其它时候open做打开计数 */
else if (!evdev->open++) {
/* 这个打开设备的是再核心层定义的,其实它也是一个假的函数,又要调用设备驱动层的open函数 */
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
核心层的open函数
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
*
* This function should be called by input handlers when they
* want to start receive events from given input device.
*/
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函数在设备驱动层,当然设备驱动层也可以不实现这个函数 */
/* 如果没有在设备驱动层定义open函数,不会执行执行到open函数,open进行自减操作,表示没有调用过open,这个值主要是为了close中判断open为0时释放资源使用。 */
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;
int retval;
if (count < input_event_size()) /* 熟读的数据长度至少要满足一个input_event的大小 */
return -EINVAL;
/* 当client缓冲区无数据;evdev不存在;文件非阻塞打开,那个read直接返回错误 */
if (client->head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/* 当client里面没有数据时,将应用程序请到evdev->wait等待队列休息,还记得在哪唤醒的吗(evdev_event的最后一行) */
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
/* 当要读取的数据大于struct input_event且client里面的buffer有数据,则把数据拷贝到用户空间*/
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event)) /* 对copy_to_user的封装 */
return -EFAULT;
retval += input_event_size();
}
return retval;
}
查看有没有数据,有的话返回1,且*event参数会把数据带出来一包
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail; /* 头不等于尾说明有数据 */
if (have_event) {
*event = client->buffer[client->tail++]; /* 读数据到*event,每次只能读一个包 */
client->tail &= EVDEV_BUFFER_SIZE - 1; /* 么次读完一个包,尾指针都要移动 */
if (client->head == client->tail)
wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
事件驱动层就分析道这里,虽然不由全部函数分析。但主要的都了解了,其它用到来再学。