在input子系统中事件处理层有很多evdev,joydev,evbug,mousedev。但是他们的实现基本上是一样的,对于触摸屏来说,事件处理器是evdev,现在就来分析一下evdev的代码。
首先还是evdev的入口和出口
static int __init evdev_init(void)
{
/*将evdev_handler注册到系统中*/
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是事件处理器,给用户空间提供接口,所以必须对它进行注册。
static struct input_handler evdev_handler = {
.event = evdev_event,//当有事件来的时候调用,把事件放入input_event数组,为用户空间读写事件做准备
.connect = evdev_connect,//在input_dev和input_handler注册过程最终都会调用这个函数,完成handle的注册
.disconnect = evdev_disconnect,//在input_dev和input_handler注销过程最终会调用这个函数,完成handle的注销
.fops = &evdev_fops,//对事件的操作函数集
.minor = EVDEV_MINOR_BASE,//次设备号基数
.name = "evdev",
.id_table = evdev_ids,
};
这里有一个id_table成员,这个成员在注册input_dev和input_handler的里面,都需要通过 evdev_ids来筛选出同种类型的ID和比较支持的事件,然后建立device和handler关联。
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices *///适合所有的类型的设备
{ }, /* Terminating zero entry */
};
对于event和connect函数在我的事件上报过程和input重要结构体分析的日志中有讲,这里不再分析。
现在主要来看提供给用户空间的操作函数集,这有点像标准的字符设备驱动
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,
};
先来看两个结构体
/*在进程打开event0设备的时候调用evdev的open方法,在open中创建和初始化*/
struct evdev_client {
unsigned int head;//针对buffer数组的索引
unsigned int tail;//针对buffer数组的索引,当head和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 fasync_struct *fasync;//异步通知函数
struct evdev *evdev;//evdev设备
struct list_head node;//evdev_client链表项
unsigned int bufsize;
struct input_event buffer[];//一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type)编码(code)值(value)
};
/*evdev结构体再配对成功的时候生成,由handler->connect生成*/
struct evdev {
int open;//打开引用计数
int minor;//次设备号
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;
};
同字符设备一样先来看open函数
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;
evdev = evdev_table[i];//得到evdev设备结构,每次调用evdev_connect配对成功后都会把分配的evdev结构以minor为索引保存在evdev_table中
if (evdev)
get_device(&evdev->dev);//增加device引用计数
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->evdev = evdev;//使用户端与evdev设备结构关联起来
evdev_attach_client(evdev, client);//把client链接到evdev的client链表上
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);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
evdev_attach_client就是把client关联到evdev->client_list的链表上
static void evdev_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);
}
evdev_open_device主要通过evdev的exist标志判断是否已经获得了evdev结构体
static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist)//判断evdev结构体是否存在,在evdev_connect中初始化成员为1
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
真正的打开是在input.c中 input_open_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) {//判断设备是否在open期间被注销
retval = -ENODEV;
goto out;
}
handle->open++;//handle的打开计数加一
if (!dev->users++ && dev->open)//如果输入设备没有进程引用,并定义了open方法,就调用open方法
retval = dev->open(dev);
if (retval) {//retval=1,说明没有打开成功
dev->users--;
if (!--handle->open) {//说明有其它进程打开了这个handle
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex);
return retval;
}
到这里,如果input_dev有定义open方法,打开函数最终会调用到input_dev下面的open函数,否者只是简单的增加打开引用计数。
这里read函数也在事件上报过程的那片日志有分析到,我们来看write函数。
static ssize_t evdev_write(struct file *file, const 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 = 0;
if (count < input_event_size())//write操作的数据大小至少要大于一个事件的大小
return -EINVAL;
retval = mutex_lock_interruptible(&evdev->mutex);//上锁
if (retval)
return retval;
if (!evdev->exist) {
retval = -ENODEV;
goto out;
}
do {
if (input_event_from_user(buffer + retval, &event)) {//得到用户空间的数据
retval = -EFAULT;
goto out;
}
retval += input_event_size();//累加事件长度,计算得到用户空间的数据长度
input_inject_event(&evdev->handle,
event.type, event.code, event.value);//从input handler发送一个input事件
} while (retval + input_event_size() <= count);
out:
mutex_unlock(&evdev->mutex);
return retval;
}
看看发送事件的实现
void input_inject_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_dev *dev = handle->dev;
struct input_handle *grab;
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
rcu_read_lock();
grab = rcu_dereference(dev->grab);
if (!grab || grab == handle)
input_handle_event(dev, type, code, value);
rcu_read_unlock();
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_inject_event);
input_inject_event->input_handle_event->input_pass_event->handler下的event
最终还是由evdev中的event完成的发送.所以这个事件就不是有具体设备驱动发出的,而是我们从用户空间直接写一个事件到evdev中,然后通过input core最终调用event发送,一般提供给测试用的接口。
接下来看poll的实现,允许进程决定是否可以完成非阻塞的操作
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
unsigned int mask;
poll_wait(file, &evdev->wait, wait);//添加等待队列到evdev->wait
mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;//设置位掩码
if (client->packet_head != client->tail)
mask |= POLLIN | POLLRDNORM;
return mask;
}
ioctl的实现
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
}
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist) {
retval = -ENODEV;
goto out;
}
retval = evdev_do_ioctl(file, cmd, p, compat_mode);
out:
mutex_unlock(&evdev->mutex);
return retval;
}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
unsigned int size;
int error;
/* First we check for fixed-length commands */
switch (cmd) {
case EVIOCGVERSION:
return put_user(EV_VERSION, ip);
case EVIOCGID:
if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
return -EFAULT;
return 0;
case EVIOCGREP:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (put_user(dev->rep[REP_DELAY], ip))
return -EFAULT;
if (put_user(dev->rep[REP_PERIOD], ip + 1))
return -EFAULT;
return 0;
case EVIOCSREP:
if (!test_bit(EV_REP, dev->evbit))
return -ENOSYS;
if (get_user(u, ip))
return -EFAULT;
if (get_user(v, ip + 1))
return -EFAULT;
input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
return 0;
case EVIOCRMFF:
return input_ff_erase(dev, (int)(unsigned long) p, file);
case EVIOCGEFFECTS:
i = test_bit(EV_FF, dev->evbit) ?
dev->ff->max_effects : 0;
if (put_user(i, ip))
return -EFAULT;
return 0;
case EVIOCGRAB:
if (p)
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);
case EVIOCGKEYCODE:
return evdev_handle_get_keycode(dev, p);
case EVIOCSKEYCODE:
return evdev_handle_set_keycode(dev, p);
case EVIOCGKEYCODE_V2:
return evdev_handle_get_keycode_v2(dev, p);
case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
}
size = _IOC_SIZE(cmd);
/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {
case EVIOCGPROP(0):
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
size, p, compat_mode);
case EVIOCGKEY(0):
return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
case EVIOCGLED(0):
return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
case EVIOCGSND(0):
return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
case EVIOCGSW(0):
return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
case EVIOCGNAME(0):
return str_to_user(dev->name, size, p);
case EVIOCGPHYS(0):
return str_to_user(dev->phys, size, p);
case EVIOCGUNIQ(0):
return str_to_user(dev->uniq, size, p);
case EVIOC_MASK_SIZE(EVIOCSFF):
if (input_ff_effect_from_user(p, size, &effect))
return -EFAULT;
error = input_ff_upload(dev, &effect, file);
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;
return error;
}
/* Multi-number variable-length handlers */
if (_IOC_TYPE(cmd) != 'E')
return -EINVAL;
if (_IOC_DIR(cmd) == _IOC_READ) {
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
return handle_eviocgbit(dev,
_IOC_NR(cmd) & EV_MAX, size,
p, compat_mode);
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
if (!dev->absinfo)
return -EINVAL;
t = _IOC_NR(cmd) & ABS_MAX;
abs = dev->absinfo[t];
if (copy_to_user(p, &abs, min_t(size_t,
size, sizeof(struct input_absinfo))))
return -EFAULT;
return 0;
}
}
if (_IOC_DIR(cmd) == _IOC_WRITE) {
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
if (!dev->absinfo)
return -EINVAL;
t = _IOC_NR(cmd) & ABS_MAX;
if (copy_from_user(&abs, p, min_t(size_t,
size, sizeof(struct input_absinfo))))
return -EFAULT;
if (size < sizeof(struct input_absinfo))
abs.resolution = 0;
/* We can't change number of reserved MT slots */
if (t == ABS_MT_SLOT)
return -EINVAL;
/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
* of event.
*/
spin_lock_irq(&dev->event_lock);
dev->absinfo[t] = abs;
spin_unlock_irq(&dev->event_lock);
return 0;
}
}
return -EINVAL;
}
ioctl主要通过cmd的不同来完成不同的操作,这里不再详细分析.
版权声明:本文为zhengsheng23原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。