文章目录

  • DTB存储格式
  • 编译和查看工具
  • Device Tree中的节点信息举例
  • Device Tree文件结构
  • DTB数据结构
    • struct ftd_header区域数据结构
    • memory reservation block区域数据结构
    • struct block区域
    • strings block
  • 内核对设备树中平台信息的处理
    • machine_desc
    • 内核源码处理分析
      • setup_arch
      • setup_machine_fdt
      • of_flat_dt_match_machine
    • 内核对设备树运行时配置信息处理
      • 解析/chosen节点
      • 解析根节点的{size,address}-cells属性
      • 解析/memory节点
  • kernel解析Device Tree
    • 从DTB到struct device_node示例
    • 将device_node转换成platform_device
    • 设备树在文件系统中的表示

DTB存储格式


  • 头部(struct ftd_header):用来表明各个部分的偏移地址,整个文件的大小,版本号等等;
  • 内存的保留信息块(memory reservation block):存放dts文件中申明的需要预留的内存的信息;
  • 节点块(structure block):各个节点的信息将放在structure block中;
  • 字符串块(strings block):存放字符串信息;

编译和查看工具

dtc工具的使用方法是:dtc –I dts –O dtb –o xxx.dtb xxx.dts,即可生成dts文件对应的dtb文件
Linux终端执行ftddump –h

Device Tree中的节点信息举例

  • dt_struct存储节点数值及名称相关信息,如:”A string“及node1
  • dt_string存储属性名,如:“a-string-property”
  • 可以给一个设备节点添加lable,之后可以通过&lable的形式访问这个lable,这种引用是通过phandle(pointer handle)进行的。例如,图中的node1就是一个lable,node@0的子节点child-node@0通过&node1引用node@1节点,在经过DTC工具编译之后,&node1会变成一个特殊的整型数字n,假设n值为1,那么在node@1节点下自动生成两个属性,属性如下:
    linux,phandle = <0x00000001>;
    phandle = <0x00000001>;
    node@0的子节点child-node@0中的a-reference-to-something = <&node1>会变成a-reference-to-something = < 0x00000001>。此处0x00000001就是一个phandle值(独一无二的整型值),在后续kernel中通过这个特殊的数字间接找到引用的节点

Device Tree文件结构

  • dtb的头部首先存放的是fdt_header的结构体信息,接着是填充区域,填充大小为off_dt_struct – sizeof(struct fdt_header),填充的值为0。接着就是struct fdt_property结构体的相关信息。最后是dt_string部分

DTB数据结构

dtb文件为大端存储模式

struct ftd_header区域数据结构

struct fdt_header {uint32_t magic; //dtb文件的固定开始数字 0xd00dfeed
uint32_t totalsize; //dtb文件的大小
uint32_t off_dt_struct; //structure block区域的地址偏移值,从文件开头计算
uint32_t off_dt_strings; //strings block区域的地址偏移值,从文件开头计算
uint32_t off_mem_rsvmap; //memory reservation block区域的地址偏移值,从文件开头计算
uint32_t version; //设备树数据结构的版本
uint32_t last_comp_version; //所用版本向后兼容的最低版本的设备树数据结构
uint32_t boot_cpuid_phys; //系统引导CPU的物理ID,它的值应该与设备树文件中CPU节点下的reg属性值相等
uint32_t size_dt_strings;  //structure block区域的字节数
uint32_t size_dt_struct; //strings block区域的字节数
};

memory reservation block区域数据结构

  • 该区域信息描述了一块受保护的内存区域,该内存区域不用作一般内存使用
  • memory reservation block区域的结尾固定是一个address和size全0的结构体
struct fdt_reserve_entry {uint64_t address; //开始地址
uint64_t size; //大小
};
  • 假设要将64M内存的最高1M留下自己使用,则在dts文件中添加下面这句:/memreserve/ 0x33f00000 0x100000
/memreserve/ 0x33f00000 0x100000;
/ {name = "sample"model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory {  /* /memory */device_type = "memory";reg =  <0x30000000 0x4000000 0 4096>;       };
/*cpus {cpu {compatible = "arm,arm926ej-s";};};
*/  chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};
};

struct block区域

Struct Block是由一系列连续的片组成的,每个片的以一个32位token开始,token按照大字节序存储,某些token后面还会接着额外的数据(extra data)。所有的token都是32bit对齐的,不满32bit的会在前面补0。

struct fdt_node_header {fdt32_t tag;//对应的token,如FDT_BEGIN_NODE、char name[0];
};
//描述属性采用struct fdt_property描述,tag标识是属性,取值为FDT_PROP;len为属性值的长度(包括‘\0’,单位:字节);nameoff为属性名称存储位置相对于off_dt_strings的偏移地址
struct fdt_property {fdt32_t tag;fdt32_t len;fdt32_t nameoff;char data[0];
};

五种类型的token:

  • 0x00000001 //FDT_BEGIN_NODE,表示一个note节点开始,说明后面的内容都是节点信息。FDT_BEGIN_NODE后接节点名字,节点名字是以"\0"结尾的字符串,如果名字中包含地址,则该字符串中也应该包含地址(32位对齐,不满足补0)。
  • 0x00000002 //FDT_END_NODE,表示一个note节点结束。FDT_END_NODE 没有额外的信息,所以它后面马上接着下一个除FDT_PROP之外的token。
  • 0x00000003 //FDT_PROP,表示一个属性开始。后接描述该属性信息的extra data,它的extra data是按以下C结构体存储的,该结构体后接属性名字,以"\0"结尾的字符串。
  • 0x00000004 //FDT_NOP,特殊数据,解析设备树时将被忽略,这个token没有extra data,所以后面紧接下一个token。如果要从dtb去除莫个note或者属性,可以用他覆盖该note或者属性,这样就不用移动dtb文件中其他数据了
  • 0x00000009 //FDT_END,表示struct block区域结束。一个dtb文件中应该只包含一个FDT_END token,并且FDT_END token应该是最后一个token。没有extra data,后面直接接string block的内容。

    案例:
led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;
};

FDT_BEGIN_NODE+节点名称开始,然后是FDT_PROP描述节点的属性,然后是struct(len+nameoff)+val,该节点的属性描述完成后,使用FDT_END_NODE表示节点结束;最后,当所有节点描述完毕后,使用FDT_END结束struct block。

strings block

存放struct block中用到的属性名,可能这些属性名重复率很高,这样节省空间。该区域的字符串简单地拼接在一起,没有字节对其。

内核对设备树中平台信息的处理

machine_desc

  • 一个kernel镜像通常会支持很多板子,针对每种板子,kernel都会为其定义一个struct machine_desc的结构,其中就记录各个板子的硬件信息,比如板子的ID号、名字、支持的中断数量、初始化函数等
  • 在kernel启动时,可以根据u-boot传递的参数/DTB文件选则合适的machine_desc,从而正确的初始化当前硬件
  • kernel会将一系列machine_desc集中存放在.init.arch.info节中,形成如同数组一样的内存分布,并以符号__arch_info_begin和__arch_info_end记录该节的起始和结尾,如此一来,就可以向访问数组元素那样访问每个machine_desc
.init.arch.info : {__arch_info_begin = .;*(.arch.info.init)__arch_info_end = .;}
  • 选则machine_desc时,kernel首先会获取DTB的根节点的compatible属性,将其中的一个或多个字符串与machine_desc的dt_compat成员记录的一个或多个字符串进行比较,当匹配时,返回相应的machine_desc,compatible属性值中,位置靠前的字符串会优先比较

内核源码处理分析

setup_arch

void __init setup_arch(char **cmdline_p)
{const struct machine_desc *mdesc;/* 初始化一些处理器相关的全局变量 */setup_processor();/* 优先按照设备树获取machine_desc */mdesc = setup_machine_fdt(__atags_pointer);/* 如果u-boot传递的不是DTB,则按照ATAGS获取machine_desc */if (!mdesc)mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);....../* 记录获取到的machine_desc及其名字 */machine_desc = mdesc;machine_name = mdesc->name;dump_stack_set_arch_desc("%s", mdesc->name);....../* 将boot_command_line的内容拷贝到cmd_line */strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);/* 输出指向启动参数的指针 */*cmdline_p = cmd_line;....../* 根据DTB创建device_node树 */unflatten_device_tree();......
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER/* 设置handle_arch_irq */handle_arch_irq = mdesc->handle_irq;
#endif....../* 调用machine_desc中注册的初始化函数 */if (mdesc->init_early)mdesc->init_early();
}

setup_machine_fdt

setup_arch函数调用setup_machine_fdt解析设备树中的相关信息,并返回合适的machine_desc

const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{const struct machine_desc *mdesc, *mdesc_best = NULL;/* 验证DTB文件是否存在:地址不为NULL && 文件头部magic正确 *//* initial_boot_params = dt_phys */if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))return NULL;/* 获取compatible属性并匹配合适的machine_desc */mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);if (!mdesc) {/* 打印一些信息 */....../* 把当前kernel支持的单板的名字和单板ID打印出来 *//* 该函数不会返回(内部有死循环) */dump_machine_table();}/* 当DTB文件提供的数据有问题,这里会做一些修补工作 */if (mdesc->dt_fixup)mdesc->dt_fixup();/* 获取运行时配置信息,再第3节中细说 */early_init_dt_scan_nodes();/* 记录machine ID */__machine_arch_type = mdesc->nr;return mdesc;
}

of_flat_dt_match_machine

of_flat_dt_match_machine函数是匹配合适的machine_desc的关键

/*传入的第一个参数为NULL传入的第二个参数为arch_get_next_macharch_get_next_mach的原理非常简单:初始化一个静态局部变量为__arch_info_begin,每次被调用时该变量(指针)+1并返回,如果超出了__arch_info_end,则返回NULL
*/
const void * __init of_flat_dt_match_machine(const void *default_match,const void * (*get_next_compat)(const char * const**))
{const void *data = NULL;const void *best_data = default_match;const char *const *compat;unsigned long dt_root;unsigned int best_score = ~1, score = 0;/* dt_root = 0 */dt_root = of_get_flat_dt_root();/* 遍历所有machine_desc,将machine_desc的dt_compat保存到compatcompat指向一系列字符串(一个machine_desc也可能支持多个单板)*/while ((data = get_next_compat(&compat))) {/*DTB根节点的compatible属性值是一系列字符串,假设为"aaa", "bbb", "ccc"machine_desc的dt_compat(指针的指针)也指向一系列字符串,假设为"xxx", "ccc"第一轮比较(score = 0):1、score++, compatible的"aaa"<==>dt_compat的"xxx"2、score++, compatible的"bbb"<==>dt_compat的"xxx"3、score++, compatible的"ccc"<==>dt_compat的"xxx"第二轮比较(score = 0):1、score++, compatible的"aaa"<==>dt_compat的"ccc"2、score++, compatible的"bbb"<==>dt_compat的"ccc"3、score++, compatible的"ccc"<==>dt_compat的"ccc",此时匹配上,返回score(值为3)*/score = of_flat_dt_match(dt_root, compat);/* 记录得分最低(最匹配)的machine_desc */if (score > 0 && score < best_score) {best_data = data;best_score = score;}}/* 没有匹配到合适的machine_desc就返回NULL */if (!best_data) {/* 打印根节点的compatible属性值 */......return NULL;}/* 打印根节点的model属性值,若不存在则打印compatible属性值 */pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());return best_data;
}

内核对设备树运行时配置信息处理

kernel使用setup_arch ==> setup_machine_fdt ==> early_init_dt_scan_nodes来处理DTB中的运行时配置信息

void __init early_init_dt_scan_nodes(void)
{int rc = 0;/* 获取/chosen节点的信息 */rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);if (!rc)pr_warn("No chosen node found, continuing without\n");/* 获取根节点的{size,address}-cells属性值,之后才方便解析根节点的子节点的reg属性 */of_scan_flat_dt(early_init_dt_scan_root, NULL);/* 解析/memory节点,设置内存信息 */of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
/*** 遍历DTB的节点,直到参数传入的回调函数it返回非0值*/
int __init of_scan_flat_dt(int (*it)(unsigned long node,const char *uname, int depth,void *data),void *data)
{/* blob指向DTB在内存中的起始地址 */const void *blob = initial_boot_params;const char *pathp;int offset, rc = 0, depth = -1;/* 若设备树不存在则返回 */if (!blob)return 0;/* 从根节点开始遍历 */for (offset = fdt_next_node(blob, -1, &depth);/* 如果找到了有效的节点并且回调函数it返回0,则执行循环体 */offset >= 0 && depth >= 0 && !rc;/* 继续遍历下一个节点 */offset = fdt_next_node(blob, offset, &depth)) {/* 获取节点名 */pathp = fdt_get_name(blob, offset, NULL);/* 对于老版本的设备树,得到的是节点的路径名,因此要去掉多余的前缀 *//* 不过fdt_get_name已经考虑过这个问题了,这里有点多余 */if (*pathp == '/')pathp = kbasename(pathp);/*调用回调函数itoffset: 节点起始位置在DTB的structure block中的偏移pathp : 指向节点名depth : 节点的深度(层次)data  : 参数data,取决于调用者*/rc = it(offset, pathp, depth, data);}return rc;
}

解析/chosen节点

传入的回调函数early_init_dt_scan_chosen用于解析/chosen节点

/*offset: 节点起始位置在DTB的structure block中的偏移pathp : 指向节点名depth : 节点的深度(层次)data  : boot_command_line,一个字符数组
*/
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,int depth, void *data)
{int l;const char *p;const void *rng_seed;pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);/* 如果遍历到的不是作为根节点的子节点的chosen节点,则指示of_scan_flat_dt继续遍历下一个节点 */if (depth != 1 || !data ||(strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))return 0;/* 当前节点是/chosen节点 *//* 解析/chosen节点的initrd属性,设置全局变量phys_initrd_start和phys_initrd_size */early_init_dt_check_for_initrd(node);/* 获取/chosen节点的bootargs属性的属性值 */p = of_get_flat_dt_prop(node, "bootargs", &l);/* 如果属性存在,则p指向bootargs属性值——一个字符串,l记录了字符串的长度(含'\0') */if (p != NULL && l > 0)/* 将启动参数拷贝到boot_command_line */strlcpy(data, p, min(l, COMMAND_LINE_SIZE));/** CONFIG_CMDLINE配置项意味着如果u-boot传递的参数不含启动参数,那么* CONFIG_CMDLINE就是默认的启动参数。如果含有启动参数,那么,是追加* 还是覆盖已有的启动参数,取决于另外两个配置项CONFIG_CMDLINE_EXTEND* 和CONFIG_CMDLINE_FORCE。*/
#ifdef CONFIG_CMDLINE
#if defined(CONFIG_CMDLINE_EXTEND)strlcat(data, " ", COMMAND_LINE_SIZE);strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#else/* 如果DTB不带有启动参数,就使用kernel的启动参数——CONFIG_CMDLINE */if (!((char *)data)[0])strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif
#endif /* CONFIG_CMDLINE */pr_debug("Command line is: %s\n", (char*)data);/* 对rng-seed节点的解析,暂时不清楚这个东西 */rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);if (rng_seed && l > 0) {......}/* 返回非0值,指示of_scan_flat_dt停止遍历 */return 1;
}

解析根节点的{size,address}-cells属性

在解析/memory节点之前,应该先得到根节点的{size,address}-cells属性值,因为/memory节点使用reg属性来存放内存的起始地址和长度,而解析reg属性少不了{size,address}-cells

int __init early_init_dt_scan_root(unsigned long node, const char *uname,int depth, void *data)
{const __be32 *prop;/* 验证当前节点是否是根节点 */if (depth != 0)return 0;/* 设置默认值 */dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;/* 如果有#size-cells属性则获取其值,并重新设置dt_root_size_cells */prop = of_get_flat_dt_prop(node, "#size-cells", NULL);if (prop)/* 注意大小端的转换 */dt_root_size_cells = be32_to_cpup(prop);pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);/* 如果存在#address-cells属性,则重新设置dt_root_addr_cells */prop = of_get_flat_dt_prop(node, "#address-cells", NULL);if (prop)dt_root_addr_cells = be32_to_cpup(prop);pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);/* 停止遍历 */return 1;
}

解析/memory节点

int __init early_init_dt_scan_memory(unsigned long node, const char *uname,int depth, void *data)
{/* 获取/memory节点的device_type属性 */const char *type = of_get_flat_dt_prop(node, "device_type", NULL);const __be32 *reg, *endp;int l;bool hotpluggable;/* /memory节点的device_type属性值必须是memory */if (type == NULL || strcmp(type, "memory") != 0)return 0;/* 获取/memory节点的linux,usable-memory或reg属性值(存放了内存的起始地址和长度信息) */reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);if (reg == NULL)reg = of_get_flat_dt_prop(node, "reg", &l);if (reg == NULL)return 0;endp = reg + (l / sizeof(__be32));/* 获取hotpluggable属性值(指示是否可以热插拔) */hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);pr_debug("memory scan node %s, reg size %d,\n", uname, l);/* 遍历reg属性记录的一块或多块内存 */while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {u64 base, size;/* 获取当前内存块的起始地址 */base = dt_mem_next_cell(dt_root_addr_cells, &reg);size = dt_mem_next_cell(dt_root_size_cells, &reg);if (size == 0)continue;pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,(unsigned long long)size);/* 对base和size进行一系列校验后,调用memblock_add添加内存块(struct memblock) */early_init_dt_add_memory_arch(base, size);if (!hotpluggable)continue;/* 若当前内存块可以热插拔,那么标记之 */if (early_init_dt_mark_hotplug_memory_arch(base, size))pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",base, base + size);}return 0;
}

kernel解析Device Tree

  • Device Tree文件结构描述就以上struct fdt_header、struct fdt_node_header及struct fdt_property三个结构体描述
  • kernel会根据Device Tree的结构解析出kernel能够使用的struct property结构体,并根据Device Tree中的属性填充结构体
  • struct property结构体描述如下:
struct property {char *name; /* 指向属性名字符串,位于DTB的strings block*/int length; /* 属性的长度 */void *value; /* void *类型,指向属性值,位于DTB的structure block */struct property *next; /* 一个节点的所有属性构成一个链表 */unsigned long _flags;unsigned int unique_id;struct bin_attribute attr; /* 属性文件,与sysfs文件系统挂接 */
};
  1. kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表
  2. 在early_init_dt_scan_nodes()中会做以下三件事
  • 扫描/chosen或者/chose@0节点下面的bootargs属性值到boot_command_line,此外,还处理initrd相关的property,并保存在initrd_start和initrd_end这两个全局变量中
  • 扫描根节点下面,获取{size,address}-cells信息,并保存在dt_root_size_cells和dt_root_addr_cells全局变量中
  • 扫描具有device_type =“memory”属性的/memory或者/memory@0节点下面的reg属性值,并把相关信息保存在meminfo中,全局变量meminfo保存了系统内存相关的信息
  1. Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,struct device_node最终一般会被挂接到具体的struct device结构体。struct device_node结构体描述如下:
struct device_node {const char *name;              /* node的名称,取最后一次“/”和“@”之间子串,位于DTB的structure block*/const char *type;              /* device_type的属性名称,没有为<NULL> */phandle phandle;               /* phandle属性值 */const char *full_name;         /* 指向该结构体结束的位置,存放node的路径全名,例如:/chosen */struct fwnode_handle fwnode;struct  property *properties;  /* 指向该节点下的第一个属性,其他属性与该属性链表相接 */struct  property *deadprops;   /* removed properties */struct  device_node *parent;   /* 父节点 */struct  device_node *child;    /* 子节点 */struct  device_node *sibling;  /* 姊妹节点,与自己同等级的node */struct  kobject kobj;          /* sysfs文件系统目录体现 */unsigned long _flags;          /* 当前node状态标志位,见/include/linux/of.h line124-127 */void    *data;
};/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC        1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED       2 /* node has been detached from the device tree*/
#define OF_POPULATED      3 /* device already created for the node */
#define OF_POPULATED_BUS  4 /* of_platform_populate recursed to children of this node */

从DTB到struct device_node示例

  • kernel调用unflatten_device_tree函数,将DTB文件中的设备节点转换为一个个的struct device_node
    设备树示例:
/ {model = "SMDK2416";compatible = "samsung,s3c2416";#address-cells = <1>;#size-cells = <1>;memory@30000000 {device_type = "memory";reg =  <0x30000000 0x4000000>;};pinctrl@56000000 {name = "example_name";compatible = "samsung,s3c2416-pinctrl";};
};
  • 设备树文件被DTC编译为DTB之后,被u-boot传递给kernel,然后内核读取其节点信息,建立如下的由device_node构成的树状结构:

    为了突出device_node的name成员和full_name成员的差别,在示例设备树中没有对节点memory@30000000设置name属性,则对应的device_node的name成员置为,源于函数populate_node的处理。

将device_node转换成platform_device

  • 能转换platform_device的先决条件:含有compatible属性的根节点的子节点,或者compatibe属性值为"simple-bus"、“simple-mfd”、“isa”、"arm,amba-bus"的节点的含有compatible属性的子节点
  • drivers/of/platform.c的of_platform_default_populate_init函数负责为合适的设备节点构建platform_device
  • kernel为该节点创建platform_device后,将其注册到platform_bus_type,根据kernel的总线-设备-驱动模型,如果匹配总线上的某个platform_driver,那么该驱动的probe函数会被调用

设备树在文件系统中的表示

  1. 所有设备树的信息存放于/sys/firmware目录下:
  • /sys/firmware/fdt 该文件表示原始DTB文件,可用hexdump -C /sys/firmware/fdt查看
  • /sys/firmware/devicetree 以目录结构呈现设备树,每个device_node对应一个目录,每个属性对应节点目录下的一个文件,比如根节点对应base目录,该目录下有compatible等文件
  • 所有的platform_device会在/sys/devices/platform下对应一个目录,这些platform_device有来自设备树的,也有来自.c文件中手工注册的。由kernel根据设备树创建的platform_device对应的目录下存在一个名为of_node的软链接,链接向该platform_device对应的device_node对应的目录。
  1. /proc与设备树:/proc/device-tree作为链接文件指向/sys/firmware/devicetree/base。

Linux设备树DTB存储格式相关推荐

  1. Linux设备树 .dtb文件,查看“第二课:设备树的规范(dts和dtb)”的源代码

    因为以下原因,您没有权限编辑本页: 您所请求的操作仅限于该用户组的用户使用:用户 您可以查看与复制此页面的源代码.= 第01节_DTS格式= dts文件通过编译生成dtb格式文件 [[File:ldd ...

  2. Linux设备树 .dtb文件,内核使用dtb文件的过程

    一是在驱动程序中,直接写死硬件资源,如:GPIO.寄存器地址.中断号等,使得硬件改动时,必须修改驱动程序. 二是采用总线驱动platform模型,将硬件资源与驱动软件分离,在platform_devi ...

  3. linux设备树笔记__dts基本概念及语法

    设备树手册(Device Tree Usage)原文地址:http://www.devicetree.org/Device_Tree_Usage 有关device tree数据格式的更完整技术说明,读 ...

  4. 一文搞定 Linux 设备树

    设备树是一种描述硬件的数据结构,它起源于OpenFirmware(OF). 在Linux 2.6中, ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/ma ...

  5. 我眼中的Linux设备树(五 根节点)

    五 根节点 一个最简单的设备树必须包含根节点,cpus节点,memory节点.根节点的名字及全路径都是"/",至少需要包含model和compatible两个属性.model属性我 ...

  6. 奇小葩讲设备树(4/5)-- Linux设备树详解(四)kernel的解析

    uboot将一些参数,设备树文件传给内核,那么内核如何处理这些设备树文件呢?本章就kernel解析设备树的过程和原理,本章的主要内容以Device Tree相关的数据流分析为索引,对ARM linux ...

  7. Linux设备树语法详解【转】

    转自:http://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备 ...

  8. Linux 设备树的使用技巧

    Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写.引入了设备树之后,驱动代码只负 ...

  9. Linux设备树特殊节点(aliases、chosen)介绍

    引用一个特定的节点通常使用全路径,aliases 节点可以用于指定一个设备全路径的别名.例如: 1 2 3 4 aliases {         ethernet0 = ð0;         se ...

最新文章

  1. 你为什么“啃不动”你手中的技术书?
  2. CLR中的IL、CTS和CLS总结
  3. POJ 3981.字符串替换
  4. 配置msf连接postgresql数据库
  5. Android UI卡顿监控
  6. C# 设计模式 - 单例模式 演示
  7. dapper利用DynamicParameters构建动态参数查询
  8. 最近200篇文章汇总
  9. 合格的程序员精通c语言吗,只精通一门C语言的人算不算程序员?
  10. 后盾网向军零基础学PHP视频教程
  11. 解决小米手机无法安装证书问题
  12. 新浪微博架构和FEED架构分析--人人架构
  13. 数据结构 | 合并两个长度分别为m和n的有序表,最坏情况下需要比较m+n-1次
  14. 解决苹果手机滑动卡顿
  15. 投资理财-有趣的灵魂
  16. 使用 Traci进行仿真
  17. win7桌面图标全变成windows media center 解决办法
  18. Python 知识点超全学习笔记整理
  19. 车载监控平台(VIMS)介绍
  20. MATLAB练习题(数学实验作业)

热门文章

  1. 设置网络IP及DNS的批处理
  2. 负载测试、压力测试和性能测试的区别
  3. win10如何用管理员身份打开hosts文件
  4. 中科院回应木兰事件,当事人否认骗科研经费
  5. javaweb404
  6. 央视专访巴菲特:投资股票其实就是投资公司
  7. 所谓的“警惕谷歌盗取中国文化资源”
  8. IDEA 设置背景图片和一些图片分享
  9. 电脑怎么恢复数据?恢复数据的3个技巧分享
  10. Oracle中同一个数据库下不同用户之间授权用户查询另一个用户下的表与视图