evdev输入事件驱动分析

evdev[输入事件驱动],为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。

evdev从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对sys文件系统的操作,就能够达到处理事件的能力。

1. evdev的初始化
evdev以模块的方式被组织在内核中,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识evdev的存在。
evdev模块定义在/drivers/input/evdev.c文件中,该模块的初始化函数是evdev_init()。在初始化函数中注册了一个evdev_handler结构体,用来对一些通用的抽象事件进行统一处理,函数的代码如下

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);

第03行,调用input_register_handler()函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如下:

static struct input_handler evdev_handler = {
    .event      = evdev_event,
    .connect    = evdev_connect,
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,
    .minor      = EVDEV_MINOR_BASE,
    .name       = "evdev",
    .id_table   = evdev_ids,
    };

第06行,定义了minor为EVDEV_MINOR_BASE(64)。因为一个handler可以处理32个设备,所以evdev_handler所能处理的设备文件范围为(13,64)~(13,64+32),其中13是所有输入设备的主设备号。

第08行,定义了id_table结构。回忆前面几节的内容,由input_attach_handler()函数可知,input_dev与handler匹配成功的关键,在于handler中的blacklist和id_talbe.。Evdev_handler只定义了id_table,其定义如下:

static const struct input_device_id evdev_ids[] = { 
    {iver_info = 1 },   /* Matches all devices */      
        { },            /* Terminating zero entry */      
};

evdev_ids没有定义flags,也没有定义匹配属性值。这个evdev_ids的意思就是:evdev_handler可以匹配所有input_dev设备,也就是所有的input_dev发出的事件,都可以由evdev_handler来处理。
先来看两个结构体struct evdev, struct evdev_client:

struct evdev {
    int open;
    int minor;   //在evdev_table中的索引
    struct input_handle handle;  //连接input_dev和input_handler
    wait_queue_head_t wait;
    struct evdev_client __rcu *grab;   // 用户每调用一次open,将创建一个evdev_client
    struct list_head client_list;
    spinlock_t client_lock; /* protects client_list */
    struct mutex mutex;
    struct device dev;
    bool exist;
};

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;
    struct input_event buffer[];  //存放所有这个设备产生的input_event,由evdev_event写入
};

另外,从前面的分析可以知道,匹配成功之后会调用handler->connect()函数,对该函数的介绍如下:

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;
    /*由于EVDEV_MINORS等于32,说明evdev可以同时有32个设备和它配对,evdev_table的下标minor并不是次设备号*/
    for (minor = 0; minor < EVDEV_MINORS; minor++)
        if (!evdev_table[minor])
            break;
    /*说明32个设备全部被占用了,链接失败*/
    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);
    /* 设置evdev中device的名字,它也将出现在/class/input/下,但是他和input_dev下面的device是有区别的,
     * evdev配对以后的虚拟设备结构,没有对应的硬件,但是可以通过它找到相应的硬件*/
    dev_set_name(&evdev->dev, "event%d", minor);
    evdev->exist = true;
    evdev->minor = minor;

    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, EVDEV_MINOR_BASE + minor);//minor不是真正的次设备号,还要加上EVDEV_MINOR_BASE
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;//配对生成新的device,父设备是与他相关联的input_dev
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);
    error = input_register_handle(&evdev->handle);//注册handle结构体
    if (error)
        goto err_free_evdev;
    error = evdev_install_chrdev(evdev);//把evdev结构保存到evdev_table中,这个数组以minor为索引
    if (error)
        goto err_unregister_handle;
    error = device_add(&evdev->dev);//将evdev下面的device注册到linux设备模型中
    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;
}

下面对该函数进行简要的分析。

第04~06行,声明了一些必要的局部变量。

第08~14行,for循环中的EVDEV_MINORS定义为32,表示evdev_handler所表示的32个设备文件。evdev_talbe是一个struct evdev类型的数组,struct evdev是模块使用的封装结构,与具体的输入设备有关。第09行,这一段代码的在evdev_talbe找到为空的那一项,当找到为空的一项,便结束for循环。这时,minor就是数组中第一项为空的序号。第11到14行,如果没有空闲的表项,则退出。

第16~18行,分配一个struct evdev的空间,如果分配失败,则退出。

第19~22行,对分配的evdev结构进行初始化,主要对链表、互斥锁和等待队列做必要的初始化。在evdev中,封装了一个handle结构,。
第25行,对evdev命一个名字,这个设备的名字形如eventx,例如event1、event2和event3等。最大有32个设备,这个设备将在/dev/input/目录下显示。

第29~32行,对evdev进行必要的初始化。其中,主要对handle进行初始化,这些初始化的目的是使input_dev和input_handler联系起来。

第33~36行,在设备驱动模型中注册一个evdev->dev的设备,并初始化一个evdev->dev的设备。这里,使evdev->dev所属的类指向input_class。这样在/sysfs中创建的设备目录就会在/sys/class/input/下显示。

第38行,调用input_register_handle()函数注册一个input_handle结构体。

第41行,注册handle,如果成功,那么调用evdev_install_chrdev将evdev_table的minor项指向evdev.。

第44行,将evdev->device注册到sysfs文件系统中。

2. evdev 事情处理流程
用户程序通过输入子系统创建的设备结点函数open()、read()和write()等,打开和读写输入设备。
当用户每调用open()函数,就要创建一个evdev_client,并加入到client_list链表中。当input_dev产生事件时,evdev_event函数将把此input_event放入evdev->client_list链表中的每个evdev_client的buffer中。它们的关系如1-5图所示:
evdev输入事件驱动分析_第1张图片

创建的设备结点显示在/dev/input/目录下,由eventx表示。
1、evdev_open()函数
对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。在evdev_handler中定义了一个fops集合,被赋值为evdev_fops的指针。evdev_fops就是设备结点的操作集,其定义代码如下

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,
};

evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码open(“/dev/input/event1”,O_RDONLY)函数打开设备结点时,会调用evdev_fops中的evdev_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;
    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;
}

第20~36行,分配并初始化一个client结构体,并将它和evdev关联起来。关联的内容是,将client->evdev指向它所表示的evdev,调用evdev_attach_client()将client挂到evdev->client_list上。
第34行,将client赋给file的private_data。

在evdev中,这个操作集就是evdev_fops,对应的open()函数如下:

static int evdev_open(struct inode *inode, struct file *file)

第31行,调用evdev_open_device()函数,打开输入设备。该函数的具体功能将在下面详细介绍。

2 、evdev_open_device()函数
用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用input_open_device()函数打开设备。

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这个函数完成的。

3.input_open_device()函数
在这个函数中,递增handle的打开计数。如果是第一次打开,则调用input_dev的open()函数。

int input_open_device(struct input_handle *handle)
{      
     ...........
    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();
        }
    } 
 .........
}

4、evdev_write( ) 函数
我们来看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_event完成发送.所以这个事件就不是有具体设备驱动发出的,而是我们从用户空间直接写一个事件到evdev中,然后通过input core最终调用event发送,一般提供给测试用的接口。

5、evdev_poll()函数
接下来看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;
}

5、evdev_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)
               {......}

ioctl主要通过cmd的不同来完成不同的操作,这里不再详细分析.

总结
  分析了整个输入子系统的架构。Linux设备驱动采用了分层的模式,从最下层的设备模型到设备、驱动、总线再到input子系统最后到input device。这样的分层结构使得最上层的驱动不必关心下层是怎么实现的,而下层驱动又为多种型号同样功能的驱动提供了一个统一的接口。

你可能感兴趣的:(drive)