许多刚接触OHOS的开发者,可能对device_info.hcs这个文件的来由去脉存在很多疑问,故将问题提取出来做以下总结,希望对刚入门OHOS的开发者有所帮助。

1、基本概念:

首先看一下开发指南官方文档对hcs的描述:

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

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

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

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

图1 配置使用流程图

img

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

2、编译流程

上面的概念对刚接触的人来说可能有些抽象。

device_info.hcs里面的具体内容还是比较好理解的,这里不做赘述,相较于内容,可能有些开发者更好奇这个文件最终编译的编译产物是什么?编译到哪里了?有编译到内核吗?下面我们详细了解一下。

首先hcs可以分成三类:

  • 1、编译入口文件:hdf.hcs

  • 2、设备节点描述文件:device_info.hcs

  • 3、驱动的私有配置文件:xxx_config.hcs

device_info.hcs及各驱动的私有配置文件xxx_config.hcs最终都会汇总到hdf.hcs文件,作为编译的入口文件,如:

//vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs

//设备节点描述文件
#include "device_info/device_info.hcs"
//驱动私有的配置信息描述文件
#include "platform/adc_config_linux.hcs"
#include "platform/pwm_config.hcs"
#include "platform/rk3568_watchdog_config.hcs"
#include "platform/rk3568_uart_config.hcs"
#include "platform/sdio_config.hcs"
#include "platform/emmc_config.hcs"
#include "platform/rk3568_spi_config.hcs"
#include "input/input_config.hcs"
#include "wifi/wlan_platform.hcs"
#include "wifi/wlan_chip_ap6275s.hcs"
#include "camera/camera_config.hcs"
#include "sensor/sensor_config.hcs"
#include "audio/audio_config.hcs"
#include "audio/codec_config.hcs"
#include "audio/dai_config.hcs"
#include "audio/dma_config.hcs"
#include "audio/dsp_config.hcs"
#include "audio/analog_headset_config.hcs"
#include "light/light_config.hcs"
#include "vibrator/vibrator_config.hcs"
#include "vibrator/linear_vibrator_config.hcs"
#include "vibrator/drv2605l_linear_vibrator_config.hcs"
#include "lcd/lcd_config.hcs"

//根节点定义
root {
    module = "rockchip,rk3568_chip";
}

查看编译脚本:

//vendor/hihope/rk3568/hdf_config/khdf/Makefile

ifeq ($(LOCAL_HCS_ROOT),)
  LOCAL_HCS_ROOT := $(PRODUCT_PATH)
endif
CURRENT_DIR := $(abspath $(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
SOURCE_ROOT := $(abspath $(CURRENT_DIR)/../../../../../)

HC_GEN_DIR := $(abspath $(SOURCE_ROOT)/drivers/hdf_core/framework/tools/hc-gen)
ifneq ($(OUT_DIR),)
HC_GEN := $(OUT_DIR)/kernel/OBJ/${KERNEL_VERSION}/drivers/hdf/khdf/hc_gen_build/hc-gen
else
HC_GEN := $(HC_GEN_DIR)/build/hc-gen
endif
LOCAL_HCS_ROOT := $(CURRENT_DIR)

HCS_DIR := $(LOCAL_HCS_ROOT)

ifneq ($(TARGET_BOARD_PLATFORM),)
  HCS_DIR := $(LOCAL_HCS_ROOT)/$(TARGET_BOARD_PLATFORM)
else
  ifneq ($(CONFIG_ARCH_HI3516DV300),)
    HCS_DIR := $(LOCAL_HCS_ROOT)
  endif
  ifneq ($(CONFIG_ARCH_HI3518EV300),)
    HCS_DIR := $(LOCAL_HCS_ROOT)
  endif
endif
$(info HCS_DIR = $(HCS_DIR))
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)))

$(obj)/$(HCS_OBJ): $(CONFIG_GEN_HEX_SRC)
	$(Q)$(CC) $(c_flags) -c -o $@ $<
	$(Q)rm -f $<

$(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,,$(@)) $<

$(CONFIG_GEN_SRCS): $(CONFIG_OUT_DIR)%.c: $(HCS_DIR)/%.hcs | $(HC_GEN)
	$(Q)echo gen hdf driver config
	$(Q)if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
	$(Q)$(HC_GEN) -t -o $@ $<

$(HC_GEN):
	$(HIDE)make -C $(HC_GEN_DIR) BUILD_DIR=$(dir $@)

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

脚本中有几个关键地方:

1、编译的入口文件:hdf.hcs

2、使用hc-gen工具将 hdf.hcs 转换为hdf_hcs_hex.c

$(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,,$(@)) $<

3、编译:生成目标文件 hdf_hcs_hex.o,最终hdf_hcs_hex.o 文件被编译进内核镜像。

$(obj)/$(HCS_OBJ): $(CONFIG_GEN_HEX_SRC)
	$(Q)$(CC) $(c_flags) -c -o $@ $<
	$(Q)rm -f $<

后面在驱动框架启动阶段会被hcs-parser工具加载和解析,还原成以g_hcsTreeRoot为根节点的树状结构,最终被驱动框架各模块所使用。

 

上面都是内核态的hcs描述,在ohos中还有用户态的hcs文件,它们的结构与内核态的相同,但不会被编译进内核,在被编译成hcb二进制文件之后,会被拷贝到指定目录被读取和使用。

import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/hcs/hcs.gni")

hdf_hcb("hdf_default.hcb") {
  source = "./hdf.hcs"
  part_name = "product_rk3568"
  subsystem_name = "product_hihope"
}

hdf_cfg("hdf_devhost.cfg") {
  source = "./hdf.hcs"
  part_name = "product_rk3568"
  subsystem_name = "product_hihope"
}

ohos_prebuilt_etc("hdf_peripheral.cfg") {
  source = "hdf_peripheral.cfg"
  relative_install_dir = "init"
  install_images = [ chipset_base_dir ]
  subsystem_name = "product_hihope"
  part_name = "product_rk3568"
}

group("hdf_config") {
  deps = [
    ":hdf_default.hcb",
    ":hdf_devhost.cfg",
    ":hdf_peripheral.cfg",
  ]
}

3、总结:

1)device_info.hcs是驱动设备描述配置文件,分为内核态和用户态;

2)device_info.hcs及各驱动的私有配置文件最终都会汇总到hdf.hcs文件,如果是内核态的会被hc-gen工具编译成hcb二进制文件然后打包成hdf_hcs_hex.o 文件编译进内核镜像,然后在驱动框架启动阶段被hcs-parser工具加载和解析,还原成以g_hcsTreeRoot为根节点的树状结构,最终被驱动框架各模块所使用;部署在用户空间的hcb文件,则会被拷贝到指定目录被读取和使用。

3)想具体了解,可以去查看对应的编译脚本(以rk3568为例):

khdf通过Makefile:https://gitee.com/openharmony/vendor_hihope/blob/master/rk3568/hdf_config/khdf/Makefile

uhdf通过Build.gn:https://gitee.com/openharmony/vendor_hihope/blob/master/rk3568/hdf_config/uhdf/BUILD.gn

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐