瑞芯微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主板
第117章uevent_helper实验
在前面的章节中已经讲解了广播这一内核发送事件到用户空间的方法,而在本章节中将会讲解内核发送事件到用户空间的第二种方法-调用可执行程序。
117.1 设置uevent_helper
在114.2小节的第十部分中进行了定义,具体内容如下所示:
#ifdef CONFIG_UEVENT_HELPER
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
struct subprocess_info *info;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
retval = init_uevent_argv(env, subsystem);
if (retval)
goto exit;
retval = -ENOMEM;
info = call_usermodehelper_setup(env->argv[0], env->argv,
env->envp, GFP_KERNEL,
NULL, cleanup_uevent_env, env);
if (info) {
retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
env = NULL; /* freed by cleanup_uevent_env */
}
}
#endif
第3行为一个if表达式,它检查 uevent_helper 数组的第一个元素是否为真。并调用 kobj_usermode_filter 函数进行用户模式过滤, uevent_helper定义如下所示:
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; |
其中CONFIG_UEVENT_HELPER_PATH 是一个宏定义在内核源码的“include/generated/autoconf.h”文件中,如下所示:
1 | #define CONFIG_UEVENT_HELPER_PATH "" |
该宏为空,所以为了使能uevent_helper功能需要在图形配置界面使能CONFIG_UEVENT_HELPER和CONFIG_UEVENT_HELPER_PATH两个宏。首先来到内核源码目录下,如下图(图 117-1)所示:
图 117-1
然后输入以下命令将平台切换为arm64、加载rk3568的默认配置文件和进入图像配置界面,如下图(图117- 2)所示:
图117- 2
然后需要在menuconfig的图形配置界面进行以下配置:
配置1:
Device Drivers
Generic Driver Options
[*] Support for uevent helper//选中
(/sbin/mdev) path to uevent helper//设置mdev路径
图117- 3
配置2:
File systems
Pseudo filesystems.
[*]/proc file system support//选中
图117- 4
配置3:
File systems
Pseudo filesystems.
[*] Sysctl support(/proc/sys)//选中
图117- 5
配置4:
1 | [*]Networking support//选中 |
图117- 6
在上面的配置1中设置了uevent helper和相对应的路径,这就是配置方法1,但是这种方式需要重新编译内核,使用起来较为麻烦,除了方法一之外还有更快捷的方法2和方法3,具体内容如下所示:
配置方法2:
无论是否配置了CONFIG_UEVENT_HELPER_PATH,在系统启动后,可以使用以下命令来设置uevent_helper:
echo /sbin/mdev > /sys/kernel/uevent_helper
这将把uevent_helper设置为/sbin/mdex。
配置方法3:
无论是否配置了CONFIG_UEVENT_HELPER_PATH,在系统启动后,可以使用以下命令来设置uevent_helper:
echo /sbin/mdev > /proc/sys/kernel/hotplug
这将把uevent_helper设置为/sbin/mdexw.。
需要注意的是配置方法2和配置方法3依赖于上面的配置2、3、4选项,并且可以通过配置方法2和配置方法3修改配置方法1中已经写好的值。
对/proc/sys/kernel/hotplug和/sys/kernel/uevent_helper进行读写都是为了对uevent_helper属性进行读写操作。
/sys/kernel/uevent_helper是sysfs文件系统中的一个文件,它是uevent_helper属性的接口。通过对该文件进行读写操作,可以读取或修改uevent_helper属性的值。在内核源码的kernel/ksysfs.c目录下可以找到对uevent_helper属性的定义和相关操作的实现,具体内容如下所示:
#ifdef CONFIG_UEVENT_HELPER
/* uevent helper program, used during early boot */
static ssize_t uevent_helper_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", uevent_helper);
}
static ssize_t uevent_helper_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
if (count + 1 > UEVENT_HELPER_PATH_LEN)
return -ENOENT;
memcpy(uevent_helper, buf, count);
uevent_helper[count] = '\0';
if (count && uevent_helper[count - 1] == '\n')
uevent_helper[count - 1] = '\0';
return count;
}
KERNEL_ATTR_RW(uevent_helper);
#endif
uevent_helper_show函数用于将uevent_helper的值写入buf中,并返回写入的字符数。
uevent_helper_store函数用于将buf中的值复制到uevent_helper中,并根据需要进行处理,然后返回写入的字符数。
而/proc/sys/kernel/hotplug是一个虚拟文件,用于配置内核中的热插拔事件处理程序。通过对该文件进行写操作,可以设置uevent_helper属性的值。在内核源码的kernel/sysctl.c文件中,可以看到对hotplug操作其实是对uevent_helper进行操作。具体内容如下所示:
#ifdef CONFIG_UEVENT_HELPER
{
.procname = "hotplug",
.data = &uevent_helper,
.maxlen = UEVENT_HELPER_PATH_LEN,
.mode = 0644,
.proc_handler = proc_dostring,
}
这段代码定义了一个名为hotplug的文件,用于处理uevent事件。它与uevent_helper属性相关联。
.procname表示文件名,即/proc/hotplug。
.data是一个指向uevent_helper结构体的指针,用于保存与该文件相关的数据。该指针指向uevent_helper结构体,用于处理uevent事件。
.maxlen表示文件的最大长度,即文件内容的最大长度。该值为UEVENT_HELPER_PATH_LEN,表示文件内容的最大长度为UEVENT_HELPER_PATH_LEN。
.mode表示文件的访问权限。该值为0644,表示该文件的权限为 -rw-r--r--,即所有用户都可以读取该文件,但只有root用户可以写入该文件。
117.2 处理uevent事件
在上一章节中我们使用了netlink机制来监听内核向用户空间发送的uevent事件,而在上一小节中我们设置了uevent_helper,所以本小节将会学习调用用户空间程序来处理内核发送的uevent事件。
117.2.1 编写应用程序
本应用程序对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\81_mdev。
本小节编写的是应用程序,所要实现的效果极其简单,只是获取SUBSYSTEM环境变量并打印即可,编写完成的应用程序内容如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd = open("/dev/ttyFIQ0", O_WRONLY);
dup2(fd, STDOUT_FILENO);
printf("SUBSYSTEM is %s\n", getenv("SUBSYSTEM"));
return 0;
}
117.2.2 编译应用程序
下面进行应用程序编译,因为测试APP是要在开发板上运行的,所以需要aarch64-linux-gnu-gcc来编译,输入以下命令,编译完成以后会生成一个mdev的可执行程序,如下图(图 117-7)所示:
aarch64-linux-gnu-gcc -o mdev mdev.c
图 117-7
下面进行程序的测试。
117.2.3 运行测试
本小节测试所使用的驱动文件为115章编译生成的uevent_ops.ko,应用程序为上一小节编译出来的mdev。
mdev可执行文件和uevent_ops.ko驱动存放在开发板的/mnt目录下,如下图(图 117-8)所示:
图 117-8
在第一小节中讲解了三种配置方法,配置方法1是在内核中直接指定的,使用起来较为麻烦,这里直接使用配置方法2 和配置方法3这两种方式进行演示。
配置方法2:
echo /mnt/mdev > /sys/kernel/uevent_helper
然后使用以下命令加载uevent_ops.ko驱动,如下图(图 117-9)所示:
图 117-9
配置方法3:
echo /mnt/mdev > /proc/sys/kernel/hotplug
然后使用以下命令加载uevent_ops.ko驱动,如下图(图 117-10)所示:
图 117-10
上面两种配置都可以打印出内核加载时传递的SUBSYSTEM环境变量,最后可以使用以下命令进行驱动的卸载,如下图(图54-12)所示:
rmmod uevent_ops.ko
图 117-11
至此,uevent_helper实验就完成了。