Linux驱动开发(速记版)--设备树插件

第六十八章 设备树插件介绍

        Linux 4.4之后引入了动态设备树,其中的设备树插件(Device Tree Overlay)是一种扩展机制,允许在运行时动态添加、修改或删除设备节点和属性

        设备树插件机制通过DTS(设备树源文件)定义,提供了一种灵活配置硬件设备的方式,无需重新编译整个设备树,也无需重启系统即可进行硬件配置更改。

        在 linux 源码中 linux_sdk/kernel/Documentation/filesystems/configfs 目录下的 configfs.txt。是设备树插件的帮助文档

第六十九章 设备树插件语法和编译实验

69.1 设备树插件语法

        设备树插件的语法格式基于设备树源文件的语法,但是有一些特定的语法和指令用于描述插件的行为。

        我们新建 overlay.dts,

1 首先添加插件头部声明,它指定了插件的名称和版本等信息,并指定了要修改的设备树的路径,

/dts-v1/;  
/plugin/;  
// 插件头部声明(示例,实际内容根据需求填写)  
/plugin/name: "my_overlay";  
/plugin/version: "1.0";  
/plugin/target-path: "/"; // 目标设备树路径

2 插件节点名称用于指定要操作的设备节点及其属性。

        假设需要编写插件的设备树节点如下:

rk_485_ctl:rk-485-ctl{compatible = "topeet,rs485_ctl";gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;pinctrl-names = "default";pinctrl-0 = <&rk_485_gpio>;
}

        设备树插件有以下四种写法。 了解即可。

/dts-v1/;
/plugin/;&{/rk-485-ctl}{          或者   &rk_485_ctl{overlay_node{status = "okay";};
};/{fragment@0{target-path = "rk-485-cl";  或者 target= <rk-485-cl>;__overlay__{overlay_node{status = "okay";}}}
}

69.2 设备树插件编译

        使用方法一,然后编译设备树插件 overlay.dts,输入以下命令:

/home/topeet/Linux/linux_sdk/kernel/scripts/dtc/dtc-I dts          //输入文件的格式-O dtb          //输出文件的格式overlay.dts     //输入的源文件-o overlay.dtbo //输出文件的名称和路径

        反编译设备树,输入以下命令:

/home/topeet/Linux/linux_sdk/kernel/scripts/dtc/dtc -I dtb -O dts overlay.dtbo -o 1.dts

第七十章 设备树插件使用

70.1 准备实验环境

        要求内核镜像支持设备树插件,一般要求内核版本4.4以上。

        使用“insmod xxx.ko”命令加载设备树插件驱动。

        使用 cat /proc/filesystems 查看当前内核支持的文件系统类型的列表

cat /proc/filesystems 当前内核支持的文件系统类型列表

        在Linux系统中,/proc/filesystems文件是一个虚拟文件,它提供了当前内核支持的文件系统类型的列表。这个文件是由内核动态生成的,并且只存在于内存中,不占用磁盘空间。

        输出中的每一行都代表一个文件系统类型,前面的 nodev表示该文件系统类型不支持在磁盘上作为设备挂载(即它不是基于块设备的文件系统)。没有nodev前缀的文件系统类型则通常可以挂载在磁盘设备或其他类型的文件系统之上。

70.2 设备树插件的使用

        在上一章节中,我们编写了 overlay.dts。

        在 overlay.dts 中,rk-485-ctl 节点下添加新的节点overlay_node 节点,如下所示:

/dts-v1/;
/plugin/;&{/rk-485-ctl}{          overlay_node{status = "okay";};
};

        使用 dtc 编译器编译得到 dtbo 文件,并将 dtbo 拷贝到开发板上。

/home/topeet/Linux/linux_sdk/kernel/scripts/dtc/dtc 
-I dts 
-O dtb 
overlay.dts 
-o overlay.dtbo

        我们进到系统 /sys/kernel/config/device-tree/overlays/(这个目录需要加载设备树插件才会生成)目录下。

        在这个目录下使用以下命令创建一个内核对象,

mkdir test

         使用命令 cd test 进到 test 文件夹,在文件夹下创建 status和 dtbo两个文件

cd test

         使用 cat /overlay.dtbo > dtbo插件.dtbo文件 写入 dtbo文件。

         在状态文件status中写入 1,来使能插件echo 1 > status

        此时查看 proc文件系统的devicetree可以看到加载的 插件节点。

ls /proc/device-tree/rk-485-ctl/overlay_node/
/proc/device-tree/节点/插件节点

要删掉插件节点,直接把 /sys/kernel/config/device-tree/overlays/ 下的 test文件删掉就可。

        要加载多个插件,多写几个文件夹,把 dtc编译后的插件内容用 cat xxx > bbb 写到 dtbo 文件就行。 

第七十一章 虚拟文件系统 ConfigFS

        虚拟文件系统 ConfigFS 是一个特殊的文件系统,旨在提供一种动态配置 Linux 内核和设备的机制

71.1 常用的虚拟文件系统

        在Linux内核中,虚拟文件系统为应用程序提供了统一的文件访问接口,简化了开发、维护,提高了可移植性和灵活性。以下是几个常用的虚拟文件系统及其功能简述:

Procfs:    主要用于进程、设备、驱动等系统信息,表示运行时状态。

Sysfs:     主要用于设备、驱动等内核对象的属性和状态

Configfs:主要用于动态管理内核对象。允许运行时添加、修改和删除内核对象

Procfs

        功能:访问内核运行时状态,包括进程设备驱动程序等系统信息

        路径:通常在 /proc 目录下。

# 访问CPU信息
/proc/cpuinfo 

Sysfs

        功能:表示系统中的设备驱动程序等内核对象,为这些对象的属性和状态提供统一的访问接口。

路径:通常在 /sys 目录下。

# 查看设备信息
/sys/class/tty/ttyS0/device/idVendor  

Configfs

        功能:动态配置和管内核对象,允许在运行时添加、修改和删除内核对象

        路径:通常在 /sys/kernel/config 目录下。

# 动态配置内核对象
/sys/kernel/config  

        设备树插件选择 ConfigFS 原因在于,

        configfs允许用户空间动态配置内核对象,无需改内核代码,适合设备树插件技术。

第七十二章 ConfigFS 核心数据结构

72.1 关键数据结构

ConfigFS核心数据结构:

configfs_subsystem顶层结构,代表整个ConfigFS系统,含根配置项组和系统状态。

config_group配置项组,可含多个相关配置项,成层次结构,含父子配置项指针。

config_item基本配置项,表示内核对象,含类型、名称、属性和状态、父子关系指针。

        这些结构形成树形,subsystem为根,group为组,item为项,通过指针连接成父子关系。

configfs核心数据结构

72.2 子系统、配置组和 配置项

        configfs_subsystem 结构体:成员有config_group 结构体: 和互斥锁,

        config_group 结构体:成员有config_item结构体,和多个链表头,

        config_item结构体,成员有 config_item_type结构体引用计数等。

        config_item_type结构体有

                configfs_item_operations配置项操作集

                configfs_group_operations配置组操作集

                configfs_attribute属性文件操作集

config_item_operations操作集release方法。

config_group_operations操作集make_itemdrop_itemmake_group等方法。

configfs_attribute属性文件操作集有 myread_showmywrite_store方法,在 只读属性文件、只写属性文件分别被 读出、写入时执行。 

        configfs_subsystem 结构体

/*configfs子系统结构体*/
struct configfs_subsystem {struct config_group su_group;//配置项组结构体struct mutex su_mutex;       //互斥锁
};

        config_group 结构体: 

/*config_group配置组*/
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;        //双向链表头,链接配置组所属子系统的条目
};

         config_item 结构体

/*config_item配置项*/
struct config_item {char *ci_name;                           //配置项名称指针char ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; //配置项名称缓冲区.一个配置项就是一个目录struct kref ci_kref;                     //配置项引用计数          struct list_head ci_entry;               //配置项的链表条目struct config_item *ci_parent;           //父配置项struct config_group *ci_group;           //配置项组const struct config_item_type *ci_type; //目录下属性文件和属性操作struct dentry *ci_dentry;               //指向与配置项关联的目录项(dentry)的指针。
};//每个配置项都对应一个文件系统目录项,
//config_item_type用于描述配置项类型和操作,包括用于读写目录项属性的回调函数//在ConfigFS中,每个配置项都对应一个文件系统目录项,
//该目录项用于与用户空间进行交互
//(如通过mount命令挂载ConfigFS后,可以通过文件操作来访问这些配置项)。

        接下来我们分析一下设备树插件驱动代码

        设备树插件驱动代码定义了ConfigFS子系统配置项组

        dtbocfg_root_subsys是子系统实例,其根配置项组由 config_group表示,命名为"device-tree",并指定了自定义类型 dtbocfg_root_type。子系统使用互斥锁保护操作。

        首先,在这里,该结构体的.cg_item 字段表示根配置项组的基本配置项。

/*创建configfs子系统实例的定义和初始化*/
static struct configfs_subsystem dtbocfg_root_subsys = {  //dtbocfg_root_subsys.su_group 是一个 config_group 结构体,它表示子系统的根配置项组。//.cg_item 字段表示根配置项组的基本配置项.su_group.cg_item.ci_namebuf = "device-tree",   /*根配置项名称*///注册子文件系统时,会在sys/kernel/configs目录下,用根配置项名称创建子文件系统//配置项的类型=一个自定义config_item_type.su_group.cg_item.ci_type = &dtbocfg_root_type,//初始化互斥锁.su_mutex = __MUTEX_INITIALIZER(dtbocfg_root_subsys.su_mutex),  
};  

        注意,子文件系统有 配置项组成员,配置项组成员有根配置项,注册子文件系统时,用的是根配置项的名字。 

        注册 配置项组的时候,用的是注册函数给的名字参数 

        通过这段代码,创建了一个名为"device-tree"的子系统,它的根配置项组为空。

        可在该子系统下添加更多配置项和配置项组,用于动态配置和管理设备树相关的内核对象。

         Linux 系统下创建了 device-tree 这个子系统,

创建configfs_subsystem对象后sys/kernel/config目录下新建了 与[子系统名]同名的目录

         接下来是设备树插件驱动代码中注册配置项组的部分,

/*创建配置项组实例*/
static struct config_group dtbocfg_overlay_group = {  .cg_item.ci_type = &dtbocfg_overlays_type,  
};  /**/
static int dtbocfg_module_init(void) {  int retval;  /*初始化Configfs子系统的根配置项组*/config_group_init(&dtbocfg_root_subsys.su_group); /*初始化Confifs子系统的名为 overlays的配置项组*/ config_group_init_type_name(&dtbocfg_overlay_group,"overlays",     &dtbocfg_overlays_type);/*注册子系统*/retval = configfs_register_subsystem(&dtbocfg_root_subsys);  if (retval) goto register_subsystem_failed; /*注册配置项组*/retval = configfs_register_group(&dtbocfg_root_subsys.su_group, &dtbocfg_overlay_group);  if (retval) goto register_group_failed;  pr_info("OK\n");  return 0;  register_group_failed:  /*注销子系统*/configfs_unregister_subsystem(&dtbocfg_root_subsys);  
register_subsystem_failed:  return retval;  
}

初始化函数 dtbocfg_module_init()执行以下步骤:

        初始化子系统的根配置项组和名为"overlays"的配置项组,指定类型dtbocfg_overlays_type。

        configfs_register_subsystem()注册子系统。

        将"overlays"配置项组添加到子系统的根配置项组下。

        configfs_register_group()在子系统中注册配置项组。

        如注册失败,注销子系统并返回错误码;成功则打印"OK"。

        结果是在/sys/kernel/config下创建了名为"device-tree"的子系统,并在其下创建了名为"overlays"的容器。

overlays配置项组注册成功,子系统目录下出现对应的配置项目录

72.3 属性和方法

        我们要在容器下放目录或属性文件,所以我们看一下 config_item 结构体

struct config_item {char *ci_name;char ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; //目录的名字struct kref ci_kref;struct list_head ci_entry;struct config_item *ci_parent;struct config_group *ci_group;const struct config_item_type *ci_type; //目录下属性文件和属性操作struct dentry *ci_dentry;
};

        config_item 结构体中包含了 config_item_type 结构体

/*配置项的属性和操作*/
struct config_item_type {    struct module *ct_owner;                        //所属模块struct configfs_item_operations *ct_item_ops;   //item(目录)的操作方法struct configfs_group_operations *ct_group_ops; //group(容器)的操作方法struct configfs_attribute **ct_attrs;           //属性文件的操作方法struct configfs_bin_attribute **ct_bin_attrs;   //bin 属性文件的操作方法
}; 

        config_item_type 结构体中包含了 struct configfs_item_operations 结构体

/*配置项组下,配置项删除和链接的创建、删除的钩子函数*/
struct configfs_item_operations {//删除 item 方法,在 group 下面使用 rmdir 命令会调用这个方法void (*release)(struct config_item *);//参数是指向被删除的配置项的指针/*在尝试创建一个从 src 到 target 的链接之前,这个函数会被调用。在 ConfigFS 中,配置项可以通过创建链接(link)来组织成树状结构或形成关联。*/int (*allow_link)(struct config_item *src, struct config_item *target);/*当从 src 到 target 的链接被删除时,这个函数会被调用。*/void (*drop_link)(struct config_item *src, struct config_item *target);
};

        config_item_type 结构体中包含了 struct configfs_group_operations 结构体

/*配置项与配置项组关联的相关钩子函数*/
struct configfs_group_operations {//当在配置组下使用 mkdir 命令创建一个新的子配置项时,这个函数会被调用struct config_item *(*make_item)(struct config_group *group, const char *name);/*其实就是目录下新建文件的时候,该函数调用*///当在配置组下使用 mkdir 命令创建一个新的子配置组时,这个函数会被调用。struct config_group *(*make_group)(struct config_group *group, const char *name);/*其实就是目录下新建文件的时候,该函数调用.相较于make_item优先级更高*///在配置项被完全设置或修改后,这个函数会被调用以确认或提交这些更改。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);
};

        config_item_type 结构体中包含了 struct configfs_attribute 结构体, 

struct configfs_attribute {const 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); 
};
struct configfs_bin_attribute {  struct configfs_attribute cb_attr;  /* 标准的属性结构体 */  void *cb_private;                  /* 私有数据指针,可用于用户空间 */  size_t cb_max_size;                /* 二进制数据的最大大小 */  ssize_t (*read)(struct config_item *, void *, size_t);  /* 读取回调函数 */  ssize_t (*write)(struct config_item *, const void *, size_t); /* 写入回调函数 */  
};

 

第七十三章 注册 configfs 子系统实验

        我们编写驱动代码创建一个名为“myconfigfs”的 configfs 子系统,并将其注册到内核中。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>//定义名为"myconfig_item_type"的配置项类型结构体
static const struct config_item_type myconfig_item_type ={.ct_owner = THIS_MODULE, .ct_item_ops = NULL, .ct_group_ops = NULL, .ct_attrs = 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 myconfigfs_init(void)
{//初始化配置组config_group_init(&myconfigfs_subsystem.su_group);//注册子系统configfs_register_subsystem(&myconfigfs_subsystem);return 0;
}// 模块退出函数
static void myconfigfs_exit(void)
{/*注销子系统*/configfs_unregister_subsystem(&myconfigfs_subsystem);
}
module_init(myconfigfs_init); // 指定模块的初始化函数
module_exit(myconfigfs_exit); // 指定模块的退出函数
MODULE_LICENSE("GPL"); // 模块使用的许可证
MODULE_AUTHOR("topeet"); // 模块的作者

        驱动加载之后,进入/sys/kernel/config 目录下,可以看到注册生成的 myconfigfs 子系统,

注册生成的子系统目录

第七十四章 注册 configs 配置项实验

        由于 注册 config_group配置项组的过程和 注册 configfs子系统的过程相似,所以略过,

        而注册配置项的过程包含了注册配置项组的过程,且涉及config_item_type的配置过程,有着较多的钩子函数需要配置。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
// 定义一个名为"mygroup"的 config_group 结构体
static struct config_group mygroup;
// 自定义的配置项结构体
struct myitem
{struct config_item item;
};// 配置项释放函数
void myitem_release(struct config_item *item)
{struct myitem *myitem = container_of(item, struct myitem, item);kfree(myitem);printk("%s\n", __func__);
}// 配置项操作结构体
struct configfs_item_operations myitem_ops = {.release = myitem_release, 
};// 配置项类型结构体
static struct config_item_type mygroup_item_type = {.ct_owner = THIS_MODULE, .ct_item_ops = &myitem_ops, 
};// 新建配置项函数
struct config_item *mygroup_make_item(struct config_group *group, const char *name)
{struct myitem *myconfig_item;printk("%s\n", __func__);myconfig_item = kzalloc(sizeof(*myconfig_item), GFP_KERNEL);//创建一个配置项,绑定 名称 和 属性及操作config_item_init_type_name(&myconfig_item->item, name, &mygroup_item_type);return &myconfig_item->item;
}// 配置组操作结构体
struct configfs_group_operations mygroup_ops = {/*绑定配置项的新建函数*/.make_item = mygroup_make_item, 
};//config_item_type 结构体,用于描述配置项类型(但其实是传给配置项组的成员)。
static const struct config_item_type mygroup_config_item_type = {.ct_owner = THIS_MODULE, .ct_group_ops = &mygroup_ops,  
};// 定义名为"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"); // 模块使用的许可

         加载模块后,进入 sys/kernel/config 目录,发现注册的文件系统目录已经生成,配置组目录也已经生成,

注册配置项

        然后输入“mkdir test”命令创建 config_item,如下图所示,创建成功之后,打印

        “mygroup_make_item”,说明驱动程序中 mygroup_make_item 函数成功执行。

配置项组下新建文件(配置项)

第七十五章 drop 和 release 函数

        .release 是在 struct config_item_type 结构体中定义的回调函数指针,用于在配置项被删除时执行资源释放操作。

         每个配置项实例都有一个与之关联的引用计数

        这个引用计数用于跟踪有多少实体(如内核模块、进程或其他内核组件)正在使用该配置项。当你创建一个新的配置项实例时,通常会初始化其引用计数为 1,表示该配置项已经被创建者所引用。随着配置项被不同的实体所使用,其引用计数会增加;相应地,当这些实体不再需要该配置项时,它们会通过调用 config_item_put 来减少其引用计数。当引用计数减少到 0就会调用.release函数执行清理工作。

         .drop_item 是在 struct configfs_group_operations 结构体中定义的回调函数指针,用于在配置组被删除时执行相关的清理操作。

// 定义一个名为"mygroup"的 config_group 结构体
static struct config_group mygroup;// 自定义的配置项结构体
struct myitem
{struct config_item item;
};// 配置项释放函数,配置项的引用计数为0时执行
void myitem_release(struct config_item *item)
{struct myitem *myitem = container_of(item, struct myitem, item);kfree(myitem);printk("%s\n", __func__);
}/* 配置项操作结构体,填充配置项操作的release函数 */
struct configfs_item_operations myitem_ops = {.release = myitem_release, 
};// 配置项类型结构体
static struct config_item_type mygroup_item_type = {.ct_owner = THIS_MODULE, .ct_item_ops = &myitem_ops,  //填充配置项操作
};// 创建配置项函数
struct config_item *mygroup_make_item(struct config_group *group, const char *name)
{struct myitem *myconfig_item;printk("%s\n", __func__);myconfig_item = kzalloc(sizeof(*myconfig_item), GFP_KERNEL);config_item_init_type_name(&myconfig_item->item, name, &mygroup_item_type);return &myconfig_item->item;
}// 删除配置项函数
void mygroup_delete_item(struct config_group *group, struct config_item *item)
{struct myitem *myitem = container_of(item, struct myitem, item);config_item_put(&myitem->item); //减少传入 config_item 实例的引用计数printk("%s\n", __func__);
}// 配置组操作结构体
struct configfs_group_operations mygroup_ops = {.make_item = mygroup_make_item,  //配置组创建配置项的钩子函数.drop_item = mygroup_delete_item,//配置组删除配置项的钩子函数 
};//  config_item_type 配置项类型结构体,用于描述配置项类型。用于将配置组注册进子系统时初始化
static const struct config_item_type mygroup_config_item_type = {.ct_owner = THIS_MODULE, .ct_group_ops = &mygroup_ops, //填充配置组类型的钩子函数
};// 定义名为"myconfig_item_type"的配置项类型结构体,用于定义子系统时初始化
static const struct config_item_type myconfig_item_type = {.ct_owner = THIS_MODULE, .ct_group_ops = NULL, 
};// 定义一个 configfs_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;
}//...省略模块出口函数

        在配置组目录下,使用 mkdir创建配置项,会执行 make_item函数,

        使用 rmdir删除配置项,会先执行 drop_item函数,然后执行release函数

第七十六章 注册 attribute 实验

// 使用CONFIGFS_ATTR_RO和CONFIGFS_ATTR_WO宏定义只读和只写属性  
CONFIGFS_ATTR_RO(my, read);  // 定义一个名为read的只读属性,关联到myread_show函数  
/*生成一个 myattr_read只读属性*/    CONFIGFS_ATTR_WO(my, write);  // 定义一个名为write的只写属性,关联到mywrite_store函数  
/*生成一个 myattr_write只写属性*//*属性文件和关联的读写方法是通过属性名和函数名关联的*/// 定义属性数组,以NULL结尾,用于注册到config_item_type中  
struct configfs_attribute *my_attrs[] = {    &myattr_read, &myattr_write, NULL,    
};  

         config_item_type 结构体中包含了 struct configfs_attribute 结构体, 

/*config_item_type结构体下,包含了configfs_attribute结构体*/
struct configfs_attribute {const 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); 
};
// 定义一个结构体myitem,它包含一个config_item成员和其他自定义成员  
struct myitem {    struct config_item item;  // 内嵌的config_item,用于与配置文件系统交互  int size;  // 用于存储数据的大小  void *addr;  // 指向数据的指针  
};  // 释放myitem资源的函数,当config_item被释放时调用  
void myitem_release(struct config_item *item) {    struct myitem *myitem = container_of(item, struct myitem, item);  // 将config_item指针转换回myitem指针  kfree(myitem);  // 释放myitem占用的内存  
}  // 读取myitem数据的回调函数,用于configfs的只读属性  
ssize_t myread_show(struct config_item *item, char *page) {    struct myitem *myitem = container_of(item, struct myitem, item);    memcpy(page, myitem->addr, myitem->size);  // 将数据复制到page中  return myitem->size;  // 返回复制的字节数  
}  // 写入数据到myitem的回调函数,用于configfs的只写属性  
ssize_t mywrite_store(struct config_item *item, const char *page, size_t size) {    struct myitem *myitem = container_of(item, struct myitem, item);    myitem->addr = kmemdup(page, size, GFP_KERNEL);  // 分配新内存并复制数据  myitem->size = size;  // 更新数据大小  return size;  // 返回写入的字节数  
}  // 使用CONFIGFS_ATTR_RO和CONFIGFS_ATTR_WO宏定义只读和只写属性  
CONFIGFS_ATTR_RO(my, read);  // 定义一个名为myattr_read的只读属性,关联到myread_show函数  
CONFIGFS_ATTR_WO(my, write); // 定义一个名为myattr_write的只写属性,关联到mywrite_store函数  // 定义属性数组,以NULL结尾,用于注册到config_item_type中  
struct configfs_attribute *my_attrs[] = {    &myattr_read, &myattr_write, NULL,    
};  // 定义myitem的操作集,包括释放函数  
struct configfs_item_operations myitem_ops = {    .release = myitem_release,    
};  // 定义mygroup的config_item_type,包括所有者、操作集和属性数组  
struct config_item_type mygroup_item_type = {    .ct_owner = THIS_MODULE,  // 模块所有者  .ct_item_ops = &myitem_ops,  // 操作集  .ct_attrs = my_attrs,  // 属性数组  
};  // 初始化函数,用于注册mygroup到配置文件系统  
static int myconfig_group_init(void) {    // 假设myconfigfs_subsystem已定义并初始化  configfs_register_subsystem(&myconfigfs_subsystem);  // 注册子系统  config_group_init_type_name(&mygroup, "mygroup", &mygroup_item_type);  // 初始化组并设置名称和类型  configfs_register_group(&myconfigfs_subsystem.su_group, &mygroup);  // 注册组到子系统中  return 0;    
}  // 清理函数,用于注销mygroup和子系统  
static void myconfig_group_exit(void) {    configfs_unregister_subsystem(&myconfigfs_subsystem);  // 注销子系统  
}

        在使用CONFIGFS_ATTR_ROCONFIGFS_ATTR_WO宏时,

        它们会生成名为 myattr_read和 myattr_write的 configfs_attribute结构体实例。

/*生成只读属性 名为[_name]attr_read的configfs_attribute实例*/
#define CONFIGFS_ATTR_RO(_name, _show)  struct configfs_attribute configfs_attr_##_name = { .ca_owner = THIS_MODULE,.ca_name = __stringify(_name), .ca_mode = S_IRUGO,.show = _show,}  /*生成只写属性 名为[_name]attr_write的configfs_attribute实例*/
#define CONFIGFS_ATTR_WO(_name, _store) struct configfs_attribute configfs_attr_##_name = { .ca_owner = THIS_MODULE, .ca_name = __stringify(_name), .ca_mode = S_IWUSR,.store = _store,}

        模块编译、加载后,在名为 myconfigfs的子系统目录下,有 配置组 mygroup目录,使用mkdir创建配置项 test,可以看到配置项目录 test下有生成的 属性文件 read 和 write。

注册 attribute实验,生成属性文件

        可以使用 以下命令对属性文件进行操作: 

cat read        //读echo 1 > write  //写入

        分别对属性文件 read属性文件 write 进行读写操作后,

        会分别执行 myread_showmywrite_store 函数。

第七十七章 实现多级目录

        完成了 configfs_group_operations 结构体下的,.make_group函数,

        .make_group函数优先级比 .make_item优先级高,因此编译好驱动装载好,使用mkdir创建目录的时候会调用 .make_group视为新建了 配置组

// 创建配置组函数
struct config_group *mygroup_make_group(struct config_group *group, const char *name)
{struct mygroup *mygroup;printk("%s\n", __func__);mygroup = kzalloc(sizeof(*mygroup), GFP_KERNEL);config_group_init_type_name(&mygroup->group, name, &mygroup_type);return &mygroup->group;
};// 配置组操作结构体
struct configfs_group_operations mygroup_ops = {.make_item = mygroup_make_item,   //新建配置项函数.drop_item = mygroup_delete_item, //删除配置项函数.make_group = mygroup_make_group, //新建配置组函数
};

第七十八章 移植设备树插件驱动

        移植设备树插件到 iTOP-RK3568 开发板上。移植设备树插件主要包括以下几个步骤

        1 配置内核支持挂载 configfs 虚拟文件系统。

        2 配置内核支持设备树插件(kernel 4.4及以上)

        3 移植设备树插件驱动

78.1 挂载 configfs 虚拟文件系统

        内核源码的menuconfig页面,选择支持configfs文件系统。

内核源码menuconfig页面,选择支持 configfis文件系统

        将编译之后的内核镜像烧写到开发板上,接着使用 mount 命令检查 configfs 虚拟文件系是否挂载成功。

如果系统没有自动挂载 configfs 虚拟文件系统,需要输入以下命令挂载:

mount -t                 //挂载的类型
configfs                 //configfs虚拟文件系统
none                     //不需要具体的设备与挂载点关联
/sys/kernel/config       //挂载的文件目录,挂载好后可通过该目录与configfs交互,来动态配置内核对象

78.2 配置内核支持设备树插件

        源码,menuconfig页面,勾选支持 device tree overlays。

驱动支持设备树插件
文件系统支持设备树插件

        内核编译成功后,就可以开始移植设备树。

78.3 移植驱动

        我们没有必要重复造轮子,github 上有大神编写好的设备树插件驱动。

        假设 dtbocfg.c 是设备树插件驱动源码,我们将此驱动编译成驱动模块或编译进内核即可。

        好了,设备树插件驱动移植完毕,设备树插件的使用参考前面的章节即可。

第七十九章 设备树插件驱动分析

        dtbocfg.c 为设备树插件驱动文件。

        在 dtbocfg_overlays_type 中实现了 ct_group_ops 下的 make_itemdrop_item

// 初始化dtbocfg模块的函数  
static int __init dtbocfg_module_init(void)  
{  int retval = 0;  // 打印初始化开始信息,注意这里应该使用__func__来获取当前函数名  pr_info("%s: Initialization started\n", __func__);  // 初始化configfs的group  config_group_init(&dtbocfg_root_subsys.su_group);  config_group_init_type_name(&dtbocfg_overlay_group, "overlays", &dtbocfg_overlays_type);  // 注册子系统到configfs  retval = configfs_register_subsystem(&dtbocfg_root_subsys);  if (retval != 0) {  pr_err("%s: Couldn't register subsystem\n", __func__);  }  // 注册group到已注册的子系统  retval = configfs_register_group(&dtbocfg_root_subsys.su_group, &dtbocfg_overlay_group);  if (retval != 0) {  pr_err("%s: Couldn't register group\n", __func__);  }  // 打印初始化成功信息  pr_info("%s: Initialization OK\n", __func__);  return 0;  
}  
make_item和drop_item的实现

        在配置组下,命令行输入 mkdir时,如果没有实现 make_group,则会去执行 make_item

        make_item负责开辟空间,并调用初始化函数去初始化 config_item

        config_item有 config_item_type成员,

        config_item_type成员 有

        config_item_operations结构体config_group_operations结构体

        attribute结构体bin_attribute结构体

部分成员未列出

        attribute结构体有 read_show,和 write_store函数可以绑定。

        bin_attribute结构体readwrite  可以绑定。

// 定义一个二进制属性,用于dtbo(设备树二进制对象)的覆盖  
CONFIGFS_BIN_ATTR(dtbocfg_overlay_item, dtbo, NULL, 1024*1024); // 1MiB足够大  // 定义一个普通属性,用于显示或设置状态  
CONFIGFS_ATTR(dtbocfg_overlay_item, status);
//定义一个status属性,该属性属于 dtbocfg_overlay_item配置项/*    dtbocfg_overlay_item_attr_dtbo 和 dtbocfg_overlay_item_attr_status 是宏展开后生成的变量名。*/// 定义一个属性结构体数组,包含所有与dtbocfg_overlay_item相关的普通属性  
static struct configfs_attribute *dtbocfg_overlay_attrs[] = {  &dtbocfg_overlay_item_attr_status,  //将属性,通过属性数组,填充给 配置项NULL, // 数组结束标志  
};  // 定义一个结构体数组,包含所有与dtbocfg_overlay_item相关的二进制属性  
static struct configfs_bin_attribute *dtbocfg_overlay_bin_attrs[] = {  &dtbocfg_overlay_item_attr_dtbo,  //将属性,通过属性数组,填充给 配置项NULL, // 数组结束标志  
};

        CONFIGFS_BIN_ATTR 用于定义二进制属性

/*定义一个二进制属性*/
#define CONFIGFS_BIN_ATTR(_name, _mode, _read,_write,_size) //二进制属性可存储的最大字节数

        CONFIGFS_ATTR 宏用于定义普通属性

/*定义一个普通属性*/
#define CONFIGFS_ATTR(_name, //名称_mode, //权限_show, //读操作函数_store) //写操作函数

        当 status 写入 1 的时,会执行 dtbocfg_overlay_item_create 函数。在这个函数中又去执行了 of_overlay_fdt_apply 函数。

        完善dtbocfg_overlay_item_create 函数,可以执行我们需要的逻辑。

static ssize_t dtbocfg_overlay_item_status_store(struct config_item *item, const char *buf, size_t count)  
{  struct dtbocfg_overlay_item *overlay = container_of(item, struct dtbocfg_overlay_item, item);  ssize_t status;  unsigned long value;  // 尝试将字符串缓冲区转换为无符号长整型值  if (0 != (status = kstrtoul(buf, 10, &value))) {  goto failed; // 转换失败,跳转到错误处理  }    // 根据值决定是释放还是创建覆盖项  if (value == 0) {  if (overlay->id >= 0) {  // 如果id有效,则释放覆盖项  dtbocfg_overlay_item_release(overlay);  } }  else {   if (overlay->id < 0) {  dtbocfg_overlay_item_create(overlay);  }  }   return count; // 假设成功写入全部数据  
failed:  return -EPERM; // 权限错误或转换失败  
}

        设备树插件(dtbo)里面的节点也要被转换成 device_node有的 device_node 也要被转换成 platform_device

        不过在进行转换之前,of_overlay_fdt_apply 函数会先创建一个改变集。然后根据这个改变集去进行修改。

        改变集是为了方便修改和复原设备树而设计的。

        设备树是静态的,编译加载后不易直接更改。改变集记录了设备树的修改操作,如增删改节点,允许运行时动态调整,无需改动源文件。它提供了高层次抽象,简化描述变化,且可保存、传递、应用于不同设备树。同时,改变集支持撤销修改,恢复原始状态。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

挖矿病毒记录 WinRing0x64.sys

之前下载过福晰pdf编辑器&#xff0c;使用正常。 某天发现机器启动后&#xff0c;过个几分钟(具体为5min)会自动运行几个 cmd 脚本(一闪而过)&#xff0c;但是打开任务管理器没有发现异常程序&#xff08;后面发现病毒程序伪装成System系统程序&#xff0c;见下图&#xff09;…

SpringCloud Config配置中心 SpringCloud Bus消息总线

一、SpringCloud Config 使用git储存配置信息 1&#xff09;什么是 SpringCloud Config项目实现的目标是将配置文件从本地项目中抽出来放到git仓库中&#xff0c;项目启动时自动从git仓库中取配置文件。 但是本地项目不直接和git仓库通信&#xff0c;而是通过配置服务器中转。…

python如何查询函数

1、通用的帮助函数help() 使用help()函数来查看函数的帮助信息。 如&#xff1a; import requests help(requests) 会有类似如下输出&#xff1a; 2、查询函数信息 ★查看模块下的所有函数&#xff1a; dir(module_name) #module_name是要查询的函数名 如&#xff1a; i…

vmvare虚拟机centos 忘记超级管理员密码怎么办?

vmvare虚拟机centos 忘记超级管理员密码怎么办?如何重置密码呢? 一、前置操作 重启vmvare虚拟机的过程中,长按住Shift键 选择第一个的时候,按下按键 e 进入编辑状态。 然后就会进入到类似这个界面中。 在下方界面 添加 init=/bin/sh,然后按下Ctrl+x进行保存退出。 init=/bi…

iPhone、iPad、iOS储存空间不足,瘦身终极方法

如果你实在是需要瘦身&#xff0c;但是确实没有什么可以删除了&#xff0c;也不想备份到其他地方&#xff0c;就这样做。 删除不需要的自带应用。 你可以删除FaceTime、股票、等app&#xff0c;但是不要删除你需要的app。 删除的界限是这样的&#xff1a;你永远都不可能使用…

OceanBase企业级分布式关系数据库

简介 OceanBase 数据库是阿里巴巴和蚂蚁集团不基于任何开源产品&#xff0c;完全自研的原生分布式关系数据库软件&#xff0c;在普通硬件上实现金融级高可用&#xff0c;首创“三地五中心”城市级故障自动无损容灾新标准&#xff0c;具备卓越的水平扩展能力&#xff0c;全球首…

Git版本控制工具--关于命令

Git版本控制工具 学习前言 在项目开发中&#xff0c;总是需要多个人同时对一个项目进行修改&#xff0c;如何高效快速地进行修改&#xff0c;且控制各自修改的版本不会和他人的进行重叠&#xff0c;这就需要用到Git分布式版本控制器了 作用 解决了一致性&#xff0c;并发性…

CSS 圆形边框与阴影

目录 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影

Megabit兆比特10月比特币激增做好准备-最新加密货币新闻

Kaiko Research最近的分析表明&#xff0c;交易员正在积极为潜在的强劲表现做好准备特币(BTC)比今年十月。目前&#xff0c;BTC的交易价格为60800美元&#xff0c;在测试了60000美元的支撑位后&#xff0c;最近上涨了800美元。Megabit兆比特自成立以来,Megabit凭借用户友好的界…

【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错

1. 运行项目 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Appl…

【设计模式-职责链】

定义 职责链模式是一种行为设计模式&#xff0c;**它通过将请求发送给链上的多个处理者来避免请求发送者与处理者之间的紧密耦合。每个处理者可以选择处理请求或将其传递给链中的下一个处理者。**这样&#xff0c;可以将处理请求的责任链式组织&#xff0c;从而实现更灵活的请…

【HDP】zookeeper未授权漏洞修复

目录 一、禁用四字命令 二、ZK-Client增加kerberos 一、禁用四字命令 Zookeeper四字命令的使用方式非常简单&#xff0c;通常有两种方式。第一种是通过Telnet方式&#xff0c;使用Telnet客户端登录ZooKeeper的对外服务端口&#xff0c;然后直接使用四字命令即可&#xff1b;第…

django的URL配置

1 django如何处理一个请求 首先Django要使用根URLconf模块&#xff0c;通过setting.py配置文件的ROOT_URLCONF来设置。 加载该模块后并查找变量 urlpatterns。这是一个Python的django.conf.urls.url()实例列表。 Django按顺序运行每个URL模式&#xff0c;并在匹配所请求的…

.NET Core 高性能并发编程

一、高性能大并发架构设计 .NET Core 是一个高性能、可扩展的开发框架&#xff0c;可以用于构建各种类型的应用程序&#xff0c;包括高性能大并发应用程序。为了设计和开发高性能大并发 .NET Core 应用程序&#xff0c;需要考虑以下几个方面&#xff1a; 1. 异步编程 异步编程…

Windows平台如何实现RTSP|RTMP流录像?

好多开发者使用场景&#xff0c;除了实现基础的低延迟RTSP、RTMP播放外&#xff0c;还需要实现RTSP、RTMP流数据的本地录像功能。本文以大牛直播SDK的Windows平台播放模块为例&#xff0c;介绍下如何实现RTSP、RTMP流录像。 功能设计 [拉流]支持拉取RTSP流录像&#xff1b; [拉…

15分钟学 Python 第34天 :小项目-个人博客网站

Day 34: 小项目-个人博客网站 1. 引言 随着互联网的普及&#xff0c;个人博客已成为分享知识、体验和见解的一个重要平台。在这一节中&#xff0c;我们将使用Python的Flask框架构建一个简单的个人博客网站。我们将通过实际的项目来学习如何搭建Web应用、处理用户输入以及管理…

二叉树深度学习——将二叉搜索树转化为排序的双向链表

1.题目解析 题目来源&#xff1a;LCR 155.将二叉搜索树转化为排序的双向链表 测试用例 2.算法原理 首先题目要求原地进行修改并且要求左指针代表前驱指针&#xff0c;右指针代表后继指针&#xff0c;所以思路就是 1.使用前序遍历创建两个指针cur、prev代表当前节点与前一个节点…

游览器输入URL并Enter时都发生了什么 面试完美回答

文章目录 前言URL解析DNS解析**浏览器缓存****操作系统缓存**&#xff1a;**路由器缓存**&#xff1a;ISP&#xff08;Internet service provider&#xff09;缓存DNS递归解析IP地址的获取缓存结果 建立TCP连接发送HTTP请求服务器响应TCP链接断开渲染页面解析一 HTML解析过程解…

U盘目录损坏数据恢复全攻略

一、U盘目录损坏现象描述 U盘作为我们日常生活中常用的存储设备&#xff0c;因其小巧便携、存储容量大等特点而备受青睐。然而&#xff0c;在使用U盘的过程中&#xff0c;有时会遇到目录损坏的问题。目录损坏通常表现为U盘中的文件夹无法正常打开&#xff0c;或者文件无法读取…

云栖实录 | 开源大数据全面升级:Native 核心引擎、Serverless 化、湖仓架构引领云上大数据发展

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 王 峰 | 阿里云智能集团研究员、开源大数据平台负责人 李 钰&#xff5c;阿里云智能集团资深技术专家 范 振&#xff5c;阿里云智能集团高级技术专家 李劲松&#xff5c;阿里云…