设备树传参中的platform device的匹配

设备树传参中的platform device的匹配在高版本的Linux内核中使用了设备树进行传参,之前购买一块nanopi的板子使用的是linux4.11.2版本的内核(使用的友善之臂的Mainlinelinux)就是这种情况,并且使用设备树传参过后,原来硬编码在mach-xxx.c文件中的platformdevice全部都放入了设备树中,而原来使用name进行platformdevice和driver进行匹配的方式也发生了变化。以

大家好,又见面了,我是你们的朋友全栈君。

问题

在高版本的Linux内核中使用了设备树进行传参,之前购买一块nanopi的板子使用的是linux 4.11.2版本的内核(使用的友善之臂的Mainline linux)就是这种情况,并且使用设备树传参过后,原来硬编码在mach-xxx.c文件中的platform device全部都放入了设备树中,而原来使用name进行platform device和driver进行匹配的方式也发生了变化。
以nanopi中PWM驱动为例,这是它的platform driver:

static struct platform_driver sun4i_pwm_driver = {
    .driver = {
        .name = "sun4i-pwm",
        .of_match_table = sun4i_pwm_dt_ids,
    },
    .probe = sun4i_pwm_probe,
    .remove = sun4i_pwm_remove,
};

在没有设备树传参时,platform device和driver的匹配是通过名字来匹配的,也就是比较platform_driver.device_driver.name和platform_device.name,而在这里整个工程中是找不到”sun4i-pwm”的字符串的,也就是说匹配方式发生了变化。

设备树的加载

设备树文件的后缀名为.dts,一般放在 /arch/arm/boot/dts 文件夹中,对内核进行编译的时候可以使用make指令将设备树编译成二进制文件.dtb,该文件在内核启动的时候会被加载到内核当中。以我的nanopi的板子为例,要求将SD卡分为boot分区和rootfs分区,编译的.dtb文件只需放入boot分区即可,uboot在环境变量中指定该.dtb文件的名字,那么内核在加载的时候就会加载相应的设备树。内核加载设备树的过程如下:

b   start_kernel                            -->arch/arm/kernel/head-common.S
    start_kernel                            -->init/main.c
        setup_arch                          -->arch/arm/kernel/setup.c
            setup_machine_fdt               -->arch/arm/kernel/devtree.c
                early_init_dt_scan_nodes    -->drivers/of/fdt.c
                    of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);  -->(1)
                    of_scan_flat_dt(early_init_dt_scan_root, NULL);  -->(2)
                    of_scan_flat_dt(early_init_dt_scan_memory, NULL);  -->(3) 
                __machine_arch_type = mdesc->nr;    -->(4)
        unflatten_device_tree               -->drivers/of/fdt.c, (5)
            __unflatten_device_tree
                unflatten_dt_nodes          // 对设备树进行展开

以下解释引用自http://blog.csdn.net/lichengtongxiazai/article/details/38941913

(1)扫描 /chosen node,保存运行时参数(bootargs) 到boot_command_line ,此外,还处理initrd相关的property ,并保存在initrd_start 和initrd_end 这两个全局变量中
(2)扫描根节点,获取 {size,address}-cells信息,并保存在dt_root_size_cells和dt_root_addr_cells全局变量中
(3)扫描DTB中的memory node,并把相关信息保存在meminfo中,全局变量meminfo保存了系统内存相关的信息
(4)Change machine number to match the mdesc we’re using
(5)unflattens a device-tree, creating the tree of struct device_node.

device和driver的匹配

以上文章还提到

系统应该会根据Device tree来动态的增加系统中的platform_device(这个过程并非只发生在platform bus上,也可能发生在其他的非即插即用的bus上,例如AMBA总线、PCI总线)。 如果要并入linux kernel的设备驱动模型,那么就需要根据device_node的树状结构(root是of_allnodes)将一个个的device node挂入到相应的总线device链表中。只要做到这一点,总线机制就会安排device和driver的约会。当然,也不是所有的device node都会挂入bus上的设备链表,比如cpus node,memory node,choose node等。

可以看到,一些设备树的device_node最终是会变为device挂载到总线上的,而这时就可以和driver进行配对了。在platform 总线中也是如此,要知道device和driver是如何配对的,就要看platform 总线的定义了,platform_bus_type定义如下:

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};

因此要看device和driver是如何配对的需要看platform_match函数是如何匹配的,platform_match函数如下:

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);

    /* Attempt an OF style match first */
    // 匹配上之后直接返回,不进行下一步匹配
    if (of_driver_match_device(dev, drv))
        return 1;

    /* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
}

可以看到和2.6版本的platform_match函数不同了,该函数在最后一步才进行pdev->name, drv->name名字的匹配,而如果在之前的if中匹配上了是直接返回1,而不会进行下一步匹配的。其中第二个if就是在设备树中进行匹配,我们追进去可以看到这个函数实际做的工作:

platform_match
    of_driver_match_device      // 利用设备树的节点进行匹配
        of_match_device             --> driver/of/device.c
            of_match_node           --> driver/of/base.c
                __of_match_node

在__of_match_node函数中才进行的真正的device和driver的匹配:

static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
                       const struct device_node *node)
{
    const struct of_device_id *best_match = NULL;
    int score, best_score = 0;

    if (!matches)
        return NULL;
    // 这里和设备树的进行匹配
    for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
        score = __of_device_is_compatible(node, matches->compatible,
                          matches->type, matches->name);
        if (score > best_score) {
            best_match = matches;
            best_score = score;
        }
    }

    return best_match;
}

这里匹配的是什么呢?在这个内核内核中匹配的是matches->compatible,为了方便说明,还是以nanopi的PWM驱动为例,这里再贴一遍它的platform_driver:

static struct platform_driver sun4i_pwm_driver = {
    .driver = {
        .name = "sun4i-pwm",
        .of_match_table = sun4i_pwm_dt_ids,
    },
    .probe = sun4i_pwm_probe,
    .remove = sun4i_pwm_remove,
};

这里匹配的就是platform_driver.driver_driver.of_match_id的内容,也就是这里的of_match_table,我们再追进去看:

static const struct of_device_id sun4i_pwm_dt_ids[] = {
    {
        .compatible = "allwinner,sun4i-a10-pwm",
        .data = &sun4i_pwm_data_a10,
    }, {
        .compatible = "allwinner,sun5i-a10s-pwm",
        .data = &sun4i_pwm_data_a10s,
    }, {
        .compatible = "allwinner,sun5i-a13-pwm",
        .data = &sun4i_pwm_data_a13,
    }, {
        .compatible = "allwinner,sun7i-a20-pwm",
        .data = &sun4i_pwm_data_a20,
    }, {
        .compatible = "allwinner,sun8i-h3-pwm",
        .data = &sun4i_pwm_data_h3,
    }, {
        /* sentinel */
    },
};

这就和__of_match_node函数匹配上了,该函数最终使用的就是platform_driver.driver_driver.of_device_id->compatible和设备树中的compatible进行比较,再工程中使用全局搜索”allwinner,sun8i-h3-pwm”(因为nanopi使用的是全志h3),可以找到对应的设备树节点在sunxi-h3-h5.dtsi中:

pwm: pwm@01c21400 {
            compatible = "allwinner,sun8i-h3-pwm";
            reg = <0x01c21400 0x8>;
            clocks = <&osc24M>;
            #pwm-cells = <3>;
            status = "disabled";
        };

总结

使用设备树传参时的platform device已经没有硬编码再内核当中了,而是使用一个设备树文件,将所有板级相关信息写在了里面,在platform总线进行匹配时,使用的是platform_driver.driver_driver.of_device_id->compatible和设备树中的compatible进行匹配,所以想要添加驱动,只需要在设备树文件中添加节点,指定device的compatible,然后在代码中再指定driver的compatible即可完成匹配,执行probe函数。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163818.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 5-去掉a标签下划线,禁止a标签的跳转「建议收藏」

    5-去掉a标签下划线,禁止a标签的跳转「建议收藏」1.去下划线:  写样式,a{text-decoration:none;  或在a标签内联里面写style=”text-decoration:none;”;2.禁用a标签跳转:a标签href不跳转禁止跳转当页面中a标签不需要任何跳转时,从原理上来讲,可分如下两种方法:标签属性href,使其指向空或不返回任何内容。如:<ahref=”java…

  • java编程常用软件

    java编程常用软件有大神曾说“给我一个记事本,我还你一个项目”,作为小白的我,以前也对这句话深信不疑,但当我参加人生第一次编程考试的时候,我发现我用记事本码代码的速度实在是太慢了,一样的代码,别人用eclipseIED编写用了5分钟,而我至少半小时。虽然有点强行甩锅IDE的嫌疑,但有款好的编程软件,就会让你打代码速度更快,让你的头发掉的更少……废话讲完了,以下是我推荐的几款编程常用软件:…

  • Gamma校正_显示器伽马值调多少

    Gamma校正_显示器伽马值调多少介绍伽马校正的由来,作用以及实践

  • goland激活码永久(注册激活)

    (goland激活码永久)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html09LVN3XLKC-eyJsaWNlbnNlSWQi…

  • 计算机如何修改任务管理器,win7如何更改任务管理器快捷键_win7更改任务管理器快捷键的教程…

    计算机如何修改任务管理器,win7如何更改任务管理器快捷键_win7更改任务管理器快捷键的教程…我们在打开任务管理器的时候,通常是CTRL+ALT+DEL就可以快速打开,不过有许多用户装完win7系统之后,发现任务管理器快捷键变成了Ctrl+Shift+Esc,这让用户们用着很不习惯,其实我们也可以自己手动更改快捷键,现在给大家带来win7更改任务管理器快捷键的教程。具体步骤如下:1、在“开始”菜单的搜索框输入指令gpedit.msc,回车打开Win7系统的组策略编辑器。2、在组策略编辑器里…

  • python 内置函数详解

    python 内置函数详解

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号