Device Tree (三) - dtb -> device_node

2024-03-18 21:04
文章标签 node tree device dtb

本文主要是介绍Device Tree (三) - dtb -> device_node,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于arm平台,Linux 5.10

1,设备树的执行入口setup_arch

linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂且不作过多讨论。

在head.s完成部分初始化之后,就开始调用C语言函数,而被调用的第一个C语言函数就是start_kernel:

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{......setup_arch(&command_line);......
}

而对于设备树的处理,基本上就在setup_arch()这个函数中。

可以看到,在start_kernel()中调用了setup_arch(&command_line);

void __init __no_sanitize_address setup_arch(char **cmdline_p)
{......//根据传入的设备树dtb首地址完成一些初始化操作setup_machine_fdt(__fdt_pointer);//保证设备树dtb本身存在于内存中而不被覆盖arm64_memblock_init();//对设备树的具体解析unflatten_device_tree();......
}

这三个被调用的函数就是主要的设备树处理函数:

* setup_machine_fdt():根据传入的设备树dtb的首地址完成一些初始化操作。

*arm64_memblock_init():主要是内存相关函数,为设备树保留相应的内存空间,保证设备树dtb本身存在于内存中而不被覆盖。用户可以在设备树中设置保留内存,这一部分同时作了保留指定内存的工作。

* unflatten_device_tree():对设备树具体的解析,事实上在这个函数中所做的工作就是将设备树各节点转换成相应的struct device_node结构体。

2,dtb -> device_node转换过程

kernel V5.10:
start_kernel(void) /* D:\work\source_code\msm-kernel\msm_kernel\init\main.c */
----setup_arch(&command_line); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
--------setup_machine_fdt(__fdt_pointer); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
------------early_init_dt_scan(dt_virt)
----------------early_init_dt_scan_nodes();
--------------------of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
--------------------of_scan_flat_dt(early_init_dt_scan_root, NULL);
--------------------of_scan_flat_dt(early_init_dt_scan_memory, NULL);--------arm64_memblock_init();
------------early_init_fdt_scan_reserved_mem();
----------------early_init_dt_reserve_memory_arch(base, size, false);
--------------------memblock_reserve(base, size);--------unflatten_device_tree(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\fdt.c */
------------__unflatten_device_tree(initial_boot_params, NULL, &of_root, early_init_dt_alloc_memory_arch, false);
----------------unflatten_dt_nodes(blob, NULL, dad, NULL);
--------------------populate_node(blob, offset, &mem, nps[depth], &nps[depth+1], dryrun))
------------------------np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, __alignof__(struct device_node));
------------------------populate_properties(blob, offset, mem, np, pathp, dryrun);
----------------------------fdt_getprop_by_offset(blob, cur, &pname, &sz);--------of_alias_scan(early_init_dt_alloc_memory_arch);
--------unittest_unflatten_overlay_base();

3,Device Tree文件(DTB)结构描述的结构体

struct fdt_header {fdt32_t magic;             /* magic word FDT_MAGIC */fdt32_t totalsize;         /* total size of DT block */fdt32_t off_dt_struct;         /* offset to structure */fdt32_t off_dt_strings;         /* offset to strings */fdt32_t off_mem_rsvmap;         /* offset to memory reserve map */fdt32_t version;         /* format version */fdt32_t last_comp_version;     /* last compatible version *//* version 2 fields below */fdt32_t boot_cpuid_phys;     /* Which physical CPU id we'rebooting on *//* version 3 fields below */fdt32_t size_dt_strings;     /* size of the strings block *//* version 17 fields below */fdt32_t size_dt_struct;         /* size of the structure block */
};struct fdt_reserve_entry {fdt64_t address;fdt64_t size;
};struct fdt_node_header {fdt32_t tag;char name[0];
};struct fdt_property {fdt32_t tag;fdt32_t len;fdt32_t nameoff;char data[0];
};

4,struct device_node/struct property关键结构体

4.1 struct device_node

Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,

struct device_node最终一般会被挂接到具体的struct device结构体。struct device_node结构体描述如下:

struct device_node {const char *name;              /* node的名称,取最后一次“/”和“@”之间子串 */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 */

4.2 struct property

kernel会根据Device Tree的结构解析出kernel能够使用的struct property结构体。

kernel根据Device Tree中所有的属性解析出数据填充struct property结构体。struct property 结构体描述如下:

struct property {char    *name;int    length;void    *value;struct property *next; //kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)struct bin_attribute attr;
#endif
};

5,setup_machine_fdt

bool __init early_init_dt_verify(void *params)
{if (!params)return false;/* check device tree validity */if (fdt_check_header(params))return false;/* Setup flat device-tree pointer *///initial_boot_params: device tree blob的虚拟地址initial_boot_params = params;//计算扁平化设备树的crc32校验码of_fdt_crc32 = crc32_be(~0, initial_boot_params,fdt_totalsize(initial_boot_params));return true;
}

扫描设备树中的各个子节点:

void __init early_init_dt_scan_nodes(void)
{int rc = 0;/* Retrieve various information from the /chosen node */rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);if (!rc)pr_warn("No chosen node found, continuing without\n");/* Initialize {size,address}-cells info */of_scan_flat_dt(early_init_dt_scan_root, NULL);/* Setup memory, calling early_init_dt_add_memory_arch */of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
int __init of_scan_flat_dt(int (*it)(unsigned long node,const char *uname, int depth,void *data),void *data);

扫描展开之前的flattened  device tree,对于找到的每一个node调用it函数,data作为it函数的一个参数。

5.1 early_init_dt_scan_chosen

dts example :

chosen {bootargs = "console=ttySAC0,115200n8 root=/dev/mmcblk0p1 rw rootwait ignore_loglevel earlyprintk";
};

解析过程:

    /* Retrieve various information from the /chosen node */rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,int depth, void *data)
{int l = 0;const char *p = NULL;const void *rng_seed;char *cmdline = data;//扫描chosen节点,打印节点的名字和节点在设备树中的深度pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);//chosen节点的深度为1,cmdline指针变量指向bootcmd信息,地址不能为空if (depth != 1 || !cmdline ||(strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))return 0;//从扁平设备树中获取initrd的位置early_init_dt_check_for_initrd(node);....../* Retrieve command line unless forcing *///从bootargs属性中读取cmdlineif (read_dt_cmdline)p = of_get_flat_dt_prop(node, "bootargs", &l);......pr_debug("Command line is: %s\n", (char *)data);
}

解析出来的command line 存储在全局变量boot_command_line中,作为bootloader启动过程中向kernel的传参。

/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];

5.2 early_init_dt_scan_root

    /* Initialize {size,address}-cells info */of_scan_flat_dt(early_init_dt_scan_root, NULL);
/**
* early_init_dt_scan_root - fetch the top level address and size cells
*/
int __init early_init_dt_scan_root(unsigned long node, const char *uname,int depth, void *data)
{const __be32 *prop;//扫描根节点的#address-cells 和 #size-cell属性,根节点深度为0if (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" propertyprop = of_get_flat_dt_prop(node, "#size-cells", NULL);if (prop)//使用的设备树dtb文件是以大端序方式存储的,转换为cpu字节序,如果CPU字节序也为大端模式直接返回dt_root_size_cells = be32_to_cpup(prop);pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);//读取根节点的"#address-cells" propertyprop = 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);/* break now */return 1;
}

解析出来的根节点的#address-cells 和 #size-cells 保存在全局变量中,用来描述地址的属性,即地址的起始位置和所占内存大小。

/* Everything below here references initial_boot_params directly. */
int __initdata dt_root_addr_cells;
int __initdata dt_root_size_cells;

5.3 early_init_dt_scan_memory

dts example:

memory
memory { device_type = "memory"; reg = <0 0 0 0>; };

解析过程:

    /* Setup memory, calling early_init_dt_add_memory_arch */of_scan_flat_dt(early_init_dt_scan_memory, NULL);
/**
* early_init_dt_scan_memory - Look for and parse memory nodes
*/
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,int depth, void *data)
{const char *type = of_get_flat_dt_prop(node, "device_type", NULL);const __be32 *reg, *endp;int l;bool hotpluggable;//device_type需要是memory/* We are scanning "memory" nodes only */if (type == NULL || strcmp(type, "memory") != 0)return 0;endp = reg + (l / sizeof(__be32));hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);pr_debug("memory scan node %s, reg size %d,\n", uname, l);//根据address-cells 和 size-cells解析memory regwhile ((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);//memblock_add(base, size)early_init_dt_add_memory_arch(base, size);if (!hotpluggable)continue;//memblock_reserve(base, size)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;
}

扫描具有device_type = “memory”属性的/memory或者/memory@0节点下面的reg属性值,并把相关信息保存在meminfo中,全局变量meminfo保存了系统内存相关的信息。

所有设备树都需要一个memory设备节点,它描述了系统的物理内存布局。如果系统有多个内存块,可以创建多个memory节点,或者可以在单个memory节点的reg属性中指定这些地址范围和内存空间大小。

6,arm64_memblock_init

dts example:

memreserve
/memreserve/ 0x0000a800 0x000f5800;reserved-memory
reserved_memory: reserved-memory {              #address-cells = <2>;#size-cells = <2>;ranges;hyp_mem: hyp_region@80000000 {no-map;reg = <0x0 0x80000000 0x0 0x600000>;};xbl_dtlog_mem: xbl_dtlog_region@80600000 {no-map;reg = <0x0 0x80600000 0x0 0x40000>;};xbl_ramdump_mem: xbl_ramdump_region@80640000 {no-map;reg = <0x0 0x80640000 0x0 0x1c0000>;};aop_image_mem: aop_image_region@80800000 {no-map;reg = <0x0 0x80800000 0x0 0x60000>;};
};

解析过程:

/**
* early_init_fdt_scan_reserved_mem() - create reserved memory regions
*
* This function grabs memory from early allocator for device exclusive use
* defined in device tree structures. It should be called by arch specific code
* once the early allocator (i.e. memblock) has been fully activated.
*/
void __init early_init_fdt_scan_reserved_mem(void)
{int n;u64 base, size;if (!initial_boot_params)return;/* Process header /memreserve/ fields */// 解析/memreserve/,告诉内核哪一些内存空间需要被保留而不应该被系统覆盖使用,因为在内核启动时常常需要动态申请大量的内存空间,只有提前进行注册,用户需要使用的内存才不会被系统征用而造成数据覆盖for (n = 0; ; n++) {fdt_get_mem_rsv(initial_boot_params, n, &base, &size);if (!size)break;early_init_dt_reserve_memory_arch(base, size, false);}// 解析"reserved-memory"节点,分配保留空间of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);fdt_init_reserved_mem();
}

有时我们需要在 Linux 内核中预留一部分内存空间用作特殊用途(给安全模块使用,给其它处理器使用,或是给特定的驱动程序使用等),在 Device Tree 中有提供两种方法对预留内存进行配置:memreserve 和 reserved-memory。

1) memreserve

memreserve 的使用方法比较简单,如下所示,会将从地址 0x40000000 开始共 1MB 的内存空间预留出来:

/memreserve/ 0x40000000 0x00100000;

使用 memreserve 预留出来的内存一般无法再被 Linux 系统使用(当然,也可以通过特殊方法让代码固定访问该地址,但这种并非标准用法,在此不展开描述)。

2) reserved-memory

reserved-memory 框架提供了更多样的使用方法,并且与内核的DMA API 和 CMA框架紧密联系。

推荐先阅读一下内核自带的文档 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt,里面有其详细的语法说明和注意事项(例如 reserved-memory 节点中的 #address-cells 和 #size-cells 的值需要与根节点的保持一致)。

7,unflatten_device_tree

Device Tree的解析首先从unflatten_device_tree()开始,代码列出如下:

/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
*/
void __init unflatten_device_tree(void)
{//参数initial_boot_params指向Device Tree在内存中的首地址,of_root在经过该函数处理之后,会指向根节点(of_root节点的树结构)//early_init_dt_alloc_memory_arch是一个函数指针,为struct device_node和struct property结构体分配内存的回调函数(callback)__unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false);/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */of_alias_scan(early_init_dt_alloc_memory_arch);unittest_unflatten_overlay_base();
}
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
* @blob: The blob to expand
* @dad: Parent device node
* @mynodes: The device_node tree created by the call
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
* @detached: if true set OF_DETACHED on @mynodes
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success.
*/
void *__unflatten_device_tree(const void *blob,struct device_node *dad,struct device_node **mynodes,void *(*dt_alloc)(u64 size, u64 align),bool detached)
{int size;void *mem;pr_debug(" -> unflatten_device_tree()\n");if (!blob) {pr_debug("No device tree pointer\n");return NULL;}pr_debug("Unflattening device tree:\n");pr_debug("magic: %08x\n", fdt_magic(blob));pr_debug("size: %08x\n", fdt_totalsize(blob));pr_debug("version: %08x\n", fdt_version(blob));//检查dtb header是否有效if (fdt_check_header(blob)) {pr_err("Invalid device tree blob header\n");return NULL;}/* First pass, scan for size *///第一次是为了得到Device Tree转换成struct device_node和struct property结构体需要分配的内存大小size = unflatten_dt_nodes(blob, NULL, dad, NULL);if (size < 0)return NULL;size = ALIGN(size, 4);pr_debug("  size is %d, allocating...\n", size);/* Allocate memory for the expanded device tree */mem = dt_alloc(size + 4, __alignof__(struct device_node));if (!mem)return NULL;memset(mem, 0, size);*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);pr_debug("  unflattening %p...\n", mem);/* Second pass, do actual unflattening *///第二次调用才是具体填充每一个struct device_node和struct property结构体unflatten_dt_nodes(blob, mem, dad, mynodes);if (be32_to_cpup(mem + size) != 0xdeadbeef)pr_warn("End of tree marker overwritten: %08x\n",be32_to_cpup(mem + size));if (detached && mynodes) {of_node_set_flag(*mynodes, OF_DETACHED);pr_debug("unflattened tree is detached\n");}pr_debug(" <- unflatten_device_tree()\n");return mem;
}

展开设备树节点unflatten_dt_nodes:

/**
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @dad: Parent struct device_node
* @nodepp: The device_node tree created by the call
*
* It returns the size of unflattened device tree or error code
*/
static int unflatten_dt_nodes(const void *blob,void *mem,struct device_node *dad,struct device_node **nodepp)
{struct device_node *root;int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH    64struct device_node *nps[FDT_MAX_DEPTH];void *base = mem;//计算展开的设备树占用内存空间大小的时候dryrun为1bool dryrun = !base;if (nodepp)*nodepp = NULL;/** We're unflattening device sub-tree if @dad is valid. There are* possibly multiple nodes in the first level of depth. We need* set @depth to 1 to make fdt_next_node() happy as it bails* immediately when negative @depth is found. Otherwise, the device* nodes except the first one won't be unflattened successfully.*/if (dad)depth = initial_depth = 1;root = dad;nps[depth] = dad;//遍历dtb,解析每一个device_node和propertyfor (offset = 0;offset >= 0 && depth >= initial_depth;offset = fdt_next_node(blob, offset, &depth)) {//子节点的深度不能超过最大深度64if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1))continue;if (!IS_ENABLED(CONFIG_OF_KOBJ) &&!of_fdt_device_is_available(blob, offset))continue;//填充节点if (!populate_node(blob, offset, &mem, nps[depth],&nps[depth+1], dryrun))return mem - base;//根节点 of_rootif (!dryrun && nodepp && !*nodepp)*nodepp = nps[depth+1];if (!dryrun && !root)root = nps[depth+1];}if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {pr_err("Error %d processing FDT\n", offset);return -EINVAL;}/** Reverse the child list. Some drivers assumes node order matches .dts* node order*/if (!dryrun)reverse_nodes(root);return mem - base;
}

填充节点:

static bool populate_node(const void *blob,int offset,void **mem,struct device_node *dad,struct device_node **pnp,bool dryrun)
{struct device_node *np;const char *pathp;unsigned int l, allocl;//获取节点的名字pathp = fdt_get_name(blob, offset, &l);if (!pathp) {*pnp = NULL;return false;}//device node name以 '\0'为结束符allocl = ++l;//为device_node 和 节点名字 分配内存空间np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));if (!dryrun) {char *fn;//初始化node,设置node的kobj和fwnode->opsof_node_init(np);np->full_name = fn = ((char *)np) + sizeof(*np);//设置node的full_namememcpy(fn, pathp, l);//设置node的父亲节点和兄弟节点if (dad != NULL) {np->parent = dad;np->sibling = dad->child;dad->child = np;}}//填充节点的属性populate_properties(blob, offset, mem, np, pathp, dryrun);if (!dryrun) {np->name = of_get_property(np, "name", NULL);if (!np->name)np->name = "<NULL>";}*pnp = np;return true;
}

填充节点的属性:

static void populate_properties(const void *blob,int offset,void **mem,struct device_node *np,const char *nodename,bool dryrun)
{struct property *pp, **pprev = NULL;int cur;bool has_name = false;pprev = &np->properties;//遍历该节点下的所有属性for (cur = fdt_first_property_offset(blob, offset);cur >= 0;cur = fdt_next_property_offset(blob, cur)) {const __be32 *val;const char *pname;u32 sz;//获取属性的名字和值 key = valueval = fdt_getprop_by_offset(blob, cur, &pname, &sz);if (!val) {pr_warn("Cannot locate property at 0x%x\n", cur);continue;}if (!pname) {pr_warn("Cannot find property name at 0x%x\n", cur);continue;}//如果有"name" 属性,做个标记if (!strcmp(pname, "name"))has_name = true;//为属性分配内存空间pp = unflatten_dt_alloc(mem, sizeof(struct property),__alignof__(struct property));if (dryrun)continue;/* We accept flattened tree phandles either in* ePAPR-style "phandle" properties, or the* legacy "linux,phandle" properties.  If both* appear and have different values, things* will get weird. Don't do that.*///对于"phandle"属性和"linux,phandle"属性,直接填充struct device_node的phandle字段,不放在属性链表中if (!strcmp(pname, "phandle") ||!strcmp(pname, "linux,phandle")) {if (!np->phandle)np->phandle = be32_to_cpup(val);}/* And we process the "ibm,phandle" property* used in pSeries dynamic device tree* stuff*/if (!strcmp(pname, "ibm,phandle"))np->phandle = be32_to_cpup(val);//填充property 结构体成员pp->name   = (char *)pname;pp->length = sz;pp->value  = (__be32 *)val;//加入属性链表*pprev     = pp;pprev      = &pp->next;}/* With version 0x10 we may not have the name property,* recreate it here from the unit name if absent*///为每个node节点添加一个name的属性//node的名称,取最后一次“/”和“@”之间子串if (!has_name) {const char *p = nodename, *ps = p, *pa = NULL;int len;while (*p) {if ((*p) == '@')pa = p;else if ((*p) == '/')ps = p + 1;p++;}if (pa < ps)pa = p;len = (pa - ps) + 1;pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,__alignof__(struct property));if (!dryrun) {pp->name   = "name";pp->length = len;pp->value  = pp + 1;*pprev     = pp;pprev      = &pp->next;memcpy(pp->value, ps, len - 1);((char *)pp->value)[len - 1] = 0;pr_debug("fixed up name for %s -> %s\n",nodename, (char *)pp->value);}}if (!dryrun)*pprev = NULL;
}

此后,内核就可以根据device_node来创建设备。

参考链接:

https://www.cnblogs.com/schips/p/linux_driver_dtb_to_device_node.html

Device Tree(四):文件结构解析

这篇关于Device Tree (三) - dtb -> device_node的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/823638

相关文章

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

Node Linux相关安装

下载经编译好的文件cd /optwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.gztar -xvf node-v10.15.3-linux-x64.tar.gzln -s /opt/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/ln -s /opt/nod

树(Tree)——《啊哈!算法》

树 什么是树?树是一种特殊的图,不包含回路的连通无向树。 正因为树有着“不包含回路”这个特点,所以树就被赋予了很多特性。 一棵树中的任意两个结点有且仅有唯一的一条路径连通。一棵树如果有n个结点,那么它一定恰好有n-1条边。在一棵树中加一条边将会构成一个回路。 一棵树有且只有一个根结点。 没有父结点的结点称为根结点(祖先)。没有子结点的结点称为叶结点。如果一个结点既不是根结点也不是叶

Usb Audio Device Descriptor(10) Hid Device

对于 Standard Interface Descriptor, 当 bInterfaceClass=0x03时,即为HID设备。Standard Interface Descriptor如下 struct usb_standard_interface_descriptor{U8 bLength; /*Size of this descriptor in bytes*/U8 bDescrip

在Debian 8上安装Node.js的方法

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 Node.js 是一个通用编程的 JavaScript 平台,允许用户快速构建网络应用程序。通过在前端和后端都使用 JavaScript,开发可以更加一致,并且可以在同一个系统中设计。 在本指南中,您将在 Debian 8 服务器上安装 Node.js。Debian 8 包含一个版本的

src/pyaudio/device_api.c:9:10: fatal error: portaudio.h: 没有那个文件或目录

(venv) shgbitai@shgbitai-C9X299-PGF:~/pythonworkspace/ai-accompany$ pip install pyaudio sounddeviceCollecting pyaudioDownloading PyAudio-0.2.14.tar.gz (47 kB)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

MTK平台AndroidP/Q快速生成dtb.img和dtbo.img(2秒生成)

背景: MTK升级新的Android版本后,无法快速编译dtbo image,当我们修改DWS和PROJECT DTS后验证问题非常麻烦,必须删除out下KERNEL_OBJ才能生成新的dtbo.img,而且必须整编。 所以跟据编译流程,写了下面这个脚本,可以快速生成dtbo和dtb image,可以几秒钟完成 使用方法: (1)将脚本放在工程跟目录 (2)修改脚本开头的几个变量,改为你

使用Node-API进行异步任务开发

一、Node-API异步任务机制概述         Node-API异步任务开发主要用于执行耗时操作的场景中使用,以避免阻塞主线程,确保应用程序的性能和响应效率。         1、应用场景: 文件操作:读取大型文件或执行复杂的文件操作时,可以使用异步工作项来避免阻塞主线程。网络请求:当需要进行网络请求并等待响应时,可以使用异步工作项来避免阻塞主线程,从而提高应用程序的响应性能。数据库操

226 Invert Binary Tree

//226 Invert Binary Tree//算法思路:主要使用递归算法public class Solution {public TreeNode invertTree(TreeNode root) {//1 出口 空节点if (root==null)return null;//2 递归 调用自己TreeNode left = root.left;TreeNode right = ro

Node.js学习记录(一)

目录 一、文件读取 readFile 二、写入文件 writeFile 三、动态路径 __dirname:表示当前文件所处的目录、path.join 四、获取路径文件名 path.basename 五、提取某文件中的css、JS、html 六、http 七、启动创建web服务器 服务器响应 八、将资源请求的 url 地址映射为文件的存放路径 九、模块 模块加载 模块作用域