赞
踩
给设备树动态增加一个gpio节点,开发测试相关的驱动,并且不重新打包系统镜像,而是采用overlay热更新设备树的方式。
/dts-v1/; /plugin/; / { fragment@0 { target-path = "/"; __overlay__ { tskeys { compatible = "dxl,keys"; ts_led { gpios = <&pio 0 7 0>; }; }; }; }; };
target-path
是定位到“/”跟节点,同样也可以使用taget = &xxx
来定位到引用节点;dtc -I dts -O dtb ./ts.dts > ts.dtbo
cp ts.dtbo /lib/firmware/
/lib/firmware/
目录,是overlay机制默认搜索路径之一!dabo_t *dt_apply_by_overlay(const char *pdev_name, const char *dtbo_path) { // dabo_t *pdabo; int ret; struct platform_device *pdev = NULL; const struct firmware *fw; int ovcs_id = 0; /**创建虚拟设备 */ pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_NONE); if (IS_ERR(pdev)) { pr_err("创建虚拟设备 %s 失败!\n", pdev_name); return ERR_CAST(pdev); } ret = platform_device_add(pdev); if (ret) { pr_err("Failed to invoke platform_device_add\n"); goto _err_0; } /**从用户空间请求DTBO */ ret = request_firmware(&fw, dtbo_path, &pdev->dev); if (ret) { pr_err("Failed to invoke request_firmware\n"); goto _err_1; } /**合并设备树 */ ret = of_overlay_fdt_apply(fw->data, fw->size, &ovcs_id); release_firmware(fw); if (ret) { pr_err("Failed to invoke of_overlay_fdt_apply\n"); goto _err_1; } dabo_t *pdabo = kzalloc(sizeof(dabo_t), GFP_KERNEL); if (IS_ERR(pdabo)) { goto _err_2; } pdabo->pdev = pdev; pdabo->ovcs_id = ovcs_id; return pdabo; _err_2: of_overlay_remove(&ovcs_id); _err_1: platform_device_del(pdev); _err_0: platform_device_put(pdev); return ERR_PTR(ret); }
request_firmware
加载/lib/firmware/xx.dtbo
overlay设备树文件of_overlay_fdt_apply
将其合并到内核主设备树结构中与此对应,还要有相关的资源释放函数:
void dt_remove(dabo_t *dabo)
{
of_overlay_remove(&dabo->ovcs_id);
platform_device_del(dabo->pdev);
platform_device_put(dabo->pdev);
kfree(dabo);
}
在驱动入口函数中先调用刚写的热加载函数,然后再注册平台驱动
static int __init test_key_init(void) { int ret = 0; gd = ERR_PTR(-EBUSY); /**overlay合并设备树 */ dabo = dt_apply_by_overlay("tskey_v", "ts.dtbo"); if (IS_ERR(dabo)) { pr_err("Failed to invoke dt_apply_by_overlay\n"); return PTR_ERR(dabo); } /**平台驱动 */ ret = platform_driver_register(&pdrv); if (ret) { pr_err("Failed to invoke platform_driver_register\n"); return ret; } pr_info("!done!\n"); return 0; }
dt_apply_by_overlay
调用之后,通过overlay机制,ts.dtbo就被合并到主设备树了!static int _probe(struct platform_device *pdev) { struct fwnode_handle *child; pr_info("probe..\n"); /**正常获取gpio信息 */ device_for_each_child_node(&pdev->dev, child) { // 这里就正常去处理热更新增加的gpio节点了!! gd = devm_fwnode_get_gpiod_from_child(&pdev->dev, NULL, child, GPIOD_OUT_LOW, "label_ts"); if (IS_ERR(gd)) { dev_err(&pdev->dev, "devm_fwnode_get_gpiod_from_child failed! rc: %d\n", PTR_ERR(gd)); return PTR_ERR(gd); } full_name = of_node_full_name(to_of_node(child)); break; } /**注册file_operations结构体 */ static struct file_operations fop = { .owner = THIS_MODULE, .open = _open, .write = _write, .read = _read, }; major = register_chrdev(0, "tskey", &fop); if (major <= 0) { dev_err(&pdev->dev, "注册chrdev失败!\n"); gpiod_put(gd); return major; } /**创建class */ _cls = class_create(THIS_MODULE, "tskeys"); /**创建char节点 */ _dev = device_create(_cls, NULL, MKDEV(major, 0), NULL, full_name); // 等等操作..... return 0; }
static void __exit test_key_exit(void)
{
dt_remove(dabo); // 这里使用我们实现的释放函数!
platform_driver_unregister(&pdrv);
pr_info("exit!\n");
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。