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

本文章基于恒玄科技BES2600W芯片的歐智通 Multi-modal V200Z-R開發板 ,進行輕量帶屏開發板的標準移植,開發了智能開關面板樣例,同時實現了ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF等部件基于OpenHarmony LiteOS-M內核的適配。移植架構上采用BoardSoC分離的方案,工具鏈Newlib C庫與Musl C庫可選,LiteOS-M內核編譯采用gn結合Kconfig圖形化配置等需求。

編譯構建

目錄規劃

本案例在芯片移植架構方面進行了一些改進,以前的芯片適配目錄規劃為:

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

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

device
├── board                                --- 單板廠商目錄
│   └── fnlink                           --- 單板廠商名字:歐智通
│       └── v200zr                       --- 單板名:v200zr
└── soc                                  --- SoC廠商目錄
    └── bestechnic                       --- SoC廠商名字:恒玄
        └── bes2600                      --- SoC Series名:bes2600是一個系列,里面包含bes2600w等SoC名

產品樣例目錄規劃為:

vendor
└── bestechnic                           --- 開發產品樣例廠商目錄,恒玄開發的帶屏樣例,因此以bestechnic命名
    └── display_demo                     --- 產品名字:以智能開關面板的帶屏顯示樣例

預編譯適配

在進行移植之前,需要進行預編譯適配。

預編譯適配主要使用hb set命令,設置整個項目的根目錄、單板目錄、產品目錄、單板公司名等環境變量,為編譯做準備。

具體的預編譯適配步驟如下:

  1. vendor/bestechnic/display_demo目錄下新增config.json文件,用于描述這個產品樣例所使用的單板、內核等信息,描述信息可參考如下內容:
{
  "product_name": "display_demo",       --- 用于hb set進行選擇時,顯示的產品名稱
  "type": "mini",                       --- 構建系統的類型,mini/small/standard
  "version": "3.0",                     --- 構建系統的版本,1.0/2.0/3.0
  "device_company": "fnlink",           --- 單板廠商名,用于編譯時找到/device/board/fnlink目錄
  "board": "v200zr",                    --- 單板名,用于編譯時找到/device/board/fnlink/v200zr目錄
  "kernel_type": "liteos_m",            --- 內核類型,因為OpenHarmony支持多內核,一塊單板可能適配了多個內核,所以需要指定某個內核進行編譯
  "kernel_version": "3.0.0",            --- 內核版本,一塊單板可能適配了多個linux內核版本,所以需要指定某個具體的內核版本進行編譯
  "subsystems": [ ]                     --- 選擇所需要編譯構建的子系統
}
  1. device/board/fnlink/v200zr/liteos_m目錄下新增config.gni文件,用于描述這個產品樣例所使用的單板、內核等信息,描述信息可參考如下內容:
# Kernel type, e.g. "linux", "liteos_a", "liteos_m".
kernel_type = "liteos_m"                --- 內核類型,跟config.json中kernel_type對應

# Kernel version.
kernel_version = "3.0.0"                --- 內核版本,跟config.json中kernel_version對應
  1. 驗證hb set配置是否正確,輸入hb set能夠顯示如下圖片表示配置正確。

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

hb env可以查看選擇出來的預編譯環境變量。

在執行hb build之前,需要準備好LiteOS-M內核適配,具體適配步驟請參內核移植

內核移植

內核移植需要完成LiteOS-M Kconfig適配、gn的編譯構建和內核啟動最小適配。

LiteOS-M Kconfig適配

//kernel/liteos_m目錄下執行make menuconfig命令,完成編譯配置選項的選擇。在Makefile文件中,會將hb env的結果轉換成環境變量,即PRODUCT_PATHDEVICE_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的每一行輸出轉化為變量形式,例如將[OHOS INFO] device company: fnlink轉換為ohos_device_company=fnlink

……

ifeq ($(BOARD_COMPANY),)
BOARD_COMPANY:=$(ohos_device_company)
endif
……
export BOARD_COMPANY
--- 將ohos_device_company轉化為BOARD_COMPANY環境變量

//kernel/liteos_m/Kconfig文件中使用這些導出的環境變量,Kconfiglib采用ulfalizer開發基于python的版本,源碼地址功能介紹連接參考,里面用到了orsource關鍵字,其中o表示optional,表示這個文件是否存在可選,r表示relative,表示這個文件相對當前文件的相對路徑。

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"                                 --- 將所有擴展板配置信息加載進來,因為單板廠商A提供擴展板可以給單板廠商B使用,所以這里使用*匹配所有的擴展板,而非BOARD_COMPANY。另外由于OpenHarmony支持多內核設計,Kconfig文件采用liteos_m作為后綴,在進行單板適配過程中,其他內核在適配過程中,可以使用對應的內核名作為后綴名進行擴展。

orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards"         --- 加載BOARD_COMPANY的所有單板預定義配置

choice
    prompt "Board Selection"

orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards"                   --- 提供Board選擇列表

endchoice

orsource "../../device/soc/*/Kconfig.liteos_m.defconfig"                                 --- 加載所有SoC的默認配置定義

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文件進行適配:

.
├── v200zr                                       --- v200zr單板配置目錄
│   ├── Kconfig.liteos_m.board                   --- 提供v200zr單板的配置選項
│   ├── Kconfig.liteos_m.defconfig.board         --- 提供v200zr單板的默認配置項
│   └── liteos_m
│       └── config.gni
├── Kconfig.liteos_m.boards                      --- 提供fnlink單板廠商下Boards配置信息
├── Kconfig.liteos_m.defconfig.boards            --- 提供fnlink單板廠商下Boards默認配置信息
├── Kconfig.liteos_m.shields                     --- 提供fnlink單板廠商下擴展板配置信息
└── shields                                      --- fnlink單板廠商的擴展板目錄
    ├── v200zr-t0                                --- fnlink單板廠商的擴展板v200zr-t0
    │   ├── Kconfig.liteos_m.defconfig.shield    --- 擴展板v200zr-t0默認配置
    │   └── Kconfig.liteos_m.shield              --- 擴展板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需要配置選擇該單板后,默認定義 BOARD 的名字為 "v200zr" ,如下:

if BOARD_v200zr
config BOARD
    string       --- string后沒有帶提示,因此用戶不可見
    default "v200zr"

endif # BOARD_v200zr

//kernel/liteos_m/Kconfig文件可以看出需要在//device/soc/bestechnic目錄下新增如下Kconfig文件進行適配:

.
├── bes2600                                  --- bes2600 SoC系列
│   ├── Kconfig.liteos_m.defconfig.bes2600w  --- bestechnic芯片廠商bes2600w SoC Series配置
│   ├── Kconfig.liteos_m.defconfig.series    --- bestechnic芯片廠商bes2600默認配置
│   ├── Kconfig.liteos_m.series              --- bestechnic芯片廠商bes2600 SoC Series配置
│   └── Kconfig.liteos_m.soc                 --- bestechnic芯片廠商bes2600 SoC配置
├── Kconfig.liteos_m.defconfig               --- bestechnic芯片廠商SoC默認配置
├── Kconfig.liteos_m.series                  --- bestechnic芯片廠商SoC Series配置
└── Kconfig.liteos_m.soc                     --- bestechnic芯片廠商 SoC配置

bes2600/Kconfig.liteos_m.series 需要配置bes2600 SoC series,以及它的芯片架構等信息,如下:

config SOC_SERIES_BES2600            --- 提供bes2600 SoC Series選項
    bool "Bestechnic 2600 Series"
    select ARM                       --- 選擇bes2600后,默認選擇ARM架構
    select SOC_COMPANY_BESTECHNIC    --- 選擇bes2600后,默認選擇bestechnic芯片公司,驅動會依賴這個宏配置,選擇配置編譯對應廠商的驅動
    select CPU_CORTEX_M33            --- 選擇bes2600后,默認選擇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后,才會出現如下配置選項

config SOC_BES2600W                  --- 增加bes2600w SoC配置選擇項
    bool "SoC BES2600w"

endchoice

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

if SOC_SERIES_BES2600                            --- 選擇了bes2600 Series后,才會增加如下默認配置選項

rsource "Kconfig.liteos_m.defconfig.bes2600w"    --- 增加bes2600w SoC的默認配置

config SOC_SERIES                                --- 增加SOC_SERIES的默認配置
    string
    default "bes2600"

endif

配置完成后,還需要根據 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)中,由產品最終定義
endif

……

update_config menuconfig:
    $(HIDE)test -f "$(CONFIG)" && cp -v "$(CONFIG)" .config && menuconfig $(args) && savedefconfig --out "$(CONFIG)"

在這個例子中,defconfig配置路徑為 $(PRODUCT_PATH)/kernel_configs/debug.config,創建該文件后,內容為空,產品的目錄文件結構如下:

.
└── display_demo
    ├── config.json
    └── kernel_configs
        └── debug.config

配置完成后,在 kernel/liteos_m 目錄下執行 make menuconfig能夠對SoC Series/SoC/Board進行選擇,如下:

結果將自動保存在$(PRODUCT_PATH)/kernel_configs/debug.config,下次執行make menuconfig時會導出保存的結果。

gn編譯適配

在上一步Kconfig的圖形化配置后,將其生成的配置結果可以作為gn編譯的輸入,以控制不同模塊是否編譯。另外為了解決之前gn編寫時,隨意include的問題,內核編譯做了模塊化編譯的設計,使得整個編譯邏輯更加清晰,設計思路請參考LiteOS-M內核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中,新增內容如下:

if (ohos_kernel_type == "liteos_m") {                    --- 由于多內核設計,對于LiteOS-M內核適配,需要用宏來隔離
  import("http://kernel/liteos_m/liteos.gni")                 --- 引入內核gn編寫模板
  module_name = get_path_info(rebase_path("."), "name")  --- 動態獲取當前文件目錄作為模塊名,防止目錄名修改后,這里還需要跟著修改
  module_group(module_name) {                            --- 采用module_group模板
    modules = [                                          --- 添加需要編譯的模塊
    ]
  }
}

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

內核啟動適配

系統啟動流程分為三個階段:

階段名稱 分區規劃 描述
BOOT1 [0, 0x10000] 第一階段啟動,進行固件啟動
BOOT2 [0x2C010000, 0x2C020000] 第二階段啟動,進行OTA升級啟動
RTOS_MAIN [0x2C080000, 0x2C860000] 第三階段啟動,進行內核啟動

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

內核啟動適配總體思路如下:

  1. 中斷向量的初始化os_vector_init ,初始化中斷的處理函數。
  2. 內核初始化osKernelInitialize 。
  3. 創建線程board_main,進行芯片平臺初始化。
  4. 內核啟動,開始調度線程osKernelStart

其中,本章節詳細對第3步進行展開,其他幾步為對內核函數調用,不作詳細描述。

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

...
    if(!ret) {
        ...
        OhosSystemAdapterHooks();    --- 系統啟動時候設置鉤子,啟動OpenHarmonyOHOS_SystemInit的之前完成打印和驅動的初始化
        ...
        OHOS_SystemInit();           --- 啟動OpenHarmony服務,以及組件初始化
    }
....

OhosSystemAdapterHooks函數在device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c文件中,如下:

int OhosSystemAdapterHooks(void)
{
    init_trace_system();     --- 初始化打印函數
    DeviceManagerStart();    --- 調用DeviceManagerStart函數進行HDF驅動初始化,這個過程會調用單板代碼中的驅動配置文件hdf.hcs以及drivers源碼實現
    return 0;
}

littlefs文件系統移植

V200Z-R開發板開發板采用最大32MB的支持XIPNor Flash,文件系統可以使用example,適配過程中,需要在指定路徑下放置文件系統預置文件,根據配置可自動生成文件系統鏡像,可以實現自動化生成和打包到燒錄包中。

  1. 配置指定目錄放置打包文件系統config.json,通過flash_partition_dir指定目錄:
  "flash_partition_dir": "fs"    --- 表示在vendor/bestechnic/display_demo/fs目錄下放置文件系統預置文件
  1. 在指定目錄vendor/bestechnic/display_demo/fs下放置兩部分內容:
  • wifi_Download_cfg.yaml:鏡像的燒錄配置文件,可以根據實際情況調整分區。
  • /data/data:第一個/data是掛載的根目錄;第二個data是根目錄里面的data目錄,里面可以存放預置文件,或者在第二個data的同級目錄再創建一個目錄,打包的時候只認第一個data掛載根目錄。
  1. config.json中根據wifi_Download_cfg.yaml最后調整結果。
  • fs_src配置文件系統掛載名字。
  • fs_name是最后生成文件系統的名字。
  • block_size配置成4K對齊,建議不修改。
  • fs_size是生成文件系統的大小。
  • burn_name是燒錄bin名字的大小。
  • enable 表示是否生成這個文件系統
  1. //device/soc/bestechnic/bes2600/liteos_m/components/hdf_config/hdf.hcs文件配置文件系統的燒錄的起始地址、文件系統的大小以及讀數據塊的大小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加載數據,進行讀寫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庫適配

在輕量系統中,C庫適配比較復雜,設計思路請參考LiteOS-M內核支持musl與newlib平滑切換方案,由于我們的工具鏈采用 gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 自帶newlib的C庫,那么系統移植整體采用newlib的C庫。那么在內核的make menuconfig中選擇newlib,如下圖:

malloc適配

malloc適配參考The Red Hat newlib C Library-malloc。實現malloc適配有以下兩種方法:

  • 實現 _sbrk_r 函數。這種方法中,內存分配函數使用newlib中的。

  • 實現 _malloc_r, _realloc_r, _reallocf_r, _free_r, _memalign_r, 和 _malloc_usable_size_r。這種方法中,內存分配函數可以使用內核的。

為了方便地根據業務進行內存分配算法調優和問題定位,在這兩種方法中,本案例選擇后者。

首先,由于newlib中已經存在這些函數的符號,因此需要用到gccwrap的鏈接選項替換這些函數符號為內核的實現,內核的實現為 //kernel/liteos_m/kal/libc/newlib/porting/src/malloc.c。

然后,在//device/board/fnlink/v200zr/liteos_m/config.gni的新增這些函數的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等適配

參考 https://sourceware.org/newlib/libc.html#vfprintf ,實現 vprintf, vfprintf, printf, snprintfsprintf。

類似malloc適配,首先要提供這些函數的實現,//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/printf.c,本案例直接采用開源協議友好的實現。與malloc適配不同的是,這個函數由芯片原廠提供。因為就打印來說,根據項目的需要,實現可大可小,內核不方便提供統一的實現。

然后,在//device/board/fnlink/v200zr/liteos_m/config.gni的新增這些函數的wrap鏈接選項。

board_ld_flags += [
     "-Wl,--wrap=printf",
     "-Wl,--wrap=sprintf",
     "-Wl,--wrap=snprintf",
     "-Wl,--wrap=vsnprintf",
     "-Wl,--wrap=vprintf",
]

open等適配

這部分實現由內核統一實現,芯片適配無須關注,內核文件//kernel/liteos_m/kal/libc/newlib/porting/src/fs.c,適配了newlib_read_write等函數,如下:

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

板級系統移植

驅動移植

SoC芯片平臺HDF驅動移植

驅動適配相關文件放置在drivers/adapter/platform中,對應有gpio,i2c,pwm,spi,uartwatchdog,都是通過HDF機制加載,本章節以gpio為例進行詳細說明。

GPIO驅動適配

gpio驅動適配需要完成編譯的適配、源碼的適配。

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

module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO)    --- 如果打開HDF的GPIO配置開關,才進行如下編譯
module_name = get_path_info(rebase_path("."), "name")

hdf_driver(module_name) {
  sources = []
  if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) {              --- 如果打開恒玄的芯片配置開關,才進行恒玄GPIO的驅動編譯
    sources += [ "gpio_bes.c" ]
  }

  include_dirs = [ "." ]
}

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

struct HdfDriverEntry g_GpioDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "BES_GPIO_MODULE_HDF",
    .Bind = GpioDriverBind,
    .Init = GpioDriverInit,
    .Release = GpioDriverRelease,
};
HDF_INIT(g_GpioDriverEntry);     --- 通過HDF_INIT 加載GPIO驅動

然后,在初始化的時候會獲取hcs參數進行初始化,如下:

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節點變量就可以獲取具體gpio配置
    if (gpioCntlr == NULL) {
      ...

編碼規范和設計思想見bes 驅動適配PR的評論。

Board外設器件HDF驅動移植

Board外設器件表示通過SoC平臺總線連接的外設器件,在本案例中,顯示屏屬于外設器件,其驅動適配放在//device/board/fnlink/drivers/liteos_m目錄中。

顯示驅動適配

SoC驅動適配,在//device/board/fnlink/drivers/liteos_m/display/BUILD.gn文件中,根據hdf_driver模板加載驅動模塊,如下:

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文件中,根據驅動框架加載顯示驅動,如下:

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

HDF_INIT(g_ZZW395DriverEntry);

其中的驅動參數根據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子系統適配

OpenHarmony子系統適配一般包含兩部分:

  • config.json中增加對應子系統和部件,這樣編譯系統會將該部件納入編譯目標中。
  • 針對該部件的HAL層接口進行硬件適配,或者可選的軟件功能適配。

分布式軟總線子系統適配

wifi_lite部件適配

首先,在config.json文件中,增加communication子系統的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的編譯目標
      ],
……
    },

//foundation/communication/wifi_lite/BUILD.gn文件中,描述需要適配的接口頭文件路徑,如下:

config("include") {
  include_dirs = [ "interfaces/wifiservice" ]    --- 因為wifi_lite只提供頭文件,不提供wifi的具體實現,所以wifi模塊暴露出適配的目錄路徑提供給硬件廠商來適配,廠商提供wifi協議棧源碼實現。
}

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

因為在本案例中,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熱點的函數,對wifi_device.h中Scan函數的適配實現
{
    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信號熱點函數,對wifi_hotspot.h中GetSignalLevel函數的適配實現。
{
    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目錄下默認配置了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\""      --- 指定在芯片廠商目錄中進行適配
          ]
        }
      ]
    },

//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網卡的初始化適配
  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網卡初始化的適配,如下:

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配置選項可供移植過程進行配置,該配置設定了軟總線移植適配的路徑。

在本案例中,softbus_adapter_config配置為//vendor/bestechnic/mini_distributed_music_player/dsoftbus_lite_config路徑,該路徑下的內容為:

.
├── feature_config                  --- 軟總線功能特性配置,例如是否開啟自發現功能等
│   └── mini
│       └── config.gni
└── spec_config                     --- 軟總線規格特性配置,例如設置軟總線日志級別設置
    ├── softbus_config_adapter.c
    ├── softbus_config_adapter.h
    └── softbus_config_type.h

config.gni文件中規定了以下配置項:

配置項 描述
dsoftbus_feature_disc_ble 是否開啟BLE發現功能
dsoftbus_feature_disc_coap 是否開啟COAP發現功能
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 是否開啟認證傳輸通道功能
dsoftbus_feature_auth_account 是否開啟基于賬號認證功能
dsoftbus_feature_qos 是否開啟QoS功能

softbus_config_adapter.c文件中規定了以下配置項:

配置項 描述
SOFTBUS_INT_MAX_BYTES_LENGTH SendBytes發送最大Bytes長度
SOFTBUS_INT_MAX_MESSAGE_LENGTH SendMessage發送最大消息的長度
SOFTBUS_INT_CONN_BR_MAX_DATA_LENGTH 藍牙最大接收數據量
SOFTBUS_INT_CONN_RFCOM_SEND_MAX_LEN 藍牙最大接收數據量
SOFTBUS_INT_ADAPTER_LOG_LEVEL 日志級別設置
SOFTBUS_STR_STORAGE_DIRECTORY 存儲目錄設置

因為軟總線配置了后,不會默認啟動,所以需要在通過啟動框架調用InitSoftBusServer函數,如下:

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部件需要通過啟動框架調用StartDBinderService函數,由于該函數正常運行依賴主機已經獲取IP地址,因此在LWIP協議棧注冊IP地址變化事件的回調函數中調用該函數,如下:

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

啟動恢復子系統適配

啟動恢復子系統適配bootstrap_lite/syspara_lite兩個部件。請在vendor/bestechnic_bak/display_demo/config.json中新增對應的配置選項。

{
  "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文件,采用的是灌段的形式,最終會保存到上述鏈接段中。主要的服務自動初始化宏如下表格所示:

接口名 描述
SYS_SERVICE_INIT(func) 標識核心系統服務的初始化啟動入口
SYS_FEATURE_INIT(func) 標識核心系統功能的初始化啟動入口
APP_SERVICE_INIT(func) 標識應用層服務的初始化啟動入口
APP_FEATURE_INIT(func) 標識應用層功能的初始化啟動入口

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

如在 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_INITInit函數符號灌段到__zinitcall_sys_service_start__zinitcall_sys_service_end中,由于Init函數是沒有顯式調用它,所以需要將它強制鏈接到最終的鏡像。如下:

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啟動就需要強制鏈接生成的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 配置強制鏈接庫bootstrap,如下:

  "bin_list": [
    {
      "elf_name": "wifiiot",
      "bsp_target_name": "best2600w_liteos",
      "signature": "false",
      "burn_name": "rtos_main",
      "enable": "true",
      "force_link_libs": [
        "bootstrap",     --- 強制鏈接libbootstrap.a
        ...
      ]
    },

適配syspara_lite部件時,系統參數會最終寫到文件中進行持久化保存。在輕量系統中,文件操作相關接口有POSIX接口與HalFiles接口這兩套實現。

因為對接內核的文件系統,采用POSIX相關的接口,所以features字段中需要增加enable_ohos_startup_syspara_lite_use_posix_file_api = true。

如果對接HalFiles相關的接口實現的,則無須修改。

在適配GetSerial接口時,開發板不像產線生產過程那樣,會寫入一個具體的Serial Number,因而需要確定一個數據對開發板進行唯一標識。本案例采用WiFi Mac地址進行適配。

#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'結束符標識


static char Hex2Char(u8 hex)
{
    if (hex < HEX_A) {
        return hex + CHAR_NUM_OFFSET;            --- 將數值0轉為char的'0'
    } else {
        return hex + CHAR_CAPITAL_OFFSET;        --- 將數值0xa轉為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) {       --- 只有第一次調用時,才去獲取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地址值轉化為serial number
    }
    return serialNumber;
}

DFX子系統適配

進行DFX子系統適配需要添加hilog_lite部件,直接在config.json文件配置即可。

{
  "subsystem": "hiviewdfx",
  "components": [
    {
      "component": "hilog_lite",
      "optional": "true"
    }
  ]
},

配置完成之后,在//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c中注冊日志輸出實現函數。

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

系統服務管理子系統適配

進行系統服務管理子系統適配需要添加samgr_lite部件,直接在config.json配置即可。

{
  "subsystem": "systemabilitymgr",
  "components": [
    {
      "component": "samgr_lite",
      "features": [
        "config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096"
      ]
    }
  ]
},

在輕量系統中,samgr_lite配置的共享任務棧大小默認為0x800。當函數調用棧較大時,會出現棧溢出的問題。在本次適配過程中,將其調整為0x1000。

安全子系統適配

進行安全子系統適配需要添加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配置選項用于指定進行mbedtls適配的目錄,用于芯片對mbedtls進行硬件隨機數等適配。

deviceauth_lite部件適配時,deviceauth_storage_path配置選項用于指定存放設備認證信息的路徑,deviceauth_hichain_thread_stack_size用于指定線程棧大小。

媒體子系統適配

進行媒體子系統適配需要添加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棧大小設置

公共基礎庫子系統適配

進行公共基礎庫子系統適配需要添加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部件時,鍵值對會寫到文件中。在輕量系統中,文件操作相關接口有POSIX接口與HalFiles接口這兩套實現。因為對接內核的文件系統,采用POSIX相關的接口,所以features需要增加enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true。如果對接HalFiles相關的接口實現的,則無須修改。

圖形子系統適配

進行圖形子系統適配需要添加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:實例化BaseGfxEngine。
  • touch_input:實例化PointerInputDevice
  • UiMainTask:初始化字體引擎,執行渲染任務等。

圖形子系統層次:

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... (圖形第三方庫)

圖形應用示例見文件//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開發框架子系統適配

進行ACE開發框架子系統適配需要添加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的應用采用js語言進行開發,詳細步驟如下:

  1. DevEco Studio編寫js應用,參考輕量級智能穿戴開發
  2. 使用預覽功能進行預覽,并且得到js包:entry\.preview\intermediates\res\debug\lite\assets\js\default。
  3. 將js包放到對應的文件系統目錄下,文件系統路徑為vendor/bestechnic/display_demo/fs/data/data/js,如下:
├── app.js
├── common
├── i18n
├── manifest.json
└── pages
  1. 最終編譯生成系統鏡像,燒錄到單板后,系統會從app.js加載啟動ace的應用。

元能力子系統適配

進行元能力子系統適配需要添加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相關的應用樣例見vendor/bestechnic/display_demo/tests/ability目錄,包含launcherjs app這兩類應用,應用的函數調用流程描述如下:

  1. launcher應用,通過InstallLauncher安裝BundleName"com.example.launcher"native ui應用,在AbilityMgrSliteFeature啟動后會調用AbilityMgrHandler::StartLauncher()啟動launcher應用。

  2. StartJSApp應用,通過StartAbility啟動任意Want,通過將want data傳遞JS_APP_PATH,
    SetWantData(&want, JS_APP_PATH, strlen(JS_APP_PATH) + 1)。

包管理子系統適配

進行包管理子系統適配需要添加appexecfwk_lite部件,直接在config.json配置即可。

    {
      "subsystem": "appexecfwk",
      "components": [
        {
          "component": "appexecfwk_lite"
        }
      ]
    },

兼容性認證

產品兼容性規范

產品兼容性規范文檔請參考產品兼容性SIG介紹。

XTS用例

XTS測試參考資料見xts參考資料,進行XTS子系統適配需要添加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 是配置掛載文件系統根目錄的名字。
  • enable_ohos_test_xts_acts_use_thirdparty_lwip 表示如果使用thirdparty/lwip目錄下的源碼編譯,則設置為true,否則設置為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兼容性測試網站進行認證,作為sig倉庫轉正到master倉庫的必要條件。詳細步驟如下:

步驟1:將XTS測試報告壓縮成zip文件。

步驟2:生成測試報告的SHA校驗碼。本案例是將zip文件傳到在線生成hash網站生成SHA校驗碼。

步驟3:進入OpenHarmony兼容性測試網站上傳報告。

  • 其中API Level填寫報告中的"sdkApiLevel"字段
  • OS版本號填寫報告中的"OS Version"字段。

todo

后續會補充以下方面的移植:

  • 藍牙
  • bms包安裝
  • 驗證運行JSbytecode
  • 分布式能力:dms、dm
  • 分布式音樂播放器樣例

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

寫在最后

如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙

  • 點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力。
  • 關注小編,同時可以期待后續文章ing??,不定期分享原創知識。
  • 想要獲取更多完整鴻蒙最新學習知識點,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容