RK3568驱动指南|第八篇 设备树插件-第84章设备树插件参考资料介绍

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


第84章设备树插件参考资料介绍

通过上述章节的学习,设备树插件的知识已经学习完了,本章节将介绍设备树插件其他的一些参考资料。

在linux源码中linux_sdk/kernel/Documentation/filesystems/configfs目录下的configfs.txt。

内容解释如下所示:

[什么是configfs?]
configfs是一种基于RAM的文件系统,提供了与sysfs功能相反的功能。sysfs是内核对象的基于文件系统的视图,而configfs是内核对象(或config_items)的基于文件系统的管理器。
使用sysfs时,在内核中创建一个对象(例如,当发现设备时),并将其注册到sysfs中。然后,它的属性会出现在sysfs中,允许用户空间通过readdir(3)/read(2)读取属性。它可能允许通过write(2)修改某些属性。重要的是,对象在内核中创建和销毁,内核控制sysfs表示的生命周期,而sysfs只是对所有这些的一种窗口。
通过显式的用户空间操作(例如mkdir(2)),可以创建一个configfs config_item。通过rmdir(2)销毁它。属性在mkdir(2)时出现,并且可以通过read(2)和write(2)读取或修改。与sysfs类似,readdir(3)查询项目和/或属性的列表。可以使用symlink(2)将项目组合在一起。与sysfs不同,表示的生命周期完全由用户空间驱动。支持项目的内核模块必须响应此操作。
sysfs和configfs可以并且应该在同一系统上同时存在。它们不是彼此的替代品。

【使用configfs】

configfs可以作为模块编译或集成到内核中。您可以通过以下方式访问它:
	mount -t configfs none /config
除非也加载了客户端模块,否则configfs树将为空。这些模块将其项目类型注册为configfs的子系统。一旦加载了客户端子系统,它将显示为/config下的一个或多个子目录。与sysfs一样,configfs树始终存在,无论是否挂载在/config上。
可以通过mkdir(2)创建项目。项目的属性也将同时出现。readdir(3)可以确定属性是什么,read(2)可以查询其默认值,write(2)可以存储新值。不要在一个属性文件中混合多个属性。
configfs有两种类型的属性:

普通属性(Normal attributes)类似于sysfs属性,是小型的ASCII文本文件,最大大小为一页(PAGE_SIZE,在i386上为4096)。最好每个文件只使用一个值,并且与sysfs相同的注意事项也适用。configfs期望write(2)一次存储整个缓冲区。当写入普通configfs属性时,用户空间进程应首先读取整个文件,修改要更改的部分,然后将整个缓冲区写回。
二进制属性(Binary attributes)与sysfs二进制属性类似,但语义上有一些细微的变化。不适用PAGE_SIZE限制,但整个二进制项必须适应单个内核vmalloc的缓冲区。来自用户空间的write(2)调用是缓冲的,并且在最终关闭时将调用属性的write_bin_attribute方法,因此用户空间必须检查close(2)的返回代码以验证操作是否成功完成。为避免恶意用户OOM(Out of Memory)内核,有每个二进制属性的最大缓冲区值。
当需要销毁项目时,使用rmdir(2)将其删除。如果任何其他项目通过symlink(2)链接到它,则无法销毁该项目。可以使用unlink(2)删除链接。

【配置FakeNBD:一个示例】
假设有一个网络块设备(Network Block Device,NBD)驱动程序,允许您访问远程块设备。将其称为FakeNBD。FakeNBD使用configfs进行配置。显然,将有一个方便的程序供系统管理员使用来配置FakeNBD,但某种方式下,该程序必须告知驱动程序。这就是configfs的用武之地。
加载FakeNBD驱动程序时,它会向configfs注册自己。readdir(3)可以看到这一点:
	# ls /config
	fakenbd
可以使用mkdir(2)创建fakenbd连接。名称是任意的,但工具可能会对名称进行一些处理。也许它是一个UUID或磁盘名称:
	# mkdir /config/fakenbd/disk1
	# ls /config/fakenbd/disk1
	target device rw
target属性包含FakeNBD将连接到的服务器的IP地址。device属性是服务器上的设备。可预测的是,rw属性确定连接是只读还是读写。
	# echo 10.0.0.1 > /config/fakenbd/disk1/target
	# echo /dev/sda1 > /config/fakenbd/disk1/device
	# echo 1 > /config/fakenbd/disk1/rw

target属性包含FakeNBD将连接到的服务器的IP地址。device属性是服务器上的设备。可预测的是,rw属性确定连接是只读还是读写。
【使用configfs进行编码】
configfs中的每个对象都是一个config_item。config_item反映了子系统中的一个对象。它具有与该对象上的值相匹配的属性。configfs处理该对象及其属性的文件系统表示,使得子系统只需关注基本的show/store交互。
项目是在config_group内创建和销毁的。组是共享相同属性和操作的项目集合。项目通过mkdir(2)创建,并通过rmdir(2)移除,但configfs会处理这些操作。组具有一组操作来执行这些任务。
子系统是客户端模块的顶层。在初始化过程中,客户端模块向configfs注册子系统,该子系统将在configfs文件系统的顶部显示为一个目录。子系统也是一个config_group,并且可以执行config_group的所有功能。
[struct config_item]

	struct config_item {
		char                    *ci_name;
		char                    ci_namebuf[UOBJ_NAME_LEN];
		struct kref             ci_kref;
		struct list_head        ci_entry;
		struct config_item      *ci_parent;
		struct config_group     *ci_group;
		struct config_item_type *ci_type;
		struct dentry           *ci_dentry;
	};

	void config_item_init(struct config_item *);
	void config_item_init_type_name(struct config_item *,
					const char *name,
					struct config_item_type *type);
	struct config_item *config_item_get(struct config_item *);
	void config_item_put(struct config_item *);

通常,struct config_item被嵌入在一个容器结构中,这个结构实际上代表了子系统正在做的事情。该结构中的config_item部分是对象与configfs进行交互的方式。
无论是在源文件中静态定义还是由父config_group创建,config_item都必须调用其中一个_init()函数。这将初始化引用计数并设置适当的字段。
所有使用config_item的用户都应该通过config_item_get()对其进行引用,并在完成后通过config_item_put()释放引用。
单独一个config_item不能做更多的事情,只能出现在configfs中。通常,子系统希望该项显示和/或存储属性,以及其他一些操作。为此,它需要一个类型。

[struct config_item_type]

	struct configfs_item_operations {
		void (*release)(struct config_item *);
		int (*allow_link)(struct config_item *src,
				  struct config_item *target);
		void (*drop_link)(struct config_item *src,
				 struct config_item *target);
	};

	struct config_item_type {
		struct module                           *ct_owner;
		struct configfs_item_operations         *ct_item_ops;
		struct configfs_group_operations        *ct_group_ops;
		struct configfs_attribute               **ct_attrs;
		struct configfs_bin_attribute		**ct_bin_attrs;
	};
config_item_type的最基本功能是定义可以在config_item上执行的操作。所有动态分配的项目都需要提供ct_item_ops->release()方法。当config_item的引用计数达到零时,将调用此方法。

[struct configfs_attribute]

	struct configfs_attribute {
		char                    *ca_name;
		struct module           *ca_owner;
		umode_t                  ca_mode;
		ssize_t (*show)(struct config_item *, char *);
		ssize_t (*store)(struct config_item *, const char *, size_t);
	};
当config_item希望将属性显示为项目的configfs目录中的文件时,它必须定义一个描述该属性的configfs_attribute。然后将属性添加到以NULL结尾的config_item_type->ct_attrs数组中。当项目出现在configfs中时,属性文件将显示为configfs_attribute->ca_name文件名。configfs_attribute->ca_mode指定文件权限。
如果属性是可读的并提供了一个->show方法,每当用户空间请求对属性进行read(2)时,该方法将被调用。如果属性是可写的并提供了一个->store方法,每当用户空间请求对属性进行write(2)时,该方法将被调用。
[struct configfs_bin_attribute]

	struct configfs_attribute {
		struct configfs_attribute	cb_attr;
		void				*cb_private;
		size_t				cb_max_size;
	};
当需要使用二进制数据块作为文件内容显示在项目的configfs目录中时,可以使用二进制属性(binary attribute)。为此,将二进制属性添加到以NULL结尾的config_item_type->ct_bin_attrs数组中,当项目出现在configfs中时,属性文件将显示为configfs_bin_attribute->cb_attr.ca_name文件名。configfs_bin_attribute->cb_attr.ca_mode指定文件权限。
cb_private成员供驱动程序使用,而cb_max_size成员指定要使用的vmalloc缓冲区的最大大小。
如果二进制属性是可读的,并且config_item提供了ct_item_ops->read_bin_attribute()方法,那么每当用户空间请求对属性进行read(2)时,该方法将被调用。对于write(2),也会发生相反的情况。读取/写入是缓冲的,因此只会发生单个读取/写入;属性本身不需要关心这一点。
[struct config_group]
config_item不能独立存在。创建config_item的唯一方法是通过在config_group上执行mkdir(2)操作。这将触发创建子项。
	struct config_group {
		struct config_item		cg_item;
		struct list_head		cg_children;
		struct configfs_subsystem 	*cg_subsys;
		struct list_head		default_groups;
		struct list_head		group_entry;
	};

	void config_group_init(struct config_group *group);
	void config_group_init_type_name(struct config_group *group,
					 const char *name,
					 struct config_item_type *type);


config_group结构包含一个config_item。适当配置该项意味着组可以作为一个独立的项进行操作。然而,它还可以做更多的事情:它可以创建子项或子组。这是通过在组的config_item_type上指定的组操作来实现的。

	struct configfs_group_operations {
		struct config_item *(*make_item)(struct config_group *group,
						 const char *name);
		struct config_group *(*make_group)(struct config_group *group,
						   const char *name);
		int (*commit_item)(struct config_item *item);
		void (*disconnect_notify)(struct config_group *group,
					  struct config_item *item);
		void (*drop_item)(struct config_group *group,
				  struct config_item *item);
	};
组通过提供ct_group_ops->make_item()方法来创建子项。如果提供了该方法,它将在组的目录中的mkdir(2)操作中调用。子系统分配一个新的config_item(或更常见的是其容器结构),对其进行初始化,并将其返回给configfs。然后,configfs将填充文件系统树以反映新的项。
如果子系统希望子项本身成为一个组,子系统将提供ct_group_ops->make_group()。其他操作与之相同,在组上使用组的_init()函数。
最后,当用户空间对项或组调用rmdir(2)时,将调用ct_group_ops->drop_item()。由于config_group也是一个config_item,因此不需要单独的drop_group()方法。子系统必须对在项分配时初始化的引用执行config_item_put()。如果子系统没有其他工作要执行,可以省略ct_group_ops->drop_item()方法,configfs将代表子系统对项执行config_item_put()。
重要提示:drop_item()是void类型的,因此无法失败。当调用rmdir(2)时,configfs将从文件系统树中删除该项(假设没有子项)。子系统负责对此作出响应。如果子系统在其他线程中引用该项,则内存是安全的。实际上,该项从子系统的使用中消失可能需要一些时间。但它已经从configfs中消失了。
当调用drop_item()时,项的链接已经被拆除。它不再引用其父项,并且在项层次结构中没有位置。如果客户端在此拆除发生之前需要进行一些清理工作,子系统可以实现ct_group_ops->disconnect_notify()方法。该方法在configfs将项从文件系统视图中删除之后、但在将项从其父组中删除之前调用。与drop_item()一样,disconnect_notify()是void类型的,不会失败。客户端子系统不应在此处删除任何引用,因为它们仍然必须在drop_item()中执行。
只要config_group仍然具有子项,就无法删除它。这在configfs的rmdir(2)代码中实现。不会调用->drop_item(),因为项尚未被删除。rmdir(2)将失败,因为目录不为空。

[struct configfs_subsystem]
子系统通常在module_init时间注册自身。这告诉configfs将子系统显示在文件树中。
	struct configfs_subsystem {
		struct config_group	su_group;
		struct mutex		su_mutex;
	};
	int configfs_register_subsystem(struct configfs_subsystem *subsys);
	void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
	一个子系统由一个顶级的config_group和一个互斥锁组成。config_group是创建子config_item的地方。对于子系统来说,这个组通常是静态定义的。在调用configfs_register_subsystem()之前,子系统必须通过常规的group_init()函数对组进行初始化,并且还必须初始化互斥锁。
当注册调用返回时,子系统将处于活动状态,并且将通过configfs可见。此时,可以调用mkdir(2),子系统必须准备好接收该调用。
【示例】
这些基本概念的最佳示例是在samples/configfs/configfs_sample.c中的simple_children子系统/组和simple_child项。它展示了一个简单的对象,显示和存储属性,以及一个简单的组来创建和销毁这些子项。
【层次结构导航和子系统互斥锁】
configfs提供了一个额外的功能。由于config_groups和config_items出现在文件系统中,它们按层次结构排列。子系统永远不会触及文件系统的部分,但子系统可能对此层次结构感兴趣。因此,层次结构通过config_group->cg_children和config_item->ci_parent结构成员进行镜像。
子系统可以通过cg_children列表和ci_parent指针遍历子系统创建的树。这可能与configfs对层次结构的管理发生竞争,因此configfs使用子系统互斥锁来保护修改操作。每当子系统想要遍历层次结构时,必须在子系统互斥锁的保护下进行。
在新分配的项尚未链接到该层次结构时,子系统将无法获取互斥锁。类似地,在放弃项尚未取消链接时,它也无法获取互斥锁。这意味着项的ci_parent指针在项位于configfs中时永远不会为NULL,并且项仅在其父项的cg_children列表中存在相同的时间段。这允许子系统在持有互斥锁时信任ci_parent和cg_children。
【通过symlink(2)进行项聚合】

configfs通过group->item父/子关系提供了一个简单的组。然而,通常需要在父/子连接之外进行聚合。这是通过symlink(2)实现的。
config_item可以提供ct_item_ops->allow_link()和ct_item_ops->drop_link()方法。如果存在->allow_link()方法,可以使用config_item作为链接源调用symlink(2)。这些链接仅允许在configfs config_item之间创建。任何在configfs文件系统之外的symlink(2)尝试都将被拒绝。
调用symlink(2)时,将使用源config_item的->allow_link()方法和自身和目标项作为参数。如果源项允许链接到目标项,则返回0。如果源项只希望链接到某种类型的对象(例如,在其自己的子系统中),则可以拒绝链接。
在符号链接上调用unlink(2)时,将通过->drop_link()方法通知源项。与->drop_item()方法一样,这是一个无返回值的函数,无法返回失败。子系统负责响应该更改。
在任何项链接到其他项时,无法删除config_item,也无法在有项链接到它时删除config_item。在configfs中,不允许存在悬空的符号链接。
【自动创建的子组】
新的config_group可能希望具有两种类型的子config_item。虽然可以通过->make_item()中的魔术名称来编码这一点,但更明确的做法是让用户空间看到这种分歧的方法。
configfs提供了一种方法,通过该方法可以在父级创建时自动在其中创建一个或多个子组。因此,mkdir("parent")将导致创建"parent"、"parent/subgroup1",一直到"parent/subgroupN"。类型为1的项现在可以在"parent/subgroup1"中创建,类型为N的项可以在"parent/subgroupN"中创建。
这些自动创建的子组,或默认组,不排除父组的其他子项。如果存在ct_group_ops->make_group(),可以直接在父组上创建其他子组。
通过向父config_group结构添加它们,configfs子系统可以指定默认组。这是一个关于configfs的C代码示例,它展示了configfs的一些基本概念和用法。
首先,在代码中定义了一个名为"simple_children"的子系统/组和一个名为"simple_child"的项。这个简单的对象展示了如何显示和存储属性,并且使用一个简单的组来创建和销毁这些子项。
configfs中的子系统和组是以层次结构排列的,可以通过config_group->cg_children和config_item->ci_parent来遍历子系统创建的树。为了保护修改操作,configfs使用了子系统互斥锁。
在代码中还使用了symlink(2)函数来创建项之间的链接。config_item可以提供allow_link()和drop_link()方法来控制链接的创建和删除。使用symlink(2)函数创建的链接只允许在configfs中的config_item之间创建,对于configfs文件系统之外的symlink(2)尝试会被拒绝。
此外,代码中还介绍了自动创建的子组的概念。通过在父级创建时自动创建一个或多个子组,可以更方便地组织config_item。这些自动创建的子组不会排除父组的其他子项。
这只是一个简单的示例,用于介绍configfs的基本概念和用法。实际使用configfs时,可能会有更复杂的场景和用法。

Linux内核源码linux_sdk/kernel/samples/configfs目录下的configfs_sample.c,如下所示:

/*
 * vim: noexpandtab ts=8 sts=0 sw=8:
 *
 * configfs_example_macros.c - This file is a demonstration module
 *      containing a number of configfs subsystems.  It uses the helper
 *      macros defined by configfs.h
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 *
 * Based on sysfs:
 * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
 *
 * configfs Copyright (C) 2005 Oracle.  All rights reserved.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>

#include <linux/configfs.h>



/*
 * 01-childless
 *
 * This first example is a childless subsystem.  It cannot create
 * any config_items.  It just has attributes.
 *
 * Note that we are enclosing the configfs_subsystem inside a container.
 * This is not necessary if a subsystem has no attributes directly
 * on the subsystem.  See the next example, 02-simple-children, for
 * such a subsystem.
 */

struct childless {
	struct configfs_subsystem subsys;
	int showme;
	int storeme;
};

static inline struct childless *to_childless(struct config_item *item)
{
	return item ? container_of(to_configfs_subsystem(to_config_group(item)),
			struct childless, subsys) : NULL;
}

static ssize_t childless_showme_show(struct config_item *item, char *page)
{
	struct childless *childless = to_childless(item);
	ssize_t pos;

	pos = sprintf(page, "%d\n", childless->showme);
	childless->showme++;

	return pos;
}

static ssize_t childless_storeme_show(struct config_item *item, char *page)
{
	return sprintf(page, "%d\n", to_childless(item)->storeme);
}

static ssize_t childless_storeme_store(struct config_item *item,
		const char *page, size_t count)
{
	struct childless *childless = to_childless(item);
	unsigned long tmp;
	char *p = (char *) page;

	tmp = simple_strtoul(p, &p, 10);
	if (!p || (*p && (*p != '\n')))
		return -EINVAL;

	if (tmp > INT_MAX)
		return -ERANGE;

	childless->storeme = tmp;

	return count;
}

static ssize_t childless_description_show(struct config_item *item, char *page)
{
	return sprintf(page,
"[01-childless]\n"
"\n"
"The childless subsystem is the simplest possible subsystem in\n"
"configfs.  It does not support the creation of child config_items.\n"
"It only has a few attributes.  In fact, it isn't much different\n"
"than a directory in /proc.\n");
}

CONFIGFS_ATTR_RO(childless_, showme);
CONFIGFS_ATTR(childless_, storeme);
CONFIGFS_ATTR_RO(childless_, description);

static struct configfs_attribute *childless_attrs[] = {
	&childless_attr_showme,
	&childless_attr_storeme,
	&childless_attr_description,
	NULL,
};

static const struct config_item_type childless_type = {
	.ct_attrs	= childless_attrs,
	.ct_owner	= THIS_MODULE,
};

static struct childless childless_subsys = {
	.subsys = {
		.su_group = {
			.cg_item = {
				.ci_namebuf = "01-childless",
				.ci_type = &childless_type,
			},
		},
	},
};


/* ----------------------------------------------------------------- */

/*
 * 02-simple-children
 *
 * This example merely has a simple one-attribute child.  Note that
 * there is no extra attribute structure, as the child's attribute is
 * known from the get-go.  Also, there is no container for the
 * subsystem, as it has no attributes of its own.
 */

struct simple_child {
	struct config_item item;
	int storeme;
};

static inline struct simple_child *to_simple_child(struct config_item *item)
{
	return item ? container_of(item, struct simple_child, item) : NULL;
}

static ssize_t simple_child_storeme_show(struct config_item *item, char *page)
{
	return sprintf(page, "%d\n", to_simple_child(item)->storeme);
}

static ssize_t simple_child_storeme_store(struct config_item *item,
		const char *page, size_t count)
{
	struct simple_child *simple_child = to_simple_child(item);
	unsigned long tmp;
	char *p = (char *) page;

	tmp = simple_strtoul(p, &p, 10);
	if (!p || (*p && (*p != '\n')))
		return -EINVAL;

	if (tmp > INT_MAX)
		return -ERANGE;

	simple_child->storeme = tmp;

	return count;
}

CONFIGFS_ATTR(simple_child_, storeme);

static struct configfs_attribute *simple_child_attrs[] = {
	&simple_child_attr_storeme,
	NULL,
};

static void simple_child_release(struct config_item *item)
{
	kfree(to_simple_child(item));
}

static struct configfs_item_operations simple_child_item_ops = {
	.release		= simple_child_release,
};

static const struct config_item_type simple_child_type = {
	.ct_item_ops	= &simple_child_item_ops,
	.ct_attrs	= simple_child_attrs,
	.ct_owner	= THIS_MODULE,
};


struct simple_children {
	struct config_group group;
};

static inline struct simple_children *to_simple_children(struct config_item *item)
{
	return item ? container_of(to_config_group(item),
			struct simple_children, group) : NULL;
}

static struct config_item *simple_children_make_item(struct config_group *group,
		const char *name)
{
	struct simple_child *simple_child;

	simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL);
	if (!simple_child)
		return ERR_PTR(-ENOMEM);

	config_item_init_type_name(&simple_child->item, name,
				   &simple_child_type);

	simple_child->storeme = 0;

	return &simple_child->item;
}

static ssize_t simple_children_description_show(struct config_item *item,
		char *page)
{
	return sprintf(page,
"[02-simple-children]\n"
"\n"
"This subsystem allows the creation of child config_items.  These\n"
"items have only one attribute that is readable and writeable.\n");
}

CONFIGFS_ATTR_RO(simple_children_, description);

static struct configfs_attribute *simple_children_attrs[] = {
	&simple_children_attr_description,
	NULL,
};

static void simple_children_release(struct config_item *item)
{
	kfree(to_simple_children(item));
}

static struct configfs_item_operations simple_children_item_ops = {
	.release	= simple_children_release,
};

/*
 * Note that, since no extra work is required on ->drop_item(),
 * no ->drop_item() is provided.
 */
static struct configfs_group_operations simple_children_group_ops = {
	.make_item	= simple_children_make_item,
};

static const struct config_item_type simple_children_type = {
	.ct_item_ops	= &simple_children_item_ops,
	.ct_group_ops	= &simple_children_group_ops,
	.ct_attrs	= simple_children_attrs,
	.ct_owner	= THIS_MODULE,
};

static struct configfs_subsystem simple_children_subsys = {
	.su_group = {
		.cg_item = {
			.ci_namebuf = "02-simple-children",
			.ci_type = &simple_children_type,
		},
	},
};


/* ----------------------------------------------------------------- */

/*
 * 03-group-children
 *
 * This example reuses the simple_children group from above.  However,
 * the simple_children group is not the subsystem itself, it is a
 * child of the subsystem.  Creation of a group in the subsystem creates
 * a new simple_children group.  That group can then have simple_child
 * children of its own.
 */

static struct config_group *group_children_make_group(
		struct config_group *group, const char *name)
{
	struct simple_children *simple_children;

	simple_children = kzalloc(sizeof(struct simple_children),
				  GFP_KERNEL);
	if (!simple_children)
		return ERR_PTR(-ENOMEM);

	config_group_init_type_name(&simple_children->group, name,
				    &simple_children_type);

	return &simple_children->group;
}

static ssize_t group_children_description_show(struct config_item *item,
		char *page)
{
	return sprintf(page,
"[03-group-children]\n"
"\n"
"This subsystem allows the creation of child config_groups.  These\n"
"groups are like the subsystem simple-children.\n");
}

CONFIGFS_ATTR_RO(group_children_, description);

static struct configfs_attribute *group_children_attrs[] = {
	&group_children_attr_description,
	NULL,
};

/*
 * Note that, since no extra work is required on ->drop_item(),
 * no ->drop_item() is provided.
 */
static struct configfs_group_operations group_children_group_ops = {
	.make_group	= group_children_make_group,
};

static const struct config_item_type group_children_type = {
	.ct_group_ops	= &group_children_group_ops,
	.ct_attrs	= group_children_attrs,
	.ct_owner	= THIS_MODULE,
};

static struct configfs_subsystem group_children_subsys = {
	.su_group = {
		.cg_item = {
			.ci_namebuf = "03-group-children",
			.ci_type = &group_children_type,
		},
	},
};

/* ----------------------------------------------------------------- */

/*
 * We're now done with our subsystem definitions.
 * For convenience in this module, here's a list of them all.  It
 * allows the init function to easily register them.  Most modules
 * will only have one subsystem, and will only call register_subsystem
 * on it directly.
 */
static struct configfs_subsystem *example_subsys[] = {
	&childless_subsys.subsys,
	&simple_children_subsys,
	&group_children_subsys,
	NULL,
};

static int __init configfs_example_init(void)
{
	int ret;
	int i;
	struct configfs_subsystem *subsys;

	for (i = 0; example_subsys[i]; i++) {
		subsys = example_subsys[i];

		config_group_init(&subsys->su_group);
		mutex_init(&subsys->su_mutex);
		ret = configfs_register_subsystem(subsys);
		if (ret) {
			printk(KERN_ERR "Error %d while registering subsystem %s\n",
			       ret,
			       subsys->su_group.cg_item.ci_namebuf);
			goto out_unregister;
		}
	}

	return 0;

out_unregister:
	for (i--; i >= 0; i--)
		configfs_unregister_subsystem(example_subsys[i]);

	return ret;
}

static void __exit configfs_example_exit(void)
{
	int i;

	for (i = 0; example_subsys[i]; i++)
		configfs_unregister_subsystem(example_subsys[i]);
}

module_init(configfs_example_init);
module_exit(configfs_example_exit);
MODULE_LICENSE("GPL");

上面的驱动文件,大家可以好好分析下代码。至此,设备树模型课程学习完毕。


最近更新

  1. TCP协议是安全的吗?

    2023-12-26 17:38:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-26 17:38:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-26 17:38:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-26 17:38:03       20 阅读

热门阅读

  1. js 学习

    2023-12-26 17:38:03       26 阅读
  2. ebpf基础篇(一) -------- hello ebpf

    2023-12-26 17:38:03       38 阅读
  3. 服务器的出口IP地址查询

    2023-12-26 17:38:03       33 阅读
  4. LeetCode 75| 前缀和

    2023-12-26 17:38:03       40 阅读
  5. MFC或QT中,自绘控件的目的和实现步骤

    2023-12-26 17:38:03       33 阅读
  6. 关于 log4net 日志功能使用方法

    2023-12-26 17:38:03       27 阅读
  7. ecology-SQL优化技巧

    2023-12-26 17:38:03       39 阅读
  8. vue2对象丢失响应式解决办法

    2023-12-26 17:38:03       38 阅读
  9. C/C++常见面试题(五)

    2023-12-26 17:38:03       28 阅读