浅谈linux - 描述硬件的文件设备树

概述

设备树(device tree),通俗的讲就是将设备描述信息抽象成树的结构。树的主干为系统总线,也就是根节点,根节点下是各种总线控制器,控制器下则是一系列硬件设备节点等。

在Linux2.6中,板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,导致内核变得庞大且冗余,然而这些板级细节对于内核来讲,不过是垃圾,因此设备树应运而生,工程师可以将硬件细节直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

注意

1、dts(Device Tree Source),设备树源文件,放在内核的/arch/arm/boot/dts目录。

2、dtsi(Device Tree Source Include),设备树共有信息,类似C语言的头文件。

3、dtb(Device Tree binary ),设备树dts文件编译后的目标文件,传递给内核,由内核进行解析。

4、dtc(Device Tree Compiler),设备树编译工具,用于将设备树dts文件编译成dtb文件。

5、设备树执行流程:

①、用户编写dtsi、dts文件。

②、执行命令make dtbs,使用DTC工具将dts文件编译成目标文件dtb。

③、uboot启动时自动将dtb文件传递给内核。

④、内核解析dtb文件。

接口

设备树(由节点构成)语法:

①节点命名一般为label:node-name@unit-address格式。

label表示节点标签,目的是为了方便访问节点。访问节点通过&label。

node-name表示节点名,字符串类型,用于描述节点功能。

unit-address一般为设备地址或寄存器首地址,也可以没有。

注意:node-name@unit-address才表示节点名。

fengdts:fdts@77777777 {
    ...
    reg = <
        0x77777777 0x7
        0x37373737 0x8
    >;
    child_fnode1:fnode1@12345678 {
        ...
        reg = <
            0x12345678 
            0x12345679
        >;
    }; 
};

②节点由属性构成,属性实则是一些键值对(key = value),值(value)可以为空或者任意数据流。

值是字符串数据时用‘“”’限定,数组元素之间用逗号(,)隔开。

值是二进制数据用‘[]’限定,数组元素之间空格隔开。

值是32位无符号数据用‘<>’限定,数组元素之间空格隔开。

值是不同类型数据用‘,’隔开。

key = value;                    /* 属性表示方法,使用键值对的方式,即key = value,value可以为空 */

good;                           /* value可以为空 */
string_buf = "okey", "feng";    /* 字符串数据用‘“”’限定, 数组元素之间逗号隔开 */
byte_buf = [12 34 45 67];       /* 二进制数据用‘[]’限定,数组元素之间空格隔开,0x12,0x34,0x45,0x67 */
reg = <
        0x77777777 0x7
        0x37373737 0x8
>;                             /* 32位无符号数据用‘<>’限定,数组元素之间空格隔开 */
mix_buf = [12 45], "good", <0x23456789 0x87654321>;  /* 不同类型数据用‘,’隔开 */

③节点的一些特殊属性。

compatible:平台兼容,一般格式为“制造商,型号”,用于驱动匹配。

reg:寄存器,格式是"<address,length>",是一个可变u32的数组,由一系列地址和长度组成。

#address-cells和#size-cells:用来标识reg属性中address和length字段长度,注意:#address-cells和#size-cells仅仅对子节点有效,当前节点的address和length字段长度由父节点#address-cells和#size-cells值决定。

device_type:设备类型,寻找节点时可以依据这个属性(type)。

interrupts:中断控制器。

④一些特殊节点。

/:代表根节点。

chosen:包含板级启动参数(bootargs)。

aliases:节点别名,必须节点全称,可以通过地址引用获取。

cpus:CPU相关信息。

memory:内存相关信息。

⑤帮助文档,在linux内核源码目录下的/Documentation/devicetree/bindings,有关于设备树节点的详细介绍和说明,当你没有思路的时候,或许可以去看看。

设备树常用操作函数,主要定义在头文件linux/of.h中。

①内核使用struct device_node来描述设备树对象,并提供相应的接口供用户找到指定的节点。

/* 内核描述设备节点信息的结构定义,在linux/of.h中 */
struct device_node {
    const char *name;          /* 名字 */
    const char *type;          /* 类型 */
    phandle phandle;
    const char *full_name;  /* 全名 */
    struct fwnode_handle fwnode;

    struct  property *properties;   /* 属性 */     
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent;    /* 指向父节点 */
    struct  device_node *child;     /* 指向子节点 */
    ...
};

/* 查找节点,在linux/of.h中 */
/**
 * @根据节点名字查找节点
 * @from: 开始查找的节点,如果为NULL表示从根节点开始查找整个设备树。       name:节点名
 * @成功返回找到的节点,失败返回 NULL
 */
extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);

/**
 * @根据节点device_type属性查找节点
 * @from: 开始查找的节点,如果为NULL表示从根节点开始查找整个设备树。       type:device_type的属性值
 * @成功返回找到的节点,失败返回 NULL
 */
extern struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

/**
 * @根据节点 device_type和compatible查找节点
 * @from: 开始查找的节点,如果为NULL表示从根节点开始查找整个设备树。       
 * @type:device_type的属性值       compat:compatible的属性值 
 * @成功返回找到的节点,失败返回 NULL
 */
extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);

/**
 * @根据节点路径查找节点
 * @path: 节点的全路径,从根节点‘/’开始
 * @成功返回找到的节点,失败返回 NULL
 */
static inline struct device_node *of_find_node_by_path(const char *path);

②节点由属性构成,内核使用struct property来描述属性对象,并提供相应的接口供用户获取指定的节点属性信息。

/* 内核描述设备节点属性信息的结构定义 */
struct property {
    char    *name;  /* 名字 */  
    int length;     /* 长度 */
    void    *value; /* 值 */
    struct property *next;  /* 下一个属性 */
    ...
};
/* 获取节点指定属性,在linux/of.h中 */
/**
 * @获取节点指定属性
 * @np: 节点        name:属性名,如:compatible、reg等         lenp:属性值长度,字节为单位
 * @成功返回找到的属性,失败返回NULL
 */
extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);

/**
 * @获取节点指定属性元素个数,一般用于数组元素数量获取,如reg等
 * @np: 节点        propname:属性名,如:compatible、reg等     elem_size:属性单个元素长度,字节为单位
 * @返回个数
 */
extern int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size);

/**
 * @获取节点指定属性指定标号的u32类型数据值,注意:标号从0开始,一般用于获取数组中指定下标元素值
 * @np: 节点        propname:属性名,如:compatible、reg等     index:下标,从0开始    out_value:保存值
 * @成功返回0,-EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小
 */
extern int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value);

/**
 * @获取节点指定属性指定标号的字符串类型数据值,注意:标号从0开始,一般用于获取数组中指定下标元素值
 * @np: 节点        propname:属性名,如:compatible、reg等     index:下标,从0开始    output:保存值
 * @成功返回0,-EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小
 */
static inline int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output);


/**
 * @当节点属性只有一个元素(非数组)时,获取节点指定属性值,包含bool/u8/u16/u32/s32/u64/string类型数据读取
 * @np: 节点        propname:属性名,如:compatible、reg等     out_value/out_string:保存值
 * @成功返回0,-EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小
 */
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
static inline int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value);
static inline int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value);
static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);
static inline int of_property_read_s32(const struct device_node *np, const char *propname, s32 *out_value);
extern int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);
static inline int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);

/**
 * @当节点属性有多个元素(数组)时,获取节点指定属性值,包含u8/u16/u32/u64/string类型数据读取
 * @np: 节点        propname:属性名,如:compatible、reg等     out_value:保存值       sz:数组元素个数
 * @成功返回0,-EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小
 * @注意:对于字符串数组的读取of_property_read_string_array,返回值应该是实际数组元素个数
 */
extern int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz);
extern int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz);
extern int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz);
extern int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz);
static inline int of_property_read_string_array(struct device_node *np, const char *propname, const char **out_strs, size_t sz);

③获取节点#address-cells和#size-cells属性值。

/**
 * @获取节点#address-cells和#size-cells属性值,在linux/of.h中
 * @np: 节点
 * @返回属性值,注意#address-cells和#size-cells属性针对的是子节点,对当前节点和父节点无效
 */
extern int of_n_addr_cells(struct device_node *np);
extern int of_n_size_cells(struct device_node *np);

④获取节点的父节点和子节点。

/* 获取父子节点,在linux/of.h中 */
/**
 * @获取父节点
 * @node: 节点
 * @成功返回父节点,失败返回NULL
 */
extern struct device_node *of_get_parent(const struct device_node *node);
/**
 * @获取子节点
 * @node: 节点      prev:上一个子节点,NULL表示获取第一个子节点
 * @成功返回子节点,失败返回NULL
 */
extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev);
/**
 * @获取指定名字的子节点
 * @node: 节点      name:子节点名字
 * @成功返回子节点,失败返回NULL
 */
extern struct device_node *of_get_child_by_name(const struct device_node *node, const char *name);

示例

★示例仅用于展示设备树的应用,编写的设备信息并无实际作用,采用正点原子的阿尔法开发板进行验证。

★包含驱动头文件dev_tree.h和源文件dev_tree.c、设备树源文件atomic.dts(只截取了测试部分的设备树内容)、拷贝设备树目标文件和内核文件到tftp指定目录的脚本文件cp_kennel以及编译规则文件Makefile(均已验证通过)。

 dev_tree.h

/**
 * @Filename : dev_tree.h
 * @Revision : $Revision: 1.00 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 设备树示例
**/

#ifndef __DEV_TREE_H__
#define __DEV_TREE_H__

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define MAX_REG_SIZE        10  /* 最大寄存器数量 */
#define CHILD1_REG_SIZE     2   /* 子节点1寄存器数量 */
#define CHILD2_REG_SIZE     2   /* 子节点2寄存器数量 */
#define PARENT_REG_SIZE     4   /* 父节点寄存器数量 */
#define BYTE_BUF_SIZE       4   /* byte_buf数组元素数量 */
#define STR_BUF_SIZE        2   /* string_buf数组元素数量 */

/*
fengdts:fdts@77777777 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible ="feng, dtstest";
    reg = <
        0x77777777 0x7
        0x37373737 0x8
    >;
    feng_state = "okey";
    string_buf = "seven","feng";
    byte_buf = [12 34 45 67];
    good;   
    child_fnode1:fnode1@12345678 {
        compatible ="feng, dtstest_child1";
        reg = <
            0x12345678 
            0x12345679
        >;
        mix_buf = [12 45], "good", <0x23456789 0x87654321>;
    }; 
    child_fnode2: fnode2@87654321 {
        compatible ="feng, dtstest_child2";
        reg = <
            0x87654321 
            0x23456789
        >;
    };
};
*/

/* 设备树基本信息定义 */
struct dev_tree_base {
    struct device_node *np;          /* 保存节点 */
    struct property *compatible;     /* 保存compatible属性 */  
    int reg_n_addr;                  /* 寄存器地址占位 #address-cells */
    int reg_n_size;                  /* 寄存器长度占位 #size-cells */
    unsigned int reg[MAX_REG_SIZE];  /* 保存寄存器信息 */
};

/* 设备树资源信息定义 */
struct dev_tree {
    struct dev_tree_base base;         /* 基本信息 */
    struct dev_tree_base child1;       /* 子节点1信息 */
    struct dev_tree_base child2;       /* 子节点2信息 */

    unsigned char good;                 /* 保存good属性 */  
    char *string[STR_BUF_SIZE];         /* 保存string_buf信息 */
    unsigned char byte[BYTE_BUF_SIZE];  /* 保存byte_buf信息 */
    struct property *state;             /* 保存feng_state属性 */
    struct property *mix_buf;           /* 保存mix_buf属性 */
};

#endif

 dev_tree.c

/**
 * @Filename : dev_tree.c
 * @Revision : $Revision: 1.00 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 设备树示例
**/

#include "dev_tree.h"

struct dev_tree feng_dev_tree;

/**
 * @设备树基本信息获取
 * @p_base: 存储设备树基本信息       reg_num:寄存器元素数量
 */
static void _get_dev_tree_base_info(struct dev_tree_base *p_base, int reg_num)
{
    int i;

    /* 节点不存在,直接返回 */
    if (p_base->np == NULL)
        return;

    printk("name : %s\n", (char *)p_base->np->name);
    printk("full name : %s\n", (char *)p_base->np->full_name);

    /* 获取compatible属性 */
    if ((p_base->compatible = of_find_property(p_base->np, "compatible", NULL)) == NULL)
        printk("compatible not found\n");
    else
        printk("compatible : %s\n", (char *)p_base->compatible->value);

    /* 获取#address-cells的值 */
    p_base->reg_n_addr = of_n_addr_cells(p_base->np);
    printk("reg_n_addr : %d\n", p_base->reg_n_addr);

    /* 获取#size-cells的值 */
    p_base->reg_n_size = of_n_size_cells(p_base->np);
    printk("reg_n_size : %d\n", p_base->reg_n_size);

    /* 获取寄存器属性 */
    if (of_property_read_u32_array(p_base->np, "reg", p_base->reg, reg_num) == 0) {
        printk("reg : ");
        for (i=0; i<reg_num; i++)
            printk("0x%x ", p_base->reg[i]);
        printk("\n");
    } else {
        printk("reg not found\n");
    }
}

/**
 * @设备树信息获取
 * @p_tree: 存储设备树相关信息   
 * @成功返回0,失败返回-1
 */
static int _get_dev_tree_info(struct dev_tree *p_tree)
{
    char *str;
    int i, num = 0;

    printk("----------------parent node----------------\n");
    /* 查找节点 */
    if ((p_tree->base.np = of_find_node_by_path("/fdts@77777777")) == NULL)
        return -1;

    _get_dev_tree_base_info(&p_tree->base, PARENT_REG_SIZE);    /* 获取基本属性 */

    /* 获取good属性 */
    if ((of_find_property(p_tree->base.np, "good", NULL)) == NULL)
        p_tree->good = 0;
    else
        p_tree->good = 1;
   printk("good : %d\n", p_tree->good);     

    /* 获取string_buf属性 */
    if (of_property_read_string_array(p_tree->base.np, "string_buf", (const char **)p_tree->string, STR_BUF_SIZE) > 0) {
        printk("string : ");
        for (i=0; i<STR_BUF_SIZE; i++)
            printk("%s ", p_tree->string[i]);
        printk("\n");
    } else {
        printk("string not found\n");
    }

    /* 获取string_buf[1]的值 */
    if (of_property_read_string_index(p_tree->base.np, "string_buf", 1, (const char **)&str) == 0)
        printk("string_buf[1] : %s\n", str); 
    else
        printk("string_buf[1] not found\n");

    /* 获取byte_buf数组元素个数 */
    if ((num = of_property_count_elems_of_size(p_tree->base.np, "byte_buf", sizeof(unsigned char))) > 0)
        printk("byte_buf num : %d\n", num); 
    else
        printk("byte_buf num : 0\n");
    /* 获取byte_buf属性 */
    if (of_property_read_u8_array(p_tree->base.np, "byte_buf", p_tree->byte, BYTE_BUF_SIZE) == 0) {
        printk("byte : ");
        for (i=0; i<BYTE_BUF_SIZE; i++)
            printk("0x%x ", p_tree->byte[i]);
        printk("\n");
    } else {
        printk("byte not found\n");
    }

    /* 获取feng_state属性 */
    if ((p_tree->state = of_find_property(p_tree->base.np, "feng_state", NULL)) == NULL)
        printk("compatible not found\n"); 
    else
        printk("feng_state : %s\n", (char *)p_tree->state->value);

    /* 获取子节点1的节点信息 */
    printk("----------------child1 node----------------\n");
    if ((p_tree->child1.np = of_get_next_child(p_tree->base.np, NULL)) == NULL)
        return 0;
    _get_dev_tree_base_info(&p_tree->child1, CHILD1_REG_SIZE);    /* 获取子节点1基本属性 */

    /* 获取子节点2的节点信息 */
    printk("----------------child2 node----------------\n");
    if ((p_tree->child2.np = of_get_next_child(p_tree->base.np, p_tree->child1.np)) == NULL)
        return 0;
    _get_dev_tree_base_info(&p_tree->child2, CHILD2_REG_SIZE);    /* 获取子节点2基本属性 */

    return 0;
}

/**
 * @模块入口函数
 */
static int __init dev_tree_init(void)
{
     _get_dev_tree_info(&feng_dev_tree);
    return 0;
}

/**
 * @模块出口函数
 */
static void __exit dev_tree_exit(void)
{

}

module_init(dev_tree_init);
module_exit(dev_tree_exit);
MODULE_LICENSE("GPL");

/* 调用modinfo xx(模块名)查看 */
MODULE_AUTHOR("feng");          /* 模块的作者 */
MODULE_VERSION ("1.00");        /* 模块版本号 */
/* MODULE_DESCRIPTION("xxxxx");    模块描述 */
/* MODULE_ALIAS("xxx");            模块别名 */

 atomic.dts

fengdts:fdts@77777777 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible ="feng, dtstest";
    reg = <
        0x77777777 0x7
        0x37373737 0x8
    >;
    feng_state = "okey";
    string_buf = "seven","feng";
    byte_buf = [12 34 45 67];
    good;   
    child_fnode1:fnode1@12345678 {
        compatible ="feng, dtstest_child1";
        reg = <
            0x12345678 
            0x12345679
        >;
        mix_buf = [12 45], "good", <0x23456789 0x87654321>;
    }; 
    child_fnode2: fnode2@87654321 {
        compatible ="feng, dtstest_child2";
        reg = <
            0x87654321 
            0x23456789
        >;
    };
};

 cp_kennel

#!/bin/bash

#内核名字
IMAGE_NAME=zImage
#DTB_NAME=imx6ull-14x14-evk.dtb
#DTB_NAME=imx6ull-alientek-emmc.dtb
DTB_NAME=atomic.dtb

#目录
KERNEL_DIR=arch/arm/boot/
DTB_DIR=arch/arm/boot/dts/
TFTP_DIR=/opt/tftpboot

cp ${KERNEL_DIR}${IMAGE_NAME} ${TFTP_DIR}
cp ${DTB_DIR}${DTB_NAME} ${TFTP_DIR}

 Makefile

#KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek 
CURRENT_PATH := $(shell pwd) 

obj-m := dtsled.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

    #根文件所在目录
ROOTFS_DIR = /home/feng/atomic/rootfs

#交叉编译工具链
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc

#目标文件名
TAR_NAME = dev_tree

#应用程序名字
APP_NAME = my$(TAR_NAME)

#驱动目录路径
DRV_DIR = $(ROOTFS_DIR)/home/drv
DRV_DIR_LIB = $(ROOTFS_DIR)/lib/modules/4.1.15

#动态库目录路径
LIB_DIR = $(ROOTFS_DIR)/home/lib

#应用程序目录路径
APP_DIR = $(ROOTFS_DIR)/home/app

#KERNELRELEASE由内核makefile赋值
ifeq ($(KERNELRELEASE), )

#内核路径
KERNEL_DIR =/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga

#当前文件路径
CURR_DIR = $(shell pwd)

all:
    #编译模块
    make -C $(KERNEL_DIR) M=$(CURR_DIR) modules

    #编译应用程序
    #$(CC) -o $(APP_NAME) $(APP_NAME).c

clean:
    #清除模块文件
    make -C $(KERNEL_DIR) M=$(CURR_DIR) clean

    #清除应用文件
    #rm $(APP_NAME)

install:
    #拷贝模块文件
    #cp -raf $(TAR_KEY_NAME)_drv.ko $(TAR_KEY_NAME)_dev.ko $(DRV_DIR)
    #cp -raf keyin.ko wq.ko timer.ko $(DRV_DIR_LIB)
    cp -raf *.ko $(DRV_DIR_LIB)

    #拷贝应用文件
    #cp -raf $(APP_NAME) $(APP_DIR)
else
#指定编译什么文件
#obj-m += $(TAR_NAME)_drv.o $(TAR_KEY_NAME)_dev.o keyin.o wq.o timer.o
obj-m += $(TAR_NAME).o 

endif

结论

1、进入内核目录,执行make dtbs命令编译设备树;然后执行./cp_kernel.sh命令,拷贝设备树文件到tftp目录。

feng:linux-imx-rel_imx_4.1.15_2.1.0_ga$ make dtbs
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: “include/generated/mach-types.h”已是最新。
  CHK     include/generated/bounds.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  DTC     arch/arm/boot/dts/atomic.dtb
feng:linux-imx-rel_imx_4.1.15_2.1.0_ga$ ./cp_kernel.sh 
feng:linux-imx-rel_imx_4.1.15_2.1.0_ga$ 

2、重启目标机,进入设备树目录(/proc/device-tree),查看相关设备节点是否挂载成功。

/* 重启目标机,进入设备树目录,查看相关设备节点是否挂载成功 */
/ # cd /proc/device-tree
/sys/firmware/devicetree/base # ls
#address-cells            feng_alpha_gpioled
#size-cells               feng_alpha_pf_gpioled
aliases                   interrupt-controller@00a01000
alphaled                  memory
backlight                 model
chosen                    name
clocks                    pxp_v4l2
compatible                regulators
cpus                      reserved-memory
fdts@77777777             soc
feng_alpha_gpiobeep       sound
feng_alpha_gpiokey        spi4
/sys/firmware/devicetree/base # 

3、进入设备节点目录,查看设备节点信息。

/sys/firmware/devicetree/base # cd fdts@77777777/
/sys/firmware/devicetree/base/fdts@77777777 # ls
#address-cells   compatible       fnode2@87654321  reg
#size-cells      feng_state       good             string_buf
byte_buf         fnode1@12345678  name
/sys/firmware/devicetree/base/fdts@77777777 # cat compatible 
feng, dtstest/sys/firmware/devicetree/base/fdts@77777777 # cat feng_state
okey/sys/firmware/devicetree/base/fdts@77777777 # 

4、进入子节点1目录,查看子节点1信息。

/sys/firmware/devicetree/base/fdts@77777777 # cd fnode1@12345678/
/sys/firmware/devicetree/base/fdts@77777777/fnode1@12345678 # ls
compatible  mix_buf     name        reg
/sys/firmware/devicetree/base/fdts@77777777/fnode1@12345678 # cat compatible 
feng, dtstest_child1/sys/firmware/devicetree/base/fdts@77777777/fnode1@12345678 # cat name
fnode1/sys/firmware/devicetree/base/fdts@77777777/fnode1@12345678 # 

5、进入子节点2目录,查看子节点2信息。

/sys/firmware/devicetree/base/fdts@77777777/fnode1@12345678 # cd ../fnode2@87654321/
/sys/firmware/devicetree/base/fdts@77777777/fnode2@87654321 # ls
compatible  name        reg
/sys/firmware/devicetree/base/fdts@77777777/fnode2@87654321 # cat name
fnode2/sys/firmware/devicetree/base/fdts@77777777/fnode2@87654321 # cat compatible
feng, dtstest_child2/sys/firmware/devicetree/base/fdts@77777777/fnode2@87654321 # 

6、进入模块目录,执行make命令编译模块;然后执行make install命令,拷贝模块到目标机指定目录。

feng:dev_tree$ make
#编译模块
make -C /home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/mnt/hgfs/Share/linux/atomic/driver/dev_tree modules
make[1]: 进入目录“/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  CC [M]  /mnt/hgfs/Share/linux/atomic/driver/dev_tree/dev_tree.o
  Building modules, stage 2.
  MODPOST 1 modules
  LD [M]  /mnt/hgfs/Share/linux/atomic/driver/dev_tree/dev_tree.ko
make[1]: 离开目录“/home/feng/atomic/resource/linux-imx-rel_imx_4.1.15_2.1.0_ga”
#编译应用程序
#arm-linux-gnueabihf-gcc -o mydev_tree mydev_tree.c
feng:dev_tree$ make install 
#拷贝模块文件
#cp -raf _drv.ko _dev.ko /home/feng/atomic/rootfs/home/drv
#cp -raf keyin.ko wq.ko timer.ko /home/feng/atomic/rootfs/lib/modules/4.1.15
cp -raf *.ko /home/feng/atomic/rootfs/lib/modules/4.1.15
#拷贝应用文件
#cp -raf mydev_tree /home/feng/atomic/rootfs/home/app
feng:dev_tree$ 

7、在目标机上执行modprobe命令加载模块。

注意:在模块加载之前,需要先调用depmod命令,生成模块依赖文件。

/ # depmod
/ # modprobe dev_tree.ko
----------------parent node----------------
name : fdts
full name : /fdts@77777777
compatible : feng, dtstest
reg_n_addr : 1
reg_n_size : 1
reg : 0x77777777 0x7 0x37373737 0x8 
good : 1
string : seven feng 
string_buf[1] : feng
byte_buf num : 4
byte : 0x12 0x34 0x45 0x67 
feng_state : okey
----------------child1 node----------------
name : fnode1
full name : /fdts@77777777/fnode1@12345678
compatible : feng, dtstest_child1
reg_n_addr : 1
reg_n_size : 0
reg : 0x12345678 0x12345679 
----------------child2 node----------------
name : fnode2
full name : /fdts@77777777/fnode2@87654321
compatible : feng, dtstest_child2
reg_n_addr : 1
reg_n_size : 0
reg : 0x87654321 0x23456789 
/ #  

8、在目标机上执行modprobe -r命令卸载模块。

/* 卸载测试模块 */
/ # modprobe -r dev_tree.ko
/ # lsmod
Module                  Size  Used by    Tainted: G  
/ # 

9、综上、示例展示了设备树的框架以及如何在驱动程序中获取设备信息。

往期 · 推荐

浅谈linux - 字符设备框架

帮你自动化办公的python-自动提取pdf指定页(项目概述)

也没想象中那么神秘的数据结构-一种通用化的双向链表设计(底层源码)

也没想象中那么神秘的数据结构-一环扣一环的“链表”(双向链表)

我用C语言玩对象,偷偷关注着你的观察者模式(基类设计)

关注

更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:linux驱动源码。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不只会拍照的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值