• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 轻量带屏解决方案之恒玄芯片移植案例
2
3本文章基于恒玄科技`BES2600W`芯片的欧智通[Multi-modal V200Z-R开发板](https://gitee.com/openharmony/device_board_fnlink),进行轻量带屏开发板的标准移植,开发了智能开关面板样例,同时实现了`ace_engine_lite`、`arkui_ui_lite`、`aafwk_lite`、`appexecfwk_lite`、`HDF`等部件基于`OpenHarmony LiteOS-M`内核的适配。移植架构上采用`Board`与`SoC`分离的方案,工具链`Newlib C`库与`Musl C`库可选,`LiteOS-M`内核编译采用`gn`结合`Kconfig`图形化配置等需求。
4
5## 编译构建
6
7### 目录规划
8
9本案例在芯片移植架构方面进行了一些改进,以前的芯片适配目录规划为:
10
11```
12device
13└── <device_company>
14    └── <device_name>
15```
16
17这样会导致,小熊派`BearPi-HM Nano`开发板与润和的`HiSpark Pegasus`开发板使用小海思的`hi3861`的`SoC`时,需要在这两款开发板里面都放置一份重复的代码。为了解决该问题,本案例将单板厂商与`SoC`厂商进行分离,可以参考[Board和SoC解耦的设计思路](https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md),并把芯片适配目录规划为:
18
19```
20device
21├── board                                --- 单板厂商目录。
22│   └── fnlink                           --- 单板厂商名字:欧智通。
23│       └── v200zr                       --- 单板名:v200zr。
24└── soc					 --- SoC厂商目录。
25    └── bestechnic                       --- SoC厂商名字:恒玄。
26        └── bes2600		         --- SoC Series名:bes2600是一个系列,里面包含bes2600w等SoC名。
27```
28
29产品样例目录规划为:
30
31```
32vendor
33└── bestechnic				 --- 开发产品样例厂商目录,恒玄开发的带屏样例,因此以bestechnic命名。
34    └── display_demo         		 --- 产品名字:以智能开关面板的带屏显示样例。
35```
36
37### 预编译适配
38
39在进行移植之前,需要进行预编译适配。
40
41预编译适配主要使用`hb set`命令,设置整个项目的根目录、单板目录、产品目录、单板公司名等环境变量,为编译做准备。
42
43具体的预编译适配步骤如下:
44
451. 在`vendor/bestechnic/display_demo`目录下新增`config.json`文件,用于描述这个产品样例所使用的单板、内核等信息,描述信息可参考如下内容:
46
47   ```json
48   {
49      "product_name": "display_demo",       --- 用于hb set进行选择时,显示的产品名称。
50      "type": "mini",                       --- 构建系统的类型,mini/small/standard51      "version": "3.0",                     --- 构建系统的版本,1.0/2.0/3.0。
52      "device_company": "fnlink",           --- 单板厂商名,用于编译时找到/device/board/fnlink目录。
53      "board": "v200zr",                    --- 单板名,用于编译时找到/device/board/fnlink/v200zr目录。
54      "kernel_type": "liteos_m",            --- 内核类型,因为OpenHarmony支持多内核,一块单板可能适配了多个内核,所以需要指定某个内核进行编译。
55      "kernel_version": "3.0.0",            --- 内核版本,一块单板可能适配了多个linux内核版本,所以需要指定某个具体的内核版本进行编译。
56      "subsystems": [ ]                     --- 选择所需要编译构建的子系统。
57   }
58   ```
59
602. 在`device/board/fnlink/v200zr/liteos_m`目录下新增`config.gni`文件,用于描述这个产品样例所使用的单板、内核等信息,描述信息可参考如下内容:
61
62   ```
63   # Kernel type, e.g. "linux", "liteos_a", "liteos_m".
64   kernel_type = "liteos_m"                --- 内核类型,跟config.json中kernel_type对应。
65
66   # Kernel version.
67   kernel_version = "3.0.0"                --- 内核版本,跟config.json中kernel_version对应。
68   ```
69
703. 验证`hb set`配置是否正确,输入`hb set`能够显示如下图片表示配置正确。
71
72   执行`hb set`输入项目根目录,并且回车,`hb`命令会遍历所有`//vendor/<product_company>/<product_name>`目录下的`config.json`,给出可选产品编译选项,`config.json`的`product_name`用于显示产品名,`device_company`和`board`用于关联出`//device/board/<device_company>/<board>`目录,并且匹配`<any_dir_name>/config.gni`文件,如果能够匹配多个文件,表示该单板适配了多个内核,那么可以根据`config.json`的`kernel_type`和`kernel_version`来唯一匹配`config.gni`的`kernel_type`和`kernel_version`,即可确定了需要编译适配了哪个内核的单板。
73
74   ![hb set](figures/bes2600_hb_set.png)
75
76   通过`hb env`可以查看选择出来的预编译环境变量。
77
78   ![hb env](figures/bes2600_hb_env.png)
79
80   在执行`hb build`之前,需要准备好`LiteOS-M`内核适配,具体适配步骤请参[内核移植](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/porting/porting-bes2600w-on-minisystem-display-demo.md#%E5%86%85%E6%A0%B8%E7%A7%BB%E6%A4%8D)81
82## 内核移植
83
84内核移植需要完成`LiteOS-M Kconfig`适配、`gn`的编译构建和内核启动最小适配。
85
86### LiteOS-M Kconfig适配
87
88在`//kernel/liteos_m`目录下执行`make menuconfig`命令,完成编译配置选项的选择。在`Makefile`文件中,会将`hb env`的结果转换成环境变量,即`PRODUCT_PATH`、`DEVICE_PATH`和`BOARD_COMPANY`。如下代码块所示:
89
90```
91$(foreach line,$(shell hb env | sed 's/\[OHOS INFO\]/ohos/g;s/ /_/g;s/:_/=/g' || true),$(eval $(line)))
92ifneq ($(ohos_kernel),liteos_m)
93$(error The selected product ($(ohos_product)) is not a liteos_m kernel type product)
94endif
95--- 将hb env的每一行输出转化为变量形式,例如将[OHOS INFO] device company: fnlink转换为ohos_device_company=fnlink
96
97……
98
99ifeq ($(BOARD_COMPANY),)
100BOARD_COMPANY:=$(ohos_device_company)
101endif
102……
103export BOARD_COMPANY
104--- 将ohos_device_company转化为BOARD_COMPANY环境变量
105```
106
107在`//kernel/liteos_m/Kconfig`文件中使用这些导出的环境变量,`Kconfiglib`采用`ulfalizer`开发基于`python`的版本,[源码地址](https://github.com/ulfalizer/Kconfiglib),[功能介绍连接参考](https://github.com/zephyrproject-rtos/zephyr/blob/main/scripts/kconfig/kconfiglib.py),里面用到了`orsource`关键字,其中`o`表示`optional`,表示这个文件是否存在可选,`r`表示`relative`,表示这个文件相对当前文件的相对路径。
108
109```
110config SOC_COMPANY
111    string "SoC company name to locate soc build path"
112    help
113      This option specifies the SoC company name, used to locate the build path for soc. This option is set by the
114      SoC's Kconfig file, and should be exactly the same with SoC company path, and the user should generally avoid
115       modifying it via the menu configuration.
116
117orsource "../../device/board/*/Kconfig.liteos_m.shields"                                 --- 将所有扩展板配置信息加载进来,因为单板厂商A提供扩展板可以给单板厂商B使用,所以这里使用*匹配所有的扩展板,而非BOARD_COMPANY。另外由于OpenHarmony支持多内核设计,Kconfig文件采用liteos_m作为后缀,在进行单板适配过程中,其他内核在适配过程中,可以使用对应的内核名作为后缀名进行扩展。
118
119orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards"         --- 加载BOARD_COMPANY的所有单板预定义配置
120
121choice
122    prompt "Board Selection"
123
124orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards"                   --- 提供Board选择列表
125
126endchoice
127
128orsource "../../device/soc/*/Kconfig.liteos_m.defconfig"                                 --- 加载所有SoC的默认配置定义
129
130choice
131    prompt "SoC Series Selection"
132
133orsource "../../device/soc/*/Kconfig.liteos_m.series"                                    --- 提供所有SoC Series选择列表
134
135endchoice
136
137orsource "../../device/soc/*/Kconfig.liteos_m.soc"                                       --- 加载所有SoC配置
138```
139
140从`//kernel/liteos_m/Kconfig`文件可以看出需要在`//device/board/fnlink`目录下新增如下`Kconfig`文件进行适配:
141
142```
143.
144├── v200zr                                       --- v200zr单板配置目录
145│   ├── Kconfig.liteos_m.board                   --- 提供v200zr单板的配置选项
146│   ├── Kconfig.liteos_m.defconfig.board         --- 提供v200zr单板的默认配置项
147│   └── liteos_m
148│       └── config.gni
149├── Kconfig.liteos_m.boards                      --- 提供fnlink单板厂商下Boards配置信息
150├── Kconfig.liteos_m.defconfig.boards		 --- 提供fnlink单板厂商下Boards默认配置信息
151├── Kconfig.liteos_m.shields			 --- 提供fnlink单板厂商下扩展板配置信息
152└── shields					 --- fnlink单板厂商的扩展板目录
153    ├── v200zr-t0				 --- fnlink单板厂商的扩展板v200zr-t0
154    │   ├── Kconfig.liteos_m.defconfig.shield	 --- 扩展板v200zr-t0默认配置
155    │   └── Kconfig.liteos_m.shield		 --- 扩展板v200zr-t0配置信息
156    ├── v200zr-t1
157    │   ├── Kconfig.liteos_m.defconfig.shield
158    │   └── Kconfig.liteos_m.shield
159    └── Kconfig.liteos_m.shields
160```
161
162在 `v200zr/Kconfig.liteos_m.board`需要配置选择该单板的选项,以及它依赖的`SoC`,如下:
163
164```
165config BOARD_v200zr
166    bool "select board v200zr"
167    depends on SOC_BES2600W		 --- v200zr单板用的bes2600w的SoC,只有 bes2600w的SoC被选择后,v200zr单板配置选项才可见,可以被选择。
168```
169
170在 `v200zr/Kconfig.liteos_m.defconfig.board`需要配置选择该单板后,默认定义 `BOARD` 的名字为 `"v200zr"` ,如下:
171
172```
173if BOARD_v200zr
174config BOARD
175    string		 --- string后没有带提示,因此用户不可见
176    default "v200zr"
177
178endif # BOARD_v200zr
179```
180
181从`//kernel/liteos_m/Kconfig`文件可以看出需要在`//device/soc/bestechnic`目录下新增如下`Kconfig`文件进行适配:
182
183```
184.
185├── bes2600					 --- bes2600 SoC系列
186│   ├── Kconfig.liteos_m.defconfig.bes2600w	 --- bestechnic芯片厂商bes2600w SoC Series配置
187│   ├── Kconfig.liteos_m.defconfig.series	 --- bestechnic芯片厂商bes2600默认配置
188│   ├── Kconfig.liteos_m.series			 --- bestechnic芯片厂商bes2600 SoC Series配置
189│   └── Kconfig.liteos_m.soc			 --- bestechnic芯片厂商bes2600 SoC配置
190├── Kconfig.liteos_m.defconfig			 --- bestechnic芯片厂商SoC默认配置
191├── Kconfig.liteos_m.series			 --- bestechnic芯片厂商SoC Series配置
192└── Kconfig.liteos_m.soc			 --- bestechnic芯片厂商 SoC配置
193```
194
195在 `bes2600/Kconfig.liteos_m.series` 需要配置`bes2600 SoC series`,以及它的芯片架构等信息,如下:
196
197```
198config SOC_SERIES_BES2600	     --- 提供bes2600 SoC Series选项
199    bool "Bestechnic 2600 Series"
200    select ARM			     --- 选择bes2600后,默认选择ARM架构
201    select SOC_COMPANY_BESTECHNIC    --- 选择bes2600后,默认选择bestechnic芯片公司,驱动会依赖这个宏配置,选择配置编译对应厂商的驱动
202    select CPU_CORTEX_M33	     --- 选择bes2600后,默认选择cortex-m33 CPU
203    help
204        Enable support for Bestechnic 2600 series
205```
206
207在 `bes2600/Kconfig.liteos_m.soc` 需要提供`bes2600 SoC series`下有多少个具体的`SoC`可供选择,如下:
208
209```
210choice
211    prompt "Bestechnic 2600 series SoC"
212    depends on SOC_SERIES_BES2600	 --- 只有选择了bes2600 Series后,才会出现如下配置选项
213
214config SOC_BES2600W			 --- 增加bes2600w SoC配置选择项
215    bool "SoC BES2600w"
216
217endchoice
218```
219
220在 `bes2600/Kconfig.liteos_m.defconfig.series` 需要提供`bes2600 SoC series`选择后的默认配置,如下:
221
222```
223if SOC_SERIES_BES2600				 --- 选择了bes2600 Series后,才会增加如下默认配置选项
224
225rsource "Kconfig.liteos_m.defconfig.bes2600w"	 --- 增加bes2600w SoC的默认配置
226
227config SOC_SERIES				 --- 增加SOC_SERIES的默认配置
228    string
229    default "bes2600"
230
231endif
232```
233
234配置完成后,还需要根据 `kernel/liteos_m/Makefile` 文件配置`make menuconfig`的`defconfig`保存路径:
235
236```
237ifeq ($(TEE:1=y),y)
238tee = _tee
239endif
240ifeq ($(RELEASE:1=y),y)
241CONFIG ?= $(PRODUCT_PATH)/kernel_configs/release$(tee).config
242else
243CONFIG ?= $(PRODUCT_PATH)/kernel_configs/debug$(tee).config		 --- 配置文件保存在$(CONFIG)中,由产品最终定义
244endif
245
246……
247
248update_config menuconfig:
249	$(HIDE)test -f "$(CONFIG)" && cp -v "$(CONFIG)" .config && menuconfig $(args) && savedefconfig --out "$(CONFIG)"
250```
251
252在这个例子中,`defconfig`配置路径为 `$(PRODUCT_PATH)/kernel_configs/debug.config`,创建该文件后,内容为空,产品的目录文件结构如下:
253
254```
255.
256└── display_demo
257    ├── config.json
258    └── kernel_configs
259        └── debug.config
260```
261
262配置完成后,在 `kernel/liteos_m` 目录下执行 `make menuconfig`能够对`SoC Series`/`SoC`/`Board`进行选择,如下:
263
264![board make menuconfig](figures/bes2600_board_make_menuconfig.png)
265
266结果将自动保存在`$(PRODUCT_PATH)/kernel_configs/debug.config`,下次执行`make menuconfig`时会导出保存的结果。
267
268### gn编译适配
269
270在上一步`Kconfig`的图形化配置后,将其生成的配置结果可以作为`gn`编译的输入,以控制不同模块是否编译。另外为了解决之前`gn`编写时,随意include的问题,内核编译做了模块化编译的设计,使得整个编译逻辑更加清晰,设计思路请参考[LiteOS-M内核BUILD.gn编写指南](https://gitee.com/caoruihong/kernel_liteos_m/wikis/LiteOS-M%E5%86%85%E6%A0%B8BUILD.gn%E7%BC%96%E5%86%99%E6%8C%87%E5%8D%97)271
272在 `kernel/liteos_m/BUILD.gn` 中,指定了`Board`和`SoC`的编译入口为`//device/board/fnlink`和`//device/soc/bestechnic`。
273
274```
275deps += [ "//device/board/$device_company" ]
276deps += [ "//device/soc/$LOSCFG_SOC_COMPANY" ]
277```
278
279在`//device/board/fnlink/BUILD.gn`中,新增内容如下:
280
281```
282if (ohos_kernel_type == "liteos_m") {                    --- 由于多内核设计,对于LiteOS-M内核适配,需要用宏来隔离
283  import("//kernel/liteos_m/liteos.gni")		 --- 引入内核gn编写模板
284  module_name = get_path_info(rebase_path("."), "name")	 --- 动态获取当前文件目录作为模块名,防止目录名修改后,这里还需要跟着修改
285  module_group(module_name) {				 --- 采用module_group模板
286    modules = [						 --- 添加需要编译的模块
287    ]
288  }
289}
290```
291
292同理`//device/soc/bestechnic/BUILD.gn`也是一样。
293
294### 内核启动适配
295
296系统启动流程分为三个阶段:
297
298| 阶段名称  | 分区规划                 | 描述                          |
299| --------- | ------------------------ | ----------------------------- |
300| BOOT1     | [0, 0x10000]             | 第一阶段启动,进行固件启动。    |
301| BOOT2     | [0x2C010000, 0x2C020000] | 第二阶段启动,进行OTA升级启动。 |
302| RTOS_MAIN | [0x2C080000, 0x2C860000] | 第三阶段启动,进行内核启动。    |
303
304在第三阶段内核启动中,需要适配的文件路径在 `//device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/rtos/liteos/liteos_m/board.c`。
305
306内核启动适配总体思路如下:
307
3081. 中断向量的初始化`os_vector_init`,初始化中断的处理函数。
3092. 内核初始化`osKernelInitialize`。
3103. 创建线程`board_main`,进行芯片平台初始化。
3114. 内核启动,开始调度线程`osKernelStart`。
312
313其中,本章节详细对第3步进行展开,其他几步为对内核函数调用,不作详细描述。
314
315第3步中`board_main`在启动`OHOS_SystemInit`之前,需要初始化必要的动作,如下:
316
317```
318...
319    if(!ret) {
320        ...
321        OhosSystemAdapterHooks();    --- 系统启动时候设置钩子,启动OpenHarmonyOHOS_SystemInit的之前完成打印和驱动的初始化
322        ...
323        OHOS_SystemInit(); 	     --- 启动OpenHarmony服务,以及组件初始化
324    }
325....
326```
327
328`OhosSystemAdapterHooks`函数在`device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c`文件中,如下:
329
330```
331int OhosSystemAdapterHooks(void)
332{
333    init_trace_system(); 	 --- 初始化打印函数
334    DeviceManagerStart(); 	 --- 调用DeviceManagerStart函数进行HDF驱动初始化,这个过程会调用单板代码中的驱动配置文件hdf.hcs以及drivers源码实现
335    return 0;
336}
337
338```
339
340### littlefs文件系统移植
341
342 `V200Z-R`开发板开发板采用最大`32MB`的支持`XIP`的`Nor Flash`,文件系统可以使用`example`,适配过程中,需要在指定路径下放置文件系统预置文件,根据配置可自动生成文件系统镜像,可以实现自动化生成和打包到烧录包中。
343
3441. 配置指定目录放置打包文件系统`config.json`,通过`flash_partition_dir`指定目录:
345
346   ```
347   "flash_partition_dir": "fs" 	 --- 表示在vendor/bestechnic/display_demo/fs目录下放置文件系统预置文件
348   ```
349
3502. 在指定目录`vendor/bestechnic/display_demo/fs`下放置两部分内容:
351
352   - `wifi_Download_cfg.yaml`:镜像的烧录配置文件,可以根据实际情况调整分区。
353   - `/data/data`:第一个/`data`是挂载的根目录;第二个`data`是根目录里面的`data`目录,里面可以存放预置文件,或者在第二个`data`的同级目录再创建一个目录,打包的时候只认第一个`data`挂载根目录。
354
3553. `config.json`中根据`wifi_Download_cfg.yaml`最后调整结果。
356
357   - `fs_src`配置文件系统挂载名字。
358   - `fs_name`是最后生成文件系统的名字。
359   - `block_size`配置成`4K`对齐,建议不修改。
360   - `fs_size`是生成文件系统的大小。
361   - `burn_name`是烧录`bin`名字的大小。
362   - `enable` 表示是否生成这个文件系统。
363
3644. 在`//device/soc/bestechnic/bes2600/liteos_m/components/hdf_config/hdf.hcs`文件配置文件系统的烧录的起始地址、文件系统的大小以及读数据块的大小`block_size`等信息,参考配置如下:
365
366   ```
367    misc {
368        fs_config {
369            example_config {
370                match_attr = "littlefs_config";
371                mount_points = ["/data"];
372                partitions = [10];
373                block_size = [4096];
374                block_count = [1024];
375            }
376        }
377        storage_config {
378            flash_config {
379                match_attr = "flash_config";
380                partitions = [10];
381                owner = [0];
382                description = ["littlefs"];
383                start_addr = [0xB60000];
384                length = [0x400000];
385                options = [3];
386            }
387        }
388    }
389   ```
390
391   最后在`device/soc/bestechnic/bes2600/liteos_m/components/fs/fs_init.c`中,通过`hdf`加载数据,进行读写`flash`,如下:
392
393   ```c
394   static int32_t FsDriverInit(struct HdfDeviceObject *object)
395   {
396       if (object == NULL) {
397           return HDF_FAILURE;
398       }
399       if (object->property) {
400           if (FsGetResource(fs, object->property) != HDF_SUCCESS) {
401               HDF_LOGE("%s: FsGetResource failed", __func__);
402               return HDF_FAILURE;
403           }
404       }
405       for (int i = 0; i < sizeof(fs) / sizeof(fs[0]); i++) {
406           if (fs[i].mount_point == NULL)
407               continue;
408
409           fs[i].lfs_cfg.read = littlefs_block_read;
410           fs[i].lfs_cfg.prog = littlefs_block_write;
411           fs[i].lfs_cfg.erase = littlefs_block_erase;
412           fs[i].lfs_cfg.sync = littlefs_block_sync;
413
414           fs[i].lfs_cfg.read_size = 256;
415           fs[i].lfs_cfg.prog_size = 256;
416           fs[i].lfs_cfg.cache_size = 256;
417           fs[i].lfs_cfg.lookahead_size = 16;
418           fs[i].lfs_cfg.block_cycles = 1000;
419
420           int ret = mount(NULL, fs[i].mount_point, "littlefs", 0, &fs[i].lfs_cfg);
421           HDF_LOGI("%s: mount fs on '%s' %s\n", __func__, fs[i].mount_point, (ret == 0) ? "succeed" : "failed");
422       }
423       return HDF_SUCCESS;
424   }
425   ```
426
427
428
429### C库适配
430
431在轻量系统中,C库适配比较复杂,设计思路请参考[LiteOS-M内核支持musl与newlib平滑切换方案](https://gitee.com/arvinzzz/ohos_kernel_design_specification/blob/master/liteos_m/%E6%94%AF%E6%8C%81newlib/%E5%86%85%E6%A0%B8%E9%80%82%E9%85%8Dnewlib%E6%96%B9%E6%A1%88%E6%80%9D%E8%B7%AF.md),由于我们的工具链采用 [gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2](https://gitee.com/link?target=https%3A%2F%2Fdeveloper.arm.com%2F-%2Fmedia%2FFiles%2Fdownloads%2Fgnu-rm%2F10.3-2021.10%2Fgcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2) 自带`newlib`的C库,那么系统移植整体采用`newlib`的C库。那么在内核的`make menuconfig`中选择`newlib`,如下图:
432
433![image-20211212191013553](figures/bes2600_newlib_make_menuconfig.png)
434
435#### malloc适配
436
437malloc适配参考[The Red Hat newlib C Library-malloc](https://sourceware.org/newlib/libc.html#malloc)。实现malloc适配有以下两种方法:
438
439- 实现 `_sbrk_r` 函数。这种方法中,内存分配函数使用`newlib`中的。
440
441- 实现 `_malloc_r`, `_realloc_r`, `_reallocf_r`, `_free_r`, `_memalign_r`, 和 `_malloc_usable_size_r`。这种方法中,内存分配函数可以使用内核的。
442
443为了方便地根据业务进行内存分配算法调优和问题定位,在这两种方法中,本案例选择后者。
444
445首先,由于`newlib`中已经存在这些函数的符号,因此需要用到`gcc`的`wrap`的链接选项替换这些函数符号为内核的实现,内核的实现为 `//kernel/liteos_m/kal/libc/newlib/porting/src/malloc.c`。
446
447然后,在`//device/board/fnlink/v200zr/liteos_m/config.gni`的新增这些函数的`wrap`链接选项。
448
449```
450board_ld_flags += [
451     "-Wl,--wrap=_malloc_r",
452     "-Wl,--wrap=_realloc_r",
453     "-Wl,--wrap=_reallocf_r",
454     "-Wl,--wrap=_free_r",
455     "-Wl,--wrap=_memalign_r",
456     "-Wl,--wrap=_malloc_usable_size_r",
457]
458```
459
460#### vsprintf等适配
461
462参考 https://sourceware.org/newlib/libc.html#vfprintf ,实现 `vprintf`, `vfprintf`, `printf`, `snprintf` 和`sprintf`。
463
464类似`malloc`适配,首先要提供这些函数的实现,`//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/printf.c`,本案例直接采用开源协议友好的实现。与`malloc`适配不同的是,这个函数由芯片原厂提供。因为就打印来说,根据项目的需要,实现可大可小,内核不方便提供统一的实现。
465
466然后,在`//device/board/fnlink/v200zr/liteos_m/config.gni`的新增这些函数的wrap链接选项。
467
468```
469board_ld_flags += [
470     "-Wl,--wrap=printf",
471     "-Wl,--wrap=sprintf",
472     "-Wl,--wrap=snprintf",
473     "-Wl,--wrap=vsnprintf",
474     "-Wl,--wrap=vprintf",
475]
476```
477
478#### open等适配
479
480这部分实现由内核统一实现,芯片适配无须关注,内核文件`//kernel/liteos_m/kal/libc/newlib/porting/src/fs.c`,适配了`newlib`的`_read`、`_write`等函数,如下:
481
482```
483……
484ssize_t _read(int fd, void *buf, size_t nbyte)
485{
486    return LOS_Read(fd, buf, nbyte);
487}
488
489ssize_t _write(int fd, const void *buf, size_t nbyte)
490{
491    return LOS_Write(fd, buf, nbyte);
492}
493
494off_t _lseek(int fd, off_t offset, int whence)
495{
496    return LOS_Lseek(fd, offset, whence);
497}
498……
499```
500
501## 板级系统移植
502
503### 驱动移植
504
505#### SoC芯片平台HDF驱动移植
506
507驱动适配相关文件放置在`drivers/adapter/platform`中,对应有`gpio`,`i2c`,`pwm`,`spi`,`uart`,`watchdog`,都是通过`HDF`机制加载,本章节以`gpio`为例进行详细说明。
508
509##### GPIO驱动适配
510
511`gpio`驱动适配需要完成编译的适配、源码的适配。
512
513在`//drivers/adapter/platform/gpio/BUILD.gn`文件中,描述了恒玄`gpio`驱动的编译适配。如下:
514
515```
516module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO)	 --- 如果打开HDF的GPIO配置开关,才进行如下编译
517module_name = get_path_info(rebase_path("."), "name")
518
519hdf_driver(module_name) {
520  sources = []
521  if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) {		         --- 如果打开恒玄的芯片配置开关,才进行恒玄GPIO的驱动编译
522    sources += [ "gpio_bes.c" ]
523  }
524
525  include_dirs = [ "." ]
526}
527```
528
529在`//drivers/adapter/platform/gpio/gpio_bes.c`文件中,描述了恒玄`gpio`驱动的源码适配。
530首先,按照`OpenHarmony`的`HDF`驱动框架加载驱动基本适配框架,如下:
531
532```
533struct HdfDriverEntry g_GpioDriverEntry = {
534    .moduleVersion = 1,
535    .moduleName = "BES_GPIO_MODULE_HDF",
536    .Bind = GpioDriverBind,
537    .Init = GpioDriverInit,
538    .Release = GpioDriverRelease,
539};
540HDF_INIT(g_GpioDriverEntry); 	 --- 通过HDF_INIT 加载GPIO驱动
541```
542
543然后,在初始化的时候会获取`hcs`参数进行初始化,如下:
544
545```
546static int32_t GpioDriverInit(struct HdfDeviceObject *device)
547{
548    int32_t ret;
549    struct GpioCntlr *gpioCntlr = NULL;
550
551    if (device == NULL) {
552        HDF_LOGE("%s: device is NULL", __func__);
553        return HDF_ERR_INVALID_PARAM;
554    }
555
556    gpioCntlr = GpioCntlrFromDevice(device); 	 --- gpioCntlr节点变量就可以获取具体gpio配置
557    if (gpioCntlr == NULL) {
558      ...
559```
560
561编码规范和设计思想见[bes 驱动适配PR](https://gitee.com/openharmony/drivers_adapter/pulls/278)的评论。
562
563#### Board外设器件HDF驱动移植
564
565`Board`外设器件表示通过`SoC`平台总线连接的外设器件,在本案例中,显示屏属于外设器件,其驱动适配放在`//device/board/fnlink/drivers/liteos_m`目录中。
566
567##### 显示驱动适配
568
569同`SoC`驱动适配,在`//device/board/fnlink/drivers/liteos_m/display/BUILD.gn`文件中,根据`hdf_driver`模板加载驱动模块,如下:
570
571```
572module_name = get_path_info(rebase_path("."), "name")
573hdf_driver(module_name) {
574  sources = [
575    "zzw395.c",
576  ]
577  include_dirs = [
578    "//drivers/peripheral/display/interfaces/include",
579  ...
580  ]
581}
582```
583
584在`//device/board/fnlink/drivers/liteos_m/display/zzw395.c`文件中,根据驱动框架加载显示驱动,如下:
585
586```
587static struct HdfDriverEntry g_ZZW395DriverEntry = {
588    .moduleVersion = 1,
589    .moduleName = "HDF_PANEL_ZZW395",
590    .Bind = PanelDriverBind,
591    .Init = PanelDriverInit,
592    .Release = PanelDriverRelease,
593};
594
595HDF_INIT(g_ZZW395DriverEntry);
596```
597
598其中的驱动参数根据`hcs`配置,在`PanelDriverInit`初始化时加载,如下:
599
600```
601static int32_t PanelDriverInit(struct HdfDeviceObject *object)
602{
603    if (object == NULL) {
604        return HDF_FAILURE;
605    }
606    HDF_LOGD("%s entry !!!", __func__);
607    if (object->property) {
608        if (PanelGetResource(&priv, object->property) != HDF_SUCCESS) {
609            HDF_LOGE("%s: PanelGetResource failed", __func__);
610            return HDF_FAILURE;
611        }
612    }
613...
614```
615
616### OpenHarmony子系统适配
617
618`OpenHarmony`子系统适配一般包含两部分:
619
620- 在`config.json`中增加对应子系统和部件,这样编译系统会将该部件纳入编译目标中。
621- 针对该部件的`HAL`层接口进行硬件适配,或者可选的软件功能适配。
622
623#### 分布式软总线子系统适配
624
625##### wifi_lite部件适配
626
627首先,在`config.json`文件中,增加`communication`子系统的`wifi_lite`部件,如下:
628
629```
630    {
631      "subsystem": "communication",
632      "components": [
633        {
634          "component": "wifi_lite",
635          "optional": "true"
636        }
637      ]
638    },
639```
640
641`wifi_lite`部件在`//build/lite/components/communication.json`文件中,描述如下:
642
643```
644    {
645      "component": "wifi_lite",
646……
647      "targets": [
648        "//foundation/communication/wifi_lite:wifi"		 --- wifi_lite的编译目标
649      ],
650……
651    },
652
653```
654
655在`//foundation/communication/wifi_lite/BUILD.gn`文件中,描述需要适配的接口头文件路径,如下:
656
657```
658config("include") {
659  include_dirs = [ "interfaces/wifiservice" ]	 --- 因为wifi_lite只提供头文件,不提供wifi的具体实现,所以wifi模块暴露出适配的目录路径提供给硬件厂商来适配,厂商提供wifi协议栈源码实现。
660}
661
662group("wifi") {
663  public_configs = [ ":include" ]
664}
665```
666
667因为在本案例中,`wifi`属于`SoC`提供的功能,所以适配源码放在`SoC`的`//device/soc/bestechnic/hals/communication/wifi_lite/wifiservice`目录下,包含`wifi_device.c`和`wifi_hotspot.c`分别适配`wifi_device.h`和`wifi_hotspot.h`。如下:
668
669```
670……
671WifiErrorCode Scan(void)	 --- wifi_device.c中扫描wifi热点的函数,对wifi_device.h中Scan函数的适配实现
672{
673    WifiErrorCode ret = ERROR_WIFI_BUSY;
674
675
676    if (IsWifiActive() != WIFI_STA_ACTIVE)
677        return ERROR_WIFI_IFACE_INVALID;
678
679    if (g_HalHmosWifiInfo.scan_state == SCAN_REQUEST ||
680        g_HalHmosWifiInfo.scan_state == SCAN_TRIGGER)
681        return ERROR_WIFI_BUSY;
682
683    HalHmosWifiLock();
684    ret = ((HalHmosSendEvent(HMOS_ON_WIFI_SCAN_STATE_CHANGED, NULL) == 0) ? WIFI_SUCCESS : ERROR_WIFI_BUSY);
685    HalHmosWifiUnLock();
686
687    return ret;
688}
689……
690int GetSignalLevel(int rssi, int band)	 --- wifi_hotspot.c中获取wifi信号热点函数,对wifi_hotspot.h中GetSignalLevel函数的适配实现。
691{
692    if (band == HOTSPOT_BAND_TYPE_2G) {
693        if (rssi >= RSSI_LEVEL_4_2_G)
694            return RSSI_LEVEL_4;
695        if (rssi >= RSSI_LEVEL_3_2_G)
696            return RSSI_LEVEL_3;
697        if (rssi >= RSSI_LEVEL_2_2_G)
698            return RSSI_LEVEL_2;
699        if (rssi >= RSSI_LEVEL_1_2_G)
700            return RSSI_LEVEL_1;
701    }
702
703    if (band == HOTSPOT_BAND_TYPE_5G) {
704        if (rssi >= RSSI_LEVEL_4_5_G)
705            return RSSI_LEVEL_4;
706        if (rssi >= RSSI_LEVEL_3_5_G)
707            return RSSI_LEVEL_3;
708        if (rssi >= RSSI_LEVEL_2_5_G)
709            return RSSI_LEVEL_2;
710        if (rssi >= RSSI_LEVEL_1_5_G)
711            return RSSI_LEVEL_1;
712    }
713    return ERROR_WIFI_INVALID_ARGS;
714}
715```
716
717##### LWIP部件适配
718
719`LiteOS-M kernel`目录下默认配置了`lwip`,因而具有编译功能,可以在`kernel`组件中指定`lwip`编译的目录。如下:
720
721```
722    {
723      "subsystem": "kernel",
724      "components": [
725        {
726          "component": "liteos_m",
727          "features": [
728            "ohos_kernel_liteos_m_lwip_path = \"//device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1\""		 --- 指定在芯片厂商目录中进行适配
729          ]
730        }
731      ]
732    },
733```
734
735在`//device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/BUILD.gn`文件中,描述了`lwip`的编译,如下:
736
737```
738import("//kernel/liteos_m/liteos.gni")
739import("$LITEOSTHIRDPARTY/lwip/lwip.gni")
740import("$LITEOSTOPDIR/components/net/lwip-2.1/lwip_porting.gni")
741
742module_switch = defined(LOSCFG_NET_LWIP_SACK)
743module_name = "lwip"
744kernel_module(module_name) {
745  sources = LWIP_PORTING_FILES + LWIPNOAPPSFILES -
746            [ "$LWIPDIR/api/sockets.c" ] + [ "porting/src/ethernetif.c" ]		 --- 增加ethernetif.c文件,用以适配ethernet网卡的初始化适配
747  defines = [ "LITEOS_LWIP=1" ]
748  defines += [ "CHECKSUM_BY_HARDWARE=1" ]
749}
750
751config("public") {
752  defines = [ "_BSD_SOURCE=1" ]
753  include_dirs =
754      [ "porting/include" ] + LWIP_PORTING_INCLUDE_DIRS + LWIP_INCLUDE_DIRS
755}
756
757```
758
759在`//device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/porting/include/lwip/lwipopts.h`文件中,说明原有`lwip`配置选项保持不变,软总线会依赖这些配置选项,并且新增硬件适配的配置项,如下:
760
761```
762#ifndef _PORTING_LWIPOPTS_H_
763#define _PORTING_LWIPOPTS_H_
764
765#include_next "lwip/lwipopts.h"				 --- 保持原来的配置项不变
766
767#define LWIP_NETIF_STATUS_CALLBACK      1
768#define LWIP_CHECKSUM_ON_COPY           0
769#define CHECKSUM_GEN_UDP                0	 --- 新增硬件适配选项
770
771#endif /* _PORTING_LWIPOPTS_H_ */
772
773```
774
775在`//device/soc/bestechnic/bes2600/liteos_m/components/net/lwip-2.1/porting/src/ethernetif.c`文件中,说明对`ethernet`网卡初始化的适配,如下:
776
777```
778err_t
779ethernetif_init(struct netif *netif)
780{
781……
782#ifdef CHECKSUM_BY_HARDWARE
783    eth_hw_checksum_init();
784#endif
785……
786    netif->linkoutput = low_level_output;
787
788    netif->drv_send = liteos_low_level_output;
789    netif->hwaddr_len = NETIF_MAX_HWADDR_LEN;
790    low_level_init(netif);
791    driverif_init(netif);
792    return ERR_OK;
793……
794}
795```
796
797##### dsoftbus部件适配
798
799在`config.json`中增加`dsoftbus`部件配置如下:
800
801```
802{
803  "component": "dsoftbus",
804  "features": [
805    "softbus_adapter_config = \"//vendor/bestechnic/mini_distributed_music_player/dsoftbus_lite_config\""
806  ]
807},
808```
809
810`dsoftbus`部件在`//foundation/communication/dsoftbus/dsoftbus.gni`文件中提供了`softbus_adapter_config`配置选项可供移植过程进行配置,该配置设定了软总线移植适配的路径。
811
812在本案例中,`softbus_adapter_config`配置为`//vendor/bestechnic/mini_distributed_music_player/dsoftbus_lite_config`路径,该路径下的内容为:
813
814```
815.
816├── feature_config					--- 软总线功能特性配置,例如是否开启自发现功能等
817│   └── mini
818│       └── config.gni
819└── spec_config						--- 软总线规格特性配置,例如设置软总线日志级别设置
820    ├── softbus_config_adapter.c
821    ├── softbus_config_adapter.h
822    └── softbus_config_type.h
823```
824
825在`config.gni`文件中规定了以下配置项:
826
827| 配置项                                     | 描述                     |
828| ------------------------------------------ | ------------------------ |
829| dsoftbus_feature_disc_ble         | 是否开启BLE发现功能。      |
830| dsoftbus_feature_disc_coap        | 是否开启COAP发现功能。     |
831| dsoftbus_feature_conn_tcp         | 是否开启TCP连接功能。      |
832| dsoftbus_feature_conn_br          | 是否开启BR连接功能。       |
833| dsoftbus_feature_conn_ble         | 是否开启BLE连接功能。      |
834| dsoftbus_feature_conn_p2p         | 是否开启P2P连接功能。     |
835| dsoftbus_feature_trans_udp        | 是否开启UDP传输功能。      |
836| dsoftbus_feature_trans_udp_stream | 是否开启UDP传输流功能。    |
837| dsoftbus_feature_trans_udp_file   | 是否开启UDP传输文件功能。  |
838| dsoftbus_feature_ip_auth          | 是否开启认证传输通道功能。 |
839| dsoftbus_feature_auth_account     | 是否开启基于账号认证功能。 |
840| dsoftbus_feature_qos              | 是否开启QoS功能。          |
841
842在`softbus_config_adapter.c`文件中规定了以下配置项:
843
844| 配置项                               | 描述                          |
845| ------------------------------------ | ----------------------------- |
846| SOFTBUS_INT_MAX_BYTES_LENGTH         | SendBytes发送最大Bytes长度。    |
847| SOFTBUS_INT_MAX_MESSAGE_LENGTH       | SendMessage发送最大消息的长度。 |
848| SOFTBUS_INT_CONN_BR_MAX_DATA_LENGTH  | 蓝牙最大接收数据量。            |
849| SOFTBUS_INT_CONN_RFCOM_SEND_MAX_LEN  | 蓝牙最大接收数据量。            |
850| SOFTBUS_INT_ADAPTER_LOG_LEVEL        | 日志级别设置。                  |
851| SOFTBUS_STR_STORAGE_DIRECTORY        | 存储目录设置。                  |
852
853因为软总线配置了后,不会默认启动,所以需要在通过启动框架调用`InitSoftBusServer`函数,如下:
854
855```
856static void DSoftBus(void)
857{
858    osThreadAttr_t attr;
859    attr.name = "dsoftbus task";
860    attr.attr_bits = 0U;
861    attr.cb_mem = NULL;
862    attr.cb_size = 0U;
863    attr.stack_mem = NULL;
864    attr.stack_size = 65536;
865    attr.priority = 24;
866
867    extern void InitSoftBusServer(void);
868    if (osThreadNew((osThreadFunc_t) InitSoftBusServer, NULL, &attr) == NULL) {
869        printf("Failed to create WifiSTATask!\n");
870    }
871}
872
873APP_FEATURE_INIT(DSoftBus);
874```
875
876##### RPC部件适配
877
878在`config.json`中增加`rpc`部件配置如下:
879
880```
881{
882  "component": "rpc"
883},
884```
885
886同样地,`rpc`部件需要通过启动框架调用`StartDBinderService`函数,由于该函数正常运行依赖主机已经获取`IP`地址,因此在`LWIP`协议栈注册`IP`地址变化事件的回调函数中调用该函数,如下:
887
888```
889static void RpcServerWifiDHCPSucCB(struct netif *netif, netif_nsc_reason_t reason,
890                                   const netif_ext_callback_args_t *args)
891{
892    (void) args;
893    if (netif == NULL) {
894        printf("%s %d, error: input netif is NULL!\n", __FUNCTION__, __LINE__);
895        return;
896    }
897    if (reason == LWIP_NSC_IPSTATUS_CHANGE) {
898        if (netif_is_up(netif) && !ip_addr_isany(&netif->ip_addr)) {
899            printf("%s %d, start rpc server!\n", __FUNCTION__, __LINE__);
900            StartDBinderService();
901        }
902    }
903}
904
905static void WifiDHCPRpcServerCB(void)
906{
907    NETIF_DECLARE_EXT_CALLBACK(WifiReadyRpcServerCallback);
908    netif_add_ext_callback(&WifiReadyRpcServerCallback, RpcServerWifiDHCPSucCB);
909}
910
911APP_FEATURE_INIT(WifiDHCPRpcServerCB);
912```
913
914#### 启动恢复子系统适配
915
916启动恢复子系统适配`bootstrap_lite`/`syspara_lite`两个部件。请在`vendor/bestechnic_bak/display_demo/config.json`中新增对应的配置选项。
917
918```
919{
920  "subsystem": "startup",
921  "components": [
922	{
923	  "component": "bootstrap_lite"		 --- bootstrap_lite 部件
924	},
925	{
926	  "component": "syspara_lite",		 --- syspara_lite 部件
927	  "features": [
928		"enable_ohos_startup_syspara_lite_use_posix_file_api = true"
929	  ]
930	}
931  ]
932},
933```
934
935适配`bootstrap_lite`部件时,需要在连接脚本文件`//device/soc/bestechnic/bes2600/liteos_m/sdk/bsp/out/best2600w_liteos/_best2001.lds`中手动新增如下段:
936
937```
938       __zinitcall_bsp_start = .;
939      KEEP (*(.zinitcall.bsp0.init))
940      KEEP (*(.zinitcall.bsp1.init))
941      KEEP (*(.zinitcall.bsp2.init))
942      KEEP (*(.zinitcall.bsp3.init))
943      KEEP (*(.zinitcall.bsp4.init))
944      __zinitcall_bsp_end = .;
945      __zinitcall_device_start = .;
946      KEEP (*(.zinitcall.device0.init))
947      KEEP (*(.zinitcall.device1.init))
948      KEEP (*(.zinitcall.device2.init))
949      KEEP (*(.zinitcall.device3.init))
950      KEEP (*(.zinitcall.device4.init))
951      __zinitcall_device_end = .;
952      __zinitcall_core_start = .;
953      KEEP (*(.zinitcall.core0.init))
954      KEEP (*(.zinitcall.core1.init))
955      KEEP (*(.zinitcall.core2.init))
956      KEEP (*(.zinitcall.core3.init))
957      KEEP (*(.zinitcall.core4.init))
958      __zinitcall_core_end = .;
959      __zinitcall_sys_service_start = .;
960      KEEP (*(.zinitcall.sys.service0.init))
961      KEEP (*(.zinitcall.sys.service1.init))
962      KEEP (*(.zinitcall.sys.service2.init))
963      KEEP (*(.zinitcall.sys.service3.init))
964      KEEP (*(.zinitcall.sys.service4.init))
965      __zinitcall_sys_service_end = .;
966      __zinitcall_sys_feature_start = .;
967      KEEP (*(.zinitcall.sys.feature0.init))
968      KEEP (*(.zinitcall.sys.feature1.init))
969      KEEP (*(.zinitcall.sys.feature2.init))
970      KEEP (*(.zinitcall.sys.feature3.init))
971      KEEP (*(.zinitcall.sys.feature4.init))
972      __zinitcall_sys_feature_end = .;
973      __zinitcall_run_start = .;
974      KEEP (*(.zinitcall.run0.init))
975      KEEP (*(.zinitcall.run1.init))
976      KEEP (*(.zinitcall.run2.init))
977      KEEP (*(.zinitcall.run3.init))
978      KEEP (*(.zinitcall.run4.init))
979      __zinitcall_run_end = .;
980      __zinitcall_app_service_start = .;
981      KEEP (*(.zinitcall.app.service0.init))
982      KEEP (*(.zinitcall.app.service1.init))
983      KEEP (*(.zinitcall.app.service2.init))
984      KEEP (*(.zinitcall.app.service3.init))
985      KEEP (*(.zinitcall.app.service4.init))
986      __zinitcall_app_service_end = .;
987      __zinitcall_app_feature_start = .;
988      KEEP (*(.zinitcall.app.feature0.init))
989      KEEP (*(.zinitcall.app.feature1.init))
990      KEEP (*(.zinitcall.app.feature2.init))
991      KEEP (*(.zinitcall.app.feature3.init))
992      KEEP (*(.zinitcall.app.feature4.init))
993      __zinitcall_app_feature_end = .;
994      __zinitcall_test_start = .;
995      KEEP (*(.zinitcall.test0.init))
996      KEEP (*(.zinitcall.test1.init))
997      KEEP (*(.zinitcall.test2.init))
998      KEEP (*(.zinitcall.test3.init))
999      KEEP (*(.zinitcall.test4.init))
1000      __zinitcall_test_end = .;
1001      __zinitcall_exit_start = .;
1002      KEEP (*(.zinitcall.exit0.init))
1003      KEEP (*(.zinitcall.exit1.init))
1004      KEEP (*(.zinitcall.exit2.init))
1005      KEEP (*(.zinitcall.exit3.init))
1006      KEEP (*(.zinitcall.exit4.init))
1007      __zinitcall_exit_end = .;
1008```
1009
1010需要新增上述段是因为`bootstrap_init`提供的对外接口,见`//utils/native/lite/include/ohos_init.h`文件,采用的是灌段的形式,最终会保存到上述链接段中。主要的服务自动初始化宏如下表格所示:
1011
1012| 接口名                 | 描述                             |
1013| ---------------------- | -------------------------------- |
1014| SYS_SERVICE_INIT(func) | 标识核心系统服务的初始化启动入口。 |
1015| SYS_FEATURE_INIT(func) | 标识核心系统功能的初始化启动入口。 |
1016| APP_SERVICE_INIT(func) | 标识应用层服务的初始化启动入口。   |
1017| APP_FEATURE_INIT(func) | 标识应用层功能的初始化启动入口。   |
1018
1019![](../public_sys-resources/icon-note.gif) **说明:**
1020	通过上面加载的组件编译出来的lib文件需要手动加入强制链接。
1021
1022​	如在 `vendor/bestechnic/display_demo/config.json` 中配置了`bootstrap_lite` 部件。
1023
1024```
1025    {
1026      "subsystem": "startup",
1027      "components": [
1028        {
1029          "component": "bootstrap_lite"
1030        },
1031        ...
1032      ]
1033    },
1034```
1035
1036​	`bootstrap_lite`部件会编译`//base/startup/bootstrap_lite/services/source/bootstrap_service.c`,该文件中,通过`SYS_SERVICE_INIT`将`Init`函数符号灌段到`__zinitcall_sys_service_start`和`__zinitcall_sys_service_end`中,由于`Init`函数是没有显式调用它,所以需要将它强制链接到最终的镜像。如下:
1037
1038```
1039static void Init(void)
1040{
1041    static Bootstrap bootstrap;
1042    bootstrap.GetName = GetName;
1043    bootstrap.Initialize = Initialize;
1044    bootstrap.MessageHandle = MessageHandle;
1045    bootstrap.GetTaskConfig = GetTaskConfig;
1046    bootstrap.flag = FALSE;
1047    SAMGR_GetInstance()->RegisterService((Service *)&bootstrap);
1048}
1049SYS_SERVICE_INIT(Init);   --- 通过SYS启动即SYS_INIT启动就需要强制链接生成的lib
1050```
1051
1052​	在`//base/startup/bootstrap_lite/services/source/BUILD.gn`文件中,描述了在`out/v200zr/display_demo/libs` 生成 `libbootstrap.a`,如下:
1053
1054```
1055static_library("bootstrap") {
1056  sources = [
1057    "bootstrap_service.c",
1058    "system_init.c",
1059  ]
1060  ....
1061```
1062
1063​	那么需要在 `vendor/bestechnic/display_demo/config.json` 配置强制链接库`bootstrap`,如下:
1064
1065```
1066  "bin_list": [
1067    {
1068      "elf_name": "wifiiot",
1069      "bsp_target_name": "best2600w_liteos",
1070      "signature": "false",
1071      "burn_name": "rtos_main",
1072      "enable": "true",
1073      "force_link_libs": [
1074        "bootstrap",	 --- 强制链接libbootstrap.a
1075        ...
1076      ]
1077    },
1078```
1079
1080
1081
1082适配`syspara_lite`部件时,系统参数会最终写到文件中进行持久化保存。在轻量系统中,文件操作相关接口有`POSIX`接口与`HalFiles`接口这两套实现。
1083
1084因为对接内核的文件系统,采用`POSIX`相关的接口,所以`features`字段中需要增加`enable_ohos_startup_syspara_lite_use_posix_file_api = true`。
1085
1086如果对接`HalFiles`相关的接口实现的,则无须修改。
1087
1088在适配`GetSerial`接口时,开发板不像产线生产过程那样,会写入一个具体的`Serial Number`,因而需要确定一个数据对开发板进行唯一标识。本案例采用`WiFi Mac`地址进行适配。
1089
1090```
1091#define ETH_ALEN 6
1092#define MAC_BITS 4
1093#define MAC_HIGH_MASK 0xf0
1094#define MAC_LOW_MASK 0x0f
1095#define HEX_A 0xa
1096#define CHAR_NUM_OFFSET 0x30
1097#define CHAR_CAPITAL_OFFSET 0x37
1098#define STR_END_FLAG '\0'
1099
1100typedef unsigned char               u8;
1101
1102static char serialNumber[2*ETH_ALEN + 1];		 --- 最后一位留作'\0'结束符标识
1103
1104
1105static char Hex2Char(u8 hex)
1106{
1107    if (hex < HEX_A) {
1108        return hex + CHAR_NUM_OFFSET;			 --- 将数值0转为char的'0'
1109    } else {
1110        return hex + CHAR_CAPITAL_OFFSET;		 --- 将数值0xa转为char的'A'
1111    }
1112}
1113
1114const char* HalGetSerial(void)
1115{
1116    char macAddr[ETH_ALEN];
1117    // as devboard has no production serial number, we just
1118    // use wifi mac address as device serial number.
1119    if (serialNumber[0] == STR_END_FLAG) {		 --- 只有第一次调用时,才去获取mac地址
1120        extern int bwifi_get_own_mac(u8 *addr);
1121        bwifi_get_own_mac(macAddr);				 --- 获取mac地址
1122        int j = 0;
1123        for (int i = 0; i < ETH_ALEN; i++) {
1124            u8 lowFour, highFour;
1125            highFour = (macAddr[i] & MAC_HIGH_MASK) >> MAC_BITS;
1126            serialNumber[j] = Hex2Char(highFour);
1127            j++;
1128            lowFour = macAddr[i] & MAC_LOW_MASK;
1129            serialNumber[j] = Hex2Char(lowFour);
1130            j++;
1131        }		 --- 将mac地址值转化为serial number
1132    }
1133    return serialNumber;
1134}
1135```
1136
1137#### DFX子系统适配
1138
1139进行`DFX`子系统适配需要添加`hilog_lite`部件,直接在`config.json`文件配置即可。
1140
1141```
1142{
1143  "subsystem": "hiviewdfx",
1144  "components": [
1145	{
1146	  "component": "hilog_lite",
1147	  "optional": "true"
1148	}
1149  ]
1150},
1151```
1152
1153配置完成之后,在`//device/soc/bestechnic/bes2600/liteos_m/components/utils/src/hm_sys.c`中注册日志输出实现函数。
1154
1155```
1156boolean HilogProc_Impl(const HiLogContent *hilogContent, uint32 len)
1157{
1158    char tempOutStr[LOG_FMT_MAX_LEN] = {0};
1159    if (LogContentFmt(tempOutStr, sizeof(tempOutStr), hilogContent) > 0) {
1160        printf(tempOutStr);
1161    }
1162	return TRUE;
1163}
1164
1165HiviewRegisterHilogProc(HilogProc_Impl);
1166```
1167
1168#### 系统服务管理子系统适配
1169
1170进行系统服务管理子系统适配需要添加`samgr_lite`部件,直接在`config.json`配置即可。
1171
1172```
1173{
1174  "subsystem": "systemabilitymgr",
1175  "components": [
1176	{
1177	  "component": "samgr_lite",
1178	  "features": [
1179		"config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096"
1180	  ]
1181	}
1182  ]
1183},
1184```
1185
1186在轻量系统中,`samgr_lite`配置的共享任务栈大小默认为`0x800`。当函数调用栈较大时,会出现栈溢出的问题。在本次适配过程中,将其调整为`0x1000`。
1187
1188#### 安全子系统适配
1189
1190进行安全子系统适配需要添加`huks/deviceauth_lite`部件,直接在`config.json`配置即可。
1191
1192```
1193    {
1194      "subsystem": "security",
1195      "components": [
1196        {
1197          "component": "huks",
1198          "features": [
1199            "huks_use_lite_storage = true",
1200            "huks_use_hardware_root_key = true",
1201            "huks_config_file = \"hks_config_lite.h\"",
1202            "huks_key_store_path = \"/data/\"",
1203            "ohos_security_huks_mbedtls_porting_path = \"//device/soc/bestechnic/hals/mbedtls\""
1204          ]
1205        },
1206        {
1207          "component": "deviceauth_lite",
1208          "features": [
1209            "deviceauth_storage_path = \"/data/\"",
1210            "deviceauth_hichain_thread_stack_size = 9472"
1211          ]
1212        }
1213      ]
1214    }
1215```
1216
1217`huks`部件适配时,`huks_key_store_path`配置选项用于指定存放秘钥路径,`ohos_security_huks_mbedtls_porting_path`配置选项用于指定进行`mbedtls`适配的目录,用于芯片对`mbedtls`进行硬件随机数等适配。
1218
1219`deviceauth_lite`部件适配时,`deviceauth_storage_path`配置选项用于指定存放设备认证信息的路径,`deviceauth_hichain_thread_stack_size`用于指定线程栈大小。
1220
1221#### 媒体子系统适配
1222
1223进行媒体子系统适配需要添加`histreamer`部件,直接在`config.json`配置即可。
1224
1225```
1226{
1227  "subsystem": "multimedia",
1228  "components": [
1229    {
1230      "component": "histreamer",
1231      "features": [
1232        "histreamer_enable_plugin_hdi_adapter = true",
1233        "histreamer_enable_plugin_minimp3_adapter = true",
1234        "histreamer_enable_plugin_ffmpeg_adapter = false",
1235        "config_ohos_histreamer_stack_size = 65536"
1236      ]
1237    }
1238  ]
1239},
1240```
1241
1242`histreamer`部件配置项说明如下:
1243
1244| 配置项                                   | 说明                            |
1245| ---------------------------------------- | ------------------------------- |
1246| histreamer_enable_plugin_hdi_adapter     | 是否使能histreamer对接到hdi接口。 |
1247| histreamer_enable_plugin_minimp3_adapter | 是否使能插件适配minimp3。         |
1248| histreamer_enable_plugin_ffmpeg_adapter  | 是否使能插件适配FFmpeg。          |
1249| config_ohos_histreamer_stack_size        | histreamer栈大小设置。            |
1250
1251#### 公共基础库子系统适配
1252
1253进行公共基础库子系统适配需要添加`kv_store`/`js_builtin`/`timer_task`/`kal_timer`部件,直接在`config.json`配置即可。
1254
1255```
1256{
1257  "subsystem": "utils",
1258  "components": [
1259	{
1260	  "component": "kv_store",
1261	  "features": [
1262		"enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true"
1263	  ]
1264	},
1265	{
1266	  "component": "js_builtin"
1267	},
1268	{
1269	  "component": "timer_task"
1270	},
1271	{
1272	  "component": "kal_timer",
1273	}
1274  ]
1275},
1276```
1277
1278与适配`syspara_lite`部件类似,适配`kv_store`部件时,键值对会写到文件中。在轻量系统中,文件操作相关接口有`POSIX`接口与`HalFiles`接口这两套实现。因为对接内核的文件系统,采用`POSIX`相关的接口,所以`features`需要增加`enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true`。如果对接`HalFiles`相关的接口实现的,则无须修改。
1279
1280#### 图形子系统适配
1281
1282进行图形子系统适配需要添加`graphic_utils`部件,直接在`config.json`配置即可。
1283
1284```
1285    {
1286      "components": [
1287        {
1288          "component": "graphic_utils",
1289          "features": [
1290            "enable_ohos_graphic_utils_product_config = true"
1291          ]
1292        },
1293        {
1294          "component": "ui"
1295        }
1296      ]
1297    },
1298```
1299
1300`graphic`配置文件见 `//vendor/bestechnic/display_demo/graphic_config/product_graphic_lite_config.h`。
1301
1302`graphic`适配见`//device/soc/bestechnic/bes2600/liteos_m/components/ui`, 主要功能如下:
1303
1304- `display_device`:实例化`BaseGfxEngine`。
1305- `touch_input`:实例化`PointerInputDevice`。
1306- `UiMainTask`:初始化字体引擎,执行渲染任务等。
1307
1308图形子系统层次:
1309
1310```
1311aafwk_lite + appexecfwk_lite    (AAFWK + APPEXECFWK)
1312      |
1313ace_engine_lite + jerryscript + i18n_lite + resmgr_lite + utils/native/lite/... (ACE,JS引擎及其依赖)
1314      |
1315arkui_ui_lite + graphic_utils      (图形框架)
1316      |
1317giflib + libjpeg + libpng + qrcodegen + freetype... (图形第三方库)
1318```
1319
1320图形应用示例见文件`//vendor/bestechnic/display_demo/tests/app.cpp`,如下:
1321
1322```
1323/* ui app entry */
1324void RunApp()
1325{
1326#ifdef UI_TEST
1327    AnimatorDemoStart(); 	 --- native ui demo
1328#elif defined(ABILITY_TEST)
1329    StartJSApp(); 			 --- js demo
1330#endif
1331}
1332
1333void AppEntry(void)
1334{
1335    UiMain();
1336}
1337
1338APP_FEATURE_INIT(AppEntry);
1339```
1340
1341#### ACE开发框架子系统适配
1342
1343进行`ACE`开发框架子系统适配需要添加`ace_engine_lite`部件,直接在`config.json`配置即可。
1344
1345    {
1346      "subsystem": "ace",
1347      "components": [
1348        {
1349          "component": "ace_engine_lite",
1350          "features": [
1351            "ace_engine_lite_feature_product_config = true"
1352          ]
1353        }
1354      ]
1355    },
1356
1357`ace_engine_lite`部件配置文件见 `//vendor/bestechnic/display_demo/ace_lite_config/product_acelite_config.h`。
1358
1359`ace_lite`的应用采用js语言进行开发,详细步骤如下:
1360
13611. 用`DevEco Studio`编写js应用,参考[《DevEco Studio指南》](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-tools-overview)13622. 使用预览功能进行预览,并且得到js包:`entry\.preview\intermediates\res\debug\lite\assets\js\default`。
13633. 将js包放到对应的文件系统目录下,文件系统路径为`vendor/bestechnic/display_demo/fs/data/data/js`,如下:
1364
1365   ```
1366   ├── app.js
1367   ├── common
1368   ├── i18n
1369   ├── manifest.json
1370   └── pages
1371   ```
1372
13734. 最终编译生成系统镜像,烧录到单板后,系统会从`app.js`加载启动`ace`的应用。
1374
1375#### 元能力子系统适配
1376
1377进行元能力子系统适配需要添加`aafwk_lite`部件,直接在`config.json`配置即可。
1378
1379```
1380    {
1381      "subsystem": "aafwk",
1382      "components": [
1383        {
1384          "component": "aafwk_lite",
1385          "features": [
1386            "ability_lite_enable_ohos_appexecfwk_feature_ability = true",	 --- 支持FA特性,即包含图形能力
1387            "ability_lite_config_ohos_aafwk_ams_task_size = 4096"			 --- 配置aafwk栈的大小
1388          ]
1389        }
1390      ]
1391    },
1392```
1393
1394`aafwk_lite`相关的应用样例见`vendor/bestechnic/display_demo/tests/ability`目录,包含`launcher`和`js app`这两类应用,应用的函数调用流程描述如下:
1395
13961. `launcher`应用,通过`InstallLauncher`安装`BundleName`为 `"com.example.launcher"`的`native ui`应用,在`AbilityMgrSliteFeature`启动后会调用`AbilityMgrHandler::StartLauncher()`启动`launcher`应用。
1397
13982. `StartJSApp`应用,通过`StartAbility`启动任意`Want`,通过将`want data`传递`JS_APP_PATH`,
1399   `SetWantData(&want, JS_APP_PATH, strlen(JS_APP_PATH) + 1)`。
1400
1401#### 包管理子系统适配
1402
1403进行包管理子系统适配需要添加`appexecfwk_lite`部件,直接在`config.json`配置即可。
1404
1405```
1406    {
1407      "subsystem": "appexecfwk",
1408      "components": [
1409        {
1410          "component": "appexecfwk_lite"
1411        }
1412      ]
1413    },
1414```
1415
1416## 兼容性认证
1417
1418### 产品兼容性规范
1419
1420产品兼容性规范文档请参考[产品兼容性SIG介绍](https://gitee.com/openharmony-sig/compatibility/tree/master)1421
1422### XTS用例
1423
1424`XTS`测试参考资料见[xts参考资料](../device-test/xts.md),进行`XTS`子系统适配需要添加`xts_acts`/`xts_tools`部件,直接在`config.json`配置即可,配置如下:
1425
1426    {
1427      "subsystem": "xts",
1428      "components": [
1429        { "component": "xts_acts", "features":
1430          [
1431            "config_ohos_xts_acts_utils_lite_kv_store_data_path = \"/data\"",
1432            "enable_ohos_test_xts_acts_use_thirdparty_lwip = true"
1433          ]
1434        },
1435        { "component": "xts_tools", "features":[] }
1436      ]
1437    }
1438
1439其中,
1440
1441- `config_ohos_xts_acts_utils_lite_kv_store_data_path` 是配置挂载文件系统根目录的名字。
1442- `enable_ohos_test_xts_acts_use_thirdparty_lwip` 表示如果使用`thirdparty/lwip`目录下的源码编译,则设置为`true`,否则设置为`false`。
1443
1444全部跑完会有显示`xx Tests xx Failures xx Ignored`,如下:
1445
1446```
1447...
1448[16:53:43:438]../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:793:testKvStoreMaxSize004:PASS
1449[16:53:43:438]+-------------------------------------------+
1450[16:53:43:438]
1451[16:53:43:438]-----------------------
1452[16:53:43:438]32 Tests 0 Failures 0 Ignored
1453[16:53:43:438]OK
1454[16:53:43:439]All the test suites finished!
1455```
1456
1457### 报告提交
1458
1459将上图`XTS`用例的情况保存为测试报告,上传到`OpenHarmony`兼容性测试网站进行认证,作为`sig`仓库转正到`master`仓库的必要条件。详细步骤如下:
1460
1461步骤1:将`XTS`测试报告压缩成`zip`文件。
1462
1463步骤2:生成测试报告的`SHA`校验码。本案例是将`zip`文件传到在线生成`hash`的[网站]( https://tool.lmeee.com/jiami/filehash)生成`SHA`校验码。
1464
1465步骤3:进入`OpenHarmony`[兼容性测试网站](https://www.openharmony.cn/certification/document/guid)上传报告。
1466
1467 - 其中`API Level`填写报告中的`"sdkApiLevel"`字段
1468 - `OS`版本号填写报告中的`"OS Version"`字段。
1469
1470## todo
1471
1472后续会补充以下方面的移植:
1473
1474- 蓝牙
1475- `bms`包安装
1476- 验证运行`JS`的`bytecode`
1477- 分布式能力:`dms`、`dm`
1478- 分布式音乐播放器样例