aliases节点解析

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

 / {
     ... ...
  
     chosen {
         stdout-path = "/serial@13800000";
         bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200         
         ethmac=1C:6F:65:34:51:7E init=/linuxrc";
     };
  
     aliases {
         spi0 = "/spi@13920000";
         spi1 = "/spi@13930000";
         spi2 = "/spi@13940000";
         i2c0 = "/i2c@13860000";
         i2c1 = "/i2c@13870000";
         i2c2 = "/i2c@13880000";
         i2c3 = "/i2c@13890000";
         ... ...
     };
 ... ...
 };

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

start_kernel
    ---> setup_arch
            ---> unflatten_device_tree
                    ---> of_alias_scan

在of_alias_scan中会扫描这个节点:

of_alias_scan:

void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
    struct property *pp;
 
    of_aliases = of_find_node_by_path("/aliases"); // 找到/aliases节点对应的device_node
    of_chosen = of_find_node_by_path("/chosen"); // 找到/chosen节点对应的device_node
    if (of_chosen == NULL) // 如果没有/chosen的话,就找/chosen@0节点
        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 = of_get_property(of_chosen, "stdout-path", NULL);
        if (!name)
            name = of_get_property(of_chosen, "linux,stdout-path", NULL);
        if (IS_ENABLED(CONFIG_PPC) && !name)
            name = of_get_property(of_aliases, "stdout", NULL);
        if (name)
            of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
    }
 
    if (!of_aliases)
        return;
 
    for_each_property_of_node(of_aliases, pp) { // 遍历/aliases节点的属性,以属性i2c2 = "/i2c@13880000";为例
        const char *start = pp->name; // 属性的名字,如"i2c2"
        const char *end = start + strlen(start); // 名字的结尾,*end是'\0'
        struct device_node *np;
        struct alias_prop *ap;
        int id, len;
 
        /* 不处理名字是name、phandle、linux,phandle的属性 */
        if (!strcmp(pp->name, "name") || 
            !strcmp(pp->name, "phandle") ||
            !strcmp(pp->name, "linux,phandle"))
            continue;
 
        np = of_find_node_by_path(pp->value); 
        /*
            根据属性的值(如"/i2c@13880000")获得这个值对应的节点
            i2c@13880000 {
                #address-cells = <0x1>;
                #size-cells = <0x0>;
                compatible = "samsung,s3c2440-i2c";
                reg = <0x13880000 0x100>;
                interrupts = <0x0 0x3c 0x0>;
                clocks = <0x7 0x13f>;
                clock-names = "i2c";
                pinctrl-names = "default";
                pinctrl-0 = <0x22>;
                status = "disabled";
            };        
        */
        if (!np)
            continue;
 
        /* walk the alias backwards to extract the id and work out
         * the 'stem' string */
        while (isdigit(*(end-1)) && end > start) //对于"i2c2",end最终会指向字符'2'的地址
            end--;
        len = end - start; // 获得"i2c"的长度(不包含结尾的数字2),就是3
 
        if (kstrtoint(end, 10, &id) < 0) // 将end指向的字符'2'转化为数字2,赋值给id
            continue;
 
        /* Allocate an alias_prop with enough space for the stem */
        ap = dt_alloc(sizeof(*ap) + len + 1, 4); // 分配内存,多分配的"len+1"用于存放stem的名字
        if (!ap)
            continue;
        memset(ap, 0, sizeof(*ap) + len + 1);
        ap->alias = start; // ap->alias指向字符串"i2c2"
        of_alias_add(ap, np, id, start, len);
    }
}

of_alias_add:

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是"/i2c@13880000"对应的节点device_node
     ap->id = id; // id的值是2
     strncpy(ap->stem, stem, stem_len); // 由于stem_len是3,所以ap->stem被赋值为"i2c"
     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=%s\n",
          ap->alias, ap->stem, ap->id, of_node_full_name(np));
 }

使用:

在drivers/i2c/i2c-core.c中:

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);
        }
    }
    ... ...
}

第7行调用of_alias_get_id获得与这个device_node(即/i2c@13880000节点)对应的alias_prop的id,如果以/i2c@13880000节点为例,这里得到的id就是2。

of_alias_get_id:

 int of_alias_get_id(struct device_node *np, const char *stem)
 {
     struct alias_prop *app;
     int id = -ENODEV;
  
     mutex_lock(&of_mutex);
     list_for_each_entry(app, &aliases_lookup, link) { // 遍历全局链表aliases_lookup
         if (strcmp(app->stem, stem) != 0) // 找到 stem 是 "i2c" 的alias_prop
             continue;
  
         if (np == app->np) { // 判断这个alias_prop指向的device_node是不是跟传入的匹配
             id = app->id;  // 获得 id,2
             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(pdev->dev.of_node, "spi")

of_alias_get_id(node, "fimc")

of_alias_get_id(pdev->dev.of_node, "serial")

of_alias_get_id(pdev->dev.of_node, "uart")

of_alias_get_id(dev->of_node, "gpio")

... ...

完。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值