Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析


概述

  • input子系统就是管理输入的子系统,和Linux其他子系统一样,都是Linux内核针对某一类设备而创建的框架。
  • 鼠标、键盘、触摸屏等都属于输入设备,Linux将这些设备的共同特性抽象出来,这就形成了input子系统的框架。
  • Linux内核只需要通过input框架向用户层上报输入事件(如:按键值,坐标等),不需要关心应用层的事情
  • 输入设备本质上就是字符设备,经过input框架后,最终给用户空间提供可以访问的设备节点

input 子系统框架

在这里插入图片描述
Linux Input 子系统框架

硬件输入设备: 最底层具体设备(如:触摸屏、键盘、鼠标等)

内核空间:

  • 驱动层: 输入设备的具体驱动程序。负责将底层的硬件输入转化为统一的事件形式,向input核心层传达。
  • 核心层: 连接驱动层和事件层之间。负责双向提供接口,向下提供驱动层接口,向上提供事件处理的接口。
  • 事件层: 底层的设备抽象出对应的接口提供给应用层。将底层设备的触发的事件通过这个接口传达给应用层。

input 子系统相关结构体介绍

input_dev结构体

input_dev结构体是硬件驱动层,代表一个input设备。
最底层具体设备,都会抽象出一个input_dev结构体。

// include/linux/input.h 
struct input_dev {
    
    
    const char *name;            /* 设备名称 */
    const char *phys;            /* 设备在系统中的路径 */
    const char *uniq;            /* 设备唯一id */
    struct input_id id;          /* input设备id号 */

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; 

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];        /* 设备支持的事件类型,主要有EV_SYNC,EV_KEY,EV_KEY,EV_REL,EV_ABS等*/    
    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)];        /* 支持led事件 */
    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);    /* 事件处理函数,主要是接收用户下发的命令,如点亮led */

    struct input_handle __rcu *grab;        /* 当前占有设备的input_handle */

    spinlock_t event_lock;        /* 事件锁 */
    struct mutex mutex;            /* 互斥体 */

    unsigned int users;        /* 打开该设备的用户数量(input_handle) */
    bool going_away;            /* 标记正在销毁的设备 */

    struct device dev;        /* 一般设备 */

    struct list_head    h_list;        /* 设备所支持的input handle */
    struct list_head    node;        /* 用于将此input_dev连接到input_dev_list */

    unsigned int num_vals;        /* 当前帧中排队的值数 */
    unsigned int max_vals;        /*  队列最大的帧数*/
    struct input_value *vals;    /*  当前帧中排队的数组*/

    bool devres_managed;        /* 表示设备被devres 框架管理,不需要明确取消和释放*/
};

Linux下设备支持的事件类型:

编码 事件描述
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件(鼠标,键盘等)
EV_REL 0x02 相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS 0x03 绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC 0x04 其它
EV_SW 0x05 开关
EV_LED 0x11 按键/设备灯
EV_SND 0x12 声音/警报
EV_REP 0x14 重复
EV_FF 0x15 力反馈
EV_PWR 0x16 电源
EV_FF_STATUS 0x17 力反馈状态
EV_MAX 0x1f 事件类型最大个数和提供位掩码支持

Linux下定义按键值(列出部分):

#define KEY_RESERVED        0
#define KEY_ESC         1
#define KEY_1           2
#define KEY_2           3
#define KEY_3           4
#define KEY_4           5
#define KEY_5           6
#define KEY_6           7
#define KEY_7           8
#define KEY_8           9
#define KEY_9           10
#define KEY_0           11

input_handler结构体

input_handler结构体是事件层,代表一个事件处理器。

// include/linux/input.h 
struct input_handler {
    
    

    void *private;        /* 存放handle数据 */

    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);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    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;        /* input_dev匹配用的id */

    struct list_head    h_list;    /* 用于链接和handler相关的handle,input_dev与input_handler配对之后就会生成一个input_handle结构 */
    struct list_head    node;    /* 用于将该handler链入input_handler_list,链接所有注册到内核的所有注册到内核的事件处理器 */
};

input_handle结构体

input_handle结构体属于核心层,代表一个配对的input设备与input事件处理器。

// include/linux/input.h 
struct input_handle {
    
    

    void *private;                /* 数据指针 */

    int open;                    /* 打开标志,每个input_handle 打开后才能操作 */
    const char *name;            /* 设备名称 */

    struct input_dev *dev;            /* 指向所属的input_dev */
    struct input_handler *handler;    /* 指向所属的input_handler */

    struct list_head    d_node;        /* 用于链入所指向的input_dev的handle链表 */
    struct list_head    h_node;        /* 用于链入所指向的input_handler的handle链表 */
};

以上三个结构体在input子系统扮演重要角色,从结构体成员能看出有两个链表。
input_dev和input_handler链表。三张表连接图如下。

在这里插入图片描述
input-dev 、 input-handler、input-handle三结构体关系

input_dev和input_handler各自内部链表图
在这里插入图片描述

  • input_handler 通过全局的input_handler_list链接在一起。

  • input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。

  • 通过input_dev 和input_handler就可以找到input_handle在设备注册和事件处理器,注册的时候都要进行配对工作,配对后就会实现链接。

  • 通过input_handle也可以找到input_dev和input_handler。input_handle是用来关联input_dev和input_handler的。


Evdev事件相关结构体

1、Evdev(字符设备事件)结构体

/* drivers/input/evdev.c */
struct evdev {
    
    
        int open;    /* 设备被打开的计数 */
        struct input_handle handle;  /* 关联的input_handle */ 
        wait_queue_head_t wait;  /* 等待队列,当前进程读取设备,没有事件产生时,
进程就会sleep */
        struct evdev_client __rcu *grab;  /* event响应 */
struct list_head client_list;  /* evdev_client链表,说明evdev设备可以处理多个 evdev _client,可以有多个进程访问evdev设备 */
        spinlock_t client_lock;
        struct mutex mutex;
        struct device dev;
        struct cdev cdev;
        bool exist;   /* 设备存在判断 */
};

2、evdev_client(字符设备事件响应)结构体

struct evdev_client {
    
    
    unsigned int head;                 /* 动态索引,每加入一个event到buffer中,head++ */
    unsigned int tail;                /* 动态索引,每取出一个buffer中到event,tail++ */
    unsigned int packet_head;         /* 数据包头部 */
    spinlock_t buffer_lock; 
    struct fasync_struct *fasync;     /* 异步通知函数 */
    struct evdev *evdev;
    struct list_head node;            /* evdev_client链表项 */
    int clkid;
    bool revoked;
    unsigned int bufsize;
    struct input_event buffer[];        /* 用来存放input_dev事件缓冲区 */
};

3、evdev_handler(事件处理函数)结构体

/* drivers/input/input.c */
static struct input_handler evdev_handler = {
    
    
        .event        = evdev_event,   /* 事件处理函数, */  
        .events    = evdev_events,  /* 事件处理函数, */
        .connect    = evdev_connect, /* 连接函数,将事件处理和输入设备联系起来 */
        .disconnect    = evdev_disconnect,  /* 断开该链接 */
        .legacy_minors    = true,
        .minor        = EVDEV_MINOR_BASE,
        .name        = "evdev", /* handler名称 */
        .id_table    = evdev_ids, /* 断开该链接 */
};

input_event结构体(标准按键编码信息)

/* drivers/input/evdev.c */
struct input_event {
    
                                                                
    struct timeval time;   /* 事件发生的时间  */                                
    __u16 type;             /* 事件类型 */                                      
    __u16 code;             /* 事件码 */                                        
    __s32 value;            /* 事件值 */                                        
}; 

设备相关信息结构体

/* include/uapi/linux/input.h */
struct input_id {
    
      
    __u16 bustype;  /* 总线类型 */  
    __u16 vendor;  /* 生产厂商 */  
    __u16 product;  /* 产品类型 */ 
    __u16 version;  /* 版本 */
 };
/* include/uapi/linux/input.h */
struct input_device_id {
    
    

        kernel_ulong_t flags;

        __u16 bustype;  /* 总线类型 */
        __u16 vendor;  /* 生产厂商 */
        __u16 product;  /* 产品类型 */
        __u16 version;  /* 版本 */

        kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1];

        kernel_ulong_t driver_info;
};

猜你喜欢

转载自blog.csdn.net/weixin_43564241/article/details/130436591