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.mzph.cn/news/880956.shtml

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

相关文章

protobuf 讲解

一、序列化概念回顾 二、什么是PB 将结构化数据进行序列化的一种方式 三、PB的特点 语言无关、平台无关&#xff1a;即PB支持Java&#xff0c;C、Python等多种语言。支持多个平台 高效&#xff1a;即比XML更小&#xff0c;更快&#xff0c;更为简单。 扩展性、兼容性好&am…

WPF之UI进阶--控件样式与样式模板及词典

WPF的优势之一就是能够更加容易快捷的对窗体和控件的外面进行改造&#xff0c;换句话说&#xff0c;那就是UI设计个性化更加容易。主要是借助了样式、模板及词典来实现的。那么本篇博文就一一对他们进行介绍。 文章目录 一、样式1: 定义样式2: 使用Setter设置属性关于Property和…

C或C++判断指针是否指向同一块内存

有时需要判断指针是否指同一块内存&#xff0c;例如设计字符串时&#xff1a; &#xff08;1&#xff09;insert函数 &#xff08;2) replace函数 &#xff08;3&#xff09;assign函数 难点是迭代器&#xff0c;判断是否同一个迭代器时&#xff0c;需要你在设计迭代器时加…

Kubernetes-环境篇-01-mac开发环境搭建

1、brew安装 参考知乎文章&#xff1a;https://zhuanlan.zhihu.com/p/111014448 苹果电脑 常规安装脚本&#xff08;推荐 完全体 几分钟安装完成&#xff09; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"苹果电脑 极…

Rstudio:强大的R语言集成开发环境(IDE)

Rstudio 应该是 R 语言使用的标配&#xff0c;尽管 Rstudio 的母公司 Posit 推出了新一代的集成开发环境 Positron&#xff0c;但其还处于开发阶段。作为用户不妨让其成熟后再使用&#xff0c;现阶段还是 Rstudio 更稳定。 如果你在生物信息学或统计学领域工作&#xff0c;R语言…

Python | Leetcode Python题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()m, n len(g), len(s)i j count 0while i < m and j < n:while j < n and g[i] > s[j]:j 1if j < n:count 1i …

uni-app - - - - -vue3使用i18n配置国际化语言

uni-app - - - - -使用i18n配置国际化语言 1. 安装vue-i18n2. 配置文件2.1 创建如下文件2.2 文件配置2.3 main文件导入i18n 3. 页面内使用3.1 template内直接使用3.2 变量接收使用 1. 安装vue-i18n npm install vue-i18n --save2. 配置文件 2.1 创建如下文件 locales文件夹里…

水泵模块(5V STM32)

目录 一、介绍 二、传感器原理 1.尺寸介绍 2.继电器控制水泵电路原理图 三、程序设计 main.c文件 bump.h文件 bump.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 水泵模块(bump)通常是指用于液体输送系统的组件&#xff0c;它负责将水或其他流体从低处提…

四.网络层(上)

目录 4.1网络层功能概述 4.2 SDN基本概念 4.3 路由算法与路由协议 4.3.1什么是路由协议&#xff1f; 4.3.2什么是路由算法&#xff1f; 4.3.3路由算法分类 (1)静态路由算法 (2)动态路由算法 ①全局性 OSPF协议与链路状态算法 ②分散性 RIP协议与距离向量算法 4.3.…

【C语言】内存函数的使用和模拟实现

文章目录 一、memcpy的使用和模拟实现二、memmove的使用和模拟实现三、memset的使用四、memcmp的使用 一、memcpy的使用和模拟实现 在之前我们学习了使用和模拟实现strncpy函数&#xff0c;它是一个字符串函数&#xff0c;用来按照给定的字节个数来拷贝字符串&#xff0c;那么问…

【本地免费】SimpleTex 图像识别latex公式

文章目录 相关教程相关文献安装教程 由于mathpix开始收费了&#xff0c;于是本文将介绍一款目前本地免费的SimpleTex工具 相关教程 【超详细安装教程】LaTeX-OCR 图像识别latex公式&#xff08;开源免费&#xff09;_latex图片识别-CSDN博客 相关文献 SimpleTex主页——致力…

数据结构双向链表和循环链表

目录 一、循环链表二、双向链表三、循环双向链表 一、循环链表 循环链表就是首尾相接的的链表&#xff0c;就是尾节点的指针域指向头节点使整个链表形成一个循环&#xff0c;这就弥补了以前单链表无法在后面某个节点找到前面的节点&#xff0c;可以从任意一个节点找到目标节点…

5.3 克拉默法则、逆矩阵和体积

本节是使用代数而不是消元法来求解 A x b A\boldsymbol x\boldsymbol b Axb 和 A − 1 A^{-1} A−1。所有的公式都会除以 det ⁡ A \det A detA&#xff0c; A − 1 A^{-1} A−1 和 A − 1 b A^{-1}\boldsymbol b A−1b 中的每个元素都是一个行列式除以 A A A 的行列式。…

C(十一)scanf、getchar(第三弹)

问题引入&#xff1a;如何实现输入一串密码&#xff0c;如&#xff1a;“123 xxxx” &#xff0c;然后读取并确认&#xff0c;是 -- Y&#xff1b;否 -- N。 自然的&#xff0c;我们想到用scanf&#xff0c;但是在使用过程中你是否遇到跟我一样的困惑呢&#xff1f;如下&…

如何高效删除 MySQL 日志表中的历史数据?实战指南

在处理高并发的物联网平台或者其他日志密集型应用时&#xff0c;数据库中的日志表往往会迅速增长&#xff0c;数据量庞大到数百GB甚至更高&#xff0c;严重影响数据库性能。如何有效管理这些庞大的日志数据&#xff0c;特别是在不影响在线业务的情况下&#xff0c;成为了一项技…

【LeetCode HOT 100】详细题解之二叉树篇

【LeetCode HOT 100】详细题解之二叉树篇 94 二叉树的中序遍历方法一&#xff1a;递归方法二&#xff1a;迭代 104 二叉树的最大深度方法一&#xff1a;递归方法二&#xff1a;迭代 226 翻转二叉树方法一&#xff1a;递归方法二&#xff1a;迭代 101 对称二叉树方法一&#xff…

小程序-使用npm包

目录 Vant Weapp 安装 Vant 组件库 使用 Vant 组件 定制全局主题样式 API Promise化 1. 基于回调函数的异步 API 的缺点 2. 什么是 API Promise 化 3. 实现 API Promise 化 4.调用 Promise 化之后的异步 API 小程序对 npm 的支持与限制 目前&#xff0c;小程序中已经…

Java 之深入理解 String、StringBuilder、StringBuffer

前言 由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到&#xff0c;这里就顺便总结一下&#xff1a;本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析 ✨上期回顾&#xff1a;Java 哈希表 ✨目录 前言 String 介绍 String 的不可变…

全局安装cnpm并设置其使用淘宝镜像的仓库地址(地址最新版)

npm、cnpm和pnpm基本概念 首先介绍一下npm和cnpm是什么&#xff0c;顺便说一下pnpm。 npm npm&#xff08;Node Package Manager&#xff09;是Node.js的默认包管理器&#xff0c;用于安装、管理和分享JavaScript代码包。它是全球最大的开源库生态系统之一&#xff0c;提供了数…

如何使用ssm实现基于HTML的中国传统面食介绍网站的搭建+vue

TOC ssm758基于HTML的中国传统面食介绍网站的搭建vue 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔…