打开并查看 //vendor/hisilicon/hispark_taurus/config.json, 这是小型系统Hi3516开发板的产品配置表,仔细查看与驱动紧密相关的子系统和组件有:
- {
- "subsystem": "kernel",
- "components": [
- { "component": "liteos_a", "features":[] }
- ]
- },
- {
- "subsystem": "drivers",
- "components": [
- { "component": "drivers_framework", "features":[] },
- { "component": "adapter_uhdf", "features":[] },
- { "component": "peripheral_display", "features":[] },
- { "component": "peripheral_input", "features":[] },
- { "component": "peripheral_sensor", "features":[] },
- { "component": "peripheral_wlan", "features":[] }
- ]
- },
- {
- "subsystem": "vendor",
- "components": [
- { "component": "hi3516dv300_init", "features":[] },
- { "component": "hardware", "features":[] },
- { "component": "middleware", "features":[] }
- ]
- },
结合//build/lite/components/目录下对应的 json文件,列出下表:
上表包含了整个驱动子系统的编译相关内容,互相交叉,关系稍微有点复杂,我花了一点时间做了梳理,先把一些要点做一下总结:
1. //drivers/framework/ 目录是纯代码目录,不包含编译配置脚本文件,对该目录代码文件的编译配置文件都在 //drivers/adapter/目录下了,分成两部分:
- uhdf 部分是编译framework部署在用户空间的代码,见上表中adapter_uhdf部分。
- khdf 部分是编译framework部署在内核空间的代码,上表中没有体现出来,但是在编译 liteos_a:kernel 的时候,会通过kernel模块的mk去include这里的 ./khdf/liteos/目录下的mk,从而触发khdf部分的编译。
2. liteos_a:kernel 和framework的khdf部分的编译,会由 hi3516dv300_init 组件的编译来触发,最终生成了OHOS_Image*四个镜像文件。上表只做了简单地描述,可以去阅读和分析//device/hisilicon/build/hi3516dv300/ 目录下的几个编译脚本,顺着编译流程去做理解,下文会做进一步地展开分析。
3. //drivers/peripheral/ 目录下的几个模块,会被编译成对应的动态链接库文件,部署到 rootfs/usr/lib/目录下。
4. hardware和middleware两个组件,都是根据各自目录下的 BUILD.gn和build.sh脚本,对相关目录的代码进行编译,或者对子目录下的预编译库文件做选择,最终将库文件拷贝到项目输出根目录下,打包生成rootfs时,部署到rootfs/usr/lib/目录下
上面1.uhdf和3/4的编译输出,都部署在鸿蒙系统的用户空间,直接阅读编译脚本基本上就可以理解了,本文就不做详细分析了。
而1.khdf和2的编译有密切的相关性,其输出一并打包到OHOS系统镜像中,下面就来详细看一下。
从 hi3516dv300_init 组件的编译脚本 //device/hisilicon/build/hi3516dv300/BUILD.gn 入手:
- build_ext_component("hi3516dv300_image") {
- exec_path = rebase_path(".", root_build_dir)
- outdir = rebase_path("$root_out_dir")
- command = "./build.sh ${outdir} ${device_path} ${board} ${ohos_kernel_type} ${ohos_build_compiler}"
- deps = [ "//kernel/liteos_a:kernel" ]
- }
依赖liteos_a:kernel组件,执行这里的build.sh,根据参数,拷贝一组.so文件后,执行make。查看Makefile和hi3516dv300.mak,可以知道这是要编译 sdk_init和system_init 并打包生成 OHOS_Image 镜像。
编译liteos_a:kernel组件,入口则在 //kernel/liteos_a/BUILD.gn,
- build_ext_component("make") {
- exec_path = rebase_path(".", root_build_dir)
- tee_enable = "false"
- if (board_name == "hi3516dv300" && enable_tee_ree) {
- tee_enable = "tee"
- }
- prebuilts = "sh build.sh ${board_name} ${ohos_build_compiler} ${root_build_dir} ${ohos_build_type} ${tee_enable} \"${device_company}\" \"${product_path}\""
- outdir = rebase_path(get_path_info(".", "out_dir"))
- command = "make clean OUTDIR=$outdir && make rootfs VERSION=\"${ohos_version}\" -j 16 OUTDIR=$outdir"
- }
根据build的参数,到 //kernel/liteos_a/tools/build/config/ 目录下寻找一个匹配的config文件,拷贝到 //kernel/liteos_a/.config,但这还不是.config的全部,执行make时,根据Kconfig的配置,层层将编译内核的默认配置收集汇总到.config文件内,driver部分就包含了:
- ######################## config options os drivers ########################
- menu "Driver"
- config DRIVERS
- bool "Enable Driver"
- default y
- help
- Answer Y to enable LiteOS support driver.
- source "../../kernel/liteos_a/bsd/dev/usb/Kconfig"
- source "../../drivers/adapter/khdf/liteos/Kconfig"
这就把 //drivers/adapter/khdf/ 模块的编译配置包含进来了,并且由这个Kconfig关联驱动模块的其他Kconfig:
- source "../../drivers/adapter/khdf/liteos/model/bus/usb/Kconfig"
- source "../../drivers/adapter/khdf/liteos/test/Kconfig"
- source "../../drivers/adapter/khdf/liteos/model/display/Kconfig"
- source "../../drivers/adapter/khdf/liteos/model/input/Kconfig"
- source "../../drivers/adapter/khdf/liteos/model/sensor/Kconfig"
- source "../../device/hisilicon/drivers/Kconfig"
经过这一步,编译内核的所有配置项都集中到了.config文件中。
再看 //kernel/liteos_a/config.mk:
- -include $(LITEOSTOPDIR)/tools/build/mk/los_config.mk
打开这个 los_config.mk,找到driver部分:
- ################################## Driver Option Begin #################################
- ifeq ($(LOSCFG_DRIVERS_HDF), y)
- include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/hdf_lite.mk
- endif
【*】再打开这个hdf_lite.mk,找到下面这段:
- HAVE_VENDOR_CONFIG := $(shell if [ -d $(LITEOS_SOURCE_ROOT)/vendor/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config ]; then echo y; else echo n; fi)
- ifeq ($(LOSCFG_DRIVERS_HDF_TEST), y)
- include $(LITEOS_DRIVERS_HDF)/test/test_lite.mk
- # test
- LITEOS_BASELIB += -lhdf_test
- LIB_SUBDIRS += $(LITEOS_DRIVERS_HDF)/test
- #//drivers/adapter/khdf/liteos/test,通过Makefile可以编译驱动子系统的测试文件
- LITEOS_BASELIB += -lhdf_test_config
- LIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/vendor/$(LOSCFG_DEVICE_COMPANY)/$(LOSCFG_PRODUCT_NAME)/config/hdf_test
- #//vendor/hisilicon/hispark_taurus/config/hdf_test
- #这里并不显式地去编译hdf_config,即显式地执行Makefile,
- #而是通过hdf_test/hdf_test.hcs 里面的 #include "../hdf.hcs" 去将其编译到
- # ./out/hispark_taurus/ipcamera_hispark_taurus/obj/kernel/liteos_a/lib/libhdf_test_config.a 里面去
- else
- # config
- LITEOS_BASELIB += -lhdf_config
- ifeq ($(HAVE_VENDOR_CONFIG), y)
- LIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/vendor/$(LOSCFG_DEVICE_COMPANY)/$(LOSCFG_PRODUCT_NAME)/config
- #要是上面LOSCFG_DRIVERS_HDF_TEST是 n,不编译hdf_test/,这里才会显式地编译 hdf_config,执行这个目录下的Makefile指令,
- # ./out/hispark_taurus/ipcamera_hispark_taurus/obj/kernel/liteos_a/lib/libhdf_config.a 里面去
- else
- LIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/device/$(LOSCFG_DEVICE_COMPANY)/$(LOSCFG_PRODUCT_NAME)/config
- endif
- endif
通过参数对路径下的config进行确认,HAVE_VENDOR_CONFIG 是存在的,也就是y,上面的.config中,LOSCFG_DRIVERS_HDF_TEST也是默认配置为y的,会跑# test这个,而不是# config。
LIB_SUBDIRS += //vendor/hisilicon/hispark_taurus/config/hdf_test
而不是 //device/hisilicon/hispark_taurus/sdk_liteos/config 下的 config 目录。
【**】进入//vendor/hisilicon/hispark_taurus/config/hdf_test/目录查看 Makefile 文件:
- include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk
- MODULE_NAME := hdf_test_config
- LOCAL_HCS_ROOT := $(shell pwd)
- LOCAL_PLATFORM_HCS_SRC := hdf_test.hcs
- include $(HDF_DRIVER)
再去打开这个lite.mk,最后面有一句:
- HDF_DRIVER = $(HDF_ROOT_DIR)/adapter/khdf/liteos/hdf_driver.mk
再打开这个hdf_driver.mk
- HAVE_VENDOR_CONFIG := $(shell if [ -d $(LITEOS_SOURCE_ROOT)/vendor/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config ]; then echo y; else echo n; fi)
- ifeq ($(LOCAL_HCS_ROOT),)
- ifeq ($(HAVE_VENDOR_CONFIG), y)
- LOCAL_HCS_ROOT := $(abspath $(LITEOSTOPDIR)/../../vendor/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config)
- else
- LOCAL_HCS_ROOT := $(abspath $(LITEOSTOPDIR)/../../device/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config)
- endif
- endif
所以LOCAL_HCS_ROOT是vendor的://vendor/hisilicon/hispark_taurus/config
而不是device目录下的 //device/hisilicon/hispark_taurus/sdk_liteos/config
回到上面【**】 //vendor/hisilicon/hispark_taurus/config/hdf_test/Makefile 文件,要编译 hdf_test.hcs,打开该文件:
- #include "../hdf.hcs"
- #include "hdf_test_manager/device_info.hcs"
- #include "gpio_test_config.hcs"
- #include "i2c_test_config.hcs"
- #include "spi_test_config.hcs"
- #include "uart_test_config.hcs"
- #include "hdf_config_test.hcs"
- #include "sdio_test_config.hcs"
- #include "emmc_test_config.hcs"
- #include "pwm_test_config.hcs"
也就是编译hdf_test目录下的 测试相关的 hcs 之外,还编译上一级目录的 hdf.hcs,再打开这个 hdf.hcs 文件:
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/i2c/i2c_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/gpio/gpio_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/uart/uart_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/sdio/sdio_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/emmc/emmc_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/watchdog/watchdog_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/usb/usb_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/rtc/rtc_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/spi/spi_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/pwm/pwm_config.hcs"
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/dmac/dmac_config.hcs"
- #include "device_info/device_info.hcs"
- #include "wifi/wlan_platform.hcs"
- #include "wifi/wlan_chip_hi3881.hcs"
- #include "lcd/lcd_config.hcs"
- #include "input/input_config.hcs"
- #include "sensor/sensor_config.hcs"
- #include "../../../../vendor/huawei/hdf/sample/config/uart/uart_config.hcs"
- #include "../../../../vendor/huawei/hdf/sample/config/device_info/device_info.hcs"
从这里可以看出,//device/hisilicon/hispark_taurus/sdk_liteos/config 目录下的 hcs 文件,除了device_info、input、lcd、wifi四个模块并不参与编译之外,大部分都会得到编译,而这四个模块在vendor目录下都有对应的 hcs 来替代。实际上对比一下device和vendor目录下的这四个模块,差别不大。
最后面的两句include hdf/sample 的hcs,就是前文《小型系统驱动示例程序的编译和验证》中添加进来的,编译示例程序的hcs文件。
全工程搜索文件 hdf.hcs 和 device_info.hcs:
- ohos@ubuntu:~/Ohos/B_LTS$ find ./ -name hdf.hcs
- ./device/hisilicon/hispark_aries/sdk_liteos/config/hdf.hcs
- ./device/hisilicon/hispark_taurus/sdk_liteos/config/hdf.hcs 【不编译】
- ./device/qemu/arm_virt/config/hdf.hcs
- ./vendor/hisilicon/hispark_aries/config/hdf.hcs
- ./vendor/hisilicon/hispark_taurus/config/hdf.hcs 【编译,hdf_test.hcs -->> hdf.hcs】
- ohos@ubuntu:~/Ohos/B_LTS$find ./ -name device_info.hcs
- ./device/hisilicon/hispark_aries/sdk_liteos/config/device_info/device_info.hcs
- ./device/hisilicon/hispark_taurus/sdk_liteos/config/device_info/device_info.hcs【不编译】
- ./device/qemu/arm_virt/config/device_info/device_info.hcs
- ./vendor/huawei/hdf/sample/config/device_info/device_info.hcs【编译,device_info.hcs -->> sample/.../device_info.hcs】
- ./vendor/hisilicon/hispark_aries/config/device_info/device_info.hcs
- ./vendor/hisilicon/hispark_aries/config/hdf_test/hdf_test_manager/device_info.hcs
- ./vendor/hisilicon/hispark_taurus/config/device_info/device_info.hcs 【编译,hdf.hcs -->> device_info.hcs】
- ./vendor/hisilicon/hispark_taurus/config/hdf_test/hdf_test_manager/device_info.hcs 【编译,hdf_test.hcs -->> hdf_test_manager/device_info.hcs】
把无关的先去除,剩下的就是就是上面几个了。
上面就是对驱动子系统的驱动配置和设备树描述文件(.hcs)的编译关系的梳理,上面【*】提到的 hdf_driver.mk 的作用,就是配置编译工具,将 *.hcs 配置文件编译成 *_hex.c,再编译成 *.o和 *.hcb 文件,存放在:项目输出根目录下的/obj/kernel/liteos_a/obj/vendor/hisilicon/hispark_taurus/config/hdf_test_config/config/目录下。
至于驱动子系统部署在内核空间部分代码的编译,则需要到 //drivers/adapter/khdf/liteos/Makefile 来查看:
- include $(LITEOSTOPDIR)/config.mk #//kernel/liteos_a/config.mk
- include ./lite.mk #HDF_DRIVER = $(HDF_ROOT_DIR)/adapter/khdf/liteos/hdf_driver.mk 上面有展开
- MODULE_NAME := hdf
- HDF_FRAMEWORKS = ../../../framework
根据source,编译出libhdf.a静态链接库文件到 //out/.../obj/kernel/liteos_a/lib/libhdf.a,生成OHOS_Image镜像时将其链接进去。
而//device/hisilicon/drivers/lite.mk 则描述了具体的驱动模块代码编译成对应的静态库文件,文件末尾的:
- LITEOS_BASELIB += -lhdf_uart_sample
- LIB_SUBDIRS += $(LITEOSTOPDIR)/../../vendor/huawei/hdf/sample/platform/uart
就是前文《小型系统驱动示例程序的编译和验证》添加上去的,让例子程序的内核部分参与编译,生成静态库文件,生成OHOS_Image镜像时将其链接进去。