设备树插件里面:注册group 实验
文章目录
前言
参考资料
前面设备树插件相关的基本知识点可供参考
Linux驱动-设备树插件语法
设备树插件基础必备
驱动-设备树插件注册子系统
一、注册 group 容器
基础代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
// 定义一个名为"mygroup"的config_group结构体
static struct config_group mygroup;
// 定义一个名为"mygroup_config_item_type"的config_item_type结构体,用于描述配置项类型。
static const struct config_item_type mygroup_config_item_type = {
.ct_owner = THIS_MODULE,
.ct_item_ops = NULL,
.ct_group_ops = NULL,
.ct_attrs = NULL,
};
// 定义名为"myconfig_item_type"的配置项类型结构体
static const struct config_item_type myconfig_item_type = {
.ct_owner = THIS_MODULE,
.ct_group_ops = NULL,
};
// 定义一个configfs_subsystem结构体实例"myconfigfs_subsystem"
static struct configfs_subsystem myconfigfs_subsystem = {
.su_group = {
.cg_item = {
.ci_namebuf = "myconfigfs",
.ci_type = &myconfig_item_type,
},
},
};
// 模块的初始化函数
static int myconfig_group_init(void)
{
// 初始化配置组
config_group_init(&myconfigfs_subsystem.su_group);
// 注册子系统
configfs_register_subsystem(&myconfigfs_subsystem);
// 初始化配置组"mygroup"
config_group_init_type_name(&mygroup, "mygroup", &mygroup_config_item_type);
// 在子系统中注册配置组"mygroup"
configfs_register_group(&myconfigfs_subsystem.su_group, &mygroup);
return 0;
}
// 模块退出函数
static void myconfig_group_exit(void)
{
// 注销子系统
configfs_unregister_subsystem(&myconfigfs_subsystem);
}
module_init(myconfig_group_init); // 指定模块的初始化函数
module_exit(myconfig_group_exit); // 指定模块的退出函数
MODULE_LICENSE("GPL"); // 模块使用的许可证
MODULE_AUTHOR("fangchen"); // 模块的作者
代码分析
这份代码和前面 驱动-设备树插件注册子系统 代码基础上多了一些代码如下:
//第一步
// 定义一个名为"mygroup"的config_group结构体
static struct config_group mygroup;
// 定义一个名为"mygroup_config_item_type"的config_item_type结构体,用于描述配置项类型。
static const struct config_item_type mygroup_config_item_type = {
.ct_owner = THIS_MODULE,
.ct_item_ops = NULL,
.ct_group_ops = NULL,
.ct_attrs = NULL,
};
// 第二步
// 初始化配置组"mygroup"
config_group_init_type_name(&mygroup, "mygroup", &mygroup_config_item_type);
//第三步
// 在子系统中注册配置组"mygroup"
configfs_register_group(&myconfigfs_subsystem.su_group, &mygroup);
其实在之前驱动-设备树插件注册子系统 多了三部分内容:
-
定义组:config_group 和 后面这个组关联的 config_item_type
-
初始化一个 config_group 结构体,设置其名称和类型。
-
config_group 注册到父组中,在 configfs 文件系统中创建对应的目录和属性
实验结果
直接看实际测试结果,如下:路径/sys/kernel/config/myconfigfs/mygroup,生成了:myconfigfs、mygroup 两个组。

API函数详解
函数config_group_init_type_name
功能
初始化一个 config_group 结构体,设置其名称和类型。
函数原型
void config_group_init_type_name(
struct config_group *group,
const char *name,
struct config_item_type *type);
参数详解
- group: 要初始化的 config_group 结构体指针
- name: 该组的名称(将显示在 configfs 文件系统中)
- type: 指向 config_item_type 结构的指针,定义该组的行为和属性
内部实现
- 调用 config_item_init 初始化基础的 config_item
- 设置组的名称
- 初始化组的默认组(default_groups)
- 设置组的 item_type
使用示例
static struct config_group my_group;
static struct config_item_type my_group_type = {
.ct_owner = THIS_MODULE,
// 其他回调函数
};
config_group_init_type_name(&my_group, "my_group", &my_group_type);
函数configfs_register_group
功能
将一个已初始化的 config_group 注册到父组中,在 configfs 文件系统中创建对应的目录和属性。
函数原型
int configfs_register_group(
struct config_group *parent_group,
struct config_group *group);
参数详解
- parent_group: 父组,新组将作为其子组
- group: 要注册的组(必须已通过 config_group_init_type_name 初始化)
内部操作
- 在父组的目录下创建新目录(使用 group 的名称)
- 创建该组的所有属性文件
- 如果定义了默认子组,也会创建它们
使用示例
int ret;
struct config_group *parent = ...; // 获取父组
ret = configfs_register_group(parent, &my_group);
if (ret < 0) {
pr_err("Failed to register group: %d\n", ret);
return ret;
}
典型使用场景
如上,两个API 结合起来用,如下:
// 1. 定义组类型
static struct config_item_type my_group_type = {
.ct_owner = THIS_MODULE,
.ct_item_ops = &my_item_ops,
.ct_attrs = my_group_attrs,
};
// 2. 初始化组
struct config_group my_group;
config_group_init_type_name(&my_group, "my_config", &my_group_type);
// 3. 注册组
int ret = configfs_register_group(parent_group, &my_group);
二、知识扩展一-子系统和组的区别和联系-在子系统中添加组操作
在设备树插件(Device Tree Overlay)中,子系统(Subsystem)和组(Group)是用于模块化管理和动态加载的重要概念。它们的核心区别在于:
-
子系统是逻辑分类(按功能划分,如 i2c、gpio)。
-
组是物理加载单位(一个 .dtbo 文件包含多个节点)。
而在子系统中添加组操作,通常是指通过设备树插件的机制,将一组节点(组)动态绑定到某个子系统中。以下是具体分析和实现方法:
子系统和组的核心区别
| 维度 | 子系统(Subsystem) | 组(Group) |
|---|---|---|
| 定义 | 功能逻辑单元(如 i2c、spi 总线下的设备) | 物理加载单元(一个 .dtbo 文件) |
| 作用范围 | 跨多个设备树插件存在 | 仅限当前插件文件内 |
| 标识方式 | 通过 compatible 属性或节点标签标识 | 通过文件名(如 my_group.dtbo)标识 |
| 操作对象 | 内核驱动通过子系统匹配设备 | 用户或脚本通过 dtc 或内核接口加载/卸载 |
在子系统中添加组的操作
第一种方案:如上,写一个驱动,动态实现。
第二种方案:前面讲过的知识点,在设备树插件中.dts 文件中描述,生成dtbo,然后命令实现。
- 定义设备树插件(组)
在插件文件中,将节点挂载到目标子系统(如 i2c1)下:
// 文件:sensors.dtbo.dts
/dts-v1/;
/plugin/;
&i2c1 { // 目标子系统:i2c1
#address-cells = <1>;
#size-cells = <0>;
/* 在i2c子系统中添加一组设备 */
temp_sensor: temp@48 {
compatible = "ti,tmp75";
reg = <0x48>;
};
humidity_sensor: humidity@49 {
compatible = "sensirion,sht3x";
reg = <0x49>;
};
};
- 编译并加载组
将插件编译为 .dtbo 文件,并动态加载到内核:
dtc -@ -I dts -O dtb -o sensors.dtbo sensors.dtbo.dts
echo sensors.dtbo > /sys/kernel/config/device-tree/overlays/load
- 验证子系统中的组
加载后,新的设备节点会出现在 i2c1 子系统中:
ls /sys/bus/i2c/devices/ # 查看i2c1下的设备
cat /proc/device-tree/i2c@40000000/temp@48/compatible # 验证节点
三、 知识扩展二-数据结构_关联关系_在此注册group
对于设备树插件,每次写代码头疼,也不知道怎么写,看似就是一个简单的树结构,看看其它人的文章知识点和视频 也会绕晕。 基础还是补一补,加深印象,日积月累吧!
核心数据结构及关联关系
(1) config_item
-
作用:基础配置单元(如参数、设备等)。
-
关键成员:
struct config_item {
char ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; // 名称缓冲区(默认20字节)
struct config_group *ci_group; // 所属父group
struct config_item_type *ci_type; // 关联的操作和属性
};
- ci_namebuf:存储该item在configfs中的显示名称(如echo “name” > /config/path/item时会写入此缓冲区)。
(2) config_group
-
作用:既是config_item(继承),又是其他item/group的容器。
-
关键成员:
struct config_group {
struct config_item cg_item; // 内嵌的config_item
struct list_head cg_children; // 子item/group链表
};
- 关联关系:
通过cg_item.ci_group指向父group。
子项通过cg_children链表管理。
(3) config_item_type
-
作用:定义item/group的类型行为(操作和属性)。
-
结构体:
struct config_item_type {
const struct configfs_item_operations *ct_item_ops; // item操作(如show/store)
const struct configfs_group_operations *ct_group_ops; // group操作(如make_group)
struct configfs_attribute **ct_attrs; // 属性数组
};
- 关键操作:
ct_item_ops:处理item的读写(如show_attribute、store_attribute)。
ct_group_ops:处理group的动态创建(如make_group)。
(4) configfs_attribute
-
作用:定义item/group的属性文件(在configfs中表现为文件)。
-
结构体:
struct configfs_attribute {
char *ca_name; // 属性文件名(如"value")
umode_t ca_mode; // 文件权限(如0644)
};
- 关联关系:通过config_item_type.ct_attrs数组关联到item/group。
(5) configfs_subsystem
-
作用:子系统的顶级描述符。
-
结构体:
struct configfs_subsystem {
struct config_group su_group; // 根group
struct mutex su_mutex; // 互斥锁
};
首次注册子系统
static struct configfs_attribute root_attr = {
.ca_name = "version",
.ca_mode = S_IRUGO,
};
static struct configfs_attribute *root_attrs[] = {
&root_attr,
NULL,
};
static struct config_item_type root_group_type = {
.ct_attrs = root_attrs,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem my_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "my_subsystem", // 根group名称
.ci_type = &root_group_type, // 关联类型
},
},
};
static int __init my_init(void) {
config_group_init(&my_subsys.su_group);
configfs_register_subsystem(&my_subsys);
return 0;
}
结果:在/config下创建目录my_subsystem,并包含属性文件version。 这个基础代码之前就了解过,基本操作没有什么特别要讲的
二次注册Group(子Group)
这里有部分内容是需要后面讲解的,先了解下吧
在已注册的子系统下添加子group,需以下步骤:
- (1) 定义子Group及其Item
// 子group的属性
static ssize_t child_attr_show(struct config_item *item, char *buf) {
return sprintf(buf, "Hello from child!\n");
}
static struct configfs_attribute child_attr = {
.ca_name = "message",
.ca_mode = S_IRUGO,
};
static struct configfs_attribute *child_attrs[] = {
&child_attr,
NULL,
};
// 子group的item操作
static struct configfs_item_operations child_item_ops = {
.show_attribute = child_attr_show,
};
// 子group的类型
static struct config_item_type child_group_type = {
.ct_item_ops = &child_item_ops,
.ct_attrs = child_attrs,
.ct_owner = THIS_MODULE,
};
ci_namebuf = “my_dto_plugin”:
子系统的名称,会在 /config/ 下生成对应的目录(如 /config/my_dto_plugin)。
- (2) 实现父Group的make_group回调
static struct config_group *create_child(struct config_group *parent, const char *name) {
struct config_group *child = kzalloc(sizeof(*child), GFP_KERNEL);
if (!child) return ERR_PTR(-ENOMEM);
config_group_init_type_name(child, name, &child_group_type);
return child;
}
static struct configfs_group_operations parent_group_ops = {
.make_group = create_child, // 动态创建子group
};
static struct config_item_type parent_group_type = {
.ct_group_ops = &parent_group_ops,
.ct_owner = THIS_MODULE,
};
// 在初始化时设置父group类型
my_subsys.su_group.cg_item.ci_type = &parent_group_type;
- 用户态触发子Group创建
# 创建子group
mkdir /config/my_subsystem/child_group
# 查看属性
cat /config/my_subsystem/child_group/message
# 输出: Hello from child!
关键关联关系图示
configfs_subsystem
│
├── su_group (config_group)
│ ├── cg_item (config_item)
│ │ ├── ci_namebuf = "my_subsystem"
│ │ ├── ci_type = &parent_group_type
│ │ └── ci_group = NULL (根group)
│ │
│ ├── ct_group_ops.make_group = create_child
│ └── cg_children (链表)
│ └── child_group (config_group)
│ ├── cg_item.ci_namebuf = "child_group"
│ ├── cg_item.ci_type = &child_group_type
│ ├── ct_attrs = [&child_attr]
│ └── ct_item_ops.show_attribute = child_attr_show
│
└── su_mutex (保护并发)
总结
- 进一步理解了子系统和组的概念、联系、如何注册的
- 学习了两个api 初始化组:
config_group_init_type_name,组注册到子系统configfs_register_group - 通过这个简单的新增子group案例,再次分析细节基础知识
2969

被折叠的 条评论
为什么被折叠?



