设备树学习(二十三、aliases节点分析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88958098

 在设备树中有一个叫做aliases的节点:


/ {
	#address-cells = <1>;
	#size-cells = <1>;

	aliases {
		csis0 = &csis0;
		fimc0 = &fimc0;
		fimc1 = &fimc1;
		fimc2 = &fimc2;
		i2c0 = &i2c0;
		i2c1 = &i2c1;
		i2c2 = &i2c2;
		i2s0 = &i2s0;
		i2s1 = &i2s1;
		i2s2 = &i2s2;
		pinctrl0 = &pinctrl0;
		spi0 = &spi0;
		spi1 = &spi1;
	};
    
    ......
};

对这个dtb文件反编译后的信息如下

dtc -I dtb -O dts -o s5pv210-x210_temp.dts s5pv210-x210.dtb 
/ {
        #address-cells = <0x1>;
        #size-cells = <0x1>;

        aliases {
                csis0 = "/soc/camera/csis@fa600000";
                fimc0 = "/soc/camera/fimc@fb200000";
                fimc1 = "/soc/camera/fimc@fb300000";
                fimc2 = "/soc/camera/fimc@fb400000";
                i2c0 = "/soc/i2c@e1800000";
                i2c1 = "/soc/i2c@fab00000";
                i2c2 = "/soc/i2c@e1a00000";
                i2s0 = "/soc/audio-subsystem/i2s@eee30000";
                i2s1 = "/soc/i2s@e2100000";
                i2s2 = "/soc/i2s@e2a00000";
                pinctrl0 = "/soc/pinctrl@e0200000";
                spi0 = "/soc/spi@e1300000";
                spi1 = "/soc/spi@e1400000";
        };

};

在Linux内核启动的时候会解析这个节点:

start_kernel
    setup_arch
        unflatten_device_tree
            of_alias_scan
/**
 * 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)
{
	__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();
}

/**
 * of_alias_scan - Scan all properties of the 'aliases' node
 *
 * The function scans all the properties of the 'aliases' node and populates
 * the global lookup table with the properties.  It returns the
 * number of alias properties found, or an error code in case of failure.
 *
 * @dt_alloc:	An allocator that provides a virtual address to memory
 *		for storing the resulting tree
 */
void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
	struct property *pp;

    /* 找到/aliases节点对应的device_node */
	of_aliases = of_find_node_by_path("/aliases");

    /*  找到/chosen节点对应的device_node */
	of_chosen = of_find_node_by_path("/chosen");

    /* 如果没有/chosen的话,就找/chosen@0节点 */
	if (of_chosen == NULL)
		of_chosen = of_find_node_by_path("/chosen@0");

	if (of_chosen) {
		/* linux,stdout-path and /aliases/stdout are for legacy compatibility */
		const char *name = NULL;

		if (of_property_read_string(of_chosen, "stdout-path", &name))
			of_property_read_string(of_chosen, "linux,stdout-path",
						&name);
		if (IS_ENABLED(CONFIG_PPC) && !name)
			of_property_read_string(of_aliases, "stdout", &name);
		if (name)
			of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
	}

	if (!of_aliases)
		return;

    /* 遍历/aliases节点的属性 */
	for_each_property_of_node(of_aliases, pp) {
		const char *start = pp->name;            // 属性的名字
		const char *end = start + strlen(start); // 名字的结尾,*end是'\0'
		struct device_node *np;
		struct alias_prop *ap;
		int id, len;

		/* Skip those we do not want to proceed */
        /* 忽略名字是name、phandle、linux,phandle的属性 */
		if (!strcmp(pp->name, "name") ||
		    !strcmp(pp->name, "phandle") ||
		    !strcmp(pp->name, "linux,phandle"))
			continue;

        /* 根据属性的值获得这个值对应的节点 */
        /* 比如这个这个属性,fimc2 = "/soc/camera/fimc@fb400000"通过路径来找到节点 */
		np = of_find_node_by_path(pp->value);
		if (!np)
			continue;

		/* walk the alias backwards to extract the id and work out
		 * the 'stem' string */
        /* 正常一个属性名字是fimc1,i2c0,这样,这里是把end指针指向数字前面那一个位置,
         * 比如fimc2的c字符的地址 */
		while (isdigit(*(end-1)) && end > start)
			end--;
		len = end - start;    /* 这个len就是名字除去数字的长度 */

        /* 把end字符串后面的数字,由字符串转换成数字复制给id,比如fimc2的,数子2 */
		if (kstrtoint(end, 10, &id) < 0)        
			continue;

		/* Allocate an alias_prop with enough space for the stem */
        /* 分配内存,多分配的"len+1"用于存放stem的名字 */
		ap = dt_alloc(sizeof(*ap) + len + 1, __alignof__(*ap));
		if (!ap)
			continue;
		memset(ap, 0, sizeof(*ap) + len + 1);
		ap->alias = start;        /*  ap->alias指向字符串"fimc2 */
		of_alias_add(ap, np, id, start, len);
	}
}
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
			 int id, const char *stem, int stem_len)
{
	ap->np = np;        // np是"/soc/camera/fimc@fb400000";"对应的节点device_node
	ap->id = id;        // id的值是2,代表设备序号,使用的时候要用到

    //由于stem_len是名字除去数字的长度,所以ap->stem被赋值为"fimc"
	strncpy(ap->stem, stem, stem_len);    
	ap->stem[stem_len] = 0;
	list_add_tail(&ap->link, &aliases_lookup);    //将这个ap加入到全局aliases_lookup链表中
	pr_debug("adding DT alias:%s: stem=%s id=%i node=%pOF\n",
		 ap->alias, ap->stem, ap->id, np);
}

使用:

假设在i2c总选注册为时

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	struct device *dev = &adapter->dev;
	int id;

	if (dev->of_node) {
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
			adapter->nr = id;
			return __i2c_add_numbered_adapter(adapter);
		}
	}

	mutex_lock(&core_lock);
	id = idr_alloc(&i2c_adapter_idr, adapter,
		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
	mutex_unlock(&core_lock);
	if (WARN(id < 0, "couldn't get idr"))
		return id;

	adapter->nr = id;

	return i2c_register_adapter(adapter);
}

id = of_alias_get_id(dev->of_node, "i2c");

调用of_alias_get_id获得与这个device_node对应的alias_prop的id。如果以 i2c2 = "/soc/i2c@e1a00000";节点为例,这里得到的id就是2。


/**
 * of_alias_get_id - Get alias id for the given device_node
 * @np:		Pointer to the given device_node
 * @stem:	Alias stem of the given device_node
 *
 * The function travels the lookup table to get the alias id for the given
 * device_node and alias stem.  It returns the alias id if found.
 */
int of_alias_get_id(struct device_node *np, const char *stem)
{
	struct alias_prop *app;
	int id = -ENODEV;

	mutex_lock(&of_mutex);
    /* 遍历全局链表aliases_lookup */
	list_for_each_entry(app, &aliases_lookup, link) {

        /* 找到 stem 是 "i2c" 的alias_prop */
		if (strcmp(app->stem, stem) != 0)
			continue;

        /* 所有的i2c控制的stem都叫"i2c",所以要保证设备节点一致 */
		if (np == app->np) {
			id = app->id;        /* 最终返回找到的编号名字 */
			break;
		}
	}
	mutex_unlock(&of_mutex);

	return id;
}

從上面的分析就可以知道alias節點的作用了:

比如SoC上有如果多個i2c控制器,alias的相当于給每個i2c控制器分配一個唯一的編號,如上面的i2c@13880000對應的alias是i2c2,那麼這個編號就是2,將來就可以在/dev下看到名爲i2c-2的設備節點。

在內核中可以看到很多地方都會調用of_alias_get_id,他的作用就是根據傳入的device node,在alias中找到對應的唯一編號,如:

of_alias_get_id(np, "serial");

of_alias_get_id(np, "ssc");

of_alias_get_id(dev->of_node, "rtc");

of_alias_get_id(dev->of_node, "fimc");

of_alias_get_id(pdev->dev.of_node, "gpio");

of_alias_get_id(pdev->dev.of_node, "spi");

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/88958098