概述

设备树(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 0x70x37373737 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 0x70x37373737 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 0x70x37373737 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");elseprintk("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;elsep_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); elseprintk("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); elseprintk("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"); elseprintk("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 0x70x37373737 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/tftpbootcp ${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.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(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).cclean:#清除模块文件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 dtbsCHK     include/config/kernel.releaseCHK     include/generated/uapi/linux/version.hCHK     include/generated/utsrelease.h
make[1]: “include/generated/mach-types.h”已是最新。CHK     include/generated/bounds.hCHK     include/generated/asm-offsets.hCALL    scripts/checksyscalls.shDTC     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.oBuilding modules, stage 2.MODPOST 1 modulesLD [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驱动源码。

浅谈linux - 描述硬件的文件设备树相关推荐

  1. linux cp mv区别,浅谈Linux下mv和cp命令的区别

    1.功能上的区别 mv:用户可以使用该命令为文件或目录重命名或将文件由一个目录移入另一个目录中. cp: 该命令的功能是将给出的文件或目录拷贝到另一文件或目录中. 2.从inode角度来区分 mv:会 ...

  2. 浅谈Linux PCI设备驱动(二)

    我们在浅谈Linux PCI设备驱动(一)中(以下简称浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设备驱 ...

  3. Linux命令删除find,浅谈Linux下通过find命令进行rm文件删除的小技巧

    我们经常会通过find命令进行批量操作,如:批量删除旧文件.批量修改.基于时间的文件统计.基于文件大小的文件统计等,在这些操作当中,由于rm删除操作会导致目录结构变化,如果要通过find结合rm的操作 ...

  4. linux gz文件压缩比,浅谈Linux下各种压缩 解压命令和压缩比率对比.doc

    浅谈Linux下各种压缩 解压命令和压缩比率对比 浅谈Linux下各种压缩 解压命令和压缩比率对比 Linux下压缩.解压命令五花八门,不像在windows下一个winrar打遍天下无敌手,清一色的. ...

  5. 浅谈linux - 内核时间的处理

    概述 对于嵌入式开发,经常会遇到一些定时.延时以及周期调度的情况,所以定时器是必不可少的一种资源. 相对于裸机开发,我们使用定时器只需先选择时钟源,然后设置分频系数和计数值,配置好中断后,就可以静静的 ...

  6. 浅谈Linux标准的文件系统(Ext2/Ext3/Ext4)

    Ext 全称Linux extended file system, extfs,即Linux扩展文件系统,Ext2就代表第二代文件扩展系统,Ext3/Ext4以此类推,它们都是Ext2的升级版,只不过 ...

  7. 浅谈 Linux 系统中的 SNMP Trap 【转】

    文章来源:浅谈 Linux 系统中的 SNMP Trap 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前,首先认识一下 SNMP 吧. 简单网络管理协议(Simple Network ...

  8. 浅谈Linux下的媒体播放器(转)

    浅谈Linux下的媒体播放器(转)[@more@]Linux开放实验室(Linux OpenLab)郝煜.季冰Linux开放实验室校园爱好者小组 陈强(农大).吴迪.雷凌.戴二红.刘志强(北科大)Li ...

  9. linux 易语言窗口程序_浅谈Linux入门的基本知识

    浅谈Linux入门的基本知识 图形模式与文字模式的切换方式Linux预设提供了六个命令窗口终端机让我们来登录. 默认我们登录的就是第一个窗口,也就是tty1,这个六个窗口分别为tty1.tty2 - ...

最新文章

  1. 美国要求台积电、三星45天内主动交出商业数据,不配合将采取必要行动
  2. PERL 语言中的q,qw,qr,qx,qq......符号用法总结
  3. extract进程 oracle,ogg extract进程stoped问题
  4. 【学术相关】博士新生应该懂得哪些道理?
  5. angular组件-特殊的瀑布流(原创)
  6. html选择器 并列,CSS 中的选择器 (二)- 组合选择器
  7. SCJP 认证考试指南
  8. StringBuilder和StringBuffer的效率比较
  9. 设计模式七大原则——里氏替换原则
  10. 【一文读懂】python 中的 numpy.reshape(a, newshape, order=‘C‘) 详细说明及实例讲解
  11. U盘名从E:/变成F:/,怎么修改回来
  12. Python实现ABC人工蜂群优化算法优化支持向量机回归模型(SVR算法)项目实战
  13. 关于在GET请求中使用body
  14. WinCC RT Professional网络通讯配置步骤
  15. Camera ISO、快门、光圈、曝光这几个概念
  16. 【深入理解TcaplusDB技术】入门Tcaplus SQL Driver
  17. 深圳软件测试几月份好找工作,上海与深圳的软件测试发展,未来哪个更有发展前景?...
  18. 使用exe4j打包exe文件
  19. 王勇杰《音乐漫步》1-10单元测试答案
  20. 超简单EventBus使用

热门文章

  1. 使用组策略实现统一企业形象
  2. three.js光带冲击波效果
  3. Ubuntu文件夹去锁
  4. SDM670 AMSS代码编译
  5. 世链财经|教你两种方法创建冷钱包的方法
  6. Python call()方法
  7. 自己做量化交易软件(35)小白量化实战8--事件型回测程序
  8. 3051变送器安装注意事项
  9. RK音量调节及音量曲线
  10. 回归Qt——写在Qt5.10发布之日