本文主要是介绍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章 编写最简单的设备驱动层代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!