集成三方SDK

OpenHarmony致力于打造一套更加开放完善的IoT生态系统,为此OpenHarmony规划了一组目录,用于将各厂商的SDK集成到OpenHarmony中。本文档基于Hi3861开发板,向平台开发者介绍将SDK集成到OpenHarmony的方法。

规划目录结构

三方SDK通常由静态库和适配代码构成。SDK的业务逻辑通过硬件模组工具链编译得到静态库libs,每款模组都有其对应的libs。SDK的南向API与OpenHarmony API的存在使用差异,该差异可通过adapter适配代码屏蔽,不同模组可共用一套adapter。

基于以上特征,在OpenHarmony目录结构中,可以对三方SDK目录做如下划分。

  • 适配代码adapter,放置到./domains/iot/link/ 目录下,与模组解耦。
  • 业务库libs,放置到./vendor/hisi/hi3861/hi3861/3rd_sdk/ 目录下,与模组绑定。

平台开发者在适配前,务必先依次完成以下步骤,下面以demolink SDK举例,进行介绍。

  1. 创建厂商目录,./domains/iot/link/demolink/、./vendor/hisi/hi3861/hi3861/3rd_sdk/demolink/ ,用于厂商隔离。
  2. 创建./domains/iot/link/demolink/BUILD.gn ,用于构建适配代码。
  3. 创建./vendor/hisi/hi3861/hi3861/3rd_sdk/demolink/libs/ 目录,用于存放业务库libs。
  1. .
  2. ├── domains
  3. └── iot
  4. └── link
  5. ├── demolink
  6. └── BUILD.gn
  7. ├── libbuild
  8. └── BUILD.gn
  9. └── BUILD.gn
  10. └── vendor
  11. └── hisi
  12. └── hi3861
  13. └── hi3861
  14. └── 3rd_sdk
  15. └── demolink
  16. └── libs

构建业务libs

平台SDK业务一般以静态库的形式提供,平台厂商在获取到OpenHarmony代码后,需要根据对应的硬件模组vendor,进行编译业务libs,并将编译结果放置在./vendor/hisi/hi3861/hi3861/3rd_sdk/demolink/libs/ 目录下。下面介绍业务libs的构建方法。

OpenHarmony已规划用于编译业务libs的目录./domains/iot/link/libbuild/ ,该目录中包含./domains/iot/link/libbuild/BUILD.gn和./domains/iot/link/BUILD.gn文件,目录结构如下。

  1. .
  2. └── domains
  3. └── iot
  4. └── link
  5. ├── demolink
  6. └── BUILD.gn
  7. ├── libbuild
  8. └── BUILD.gn
  9. └── BUILD.gn

平台开发者在构建libs前,务必先完成如下步骤。

  1. 在./domains/iot/link/libbuild/ 目录下放置业务源码文件,包括.c和.h文件。编译完成后清空目录下源码文件。

    1. .
    2. └── domains
    3. └── iot
    4. └── link
    5. ├── demolink
    6. ├── demosdk_adapter.c
    7. ├── demosdk_adapter.h
    8. └── BUILD.gn
    9. ├── libbuild
    10. ├── demosdk.c
    11. ├── demosdk.h
    12. └── BUILD.gn
    13. └── BUILD.gn
  2. 适配./domains/iot/link/libbuild/BUILD.gn,在编译完成后还原该文件。

    在BUILD.gn中,sources为需要参与构建的源文件,include_dirs为依赖的头文件路径,构建的目标结果是生成静态库libdemosdk.a。

    1. static_library("demosdk") {
    2. sources = [
    3. "demosdk.c"
    4. ]
    5. include_dirs = [
    6. "//domains/iot/link/libbuild",
    7. "//domains/iot/link/demolink"
    8. ]
    9. }
  3. 适配./domains/iot/link/BUILD.gn,在编译完成后还原该文件。

    此BUILD.gn文件用于指定构建条目,需要在features中填入所有需参与编译的静态库条目,使./domains/iot/link/libbuild/BUILD.gn参与到构建中来。

    1. import("//build/lite/config/subsystem/lite_subsystem.gni")
    2. import("//build/lite/config/component/lite_component.gni")
    3. lite_subsystem("iot") {
    4. subsystem_components = [
    5. ":link"
    6. ]
    7. }
    8. lite_component("link") {
    9. version = "1.0.0"
    10. dependences = []
    11. features = [
    12. "libbuild:demosdk"
    13. ]
    14. }

完成以上3点后,需在代码根目录下执行命令python build.py wifiiot —target //domains/iot/link:iot,等待执行完成,检查./out/wifiiot/libs目录下是否生成了目标库文件。

集成三方SDK - 图1

将库文件拷贝到./vendor/hisi/hi3861/hi3861/3rd_sdk/demolink/libs/ 目录下,并将./domains/iot/link/libbuild/ 目录中的.c和.h文件清除。

编写适配代码

代码编写

平台SDK中使用的API通常与OpenHarmony API存在差异,无法直接使用,需要一层适配代码adapter进行中间转换。本节以./domains/iot/link/demolink/demosdk_adapter.c中的任务创建接口DemoSdkCreateTask举例,向开发者演示如何在OpenHarmony上编写适配代码。

  1. 查看待适配接口DemoSdkCreateTask的描述、参数、返回值。

    1. struct TaskPara {
    2. char *name;
    3. void *(*func)(char* arg);
    4. void *arg;
    5. unsigned char prio;
    6. unsigned int size;
    7. };
    8. /*
    9. * IoT OS 创建线程接口
    10. * 返回值: 返回0 成功, 其他 失败
    11. */
    12. int DemoSdkCreateTask(unsigned int *handle, const struct TaskPara *para);
  2. 查看OpenHarmony API接口文档,选取一个功能类似的接口,并比对参数及用法上的差异。例如本文选取osThreadNew ,通过和DemoSdkCreateTask接口比对,可以发现两接口依赖的参数基本一致,只是参数所归属的结构体不同。

    1. typedef struct {
    2. const char *name; ///< name of the thread
    3. uint32_t attr_bits; ///< attribute bits
    4. void *cb_mem; ///< memory for control block
    5. uint32_t cb_size; ///< size of provided memory for control block
    6. void *stack_mem; ///< memory for stack
    7. uint32_t stack_size; ///< size of stack
    8. osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
    9. TZ_ModuleId_t tz_module; ///< TrustZone module identifier
    10. uint32_t reserved; ///< reserved (must be 0)
    11. } osThreadAttr_t;
    12. /// Create a thread and add it to Active Threads.
    13. /// \param[in] func thread function.
    14. /// \param[in] argument pointer that is passed to the thread function as start argument.
    15. /// \param[in] attr thread attributes; NULL: default values.
    16. /// \return thread ID for reference by other functions or NULL in case of error.
    17. osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
  3. 完成代码差异转换。

    1. int DemoSdkCreateTask(unsigned int *handle, const struct TaskPara *para)
    2. {
    3. osThreadAttr_t attr = {0};
    4. osThreadId_t threadId;
    5. if (handle == 0 || para == 0) {
    6. return DEMOSDK_ERR;
    7. }
    8. if (para->func == 0) {
    9. return DEMOSDK_ERR;
    10. }
    11. if (para->name == 0) {
    12. return DEMOSDK_ERR;
    13. }
    14. attr.name = para->name;
    15. attr.priority = para->prio;
    16. attr.stack_size = para->size;
    17. threadId = osThreadNew((osThreadFunc_t)para->func, para->arg, &attr);
    18. if (threadId == 0) {
    19. printf("osThreadNew fail\n");
    20. return DEMOSDK_ERR;
    21. }
    22. *(unsigned int *)handle = (unsigned int)threadId;
    23. return DEMOSDK_OK;
    24. }

脚本编写

开发者在完成代码适配后,还需要在adapter同级目录下新建BUILD.gn文件。该文件可在整包构建时,将适配代码编译成静态库,并链接到bin包中去。在./domains/iot/link/demolink/BUILD.gn中,sources中为需要参与构建的源文件,include_dirs中为依赖的头文件路径,构建目标结果是生产静态库libdemolinkadapter.a。

  1. import("//build/lite/config/component/lite_component.gni")
  2. static_library("demolinkadapter") {
  3. sources = [
  4. "demosdk_adapter.c"
  5. ]
  6. include_dirs = [
  7. "//kernel/liteos-m/components/kal/include",
  8. "//kernel/liteos-m/components/cmsis/2.0",
  9. "//domains/iot/link/demolink"
  10. ]
  11. }

修改./domains/iot/link/BUILD.gn文件,使./domain/iot/hilink/BUILD.gn参与到构建系统中。

  1. import("//build/lite/config/subsystem/lite_subsystem.gni")
  2. import("//build/lite/config/component/lite_component.gni")
  3. lite_subsystem("iot") {
  4. subsystem_components = [
  5. ":link"
  6. ]
  7. }
  8. lite_component("link") {
  9. version = "1.0.0"
  10. dependences = []
  11. features = [
  12. "demolink:demolinkadapter"
  13. ]
  14. }

编写业务代码

业务libs库和适配代码准备就绪后,还需要编写业务入口函数,调起三方SDK的业务入口。

下面以demolink举例,介绍如何在./applications/sample/wifi-iot/app/路径下编写代码,调起demosdk的入口函数。

  1. 目录创建

    开发者编写业务时,务必先在./applications/sample/wifi-iot/app/ 路径下新建一个目录(或一套目录结构),用于存放业务源码文件。

    例如:在app下新增业务目录demolink,并在其中创建业务入口代码helloworld.c和编译构建文件BUILD.gn,如下。

    1. .
    2. └── applications
    3. └── sample
    4. └── wifi-iot
    5. └── app
    6. │── demolink
    7. │── helloworld.c
    8. └── BUILD.gn
    9. └── BUILD.gn
  2. 编写业务代码。

    在helloworld.c文件中编写业务入口函数DemoSdkMain,并调起demolink的业务DemoSdkEntry,最后通过SYS_RUN()调用入口函数完成业务启动。

    1. #include "hos_init.h"
    2. #include "demosdk.h"
    3. void DemoSdkMain(void)
    4. {
    5. DemoSdkEntry();
    6. }
    7. SYS_RUN(DemoSdkMain);
  3. 编写构建脚本

    新增./applications/sample/wifi-iot/app/demolink/BUILD.gn文件,指定源码和头文件路径,编译输出静态库文件libexample_demolink.a。

    1. static_library("example_demolink") {
    2. sources = [
    3. "helloworld.c"
    4. ]
    5. include_dirs = [
    6. "//utils/native/liteos/include",
    7. "//vendor/hisi/wifi-iot/hi3861/3rd_sdk/demolink"
    8. ]
    9. }

    修改./applications/sample/wifi-iot/app/BUILD.gn,使demolink参与编译。

    1. import("//build/lite/config/component/lite_component.gni")
    2. lite_component("app") {
    3. features = [
    4. "demolink:example_demolink"
    5. ]
    6. }

运行

在代码根目录下,执行命令“python build.py wifiiot”编译输出版本包。最后启动运行,运行结果如图所示,与demolink预期相符。

  1. ready to OS start
  2. sdk ver:Hi3861V100R001C00SPC024 2020-08-05 16:30:00
  3. formatting spiffs...
  4. FileSystem mount ok.
  5. wifi init success!
  6. it is demosdk entry.
  7. it is demo biz: hello world.
  8. it is demo biz: hello world.

结束

至此,三方SDK集成已介绍完毕。