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

这篇具有很好参考价值的文章主要介绍了Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


概述

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

input 子系统框架

Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析
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链表。三张表连接图如下。

Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析
input-dev 、 input-handler、input-handle三结构体关系

input_dev和input_handler各自内部链表图
Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析

  • 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(事件处理函数)结构体文章来源地址https://www.toymoban.com/news/detail-454274.html

/* 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;
};

到了这里,关于Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux GPIO 和 Pinctrl 子系统的使用(十四)

     个人名片: 🦁作者简介:学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755@qq.com 🦉个人WeChat:Vir2021GKBS 🐼 本文由妄北y原创,首发CSDN 🎊🎊🎊 🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。 专栏导航: 妄北y系列专栏导航: C/C++的基

    2024年04月26日
    浏览(36)
  • 【嵌入式Linux内核驱动】SPI子系统 | 硬件原理 | 应用编程 | 内核驱动 | 总体框架

    1.1 SPI通信协议 SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线 四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select) 同步,全双工 支持总线挂载多设备(一主多从) 1.2 硬件连接 多NSS独立片选方式 菊花

    2024年02月16日
    浏览(67)
  • 【嵌入式Linux学习笔记】platform设备驱动和input子系统

    对于Linux这种庞大的操作系统,代码重用性非常重要,所以需要有相关的机制来提升效率,去除重复无意义的代码,尤其是对于驱动程序,所以就有了platform和INPUT子系统这两种工作机制。 学习视频地址:【正点原子】STM32MP157开发板 platform 驱动框架分为总线、设备和驱动。总

    2024年02月07日
    浏览(60)
  • I.MX6ULL_Linux_驱动篇(45)linux INPUT子系统

    按键、鼠标、键盘、触摸屏等都属于输入(input)设备, Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了 input 框 架,用户只需要负责上报输入事件,比如按键值、坐标等信息, input 核心层负责处理这些

    2024年02月14日
    浏览(40)
  • 【嵌入式Linux内核驱动】05_IIC子系统 | 硬件原理与常见面试问题 | 应用编程 | 内核驱动 | 总体框架

    1.1 IIC 基础 IIC协议简介—学习笔记_iic标准协议_越吃越胖的黄的博客-CSDN博客 I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器、传感器、存储器和其他外设。 I2C使用两条线(SDA和SCL)进行通信,可以连接多个设备,每个设备都有一个唯一的地址。I2C总线上的

    2024年02月09日
    浏览(64)
  • Linux MMC 驱动子系统详解

    SD/SDIO/MMC 驱动是一种基于 SDMMC 和 SD SPI 主机驱动的协议级驱动程序,目前已支持 SD 存储器、SDIO 卡和 eMMC 芯片。 因为linux内核mmc子系统里面已经实现了这些协议,我们以后并不需要重新实现这些,只需要对协议有个简单的了解。 mmc是比较老的存储卡了,sd是mmc的替代者,sdi

    2024年02月06日
    浏览(41)
  • Android/Linux 子系统Graphics图形栈入门普法介绍

      由于工作原因,最近在公司做了一个关于Android/Linux 子系统Graphics图形栈入门相关知识的培训介绍,个人感觉对于想要了解入门这块的朋友还是有一定帮助的。由于博客不能直接放入ppt,这里我就将相关的ppt转换成可以博客展示的发表出来,希望能帮助到对这一块感兴趣的

    2024年01月17日
    浏览(42)
  • lv15 input子系统框架、外设驱动开发 5

     在我们日常的Linux系统中,存在大量的输入设备,例如按键、鼠标、键盘、触摸屏、摇杆等,他们本身就是字符设备,linux内核将这些字符设备的共同性抽象出来,简化驱动开发建立了一个input子系统。 Linux内核为了两个目的: 简化纯输入类外设 (如:键盘、鼠标、游戏杆

    2024年02月19日
    浏览(41)
  • 1.内核驱动中,驱动注册,阻塞IO,gpio子系统,中断处理的整体结合示例

    /*功能实现 在stm32开发板上实现功能            1.使用阻塞IO读取number变量的值,当number的值改变时打印number的值            2.注册KEY1按键的驱动和LED1的驱动以及对应的设备文件,            3.按键和指示灯设备信息放在同一个设备树的节点中            4.当KEY1按下时

    2024年02月15日
    浏览(39)
  • 【Windows 11】安装 Android子系统 和 Linux子系统

    本文使用电脑系统: 主要就是安装一个名为: 适用于Android的Windows子系统 (WSA)的软件。 首先在电脑的设置里面:时间和语言——语言和地区里面把地区改为美国。 然后到微软商店搜索: Amazon AppStore 。 安装亚马逊应用商店的时候,会首先提示你安装前面说的WSA。如此,我

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包