设备树传参中的platform device的匹配
来源:互联网 发布:手机快速截图软件 编辑:程序博客网 时间:2024/06/01 14:20
问题
在高版本的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的匹配:
staticconst 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函数。
- 设备树传参中的platform device的匹配
- linux platform 设备与驱动的匹配
- [platform]新旧内核的device设备注册对比
- platform平台device和driver如何匹配
- linux中的platform设备
- linux中的platform设备
- 从串口驱动的移植看linux2.6内核中的驱动模型 platform device & platform driver
- 设备驱动外传 - 虚拟总线和platform device(不同于I2C的驱动编写)
- platform-device
- platform device
- Platform device
- 设备驱动外传 - 虚拟总线和platform device
- 设备驱动外传 - 虚拟总线和platform device
- 设备驱动外传 - 虚拟总线和platform device
- 设备驱动外传 - 虚拟总线和platform device
- 设备驱动外传 - 虚拟总线和platform device
- platform总线中device和driver的两种常用匹配方式(以mtk驱动为例)
- 对linux下platform device和Platform Driver的理解
- 常见浏览器兼容问题与解决方案
- str和repr的区别
- 不要随意删除系统自带的python
- WebService到底是什么?何时应该用?
- 小组的晋级球队
- 设备树传参中的platform device的匹配
- vue开发的一个后台系统
- java 加密
- 并查集(入门篇)
- Android7.0检查权限
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- new和malloc的区别
- 2017 ACM-ICPC 亚洲区(西安赛区)网络赛 Xor
- Oracle第一次测试分析