美文网首页
input subsystem简要分析

input subsystem简要分析

作者: 季风落地窗 | 来源:发表于2014-11-19 10:47 被阅读129次

    Input Subsystem 系统主要由 drivers/input/input.c input_register_device() 和 input_register_handler() 两个函数实现。
    Handler 组件通过 input_register_handler 函数将逻辑设备操作结构体注册到 input_handler_list 链表中;
    device 组件通过 input_register_device 组件将直接对具体硬件的操作结构体注册到 input_dev_list 链表中

    struct input_dev {
        ...
        struct device dev;
        ...
        struct list_head    h_list; //与 input_dev关联的 input_headle 的链表头
        struct list_head    node;   //用于将本设备连接到 input_dev_list
    };
    
    struct input_handler {
        ...
        const struct file_operations *fops;
        ...
        const struct input_device_id *id_table;//用户和设备匹配, 这个是事件处理器所支持的input设备
        ...
        struct list_head    h_list; //所支持的 input_headle 结构的链表的表头
        struct list_head    node;   //链入全局链表input_handler_list, 它连接了所有注册到内核的handler 
    };
    
    struct input_handle {
        ...
        struct input_dev *dev;              //关联的input_dev结构
        struct input_handler *handler;      //关联的input_headler结构
    
        struct list_head    d_node;         //input_headle通过d_node连接到了input_dev的h_list上
        struct list_head    h_node;         //input_headle通过h_node连接到了input_headler的h_list上
    };
    

    input_dev / input_headler / input_headle 关系:
    input_dev 是硬件驱动层,代表一个input设备
    input_headler 是事件处理层,代表一个事件处理器
    input_headle 个人认为属于核心层, 代表一个配对的设备与事件处理器
    那么input_dev 和input_handler是如何跟input子系统总线联系起来的呢?

    (linux)/drivers/input/input.c

    input_init()
        class_register(&input_class);                       //创建类
        register_chrdev(INPUT_MAJOR, "input", &input_fops); //创建类下的设备, 并由此可知   file_operations结构体变量为input_fops
    
    static const struct file_operations input_fops = {
        .owner = THIS_MODULE,
        .open = input_open_file,
    };
    

    //该结构体只填充了 .open 方法, read / write / poll 等方法没有填充, 那么肯定是由open做了些"工作"来间接实现了其他的功能, 那么就让我们来看看 input_open_file的内部

    static int input_open_file(struct inode *inode, struct file *file)
    {
        struct input_handler *handler;
        ...
        //以次设备号为下标,拿到一个 input_handler, 在input_handler内部有个file_operations成员
        handler = input_table[iminor(inode) >> 5];
        ...
        //将input_handler的file_operations成员拿出
        new_fops = fops_get(handler->fops);
        ...
        //将原来的fops保存起来, 将新的fops给 file->f_op
        old_fops = file->f_op;
        file->f_op = new_fops;
        //从此file->f_op都是 新的fops
        err = new_fops->open(inode, file);
        ...
        return err;
    }
    

    以上程序的关键是 input_table[], 那么input_table是怎么来的呢?

    insmod时候调用

    //注册input_handler

    input_register_handler()
        input_table[handler->minor >> 5] = handler;     //那么是谁调用了input_register_handler 了呢?
        //将handler加入 input_handler_list链表
        list_add_tail(&handler->node, &input_handler_list);
        //每个 input_handler 都调用 input_attach_handler, 遍历headler 查看是否支持dev
        list_for_each_entry(handler, &input_handler_list, node)
           //根据input_handler 的id_table 判断能否支持该input_device
           input_attach_handler(dev, handler);          
    

    插入设备调用

    //注册输入设备

    input_register_device()
        //将每个 input_device 都放入链表
        list_add_tail(&dev->node, &input_dev_list);
        //每个 input_handler 都调用input_attach_handler, 遍历handler, 查看能否支持dev
        list_for_each_entry(handler, &input_handler_list, node)
           //根据input_handler 的id_table 判断能够支持该input_device
           input_attach_handler(dev, handler);          
    

    以上两个函数非常对称, 不管先调用哪个函数都最后调用了 input_attach_handler, 那么input_attach_handler 做了什么工作呢?

    static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
        id = input_match_device(handler->id_table, dev);//遍历id_table, 看找有没有与dev匹配的
        if (!id)
            return -ENODEV;
    
        error = handler->connect(handler, dev, id);
    

    它先比较了handler层的 input_handler的id_table和device层的input_dev, 如果handler能够支持input_dev 则调用handler的connect将device和handler"连接"起来

    //那么他们是怎么建立"连接"的呢? 主要看 handler 里的 connect 函数 (这里以evdev为例来说明)

    evdev_connect
    1. 申请一个  handle 结构体
    2. 设置
        handle.dev = input_dev;
        handle.handler = input_handler;
    3. input_dev->h_list = &handle;
        input_hanlder->h_list = &handle;
    

    这样一来
    device层可以通过 input_dev的h_list先找到handle 然后再通过handle的handler找到 input_handler
    handler层可以通过input_handler的h_list先找到handle然后再通过handle的dev找到 input_dev
    两者就靠 handle连接了起来

    从编写代码的过程再捋一遍思绪:
    1. 申请input_dev结构体 input_allocate_device

    2. 设置 input_dev 
       1) 可以产生哪类事件
       2) 产生该类事件中的哪些事件
    
    3. 将input_dev注册到内核
    
    4. 实现业务逻辑代码
        申请中断等...
    

    问题:

    1. input_table 是什么?
    2. 应用层如何获取input_event上报的事件?

    相关文章

      网友评论

          本文标题:input subsystem简要分析

          本文链接:https://www.haomeiwen.com/subject/gfoitttx.html