输入事件层目前系统帮我们区分了三种,

通用各种类型输入类设备的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;
}

标签: linux, dev, handle, input, struct, client, evdev, 事件驱动

相关文章推荐

添加新评论,含*的栏目为必填