Linux input输入子系统

Linux input

更多内容可以查看我的github

Linux输入子系统框架

请添加图片描述Linux输入子系统由驱动层、核心层、事件处理层三部分组成。

  • 驱动层:输入设备的具体驱动程序,负责与具体的硬件设备进行交互,并将底层的硬件输入转化为统一的事件形式,向核心层发送
  • 核心层:连接驱动层和事件处理层,负责对下层提供驱动层借口,向上层提供事件处理接口
  • 事件处理层:负责对输入事件进行处理,并将处理结果传递给应用程序
  • 层的设备抽象出对应的接口提供给应用层。将底层设备的触发的事件通过这个接口传达给应用层。

核心层的代码在 linux/drivers/input/input.c 中实现

重要结构体

input_dev

这个结构体属于驱动层,描述了一个具体的input设备,记录相关的硬件信息,事件位图等,

struct input_dev {
	const char *name;       // 设备名称
	struct input_id id;     // 设备id,存储输入设备的总线、厂商等信息
    ...
    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];     // 支持事件类型
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];   // 按键位图
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];   // 相对位移位图
    ...
    struct list_head	h_list;     // 内核链表头
	struct list_head	node;       // 内核链表节点
};

Linux设备支持的事件类型:

事件类型编码事件描述
EV_SYN0x00同步事件
EV_KEY0x01按键事件(鼠标,键盘等)
EV_REL0x02相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS0x03绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC0x04其它
EV_SWx05开关
EV_LED0x11按键/设备灯
EV_SND0x12声音/警报
EV_REP0x14重复
EV_FFx15力反馈
EV_PWR0x16电源
EV_FF_STATUS0x17力反馈状态
EV_MAX0x1f事件类型最大个数和提供位掩码支持
input_handler

这个结构体属于事件处理层,描述一个事件处理器

struct input_handler {

	void *private;
    // 事件处理函数
	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;

	struct list_head	h_list;
	struct list_head	node;
};
input_handle

这个结构体属于核心层,描述一个配对的input设备和input设备处理器

struct input_handle {

	void *private;
    
	int open;               // 打开标志
	const char *name;       // 名称

	struct input_dev *dev;
	struct input_handler *handler;

	struct list_head	d_node;
	struct list_head	h_node;
};
input_handle_list
struct input_handle_list {
	struct list_head	list;
	struct input_handle	*first;
	struct input_handle	*last;
};
两条重要链表

在 input.c 中,全局维护了两条重要的链表,分别是输入设备链表和事件处理器链表

static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
总结

上面结构体的链表的关系如下面两图所示(这个图好难画-.-,就在网上找了一个,原文连接)

请添加图片描述

请添加图片描述

当我们使用input_register_device()注册一个设备的时候,就会将设备添加到input_dev_list链表中,同时便利input_handler_list进行匹配,匹配成功就会调用input_handler->connect()函数进行连接

事件相关结构体

input_event

事件的输入就是以一个input_event为基本单位的

struct input_event {
	struct timeval time;  /* 事件发生的时间 */
	__u16 type;           /* 事件总类型 */
	__u16 code;           /* 事件子类型 */
	__s32 value;          /* 事件的值 */
};
evdev_client
struct evdev_client {
	unsigned int head;
	unsigned int tail;
	unsigned int packet_head; /* [future] position of the first element of next packet */
	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
	struct fasync_struct *fasync;
	struct evdev *evdev;
	struct list_head node;
	int clk_type;
	bool revoked;
	unsigned int bufsize;
	struct input_event buffer[];
};
  • head:表示客户端缓冲区中下一个要读取的事件的索引。当客户端从缓冲区读取事件时,它会从 buffer[head] 开始读取,并递增 head 的值。因此,head 指向的是最老的未读取事件的位置。
  • tail:表示客户端缓冲区中下一个要写入的事件的索引。当输入事件到达并需要被缓冲时,它将被写入到 buffer[tail] 的位置,并递增 tail 的值。因此,tail 指向的是最新的未写入事件的位置。

实际上,evdev_client 实现了一个环形队列,head是头指针,tail是尾指针,这两个指针都是以 input_event 为单位移动的。

packet_head 与 head 和 tail 不同, 它以数据包(多个input_event)为单位,主要负责记录缓冲区的入口偏移量。

buffer 就是循环队列数组,即缓冲区

所以,根据这些变量我们可以知道,当循环队列满的时候,head = tail;当循环队列空的时候,packet_head = tail

evdev
struct evdev {
	int open;   // 设备打开计数
	struct input_handle handle; 
	wait_queue_head_t wait;     // 等待队列,没有事件时进程睡眠
	struct evdev_client __rcu *grab;    // 事件响应

    // 客户端链表,可以有多个进程访问设备
	struct list_head client_list;
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
	struct cdev cdev;
	bool exist;
};
总结
  • input_event: 表示一个输入事件
  • evdev_client: 表示一个用户空间的应用程序或实体设备与输入设备之间的连接
  • evdev: 输入设备驱动程序的接口实现,应用程序可以通过evdev与evdev_client之间的交互,实现输入事件的读取和输入

流程

这部分建议阅读源码

在输入设备驱动(input_dev)中,一般通过轮询或者中断方式获取事件的原始值,经过一些处理后,通过input_event()函数将数据上报给核心层(input_core)。

在核心层中,通过input_handle_event()input_pass_values()对数据进行处理(type、code、value),然后使用input_to_handler()函数将数据上报给事件处理层(input_handler),在input_to_handler()中,使用input_handler结构体中的事件处理函数(event、events、filter)上报,这些函数可以在evdev.c的1235行的evdev_handler中找到。

在事件处理层中,通过evdev_events()evdev_pass_values()为事件加上时间戳,完成了一个完整的input_event,然后使用__pass_event()将事件传递给用户空间(evdev_client的buffer中)

__pass_event()中,事件input_event会被填充到evdev_clientbuffer中。对于用于空间的应用程序,可以通过read()函数调用内核空间的evdev_read()函数,然后调用evdev_fetch_next_event()函数从环形缓冲区中读取input_event事件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/669793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自然语言处理(NLP)—— 信息提取与文档分类

1. 初识信息提取 1.1 信息提取的基本知识 1.1.1 信息提取的概念 信息提取(IE, Information Extraction)是自然语言处理(NLP)领域的一个重要分支,它专注于从文档或语料库中提取结构化信息。这与信息检索(I…

江协科技STM32学习-1 购买24Mhz采样逻辑分析仪

前言: 本文是根据哔哩哔哩网站上“江协科技STM32”视频的学习笔记,在这里会记录下江协科技STM32开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技STM32教学视频和链接中的内容。 引用: STM32入门教程-2023版 细致讲…

windows11家庭版、专业版、工作站版区别

windows11家庭版、专业版、工作站版区别 1、windows11家庭版和专业版的区别2、windows11家庭版和工作站版的区别 1、windows11家庭版和专业版的区别 windows11专业版需要$808 windows11专业版和家庭版功能对比 2、windows11家庭版和工作站版的区别 windows11工作站版需要$168…

Python基础教程——数据类型和变量

数据类型和变量 Python使用缩进来组织代码块,一般使用4个空格的缩进.使用#来注释一行,其他每一行都是一个语句,当语句以冒号:结尾时,缩进的语句视为代码块.Python对大小写敏感. 1.1 整数 Python可以处理任意大小的整数,包括负整数,写法与数学上写法一致,例如:-10…

揭秘小程序商城的团购奇迹:独特模式引领盈利新纪元

在数字经济的新纪元里,你是否对那些不张扬却充满潜力的商业模式心生好奇?今天,我要为你揭示一种别出心裁的商业模式,它以其独特的魅力,不仅迅速吸引了大量用户的目光,更在短短一个月内创造了超过600万的惊人…

javascript DOM 设置样式

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、直接…

EXSI虚拟机新增磁盘并将空间扩充到已有分区

这里写自定义目录标题 1、在EXSI虚拟机中新增一块磁盘配置大小2、确认新磁盘3、格式化新分区4、添加新分区到LVM5、将新增分区添加到已有分区里 1、在EXSI虚拟机中新增一块磁盘配置大小 注意事项: (1)需确保虚拟机已关闭活处于维护模式,避免数据丢失 (2…

【通信专题】I2C上拉电阻计算方法

I2C 通信总线是电子设计中常见的总线之一,由于 I2C 的硬件芯片内部为开漏输出,所以要求在外部增加一个上拉电阻,总线上拉电阻的选取受多个因素的影响,因此如何计算 I2C 总线的上拉电阻阻值成为硬件工程师在使用 I2C总统时需要关注的话题。 从本质上讲: I2C 总线电容和上升…

善听提醒遵循易经原则。世界大同只此一路。

如果说前路是一个大深坑,那必然是你之前做的事情做的不太好,当坏的时候,坏的结果来的时候,是因为你之前的行为,你也就不会再纠结了,会如何走出这个困境,是好的来了,不骄不躁&#xf…

阿里云 通过EIP实现VPC下的SNAT以及DNAT

192.168.0.85 有公网地址192.1680.95无公网地址 在192.168.0.85(有公网地址服务器上操作) #开启端口转发 echo "net.ipv4.ip_forward 1" >> /etc/sysctl.conf sysctl -p#仅允许192.168.0.95 iptables -t nat -I POSTROUTING -s 192.16…

中医的悠久历史文化

中医,作为中华民族的传统医学,拥有着悠久的历史和深厚的文化底蕴。自古以来,中医便以其独特的理论体系和治疗方法,为中华民族的繁衍昌盛做出了巨大贡献。如今,随着现代医学的不断发展,中医依然以其独特的魅…

使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测

在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe 工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容…

Unity实现简单的持久化存储

在Unity中,运行过程中的内容是不会保存的,但是如果我们有些游戏数据需要持久化存储,应该怎么办呢,所以Unity为我们提供了一个简单的数据存储的API。 附上代码片段 //写入数据PlayerPrefs.SetInt("IntType", 1);PlayerPr…

CMake的作用域:public/private/interface

在 CMake 中,public、private和 interface是用来指定目标属性的作用域的关键字,这三个有什么区别呢?这些关键字用于控制属性的可见性和传递性,影响了目标之间的依赖关系和属性传递。 public 如果在一个目标上使用 public关键字时…

子集树与排列树的构造

排列树的构造: 无重复画法:一条线前面出现的不再出现。 有重复画法:一条线前面出现的不再出现,如果仅仅只是相似可以出现;兄弟不能相似。 目标函数是:cnt 总元素个数分支策略是全遍历,不过存…

AI播客下载:a16z (主题为AI、web3、生物技术等风险投资)

a16z播客是一个综合性的科技和创新领域的媒体平台,通过多种节目形式和丰富的内容,为广大听众提供了一个了解最新科技趋势和创新思维的窗口。a16z播客是由安德里森霍罗威茨(Andreessen Horowitz,简称a16z)推出的一个科技…

Resilience4j结合微服务出现的异常

Resilience4j结合微服务出现的异常 1、retry未生效 由于支持aop&#xff0c;所以要引入aop的依赖。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>2、circ…

20240601在Toybrick的TB-RK3588开发板上跑IPC的SDK并确认eth0

20240601在Toybrick的TB-RK3588开发板上跑IPC的SDK并确认eth0 2024/6/1 20:06 ADB的详细LOG&#xff1a; Microsoft Windows [版本 10.0.22621.3296] (c) Microsoft Corporation。保留所有权利。 C:\Users\QQ>adb shell adb server version (40) doesnt match this client …

FreeRtos进阶——通用链表的实现方式

通用链表实现方式&#xff08;一&#xff09; struct node_t {struct node_t *next; };struct person {struct node_t node;char *name;int age; };struct dog {struct node_t node;char *name;int age;char *class; };在此链表中&#xff0c;node结构体被放在了最前面&#x…

民国漫画杂志《时代漫画》第37期.PDF

时代漫画37.PDF: https://url03.ctfile.com/f/1779803-1248636302-c017ee?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!