当前位置:   article > 正文

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

linux overlay dtsi

目标

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

先写个用作overlay的设备树

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target-path = "/";
        __overlay__ {
            tskeys {
                compatible = "dxl,keys";
                ts_led {
                    gpios = <&pio 0 7 0>;
                };
            };
        };
    };
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • target-path是定位到“/”跟节点,同样也可以使用taget = &xxx来定位到引用节点;
  • 设置了匹配符还有GPIO引脚信息。

将设备树编译成dtbo

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

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

cp ts.dtbo /lib/firmware/
  • 1
  • /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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 使用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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

使用它

  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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 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;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
    • 最后别忘在出口函数中释放先前热加载的设备树
      static void __exit test_key_exit(void)
      {
          dt_remove(dabo); // 这里使用我们实现的释放函数!
          platform_driver_unregister(&pdrv);
          pr_info("exit!\n");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

也许能帮到你~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1005053
推荐阅读
相关标签
  

闽ICP备14008679号