本文章基于恒玄科技 BES2600W 芯片的歐智通 Multi-modal V200Z-R 開發(fā)板 ,進(jìn)行輕量帶屏開發(fā)板的標(biāo)準(zhǔn)移植,開發(fā)了智能開關(guān)面板樣例,同時實(shí)現(xiàn)了 ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF 等部件基于 OpenHarmony LiteOS-M 內(nèi)核的適配。移植架構(gòu)上采用 Board 與 SoC 分離的方案,工具鏈 Newlib C 庫與 Musl C 庫可選,LiteOS-M 內(nèi)核編譯采用 gn 結(jié)合 Kconfig 圖形化配置等需求。
編譯構(gòu)建
目錄規(guī)劃
本案例在芯片移植架構(gòu)方面進(jìn)行了一些改進(jìn),以前的芯片適配目錄規(guī)劃為:
device
└── <device_company>
└── <device_name>
這樣會導(dǎo)致,小熊派 BearPi-HM Nano 開發(fā)板與潤和的 HiSpark Pegasus 開發(fā)板使用小海思的 hi3861 的 SoC 時,需要在這兩款開發(fā)板里面都放置一份重復(fù)的代碼。為了解決該問題,本案例將單板廠商與 SoC 廠商進(jìn)行分離,并把芯片適配目錄規(guī)劃為:
device
├── board --- 單板廠商目錄
│ └── fnlink --- 單板廠商名字:歐智通
│ └── v200zr --- 單板名:v200zr
└── soc --- SoC廠商目錄
└── bestechnic --- SoC廠商名字:恒玄
└── bes2600 --- SoC Series名:bes2600是一個系列,里面包含bes2600w等SoC名
產(chǎn)品樣例目錄規(guī)劃為:
vendor
└── bestechnic --- 開發(fā)產(chǎn)品樣例廠商目錄,恒玄開發(fā)的帶屏樣例,因此以bestechnic命名
└── display_demo --- 產(chǎn)品名字:以智能開關(guān)面板的帶屏顯示樣例
預(yù)編譯適配
在進(jìn)行移植之前,需要進(jìn)行預(yù)編譯適配。
預(yù)編譯適配主要使用 hb set 命令,設(shè)置整個項目的根目錄、單板目錄、產(chǎn)品目錄、單板公司名等環(huán)境變量,為編譯做準(zhǔn)備。
具體的預(yù)編譯適配步驟如下:
- 在
vendor/bestechnic/display_demo目錄下新增config.json文件,用于描述這個產(chǎn)品樣例所使用的單板、內(nèi)核等信息,描述信息可參考如下內(nèi)容:
{
"product_name": "display_demo", --- 用于hb set進(jìn)行選擇時,顯示的產(chǎn)品名稱
"type": "mini", --- 構(gòu)建系統(tǒng)的類型,mini/small/standard
"version": "3.0", --- 構(gòu)建系統(tǒng)的版本,1.0/2.0/3.0
"device_company": "fnlink", --- 單板廠商名,用于編譯時找到/device/board/fnlink目錄
"board": "v200zr", --- 單板名,用于編譯時找到/device/board/fnlink/v200zr目錄
"kernel_type": "liteos_m", --- 內(nèi)核類型,因為OpenHarmony支持多內(nèi)核,一塊單板可能適配了多個內(nèi)核,所以需要指定某個內(nèi)核進(jìn)行編譯
"kernel_version": "3.0.0", --- 內(nèi)核版本,一塊單板可能適配了多個linux內(nèi)核版本,所以需要指定某個具體的內(nèi)核版本進(jìn)行編譯
"subsystems": [ ] --- 選擇所需要編譯構(gòu)建的子系統(tǒng)
}
- 在
device/board/fnlink/v200zr/liteos_m目錄下新增config.gni文件,用于描述這個產(chǎn)品樣例所使用的單板、內(nèi)核等信息,描述信息可參考如下內(nèi)容:
# Kernel type, e.g. "linux", "liteos_a", "liteos_m".
kernel_type = "liteos_m" --- 內(nèi)核類型,跟config.json中kernel_type對應(yīng)
# Kernel version.
kernel_version = "3.0.0" --- 內(nèi)核版本,跟config.json中kernel_version對應(yīng)
- 驗證
hb set配置是否正確,輸入hb set能夠顯示如下圖片表示配置正確。
執(zhí)行 hb set 輸入項目根目錄,并且回車,hb 命令會遍歷所有 //vendor/<product_company>/<product_name> 目錄下的 config.json,給出可選產(chǎn)品編譯選項,config.json 的 product_name 用于顯示產(chǎn)品名,device_company 和 board 用于關(guān)聯(lián)出 //device/board/<device_company>/<board> 目錄,并且匹配 <any_dir_name>/config.gni 文件,如果能夠匹配多個文件,表示該單板適配了多個內(nèi)核,那么可以根據(jù) config.json 的 kernel_type 和 kernel_version 來唯一匹配 config.gni 的 kernel_type 和 kernel_version,即可確定了需要編譯適配了哪個內(nèi)核的單板。

通過 hb env 可以查看選擇出來的預(yù)編譯環(huán)境變量。

在執(zhí)行 hb build 之前,需要準(zhǔn)備好 LiteOS-M 內(nèi)核適配,具體適配步驟請參內(nèi)核移植 。
內(nèi)核移植
內(nèi)核移植需要完成 LiteOS-M Kconfig 適配、gn 的編譯構(gòu)建和內(nèi)核啟動最小適配。
LiteOS-M Kconfig 適配
在 //kernel/liteos_m 目錄下執(zhí)行 make menuconfig 命令,完成編譯配置選項的選擇。在 Makefile 文件中,會將 hb env 的結(jié)果轉(zhuǎn)換成環(huán)境變量,即 PRODUCT_PATH、DEVICE_PATH 和 BOARD_COMPANY。如下代碼塊所示:
$(foreach line,$(shell hb env | sed 's/\[OHOS INFO\]/ohos/g;s/ /_/g;s/:_/=/g' || true),$(eval $(line)))
ifneq ($(ohos_kernel),liteos_m)
$(error The selected product ($(ohos_product)) is not a liteos_m kernel type product)
endif
--- 將hb env的每一行輸出轉(zhuǎn)化為變量形式,例如將[OHOS INFO] device company: fnlink轉(zhuǎn)換為ohos_device_company=fnlink
……
ifeq ($(BOARD_COMPANY),)
BOARD_COMPANY:=$(ohos_device_company)
endif
……
export BOARD_COMPANY
--- 將ohos_device_company轉(zhuǎn)化為BOARD_COMPANY環(huán)境變量
在 //kernel/liteos_m/Kconfig 文件中使用這些導(dǎo)出的環(huán)境變量,Kconfiglib 采用 ulfalizer 開發(fā)基于 python 的版本,里面用到了 orsource 關(guān)鍵字,其中 o 表示 optional,表示這個文件是否存在可選,r 表示 relative,表示這個文件相對當(dāng)前文件的相對路徑。
config SOC_COMPANY
string "SoC company name to locate soc build path"
help
This option specifies the SoC company name, used to locate the build path for soc. This option is set by the
SoC's Kconfig file, and should be exactly the same with SoC company path, and the user should generally avoid
modifying it via the menu configuration.
orsource "../../device/board/*/Kconfig.liteos_m.shields" --- 將所有擴(kuò)展板配置信息加載進(jìn)來,因為單板廠商A提供擴(kuò)展板可以給單板廠商B使用,所以這里使用*匹配所有的擴(kuò)展板,而非BOARD_COMPANY。另外由于OpenHarmony支持多內(nèi)核設(shè)計,Kconfig文件采用liteos_m作為后綴,在進(jìn)行單板適配過程中,其他內(nèi)核在適配過程中,可以使用對應(yīng)的內(nèi)核名作為后綴名進(jìn)行擴(kuò)展。
orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards" --- 加載BOARD_COMPANY的所有單板預(yù)定義配置
choice
prompt "Board Selection"
orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards" --- 提供Board選擇列表
endchoice
orsource "../../device/soc/*/Kconfig.liteos_m.defconfig" --- 加載所有SoC的默認(rèn)配置定義
choice
prompt "SoC Series Selection"
orsource "../../device/soc/*/Kconfig.liteos_m.series" --- 提供所有SoC Series選擇列表
endchoice
orsource "../../device/soc/*/Kconfig.liteos_m.soc" --- 加載所有SoC配置
從 //kernel/liteos_m/Kconfig 文件可以看出需要在 //device/board/fnlink 目錄下新增如下 Kconfig 文件進(jìn)行適配:
.
├── v200zr --- v200zr單板配置目錄
│ ├── Kconfig.liteos_m.board --- 提供v200zr單板的配置選項
│ ├── Kconfig.liteos_m.defconfig.board --- 提供v200zr單板的默認(rèn)配置項
│ └── liteos_m
│ └── config.gni
├── Kconfig.liteos_m.boards --- 提供fnlink單板廠商下Boards配置信息
├── Kconfig.liteos_m.defconfig.boards --- 提供fnlink單板廠商下Boards默認(rèn)配置信息
├── Kconfig.liteos_m.shields --- 提供fnlink單板廠商下擴(kuò)展板配置信息
└── shields --- fnlink單板廠商的擴(kuò)展板目錄
├── v200zr-t0 --- fnlink單板廠商的擴(kuò)展板v200zr-t0
│ ├── Kconfig.liteos_m.defconfig.shield --- 擴(kuò)展板v200zr-t0默認(rèn)配置
│ └── Kconfig.liteos_m.shield --- 擴(kuò)展板v200zr-t0配置信息
├── v200zr-t1
│ ├── Kconfig.liteos_m.defconfig.shield
│ └── Kconfig.liteos_m.shield
└── Kconfig.liteos_m.shields
在 v200zr/Kconfig.liteos_m.board 需要配置選擇該單板的選項,以及它依賴的 SoC,如下:
config BOARD_v200zr
bool "select board v200zr"
depends on SOC_BES2600W --- v200zr單板用的bes2600w的SoC,只有 bes2600w的SoC被選擇后,v200zr單板配置選項才可見,可以被選擇。
在 v200zr/Kconfig.liteos_m.defconfig.board 需要配置選擇該單板后,默認(rèn)定義 BOARD 的名字為 "v200zr" ,如下:
if BOARD_v200zr
config BOARD
string --- string后沒有帶提示,因此用戶不可見
default "v200zr"
endif # BOARD_v200zr
從 //kernel/liteos_m/Kconfig 文件可以看出需要在 //device/soc/bestechnic 目錄下新增如下 Kconfig 文件進(jìn)行適配:
.
├── bes2600 --- bes2600 SoC系列
│ ├── Kconfig.liteos_m.defconfig.bes2600w --- bestechnic芯片廠商bes2600w SoC Series配置
│ ├── Kconfig.liteos_m.defconfig.series --- bestechnic芯片廠商bes2600默認(rèn)配置
│ ├── Kconfig.liteos_m.series --- bestechnic芯片廠商bes2600 SoC Series配置
│ └── Kconfig.liteos_m.soc --- bestechnic芯片廠商bes2600 SoC配置
├── Kconfig.liteos_m.defconfig --- bestechnic芯片廠商SoC默認(rèn)配置
├── Kconfig.liteos_m.series --- bestechnic芯片廠商SoC Series配置
└── Kconfig.liteos_m.soc --- bestechnic芯片廠商 SoC配置
在 bes2600/Kconfig.liteos_m.series 需要配置 bes2600 SoC series,以及它的芯片架構(gòu)等信息,如下:
config SOC_SERIES_BES2600 --- 提供bes2600 SoC Series選項
bool "Bestechnic 2600 Series"
select ARM --- 選擇bes2600后,默認(rèn)選擇ARM架構(gòu)
select SOC_COMPANY_BESTECHNIC --- 選擇bes2600后,默認(rèn)選擇bestechnic芯片公司,驅(qū)動會依賴這個宏配置,選擇配置編譯對應(yīng)廠商的驅(qū)動
select CPU_CORTEX_M33 --- 選擇bes2600后,默認(rèn)選擇cortex-m33 CPU
help
Enable support for Bestechnic 2600 series
在 bes2600/Kconfig.liteos_m.soc 需要提供 bes2600 SoC series 下有多少個具體的 SoC 可供選擇,如下:
choice
prompt "Bestechnic 2600 series SoC"
depends on SOC_SERIES_BES2600 --- 只有選擇了bes2600 Series后,才會出現(xiàn)如下配置選項
config SOC_BES2600W --- 增加bes2600w SoC配置選擇項
bool "SoC BES2600w"
endchoice
在 bes2600/Kconfig.liteos_m.defconfig.series 需要提供 bes2600 SoC series 選擇后的默認(rèn)配置,如下:
if SOC_SERIES_BES2600 --- 選擇了bes2600 Series后,才會增加如下默認(rèn)配置選項
rsource "Kconfig.liteos_m.defconfig.bes2600w" --- 增加bes2600w SoC的默認(rèn)配置
config SOC_SERIES --- 增加SOC_SERIES的默認(rèn)配置
string
default "bes2600"
endif
配置完成后,還需要根據(jù) kernel/liteos_m/Makefile 文件配置 make menuconfig 的 defconfig 保存路徑:
ifeq ($(TEE:1=y),y)
tee = _tee
endif
ifeq ($(RELEASE:1=y),y)
CONFIG ?= $(PRODUCT_PATH)/kernel_configs/release$(tee).config
else
CONFIG ?= $(PRODUCT_PATH)/kernel_configs/debug$(tee).config --- 配置文件保存在$(CONFIG)中,由產(chǎn)品最終定義
endif
……
update_config menuconfig:
$(HIDE)test -f "$(CONFIG)" && cp -v "$(CONFIG)" .config && menuconfig $(args) && savedefconfig --out "$(CONFIG)"
在這個例子中,defconfig 配置路徑為 $(PRODUCT_PATH)/kernel_configs/debug.config,創(chuàng)建該文件后,內(nèi)容為空,產(chǎn)品的目錄文件結(jié)構(gòu)如下:
.
└── display_demo
├── config.json
└── kernel_configs
└── debug.config
配置完成后,在 kernel/liteos_m 目錄下執(zhí)行 make menuconfig 能夠?qū)?SoC Series/SoC/Board 進(jìn)行選擇,如下:

結(jié)果將自動保存在 $(PRODUCT_PATH)/kernel_configs/debug.config,下次執(zhí)行 make menuconfig 時會導(dǎo)出保存的結(jié)果。
gn 編譯適配
在上一步 Kconfig 的圖形化配置后,將其生成的配置結(jié)果可以作為 gn 編譯的輸入,以控制不同模塊是否編譯。另外為了解決之前 gn 編寫時,隨意 include 的問題,內(nèi)核編譯做了模塊化編譯的設(shè)計,使得整個編譯邏輯更加清晰。
在 kernel/liteos_m/BUILD.gn 中,指定了 Board 和 SoC 的編譯入口為 //device/board/fnlink 和 //device/soc/bestechnic。
deps += [ "http://device/board/$device_company" ]
deps += [ "http://device/soc/$LOSCFG_SOC_COMPANY" ]
在 //device/board/fnlink/BUILD.gn 中,新增內(nèi)容如下:
if (ohos_kernel_type == "liteos_m") { --- 由于多內(nèi)核設(shè)計,對于LiteOS-M內(nèi)核適配,需要用宏來隔離
import("http://kernel/liteos_m/liteos.gni") --- 引入內(nèi)核gn編寫模板
module_name = get_path_info(rebase_path("."), "name") --- 動態(tài)獲取當(dāng)前文件目錄作為模塊名,防止目錄名修改后,這里還需要跟著修改
module_group(module_name) { --- 采用module_group模板
modules = [ --- 添加需要編譯的模塊
]
}
}
同理 //device/soc/bestechnic/BUILD.gn 也是一樣。
內(nèi)核啟動適配
系統(tǒng)啟動流程分為三個階段:
| 階段名稱 | 分區(qū)規(guī)劃 | 描述 |
|---|---|---|
| BOOT1 | [0, 0x10000] | 第一階段啟動,進(jìn)行固件啟動 |
| BOOT2 | [0x2C010000, 0x2C020000] | 第二階段啟動,進(jìn)行 OTA 升級啟動 |
| RTOS_MAIN | [0x2C080000, 0x2C860000] | 第三階段啟動,進(jìn)行內(nèi)核啟動 |
在第三階段內(nèi)核啟動中,需要適配的文件路徑在 //device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/rtos/liteos/liteos_m/board.c
內(nèi)核啟動適配總體思路如下:
- 中斷向量的初始化
os_vector_init,初始化中斷的處理函數(shù)。 - 內(nèi)核初始化
osKernelInitialize。 - 創(chuàng)建線程
board_main,進(jìn)行芯片平臺初始化。 - 內(nèi)核啟動,開始調(diào)度線程
osKernelStart。
其中,本章節(jié)詳細(xì)對第 3 步進(jìn)行展開,其他幾步為對內(nèi)核函數(shù)調(diào)用,不作詳細(xì)描述。
第 3 步中board_main在啟動OHOS_SystemInit之前,需要初始化必要的動作,如下:
...
if(!ret) {
...
OhosSystemAdapterHooks(); --- 系統(tǒng)啟動時候設(shè)置鉤子,啟動OpenHarmonyOHOS_SystemInit的之前完成打印和驅(qū)動的初始化
...
OHOS_SystemInit(); --- 啟動OpenHarmony服務(wù),以及組件初始化
}
....
OhosSystemAdapterHooks 函數(shù)在 device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c 文件中,如下:
int OhosSystemAdapterHooks(void)
{
init_trace_system(); --- 初始化打印函數(shù)
DeviceManagerStart(); --- 調(diào)用DeviceManagerStart函數(shù)進(jìn)行HDF驅(qū)動初始化,這個過程會調(diào)用單板代碼中的驅(qū)動配置文件hdf.hcs以及drivers源碼實(shí)現(xiàn)
return 0;
}
littlefs 文件系統(tǒng)移植
V200Z-R 開發(fā)板開發(fā)板采用最大 32MB 的支持 XIP 的 Nor Flash,文件系統(tǒng)可以使用 example,適配過程中,需要在指定路徑下放置文件系統(tǒng)預(yù)置文件,根據(jù)配置可自動生成文件系統(tǒng)鏡像,可以實(shí)現(xiàn)自動化生成和打包到燒錄包中。
- 配置指定目錄放置打包文件系統(tǒng)
config.json,通過flash_partition_dir指定目錄:
"flash_partition_dir": "fs" --- 表示在vendor/bestechnic/display_demo/fs目錄下放置文件系統(tǒng)預(yù)置文件
- 在指定目錄
vendor/bestechnic/display_demo/fs下放置兩部分內(nèi)容:
-
wifi_Download_cfg.yaml:鏡像的燒錄配置文件,可以根據(jù)實(shí)際情況調(diào)整分區(qū)。 -
/data/data:第一個/data是掛載的根目錄;第二個data是根目錄里面的data目錄,里面可以存放預(yù)置文件,或者在第二個data的同級目錄再創(chuàng)建一個目錄,打包的時候只認(rèn)第一個data掛載根目錄。
-
config.json中根據(jù)wifi_Download_cfg.yaml最后調(diào)整結(jié)果。
-
fs_src配置文件系統(tǒng)掛載名字。 -
fs_name是最后生成文件系統(tǒng)的名字。 -
block_size配置成4K對齊,建議不修改。 -
fs_size是生成文件系統(tǒng)的大小。 -
burn_name是燒錄bin名字的大小。 -
enable表示是否生成這個文件系統(tǒng)
- 在
//device/soc/bestechnic/bes2600/liteos_m/components/hdf_config/hdf.hcs文件配置文件系統(tǒng)的燒錄的起始地址、文件系統(tǒng)的大小以及讀數(shù)據(jù)塊的大小block_size等信息,參考配置如下:
misc {
fs_config {
example_config {
match_attr = "littlefs_config";
mount_points = ["/data"];
partitions = [10];
block_size = [4096];
block_count = [1024];
}
}
storage_config {
flash_config {
match_attr = "flash_config";
partitions = [10];
owner = [0];
description = ["littlefs"];
start_addr = [0xB60000];
length = [0x400000];
options = [3];
}
}
}
最后在 device/soc/bestechnic/bes2600/liteos_m/components/fs/fs_init.c 中,通過 hdf 加載數(shù)據(jù),進(jìn)行讀寫 flash,如下:
static int32_t FsDriverInit(struct HdfDeviceObject *object)
{
if (object == NULL) {
return HDF_FAILURE;
}
if (object->property) {
if (FsGetResource(fs, object->property) != HDF_SUCCESS) {
HDF_LOGE("%s: FsGetResource failed", __func__);
return HDF_FAILURE;
}
}
for (int i = 0; i < sizeof(fs) / sizeof(fs[0]); i++) {
if (fs[i].mount_point == NULL)
continue;
fs[i].lfs_cfg.read = littlefs_block_read;
fs[i].lfs_cfg.prog = littlefs_block_write;
fs[i].lfs_cfg.erase = littlefs_block_erase;
fs[i].lfs_cfg.sync = littlefs_block_sync;
fs[i].lfs_cfg.read_size = 256;
fs[i].lfs_cfg.prog_size = 256;
fs[i].lfs_cfg.cache_size = 256;
fs[i].lfs_cfg.lookahead_size = 16;
fs[i].lfs_cfg.block_cycles = 1000;
int ret = mount(NULL, fs[i].mount_point, "littlefs", 0, &fs[i].lfs_cfg);
HDF_LOGI("%s: mount fs on '%s' %s\n", __func__, fs[i].mount_point, (ret == 0) ? "succeed" : "failed");
}
return HDF_SUCCESS;
}
C 庫適配
在輕量系統(tǒng)中,C 庫適配比較復(fù)雜,設(shè)計思路請參考 LiteOS-M 內(nèi)核支持 musl 與 newlib 平滑切換方案,由于我們的工具鏈采用gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 自帶 newlib 的 C 庫,那么系統(tǒng)移植整體采用 newlib 的 C 庫。那么在內(nèi)核的 make menuconfig 中選擇 newlib,如下圖:

malloc 適配
malloc 適配參考 The Red Hat newlib C Library-malloc 。實(shí)現(xiàn) malloc 適配有以下兩種方法:
- 實(shí)現(xiàn)
_sbrk_r函數(shù)。這種方法中,內(nèi)存分配函數(shù)使用newlib中的。 - 實(shí)現(xiàn)
_malloc_r,_realloc_r,_reallocf_r,_free_r,_memalign_r, 和_malloc_usable_size_r。這種方法中,內(nèi)存分配函數(shù)可以使用內(nèi)核的。
為了方便地根據(jù)業(yè)務(wù)進(jìn)行內(nèi)存分配算法調(diào)優(yōu)和問題定位,在這兩種方法中,本案例選擇后者。
首先,由于newlib中已經(jīng)存在這些函數(shù)的符號,因此需要用到gcc的wrap的鏈接選項替換這些函數(shù)符號為內(nèi)核的實(shí)現(xiàn),內(nèi)核的實(shí)現(xiàn)為//kernel/liteos_m/kal/libc/newlib/porting/src/malloc.c。
然后,在//device/board/fnlink/v200zr/liteos_m/config.gni的新增這些函數(shù)的wrap鏈接選項。
board_ld_flags += [
"-Wl,--wrap=_malloc_r",
"-Wl,--wrap=_realloc_r",
"-Wl,--wrap=_reallocf_r",
"-Wl,--wrap=_free_r",
"-Wl,--wrap=_memalign_r",
"-Wl,--wrap=_malloc_usable_size_r",
]
vsprintf 等適配
實(shí)現(xiàn) vprintf, vfprintf, printf, snprintf 和 sprintf。
類似 malloc 適配,首先要提供這些函數(shù)的實(shí)現(xiàn),//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/printf.c,本案例直接采用開源協(xié)議友好的實(shí)現(xiàn)。與 malloc 適配不同的是,這個函數(shù)由芯片原廠提供。因為就打印來說,根據(jù)項目的需要,實(shí)現(xiàn)可大可小,內(nèi)核不方便提供統(tǒng)一的實(shí)現(xiàn)。
然后,在 //device/board/fnlink/v200zr/liteos_m/config.gni 的新增這些函數(shù)的 wrap 鏈接選項。
board_ld_flags += [
"-Wl,--wrap=printf",
"-Wl,--wrap=sprintf",
"-Wl,--wrap=snprintf",
"-Wl,--wrap=vsnprintf",
"-Wl,--wrap=vprintf",
]
open 等適配
這部分實(shí)現(xiàn)由內(nèi)核統(tǒng)一實(shí)現(xiàn),芯片適配無須關(guān)注,內(nèi)核文件 //kernel/liteos_m/kal/libc/newlib/porting/src/fs.c,適配了 newlib 的 _read、_write 等函數(shù),如下:
……
ssize_t _read(int fd, void *buf, size_t nbyte)
{
return LOS_Read(fd, buf, nbyte);
}
ssize_t _write(int fd, const void *buf, size_t nbyte)
{
return LOS_Write(fd, buf, nbyte);
}
off_t _lseek(int fd, off_t offset, int whence)
{
return LOS_Lseek(fd, offset, whence);
}
……
板級系統(tǒng)移植
驅(qū)動移植
SoC 芯片平臺 HDF 驅(qū)動移植
驅(qū)動適配相關(guān)文件放置在 drivers/adapter/platform 中,對應(yīng)有 gpio,i2c,pwm,spi,uart,watchdog,都是通過 HDF 機(jī)制加載,本章節(jié)以 gpio 為例進(jìn)行詳細(xì)說明。
GPIO 驅(qū)動適配
gpio 驅(qū)動適配需要完成編譯的適配、源碼的適配。
在 //drivers/adapter/platform/gpio/BUILD.gn 文件中,描述了恒玄 gpio 驅(qū)動的編譯適配。如下:
module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO) --- 如果打開HDF的GPIO配置開關(guān),才進(jìn)行如下編譯
module_name = get_path_info(rebase_path("."), "name")
hdf_driver(module_name) {
sources = []
if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) { --- 如果打開恒玄的芯片配置開關(guān),才進(jìn)行恒玄GPIO的驅(qū)動編譯
sources += [ "gpio_bes.c" ]
}
include_dirs = [ "." ]
}
在 //drivers/adapter/platform/gpio/gpio_bes.c 文件中,描述了恒玄 gpio 驅(qū)動的源碼適配。
首先,按照 OpenHarmony 的 HDF 驅(qū)動框架加載驅(qū)動基本適配框架,如下:
struct HdfDriverEntry g_GpioDriverEntry = {
.moduleVersion = 1,
.moduleName = "BES_GPIO_MODULE_HDF",
.Bind = GpioDriverBind,
.Init = GpioDriverInit,
.Release = GpioDriverRelease,
};
HDF_INIT(g_GpioDriverEntry); --- 通過HDF_INIT 加載GPIO驅(qū)動
然后,在初始化的時候會獲取 hcs 參數(shù)進(jìn)行初始化,如下:
static int32_t GpioDriverInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct GpioCntlr *gpioCntlr = NULL;
if (device == NULL) {
HDF_LOGE("%s: device is NULL", __func__);
return HDF_ERR_INVALID_PARAM;
}
gpioCntlr = GpioCntlrFromDevice(device); --- gpioCntlr節(jié)點(diǎn)變量就可以獲取具體gpio配置
if (gpioCntlr == NULL) {
...
編碼規(guī)范和設(shè)計思想見 bes 驅(qū)動適配 PR 的評論。
Board 外設(shè)器件 HDF 驅(qū)動移植
Board 外設(shè)器件表示通過 SoC 平臺總線連接的外設(shè)器件,在本案例中,顯示屏屬于外設(shè)器件,其驅(qū)動適配放在 //device/board/fnlink/drivers/liteos_m 目錄中。
顯示驅(qū)動適配
同 SoC 驅(qū)動適配,在 //device/board/fnlink/drivers/liteos_m/display/BUILD.gn 文件中,根據(jù) hdf_driver 模板加載驅(qū)動模塊,如下:
module_name = get_path_info(rebase_path("."), "name")
hdf_driver(module_name) {
sources = [
"zzw395.c",
]
include_dirs = [
"http://drivers/peripheral/display/interfaces/include",
...
]
}
在 //device/board/fnlink/drivers/liteos_m/display/zzw395.c 文件中,根據(jù)驅(qū)動框架加載顯示驅(qū)動,如下:
static struct HdfDriverEntry g_ZZW395DriverEntry = {
.moduleVersion = 1,
.moduleName = "HDF_PANEL_ZZW395",
.Bind = PanelDriverBind,
.Init = PanelDriverInit,
.Release = PanelDriverRelease,
};
HDF_INIT(g_ZZW395DriverEntry);
其中的驅(qū)動參數(shù)根據(jù) hcs 配置,在 PanelDriverInit 初始化時加載,如下:
static int32_t PanelDriverInit(struct HdfDeviceObject *object)
{
if (object == NULL) {
return HDF_FAILURE;
}
HDF_LOGD("%s entry !!!", __func__);
if (object->property) {
if (PanelGetResource(&priv, object->property) != HDF_SUCCESS) {
HDF_LOGE("%s: PanelGetResource failed", __func__);
return HDF_FAILURE;
}
}
...
OpenHarmony 子系統(tǒng)適配
OpenHarmony 子系統(tǒng)適配一般包含兩部分:
- 在
config.json中增加對應(yīng)子系統(tǒng)和部件,這樣編譯系統(tǒng)會將該部件納入編譯目標(biāo)中。 - 針對該部件的
HAL層接口進(jìn)行硬件適配,或者可選的軟件功能適配。
分布式軟總線子系統(tǒng)適配
wifi_lite 部件適配
首先,在 config.json 文件中,增加 communication 子系統(tǒng)的 wifi_lite 部件,如下:
{
"subsystem": "communication",
"components": [
{
"component": "wifi_lite",
"optional": "true"
}
]
},
wifi_lite 部件在 //build/lite/components/communication.json 文件中,描述如下:
{
"component": "wifi_lite",
……
"targets": [
"http://foundation/communication/wifi_lite:wifi" --- wifi_lite的編譯目標(biāo)
],
……
},
在 //foundation/communication/wifi_lite/BUILD.gn 文件中,描述需要適配的接口頭文件路徑,如下:
config("include") {
include_dirs = [ "interfaces/wifiservice" ] --- 因為wifi_lite只提供頭文件,不提供wifi的具體實(shí)現(xiàn),所以wifi模塊暴露出適配的目錄路徑提供給硬件廠商來適配,廠商提供wifi協(xié)議棧源碼實(shí)現(xiàn)。
}
group("wifi") {
public_configs = [ ":include" ]
}
因為在本案例中,wifi 屬于 SoC 提供的功能,所以適配源碼放在 SoC 的 //device/soc/bestechnic/hals/communication/wifi_lite/wifiservice 目錄下,包含 wifi_device.c 和 wifi_hotspot.c 分別適配 wifi_device.h 和 wifi_hotspot.h。如下:
……
WifiErrorCode Scan(void) --- wifi_device.c中掃描wifi熱點(diǎn)的函數(shù),對wifi_device.h中Scan函數(shù)的適配實(shí)現(xiàn)
{
WifiErrorCode ret = ERROR_WIFI_BUSY;
if (IsWifiActive() != WIFI_STA_ACTIVE)
return ERROR_WIFI_IFACE_INVALID;
if (g_HalHmosWifiInfo.scan_state == SCAN_REQUEST ||
g_HalHmosWifiInfo.scan_state == SCAN_TRIGGER)
return ERROR_WIFI_BUSY;
HalHmosWifiLock();
ret = ((HalHmosSendEvent(HMOS_ON_WIFI_SCAN_STATE_CHANGED, NULL) == 0) ? WIFI_SUCCESS : ERROR_WIFI_BUSY);
HalHmosWifiUnLock();
return ret;
}
……
int GetSignalLevel(int rssi, int band) --- wifi_hotspot.c中獲取wifi信號熱點(diǎn)函數(shù),對wifi_hotspot.h中GetSignalLevel函數(shù)的適配實(shí)現(xiàn)。
{
if (band == HOTSPOT_BAND_TYPE_2G) {
if (rssi >= RSSI_LEVEL_4_2_G)
return RSSI_LEVEL_4;
if (rssi >= RSSI_LEVEL_3_2_G)
return RSSI_LEVEL_3;
if (rssi >= RSSI_LEVEL_2_2_G)
return RSSI_LEVEL_2;
if (rssi >= RSSI_LEVEL_1_2_G)
return RSSI_LEVEL_1;
}
if (band == HOTSPOT_BAND_TYPE_5G) {
if (rssi >= RSSI_LEVEL_4_5_G)
return RSSI_LEVEL_4;
if (rssi >= RSSI_LEVEL_3_5_G)
return RSSI_LEVEL_3;
if (rssi >= RSSI_LEVEL_2_5_G)
return RSSI_LEVEL_2;
if (rssi >= RSSI_LEVEL_1_5_G)
return RSSI_LEVEL_1;
}
return ERROR_WIFI_INVALID_ARGS;
}
LWIP 部件適配
LiteOS-M kernel 目錄下默認(rèn)配置了 lwip,因而具有編譯功能,可以在 kernel 組件中指定 lwip 編譯的目錄。如下:
{
"subsystem": "kernel",
"components": [
{
"component": "liteos_m",
"features": [
"ohos_kernel_liteos_m_lwip_path = \"http://device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1\"" --- 指定在芯片廠商目錄中進(jìn)行適配
]
}
]
},
在 //device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/BUILD.gn 文件中,描述了 lwip 的編譯,如下:
import("http://kernel/liteos_m/liteos.gni")
import("$LITEOSTHIRDPARTY/lwip/lwip.gni")
import("$LITEOSTOPDIR/components/net/lwip-2.1/lwip_porting.gni")
module_switch = defined(LOSCFG_NET_LWIP_SACK)
module_name = "lwip"
kernel_module(module_name) {
sources = LWIP_PORTING_FILES + LWIPNOAPPSFILES -
[ "$LWIPDIR/api/sockets.c" ] + [ "porting/src/ethernetif.c" ] --- 增加ethernetif.c文件,用以適配ethernet網(wǎng)卡的初始化適配
defines = [ "LITEOS_LWIP=1" ]
defines += [ "CHECKSUM_BY_HARDWARE=1" ]
}
config("public") {
defines = [ "_BSD_SOURCE=1" ]
include_dirs =
[ "porting/include" ] + LWIP_PORTING_INCLUDE_DIRS + LWIP_INCLUDE_DIRS
}
在 //device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/porting/include/lwip/lwipopts.h 文件中,說明原有 lwip 配置選項保持不變,軟總線會依賴這些配置選項,并且新增硬件適配的配置項,如下:
#ifndef _PORTING_LWIPOPTS_H_
#define _PORTING_LWIPOPTS_H_
#include_next "lwip/lwipopts.h" --- 保持原來的配置項不變
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_CHECKSUM_ON_COPY 0
#define CHECKSUM_GEN_UDP 0 --- 新增硬件適配選項
#endif /* _PORTING_LWIPOPTS_H_ */
在 //device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/porting/src/ethernetif.c 文件中,說明對 ethernet 網(wǎng)卡初始化的適配,如下:
err_t
ethernetif_init(struct netif *netif)
{
……
#ifdef CHECKSUM_BY_HARDWARE
eth_hw_checksum_init();
#endif
……
netif->linkoutput = low_level_output;
netif->drv_send = liteos_low_level_output;
netif->hwaddr_len = NETIF_MAX_HWADDR_LEN;
low_level_init(netif);
driverif_init(netif);
return ERR_OK;
……
}
dsoftbus 部件適配
在 config.json 中增加 dsoftbus 部件配置如下:
{
"component": "dsoftbus",
"features": [
"softbus_adapter_config = \"http://vendor/bestechnic/mini_distributed_music_player/dsoftbus_lite_config\""
]
},
dsoftbus 部件在 //foundation/communication/dsoftbus/dsoftbus.gni 文件中提供了 softbus_adapter_config 配置選項可供移植過程進(jìn)行配置,該配置設(shè)定了軟總線移植適配的路徑。
在本案例中,softbus_adapter_config 配置為 //vendor/bestechnic/mini_distributed_music_player/dsoftbus_lite_config 路徑,該路徑下的內(nèi)容為:
.
├── feature_config --- 軟總線功能特性配置,例如是否開啟自發(fā)現(xiàn)功能等
│ └── mini
│ └── config.gni
└── spec_config --- 軟總線規(guī)格特性配置,例如設(shè)置軟總線日志級別設(shè)置
├── softbus_config_adapter.c
├── softbus_config_adapter.h
└── softbus_config_type.h
在 config.gni 文件中規(guī)定了以下配置項:
| 配置項 | 描述 |
|---|---|
| dsoftbus_feature_disc_ble | 是否開啟 BLE 發(fā)現(xiàn)功能 |
| dsoftbus_feature_disc_coap | 是否開啟 COAP 發(fā)現(xiàn)功能 |
| dsoftbus_feature_conn_tcp | 是否開啟 TCP 連接功能 |
| dsoftbus_feature_conn_br | 是否開啟 BR 連接功能 |
| dsoftbus_feature_conn_ble | 是否開啟 BLE 連接功能 |
| dsoftbus_feature_conn_p2p | 是否開啟 P2P 連接功能 |
| dsoftbus_feature_trans_udp | 是否開啟 UDP 傳輸功能 |
| dsoftbus_feature_trans_udp_stream | 是否開啟 UDP 傳輸流功能 |
| dsoftbus_feature_trans_udp_file | 是否開啟 UDP 傳輸文件功能 |
| dsoftbus_feature_ip_auth | 是否開啟認(rèn)證傳輸通道功能 |
| dsoftbus_feature_auth_account | 是否開啟基于賬號認(rèn)證功能 |
| dsoftbus_feature_qos | 是否開啟 QoS 功能 |
在 softbus_config_adapter.c 文件中規(guī)定了以下配置項:
| 配置項 | 描述 |
|---|---|
| SOFTBUS_INT_MAX_BYTES_LENGTH | SendBytes 發(fā)送最大 Bytes 長度 |
| SOFTBUS_INT_MAX_MESSAGE_LENGTH | SendMessage 發(fā)送最大消息的長度 |
| SOFTBUS_INT_CONN_BR_MAX_DATA_LENGTH | 藍(lán)牙最大接收數(shù)據(jù)量 |
| SOFTBUS_INT_CONN_RFCOM_SEND_MAX_LEN | 藍(lán)牙最大接收數(shù)據(jù)量 |
| SOFTBUS_INT_ADAPTER_LOG_LEVEL | 日志級別設(shè)置 |
| SOFTBUS_STR_STORAGE_DIRECTORY | 存儲目錄設(shè)置 |
因為軟總線配置了后,不會默認(rèn)啟動,所以需要在通過啟動框架調(diào)用 InitSoftBusServer 函數(shù),如下: | |
static void DSoftBus(void)
{
osThreadAttr_t attr;
attr.name = "dsoftbus task";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 65536;
attr.priority = 24;
extern void InitSoftBusServer(void);
if (osThreadNew((osThreadFunc_t) InitSoftBusServer, NULL, &attr) == NULL) {
printf("Failed to create WifiSTATask!\n");
}
}
APP_FEATURE_INIT(DSoftBus);
RPC 部件適配
在 config.json 中增加 rpc 部件配置如下:
{
"component": "rpc"
},
同樣地,rpc 部件需要通過啟動框架調(diào)用 StartDBinderService 函數(shù),由于該函數(shù)正常運(yùn)行依賴主機(jī)已經(jīng)獲取 IP 地址,因此在 LWIP 協(xié)議棧注冊 IP 地址變化事件的回調(diào)函數(shù)中調(diào)用該函數(shù),如下:
static void RpcServerWifiDHCPSucCB(struct netif *netif, netif_nsc_reason_t reason,
const netif_ext_callback_args_t *args)
{
(void) args;
if (netif == NULL) {
printf("%s %d, error: input netif is NULL!\n", __FUNCTION__, __LINE__);
return;
}
if (reason == LWIP_NSC_IPSTATUS_CHANGE) {
if (netif_is_up(netif) && !ip_addr_isany(&netif->ip_addr)) {
printf("%s %d, start rpc server!\n", __FUNCTION__, __LINE__);
StartDBinderService();
}
}
}
static void WifiDHCPRpcServerCB(void)
{
NETIF_DECLARE_EXT_CALLBACK(WifiReadyRpcServerCallback);
netif_add_ext_callback(&WifiReadyRpcServerCallback, RpcServerWifiDHCPSucCB);
}
APP_FEATURE_INIT(WifiDHCPRpcServerCB);
啟動恢復(fù)子系統(tǒng)適配
啟動恢復(fù)子系統(tǒng)適配 bootstrap_lite/syspara_lite 兩個部件。請在 vendor/bestechnic_bak/display_demo/config.json 中新增對應(yīng)的配置選項。
{
"subsystem": "startup",
"components": [
{
"component": "bootstrap_lite" --- bootstrap_lite 部件
},
{
"component": "syspara_lite", --- syspara_lite 部件
"features": [
"enable_ohos_startup_syspara_lite_use_posix_file_api = true"
]
}
]
},
適配 bootstrap_lite 部件時,需要在連接腳本文件 //device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/out/best2600w_liteos/_best2001.lds 中手動新增如下段:
__zinitcall_bsp_start = .;
KEEP (*(.zinitcall.bsp0.init))
KEEP (*(.zinitcall.bsp1.init))
KEEP (*(.zinitcall.bsp2.init))
KEEP (*(.zinitcall.bsp3.init))
KEEP (*(.zinitcall.bsp4.init))
__zinitcall_bsp_end = .;
__zinitcall_device_start = .;
KEEP (*(.zinitcall.device0.init))
KEEP (*(.zinitcall.device1.init))
KEEP (*(.zinitcall.device2.init))
KEEP (*(.zinitcall.device3.init))
KEEP (*(.zinitcall.device4.init))
__zinitcall_device_end = .;
__zinitcall_core_start = .;
KEEP (*(.zinitcall.core0.init))
KEEP (*(.zinitcall.core1.init))
KEEP (*(.zinitcall.core2.init))
KEEP (*(.zinitcall.core3.init))
KEEP (*(.zinitcall.core4.init))
__zinitcall_core_end = .;
__zinitcall_sys_service_start = .;
KEEP (*(.zinitcall.sys.service0.init))
KEEP (*(.zinitcall.sys.service1.init))
KEEP (*(.zinitcall.sys.service2.init))
KEEP (*(.zinitcall.sys.service3.init))
KEEP (*(.zinitcall.sys.service4.init))
__zinitcall_sys_service_end = .;
__zinitcall_sys_feature_start = .;
KEEP (*(.zinitcall.sys.feature0.init))
KEEP (*(.zinitcall.sys.feature1.init))
KEEP (*(.zinitcall.sys.feature2.init))
KEEP (*(.zinitcall.sys.feature3.init))
KEEP (*(.zinitcall.sys.feature4.init))
__zinitcall_sys_feature_end = .;
__zinitcall_run_start = .;
KEEP (*(.zinitcall.run0.init))
KEEP (*(.zinitcall.run1.init))
KEEP (*(.zinitcall.run2.init))
KEEP (*(.zinitcall.run3.init))
KEEP (*(.zinitcall.run4.init))
__zinitcall_run_end = .;
__zinitcall_app_service_start = .;
KEEP (*(.zinitcall.app.service0.init))
KEEP (*(.zinitcall.app.service1.init))
KEEP (*(.zinitcall.app.service2.init))
KEEP (*(.zinitcall.app.service3.init))
KEEP (*(.zinitcall.app.service4.init))
__zinitcall_app_service_end = .;
__zinitcall_app_feature_start = .;
KEEP (*(.zinitcall.app.feature0.init))
KEEP (*(.zinitcall.app.feature1.init))
KEEP (*(.zinitcall.app.feature2.init))
KEEP (*(.zinitcall.app.feature3.init))
KEEP (*(.zinitcall.app.feature4.init))
__zinitcall_app_feature_end = .;
__zinitcall_test_start = .;
KEEP (*(.zinitcall.test0.init))
KEEP (*(.zinitcall.test1.init))
KEEP (*(.zinitcall.test2.init))
KEEP (*(.zinitcall.test3.init))
KEEP (*(.zinitcall.test4.init))
__zinitcall_test_end = .;
__zinitcall_exit_start = .;
KEEP (*(.zinitcall.exit0.init))
KEEP (*(.zinitcall.exit1.init))
KEEP (*(.zinitcall.exit2.init))
KEEP (*(.zinitcall.exit3.init))
KEEP (*(.zinitcall.exit4.init))
__zinitcall_exit_end = .;
需要新增上述段是因為 bootstrap_init 提供的對外接口,見 //utils/native/lite/include/ohos_init.h 文件,采用的是灌段的形式,最終會保存到上述鏈接段中。主要的服務(wù)自動初始化宏如下表格所示:
| 接口名 | 描述 |
|---|---|
| SYS_SERVICE_INIT(func) | 標(biāo)識核心系統(tǒng)服務(wù)的初始化啟動入口 |
| SYS_FEATURE_INIT(func) | 標(biāo)識核心系統(tǒng)功能的初始化啟動入口 |
| APP_SERVICE_INIT(func) | 標(biāo)識應(yīng)用層服務(wù)的初始化啟動入口 |
| APP_FEATURE_INIT(func) | 標(biāo)識應(yīng)用層功能的初始化啟動入口 |
說明:
通過上面加載的組件編譯出來的 lib 文件需要手動加入強(qiáng)制鏈接。
如在vendor/bestechnic/display_demo/config.json 中配置了 bootstrap_lite 部件
{
"subsystem": "startup",
"components": [
{
"component": "bootstrap_lite"
},
...
]
},
bootstrap_lite 部件會編譯 //base/startup/bootstrap_lite/services/source/bootstrap_service.c,該文件中,通過 SYS_SERVICE_INIT 將 Init 函數(shù)符號灌段到 __zinitcall_sys_service_start 和 __zinitcall_sys_service_end 中,由于 Init 函數(shù)是沒有顯式調(diào)用它,所以需要將它強(qiáng)制鏈接到最終的鏡像。如下:
static void Init(void)
{
static Bootstrap bootstrap;
bootstrap.GetName = GetName;
bootstrap.Initialize = Initialize;
bootstrap.MessageHandle = MessageHandle;
bootstrap.GetTaskConfig = GetTaskConfig;
bootstrap.flag = FALSE;
SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);
}
SYS_SERVICE_INIT(Init); --- 通過SYS啟動即SYS_INIT啟動就需要強(qiáng)制鏈接生成的lib
在//base/startup/bootstrap_lite/services/source/BUILD.gn 文件中,描述了在 out/v200zr/display_demo/libs 生成 libbootstrap.a,如下:
static_library("bootstrap") {
sources = [
"bootstrap_service.c",
"system_init.c",
]
....
那么需要在vendor/bestechnic/display_demo/config.json 配置強(qiáng)制鏈接庫 bootstrap,如下:
"bin_list": [
{
"elf_name": "wifiiot",
"bsp_target_name": "best2600w_liteos",
"signature": "false",
"burn_name": "rtos_main",
"enable": "true",
"force_link_libs": [
"bootstrap", --- 強(qiáng)制鏈接libbootstrap.a
...
]
},
適配 syspara_lite 部件時,系統(tǒng)參數(shù)會最終寫到文件中進(jìn)行持久化保存。在輕量系統(tǒng)中,文件操作相關(guān)接口有 POSIX 接口與 HalFiles 接口這兩套實(shí)現(xiàn)。
因為對接內(nèi)核的文件系統(tǒng),采用 POSIX 相關(guān)的接口,所以 features 字段中需要增加 enable_ohos_startup_syspara_lite_use_posix_file_api = true。
如果對接 HalFiles 相關(guān)的接口實(shí)現(xiàn)的,則無須修改。
在適配 GetSerial 接口時,開發(fā)板不像產(chǎn)線生產(chǎn)過程那樣,會寫入一個具體的 Serial Number,因而需要確定一個數(shù)據(jù)對開發(fā)板進(jìn)行唯一標(biāo)識。本案例采用 WiFi Mac 地址進(jìn)行適配。
#define ETH_ALEN 6
#define MAC_BITS 4
#define MAC_HIGH_MASK 0xf0
#define MAC_LOW_MASK 0x0f
#define HEX_A 0xa
#define CHAR_NUM_OFFSET 0x30
#define CHAR_CAPITAL_OFFSET 0x37
#define STR_END_FLAG '\0'
typedef unsigned char u8;
static char serialNumber[2*ETH_ALEN + 1]; --- 最后一位留作'\0'結(jié)束符標(biāo)識
static char Hex2Char(u8 hex)
{
if (hex < HEX_A) {
return hex + CHAR_NUM_OFFSET; --- 將數(shù)值0轉(zhuǎn)為char的'0'
} else {
return hex + CHAR_CAPITAL_OFFSET; --- 將數(shù)值0xa轉(zhuǎn)為char的'A'
}
}
const char* HalGetSerial(void)
{
char macAddr[ETH_ALEN];
// as devboard has no production serial number, we just
// use wifi mac address as device serial number.
if (serialNumber[0] == STR_END_FLAG) { --- 只有第一次調(diào)用時,才去獲取mac地址
extern int bwifi_get_own_mac(u8 *addr);
bwifi_get_own_mac(macAddr); --- 獲取mac地址
int j = 0;
for (int i = 0; i < ETH_ALEN; i++) {
u8 lowFour, highFour;
highFour = (macAddr[i] & MAC_HIGH_MASK) >> MAC_BITS;
serialNumber[j] = Hex2Char(highFour);
j++;
lowFour = macAddr[i] & MAC_LOW_MASK;
serialNumber[j] = Hex2Char(lowFour);
j++;
} --- 將mac地址值轉(zhuǎn)化為serial number
}
return serialNumber;
}
DFX 子系統(tǒng)適配
進(jìn)行 DFX 子系統(tǒng)適配需要添加 hilog_lite 部件,直接在 config.json 文件配置即可。
{
"subsystem": "hiviewdfx",
"components": [
{
"component": "hilog_lite",
"optional": "true"
}
]
},
配置完成之后,在 //device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c 中注冊日志輸出實(shí)現(xiàn)函數(shù)。
boolean HilogProc_Impl(const HiLogContent *hilogContent, uint32 len)
{
char tempOutStr[LOG_FMT_MAX_LEN] = {0};
if (LogContentFmt(tempOutStr, sizeof(tempOutStr), hilogContent) > 0) {
printf(tempOutStr);
}
return TRUE;
}
HiviewRegisterHilogProc(HilogProc_Impl);
系統(tǒng)服務(wù)管理子系統(tǒng)適配
進(jìn)行系統(tǒng)服務(wù)管理子系統(tǒng)適配需要添加 samgr_lite 部件,直接在 config.json 配置即可。
{
"subsystem": "systemabilitymgr",
"components": [
{
"component": "samgr_lite",
"features": [
"config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096"
]
}
]
},
在輕量系統(tǒng)中,samgr_lite 配置的共享任務(wù)棧大小默認(rèn)為 0x800。當(dāng)函數(shù)調(diào)用棧較大時,會出現(xiàn)棧溢出的問題。在本次適配過程中,將其調(diào)整為 0x1000。
安全子系統(tǒng)適配
進(jìn)行安全子系統(tǒng)適配需要添加 huks/deviceauth_lite 部件,直接在 config.json 配置即可。
{
"subsystem": "security",
"components": [
{
"component": "huks",
"features": [
"huks_use_lite_storage = true",
"huks_use_hardware_root_key = true",
"huks_config_file = \"hks_config_lite.h\"",
"huks_key_store_path = \"/data/\"",
"ohos_security_huks_mbedtls_porting_path = \"http://device/soc/bestechnic/hals/mbedtls\""
]
},
{
"component": "deviceauth_lite",
"features": [
"deviceauth_storage_path = \"/data/\"",
"deviceauth_hichain_thread_stack_size = 9472"
]
}
]
}
huks 部件適配時,huks_key_store_path 配置選項用于指定存放秘鑰路徑,ohos_security_huks_mbedtls_porting_path 配置選項用于指定進(jìn)行 mbedtls 適配的目錄,用于芯片對 mbedtls 進(jìn)行硬件隨機(jī)數(shù)等適配。
deviceauth_lite 部件適配時,deviceauth_storage_path 配置選項用于指定存放設(shè)備認(rèn)證信息的路徑,deviceauth_hichain_thread_stack_size 用于指定線程棧大小。
媒體子系統(tǒng)適配
進(jìn)行媒體子系統(tǒng)適配需要添加 histreamer 部件,直接在 config.json 配置即可。
{
"subsystem": "multimedia",
"components": [
{
"component": "histreamer",
"features": [
"histreamer_enable_plugin_hdi_adapter = true",
"histreamer_enable_plugin_minimp3_adapter = true",
"histreamer_enable_plugin_ffmpeg_adapter = false",
"config_ohos_histreamer_stack_size = 65536"
]
}
]
},
histreamer 部件配置項說明如下:
| 配置項 | 說明 |
|---|---|
| histreamer_enable_plugin_hdi_adapter | 是否使能 histreamer 對接到 hdi 接口 |
| histreamer_enable_plugin_minimp3_adapter | 是否使能插件適配 minimp3 |
| histreamer_enable_plugin_ffmpeg_adapter | 是否使能插件適配 FFmpeg |
| config_ohos_histreamer_stack_size | histreamer 棧大小設(shè)置 |
公共基礎(chǔ)庫子系統(tǒng)適配
進(jìn)行公共基礎(chǔ)庫子系統(tǒng)適配需要添加 kv_store/js_builtin/timer_task/kal_timer 部件,直接在 config.json 配置即可。
{
"subsystem": "utils",
"components": [
{
"component": "kv_store",
"features": [
"enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true"
]
},
{
"component": "js_builtin"
},
{
"component": "timer_task"
},
{
"component": "kal_timer",
}
]
},
與適配 syspara_lite 部件類似,適配 kv_store 部件時,鍵值對會寫到文件中。在輕量系統(tǒng)中,文件操作相關(guān)接口有 POSIX 接口與 HalFiles 接口這兩套實(shí)現(xiàn)。因為對接內(nèi)核的文件系統(tǒng),采用 POSIX 相關(guān)的接口,所以 features 需要增加 enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true。如果對接 HalFiles 相關(guān)的接口實(shí)現(xiàn)的,則無須修改。
圖形子系統(tǒng)適配
進(jìn)行圖形子系統(tǒng)適配需要添加 graphic_utils 部件,直接在 config.json 配置即可。
{
"components": [
{
"component": "graphic_utils",
"features": [
"enable_ohos_graphic_utils_product_config = true"
]
},
{
"component": "ui"
}
]
},
graphic 配置文件見 //vendor/bestechnic/display_demo/graphic_config/product_graphic_lite_config.h。
graphic 適配見 //device/soc/bestechnic/bes2600/liteos_m/components/ui, 主要功能如下:
-
display_device:實(shí)例化BaseGfxEngine。 -
touch_input:實(shí)例化PointerInputDevice。 -
UiMainTask:初始化字體引擎,執(zhí)行渲染任務(wù)等。
圖形子系統(tǒng)層次:
aafwk_lite + appexecfwk_lite (AAFWK + APPEXECFWK)
|
ace_engine_lite + jerryscript + i18n_lite + resmgr_lite + utils/native/lite/... (ACE,JS引擎及其依賴)
|
arkui_ui_lite + graphic_utils (圖形框架)
|
giflib + libjpeg + libpng + qrcodegen + freetype... (圖形第三方庫)
圖形應(yīng)用示例見文件 //vendor/bestechnic/display_demo/tests/app.cpp,如下:
/* ui app entry */
void RunApp()
{
#ifdef UI_TEST
AnimatorDemoStart(); --- native ui demo
#elif defined(ABILITY_TEST)
StartJSApp(); --- js demo
#endif
}
void AppEntry(void)
{
UiMain();
}
APP_FEATURE_INIT(AppEntry);
ACE 開發(fā)框架子系統(tǒng)適配
進(jìn)行 ACE 開發(fā)框架子系統(tǒng)適配需要添加 ace_engine_lite 部件,直接在 config.json 配置即可。
{
"subsystem": "ace",
"components": [
{
"component": "ace_engine_lite",
"features": [
"enable_ohos_ace_engine_lite_product_config = true"
]
}
]
},
ace_engine_lite 部件配置文件見 //vendor/bestechnic/display_demo/ace_lite_config/product_acelite_config.h。
ace_lite 的應(yīng)用采用 js 語言進(jìn)行開發(fā),詳細(xì)步驟如下:
- 用
DevEco Studio編寫 js 應(yīng)用。 - 使用預(yù)覽功能進(jìn)行預(yù)覽,并且得到 js 包:
entry\.preview\intermediates\res\debug\lite\assets\js\default。 - 將 js 包放到對應(yīng)的文件系統(tǒng)目錄下,文件系統(tǒng)路徑為
vendor/bestechnic/display_demo/fs/data/data/js,如下:
├── app.js
├── common
├── i18n
├── manifest.json
└── pages
- 最終編譯生成系統(tǒng)鏡像,燒錄到單板后,系統(tǒng)會從
app.js加載啟動ace的應(yīng)用。
元能力子系統(tǒng)適配
進(jìn)行元能力子系統(tǒng)適配需要添加 aafwk_lite 部件,直接在 config.json 配置即可。
{
"subsystem": "aafwk",
"components": [
{
"component": "aafwk_lite",
"features": [
"enable_ohos_appexecfwk_feature_ability = true", --- 支持FA特性,即包含圖形能力
"config_ohos_aafwk_ams_task_size = 4096" --- 配置aafwk棧的大小
]
}
]
},
aafwk_lite 相關(guān)的應(yīng)用樣例見 vendor/bestechnic/display_demo/tests/ability 目錄,包含 launcher 和 js app 這兩類應(yīng)用,應(yīng)用的函數(shù)調(diào)用流程描述如下:
-
launcher應(yīng)用,通過InstallLauncher安裝BundleName為"com.example.launcher"的native ui應(yīng)用,在AbilityMgrSliteFeature啟動后會調(diào)用AbilityMgrHandler::StartLauncher()啟動launcher應(yīng)用。 -
StartJSApp應(yīng)用,通過StartAbility啟動任意Want,通過將want data傳遞JS_APP_PATH,
SetWantData(&want, JS_APP_PATH, strlen(JS_APP_PATH) + 1)。
包管理子系統(tǒng)適配
進(jìn)行包管理子系統(tǒng)適配需要添加 appexecfwk_lite 部件,直接在 config.json 配置即可。
{
"subsystem": "appexecfwk",
"components": [
{
"component": "appexecfwk_lite"
}
]
},
兼容性認(rèn)證
XTS 用例
XTS 測試參考資料見 xts 參考資料,進(jìn)行 XTS 子系統(tǒng)適配需要添加 xts_acts/xts_tools 部件,直接在 config.json 配置即可,配置如下:
{
"subsystem": "xts",
"components": [
{ "component": "xts_acts", "features":
[
"config_ohos_xts_acts_utils_lite_kv_store_data_path = "/data"",
"enable_ohos_test_xts_acts_use_thirdparty_lwip = true"
]
},
{ "component": "xts_tools", "features":[] }
]
}
其中,
-
config_ohos_xts_acts_utils_lite_kv_store_data_path是配置掛載文件系統(tǒng)根目錄的名字。 -
enable_ohos_test_xts_acts_use_thirdparty_lwip表示如果使用thirdparty/lwip目錄下的源碼編譯,則設(shè)置為true,否則設(shè)置為false。
全部跑完會有顯示xx Tests xx Failures xx Ignored,如下:
...
[16:53:43:438]../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:793:testKvStoreMaxSize004:PASS
[16:53:43:438]+-------------------------------------------+
[16:53:43:438]
[16:53:43:438]-----------------------
[16:53:43:438]32 Tests 0 Failures 0 Ignored
[16:53:43:438]OK
[16:53:43:439]All the test suites finished!
報告提交
將上圖 XTS 用例的情況保存為測試報告,上傳到 OpenHarmony 兼容性測試網(wǎng)站進(jìn)行認(rèn)證,作為 sig 倉庫轉(zhuǎn)正到 master 倉庫的必要條件。詳細(xì)步驟如下:
步驟 1:將 XTS 測試報告壓縮成 zip 文件。
步驟 2:生成測試報告的 SHA 校驗碼。本案例是將 zip 文件傳到在線生成 hash 的 網(wǎng)站 生成 SHA 校驗碼。
步驟 3:進(jìn)入 OpenHarmony 兼容性測試網(wǎng)站上傳報告。
- 其中
API Level填寫報告中的"sdkApiLevel"字段 -
OS版本號填寫報告中的"OS Version"字段。
寫在最后
如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙:
- 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評論』,才是我創(chuàng)造的動力。
- 關(guān)注小編,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識。
- 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點(diǎn),請移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu
