I.MX6ULL内核开发10:设备树

news2025/7/20 14:20:56

目录

一、设备树简介

二、设备树源码

三、获取设备树信息

1、增加设备节点

2、内核编译设备树

3、替换设备树文件

4、查看设备树节点

5、在驱动中获取节点的属性

6、编译驱动模块

7、加载模块


一、设备树简介

设备树的作用是描述一个硬件平台的硬件资源。这个“设备树”可以被bootloader(uboot)传递到内核,内核可以从设备树中获取硬件信息。

几个常见的文档缩写符号:

  • DTS:是指.dts格式的文件,是一种ASII文本格式的设备树描述,设备树源码,一般一个.dts文件对应一个硬件平台。
  • DTC:是指编译设备树源码的工具,一般情况下需要手动安装。
  • DTB:是设备树源码编译生成的文件,类似于C语言中的“.bin”文件,可以被硬件识别。

二、设备树源码

文件定位:内核源码/arch/arm/boot/dts/imx6ull-mmc-npi.dts

/*----------------------头文件--------------------------*/
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

/*----------------------中间部分--------------------------*/

/ {
	model = "Embedfire i.MX6ULL Board";
	compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

	aliases {
		pwm0 = &pwm1;
		pwm1 = &pwm2;
		pwm2 = &pwm3;
		pwm3 = &pwm4;
	};
	chosen {
		stdout-path = &uart1;
	};

	memory {
		reg = <0x80000000 0x20000000>;
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		linux,cma {
			compatible = "shared-dma-pool";
			reusable;
			size = <0x14000000>;
			linux,cma-default;
		};
	};

    /*-------------以下内容省略--------------------*/
};
/*------------------追加部分--------------------------*/
&cpu0 {
	/*dc-supply = <&reg_gpio_dvfs>;*/
	clock-frequency = <800000000>;
};

&clks {
	assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
	assigned-clock-rates = <786432000>;
};

/*------------------以下内容省略--------------------------*/

文件位置:内核源码/arch/arm/boot/dts/imx6ull.dtsi

cpus {
		#address-cells = <1>;
		#size-cells = <0>;

		cpu0: cpu@0 {
			compatible = "arm,cortex-a7";
			device_type = "cpu";
        /*----------以下内容省略-----------------*/
        };
    };

设备树源码分为3部分:

  • 第1-2行:头文件。头文件的引用有两种,一种为“.h”文件,一种为“.dtsi文件”。imx6ull.dtsi由NXP官方提供,是一个imx6ull平台“共用”的设备树文件。
  • 中间部分:设备树节点。由{}组成,每一个设备树都只有一个“/”节点。
  • 追加部分:&的意思是像原有的节点中追加内容。

三、获取设备树信息

1、增加设备节点

        在ebf_linux_kernel/arch/arm/boot/dts/imx6ull-mmc-npi.dts这个设备树中,尝试增加一个新的设备节点。

led_test{
	#address-cells = <1>;
	#size-cells = <1>;

	rgb_led_red@0x0209C000{
			compatible = "fire,rgb_led_red";
			reg = <0x0209C000 0x00000020>;
			status = "okay";
	};
};
  • 以上节点中,#address-cells = <1>,#size-cells = <1>,意味着它的子节点的reg属性里的数据是“地址”、“长度”交替的。
  • 第二部分led的子节点,定了三种属性,分别为compatible、reg、status。由于在父节点设置了#address-cells = <1>,#size-cells = <1>,所以0x0209C000表示的是地址(这里填写的GPIO1控制寄存器的首地址),0x00000020表示的是地址长度。

2、内核编译设备树

        编译内核时会自自动编译设备树,到那时编译内核非常耗时,所以可以用以下命令只编译设备树。

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

执行结果如下:

 编译成功后生成的设备树文件(.dtb)位于源码目录下/arch/arm/boot/dts/,文件名为“imx6ull-mmc-npi.dtb”

3、替换设备树文件

        将上位机上生成的.dtb文件替换掉开发板中的.dtb文件,替换/usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb。

uboot在启动的时候负责将该目录的设备我呢见加载到内存,供内核使用。

4、查看设备树节点

        设备树的设备节点在文件系统中有与之相对应的文件,位于/proc/device-tree目录,进入目录查看如下:

 接着进入led_test文件夹,可以发现led_test节点中定义的属性以及它的子节点,如下所示。

 在节点属性中多了一个name,我们在led节点中并没有定义name属性,这是自己生成的,保存节点名。

这里的属性是一个文件,而子系欸但是一个文件夹,再次进入“rgb_led_red@0x0209C000”文件夹。里面有compatible name regs status四个属性文件。我们可以使用“cat”命令查看这些属性文件,如下所示:

 至此,成功的在设备树中添加了一个名为“led_test”的节点。

5、在驱动中获取节点的属性

        实验是一个简化的字符设备驱动,在驱动中没有实际操作硬件,仅在.open函数中调用of函数获取设备树节点中的属性,获取成功后打印获取到的内容。

代码:get_dts_info.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>


#define DEV_NAME            "get_dts_info"
#define DEV_CNT                 (1)
//定义字符设备的设备号
static dev_t led_devno;
//定义字符设备结构体chr_dev
static struct cdev led_chr_dev;
//创建类
struct class *led_chrdev_class;

struct device_node	*led_device_node; //led的设备树节点
struct device_node  *rgb_led_red_device_node; //rgb_led_red 红灯节点
struct property     *rgb_led_red_property;    //定义属性结构体指针
int size = 0 ;
unsigned int out_values[18];  //保存读取得到的REG 属性值

/*.open 函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
    int error_status = -1;

    printk("\n open form device \n");

    /*获取DTS属性信息*/
    led_device_node = of_find_node_by_path("/led_test");
    if(led_device_node == NULL)
    {
        printk(KERN_ALERT "\n get led_device_node failed ! \n");
        return -1;
    }
    /*根据 led_device_node 设备节点结构体输出节点的基本信息*/
    printk(KERN_ALERT "name: %s",led_device_node->name); //输出节点名
    printk(KERN_ALERT "child name: %s",led_device_node->child->name);  //输出子节点的节点名


    /*获取 rgb_led_red_device_node 的子节点*/ 
    rgb_led_red_device_node = of_get_next_child(led_device_node,NULL); 
    if(rgb_led_red_device_node == NULL)
    {
        printk(KERN_ALERT "\n get rgb_led_red_device_node failed ! \n");
        return -1;
    }
    printk(KERN_ALERT "name: %s",rgb_led_red_device_node->name); //输出节点名
    printk(KERN_ALERT "parent name: %s",rgb_led_red_device_node->parent->name);  //输出父节点的节点名


    /*获取 rgb_led_red_device_node 节点  的"compatible" 属性 */ 
    rgb_led_red_property = of_find_property(rgb_led_red_device_node,"compatible",&size);
    if(rgb_led_red_property == NULL)
    {
        printk(KERN_ALERT "\n get rgb_led_red_property failed ! \n");
        return -1;
    }
    printk(KERN_ALERT "size = : %d",size);                      //实际读取得到的长度
    printk(KERN_ALERT "name: %s",rgb_led_red_property->name);   //输出属性名
    printk(KERN_ALERT "length: %d",rgb_led_red_property->length);        //输出属性长度
    printk(KERN_ALERT "value : %s",(char*)rgb_led_red_property->value);  //属性值


    /*获取 reg 地址属性*/
    error_status = of_property_read_u32_array(rgb_led_red_device_node,"reg",out_values, 2);
    if(error_status != 0)
    {
        printk(KERN_ALERT "\n get out_values failed ! \n");
        return -1;
    }
    printk(KERN_ALERT"0x%08X ", out_values[0]);
    printk(KERN_ALERT"0x%08X ", out_values[1]);

    return 0;
}

/*.release 函数*/
static int led_chr_dev_release(struct inode *inode, struct file *filp)
{
    printk("\nrelease\n");
    return 0;
}

/*字符设备操作函数集*/
static struct file_operations  led_chr_dev_fops = 
{
    .owner = THIS_MODULE,
    .open = led_chr_dev_open,
    .release = led_chr_dev_release,
};

/*
*驱动初始化函数
*/
static int __init led_chrdev_init(void)
{
    int ret = 0;
    printk("led chrdev init\n");
    //第一步
    //采用动态分配的方式,获取设备编号,次设备号为0,
    //设备名称为EmbedCharDev,可通过命令cat  /proc/devices查看
    //DEV_CNT为1,当前只申请一个设备编号
    ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
    if(ret < 0){
        printk("fail to alloc led_devno\n");
        goto alloc_err;
    }

    led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");
    //第二步
    //关联字符设备结构体cdev与文件操作结构体file_operations
    cdev_init(&led_chr_dev, &led_chr_dev_fops);
    //第三步
    //添加设备至cdev_map散列表中
    ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
    if(ret < 0)
    {
        printk("fail to add cdev\n");
        goto add_err;
    }

    //创建设备
    device_create(led_chrdev_class, NULL, led_devno, NULL,
			      DEV_NAME);
    return 0;

add_err:
    //添加设备失败时,需要注销设备号
    unregister_chrdev_region(led_devno, DEV_CNT);
alloc_err:
    return ret;
}

/*
*驱动注销函数
*/
static void __exit led_chrdev_exit(void)
{
    printk("chrdev exit\n");
   
    device_destroy(led_chrdev_class, led_devno);   //清除设备
    cdev_del(&led_chr_dev);                        //清除设备号
    unregister_chrdev_region(led_devno, DEV_CNT);  //取消注册字符设备
    class_destroy(led_chrdev_class);               //清除类
}

module_init(led_chrdev_init);
module_exit(led_chrdev_exit);

MODULE_LICENSE("GPL");

  •  使用“of_find_node_by_path”函数勋章“led_test”设备节点。参数是“led_test”的设备节点路径。
  • 获取成功后得到的是一个device_node类型的结构体指针,然后就可以从这个结构体中获得想要的数据。获取完整的属性可能还需要使用其他of函数。
  • 获取rgb_led_red_device_node的子节点,在上一部分中获取的“led”节点的“设备节点结构体”,这里就可以使用“of_get_next_child”函数获取它的子节点。也可以从“led”节点的“设备节点结构体”中直接读取它的第一个子节点。
  • 使用“of_find_property”函数获取“rgb_led_red”节点的“compatiable”属性
  • 使用“of_property_read_u32_array”函数获取reg属性

6、编译驱动模块

        命令:make

        生成get_dts_info.ko驱动模块

7、加载模块

        将ko文件拷贝到开发板,使用insmod安装模块然后可以在/dev/目录找到get_dts_info 

         向模块中随便输入一个字符

        sudo sh -c "echo '1' >> /dev/get_dts_info"


 可以看出已经成功打印出led节点属性和rgb_led_red字节点属性。

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

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

相关文章

Redis主从和哨兵搭建

今天主要分享Redis主从架构和哨兵的搭建。 主从集群搭建 总共三个节点&#xff0c;一个主节点和两个从节点。都安装在一台机器上模拟主从集群&#xff0c;信息如下&#xff1a; IPPORT角色192.168.246.1407001slave192.168.246.1407002master192.168.246.1407003slave 我们只…

FreeRTOS入门(04):中断、内存、追踪与调试

文章目录目的中断内存堆&#xff08;heap&#xff09;栈&#xff08;stack&#xff09;断言调试总结目的 有了前面的几篇文章 FreeRTOS 基本上已经可以在项目中使用上了&#xff1a; 《FreeRTOS入门&#xff08;01&#xff09;&#xff1a;基础说明与使用演示》 《FreeRTOS入门…

【java 8】强大的 Stream API

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

因子分析计算权重

因子分析两类权重计算方法总结 案例背景 疫情爆发以来&#xff0c;越来越多的人为了避免线下与人接触&#xff0c;选择了线上购买生活必需品。网购虽然方便快捷&#xff0c;但是随着订单压力的增加&#xff0c;物流问题也随之出现&#xff0c;近期有很多卖家收到物流投诉的问题…

Ubuntu下Python的安装及管理

Ubuntu下Python的安装及管理 1.概述 Ubuntu下python的安装及配置。 2.安装 安装python2.7&#xff1a; python --version #或python2.7 --version检查检查python是否存在&#xff0c;有则无需继续安装python2.7 sudo apt-get update sudo apt-get install python #或者su…

【正点原子FPGA连载】第十五章eMMC读写测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十五章eMMC读写…

BI工具术语表大全:从字母A-Z全面收录

谈到商业智能行业&#xff0c;变革是不可避免的。为了跟上步伐&#xff0c;各种各样的BI 解决方案正在快速迭代更新&#xff0c;以满足企业的数字化需求&#xff0c;那么市场上BI 工具种类繁杂&#xff0c;到底如何选择适合功能全面、满足自己企业运转情况的、合适的BI 工具呢&…

来啦来啦,续篇来啦,CMOS逻辑IC基本工作原理总结概述

在上一篇文章中我们已经认识了CMOS逻辑IC的基本知识和分类&#xff0c;由于功耗和成本以及复杂度的平衡性最好&#xff0c;是实际应用中最常用的一种。今天我们就详细跟大家一起了解东芝CMOS逻辑IC的基本工作原理还有其CMOS逻辑IC系列型号选型吧。 东芝CMOS逻辑IC系列型号选型表…

什么是主数据

整理不易&#xff0c;转发请注明出处&#xff0c;请勿直接剽窃&#xff01; 点赞、关注、不迷路&#xff01; 摘要&#xff1a;主数据定义、解释&#xff1b;主数据管理意义、管理工具 定义&#xff1a;主数据是企业内核心业务实体数据。 解释&#xff1a;主数据&#xff08;Ma…

gRPC Ecosystem 初探

grpc是目前使用很广泛的远程过程调用库&#xff0c;在使用过程中&#xff0c;我们通常会使用protoc命令将proto文件转成特定的代码进行使用。那一些复杂点的应用有没有可以支撑的工具或类库呢。 前段时间在进行自定义grpc tls证书认证的过程中&#xff0c;知道了有一个github项…

【重庆邮电大学协办】2023年第六届数据挖掘与知识发现国际会议(DMKD 2023)

【重庆邮电大学协办】2023年第六届数据挖掘与知识发现国际会议(DMKD 2023) 重要信息 会议网址&#xff1a;www.icdmkd.org 会议时间&#xff1a;2023年6月24-26日 召开地点&#xff1a;中国-重庆 截稿时间&#xff1a;2023年5月24日 录用通知&#xff1a;投稿后2周内 收录…

测试部门来了个99年的卷王之王,老油条感叹真干不过,但是...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

实战-缓存数据一致+binlog初始+cannel监听+数据迁移,数据一致性架构设计

前言 一. 解决缓存不命中&#xff08;高并发操作击穿打挂DB的风险&#xff09; 当并发量打的时候&#xff0c;当我们的缓存过期时&#xff0c;就算到数据库的比例偏小的时候&#xff0c;我们的请求时比较大的。那也会存在数据库崩掉的情况。解决方案想法如下&#xff08;总体…

电商共享购模式,消费增值返利,app开发

在当今以市场需求为主导的数字经济时代&#xff0c;消费者需求呈现出精细化管理和多元化的特性&#xff0c;目标市场日渐完善&#xff0c;另外在大数据技术迅速进步和运用的驱动下&#xff0c;总体行业的发展节奏感也在不断加速。因而&#xff0c;企业需要建立一套灵活多变的经…

【Leedcode】数据结构中链表必备的面试题(第五期)

【Leedcode】数据结构中链表必备的面试题&#xff08;第五期&#xff09; 文章目录【Leedcode】数据结构中链表必备的面试题&#xff08;第五期&#xff09;1.题目2.思路图解&#xff08;1&#xff09;第一步&#xff1a;复制每一个结点&#xff0c;插入到原结点和下一个结点之…

OAK相机如何将yolox模型转换成blob格式?(0.1.1pre 及之后版本)

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

新能源充电桩控制板迭代升级,打开充电桩行业发展新机遇

作为新能源汽车可持续发展的关键因素&#xff0c;充电桩的基础建设和完善一直备受关注。伴随着新能源汽车充电桩规模的扩大和停车位的增加&#xff0c;传统的充电桩控制板已经无法满足人们的充电需求&#xff0c;同时急需加强对新能源汽车充电桩的有效管理&#xff0c;新能源充…

Kali Linux使用Metasploit生成木马入侵安卓系统

额&#xff0c;这是我最后一篇文章了&#xff0c;周一我们开学了 文章目录前言一、Metasploit是什么&#xff1f;演示环境二、生成可执行木马文件1.生成2.运行命令并生成木马配置参数入侵安卓手机命令1.查看对方手机系统信息查看对方手机安装哪些app文件总结前言 前言&#xf…

Sophos Firewall OS (SFOS) 19.5 MR1 - 同步下一代防火墙

Sophos Firewall OS (SFOS) 19.5 MR1 - 同步下一代防火墙 请访问原文链接&#xff1a;https://sysin.org/blog/sfos-19-5/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org Sophos Firewall v19.5 现已推出 Sophos Firewall…

H5视频上传与播放

背景 需求场景&#xff1a; 后台管理系统&#xff1a; &#xff08;1&#xff09;配置中支持上传视频、上传成功后封面缩略图展示&#xff0c;点击后自动播放视频&#xff1b; &#xff08;2&#xff09;配置中支持上传多个文件&#xff1b; 前台系统&#xff1a; &#…