小白技巧:使用overlay机制热更新设备树进行驱动调试

目标

给设备树动态增加一个gpio节点,开发测试相关的驱动,并且不重新打包系统镜像,而是采用overlay热更新设备树的方式。

先写个用作overlay的设备树

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target-path = "/";
        __overlay__ {
            tskeys {
                compatible = "dxl,keys";
                ts_led {
                    gpios = <&pio 0 7 0>;
                };
            };
        };
    };
};
  • target-path是定位到“/”跟节点,同样也可以使用taget = &xxx来定位到引用节点;
  • 设置了匹配符还有GPIO引脚信息。

将设备树编译成dtbo

dtc -I dts -O dtb ./ts.dts > ts.dtbo

将ts.dtbo放到设备的/lib/firmware目录下

cp ts.dtbo /lib/firmware/
  • /lib/firmware/目录,是overlay机制默认搜索路径之一!

编写个热记载设备树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.dtbooverlay设备树文件
  • 使用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);
}

使用它

  1. 在驱动入口函数中先调用刚写的热加载函数,然后再注册平台驱动

    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就被合并到主设备树了!
    • 此后,在probe中就可以正常处理热更新的设备树节点了,比如:
      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");
      }
      

也许能帮到你~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值