轉載於 :http://www.voidcn.com/blog/bin_linux96/article/p-1210202.html
/** * 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; }