RK3568驱动指南|第十三篇 输入子系统-第143章 多对多的匹配关系分析

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第十三篇 输入子系统_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第143章 多对多的匹配关系分析

143.1 joydev.c事件处理层匹配分析

drivers/input/joydev.c文件的input_handler结构体内容如下所示:

static struct input_handler joydev_handler = {.event		= joydev_event,.match		= joydev_match,.connect	= joydev_connect,.disconnect	= joydev_disconnect,.legacy_minors	= true,.minor		= JOYDEV_MINOR_BASE,.name		= "joydev",.id_table	= joydev_ids,
};static int __init joydev_init(void)
{return input_register_handler(&joydev_handler);
}

与上面讲解的通用设备驱动层evdev.c的evdev_handler结构体不同的是,joydev_handler结构体中有着对应的匹配函数,也就是说当设备驱动层与事件处理层进行匹配的时候,需要joydev_ids结构体数组和相应的匹配函数共同决定,首先来看joydev_ids结构体,joydev_ids结构体内容如下所示:

static const struct input_device_id joydev_ids[] = {// 第一个标识符,匹配X轴(ABS_X)的绝对事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_ABS) },    // 匹配的事件类型是EV_ABS(绝对事件).absbit = { BIT_MASK(ABS_X) },    // 匹配的绝对事件类型是ABS_X(X轴)},// 第二个标识符,匹配Z轴(ABS_Z)的绝对事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_ABS) },    // 匹配的事件类型是EV_ABS(绝对事件).absbit = { BIT_MASK(ABS_Z) },    // 匹配的绝对事件类型是ABS_Z(Z轴)},// 第三个标识符,匹配滚轮(ABS_WHEEL)的绝对事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_ABS) },    // 匹配的事件类型是EV_ABS(绝对事件).absbit = { BIT_MASK(ABS_WHEEL) },    // 匹配的绝对事件类型是ABS_WHEEL(滚轮)},// 第四个标识符,匹配油门(ABS_THROTTLE)的绝对事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_ABS) },    // 匹配的事件类型是EV_ABS(绝对事件).absbit = { BIT_MASK(ABS_THROTTLE) },    // 匹配的绝对事件类型是ABS_THROTTLE(油门)},// 第五个标识符,匹配游戏杆(BTN_JOYSTICK)的按键事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT,.evbit = { BIT_MASK(EV_KEY) },    // 匹配的事件类型是EV_KEY(按键事件).keybit = { [BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },    // 匹配的按键类型是BTN_JOYSTICK(游戏杆)},// 第六个标识符,匹配游戏手柄(BTN_GAMEPAD)的按键事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT,.evbit = { BIT_MASK(EV_KEY) },    // 匹配的事件类型是EV_KEY(按键事件).keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },    // 匹配的按键类型是BTN_GAMEPAD(游戏手柄)},// 第七个标识符,匹配快乐键(BTN_TRIGGER_HAPPY)的按键事件{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT,.evbit = { BIT_MASK(EV_KEY) },    // 匹配的事件类型是EV_KEY(按键事件).keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },    // 匹配的按键类型是BTN_TRIGGER_HAPPY(快乐键)},{ }	/* 终止项 */
};

结构体input_device_id的作用是描述输入设备的特征,以便内核能够识别和匹配正确的驱动程序。在通用设备驱动层evdev.c中的evdev_ids结构体数组设置的是driver_info表示匹配全部设备,而joydev.c中的joydev_ids结构体数组包含以下字段:

(1)flags:标识符的标志位,用于指定匹配方式。在这里,使用flags字段的INPUT_DEVICE_ID_MATCH_EVBIT和INPUT_DEVICE_ID_MATCH_ABSBIT标志表示匹配事件类型和绝对事件类型。

(2)evbit:事件类型的位掩码,用于指定要匹配的事件类型。在这里,evbit字段的位掩码表示匹配的事件类型是EV_ABS(绝对事件)或EV_KEY(按键事件)。

(3)absbit:绝对事件类型的位掩码,用于指定要匹配的绝对事件类型。在这里,absbit字段的位掩码表示匹配的绝对事件类型是ABS_X(X轴)、ABS_Z(Z轴)、ABS_WHEEL(滚轮)或ABS_THROTTLE(油门)。

(4)keybit:按键类型的位掩码,用于指定要匹配的按键类型。在这里,keybit字段的位掩码表示匹配的按键类型是BTN_JOYSTICK(游戏杆)、BTN_GAMEPAD(游戏手柄)或BTN_TRIGGER_HAPPY(快乐键)。

这个结构体数组中的每个元素都描述了一种输入设备的特征,包括事件类型和按键类型。内核可以使用这些标识符来匹配输入设备并加载适当的驱动程序,以便正确解析和处理设备的输入数据。

然后来看相应的匹配链接相关的函数,具体内容如下所示:

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);if (!id)return -ENODEV;// 调用处理程序的连接函数来建立设备和处理程序之间的连接error = handler->connect(handler, dev, id);if (error && error != -ENODEV)pr_err("failed to attach handler %s to device %s, error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);// 如果连接失败且错误码不是-ENODEV,则打印错误消息return error;
}

第7行通过input_match_device函数,进行输入设备和处理程序的匹配,input_match_device函数内容如下所示:

static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev)
{const struct input_device_id *id;// 遍历处理程序的输入设备ID表,直到找到匹配的ID或遍历完所有ID为止for (id = handler->id_table; id->flags || id->driver_info; id++) {// 使用输入设备ID匹配函数判断给定的输入设备是否与当前ID匹配if (input_match_device_id(dev, id) &&(!handler->match || handler->match(handler, dev))) {// 如果输入设备与ID匹配,并且处理程序的匹配函数返回true(或者没有匹配函数),则返回该IDreturn id;}}return NULL;
}

在6-13行的for循环中首先会依次取出id_table的值,即上面讲解的joydev_ids结构体数组中的值,一一进行匹配,在for循环中由于每个joydev_ids结构体数组中都存在flags参数且值不为零,所以for循环的条件是成立的,在for循环中会调用input_match_device_id函数判定给定的输入设备是否与当前ID匹配,input_match_device_id函数内容如下所示:

bool input_match_device_id(const struct input_dev *dev,const struct input_device_id *id)
{// 检查设备的总线类型是否匹配if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)if (id->bustype != dev->id.bustype)return false;// 检查设备的厂商ID是否匹配if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)if (id->vendor != dev->id.vendor)return false;// 检查设备的产品ID是否匹配if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)if (id->product != dev->id.product)return false;// 检查设备的版本号是否匹配if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)if (id->version != dev->id.version)return false;// 检查设备的事件位图是否是给定ID的子集if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {return false;}// 所有匹配条件都满足,返回truereturn true;
}

在5、10、15、20行的if判断中,由于flags的值和后面的宏都不为零,所以if判断都是成立的,但是第6、11、16、21四行中,dev->id在我们编写的最简单设备驱动层代码中并没有赋值,所以值为零,而在joydev.c中id->bustype、id->vendor、id->product和id->version同样没有赋值,所以值同样为0,所以第二层if判断不成立。

然后来看25-36行的if判断,bitmap_subset在上面讲解过,它是一个内联函数,用于判断两个位图是否具有子集关系,这时就需要用到上面讲解的joydev_ids结构体数组的描述特征了。在编写的最简单的设备驱动层代码中的设置如下所示:

    __set_bit(EV_KEY, myinput_dev->evbit);    // 设置支持按键事件

    __set_bit(KEY_1, myinput_dev->keybit);    // 设置支持按键1

结合joydev_ids结构体数组的描述特征来对比,在上述的if判断中并不能与之匹配,所以最终会返回false,导致匹配失败。

讲解joydev.c的目的是让了解匹配规则是有多种多样的,对设备驱动层和事件处理层的知识重新进行梳理,在下次遇到相似的情况时可以自行分析。

143.2 扩展:多对多匹配关系

143.2.1 内核源码修改

首先对内核源码进行修改,修改涉及到两个函数分别是input_match_device_id函数和input_match_device函数,修改完成的代码内容如下所示:

bool input_match_device_id(const struct input_dev *dev,const struct input_device_id *id)
{if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)if (id->bustype != dev->id.bustype)return false;if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)if (id->vendor != dev->id.vendor)return false;if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)if (id->product != dev->id.product)return false;if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)if (id->version != dev->id.version)return false;if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {printk("input dev is error %s\n", dev->name);return false;}printk("input dev is ok %s\n", dev->name);return true;
}
EXPORT_SYMBOL(input_match_device_id);static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev)
{const struct input_device_id *id;printk("handler name is %s\n", handler->name);for (id = handler->id_table; id->flags || id->driver_info; id++) {if (input_match_device_id(dev, id) &&(!handler->match || handler->match(handler, dev))) {return id;}}return NULL;
}

相较于原函数只是在第30、33和42三行添加了printk打印,目的是在匹配过程中通过打印更好的了解匹配流程,由于修改的是内核源码,所以需要在修改之后重新编译内核源码,并将编译完成后生成的boot.img内核镜像烧写到开发板上。

注:joydev事件处理层代码在内核中默认是没有勾选的,需要根据输入子系统的裁剪和配置相关章节对内核的默认配置文件进行修改,勾选"Joystick interface"选项即可,勾选完成如下所示:

修改编译完成的boot.img内核镜像的网盘路径为“iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\92_myinput_dev_02\01_img,如下图所示:

143.2.2 运行测试

本节测试仍旧使用前面编写的最简单的设备驱动层的ko文件,需要注意的是要想得到跟实验效果相同的结论,必须已经按照上一小节的操作修改过了内核源码,并重新烧写到了开发板上。

开发板上电正常启动之后如下所示:

 由于myinput_dev.ko在之前已经拷贝到了开发板上,所以这里可以直接使用下面的命令加载myinput_dev.ko文件,打印内容如下所示:

 insmod  myinput_dev.ko

其中红色框中的是joydev相关的打印,可以看到joydev是匹配失败的,证明在第一小节中我们的推理是正确的,而红色框下方的蓝色框中的是evdev通用设备驱动层程序,根据打印可以看到evdev匹配成功了,这时候细心的小伙伴可能发现了,除了evdev之外还有kbd、cpufreq_interactive和dmcfreq也匹配成功了,这是为什么呢?

143.2.3 结论

在输入子系统中,输入设备和输入处理器之间的关系是多对多的。这意味着一个输入设备可以与多个输入处理器关联,而一个输入处理器也可以处理多个输入设备的事件。

这种多对多的关系设计是为了提供更大的灵活性和可扩展性。不同的输入设备可能具有不同的特性和事件类型,而不同的输入处理器可能针对特定的事件类型提供不同的处理逻辑。通过将输入设备和输入处理器解耦并建立多对多的关系,可以使输入子系统更加灵活,可以根据需求将特定的输入设备与适合的输入处理器进行匹配,以实现定制化的事件处理。例如,对于鼠标输入设备,它可以产生鼠标移动事件、鼠标点击事件和滚轮滚动事件等,对于键盘输入设备,它可以产生按键事件,包括按下和释放按键的事件。

所以在上个小节中最简单的设备驱动层代码会跟内核中全部的事件处理层想匹配,最终匹配成功了evdev、kbd、cpufreq_interactive和dmcfreq四个事件处理层程序。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文全面了解 wxWidgets 的安装

目录 使用二进制文件 从源代码构建 获取源代码 选择构建系统 构建您的应用程序 Unix,命令行 Unix,使用GNU Make MSW,使用Microsoft Visual Studio Mac,使用Xcode 其他IDE wxWidgets的头文件和库必须可用,以便…

ubuntu20.04_PX4_1.13

说在前面:(最好找一个干净的Ubuntu系统)如果配置环境的过程中出现很多编译的错误或者依赖冲突,还是建议新建一个虚拟机,或者重装Ubuntu系统,这样会避免很多麻烦💐 , 安装PX4 1.13.2 …

小红书扫码登录分析与python实现

文章目录 1. 写在前面2. 接口分析3. 代码实现 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

变量的数据类型:基本数据类型和引用数据类型

数据分类 数据分为:静态数据、动态数据。 #静态数据 静态数据是指一些永久性的数据。一般是以文件的形式存储在硬盘上,比如文档、照片、视频等文件。 电脑关闭后,静态数据仍然还在。只要不主动删掉数据或者硬盘没损坏,这些数据…

SQL管理员高频面试问题

1.请你谈谈 MySQL 事务隔离级别,MySQL 的默认隔离级别是什么?为了达到事务的四大特性,数据库定义了 4 种不同的事务隔离级别: READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许脏…

代码随想录算法训练营第十七天(二叉树IV)| 110. 平衡二叉树、257. 二叉树的所有路径、404.左叶子之和(JAVA)

文章目录 110. 平衡二叉树解题思路源码 257. 二叉树的所有路径解题思路源码 404.左叶子之和解题思路源码 110. 平衡二叉树 给定一个二叉树,判断它是否是平衡二叉树 示例 1: 输入:root [3,9,20,null,null,15,7]输出:true 示例…

伊理威科技:抖音开网店新手刚做选啥品

在数字浪潮中,抖音不仅是展示才艺的舞台,更是创业者的新天地。新手若想在这片热土上开垦网店,选品便是首要课题。选择产品如同种下希望的种子,既要考量土壤肥沃度,也得预测风雨适宜期。 兴趣与专长是选品的罗盘。热爱所…

鸿蒙Harmony应用开发—ArkTS(@Extend装饰器:定义扩展组件样式)

在前文的示例中,可以使用Styles用于样式的扩展,在Styles的基础上,我们提供了Extend,用于扩展原生组件样式。 说明: 从API version 9开始,该装饰器支持在ArkTS卡片中使用。 装饰器使用说明 语法 Extend(UI…

光速论文能用吗 #媒体#知识分享#学习方法

光速论文是一个非常有效的论文写作、查重降重工具,它的使用非常简单方便,而且功能强大,是每个写作者必备的利器。 首先,光速论文具有强大的查重降重功能,能够快速检测论文中的抄袭部分,帮助作者避免不必要的…

面试算法-80-字符串相乘

题目 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 “2”, num2 “3” 输出: “6…

集合(set)篇(一)

文章目录 5.1 集合的创建5.2 交集5.3 ^ 对称差集5.4 - 差集5.5 并集5.6 < 判断当前集合是否为另一个集合的子集5.7 5.8 > 判断当前集合是否为另一个集合的超集 5.1 集合的创建 在Python中&#xff0c;集合&#xff08;set&#xff09;是一个无序的不重复元素序列。你可以…

用BSP优化3D渲染

3D渲染引擎设计者面临的最大问题之一是可见性计算&#xff1a;只必须绘制可见的墙壁和物体&#xff0c;并且必须以正确的顺序绘制它们&#xff08;应该在远处的墙壁前面绘制近墙&#xff09; 。 更重要的是&#xff0c;对于游戏等应用程序来说&#xff0c;开发能够快速渲染场景…

Gin简介(Go web基础知识)

Gin简介 https://geektutu.com/post/quick-go-gin.html我是从这个网站上面摘录的&#xff0c;就是做个笔记&#xff0c;仅分享。膜拜极客兔兔大佬 Go特性&#xff1a; 快速&#xff1a;路由不使用反射&#xff0c;基于Radix树&#xff0c;内存占用少。 中间件&#xff1a;HT…

新概念英语1:Lesson11学习笔记

新概念英语1&#xff1a;Lesson11学习笔记 whose shirt is that?和whose is that shirt区别 “Whose shirt is that?” 和 “Whose is that shirt?” 这两个句子在意义上是相同的&#xff0c;都用于询问一个衬衫的所有者是谁。但在语法结构上存在一些细微的差异。 “Whos…

YOLO-v8-seg实例分割使用

最近需要实例分割完成一些任务&#xff0c;一直用的SAM(segment anything&#xff09;速度慢&#xff0c;找一个轻量分割模型。 1. YOLO-v8-seg使用 git clone https://github.com/ultralytics/ultralytics.git cd ultralytics vim run.py from ultralytics import YOLO# L…

【保姆级讲解C语言中的运算符的优先级】

C语言中的运算符的优先级 C语言中的运算符的优先级决定了表达式中运算符的计算顺序&#xff0c;下面列出了C语言中运算符的优先级&#xff0c;从最高到最低&#xff1a; 括号 ( )数组下标 []成员访问运算符 . 和 ->后缀递增 后缀递减 --前缀递增 前缀递减 --一元加减 -…

SSH 免密互信视频教程

上高速&#xff0c;B 站直达通道 https://www.bilibili.com/video/BV1L1421Q7t2/ SSH 免密互信视频教程 0、大前提&#xff0c;准备两台容器 cq-master1、cq-master2 配置 IP 地址、主机名映射 vim /etc/hosts 172.17.0.4 cq-master1 172.17.0.5 cq-master21、两台机器均设…

PTA7-2 括号匹配

检查一段C语言代码的小括号( )、 中括号 [ ] 和大括号{ } 是否匹配。 输入格式: 在一行中输入一段C语言代码&#xff0c;长度不超过1000个字符&#xff08;行末以换行符结束&#xff09;。 输出格式: 第一行输出左括号的数量和右括号的数量&#xff0c;中间以一个空格间隔。…

Socket.D v2.4.7 发布

Socket.D 是什么东东&#xff1f; 是基于"事件"和"语义消息""流"的网络应用协议。在微服务、移动应用、物联网等场景&#xff0c;可替代 http、websocket 等协议。支持 tcp, udp, ws, kcp 传输。协议特点可参考《官网介绍》。 目前&#xff1a…

Python虚拟环境conda的安装使用

文章目录 conda虚拟环境的详细步骤和注意事项&#xff1a;**安装Conda****创建Conda虚拟环境****激活Conda虚拟环境****安装Python包****管理Conda环境****其他优势与特性** 相较于venv&#xff0c;使用conda管理虚拟环境有以下优势&#xff1a;**性能****资源占用****其他性能…