【鸿蒙南向开发】OpenHarmony HDF 配置管理分析及使用

199 篇文章 0 订阅
198 篇文章 0 订阅

前言

HCS(HDF Configuration Source)是 HDF 驱动框架的配置描述源码,内容以 Key-Value 为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是 HCS 配置转换工具,可以将 HDF 配置文件转换为软件可读取的文件格式:

  • 在弱性能环境中,转换为配置树源码,驱动可直接调用 C 代码获取配置。

  • 在高性能环境中,转换为 HCB(HDF Configuration Binary)二进制文件,驱动可使用 HDF 框架提供的配置解析接口获取配置。

以下是使用 HCB 模式的典型应用场景:

image.png

HCS 经过 HC-GEN 编译生成 HCB 文件,HDF 驱动框架中的 HCS Parser 模块会从 HCB 文件中重建配置树,HDF 驱动模块使用 HCS Parser 提供的配置读取接口获取配置内容。

image.png

HCS 文本更适合人类阅读,但是并不方便程序直接存取,所以经过 HC-GEN 编译,输出二进制的 HCB 数据。HCB 在编译后打包进内核镜像的.rodata 只读分区,在启动加载时,框架定位到 HCB 数据头,再将二进制数据重新构造为树形数据结构供驱动查询和读取。下面详细分析 HC-GEN 实现。

HCS源码语法

HCS 的语法介绍如下:关键字
HCS 配置语法保留了以下关键字。

image.png

基本结构HCS 主要分为属性(Attribute)和节点(Node)两种结构。

  • 属性

    属性即最小的配置单元,是一个独立的配置项。语法如下:

1.  node_name {
2.        module = "sample";
3.        ...
4.  }

attribute_name 是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。value 的可用格式如下:

  • 数字常量,支持二进制、八进制、十进制、十六进制数,具体参考数据类型节。

  • 字符串,内容使用双引号(“”)引用。

  • 节点引用。

  • attribute 必须以分号(;)结束且必须属于一个 node。

  • 节点

    节点是一组属性的集合,语法如下:

attr_foo = [0x01, 0x02, 0x03, 0x04];
attr_bar = ["hello", "world"];
  • node_name 是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。

  • 大括号后无需添加结束符“;”。

  • root为保留关键字,用于声明配置表的根节点。每个配置表必须以 root 节点开始。

  • root 节点中必须包含 module 属性,其值应该为一个字符串,用于表征该配置所属模块。

  • 节点中可以增加 match_attr 属性,其值为一个全局唯一的字符串。在解析配置时可以调用查找接口以该属性的值查找到包含该属性的节点。

数据类型

  • 整型

整型长度自动推断,根据实际数据长度给与最小空间占用的类型。

  • 二进制,0b 前缀,示例:0b1010。

  • 八进制,0 前缀,示例:0664。

  • 十进制 ,无前缀,且支持有符号与无符号,示例:1024,+1024 均合法。负值在读取时注意使用有符号数读取接口。

  • 十六进制,0x 前缀,示例:0xff00、0xFF。

  • 字符串

字符串使用双引号(“”)表示。

  • 数组

数组元素支持整型、字符串,不支持混合类型。整型数组中 uint32_t uint64_t 混用会向上转型为 uint64_t 数组。整型数组与字符串数组示例如下:

attr_foo = [0x01, 0x02, 0x03, 0x04];
attr_bar = ["hello", "world"];
  • bool类型

bool 类型中 true 表示真,false 表示假。注释

HCS 支持两种注释风格。

单行注释:

1.  // comment

多行注释:


2.  /*
3.  comment
4.  */

其他语法

  • 模板

模板的用途在于生成严格一致的 node 结构,以便对同类型 node 进行遍历和管理。

使用 template 关键字定义模板 node,子 node 通过双冒号“::”声明继承关系。子节点可以改写但不能新增和删除 template 中的属性,子节点中没有定义的属性将使用 template 中的定义作为默认值。示例如下:

root {
    module = "sample";
    template foo {
        attr_1 = 0x1;
        attr_2 = 0x2;
    }
    bar :: foo {
    }
    bar_1 :: foo {
        attr_1 = 0x2;
    }
}

生成配置树如下:

root {
    module = "sample";
    bar {
        attr_1 = 0x1;
        attr_2 = 0x2;
    }
    bar_1 {
        attr_1 = 0x2;
        attr_2 = 0x2;
    }
}

在上述示例中,bar 和 bar_1 节点继承了 foo 节点,生成配置树节点结构与 foo 保持了完全一致,只是属性的值不同。

  • 引用修改

引用修改可以实现修改另外任意一个节点的内容,语法为:

node :& source_node

上述语句表示 node 中的内容是对 source_node 节点内容的修改。示例如下:

root {
    module = "sample";
    foo {
        foo_ :& root.bar{
            attr = "foo";
        }
        foo1 :& foo2 {
            attr = 0x2;
        }
        foo2 {
            attr = 0x1;
        }
    }

    bar {
        attr = "bar";
    }
}

最终生成配置树为:

root {
    module = "sample";
    foo {
        foo2 {
            attr = 0x2;
        }
    }
    bar {
        attr = "foo";
    }
}

在以上示例中,可以看到 foo.foo_节点通过引用将 bar.attr 属性的值修改为了"foo",foo.foo1 节点通过引用将 foo.foo2.attr 属性的值修改为了 0x2。foo.foo_以及 foo.foo1 节点表示对目标节点内容的修改,其自身并不会存在最终生成的配置树中。

  • 引用同级 node,可以直接使用 node 名称,否则被引用的节点必须使用绝对路径,节点间使用“.”分隔,root 表示根节点,格式为 root 开始的节点路径序列,例如 root.foo.bar 即为一个合法的绝对路径。
  • 如果出现修改冲突(即多处修改同一个属性),编译器将提示 warning,因为这种情况下只会生效某一个修改而导致最终结果不确定。

节点复制

节点复制可以实现在节点定义时从另一个节点先复制内容,用于定义内容相似的节点。语法为:

node : source_node

上述语句表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。示例如下:


root {
    module = "sample";
    foo {
        attr_0 = 0x0;
    }
    bar:foo {
        attr_1 = 0x1;
    }
}

上述代码的最终生成配置树为:

root {
    module = "sample";
    foo {
        attr_0 = 0x0;
    }
    bar {
        attr_1 = 0x1;
        attr_0 = 0x0;
    }
}

在上述示例中,编译后 bar 节点即包含 attr_0 属性也包含 attr_1 属性,在 bar 中对 attr_0 的修改不会影响到 foo。

在 foo 和 bar 在同级 node 中可不指定 foo 的路径,否则需要使用绝对路径引用。

删除

要对 include 导入的 base 配置树中不需要的节点或属性进行删除,可以使用 delete 关键字。下面的举例中 sample1.hcs 通过 include 导入了 sample2.hcs 中的配置内容,并使用 delete 删除了 sample2.hcs 中的 attribute2 属性和 foo_2 节点,示例如下:

root {
    module = "sample";
    foo {
        attr_0 = 0x0;
    }
    bar {
        attr_1 = 0x1;
        attr_0 = 0x0;
    }
}

属性引用

为了在解析配置时快速定位到关联的节点,可以把节点作为属性的右值,通过读取属性查找到对应节点。语法为:

1\.  attribute = &node;
HCB二进制格式

HCB 为便于程序读取的 HCS 的二进制数据格式,按照下面的编码表进行数据组织:

image.png

以一个示例分析下 HCS 源码和 HCB 的对应关系:


root {
    module = "sample";
    gpio = [1, 2];
}

上述 HCS 编译后的 HCB 数据如下:

image.png

HC-GEN使用介绍

hc-gen 是 HCS 的编译器,用于在编译时将 HCS 转化为 HCB,也可以将 HCB 反编译为 HCS 以验证配置数据的正确性,这在驱动调试时将很有帮助。

hc-gen v0.7 之前版本作为 prebuilt 文件以二进制下载方式提供。0.7 版本开始,为了更好的支持多环境部署和版本管理,hc-gen 在编译过程中从源码构建。如果调试需要,可以在 OpenHarmony 源码的 drivers/framework/tools/hc-gen 下执行 make 生成,生成产物在该目录的 build 子目录中。

cd drivers/framework/tools/hc-gen
make

./build/hc-gen –v
> Hcs compiler 0.7

驱动开发过程中,在 hcs 配置文件修改后,可以手动使用 hc-gen 快速验证配置的正确性,生成 HCB 配置文件方法:

hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]

在驱动调试时,可以使用 hc-gen 反编译 HCB 文件获得 HCS 源码,进行配置数据核对。反编译 HCB 文件为 HCS 方法:

hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
HCS文件编译过程

在 linux 内核中,HCS 编译基于 KBuild 自定义规则实现自主的编译过程,Makefile 入口在 drivers/adapter/khdf/linux/hcs/Makefile。


HC_GEN_DIR := $(abspath $(SOURCE_ROOT)/drivers/framework/tools/hc-gen)
HC_GEN := $(HC_GEN_DIR)/build/hc-gen
LOCAL_HCS_ROOT := $(abspath $(dir $(realpath $(lastword $(MAKEFILE_LIST)))))

# LOCAL_HCS_ROOT为根据目标平台和产品拼接出的HCS路径
HCS_DIR := $(LOCAL_HCS_ROOT)
HCB_FLAGS := -b -i -a

HCS_OBJ := hdf_hcs_hex.o
HCS_OBJ_SRC := $(subst .o,.c,$(notdir $(HCS_OBJ)))

CONFIG_GEN_HEX_SRC := $(addprefix $(LOCAL_HCS_ROOT)/, $(HCS_OBJ_SRC))
CONFIG_HCS_SRC := $(subst _hcs_hex.o,.hcs,$(addprefix $(HCS_DIR)/, $(HCS_OBJ)))

# 使用自定义的.o生成规则覆盖KBbuild默认规则
$(obj)/$(HCS_OBJ): $(CONFIG_GEN_HEX_SRC) 
        $(Q)$(CC) $(c_flags) -c -o $@ $<
        $(Q)rm -f $<

# 将HCB文件生成后再转换为.c文件中的hex数组,依赖目标为hc-gen工具
$(CONFIG_GEN_HEX_SRC):  $(LOCAL_HCS_ROOT)/%_hcs_hex.c: $(HCS_DIR)/%.hcs | $(HC_GEN)
        $(Q)echo gen hdf built-in config
        $(Q)if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
        $(Q)$(HC_GEN) $(HCB_FLAGS) -o $(subst _hex.c,,$(@)) $<

# 生成hc-gen工具
$(HC_GEN): 
        $(Q)make -C $(HC_GEN_DIR)

obj-$(CONFIG_DRIVERS_HDF) += $(HCS_OBJ)

在 HDF 适配其他平台时,可以复用该 Makefile,核心变化点在正确配置对应平台的 HCS 根路径。

HCS配置使用

HCS配置读取接口

在驱动实现中,可以使用 device_resource_if.h 中定义的接口对配置进行查询和读取。常用 API 介绍如下:

image.png

配置读取接口使用实例以 UART 控制器驱动为例看 HCS 的使用。UART 在 HCS 中 device_info.hcs 中配置的设备信息为:

device_uart :: device {
    device0 :: deviceNode {
        policy = 1;
        priority = 40;
        permission = 0644;
        moduleName = "HDF_PLATFORM_UART";
        serviceName = "HDF_PLATFORM_UART_0";
        deviceMatchAttr = "hisilicon_hi35xx_uart_0";
    }
}

在 hi35xx_uart_config.hcs 中配置如下:


root {
    platform {
        uart_config {
            device_uart_0x0000  {
                serviceName = "";
                match_attr = "hisilicon_hi35xx_uart_0";
                driver_name = "ttyAMA";
                num = 0;
            }
        }
    }
}

注意到 UART 使用了 match_attr 特性,这样驱动框架在 device_uart 设备加载时将自动将 device_uart_0x0000 节点关联到该设备。UART 的配置解析代码如下:


static int32_t HdfUartInit(struct HdfDeviceObject *obj)
{
    int32_t ret;
    struct DeviceResourceIface *iface = NULL;
    …
    devResourceIface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    if (devResourceIface == NULL) {
        HDF_LOGE("%s: face is invalid", __func__);
        return HDF_FAILURE;
    }

    devResourceIface->GetUint32(obj->property, "num", &host->num, 0);
devResourceIface->GetString(obj->property, "driver_name", &drName, "ttyAMA");
……
   
    ret = memcpy_s(g_driverName, UART_NAME_LEN - 1, drName, strlen(drName));
    if (ret != EOK) {
        return HDF_FAILURE;
    }
    host->method = &g_uartHostMethod;
    return HDF_SUCCESS;
}

device_uart_0x0000 节点被自动关联到了 HdfDeviceObject 的 property 成员。使用 DeviceResourceGetIfaceInstance 接口获取到 HCS 接口实例后调用其成员方法 GetUint32 读取名为"num"的无符号值属性,使用 GetString 接口读取名为"driver_name"的字符串属性。从配置中获取到属性值后,再根据配置值完成相关软硬件的初始化。

总结

本文从全景介绍了 HCS 配置管理方案,重点分析了 HC-GEN 的实现和 HCS 的编译过程,希望对读者理解 HCS 的原理和配置方法能有所帮助。关于 HDF 驱动框架的更多分析,请关注后续文章。

写在最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。

这份鸿蒙(HarmonyOS NEXT)文档包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习文档能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

鸿蒙(HarmonyOS NEXT)5.0最新学习路线

在这里插入图片描述

有了路线图,怎么能没有学习文档呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

《鸿蒙 (OpenHarmony)开发入门教学视频》

在这里插入图片描述

《鸿蒙生态应用开发V3.0白皮书》

在这里插入图片描述

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

在这里插入图片描述

《鸿蒙开发基础》

●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……
在这里插入图片描述

《鸿蒙开发进阶》

●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●通知与窗口管理
●多媒体技术
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来设计
●鸿蒙系统移植和裁剪定制
……
在这里插入图片描述

《鸿蒙进阶实战》

●ArkTS实践
●UIAbility应用
●网络案例
……
在这里插入图片描述

获取以上完整鸿蒙HarmonyOS学习文档,请点击→纯血版全套鸿蒙HarmonyOS学习文档

openharmony hdf适配是指在openharmony操作系统中,使用hdf框架进行硬件设备的适配。hdf是一种硬件设备抽象层的开发框架,可以帮助开发者简化硬件设备的驱动开发工作。 在openharmony系统中,不同的硬件设备需要与操作系统进行交互,以实现功能的完整性。而hdf框架的作用就是通过提供一系列接口和函数,使不同硬件设备的驱动程序能够与openharmony系统进行交互和通信。 通过使用hdf框架进行适配,可以实现如下优点: 1. 硬件设备的抽象化:hdf框架提供了一系列标准化接口,可以将硬件设备的具体实现进行抽象化,使得不同的硬件设备能够使用相同的接口进行访问和控制。 2. 简化开发工作:使用hdf框架可以避免开发者直接与底层硬件进行交互,减少了开发工作的复杂性。开发者只需要通过hdf的接口函数进行调用和操作,就能够完成硬件设备的驱动开发。 3. 提高代码的可重用性:hdf框架提供了一套通用的接口,这些接口可以在不同的硬件设备之间进行共享和复用。这样一来,当需要适配新的硬件设备时,只需要实现部分适配层的代码即可,大大提高了代码的可重用性。 4. 方便维护和升级:通过使用hdf框架进行适配,系统维护和升级变得更加方便。当需要对硬件设备进行维护或者升级时,只需要修改hdf框架的适配代码,而不需要修改原始的驱动程序。 总之,openharmony hdf适配的目的在于简化硬件设备的驱动开发工作,提高代码的可重用性和系统的维护性。通过使用hdf框架,开发者可以更加方便地实现不同硬件设备的适配,为用户提供更加稳定和高效的系统体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值