RK3568驱动指南|第十三篇 输入子系统-第141章 编写最简单的设备驱动层代码

本文主要是介绍RK3568驱动指南|第十三篇 输入子系统-第141章 编写最简单的设备驱动层代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

瑞芯微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主板


第141章 编写最简单的设备驱动层代码

从上面的学习中可以了解到,输入子系统可以分为三层,分别为中间的核心层,靠近上层应用的事件处理层,以及靠近硬件设备的设备驱动层,事件处理层的代码一般情况下也不需要编写,所以需要我们来编写的就只有设备驱动层的代码。本小节将编写一个最简单的设备驱动层代码。

141.1 设备驱动层代码编写步骤

步骤一:创建输入设备结构体变量 在设备驱动的开发中,首先需要创建一个输入设备的结构体变量,该结构体变量将用于表示和管理设备的属性和状态。可以使用 input_allocate_device 函数来分配输入设备结构体的内存。

步骤二:初始化输入设备结构体变量 在创建输入设备结构体变量后,需要对其进行初始化。这包括设置设备的名称、支持的事件类型、事件处理函数等。可以使用结构体提供的成员变量和函数来完成初始化过程。

步骤三:注册输入设备结构体变量 在初始化输入设备结构体变量后,需要将其注册到系统中,以便系统能够正确地识别和使用该设备。可以使用 input_register_device 函数来注册输入设备结构体变量。在注册过程中,系统将完成设备的匹配和初始化工作。

步骤四:上报事件 一旦设备注册成功,就可以通过输入设备结构体变量上报事件。这可以通过调用输入设备结构体提供的函数来完成,例如 input_event 函数。根据设备类型和事件类型,可以生成相应的输入事件,并通过调用该函数将事件发送给系统。

注:本章节只是为了编写最简单的设备驱动层代码,所以本章节不会涉及到事件上报这一步骤相关的代码,会在后面的章节再进行填充。

步骤五:注销和释放输入设备结构体变量 当设备不再需要使用时,应该进行注销和释放操作,以确保资源的正确释放。可以使用 input_unregister_device 函数来注销输入设备结构体变量,并使用 input_free_device 函数来释放相关资源和内存。

141.2 input_allocate_device 函数讲解

input_allocate_device 函数定义在drivers/input/input.c文件中,具体内容如下所示:

struct input_dev *input_allocate_device(void)
{static atomic_t input_no = ATOMIC_INIT(-1);struct input_dev *dev;// 分配输入设备结构体的内存dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (dev) {// 设置设备类型和设备类dev->dev.type = &input_dev_type;dev->dev.class = &input_class;// 初始化设备device_initialize(&dev->dev);// 初始化互斥锁和事件自旋锁mutex_init(&dev->mutex);spin_lock_init(&dev->event_lock);// 初始化定时器timer_setup(&dev->timer, NULL, 0);// 初始化链表头INIT_LIST_HEAD(&dev->h_list);INIT_LIST_HEAD(&dev->node);// 设置设备名称,使用原子变量递增来保证唯一性dev_set_name(&dev->dev, "input%lu", (unsigned long)atomic_inc_return(&input_no));// 增加模块引用计数__module_get(THIS_MODULE);}return dev;
}

这个函数的作用是为输入设备分配内存并进行必要的初始化,为后续的输入事件处理和设备注册做准备。

141.3 初始化input_dev 结构体

在使用input_allocate_device函数创建了一个input_dev结构体之后,接下来就要初始化input_dev结构体内容了,在该步骤中又有两个内容,分别为设置事件类型和设置具体类型。

141.3.1 设置事件类型

在头文件include/uapi/linux/input-event-codes.h中,Linux内核已经为我们定义了一些输入事件类型,它们的含义如下:

(1)EV_SYN (0x00): 用于同步事件,表示一组输入事件的结束。

(2)EV_KEY (0x01): 用于按键事件,表示按下、释放或重复一个键。

(3)EV_REL (0x02): 用于相对位移事件,表示设备的相对位置变化,例如鼠标的移动。

(4)EV_ABS (0x03): 用于绝对位移事件,表示设备的绝对位置变化,例如触摸屏的坐标。

(5)EV_MSC (0x04): 用于杂项事件,包含一些特殊目的的事件类型,例如设备状态变化等。

(6)EV_SW (0x05): 用于开关事件,表示开关的状态变化,例如电源按钮、开合盖等。

(7)EV_LED (0x11): 用于 LED 事件,表示 LED 灯的状态变化。

(8)EV_SND (0x12): 用于声音事件,表示声音的播放相关事件。

(9)EV_REP (0x14): 用于重复事件,表示键盘重复发送事件。

(10)EV_FF (0x15): 用于力反馈事件,表示力反馈设备的输出事件。

(11)EV_PWR (0x16): 用于电源事件,表示电源状态变化。

(12)EV_FF_STATUS (0x17): 用于力反馈状态事件,表示力反馈设备的状态变化。

(13)EV_MAX (0x1f): 输入事件类型的最大值。

(14)EV_CNT: 输入事件类型的数量。

而在input_dev结构体中定义了一系列的位图,在输入子系统中用于表示输入设备的能力和支持的功能,具体定义如下所示:

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)];             // 设备支持的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)];               // 设备支持的开关位图

(1)evbit(事件类型位图)是一个长度为 EV_CNT 的数组,每个元素对应一个事件类型。通过设置相应的位,可以指示设备支持的事件类型,如按键事件、相对位移事件、绝对位移事件、杂项事件等。

(2)keybit(按键类型位图)表示输入设备支持的按键类型,通常与 EV_KEY 事件类型相关。通过设置相应的位,可以指示设备支持的按键。

(3)relbit(相对位移类型位图)表示输入设备支持的相对位移类型,通常与 EV_REL 事件类型相关。通过设置相应的位,可以指示设备支持的相对位移,例如鼠标的移动。

(4)absbit(绝对位移类型位图)表示输入设备支持的绝对位移类型,通常与 EV_ABS 事件类型相关。通过设置相应的位,可以指示设备支持的绝对位移,例如触摸屏的坐标。

(5)mscbit(杂项类型位图)表示输入设备支持的杂项类型,通常与 EV_MSC 事件类型相关。通过设置相应的位,可以指示设备支持的杂项事件,例如设备状态变化等。

(6)ledbit(LED 类型位图)表示输入设备支持的 LED 类型,通常与 EV_LED 事件类型相关。通过设置相应的位,可以指示设备支持的 LED 灯控制。

(7)sndbit(声音类型位图)表示输入设备支持的声音类型,通常与 EV_SND 事件类型相关。通过设置相应的位,可以指示设备支持的声音事件。

(8)ffbit(力反馈类型位图)表示输入设备支持的力反馈类型,通常与 EV_FF 事件类型相关。通过设置相应的位,可以指示设备支持的力反馈事件。

(9)swbit(开关类型位图)表示输入设备支持的开关类型,通常与 EV_SW 事件类型相关。通过设置相应的位,可以指示设备支持的开关状态变化。

这些位图用于向输入子系统提供关于输入设备的能力和功能的信息,以便在注册和处理输入设备时进行相应的配置和过滤。通过设置相应的位,可以告知输入子系统设备所支持的事件类型和功能,使得输入子系统能够正确识别和处理来自设备的输入数据。

__set_bit是一个位操作函数,用于设置一个位图中的特定位,例如可以通过下面的代码将输入设备设置为支持按键事件:

__set_bit(EV_KEY,myinput_dev->evbit)

141.3.2 设置具体类型

设置完事件类型之后,还需要设置具体类型,宏定义仍旧定义在头文件include/uapi/linux/input-event-codes.h中,部分内容如下所示:

#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

上一小节只是将输入设备设置为了按键事件,但具体要表示什么呢,是按键1还是按键2亦或者其他按键,都无法确定,所以仍旧需要使用__set_bit函数来确定具体类型,例如使用以下程序将该输入设备设置为按键1

__set_bit(KEY_1,myinput_dev->keybit)

141.4 驱动程序的编写

本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\91_myinput_dev。编写完成的myinput_dev.c代码如下所示:

#include <linux/input.h>
#include <linux/module.h>struct input_dev *myinput_dev;static int myinput_dev_init(void)
{int ret;// 分配输入设备结构体myinput_dev = input_allocate_device();if (myinput_dev == NULL) {printk("input_allocate_device error\n");return -1;}// 设置输入设备的名称myinput_dev->name = "myinput_dev";// 设置输入设备支持的事件类型__set_bit(EV_KEY, myinput_dev->evbit);    // 设置支持按键事件__set_bit(KEY_1, myinput_dev->keybit);    // 设置支持按键1// 注册输入设备ret = input_register_device(myinput_dev);if (ret < 0) {printk("input_register_device error\n");goto error;}return 0;error:// 注册失败,释放输入设备结构体input_free_device(myinput_dev);return ret;
}
static void myinput_dev_exit(void)
{// 注销输入设备input_unregister_device(myinput_dev);
}module_init(myinput_dev_init);
module_exit(myinput_dev_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");

141.5 运行测试

141.5.1 编译驱动程序

在上一小节中的myinput_dev.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += myinput_dev.o    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作    

对于Makefile的内容注释已在上图添加,保存退出之后,来到存放myinput_dev.c和Makefile文件目录下,如下图所示:

然后使用命令“make”进行驱动的编译,编译完成如下图所示:

编译完生成myinput_dev.ko目标文件,如下图所示:

至此驱动模块就编译成功了。

141.5.2 运行测试

首先启动开发板,进入系统之后如下所示:

然后将上一小节编译完成的myinput_dev.ko驱动文件拷贝到开发板上,拷贝完成如下所示:

在加载驱动之前首先使用以下命令查看当前的输入设备,如下所示:

ls /dev/input

可以看到目前有4个输入设备的设备节点,然后使用以下命令进行驱动的加载,如下图所示:

insmod myinput_dev.ko

然后重新查看设备节点,可以看到多出来了一个event4节点,如下图所示:

然后使用以下命令查看设备节点信息,可以根据名字确定event4正是加载驱动所创建的,如下图所示:

cat /proc/bus/input/devices

至此,关于最简单的设备驱动程序就测试完成了。 

这篇关于RK3568驱动指南|第十三篇 输入子系统-第141章 编写最简单的设备驱动层代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/833833

相关文章

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加