概述
设备树(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、综上、示例展示了设备树的框架以及如何在驱动程序中获取设备信息。
往期 · 推荐
帮你自动化办公的python-自动提取pdf指定页(项目概述)
也没想象中那么神秘的数据结构-一种通用化的双向链表设计(底层源码)
关注
更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:linux驱动源码。