设备树插件_configfs学习笔记

news2024/11/23 19:58:34

设备树插件定义和作用

​ 设备树插件(Device Tree Overlay) 是一种用于设备树(Device Tree)的扩展机制。

​ 设备树插件允许在运行时动态修改设备树结构的内容,以便添加、修改或删除设备节点和属性。嵌入式驱动开发者将不用再重新启动系统的情况下对硬件进行配置修改。

​ 在早期的时候,每个嵌入式驱动开发者都因为引入了设备树而感到兴奋和激动。因为这使得开发者不用再写两套代码,且代码移植性也变得非常灵活。

​ 然而设备树还有个不太方便的地方,就是每次配置完设备树,还得重新编译后烧录到板卡,重启系统才能完成配置。这使得驱动开发者非常头疼,有可能因为更改个GPIO属性都得重新过一遍烧录过程。所以在linux4.4以后引入了动态设备树。设备树插件是实现的方式。

一、预备知识

如果不想关注原理,只是简单使用。可以跳过本节。

设备树插件是基于configfs实现的。所以本节介绍configfs的相关知识。

1. 什么是configfs

在kernel目录下的Documentation/filesystems/configfs有官方说明文档configfs.txt 。其中是这样说明的:

  • configfs是与sysfs功能相反的基于ram的文件系统。sysfs是基于文件系统查看内核对象,configfs是基于文件系统管理内核对象或配置项
  • 对于sysfs,一个对象是在内核中创建和删除的。内核控制着sysfs表示的生命周期,和sysfs只是一个窗口而已。而configfs配置项表示的生命周期完全由用户空间驱动,内核模块支持这样项目的必须作出响应。
  • 它可以被编译成模块或者编入内核
2. 如何编写
  • 认识相关结构体
/************configfs子系统结构体********/
struct configfs_subsystem {
	 struct config_group su_group;  //config_group结构体变量
 	struct mutex su_mutex;
}
/************注册和注销API********/
int configfs_register_subsystem(struct configfs_subsystem *subsys);
void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
/************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;
};
/************初始化API********/
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_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;
}
/************初始化API********/
void config_item_init(struct config_item *);
void config_item_init_type_name(struct config_item *, const char *name,struct config_item_type *type);
/************config_item属性以及动作相关类********/
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动作结构体********/
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);
}
/************config_item属性结构体********/
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);
};

struct configfs_bin_attribute {
 struct configfs_attribute cb_attr;
 void *cb_private;
 size_t cb_max_size;
};

结构体相关性

在这里插入图片描述

  • 编写-创建一个子系统并创建group
/**第二步:实现配置项相关类**/
static struct config_item_type myconfigfs_item={
    .ct_owner=THIS_MODULE,
    .ct_item_ops=NULL,
    .ct_attrs=NULL,
    .ct_bin_attrs=NULL
};

/**第一步:创建configfs子系统结构体变量**/
static struct configfs_subsystem myconfigfs={
    .su_group={
        .cg_item={
            .ci_namebuf="myconfig",
            .ci_type=&myconfigfs_item
        },
    },
};

static int myconfigfs_init(void){
  printk("Hello World!\n");
  /**第三步:初始化子系统group**/
  config_group_init(&myconfigfs.su_group);  //注册子系统前需要初始化group
   /**第四步:注册子系统**/
  configfs_register_subsystem(&myconfigfs); ///注册子系统
  return 0;
}

static void myconfigfs_exit(void){
    /**第五步:添加卸载模块时注销子系统**/
    configfs_unregister_subsystem(&myconfigfs);
    printk("bye bye\n");
    
}

通过编译为KO文件并加载到开发板上,可以在/sys/kernel/config下查看到一个名为myconfig的group

  • 编写-在子系统group中创建group
/**第二步:实现myconfig_group的config_item_type**/
static struct config_item_type myconfigfs_item={
    .ct_owner=THIS_MODULE,
    .ct_group_ops=NULL
};

static struct configfs_subsystem myconfigfs={
    .su_group={
        .cg_item={
            .ci_namebuf="myconfig",
            .ci_type=&myconfigfs_item
        },
    },
};

static struct config_item_type mygroup_item={
    .ct_owner=THIS_MODULE,
    .ct_item_ops=NULL,
    .ct_attrs=NULL,
    .ct_bin_attrs=NULL
};

/**第一步:创建config_group结构体变量**/
static struct config_group myconfig_group;

static int myconfigfs_init(void){
  printk("Hello World!\n");
  config_group_init(&myconfigfs.su_group);
  configfs_register_subsystem(&myconfigfs);
/**第二步:初始化config_group**/
  config_group_init_type_name(&myconfig_group,"mygroup",&mygroup_item);
/**第四步:在子系统group下注册一个group**/
  configfs_register_group(&myconfigfs.su_group, &myconfig_group);
  return 0;
}

static void myconfigfs_exit(void){
    configfs_unregister_subsystem(&myconfigfs);
    printk("bye bye\n");
    
}

通过编译为KO文件并加载到开发板上,可以在/sys/kernel/config/myconfig下查看到一个名为mygroup的group

  • 编写-在group中创建item
struct myitem{
    struct config_item item;
};

/**第六步:实现rmdir相关函数**/
void mymake_item_release(struct config_item *item)
{
    struct myitem *myitem=container_of(item, struct myitem, item);
    kfree(myitem);
}

/**第五步:实现rmdir**/
struct configfs_item_operations mymake_item_ops={
    .release=&mymake_item_release
};

/**第四步:mkdir的相关项目的动作实现**/
static struct config_item_type mymake_item={
    .ct_owner=THIS_MODULE,
    .ct_item_ops=&mymake_item_ops
};

/**第三步:mkdir的相关函数**/
static struct config_item *myconfig_group_make_item(struct config_group *group, const char *name){
    struct myitem *myitem;
    myitem=kzalloc(sizeof(*myitem), GFP_KERNEL);
    config_item_init_type_name(&myitem->item,name, &mymake_item);//Initialze required  item to created

    return &myitem->item;
}

/**第二步:实现相关的动作--mkdir**/
struct configfs_group_operations myconfig_group_ops={
    .make_item=myconfig_group_make_item,//achieve a function for make item
};

/**第一步:在子系统group下注册的group中实现相关动作**/
struct config_item_type myconfig_group_item={
    .ct_owner=THIS_MODULE,
    .ct_group_ops=&myconfig_group_ops
};

static struct config_group myconfig_group;


static struct config_item_type myconfigfs_item={
    .ct_owner=THIS_MODULE
};


static struct configfs_subsystem myconfigfs={
    .su_group={
        .cg_item={
            .ci_namebuf="my_configfs",
            .ci_type=&myconfigfs_item
        }
    }
};

static int myconfigfs_init(void){
  printk("Hello World!\n");
  config_group_init(&myconfigfs.su_group);
  configfs_register_subsystem(&myconfigfs);

  config_group_init_type_name(&myconfig_group, "myconfig_group",&myconfig_group_item);
  configfs_register_group(&myconfigfs.su_group, &myconfig_group);

  return 0;
}

static void myconfigfs_exit(void){

    configfs_unregister_subsystem(&myconfigfs);
    printk("bye bye\n");
    
}

通过编译为KO文件并加载到开发板上,可以在/sys/kernel/config/myconfig/mygroup下使用mkdir创建group,使用rmdir可以删除相关group

  • 编写-实现读写属性
/**第三步:实现读写函数**/
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);
    printk("%s\n",__FUNCTION__);
    return myitem->size;
}
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;
    printk("%s\n",__FUNCTION__);
    return myitem->size;
}

/**第二步:通过宏定义实现属性的读写**/
CONFIGFS_ATTR_RO(my, read);
CONFIGFS_ATTR_WO(my, write);
static struct configfs_attribute *myattrib[]={
    &myattr_read,
    &myattr_write,
    NULL
};


struct configfs_group_operations myconfig_group_ops={
    .make_item=myconfig_group_make_item,//achieve a function for make item
};

/**第一步:在config_item_type中添加属性**/
struct config_item_type myconfig_group_item={
    .ct_owner=THIS_MODULE,
    .ct_group_ops=&myconfig_group_ops,
    .ct_attrs=myattrib
};

通过编译为KO文件并加载到开发板上,可以在/sys/kernel/config/myconfig/mygroup下使用mkdir创建group,在此group下将会有write和read两个属性。可以通过echo对write写相关参数,通过cat read查看参数值

二、内核配置

若实现设备树插件,还需要对内核进行相关配置

在这里插入图片描述

以上操作后,编译烧录到开发板。将在/sys/kernel下看到config目录,说明自动挂载上了。

如果不能自动挂载,则使用

mount -t configfs none /sys/kernel/config

接着配置内核支持设备树插件

在这里插入图片描述

在这里插入图片描述

三、移植驱动

关于设备树插件的相关驱动,我是通过网上下载得到的,就直接编译移植了(可以通过insmod加载KO方式也可以直接编译进内核)

相关文件放入如下网盘:

链接: https://pan.baidu.com/s/1uuwqCwlZNkSLorEWTmkFrQ 提取码: 5dbv

移植过程为一般驱动编译加载一样,这里不赘述。移植完成后将在sys/kernel/config下查看到一个名为device-tree的文件夹

四、设备树插件语法

1. 插件头部声明
/dts-v1/;
/plugin/;
2. 插件节点名称用于定义要添加、修改或删除的设备节点以及属性

有如下三种表示形式(以rk3568的485节点为例):

/dts-v1/;
/plugin/;
//方法一:使用&后{}中为节点绝对路径
&{/rk-485-ctl}{
    overlay_node{
        status="okay"
    };
};
//方法二:使用&后带节点别名
&rk-485-ctl{
    overlay_node{
        status="okay"
    };
};

//方法三:为方法一编译为dtbo后反编译得到
/{
    frament@1{
        target-path="/rk-485-ctl";
        __overlay__{
           overlay_node{
        		status="okay"
    		}; 
        };
    };
};

//方法四:为方法二编译为dtbo后反编译得到
/{
    frament@1{
        target=<&rk-485-ctl>;
        __overlay__{
           overlay_node{
        		status="okay"
    		}; 
        };
    };
};

五、编译与使用

1. 编译

编译的方式和编译设备树是一样的

sdk/kernel/scripts/dtc/dtc -I dts -O dtb overlay.dts -o overlay.dtbo

反编译为

sdk/kernel/scripts/dtc/dtc -I dtb -O dts overlay.dtbo -o overlay.dts
2. 使用

通过第三节加载相关驱动后,进入设备树插件相关configfs中

cd /sys/kernel/config/device-tree/overlays

创建一个内核对象

mkdir test

使用命令将dtbo写入到内核对象

cat /overlay.dtbo > /test/dtbo

使能dtbo

echo 1>/test/status

通过以下方式将能看到加载的节点

ls /proc/device-tree/rk-485-ctl/overlay_node/

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

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

相关文章

[Python图像处理] 基于图像均值消除随机噪声

基于图像均值消除随机噪声 前言图像均值计算去除图像噪声相关链接 前言 在本节中&#xff0c;我们将学习如何从一组带有噪声的输入图像中估算一个无噪声的图像&#xff0c;所有图像都是通过使用原始(无噪声)图像像素添加独立同分布的随机噪声创建的&#xff0c;只需计算噪声图…

智能手表上的音频(三):音频文件播放

上篇讲了智能手表上音频相关的驱动。本篇开始讲具体的功能&#xff0c;先讲音频文件播放。 音频文件格式众多&#xff0c;目前我们仅支持了最常见的几种&#xff1a;WAV/MP3/AAC/AMR(主要用于播放录音文件)。相对于播放WAV(即PCM)&#xff0c;MP3/AAC/AMR多了个解码过程。音频文…

Synchronized关键字详解

1. Synchronized简介及使用 1.1 简介 在Java中&#xff0c;synchronized 是一个关键字&#xff0c;用于实现多线程环境下的同步控制&#xff0c;确保线程安全性。它可以应用于方法、代码块或静态方法上&#xff0c;提供了对临界区&#xff08;共享资源&#xff09;的互斥访问…

Python知识点——高维数据的格式化

常用JSON格式对高维数据进行表达和存储&#xff1a; 常见的高维数据最典型的例子&#xff1a;<key,value>键值对 JSON格式表达键值对<key, value>的基本格式如下&#xff0c;键值对都保存在双引号中&#xff1a; "key" : "value" Json库 dump…

前后端交互常见的几种数据传输格式 form表单+get请求 form表单+post请求 json键值对格式

目录 1. get请求 query string 2.form表单get请求 3..form表单post请求 4..json格式 5.总结 1. get请求 query string 前端通过get请求携带 query string&#xff08;键值对&#xff09; ,后端通过req.getParameter(key)方法获取数据。如果key不存在&#xff0c;获取到的就…

苹果手机钱包怎么付款?教你如何使用Apple Pay支付

苹果钱包是iPhone手机的一项实用功能&#xff0c;可以将银行卡、信用卡、交通卡、学生证等收纳在其中。在日常生活中&#xff0c;手机支付变得越来越方便。那么&#xff0c;如何在苹果手机上使用Apple Pay进行支付呢&#xff1f;苹果手机钱包怎么付款&#xff1f;下面请跟着小编…

Redis小记(一)

NoSQL数据库简识 引入 随着web时代的到来&#xff0c;PC端和移动端的用户越来越多&#xff0c;之前的单体服务器已经承载不住这么大量的访问请求操作&#xff0c;如今就演化成了下图这种&#xff0c;通过ngnix负载均衡&#xff0c;将请求以分摊到多个不同的服务器上&#xff…

微信小程序rich-text里面写单行溢出显示省略号在ios中不显示的问题

项目用uniapp开发。然后赋值用v-html来写。&#xff08;v-html可以转换html格式并且展示。运行到小程序里面是用rich-text标签展示&#xff09; 原来返回的item.titleName如下&#xff1a;<font style"color: #ff4343;">测试</font>课程名称长度加加加爱…

【微信小程序】新版获取手机号码实现一键登录(uniapp语法)(完整版附源码)

需求 如图&#xff0c;点击按钮&#xff0c;获取用户手机号实现一键登录&#xff0c;当然&#xff0c;用户也可以自行输入其他手机号进行登录 问题 要想获取用户手机号并不复杂&#xff0c;但由于近几年微信小程序获取手机号的api进行了更新&#xff0c;当前很多帖子使用的…

【delphi】中 TNetHTTPClient 注意事项

一、TNetHTTPClient 是什么&#xff1f; 用于管理 HTTP 客户端的组件。相当于indy中的TidHTTP控件&#xff0c;是实现HTTP请求的客户端控件。 二、TNetHTTPClient 需要注意什么&#xff1f; 需要注意的是几个Timeout&#xff0c;因为我们使用TNetHTTPClient控件的时候&#x…

1 快速了解Paimon数据湖核心原理及架构

1.1 什么是Apache Paimon Apache Paimon的前身属于Flink的子项目&#xff1a;Flink Table Store。 目前业内主流的数据湖存储项目都是面向批处理场景设计的&#xff0c;在数据更新处理时效上无法满足流式数据湖的需求&#xff0c;因此Flink社区在2022年的时候内部孵化了 …

Golang Windows系统使用make build

gcc -v 找到上面mingw64/bin目录&#xff0c;复制mingw32-make.exe一份&#xff0c;改成make.exe ,即可。

9.spark自适应查询-AQE之动态调整Join策略

目录 概述动态调整Join策略原理实战 动态优化倾斜的 Join原理实战 概述 broadcast hash join 类似于 Spark 共享变量中的广播变量&#xff0c;Spark join 如果能采取这种策略&#xff0c;那join 的性能是最好的 自适应查询AQE(Adaptive Query Execution) 动态调整Join策略 原…

金蝶云星空单据转换下推时上游单据的主键和明细主键获取和保存

文章目录 金蝶云星空单据转换下推时上游单据的主键和明细主键获取和保存产品序列号对照表增加字段创建单据转换插件在单据转换规则注册插件测试 金蝶云星空单据转换下推时上游单据的主键和明细主键获取和保存 比如&#xff1a;售后单下推对照表&#xff0c;是一对一&#xff0…

(深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会

hi&#xff0c;同学们&#xff0c;我是赤辰 7月份的时候&#xff0c;Open AI就找来了一位全球顶级的华人产品经理Peter Deng&#xff0c;要给这个技术很强但交互很差ChatGPT动一番大手术。 在11月7日凌晨2点&#xff0c;终于等到了Open AI发布的首届开发者大会&#xff0c;也被…

Vb6 TCP Server服务端监听多个RFID读卡器客户端上传的刷卡数据

本示例使用设备介绍&#xff1a;WIFI无线4G网络RFID云读卡器远程网络开关物流网阅读器TTS语音-淘宝网 (taobao.com) Option ExplicitConst BUSY As Boolean False 定义常量 Const FREE As Boolean TrueDim ConnectState() As Boolean 定义连接状态 Dim ServerSendbuf(…

超级好用的几个工具

JamTools JamTools是一个全平台支持的小工具集软件&#xff0c;可用于Windows 7/8/10/11、MacOS、Ubuntu等系统&#xff08;其他系统可自行编译源码进行打包&#xff09;。该工具集包含了多项实用功能&#xff0c;如滚动/区域截屏、录屏、文字识别、多语言互译、多媒体格式转换…

计算机网络——物理层-物理层的基本概念、物理层下面的传输媒体

目录 物理层的基本概念 传输媒体 物理层的基本概念 在计算机网络中&#xff0c;用来连接各种网络设备的传输媒体种类众多。大致可以分为两类。一类是导引型传输媒体&#xff0c;另一类是非导引型传输媒体。 在导引型传输媒体中&#xff0c;常见的有双绞线、同轴电缆、光纤。…

Qt开发流程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;…

是时候和 Confluence 说再见了

作为最早为 Confluence 中文化提供服务的社区&#xff0c;我们也面临着最后的时间了。 Confluence 已经不再为用户签发开源许可证了&#xff0c;这意味着在今年许可证到期后&#xff0c;我们要不就需要把所有数据迁移到 Confluence 云平台上。 要不就自己部署完整的云平台服务…