輕量帶屏解決方案之恒玄芯片移植案例

本文章基于恒玄科技BES2600W芯片的歐智通 Multi-modal V200Z-R開發(fā)板 ,進(jìn)行輕量帶屏開發(fā)板的標(biāo)準(zhǔn)移植,開發(fā)了智能開關(guān)面板樣例,同時(shí)實(shí)現(xiàn)了ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF等部件基于OpenHarmony LiteOS-M內(nèi)核的適配。移植架構(gòu)上采用BoardSoC分離的方案,工具鏈Newlib C庫(kù)與Musl C庫(kù)可選,LiteOS-M內(nèi)核編譯采用gn結(jié)合Kconfig圖形化配置等需求。

編譯構(gòu)建

目錄規(guī)劃

本案例在芯片移植架構(gòu)方面進(jìn)行了一些改進(jìn),以前的芯片適配目錄規(guī)劃為:

device
└── <device_company>
    └── <device_name>

這樣會(huì)導(dǎo)致,小熊派BearPi-HM Nano開發(fā)板與潤(rùn)和的HiSpark Pegasus開發(fā)板使用小海思的hi3861SoC時(shí),需要在這兩款開發(fā)板里面都放置一份重復(fù)的代碼。為了解決該問題,本案例將單板廠商與SoC廠商進(jìn)行分離,可以參考Board和SoC解耦的設(shè)計(jì)思路,并把芯片適配目錄規(guī)劃為:

device
├── board                                --- 單板廠商目錄
│   └── fnlink                           --- 單板廠商名字:歐智通
│       └── v200zr                       --- 單板名:v200zr
└── soc                                  --- SoC廠商目錄
    └── bestechnic                       --- SoC廠商名字:恒玄
        └── bes2600                      --- SoC Series名:bes2600是一個(gè)系列,里面包含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è)置整個(gè)項(xiàng)目的根目錄、單板目錄、產(chǎn)品目錄、單板公司名等環(huán)境變量,為編譯做準(zhǔn)備。

具體的預(yù)編譯適配步驟如下:

  1. vendor/bestechnic/display_demo目錄下新增config.json文件,用于描述這個(gè)產(chǎn)品樣例所使用的單板、內(nèi)核等信息,描述信息可參考如下內(nèi)容:
{
  "product_name": "display_demo",       --- 用于hb set進(jìn)行選擇時(shí),顯示的產(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",           --- 單板廠商名,用于編譯時(shí)找到/device/board/fnlink目錄
  "board": "v200zr",                    --- 單板名,用于編譯時(shí)找到/device/board/fnlink/v200zr目錄
  "kernel_type": "liteos_m",            --- 內(nèi)核類型,因?yàn)镺penHarmony支持多內(nèi)核,一塊單板可能適配了多個(gè)內(nèi)核,所以需要指定某個(gè)內(nèi)核進(jìn)行編譯
  "kernel_version": "3.0.0",            --- 內(nèi)核版本,一塊單板可能適配了多個(gè)linux內(nèi)核版本,所以需要指定某個(gè)具體的內(nèi)核版本進(jìn)行編譯
  "subsystems": [ ]                     --- 選擇所需要編譯構(gòu)建的子系統(tǒng)
}
  1. device/board/fnlink/v200zr/liteos_m目錄下新增config.gni文件,用于描述這個(gè)產(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對(duì)應(yīng)

# Kernel version.
kernel_version = "3.0.0"                --- 內(nèi)核版本,跟config.json中kernel_version對(duì)應(yīng)
  1. 驗(yàn)證hb set配置是否正確,輸入hb set能夠顯示如下圖片表示配置正確。

執(zhí)行hb set輸入項(xiàng)目根目錄,并且回車,hb命令會(huì)遍歷所有//vendor/<product_company>/<product_name>目錄下的config.json,給出可選產(chǎn)品編譯選項(xiàng),config.jsonproduct_name用于顯示產(chǎn)品名,device_companyboard用于關(guān)聯(lián)出//device/board/<device_company>/<board>目錄,并且匹配<any_dir_name>/config.gni文件,如果能夠匹配多個(gè)文件,表示該單板適配了多個(gè)內(nèi)核,那么可以根據(jù)config.jsonkernel_typekernel_version來唯一匹配config.gnikernel_typekernel_version,即可確定了需要編譯適配了哪個(gè)內(nèi)核的單板。

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

在執(zhí)行hb build之前,需要準(zhǔn)備好LiteOS-M內(nèi)核適配,具體適配步驟請(qǐng)參內(nèi)核移植。

內(nèi)核移植

內(nèi)核移植需要完成LiteOS-M Kconfig適配、gn的編譯構(gòu)建和內(nèi)核啟動(dòng)最小適配。

LiteOS-M Kconfig適配

//kernel/liteos_m目錄下執(zhí)行make menuconfig命令,完成編譯配置選項(xiàng)的選擇。在Makefile文件中,會(huì)將hb env的結(jié)果轉(zhuǎn)換成環(huán)境變量,即PRODUCT_PATH、DEVICE_PATHBOARD_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,表示這個(gè)文件是否存在可選,r表示relative,表示這個(gè)文件相對(duì)當(dāng)前文件的相對(duì)路徑。

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)來,因?yàn)閱伟鍙S商A提供擴(kuò)展板可以給單板廠商B使用,所以這里使用*匹配所有的擴(kuò)展板,而非BOARD_COMPANY。另外由于OpenHarmony支持多內(nèi)核設(shè)計(jì),Kconfig文件采用liteos_m作為后綴,在進(jìn)行單板適配過程中,其他內(nèi)核在適配過程中,可以使用對(duì)應(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單板的配置選項(xiàng)
│   ├── Kconfig.liteos_m.defconfig.board         --- 提供v200zr單板的默認(rèn)配置項(xiàng)
│   └── 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需要配置選擇該單板的選項(xiàng),以及它依賴的SoC,如下:

config BOARD_v200zr
    bool "select board v200zr"
    depends on SOC_BES2600W      --- v200zr單板用的bes2600w的SoC,只有 bes2600w的SoC被選擇后,v200zr單板配置選項(xiàng)才可見,可以被選擇。

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選項(xiàng)
    bool "Bestechnic 2600 Series"
    select ARM                       --- 選擇bes2600后,默認(rèn)選擇ARM架構(gòu)
    select SOC_COMPANY_BESTECHNIC    --- 選擇bes2600后,默認(rèn)選擇bestechnic芯片公司,驅(qū)動(dòng)會(huì)依賴這個(gè)宏配置,選擇配置編譯對(duì)應(yīng)廠商的驅(qū)動(dòng)
    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下有多少個(gè)具體的SoC可供選擇,如下:

choice
    prompt "Bestechnic 2600 series SoC"
    depends on SOC_SERIES_BES2600    --- 只有選擇了bes2600 Series后,才會(huì)出現(xiàn)如下配置選項(xiàng)

config SOC_BES2600W                  --- 增加bes2600w SoC配置選擇項(xiàng)
    bool "SoC BES2600w"

endchoice

bes2600/Kconfig.liteos_m.defconfig.series 需要提供bes2600 SoC series選擇后的默認(rèn)配置,如下:

if SOC_SERIES_BES2600                            --- 選擇了bes2600 Series后,才會(huì)增加如下默認(rèn)配置選項(xiàng)

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 menuconfigdefconfig保存路徑:

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)"

在這個(gè)例子中,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ū)?code>SoC Series/SoC/Board進(jìn)行選擇,如下:

結(jié)果將自動(dòng)保存在$(PRODUCT_PATH)/kernel_configs/debug.config,下次執(zhí)行make menuconfig時(shí)會(huì)導(dǎo)出保存的結(jié)果。

gn編譯適配

在上一步Kconfig的圖形化配置后,將其生成的配置結(jié)果可以作為gn編譯的輸入,以控制不同模塊是否編譯。另外為了解決之前gn編寫時(shí),隨意include的問題,內(nèi)核編譯做了模塊化編譯的設(shè)計(jì),使得整個(gè)編譯邏輯更加清晰,設(shè)計(jì)思路請(qǐng)參考LiteOS-M內(nèi)核BUILD.gn編寫指南。

kernel/liteos_m/BUILD.gn 中,指定了BoardSoC的編譯入口為//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è)計(jì),對(duì)于LiteOS-M內(nèi)核適配,需要用宏來隔離
  import("http://kernel/liteos_m/liteos.gni")                 --- 引入內(nèi)核gn編寫模板
  module_name = get_path_info(rebase_path("."), "name")  --- 動(dòng)態(tài)獲取當(dāng)前文件目錄作為模塊名,防止目錄名修改后,這里還需要跟著修改
  module_group(module_name) {                            --- 采用module_group模板
    modules = [                                          --- 添加需要編譯的模塊
    ]
  }
}

同理//device/soc/bestechnic/BUILD.gn也是一樣。

內(nèi)核啟動(dòng)適配

系統(tǒng)啟動(dòng)流程分為三個(gè)階段:

階段名稱 分區(qū)規(guī)劃 描述
BOOT1 [0, 0x10000] 第一階段啟動(dòng),進(jìn)行固件啟動(dòng)
BOOT2 [0x2C010000, 0x2C020000] 第二階段啟動(dòng),進(jìn)行OTA升級(jí)啟動(dòng)
RTOS_MAIN [0x2C080000, 0x2C860000] 第三階段啟動(dòng),進(jìn)行內(nèi)核啟動(dòng)

在第三階段內(nèi)核啟動(dòng)中,需要適配的文件路徑在 //device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/rtos/liteos/liteos_m/board.c

內(nèi)核啟動(dòng)適配總體思路如下:

  1. 中斷向量的初始化os_vector_init ,初始化中斷的處理函數(shù)。
  2. 內(nèi)核初始化osKernelInitialize 。
  3. 創(chuàng)建線程board_main,進(jìn)行芯片平臺(tái)初始化。
  4. 內(nèi)核啟動(dòng),開始調(diào)度線程osKernelStart 。

其中,本章節(jié)詳細(xì)對(duì)第3步進(jìn)行展開,其他幾步為對(duì)內(nèi)核函數(shù)調(diào)用,不作詳細(xì)描述。

第3步中board_main在啟動(dòng)OHOS_SystemInit之前,需要初始化必要的動(dòng)作,如下:

...
    if(!ret) {
        ...
        OhosSystemAdapterHooks();    --- 系統(tǒng)啟動(dòng)時(shí)候設(shè)置鉤子,啟動(dòng)OpenHarmonyOHOS_SystemInit的之前完成打印和驅(qū)動(dòng)的初始化
        ...
        OHOS_SystemInit();           --- 啟動(dòng)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ū)動(dòng)初始化,這個(gè)過程會(huì)調(diào)用單板代碼中的驅(qū)動(dòng)配置文件hdf.hcs以及drivers源碼實(shí)現(xiàn)
    return 0;
}

littlefs文件系統(tǒng)移植

V200Z-R開發(fā)板開發(fā)板采用最大32MB的支持XIPNor Flash,文件系統(tǒng)可以使用example,適配過程中,需要在指定路徑下放置文件系統(tǒng)預(yù)置文件,根據(jù)配置可自動(dòng)生成文件系統(tǒng)鏡像,可以實(shí)現(xiàn)自動(dòng)化生成和打包到燒錄包中。

  1. 配置指定目錄放置打包文件系統(tǒng)config.json,通過flash_partition_dir指定目錄:
  "flash_partition_dir": "fs"    --- 表示在vendor/bestechnic/display_demo/fs目錄下放置文件系統(tǒng)預(yù)置文件
  1. 在指定目錄vendor/bestechnic/display_demo/fs下放置兩部分內(nèi)容:
  • wifi_Download_cfg.yaml:鏡像的燒錄配置文件,可以根據(jù)實(shí)際情況調(diào)整分區(qū)。
  • /data/data:第一個(gè)/data是掛載的根目錄;第二個(gè)data是根目錄里面的data目錄,里面可以存放預(yù)置文件,或者在第二個(gè)data的同級(jí)目錄再創(chuàng)建一個(gè)目錄,打包的時(shí)候只認(rèn)第一個(gè)data掛載根目錄。
  1. config.json中根據(jù)wifi_Download_cfg.yaml最后調(diào)整結(jié)果。
  • fs_src配置文件系統(tǒng)掛載名字。
  • fs_name是最后生成文件系統(tǒng)的名字。
  • block_size配置成4K對(duì)齊,建議不修改。
  • fs_size是生成文件系統(tǒng)的大小。
  • burn_name是燒錄bin名字的大小。
  • enable 表示是否生成這個(gè)文件系統(tǒng)
  1. //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庫(kù)適配

在輕量系統(tǒng)中,C庫(kù)適配比較復(fù)雜,設(shè)計(jì)思路請(qǐng)參考LiteOS-M內(nèi)核支持musl與newlib平滑切換方案,由于我們的工具鏈采用 gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 自帶newlib的C庫(kù),那么系統(tǒng)移植整體采用newlib的C庫(kù)。那么在內(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ù)的符號(hào),因此需要用到gccwrap的鏈接選項(xiàng)替換這些函數(shù)符號(hào)為內(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鏈接選項(xiàng)。

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等適配

參考 https://sourceware.org/newlib/libc.html#vfprintf ,實(shí)現(xiàn) vprintf, vfprintf, printf, snprintfsprintf。

類似malloc適配,首先要提供這些函數(shù)的實(shí)現(xiàn),//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/printf.c,本案例直接采用開源協(xié)議友好的實(shí)現(xiàn)。與malloc適配不同的是,這個(gè)函數(shù)由芯片原廠提供。因?yàn)榫痛蛴碚f,根據(jù)項(xiàng)目的需要,實(shí)現(xiàn)可大可小,內(nèi)核不方便提供統(tǒng)一的實(shí)現(xiàn)。

然后,在//device/board/fnlink/v200zr/liteos_m/config.gni的新增這些函數(shù)的wrap鏈接選項(xiàng)。

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);
}
……

板級(jí)系統(tǒng)移植

驅(qū)動(dòng)移植

SoC芯片平臺(tái)HDF驅(qū)動(dòng)移植

驅(qū)動(dòng)適配相關(guān)文件放置在drivers/adapter/platform中,對(duì)應(yīng)有gpio,i2c,pwmspi,uartwatchdog,都是通過HDF機(jī)制加載,本章節(jié)以gpio為例進(jìn)行詳細(xì)說明。

GPIO驅(qū)動(dòng)適配

gpio驅(qū)動(dòng)適配需要完成編譯的適配、源碼的適配。

//drivers/adapter/platform/gpio/BUILD.gn文件中,描述了恒玄gpio驅(qū)動(dòng)的編譯適配。如下:

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ū)動(dòng)編譯
    sources += [ "gpio_bes.c" ]
  }

  include_dirs = [ "." ]
}

//drivers/adapter/platform/gpio/gpio_bes.c文件中,描述了恒玄gpio驅(qū)動(dòng)的源碼適配。
首先,按照OpenHarmonyHDF驅(qū)動(dòng)框架加載驅(qū)動(dòng)基本適配框架,如下:

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ū)動(dòng)

然后,在初始化的時(shí)候會(huì)獲取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è)計(jì)思想見bes 驅(qū)動(dòng)適配PR的評(píng)論。

Board外設(shè)器件HDF驅(qū)動(dòng)移植

Board外設(shè)器件表示通過SoC平臺(tái)總線連接的外設(shè)器件,在本案例中,顯示屏屬于外設(shè)器件,其驅(qū)動(dòng)適配放在//device/board/fnlink/drivers/liteos_m目錄中。

顯示驅(qū)動(dòng)適配

SoC驅(qū)動(dòng)適配,在//device/board/fnlink/drivers/liteos_m/display/BUILD.gn文件中,根據(jù)hdf_driver模板加載驅(qū)動(dòng)模塊,如下:

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ū)動(dòng)框架加載顯示驅(qū)動(dòng),如下:

static struct HdfDriverEntry g_ZZW395DriverEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_PANEL_ZZW395",
    .Bind = PanelDriverBind,
    .Init = PanelDriverInit,
    .Release = PanelDriverRelease,
};

HDF_INIT(g_ZZW395DriverEntry);

其中的驅(qū)動(dòng)參數(shù)根據(jù)hcs配置,在PanelDriverInit初始化時(shí)加載,如下:

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中增加對(duì)應(yīng)子系統(tǒng)和部件,這樣編譯系統(tǒng)會(huì)將該部件納入編譯目標(biāo)中。
  • 針對(duì)該部件的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" ]    --- 因?yàn)閣ifi_lite只提供頭文件,不提供wifi的具體實(shí)現(xiàn),所以wifi模塊暴露出適配的目錄路徑提供給硬件廠商來適配,廠商提供wifi協(xié)議棧源碼實(shí)現(xiàn)。
}

group("wifi") {
  public_configs = [ ":include" ]
}

因?yàn)樵诒景咐校?code>wifi屬于SoC提供的功能,所以適配源碼放在SoC//device/soc/bestechnic/hals/communication/wifi_lite/wifiservice目錄下,包含wifi_device.cwifi_hotspot.c分別適配wifi_device.hwifi_hotspot.h。如下:

……
WifiErrorCode Scan(void)     --- wifi_device.c中掃描wifi熱點(diǎn)的函數(shù),對(duì)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信號(hào)熱點(diǎn)函數(shù),對(duì)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配置選項(xiàng)保持不變,軟總線會(huì)依賴這些配置選項(xiàng),并且新增硬件適配的配置項(xiàng),如下:

#ifndef _PORTING_LWIPOPTS_H_
#define _PORTING_LWIPOPTS_H_

#include_next "lwip/lwipopts.h"              --- 保持原來的配置項(xiàng)不變

#define LWIP_NETIF_STATUS_CALLBACK      1
#define LWIP_CHECKSUM_ON_COPY           0
#define CHECKSUM_GEN_UDP                0    --- 新增硬件適配選項(xiàng)

#endif /* _PORTING_LWIPOPTS_H_ */

//device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/porting/src/ethernetif.c文件中,說明對(duì)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配置選項(xiàng)可供移植過程進(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è)置軟總線日志級(jí)別設(shè)置
    ├── softbus_config_adapter.c
    ├── softbus_config_adapter.h
    └── softbus_config_type.h

config.gni文件中規(guī)定了以下配置項(xiàng):

配置項(xiàng) 描述
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 是否開啟基于賬號(hào)認(rèn)證功能
dsoftbus_feature_qos 是否開啟QoS功能

softbus_config_adapter.c文件中規(guī)定了以下配置項(xiàng):

配置項(xiàng) 描述
SOFTBUS_INT_MAX_BYTES_LENGTH SendBytes發(fā)送最大Bytes長(zhǎng)度
SOFTBUS_INT_MAX_MESSAGE_LENGTH SendMessage發(fā)送最大消息的長(zhǎng)度
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 日志級(jí)別設(shè)置
SOFTBUS_STR_STORAGE_DIRECTORY 存儲(chǔ)目錄設(shè)置

因?yàn)檐浛偩€配置了后,不會(huì)默認(rèn)啟動(dòng),所以需要在通過啟動(dòng)框架調(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部件需要通過啟動(dòng)框架調(diào)用StartDBinderService函數(shù),由于該函數(shù)正常運(yùn)行依賴主機(jī)已經(jīng)獲取IP地址,因此在LWIP協(xié)議棧注冊(cè)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);

啟動(dòng)恢復(fù)子系統(tǒng)適配

啟動(dòng)恢復(fù)子系統(tǒng)適配bootstrap_lite/syspara_lite兩個(gè)部件。請(qǐng)?jiān)?code>vendor/bestechnic_bak/display_demo/config.json中新增對(duì)應(yīng)的配置選項(xià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部件時(shí),需要在連接腳本文件//device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/out/best2600w_liteos/_best2001.lds中手動(dòng)新增如下段:

       __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 = .;

需要新增上述段是因?yàn)?code>bootstrap_init提供的對(duì)外接口,見//utils/native/lite/include/ohos_init.h文件,采用的是灌段的形式,最終會(huì)保存到上述鏈接段中。主要的服務(wù)自動(dòng)初始化宏如下表格所示:

接口名 描述
SYS_SERVICE_INIT(func) 標(biāo)識(shí)核心系統(tǒng)服務(wù)的初始化啟動(dòng)入口
SYS_FEATURE_INIT(func) 標(biāo)識(shí)核心系統(tǒng)功能的初始化啟動(dòng)入口
APP_SERVICE_INIT(func) 標(biāo)識(shí)應(yīng)用層服務(wù)的初始化啟動(dòng)入口
APP_FEATURE_INIT(func) 標(biāo)識(shí)應(yīng)用層功能的初始化啟動(dòng)入口

說明:
通過上面加載的組件編譯出來的lib文件需要手動(dòng)加入強(qiáng)制鏈接。

如在 vendor/bestechnic/display_demo/config.json 中配置了bootstrap_lite 部件

    {
      "subsystem": "startup",
      "components": [
        {
          "component": "bootstrap_lite"
        },
        ...
      ]
    },

bootstrap_lite部件會(huì)編譯//base/startup/bootstrap_lite/services/source/bootstrap_service.c,該文件中,通過SYS_SERVICE_INITInit函數(shù)符號(hào)灌段到__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啟動(dòng)即SYS_INIT啟動(dòng)就需要強(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)制鏈接庫(kù)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部件時(shí),系統(tǒng)參數(shù)會(huì)最終寫到文件中進(jìn)行持久化保存。在輕量系統(tǒng)中,文件操作相關(guān)接口有POSIX接口與HalFiles接口這兩套實(shí)現(xiàn)。

因?yàn)閷?duì)接內(nèi)核的文件系統(tǒng),采用POSIX相關(guān)的接口,所以features字段中需要增加enable_ohos_startup_syspara_lite_use_posix_file_api = true

如果對(duì)接HalFiles相關(guān)的接口實(shí)現(xiàn)的,則無須修改。

在適配GetSerial接口時(shí),開發(fā)板不像產(chǎn)線生產(chǎn)過程那樣,會(huì)寫入一個(gè)具體的Serial Number,因而需要確定一個(gè)數(shù)據(jù)對(duì)開發(fā)板進(jìn)行唯一標(biāo)識(shí)。本案例采用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)識(shí)


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)用時(shí),才去獲取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中注冊(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)用棧較大時(shí),會(huì)出現(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部件適配時(shí),huks_key_store_path配置選項(xiàng)用于指定存放秘鑰路徑,ohos_security_huks_mbedtls_porting_path配置選項(xiàng)用于指定進(jìn)行mbedtls適配的目錄,用于芯片對(duì)mbedtls進(jìn)行硬件隨機(jī)數(shù)等適配。

deviceauth_lite部件適配時(shí),deviceauth_storage_path配置選項(xiàng)用于指定存放設(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部件配置項(xiàng)說明如下:

配置項(xiàng) 說明
histreamer_enable_plugin_hdi_adapter 是否使能histreamer對(duì)接到hdi接口
histreamer_enable_plugin_minimp3_adapter 是否使能插件適配minimp3
histreamer_enable_plugin_ffmpeg_adapter 是否使能插件適配FFmpeg
config_ohos_histreamer_stack_size histreamer棧大小設(shè)置

公共基礎(chǔ)庫(kù)子系統(tǒng)適配

進(jìn)行公共基礎(chǔ)庫(kù)子系統(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部件時(shí),鍵值對(duì)會(huì)寫到文件中。在輕量系統(tǒng)中,文件操作相關(guān)接口有POSIX接口與HalFiles接口這兩套實(shí)現(xiàn)。因?yàn)閷?duì)接內(nèi)核的文件系統(tǒng),采用POSIX相關(guān)的接口,所以features需要增加enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true。如果對(duì)接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... (圖形第三方庫(kù))

圖形應(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ì)步驟如下:

  1. DevEco Studio編寫js應(yīng)用,參考輕量級(jí)智能穿戴開發(fā)。
  2. 使用預(yù)覽功能進(jìn)行預(yù)覽,并且得到j(luò)s包:entry\.preview\intermediates\res\debug\lite\assets\js\default。
  3. 將js包放到對(duì)應(yīng)的文件系統(tǒng)目錄下,文件系統(tǒng)路徑為vendor/bestechnic/display_demo/fs/data/data/js,如下:
├── app.js
├── common
├── i18n
├── manifest.json
└── pages
  1. 最終編譯生成系統(tǒng)鏡像,燒錄到單板后,系統(tǒng)會(huì)從app.js加載啟動(dòng)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目錄,包含launcherjs app這兩類應(yīng)用,應(yīng)用的函數(shù)調(diào)用流程描述如下:

  1. launcher應(yīng)用,通過InstallLauncher安裝BundleName"com.example.launcher"native ui應(yīng)用,在AbilityMgrSliteFeature啟動(dòng)后會(huì)調(diào)用AbilityMgrHandler::StartLauncher()啟動(dòng)launcher應(yīng)用。

  2. StartJSApp應(yīng)用,通過StartAbility啟動(dòng)任意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)證

產(chǎn)品兼容性規(guī)范

產(chǎn)品兼容性規(guī)范文檔請(qǐng)參考產(chǎn)品兼容性SIG介紹。

XTS用例

XTS測(cè)試參考資料見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

全部跑完會(huì)有顯示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!

報(bào)告提交

將上圖XTS用例的情況保存為測(cè)試報(bào)告,上傳到OpenHarmony兼容性測(cè)試網(wǎng)站進(jìn)行認(rèn)證,作為sig倉(cāng)庫(kù)轉(zhuǎn)正到master倉(cāng)庫(kù)的必要條件。詳細(xì)步驟如下:

步驟1:將XTS測(cè)試報(bào)告壓縮成zip文件。

步驟2:生成測(cè)試報(bào)告的SHA校驗(yàn)碼。本案例是將zip文件傳到在線生成hash網(wǎng)站生成SHA校驗(yàn)碼。

步驟3:進(jìn)入OpenHarmony兼容性測(cè)試網(wǎng)站上傳報(bào)告。

  • 其中API Level填寫報(bào)告中的"sdkApiLevel"字段
  • OS版本號(hào)填寫報(bào)告中的"OS Version"字段。

todo

后續(xù)會(huì)補(bǔ)充以下方面的移植:

  • 藍(lán)牙
  • bms包安裝
  • 驗(yàn)證運(yùn)行JSbytecode
  • 分布式能力:dms、dm
  • 分布式音樂播放器樣例

porting-bes2600w-on-minisystem-display-demo.md

寫在最后

如果你覺得這篇內(nèi)容對(duì)你還蠻有幫助,我想邀請(qǐng)你幫我三個(gè)小忙

  • 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評(píng)論』,才是我創(chuàng)造的動(dòng)力。
  • 關(guān)注小編,同時(shí)可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識(shí)。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識(shí)點(diǎn),請(qǐng)移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容