Input子系统框架之kernel层(Kernel-3.18)

一、Linux Input 子系统框架

本文是基于kernel-3.18版本分析的。输入(Input)子系统是分层架构的,总共分为5 层,从上到下分别是:用户空间层(User Space)事件处理层(Event Handler)、输入子系统核心层(Input Core)、硬件驱动层(Input Driver) 、硬件设备层(Hardware)。

驱动根据CORE提供的接口,向上报告发生的按键动作。然后CORE根据驱动的类型,分派这个报告给对应的事件处理层进行处理。事件处理层把数据变化反应到设备模型的文件中(事件缓冲区)。并通知在这些设备模型文件上等待的进程。

input子系统框架:
在这里插入图片描述
(1) “硬件驱动层”负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,比如你的设备是触摸输入设备,还是鼠标输入设备,还是键盘输入设备,这些不同的设备,自然有不同的硬件操作,驱动工程师往往只需要完成这层的代码编写。

(2) “输入子系统核心层”是链接其他两层之间的纽带与桥梁,向下提供硬件驱动层的接口,向上提供事件处理层的接口。

(3) “事件处理层” 负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。

各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value), Input 子系统支持的所有事件都定义在 input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间。

二、Input 主要通用数据结构

2.1、input_dev

输入设备 input_dev,这是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构

[->input.h]
struct input_dev {
   
    const char *name;    //输入设备的名称  
    const char *phys;    //输入设备节点名称  
    const char *uniq;    //指定唯一的ID号,就像MAC地址一样
    struct input_id id;    //输入设备标识ID,用于和事件处理层进行匹配

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];    //位图,记录设备支持的事件类型
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];    //位图,记录设备支持的按键类型      
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];    //位图,记录设备支持的相对坐标  
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];    //位图,记录设备支持的绝对坐标
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];    //位图,记录设备支持的其他功能
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];    //位图,记录设备支持的指示灯
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];   //位图,记录设备支持的声音或警报
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];     //位图,记录设备支持的作用力功能  
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];     //位图,记录设备支持的开关功能

    unsigned int hint_events_per_packet;

    unsigned int keycodemax;      //设备支持的最大按键值个数  
    unsigned int keycodesize;    //每个按键的字节大小
    void *keycode;      //指向按键池,即指向按键值数组首地址

    int (*setkeycode)(struct input_dev *dev,
              const struct input_keymap_entry *ke,
              unsigned int *old_keycode);    //修改按键值
    int (*getkeycode)(struct input_dev *dev,
              struct input_keymap_entry *ke);   //获取按键值

    struct ff_device *ff;     //用于强制更新输入设备的部分内容  

    unsigned int repeat_key;    //重复按键的键值
    struct timer_list timer;   //设置当有连击时的延时定时器  

    int rep[REP_CNT];

    struct input_mt *mt;

    struct input_absinfo *absinfo;

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];    //位图,按键的状态  
    unsigned long led[BITS_TO_LONGS(LED_CNT)];    //位图,led的状态  
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];    //位图,声音的状态  
    unsigned long sw[BITS_TO_LONGS(SW_CNT)];   //位图,开关的状态  

    int (*open)(struct input_dev *dev);
    void (*close)(struct input_dev *dev);
    int (*flush)(struct input_dev *dev, struct file *file);
    int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

    struct input_handle __rcu *grab; //类似私有指针,可以直接访问到事件处理接口event  

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;

    struct list_head    h_list; //该链表头用于链接此设备所关联的input_handle   
    struct list_head    node; //用于将此设备链接到input_dev_list(链接了所有注册到内核的事件处理器)  

    unsigned int num_vals;
    unsigned int max_vals;
    struct input_value *vals;

    bool devres_managed;
};

2.2、input_handler

input_handler 这是事件处理器的数据结构,代表一个事件处理器

[->input.h]
struct input_handler {
   

    void *private;
    /* 当事件处理器接收到来自Input设备传来的事件时调用的处理函数,
        event、events用于处理事件 */  
    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    void (*events)(struct input_handle *handle,
               const struct input_value *vals, unsigned int count);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    /* 比较 device's id with handler's id_table ,匹配device and handler*/
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    /* connect用于建立intput_handler和input_dev的联系,
       当一个Input设备注册到内核的时候被调用,将输入设备与事件处理器联结起来 */
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    /* disconnect用于解除handler和device的联系 */
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);

    bool legacy_minors;
    int minor;    //次设备号
    const char *name;    //次设备号

    const struct input_device_id *id_table;    //用于和device匹配 ,这个是事件处理器所支持的input设备

    //这个链表用来链接他所支持的input_handle结构,input_dev与input_handler配对之后就会生成一个input_handle结构
    struct list_head    h_list;

    //链接到input_handler_list,这个链表链接了所有注册到内核的事件处理器
    struct list_head    node;
};

2.3、input_handle

[->input.h]
struct input_handle {
   
    /*
    每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,
    注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。   
    */

    void *private;
    /*
    打开标志,每个input_handle 打开后才能操作,
    这个一般通过事件处理器的open方法间接设置  
    */  

    int open;
    const char *name;
    /* 指向Input_dev结构实体 */
    struct input_dev *dev;
    /* 指向Input_Hander结构实体 */
    struct input_handler *handler;
    /* input_handle通过d_node连接到了input_dev上的h_list链表上 */
    struct list_head    d_node;
    /* input_handle通过h_node连接到了input_handler的h_list链表上 */
    struct list_head    h_node;
};

2.4、三个数据结构之间的关系

input_dev: 是硬件驱动层,代表一个input设备。 input_handler: 是事件处理层,代表一个事件处理器。 input_handle: 属于核心层,代表一个配对的input_dev与input_handler

input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。注:(稍后详细分析)

[->input.c]
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

int input_register_device(struct input_dev *dev)
{
   
    struct input_devres *devres = NULL;
    struct input_handler *handler;
    unsigned int packet_size;
    const char *path;
    int error;

  ......
    list_add_tail(&dev->node, &input_dev_list);

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

   ......
}

input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)注:(稍后详细分析)

[->input.c]
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

int input_register_handler(struct input_handler *handler)
{
   
    struct input_dev *dev;
    int error;
    ......
    list_add_tail(&handler->node, &input_handler_list);

    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
    ......
    return 0;
}

input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle在设备注册和事件处理器,注册的时候都要进行配对工作(input_match_device),配对后就会实现链接(handler->connect)通过input_handle也可以找到input_dev和input_handler。注:(稍后详细分析)

[->input.c]
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
   
    const struct input_device_id *id;
    int error;

    id = input_match_device(handler, dev);
    ......
    error = handler->connect(handler, dev, id);
    ......
    return error;
}

我们可以看到,input_device和input_handler中都有一个h_list,而input_handle拥有指向input_dev和input_handler的指针,也就是说input_handle是用来关联input_dev和input_handler的。 那么为什么一个input_device和input_handler中拥有的是h_list而不是一个handle呢?因为一个device可能对应多个handler,而一个handler也不能只处理一个device,比如说一个鼠标,它可以对应even handler,也可以对应mouse handler,因此当其注册时与系统中的handler进行匹配,就有可能产生两个实例,一个是evdev,另一个是mousedev,而任何一个实例中都只有一个handle。至于以何种方式来传递事件,就由用户程序打开哪个实例来决定。后面一个情况很容易理解,一个事件驱动不能只为一个甚至一种设备服务,系统中可能有多种设备都能使用这类handler,比如event handler就可以匹配所有的设备。在input子系统中,有8种事件驱动,每种事件驱动最多可以对应32个设备,因此dev实例总数最多可以达到256个。
在这里插入图片描述

三、Input 核心层(Input.c)

这一节主要介绍核心层的初始化,input_device、input_handle、input_handler之间的关系(稍后回头看更佳)。 总体概览图:
在这里插入图片描述

3.1、输入核心层:初始化

首先从驱动”入口函数”开始查看

[->input.c]
static int __init input_init(void)
{
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值