/**
* unflatten_dt_node - 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
* @p: pointer to node in flat tree
* @dad: Parent struct device_node
* @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth.
*/
static unsigned long unflatten_dt_node(struct boot_param_header *blob,
unsigned long mem,
unsigned long *p,
struct device_node *dad,
struct device_node ***allnextpp,
unsigned long fpsize)
{
struct device_node *np;
struct property *pp, **prev_pp = NULL;
char *pathp;
u32 tag;
unsigned int l, allocl;
int has_name = 0;
int new_format = 0;

tag = be32_to_cpup((__be32 *)(*p)); //每个有孩子的设备节点,其tag一定是OF_DT_BEGIN_NODE
if (tag != OF_DT_BEGIN_NODE) {
pr_err("Weird tag at start of node: %x\n", tag);
return mem;
}
*p += 4; //地址+4,这样指向节点的名称
pathp = (char *)*p;
l = allocl = strlen(pathp) + 1; //该节点名称的长度
*p = ALIGN(*p + l, 4); ///*p 指向带分析的属性

/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
* it later. We detect this because the first character of the name is
* not '/'.
*/

/*计算fullpath的长度 */
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) { //根节点
/* root node: special case. fpsize accounts for path
* plus terminating zero. root node only has '/', so
* fpsize should be 2, but we want to avoid the first
* level nodes to have two '/' so we use fpsize 1 here
*/
fpsize = 1;
allocl = 2; //要分配的长度
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
*/
fpsize += l; //要分配的长度=本节点名称长度+父亲节点绝对路径的长度
allocl = fpsize;
}
}

/* 分配一个设备节点 */
//mem = 0 , np就是分配的 struct device node 大小的内存的指针
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
//下面是判断,填充np,构造出device node
/* allnextpp指向前一个设备链表的指针 */
if (allnextpp) { //第二次扫描 nextpp 存在要进入 if 内执行
memset(np, 0, sizeof(*np));
np->full_name = ((char *)np) + sizeof(struct device_node);
if (new_format) {
char *fn = np->full_name;
/* rebuild full path for new format */
if (dad && dad->parent) {
strcpy(fn, dad->full_name); //把父亲节点绝对路径先拷贝
#ifdef DEBUG
if ((strlen(fn) + l + 1) != allocl) {
pr_debug("%s: p: %d, l: %d, a: %d\n",
pathp, (int)strlen(fn),
l, allocl);
}
#endif
fn += strlen(fn);
}
*(fn++) = '/';
memcpy(fn, pathp, l); //拷贝本节点的名称
} else
memcpy(np->full_name, pathp, l);
prev_pp = &np->properties; //prev_pp指向节点的属性
**allnextpp = np; //当前节点插入链表
*allnextpp = &np->allnext;
if (dad != NULL) { //父亲节点不为空
np->parent = dad; //指向父亲节点
/* we temporarily use the next field as `last_child'*/
if (dad->next == NULL) //第一个孩子
dad->child = np; //child指向第一个孩子
else
dad->next->sibling = np; //把np插入next,这样孩子节点形成链表
dad->next = np; //指向最新挂接的child node
}
kref_init(&np->kref);
}

/* process properties */
/*分析设备的属性*/
while (1) {
u32 sz, noff;
char *pname;

tag = be32_to_cpup((__be32 *)(*p));
if (tag == OF_DT_NOP) { //空属性,则跳过
*p += 4;
continue;
}

/* tag不是属性则退出,对于有孩子节点退出时为OF_DT_BEGIN_NODE;
* 对于叶子节点退出时为OF_DT_END_NODE.
*/
if (tag != OF_DT_PROP)
break;
*p += 4; //地址加4
sz = be32_to_cpup((__be32 *)(*p)); //属性的大小,是以为占多少整形指针计算的。例如网卡interrupts = <35 2 36 2 40 2>;,其值为24
noff = be32_to_cpup((__be32 *)((*p) + 4));
*p += 8; //指向value
if (be32_to_cpu(blob->version) < 0x10)
*p = ALIGN(*p, sz >= 8 ? 8 : 4);

pname = of_fdt_get_string(blob, noff); //属性名称
if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
}
if (strcmp(pname, "name") == 0) //如果有名称为name的属性
has_name = 1;
l = strlen(pname) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property)); //分配属性结构
if (allnextpp) {
/* 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. */
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
np->phandle = be32_to_cpup((__be32*)*p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
np->phandle = be32_to_cpup((__be32 *)*p);
pp->name = pname; //属性名
pp->length = sz; //长度
pp->value = (void *)*p; //属性值
*prev_pp = pp; //属性插入属性链表
prev_pp = &pp->next;
}
*p = ALIGN((*p) + sz, 4); //指向下一个属性
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
if (!has_name) { //如果没有name,就创建一个
char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;

while (*p1) {
if ((*p1) == '@')
pa = p1;
if ((*p1) == '/')
ps = p1 + 1;
p1++;
}
if (pa < ps)
pa = p1;
sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property));
if (allnextpp) {
pp->name = "name";
pp->length = sz;
pp->value = pp + 1;
*prev_pp = pp;
prev_pp = &pp->next;
memcpy(pp->value, ps, sz - 1); //名字的值只去其中的@钱的字符,例如pcie@ffe0a000-> pcie
((char *)pp->value)[sz - 1] = 0;
pr_debug("fixed up name for %s -> %s\n", pathp,
(char *)pp->value);
}
}
if (allnextpp) { //如果有属性链表
*prev_pp = NULL;
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>";
}
while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {//对于tag为这2个取值
if (tag == OF_DT_NOP) //空属性则指向下个属性
*p += 4;
else
mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
fpsize); //OF_DT_BEGIN_NODE则表明其还有子节点,所以递归分析其子节点
tag = be32_to_cpup((__be32 *)(*p));
}
if (tag != OF_DT_END_NODE) { //对于叶子节点或者分析完成
pr_err("Weird tag at end of node: %x\n", tag);
return mem;
}
*p += 4;
return mem;
}