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
 */
 【setup_arch-->unflatten_device_tree();-->__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);--/* First pass, scan for size */
	size = unflatten_dt_nodes(blob, NULL, NULL, NULL);以及--/* Second pass, do actual unflattening */
	unflatten_dt_nodes(blob, mem, NULL, &of_root);根据这两个调用链分析】
【First pass, scan for size时为unflatten_dt_nodes(initial_boot_params,NULL&of_root,NULL);
Second pass, do actual unflattening时为
unflatten_dt_nodes(initial_boot_params,mem,&of_root,)】
【unflatten_dt_nodes(initial_boot_params,NULL&of_root,NULL)时,简化函数如下】
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	64
【注释:FDT_MAX_DEPTH 一个节点最深64,里面最多包含64个子节点】
	struct device_node *nps[FDT_MAX_DEPTH];
	void *base = mem;
	bool dryrun = !base;

	root = dad;
	nps[depth] = dad;

	for (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {
	     【一个节点超过最大64个子节点,超过则跳出,扫面同一级或上一级节点】
		if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
			continue;

		【drivers/of/kconfig中,
			config OF_KOBJ
				def_bool SYSFS
			也即IS_ENABLED(CONFIG_OF_KOBJ)为true,此判断不成立,忽略】
		if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
		    !of_fdt_device_is_available(blob, offset))
			continue;
			
		【populate_node解析见下文。知道没有node遍历完返回false,mem为从null=0累加节点名+属性值等的总占用空间大小,base=null=0if (!populate_node(blob, offset, &mem, nps[depth],
				   &nps[depth+1], dryrun))
			return mem - base;
	}

	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;

	【获取到offset偏移的节点的名子】
	pathp = fdt_get_name(blob, offset, &l);
	if (!pathp) {
		*pnp = NULL;
		【偏移计算之后,没有获取到节点名字,视为结束】
		return false;
	}

	allocl = ++l;

	【当__unflatten_device_tree中调用此句
	/* First pass, scan for size */
	size = unflatten_dt_nodes(blob, NULL, dad, NULL);时,该函数的参数mem为null。但unflatten_dt_nodes调用populate_node时传入的&mem不为null,它是指向此null指针的指针,也即下句的mem不为null,但*mem为null,np为null,详解见下文】
	np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
				
	【dryrun在unflatten_dt_nodes被调用,目的是/* First pass, scan for size */时为true,可忽略下面的两个if分支】
	if (!dryrun) {
		char *fn;
		of_node_init(np);
		np->full_name = fn = ((char *)np) + sizeof(*np);

		memcpy(fn, pathp, l);

		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);
		np->type = of_get_property(np, "device_type", NULL);

		if (!np->name)
			np->name = "<NULL>";
		if (!np->type)
			np->type = "<NULL>";
	}

	*pnp = np;
	【只要节点没有遍历完,就返回true,一直遍历】
	return true;
}

以下是fdt_get_name函数的解析begin---------------------------------------

const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
	【fdt_offset_ptr_-->
	fdt_off_dt_struct(fdt)-->
	#define fdt_off_dt_struct(fdt)		(fdt_get_header(fdt, off_dt_struct))  -->  
	#define fdt_get_header(fdt, field) \
	(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))-->(const struct fdt_header *)(fdt))->off_dt_struct
	其中fdt为uboot传给内核的dtb块地址,地址强转为fdt_header* 后取fdt_header的off_dt_struct的值,见下图。回溯上去return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; 就返回的是dt_struct中【图中展开部分】偏移量为nodeoffset的节点的地址】
	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
	const char *nameptr;
	int err;

	if (((err = fdt_check_header(fdt)) != 0)
	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
			goto fail;

	nameptr = nh->name;

	【V16前是全路径】
	if (fdt_version(fdt) < 0x10) {
		/*
		 * For old FDT versions, match the naming conventions of V16:
		 * give only the leaf name (after all /). The actual tree
		 * contents are loosely checked.
		 */
		const char *leaf;
		leaf = strrchr(nameptr, '/');
		if (leaf == NULL) {
			err = -FDT_ERR_BADSTRUCTURE;
			goto fail;
		}
		nameptr = leaf+1;
	}

	if (len)
		*len = strlen(nameptr);

	return nameptr;

 fail:
	if (len)
		*len = err;
	return NULL;
}

DTB图示:
在这里插入图片描述
图1
在这里插入图片描述
在这里插入图片描述
图3
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC /
fdt32_t totalsize; /
dtb文件大小 /
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're
				    booting 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 */

};
以上是fdt_get_name函数的解析end------------------------------

unflatten_dt_alloc函数的解析begin--------------------------

【当/* First pass, scan for size */时 
第一次调用时*mem = null= 0
size为sizeof(struct device_node) + allocl, align = 4
return*mem = 0+size.
后续多次调用此函数,*mem累加size】
static void *unflatten_dt_alloc(void **mem, unsigned long size,
				       unsigned long align)
{
	void *res;
	
	*mem = PTR_ALIGN(*mem, align);
	
	res = *mem;
	*mem += size;

	return res;
}

unflatten_dt_alloc函数的解析end--------------------------


/* Second pass, do actual unflattening */
unflatten_dt_nodes(initial_boot_params, 上面dt_alloc分配的地址, null, &of_root);

initial_boot_params 为内存中DTB的首地址
在函数内部
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
将offset = 0,depth = 0, dryrun = false 代入
在populate_node内部
看这行 pathp = fdt_get_name(blob, 0, &l);
以下为跳入fdt_get_name内部后
看const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
该函数返回 return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
结合图1蓝色部分,其中 fdt为dtb数据块首地址, fdt_off_dt_struct(fdt)返回off_dt_struct,即dt_struct相对于fdt的偏移,再加上offset,得到图3中的节点信息,用struct fdt_node_header表示。

struct fdt_node_header {
	fdt32_t tag;
	char name[0];
};

tag是标识node的起始结束等信息的标志位,name指向node名称的首地址。
tag的取值:

#define FDT_BEGIN_NODE	0x1		/* Start node: full name */
#define FDT_END_NODE	0x2		/* End node */
#define FDT_PROP	      0x3		/* Property: name off, size, content */
#define FDT_NOP		0x4		/* nop */
#define FDT_END		0x9

除了上面这个结构体,还有

struct fdt_property {
	fdt32_t tag;
	fdt32_t len;
	fdt32_t nameoff;
	char data[0];
};

他们两个共同来描述图3所示的dt_struct。
参数检查
((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
检查offset,offset映射的TAG必须是FDT_BEGIN_NODE。
进入 fdt_check_node_offset_函数里面后
(fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
对于fdt_next_tag函数,有几个事情:
1是取当前的tag。通过

tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
tag = fdt32_to_cpu(*tagp);

2是维护offset,使跳出fdt_next_tag后,即返回了当前TAG,又将offset掠过了节点名或属性等,指向下一个tag,为下一次循环做准备

offset += FDT_TAGSIZE;

case FDT_BEGIN_NODE:
		/* skip name */
		do {
			p = fdt_offset_ptr(fdt, offset++, 1);
		} while (p && (*p != '\0'));

*nextoffset = FDT_TAGALIGN(offset);

跳出fdt_check_node_offset_函数,由于nodeoffset参数是按值传递进去的,即一个copy进去,返回值,nodeoffset是不变的。接着fdt_get_name函数向下走,
节点名字通过 nameptr = nh->name;获取
if (fdt_version(fdt) < 0x10) { 不成立。
从 fdt_get_name跳出后,
pathp指向返回的节点名称字符串,l保存其长度,不含‘\0’
接着populate_node函数向下走
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
alignof(struct device_node));
该函数从上文dt_alloc分配的内存中申请空间,对应空间大小为device_node的大小以及节点名字符串,一前一后包含这两部分。
Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,struct device_node最终一般会被挂接到具体的struct device结构体。

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 */

接着populate_node函数向下走

if (!dryrun) {
		char *fn;
		of_node_init(np);  【Kobj相关】
		【full_name是一个指针,下句为其指向实际内存空间,使其不为野指针。
		上面说np指向的空间包含两部分,前部分为struct device_node,后部分为pathp字符串的长度,不含结束符的长度。也即np->full_name指向后面这部分空间,见图4】
		np->full_name = fn = ((char *)np) + sizeof(*np);
		
		【将从dtb中取出的节点名字复制到内存中np->full_name指向的地址】
		memcpy(fn, pathp, l);

		【父子,兄弟节点的挂接】
		if (dad != NULL) {
			np->parent = dad;
			np->sibling = dad->child;
			dad->child = np;
		}
	}

在这里插入图片描述

																		图4
接着populate_node函数向下走
populate_properties(blob, offset, mem, np, pathp, dryrun);
注意参数offset的连续性,承接着上面的fdt_get_name函数。
进入populate_properties
kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。

```c
struct property {
	char *name;                          /* property full name */
	int length;                          /* property value length */
	void *value;                         /* property value */
	struct property *next;             /* next property under the same node */
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;        /* 属性文件,与sysfs文件系统挂接 */
};

调用fdt_first_property_offset,该函数先调用fdt_check_node_offset_来检查当前offset对应的TAG是否是FDT_BEGIN_NODE,由于从上面的fdt_get_name函数后,并没与改变offset的值,所以还是对应FDT_BEGIN_NODE。接着调用nextprop_,调用fdt_next_tag,fdt_next_tag返回当前offset指向的TAG,若当前tag不是FDT_PROP,则继续调用fdt_next_tag,直到向下找到FDT_PROP即返回到fdt_first_property_offset,接着返回到populate_properties,并将当前FDT_PROP对应的offset赋值给cur,并接着向下走,调用 val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
接下来的调用链:fdt_getprop_by_offset–>fdt_get_property_by_offset_–>fdt_check_prop_offset(此处offset按值传递进去,是一个复制品)_

复制品的意思是:

【】
int fdt_check_prop_offset_(const void *fdt, int offset)
{
	【fdt_next_tag的参数&offset按地址传递,但这个地址不是
	static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
						              int offset,
						              int *lenp)中的offset,而是它在内存的复制对象的地址,也即fdt_get_property_by_offset_中调用fdt_check_prop_offset_后,不会改变传入fdt_get_property_by_offset_的offset值,以及影响该函数的后续使用,但fdt_check_prop_offset_的返回值那个offset会受fdt_next_tag影响】
	if ((offset < 0) || (offset % FDT_TAGSIZE)
	    || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
		return -FDT_ERR_BADOFFSET;

	return offset;
}

–>fdt_next_tag, 进入

......
offset += FDT_TAGSIZE;
......
case FDT_PROP:
		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
		if (!lenp)
			return FDT_END; /* premature end */
		/* skip-name offset, length and value */
		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
			+ fdt32_to_cpu(*lenp);
		......
		break;

offset 指向FDT_PROP这个TAG,- FDT_TAGSIZE,即减去上面+的FDT_TAGSIZE。掠过sizeof(struct fdt_property)+prop_data_len,由此指向下一个TAG。参考图3。但是由于上面调用链传进来的是copy,按值传递,当回到fdt_get_property_by_offset_时,offset值保持原来的,fdt_get_property_by_offset_返回struct fdt_property 类型的 prop到fdt_getprop_by_offset函数,再向上返回namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));以及return prop->data到populate_properties函数。populate_properties里接着往下:

if (!val) {
if (!pname) {  【这两个判断属性名和属性值不能为空,否则进行下一循环】
【遍历一个节点下的属性时,对每个有效属性,申请各自的空间】
pp = unflatten_dt_alloc(mem, sizeof(struct property),
					__alignof__(struct property));
......

pp->name   = (char *)pname;
pp->length = sz;
pp->value  = (__be32 *)val;

【同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。】
*pprev     = pp;
pprev      = &pp->next;

遍历某节点下的属性的结束条件为cur<0, 即cur = fdt_next_property_offset(blob, cur)中的if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)成立,即fdt_next_tag(fdt, offset, &offset) != FDT_PROP成立,也就是fdt_next_tag得到DT_END_NODE 。结合图3的dt_struct结构。

【fdt_next_property_offset参数 offset,按值传入fdt_check_prop_offset_,fdt_check_prop_offset_将offset按地址传入fdt_next_tag函数,fdt_next_tag会改变offset的值,其return的offset值不是fdt_check_prop_offset_的参数offset,返回给fdt_next_property_offset的offset,使nextprop_的offset不是传入fdt_next_property_offset的offset】
int fdt_next_property_offset(const void *fdt, int offset)
{
	【fdt_check_prop_offset_参数检查,由于循环体中的fdt_getprop_by_offset函数再去取属性后,并没有改变当前的offset,这里通过fdt_next_tag将offset跳过上次的已解析的属性区间,指向本次的属性区间开始处】
	if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
		return offset;
	【返回指向当前要解析的属性的对应的offset】
	return nextprop_(fdt, offset);
}

int fdt_check_prop_offset_(const void *fdt, int offset)
{
	if ((offset < 0) || (offset % FDT_TAGSIZE)
	    || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
		return -FDT_ERR_BADOFFSET;

	return offset;
}
if (!has_name) {
		const char *p = nodename, *ps = p, *pa = NULL;
		int len;

		【如nodename = virtio_mmio@10013000while循环找到@,算出virtio_mmio的长度。申请空间给property pp】
		while (*p) {
			if ((*p) == '@')
				pa = p;
			else if ((*p) == '/')
				ps = p + 1;
			p++;
		}

		if (pa < ps)
			pa = p;
		len = (pa - ps) + 1;
		【到这里,pa和p指向@,ps指向virtio_mmio@10013000的开始】
		pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
					__alignof__(struct property));
		if (!dryrun) {
			pp->name   = "name";
			pp->length = len;
			【pp + 1掠过一个struct property的空间,指向unflatten_dt_alloc申请的len长度的空间的起始地址】
			pp->value  = pp + 1;
			*pprev     = pp;
			pprev      = &pp->next;
			【将virtio_mmio@10013000中的virtio_mmio复制到pp->value作为name属性的值】
			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);
		}
	}

对于vexpress-v2p-ca9.dts,pr_debug打印内容:

OF: fdt: fixed up name for  -> 
OF: fdt: fixed up name for virtio_mmio@10013000 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013200 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013400 -> virtio_mmio
OF: fdt: fixed up name for virtio_mmio@10013600 -> virtio_mmio
OF: fdt: fixed up name for memory@60000000 -> memory
OF: fdt: fixed up name for smb@4000000 -> smb
OF: fdt: fixed up name for motherboard -> motherboard
OF: fdt: fixed up name for flash@0,00000000 -> flash
OF: fdt: fixed up name for psram@2,00000000 -> psram
OF: fdt: fixed up name for vram@3,00000000 -> vram
OF: fdt: fixed up name for ethernet@3,02000000 -> ethernet
OF: fdt: fixed up name for usb@3,03000000 -> usb
OF: fdt: fixed up name for iofpga@7,00000000 -> iofpga
OF: fdt: fixed up name for sysreg@0 -> sysreg
OF: fdt: fixed up name for gpio@8 -> gpio
OF: fdt: fixed up name for gpio@48 -> gpio
OF: fdt: fixed up name for gpio@4c -> gpio
OF: fdt: fixed up name for sysctl@1000 -> sysctl
OF: fdt: fixed up name for i2c@2000 -> i2c
OF: fdt: fixed up name for pcie-switch@60 -> pcie-switch
OF: fdt: fixed up name for aaci@4000 -> aaci
OF: fdt: fixed up name for mmci@5000 -> mmci
OF: fdt: fixed up name for kmi@6000 -> kmi
OF: fdt: fixed up name for kmi@7000 -> kmi
OF: fdt: fixed up name for uart@9000 -> uart
OF: fdt: fixed up name for uart@a000 -> uart
OF: fdt: fixed up name for uart@b000 -> uart
OF: fdt: fixed up name for uart@c000 -> uart
OF: fdt: fixed up name for wdt@f000 -> wdt
OF: fdt: fixed up name for timer@11000 -> timer
OF: fdt: fixed up name for timer@12000 -> timer
OF: fdt: fixed up name for i2c@16000 -> i2c
OF: fdt: fixed up name for dvi-transmitter@39 -> dvi-transmitter
OF: fdt: fixed up name for dvi-transmitter@60 -> dvi-transmitter
OF: fdt: fixed up name for rtc@17000 -> rtc
OF: fdt: fixed up name for compact-flash@1a000 -> compact-flash
OF: fdt: fixed up name for clcd@1f000 -> clcd
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel -> panel
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel-timing -> panel-timing
OF: fdt: fixed up name for fixed-regulator-0 -> fixed-regulator-0
OF: fdt: fixed up name for clk24mhz -> clk24mhz
OF: fdt: fixed up name for refclk1mhz -> refclk1mhz
OF: fdt: fixed up name for refclk32khz -> refclk32khz
OF: fdt: fixed up name for leds -> leds
OF: fdt: fixed up name for user1 -> user1
OF: fdt: fixed up name for user2 -> user2
OF: fdt: fixed up name for user3 -> user3
OF: fdt: fixed up name for user4 -> user4
OF: fdt: fixed up name for user5 -> user5
OF: fdt: fixed up name for user6 -> user6
OF: fdt: fixed up name for user7 -> user7
OF: fdt: fixed up name for user8 -> user8
OF: fdt: fixed up name for mcc -> mcc
OF: fdt: fixed up name for oscclk0 -> oscclk0
OF: fdt: fixed up name for oscclk1 -> oscclk1
OF: fdt: fixed up name for oscclk2 -> oscclk2
OF: fdt: fixed up name for volt-vio -> volt-vio
OF: fdt: fixed up name for temp-mcc -> temp-mcc
OF: fdt: fixed up name for reset -> reset
OF: fdt: fixed up name for muxfpga -> muxfpga
OF: fdt: fixed up name for shutdown -> shutdown
OF: fdt: fixed up name for reboot -> reboot
OF: fdt: fixed up name for dvimode -> dvimode
OF: fdt: fixed up name for chosen -> chosen
OF: fdt: fixed up name for aliases -> aliases
OF: fdt: fixed up name for cpus -> cpus
OF: fdt: fixed up name for cpu@0 -> cpu
OF: fdt: fixed up name for cpu@1 -> cpu
OF: fdt: fixed up name for cpu@2 -> cpu
OF: fdt: fixed up name for cpu@3 -> cpu
OF: fdt: fixed up name for clcd@10020000 -> clcd
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel -> panel
OF: fdt: fixed up name for port -> port
OF: fdt: fixed up name for endpoint -> endpoint
OF: fdt: fixed up name for panel-timing -> panel-timing
OF: fdt: fixed up name for memory-controller@100e0000 -> memory-controller
OF: fdt: fixed up name for memory-controller@100e1000 -> memory-controller
OF: fdt: fixed up name for timer@100e4000 -> timer
OF: fdt: fixed up name for watchdog@100e5000 -> watchdog
OF: fdt: fixed up name for scu@1e000000 -> scu
OF: fdt: fixed up name for timer@1e000600 -> timer
OF: fdt: fixed up name for watchdog@1e000620 -> watchdog
OF: fdt: fixed up name for interrupt-controller@1e001000 -> interrupt-controller
OF: fdt: fixed up name for cache-controller@1e00a000 -> cache-controller
OF: fdt: fixed up name for hello -> hello
OF: fdt: fixed up name for pmu -> pmu
OF: fdt: fixed up name for dcc -> dcc
OF: fdt: fixed up name for extsaxiclk -> extsaxiclk
OF: fdt: fixed up name for clcdclk -> clcdclk
OF: fdt: fixed up name for tcrefclk -> tcrefclk
OF: fdt: fixed up name for volt-vd10 -> volt-vd10
OF: fdt: fixed up name for volt-vd10-s2 -> volt-vd10-s2
OF: fdt: fixed up name for volt-vd10-s3 -> volt-vd10-s3
OF: fdt: fixed up name for volt-vcc1v8 -> volt-vcc1v8
OF: fdt: fixed up name for volt-ddr2vtt -> volt-ddr2vtt
OF: fdt: fixed up name for volt-vcc3v3 -> volt-vcc3v3
OF: fdt: fixed up name for amp-vd10-s2 -> amp-vd10-s2
OF: fdt: fixed up name for amp-vd10-s3 -> amp-vd10-s3
OF: fdt: fixed up name for power-vd10-s2 -> power-vd10-s2
OF: fdt: fixed up name for power-vd10-s3 -> power-vd10-s3
OF: fdt: fixed up name for hsb@e0000000 -> hsb

解析fdt_next_property_offset

从populate_properties返回到populate_node,populate_node接下来执行
*pnp = np; 将本次解析到的device_node *,给device_node指针数组,此数组定义在populate_node被调用的上层unflatten_dt_nodes函数中struct device_node *nps[FDT_MAX_DEPTH];,以 &nps[depth+1]的形式传入populate_node。dtb解析完后,所有的节点的device_node的地址指针。平坦地保存在此指针数组中。
unflatten_dt_nodes接下来

*nodepp即of_root,下面两个if语句只在根节点时执行一次】
【注意,这样执行nps[0]=null,nps从下标1开始存放root节点及后续节点】
if (!dryrun && nodepp && !*nodepp)
			*nodepp = nps[depth+1];
if (!dryrun && !root)
			root = nps[depth+1];

循环体向前递进的语句:offset = fdt_next_node(blob, offset, &depth)
由于循环体内的
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
调用,offset是按值传递的,所以不会变。当执行fdt_next_node时,还是指向上次刚已经解析的节点在dt_struct中的偏移。

int fdt_next_node(const void *fdt, int offset, int *depth)
{
	int nextoffset = 0;
	uint32_t tag;

	if (offset >= 0)
		【fdt_check_node_offset_,用于检查当前offset有效性,特别是当前offset对应的TAG是否是FDT_BEGIN_NODE,返回值nextoffset已经掠过了上次解析的节点区域,指向了当前要解析的节点】
		if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
			return nextoffset;
	【循环查找,直到找到TAG为FDT_BEGIN_NODE】
	do {
		offset = nextoffset;
		【找到offset对应的tag,并且掠过当前类型区段,nextoffset指向下一个区段的开始,用于循环查找,直到找到FDT_BEGIN_NODE或FDT_END结束】
		tag = fdt_next_tag(fdt, offset, &nextoffset);

		switch (tag) {
		case FDT_PROP:
		case FDT_NOP:
			break;
		【维护nps节点结构体数组的下标,存放当前节点的位置】
		case FDT_BEGIN_NODE:
			if (depth)
				(*depth)++;
			break;

		case FDT_END_NODE:
			if (depth && ((--(*depth)) < 0))
				return nextoffset;
			break;

		case FDT_END:
			if ((nextoffset >= 0)
			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
				return -FDT_ERR_NOTFOUND;
			else
				return nextoffset;
		}
	} while (tag != FDT_BEGIN_NODE);

	【返回当前要解析的TAG为FDT_BEGIN_NODE的节点】
	return offset;
}
### 回答1: nx.draw_networkx_nodes函数用于绘制网络中的节点。它的基本用法如下: ```python import networkx as nx import matplotlib.pyplot as plt # 创建一个空的无向图 G = nx.Graph() # 向图中添加节点 G.add_node(1) G.add_node(2) G.add_node(3) # 绘制节点 pos = nx.spring_layout(G) # 计算节点的布局位置 nx.draw_networkx_nodes(G, pos, node_size=300, node_color='r', alpha=0.8) plt.show() ``` 上述代码中,首先创建了一个空的无向图G,并向其中添加了三个节点。然后使用nx.spring_layout计算节点的布局位置,最后使用nx.draw_networkx_nodes函数绘制节点。其中,pos表示节点的位置,node_size表示节点的大小,node_color表示节点的颜色,alpha表示节点的透明度。 更多的绘制节点的参数可以查看官方文档:https://networkx.github.io/documentation/latest/reference/generated/networkx.drawing.nx_pylab.draw_networkx_nodes.html。 ### 回答2: nx.draw_networkx_nodes函数是NetworkX库中用于绘制节点的函数。该函数用于绘制代表图的节点的可视化表示。 具体用法如下: nx.draw_networkx_nodes(G, pos=None, node_size=300, node_color='r', node_shape='o', alpha=None) 其中,参数G表示输入的图,可以通过G.nodes()方法获取到图中的所有节点。 pos表示节点的位置,可以是一个映射字典,其中键是节点的名称,值是节点的坐标;也可以是一个布局函数,用于自动排列节点的位置。 node_size表示节点的大小,默认为300。 node_color表示节点的颜色,默认为‘r’(红色),可以是字符串形式的颜色名称、RGB元组、RGBA元组或颜色映射。 node_shape表示节点的形状,默认为‘o’(圆形),可以是字符串形式的形状名称或自定义的形状。 alpha表示节点的透明度,默认为None,即完全不透明。可以是0~1之间的值,表示透明度的程度。 使用该函数可以在绘图窗口中显示代表图节点的形状,并根据指定的参数调整节点的位置、大小和颜色等属性。 ### 回答3: nx.draw_networkx_nodes函数是NetworkX中用于绘制节点的函数。它可以将节点绘制在一个二维空间中的指定位置上。 nx.draw_networkx_nodes函数的参数包括: 1. G:一个NetworkX图对象,表示要绘制节点的图。 2. pos:一个字典,用于指定节点的位置。字典的键是节点,值是二维坐标元组。如果没有提供该参数,节点将会在二维平面上自动生成位置。 3. node_color:用于指定节点的颜色。可以是一个颜色字符串(如'red'),也可以是一个颜色列表,每个节点对应一个颜色。如果没有提供该参数,默认颜色是蓝色。 4. node_size:用于指定节点的尺寸。可以是一个整数,表示所有节点的大小相同;也可以是一个整数列表,每个节点对应一个尺寸。如果没有提供该参数,默认尺寸是300。 5. alpha:用于指定节点的透明度。它可以是一个0到1之间的浮点数,表示节点的透明度;或者是一个浮点数列表,每个节点对应一个透明度。如果没有提供该参数,默认值是1(不透明)。 6. cmap:用于指定节点的颜色映射。它可以是一个Matplotlib的Colormap对象,从节点的值到颜色之间进行映射。如果没有提供该参数,默认使用viridis色图。 7. vmin和vmax:用于指定颜色映射的范围。如果提供了cmap参数,那么vmin和vmax将会影响节点的颜色映射范围。如果没有提供该参数,默认值是节点值的最小和最大值。 通过调用nx.draw_networkx_nodes函数,可以将节点绘制在二维空间中,以便更好地可视化网络结构。可以根据节点的属性来设置节点的颜色、尺寸和透明度,以及使用颜色映射对节点进行更加精细的可视化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值