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");

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/272000.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于合宙Air700E的4G环境监测节点(温湿度、气压等数据),通过MQTT上传阿里云物联网平台

基于合宙Air700E的4G环境监测节点&#xff08;温度、湿度、气压等数据&#xff09;&#xff0c;通过MQTT上传阿里云物联网平台。 介绍 合宙Air700E 4G模块读取传感器&#xff08;温湿度、气压等&#xff09;数据并通过MQTT协议上传阿里云物联网平台&#xff0c;数据也会同时显…

最新9.9付费进群saas系统源码已修复定位及已知bug

距上版&#xff0c;优化一下功能&#xff0c;修复了已知问题及定位功能 1.九块九加群微信裂变人脉吃瓜宝妈同城相亲交友取图表情包 2.支持创建各种付费群&#xff0c;表情&#xff0c;吃瓜&#xff0c;创业&#xff0c;资源等等 3.支付对接第三方易支付&#xff0c;随便一家…

探索 WebRTC:数字世界的实时通信魔法

前言 在当今日常生活中&#xff0c;我们期望能够随时随地与朋友、同事或家人进行实时沟通。WebRTC&#xff08;Web实时通信&#xff09;技术就像一种魔法&#xff0c;让这些交流变得无比便捷&#xff0c;而且完全在浏览器中实现&#xff0c;无需下载任何额外应用或插件。 Web…

《Vue2.X 进阶知识点》- 防 ElementUI Divider 分割线

前言 使用 el-divider 背景为白色是没问题的。 但当背景换成其它颜色&#xff0c;问题就出现了&#xff01;&#xff01; 仔细看原来是两层&#xff0c;默认背景色是白色。 想着把背景色改为透明应该能用&#xff0c;结果发现背面是一条实线&#xff0c;难怪要用白色遮挡…不符…

python实现文件下载上传

一、文件下载到本地 import requests if __name__ __main__:response requests.get("https://dfcv-shop.oss-cn-hangzhou.aliyuncs.com/dfcv-shop0177bf6b34ee46c68be412d04654439c.jpg")local_filename "./dfcv-shop0177bf6b34ee46c68be412d04654439c.jpg&…

基于SpringBoot留守儿童爱心成长守护平台

1 引言 互联网技术的大面积普及使得网络平台成为人们获取信息的主要途径。网络时代的留守儿童们的生活自然与互联网息息相关&#xff0c;因此必然需要符合网络时代特征的新方式来对留守儿童提供帮助[1]。留守儿童平台作为向大众传递信息的一种方式&#xff0c;使得更多人能看到…

Codeforces Pinely Round 3 (Div. 1 + Div. 2)

A.Distinct Buttons(思维) 题意&#xff1a; 你在开始时站在点 ( 0 , 0 ) (0,0) (0,0)&#xff0c;同时&#xff0c;手上有一个遥控器&#xff0c;上面有四个按钮&#xff1a; U:移动到 ( x , y 1 ) (x, y 1) (x,y1)的位置 R:移动到 ( x 1 , y ) (x 1, y) (x1,y)的位置 …

关于“Python”的核心知识点整理大全43

目录 ​编辑 15.2.3 使2散点图并设置其样式 scatter_squares.py 15.2.4 使用 scatter()绘制一系列点 scatter_squares.py 15.2.5 自动计算数据 scatter_squares.py 15.2.6 删除数据点的轮廓 15.2.7 自定义颜色 15.2.8 使用颜色映射 scatter_squares.py 注意 15.2.9…

【ubuntu22.04安装mysql8并配置远程连接】

1.安装mysql 使用管理员权限以下命令安装mysql 1.更新仓库 sudo apt-get update sudo apt-get upgrade2.安装mysql sudo apt install mysql-server3.安装完成之后就可以使用命令 //查看mysql运行状态 sudo systemctl status mysql //登录mysql mysql -uroot -p 然后随便输入一个…

【NI-RIO入门】记录和监控数据

1.内部存储器 可以使用常规文件 I/O VI 在嵌入式程序中以编程方式访问实时控制器的内部存储。文件路径结构根据控制器运行的实时操作系统 (RTOS) 的不同而有所不同。 该文件路径语法记录在教程&#xff1a;使用实时目标上的文件路径 中。 可以通过在Measurement & Automati…

重定向和转发(sendRedirect()和getRequestDispatcher())

重定向 是什么 用户通过浏览器发送一个请求&#xff0c;Tomcat服务器接收这个请求&#xff0c;会给浏览器发送一个状态码302&#xff0c;并设置一个重定向的路径&#xff0c;浏览器如果接收到了这个302的状态码以后&#xff0c;就会去自动加载服务器设置的路径 一个页面跳转…

在小公司 “混” 了2年,我只认真做了5件事,如今顺利拿到字节 Offer

说下我的情况 是的&#xff0c;我一家小公司工作了整整两年时间&#xff0c;在入职这家公司前&#xff0c;也就是两年前&#xff0c;我就开始规划了我自己的人生&#xff0c;所以在两年时间里&#xff0c;我并未懈怠。 现如今&#xff0c;我已经跳槽到了字节&#xff0c;顺利…

Typora使用PicGo+Gitee上传图片

Typora使用PicGoGitee上传图片 1.下载PicGo(国内镜像) https://mirrors.sdu.edu.cn/github-release/Molunerfinn_PicGo/ 点击PicGo-Setup-2.3.0-x64.exe &#xff08;64位安装&#xff09; 然后打开gitee&#xff08;没注册先注册&#xff09; 2.下载node.js插件 https:/…

mysql原理--连接查询的成本

1.准备工作 连接查询至少是要有两个表的&#xff0c;只有一个 single_table 表是不够的&#xff0c;所以为了故事的顺利发展&#xff0c;我们直接构造一个和 single_table 表一模一样的 single_table2 表。为了简便起见&#xff0c;我们把 single_table 表称为 s1 表&#xff0…

USB启动盘是什么?要如何制作USB启动盘?本文都告诉你

如何制作USB启动盘 USB启动盘怎么制作&#xff1f;下面我们一起来看一看。注意&#xff1a;在执行以下步骤之前&#xff0c;请确保您备份了重要数据&#xff0c;因为这个过程会格式化USB驱动器&#xff0c;清除其上的所有数据。1. 选择操作系统镜像 首先&#xff0c;您需要…

MYSQL数据库的备份与恢复-数据库实验七

一、实验目的 1. 了解备份和恢复的基本概念。 2. 掌握使用MySQL命令进行数据库备份的操作方法。 3. 掌握使用MySQL命令进行数据库恢复的操作方法。 二、实验内容 1. 使用mysqldump命令备份数据库studentsdb的所有表&#xff0c;存于D:\下&#xff0c;文件名为all_tables.s…

Unity 旋转跟随

Unity 使用任意一个局部轴指向目标 效果&#xff1a; 主要用于在编辑器中可视化对象的朝向&#xff0c;同时提供了选择不同轴向的功能。在运行时&#xff0c;物体将根据所选择的轴向朝向目标&#xff0c;并在 Scene 视图中绘制一个带箭头的圆环。 定义轴向枚举&#xff1a;…

Node.js版本对比

目录 1. node版本与Npm版本对照表 2. node版本与node-sass版本对照表 3. node-sass与sass-loader版本对照表 1. node版本与Npm版本对照表 以往的版本 | Node.js 下面显示最新的对应内容&#xff0c;如果需要查找历史版本&#xff0c;可以进入上面的页面查询 VersionLTSDateV8np…

【网络安全 | 网络协议】结合Wireshark讲解HTTP协议

前言 超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff0c;HTTP&#xff09;是一个简单的请求-响应协议&#xff0c;它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。 文章目录 前言HTTP协议Wireshark抓包分析 HTTP协议在Wireshark数据包中是…

Ubuntu16.04下载安装藏文字体详细教程(附图)

Ubuntu16.04下安装藏文字体详细教程&#xff08;附图&#xff09; 你是不是也被ubuntu系统中藏文或者中文总是不显示且乱码的问题困扰呢&#xff0c;那么你可以看看我的解决方法。 在没有装藏文或中文字体前你在打开一个文本文件的时候是不是下面这样的 安装步骤 上传或下载若…