瑞芯微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");
上面的驱动文件,大家可以好好分析下代码。至此,设备树模型课程学习完毕。