[文章]

【HarmonyOS HiSpark AI Camera试用连载】HDF驱动和应用的编译框架梳理(手把手教你新增一个自定义的hdf驱动和应用)

2020-12-6 21:32:57  550 HiSpark Camera HarmonyOS Hi3516 鸿蒙系统
分享
3
参考文档
链接:https://gitee.com/openharmony/docs/blob/master/driver/HDF%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6.md
HDF驱动框架
  • HDF开发概述
  • 驱动开发
  • 驱动服务管理
  • 驱动消息机制管理
  • 配置管理
  • HDF开发实例

HDF(OpenHarmony Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

1. 配置管理

驱动配置包含两部分,HDF框架定义的驱动设备描述(必选)和驱动的私有配置信息(可选)。

HDF框架加载驱动所需要的信息来源于HDF框架定义的device_info.hcs配置文件(驱动设备描述)。

如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init(参考驱动开发)传递给驱动。配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs。

HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解,便于开发者进行配置管理。
在HDF框架的配置文件中添加该驱动的配置信息,如下所示:
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
  1. root {
  2.     device_info {
  3.         match_attr = "hdf_manager";
  4.         template host {
  5.             hostName = "";
  6.             priority = 100;
  7.             template device {
  8.                 template deviceNode {
  9.                     policy = 0;
  10.                     priority = 100;
  11.                     preload = 0;
  12.                     permission = 0664;
  13.                     moduleName = "";
  14.                     serviceName = "";
  15.                     deviceMatchAttr = "";
  16.                 }
  17.             }
  18.         }
  19.         ++++
  20.         sample_host :: host {
  21.             hostName = "sample_host"; // host名称,host节点是用来存放某一类驱动的容器
  22.             sample_device :: device { // sample设备节点
  23.                 device0 :: deviceNode { // sample驱动的DeviceNode节点
  24.                     policy = 2; // 驱动服务发布的策略:驱动对内核态和用户态都发布服务
  25.                     priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序
  26.                     preload = 0; // 0=DEVICE_PRELOAD_ENABLE,系统启动过程中默认加载驱动。
  27.                                  // 1=DEVICE_PRELOAD_DISABLE,系统启动过程中默认不加载驱动。
  28.                     permission = 0664; // 驱动创建设备节点权限
  29.                     moduleName = "sample_driver"; // 驱动和设备提供的服务相关联
  30.                     serviceName = "sample_service"; // 用户空间通过服务名即可找到对应设备的驱动进行操作
  31.                     //deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
  32.                 }
  33.             }
  34.         }
  35.         ++++
  36.     }
  37. }

  38. #if 0
  39. // 补充:
  40. // 驱动有私有配置:
  41. root {
  42.     SampLEDriverConfig {
  43.         sample_version = 1;
  44.         sample_bus = "I2C_0";
  45.         match_attr = "sample_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
  46.     }
  47. }
  48. #endif
复制代码
HDF框架定了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:

  1. typedef enum {
  2.     /* 驱动不提供服务 */
  3.     SERVICE_POLICY_NONE = 0,
  4.     /* 驱动对内核态发布服务 */
  5.     SERVICE_POLICY_PUBLIC = 1,
  6.     /* 驱动对内核态和用户态都发布服务 */
  7.     SERVICE_POLICY_CAPACITY = 2,
  8.     /* 驱动服务不对外发布服务,但可以被订阅 */
  9.     SERVICE_POLICY_FRIENDLY = 3,
  10.     /* 驱动私有服务不对外发布服务,也不能被订阅 */
  11.     SERVICE_POLICY_PRIVATE = 4,
  12.     /* 错误的服务策略 */
  13.     SERVICE_POLICY_INVALID
  14. } ServicePolicy;
复制代码

2. 编写驱动代码
2.1 基于HDF框架编写的sample驱动代码如下:
$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/hdf_sample.c
  1. #include <fcntl.h>
  2. #include <sys/stat.h>
  3. #include <sys/ioctl.h>
  4. #include "hdf_log.h"
  5. #include "hdf_base.h"
  6. #include "hdf_device_desc.h"

  7. #define HDF_LOG_TAG sample_driver

  8. // 设置cmd编号,类似于Linux的ioctl命令码。
  9. #define SAMPLE_WRITE_READ 123
  10. #define SELECT_DEFAULT 0

  11. // Dispatch是驱动中用来处理用户态发下来的消息的函数。
  12. //int32_t HdfSampleDriverDispatch( struct HdfDeviceObject *deviceObject, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
  13. int32_t HdfSampleDriverDispatch(struct HdfDeviceIoClient *deviceIoClient, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
  14. {
  15.     HDF_LOGE("%s: received cmd %d", __func__, id);
  16.     if (id == SAMPLE_WRITE_READ) { // 通常会采用switch case的方式来写。
  17.         const char *readData = HdfSbufReadString(data); // 内核从用户空间获取数据,类似Linux的copy_from_user。
  18.         if (readData != NULL) {
  19.             HDF_LOGE("%s: read data is: %s", __func__, readData); // 内核打印从用户空间独到的数据。
  20.         }

  21.         #if SELECT_DEFAULT
  22.         // do nothing
  23.         #else
  24.         // 修改用户空发过来的数据缓冲区指针data的内容,实际上修改无效
  25.         char *readDataChange = "come from kernel sapce: change default event info";
  26.         if (!HdfSbufWriteString(data, readDataChange)) {
  27.             HDF_LOGE("%s: fail to write sbuf", __func__);
  28.         }
  29.         #endif

  30.         #if SELECT_DEFAULT
  31.         // 默认向用户空间返回的是INT32_MAX=2147483647
  32.         if (!HdfSbufWriteInt32(reply, INT32_MAX)) { // 内核向用户空间返回数据,类似Linux的copy_to_user。
  33.             HDF_LOGE("%s: reply int32 fail", __func__);
  34.         }
  35.         #else
  36.         // 将向用户空间返回内容修改为字符串
  37.         char *sendData = "come from kernel sapce: default event info";
  38.         if (!HdfSbufWriteString(reply, sendData)) {
  39.             HDF_LOGE("%s: fail to write sbuf", __func__);
  40.         }
  41.         #endif

  42.         // 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
  43.             // return HdfDeviceSendEvent(deviceObject, id, data);
  44.         return HdfDeviceSendEvent(deviceIoClient->device, id, data);
  45.     }
  46.     return HDF_FAILURE;
  47. }

  48. // 驱动资源释放的接口,本例未分配需要回收的资源,因此为空。
  49. void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
  50. {
  51.     // release resources here
  52.     return;
  53. }

  54. // 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
  55. int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
  56. {
  57.     if (deviceObject == NULL) {
  58.         return HDF_FAILURE;
  59.     }
  60.     static struct IDeviceIoService testService = {
  61.         // Dispatch是用来处理用户态发下来的消息。
  62.         .Dispatch = HdfSampleDriverDispatch,
  63.     };
  64.     deviceObject->service = &testService;
  65.     return HDF_SUCCESS;
  66. }

  67. #if 0
  68. // 补充:
  69. // HdfSampleDriverBind中除了常用的IDeviceIoService,这里还可以扩展其他服务。
  70. struct ISampleDriverService {
  71.     struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员
  72.     int32_t (*ServiceA)(void);               // 驱动的第一个服务接口
  73.     int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加
  74. };

  75. 驱动服务接口的实现
  76. int32_t SampleDriverServiceA(void)
  77. {
  78.     // 驱动开发者实现业务逻辑
  79.     return 0;
  80. }

  81. int32_t SampleDriverServiceB(uint32_t inputCode)
  82. {
  83.     // 驱动开发者实现业务逻辑
  84.     return 0;
  85. }

  86. int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
  87. {
  88.     if (deviceObject == NULL) {
  89.         return HDF_FAILURE;
  90.     }
  91.     static struct ISampleDriverService testService = {
  92.         .ServiceA = SampleDriverServiceA,
  93.         .ServiceB = SampleDriverServiceB,
  94.     };
  95.     deviceObject->service = &testService.ioService;
  96.     return HDF_SUCCESS;
  97. }
  98. #endif

  99. // 驱动自身业务初始的接口。
  100. int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
  101. {
  102.     if (deviceObject == NULL) {
  103.         HDF_LOGE("%s::ptr is null!", __func__);
  104.         return HDF_FAILURE;
  105.     }
  106.     HDF_LOGE("Sample driver Init success");
  107.     return HDF_SUCCESS;
  108. }

  109. // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。
  110. struct HdfDriverEntry g_sampleDriverEntry = {
  111.     .moduleVersion = 1,
  112.     .moduleName = "sample_driver", // 驱动的关键名称。
  113.     .Bind = HdfSampleDriverBind,
  114.     .Init = HdfSampleDriverInit,
  115.     .Release = HdfSampleDriverRelease,
  116. };

  117. // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,
  118. // 再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
  119. HDF_INIT(g_sampleDriverEntry);
复制代码

2.2 创建Kconfig
$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ touch Kconfig && vi Kconfig

  1. # Copyright (c) 2020 Huawei Device Co., Ltd.
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. #     http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. # See the License for the specific language governing permissions and
  12. # limitations under the License.

  13. config DRIVERS_HDF_PLATFORM_HDF_SAMPLE
  14.     bool "Enable HDF platform HDF sample driver"
  15.     default n
  16.     depends on DRIVERS_HDF_PLATFORM
  17.     help
  18.       Answer Y to enable HDF platform HDF sample driver.
复制代码
修改上层Kconfig
$ vi ~/harmony/sdk/vendor/huawei/hdf/Kconfig

  1. source "../../vendor/huawei/hdf/display/driver/Kconfig"
  2. +source "../../vendor/huawei/hdf/sample/platform/hdf-sample/Kconfig"
复制代码

2.3 创建Makefile
$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ touch Makefile && vi Makefile

  1. # Copyright (c) 2020 Huawei Device Co., Ltd.
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. #     http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. # See the License for the specific language governing permissions and
  12. # limitations under the License.

  13. include $(LITEOSTOPDIR)/config.mk
  14. include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk

  15. MODULE_NAME := hdf_sample

  16. LOCAL_CFLAGS += $(HDF_INCLUDE)

  17. LOCAL_SRCS += hdf_sample.c

  18. # 警告需要关掉,不然unused的变量都会报错!
  19. #LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror
  20. LOCAL_CFLAGS += -fstack-protector-strong

  21. include $(HDF_DRIVER)
复制代码

2.4 将驱动模块加入编译
$ vi ~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk

  1. LITEOS_BASELIB += -lhdf_uart_sample
  2. LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/uart

  3. +LITEOS_BASELIB += -lhdf_sample
  4. +LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/hdf-sample
复制代码
LITEOS_BASELIB中的hdf_sample为Makefile里的MODULE_NAME名字。

2.5 Makefile格式说明
驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。
  1. include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk #导入hdf预定义内容,必需
  2. MODULE_NAME :=    #生成的结果文件
  3. LOCAL_INCLUDE :=  #本驱动的头文件目录
  4. LOCAL_SRCS :=     #本驱动的源代码文件
  5. LOCAL_CFLAGS :=  #自定义的编译选项
  6. include $(HDF_DRIVER) #导入模板makefile完成编译
复制代码
编译结果文件链接到内核镜像,添加到vendor目录下的hdf_vendor.mk里面,示例如下:
  1. LITEOS_BASELIB +=  -lxxx  #链接生成的静态库
  2. LIB_SUBDIRS    +=         #驱动代码Makefile的目录
复制代码

2.6 驱动编译
2.6.1 编译报错问题1:kernel deconfig报错,需要修改。
$ python build.py ipcamera_hi3516dv300 -b debug

  1. ...
  2. clean hi3516dv300 finish
  3. make[1]: Entering directory '/home/ubuntu/harmony/sdk/kernel/liteos_a'
  4. /home/ubuntu/harmony/sdk/kernel/liteos_a/tools/menuconfig/conf --silentoldconfig /home/ubuntu/harmony/sdk/kernel/liteos_a/Kconfig
  5. *
  6. * Restart config...
  7. *
  8. *
  9. * Driver
  10. *
  11. Enable Driver (DRIVERS) [Y/n/?] y
  12.   Enable USB (DRIVERS_USB) [Y/n/?] y
  13.     Enable USB HCD (DRIVERS_USB_HOST_DRIVER) [Y/n/?] y
  14.       USB HCD
  15.         1. Enable EHCI HCD (USB 2.0) (DRIVERS_USB_HOST_EHCI)
  16.       > 2. Enable XHCI HCD (USB 3.0) (DRIVERS_USB_HOST_XHCI)
  17.       choice[1-2?]: 2
  18. Enable USB Device Class Drivers (DRIVERS_USB_DEVICE_CLASS_DRIVERS) [Y/n/?] y
  19. Enable HDF manager (DRIVERS_HDF) [Y/n/?] y
  20.   Enable HDF platform driver (DRIVERS_HDF_PLATFORM) [Y/n/?] y
  21.     Enable HDF platform i2c driver (DRIVERS_HDF_PLATFORM_I2C) [Y/n/?] y
  22.     Enable HDF platform spi driver (DRIVERS_HDF_PLATFORM_SPI) [Y/n/?] y
  23.     Enable HDF platform gpio driver (DRIVERS_HDF_PLATFORM_GPIO) [Y/n/?] y
  24.     Enable HDF platform watchdog driver (DRIVERS_HDF_PLATFORM_WATCHDOG) [Y/n/?] y
  25.     Enable HDF platform sdio driver (DRIVERS_HDF_PLATFORM_SDIO) [Y/n/?] y
  26.     Enable HDF platform rtc driver (DRIVERS_HDF_PLATFORM_RTC) [Y/n/?] y
  27.     Enable HDF hisi sdk driver (DRIVERS_HDF_PLATFORM_HISI_SDK) [Y/n/?] y
  28.   Enable HDF WiFi Host driver (DRIVERS_HDF_WIFI) [Y/n/?] y
  29.     Enable Hi3881 Host driver (DRIVERS_HI3881) [Y/n/?] y
  30.   Enable HDF input driver (DRIVERS_HDF_INPUT) [Y/n/?] y
  31.     Enable HDF tp 5P5 gt911 driver (DRIVERS_HDF_TP_5P5_GT911) [Y/n/?] y
  32.   Enable HDF Lcd driver (DRIVERS_HDF_LCD) [Y/n/?] y
  33.     Enable HDF Lcd Icn9700 driver (DRIVERS_HDF_LCD_ICN9700) [Y/n/?] y
  34. Enable HDF platform HDF sample driver (DRIVERS_HDF_PLATFORM_HDF_SAMPLE) [N/y/?] (NEW) aborted!

  35. Console input/output is redirected. Run 'make oldconfig' to update configuration.

  36. make[1]: *** [Makefile:129: genconfig] Error 1
复制代码
处理:
通过搜索定位DRIVERS_HDF_LCD_ICN9700 的配置,然后参考其用法。
$ grep DRIVERS_HDF_LCD_ICN9700 . -nr

  1. ./kernel/liteos_a/.config:159:LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
  2. ./vendor/huawei/hdf/display/driver/Kconfig:8:config DRIVERS_HDF_LCD_ICN9700
复制代码
查看该目录下的一个编译脚本build.sh:
$ vi ~/harmony/sdk/kernel/liteos_a/build.sh
发现在脚本里面将hi3516dv300_release.config拷贝为了.config:
$ vi ~/harmony/sdk/kernel/liteos_a/tools/build/config/hi3516dv300_release.config
查看配置:
$ vi ~/harmony/sdk/kernel/liteos_a/.config
尝试手动添加DRIVERS_HDF_PLATFORM_HDF_SAMPLE为y:
$ vi ~/harmony/sdk/kernel/liteos_a/tools/build/config/hi3516dv300_clang.config

  1. LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
  2. +LOSCFG_DRIVERS_HDF_PLATFORM_HDF_SAMPLE=y
  3. LOSCFG_DRIVERS_HDF_USB=y
复制代码
让修改生效:
$ cd ~/harmony/sdk/kernel/liteos_a/
$ ./build.sh hi3516dv300 clang debug

  1. sh param:hi3516dv300,clang,debug
复制代码
然后重新编译,该问题解决通过。

2.6.2 编译报错问题2:变量类型不匹配

  1. hdf_sample.c:53:21:
  2. error: incompatible pointer types initializing
  3. 'int32_t (*)(struct HdfDeviceIoClient *, int, struct HdfSBuf *, struct HdfSBuf *)'
  4. (aka 'int (*)(struct HdfDeviceIoClient *, int, struct HdfSBuf *, struct HdfSBuf *)')
  5. with an expression of type
  6. 'int (struct HdfDeviceObject *, int, struct HdfSBuf *, struct HdfSBuf *)'
  7. [-Werror,-Wincompatible-pointer-types]

  8.         .Dispatch = HdfSampleDriverDispatch,
复制代码
处理:
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/hdf_sample.c

  1. // Dispatch是驱动中用来处理用户态发下来的消息的函数。
  2. -int32_t HdfSampleDriverDispatch( struct HdfDeviceObject *deviceObject, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
  3. +int32_t HdfSampleDriverDispatch(struct HdfDeviceIoClient *deviceIoClient, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
  4. {
  5.     HDF_LOGE("%s: received cmd %d", __func__, id);

  6.         // 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
  7. -        return HdfDeviceSendEvent(deviceObject, id, data);
  8. +        return HdfDeviceSendEvent(deviceIoClient->device, id, data);
  9.     }
  10.     return HDF_FAILURE;
  11. }
复制代码
再次编译,该问题解决通过。
查看驱动模块hdf_sample存放位置:
~/harmony/sdk$ find -name hdf_sample

  1. ./out/ipcamera_hi3516dv300/obj/kernel/liteos_a/obj/vendor/huawei/hdf/sample/platform/hdf_sample
复制代码

3. 编写用户程序和驱动交互代码
3.1 基于HDF框架编写的用户态程序和驱动交互的代码如下:
$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/app
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/app/hdf_sample_app.c
  1. #include <fcntl.h>
  2. #include <unistd.h>
  3. #include "hdf_log.h"
  4. #include "hdf_sbuf.h"
  5. #include "hdf_io_service_if.h"

  6. #define HDF_LOG_TAG "sample_test"
  7. #define SAMPLE_SERVICE_NAME "sample_service"  // 服务的关键名称。

  8. #define SAMPLE_WRITE_READ 123
  9. #define SELECT_DEFAULT 0

  10. int g_replyFlag = 0;

  11. // 用户空间回调函数,驱动通过HdfDeviceSendEvent发送事件后,该函数将处理返回数据。
  12. static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
  13. {
  14.     const char *string = HdfSbufReadString(data);
  15.     if (string == NULL) {
  16.         HDF_LOGE("fail to read string in event data");
  17.         g_replyFlag = 1;
  18.         return HDF_FAILURE;
  19.     }
  20.     HDF_LOGE("user space received:");
  21.     HDF_LOGE("%s: dev event received: %u %s",  (char *)priv, id, string);
  22.     g_replyFlag = 1;
  23.     return HDF_SUCCESS;
  24. }

  25. // 用户空间给内核驱动发送数据
  26. static int SendEvent(struct HdfIoService *serv, char *eventData)
  27. {
  28.     int ret = 0;

  29.     // 获取系统分配的data sbuf。
  30.     struct HdfSBuf *data = HdfSBufObtainDefaultSize();
  31.     if (data == NULL) {
  32.         HDF_LOGE("fail to obtain sbuf data");
  33.         return 1;
  34.     }

  35.     // 获取系统分配的reply sbuf。
  36.     struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
  37.     if (reply == NULL) {
  38.         HDF_LOGE("fail to obtain sbuf reply");
  39.         ret = HDF_DEV_ERR_NO_MEMORY;
  40.         goto out;
  41.     }

  42.     // 将用户空间目标数据放到sbuf。
  43.     if (!HdfSbufWriteString(data, eventData)) {
  44.         HDF_LOGE("fail to write sbuf");
  45.         ret = HDF_FAILURE;
  46.         goto out;
  47.     }

  48.     // 通过驱动服务提供的Dispatch实现用户态应用和内核态驱动的信息交互
  49.     ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
  50.     if (ret != HDF_SUCCESS) {
  51.         HDF_LOGE("fail to send service call");
  52.         goto out;
  53.     }

  54.     #if SELECT_DEFAULT
  55.     // 默认将sbuf中获取到的int数据放到用户空间变量中。
  56.     int replyData = 0;
  57.     if (!HdfSbufReadInt32(reply, &replyData)) {
  58.         HDF_LOGE("fail to get service call reply");
  59.         ret = HDF_ERR_INVALID_OBJECT;
  60.         goto out;
  61.     }
  62.     HDF_LOGE("Get reply is: %d", replyData);
  63.     #else
  64.     // 将sbuf中获取到的字符串数据放到用户空间变量中。
  65.     const char *replyData = HdfSbufReadString(reply);
  66.     if (replyData == NULL) {
  67.         HDF_LOGE("fail to get service call reply");
  68.         ret = HDF_ERR_INVALID_OBJECT;
  69.         goto out;
  70.     }
  71.     HDF_LOGE("Get reply is: %s", replyData);
  72.     #endif
  73. out:
  74.     HdfSBufRecycle(data); // 释放用于数据中转的sbuf
  75.     HdfSBufRecycle(reply); // 释放用于数据中转的sbuf
  76.     return ret;
  77. }

  78. int main()
  79. {
  80.     char *sendData = "come from user sapce: default event info";

  81.     // 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
  82.     struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME, 0);
  83.     if (serv == NULL) {
  84.         HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);
  85.         return HDF_FAILURE;
  86.     }

  87.     // 注册订阅驱动服务的回调函数。
  88.     static struct HdfDevEventlistener listener = {
  89.         .callBack = OnDevEventReceived,
  90.         .priv ="Service0"
  91.     };

  92. #if 0
  93.     // 补充:
  94.     // 驱动服务的获取有两种方式,HDF框架提供接口直接获取和HDF框架提供订阅机制获取。
  95.     // 1. 当明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:
  96.         const struct ISampleDriverService *sampleService =
  97.                 (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
  98.         if (sampleService == NULL) {
  99.             return -1;
  100.         }
  101.         sampleService->ServiceA();
  102.         sampleService->ServiceB(5);
  103.     // 2. 当对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动。
  104. #endif

  105.     // 通过HDF提供的订阅机制获取驱动服务。
  106.     if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
  107.         HDF_LOGE("fail to register event listener");
  108.         return HDF_FAILURE;
  109.     }

  110.     // 用户空间给内核驱动发送数据
  111.     HDF_LOGE("user space send:");
  112.     HDF_LOGE("cmd: %u, send data is: %s", SAMPLE_WRITE_READ, sendData);
  113.     if (SendEvent(serv, sendData)) {
  114.         HDF_LOGE("fail to send event");
  115.         return HDF_FAILURE;
  116.     }

  117.     /* wait for event receive event finishing */
  118.     while (g_replyFlag == 0) {
  119.         sleep(1);
  120.     }

  121.     // 用户态程序注册接收驱动上报事件的操作方法。
  122.     if (HdfDeviceUnregisterEventListener(serv, &listener)) {
  123.         HDF_LOGE("fail to  unregister listener");
  124.         return HDF_FAILURE;
  125.     }

  126.     // 释放驱动服务。
  127.     HdfIoServiceRecycle(serv);
  128.     return HDF_SUCCESS;
  129. }
复制代码
说明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:

  1. deps = [ "//drivers/hdf/lite/manager:hdf_core", "//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal", ]
复制代码

3.2 编写编译APP源码的BUILD.gn文件
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/BUILD.gn
  1. # Copyright (c) 2020 Huawei Device Co., Ltd.
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. #     http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. # See the License for the specific language governing permissions and
  12. # limitations under the License.

  13. import("//build/lite/config/component/lite_component.gni")
  14. import("//build/lite/ndk/ndk.gni")

  15. HDF_FRAMEWORKS = "//drivers/hdf/frameworks"

  16. executable("hdf_sample_app") {
  17.     sources = [
  18.         "hdf_sample_app.c"
  19.     ]

  20.     include_dirs = [
  21.         "$HDF_FRAMEWORKS/ability/sbuf/include",
  22.         "$HDF_FRAMEWORKS/core/shared/include",
  23.         "$HDF_FRAMEWORKS/core/host/include",
  24.         "$HDF_FRAMEWORKS/core/master/include",
  25.         "$HDF_FRAMEWORKS/include/core",
  26.         "$HDF_FRAMEWORKS/include/utils",
  27.         "$HDF_FRAMEWORKS/utils/include",
  28.         "$HDF_FRAMEWORKS/include/osal",
  29.         "//third_party/bounds_checking_function/include",
  30.     ]

  31.     deps = [
  32.         "//drivers/hdf/lite/manager:hdf_core",
  33.         "//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal",
  34.     ]

  35.     public_deps = [
  36.         "//third_party/bounds_checking_function:libsec_shared",
  37.     ]
  38.     defines = [
  39.         "__USER__",
  40.     ]

  41.     cflags = [
  42.         "-Wall",
  43.         "-Wextra",
  44.     ]
  45. }
复制代码
注意,里面的对齐不能使用table键,否则编译会报错。

3.3 将hdf_sample_app.c加入编译
$ vi ~/harmony/sdk/build/lite/product/ipcamera_hi3516dv300.json

  1. {
  2.   "subsystem": [
  3.     {
  4.       "name": "hdf",
  5.       "component": [
  6.         { "name": "posix", "dir": "//drivers/hdf/lite/posix:hdf_posix", "features":[] },
  7.         { "name": "manager", "dir": "//drivers/hdf/lite/manager:hdf_manager", "features":[] },
  8.         { "name": "wifi", "dir": "//vendor/huawei/hdf/wifi:wifi_firmware", "features":[] },
  9.         { "name": "display", "dir": "//vendor/huawei/hdf/display/hdi:hdi_display", "features":[] },
  10.         { "name": "input", "dir": "//vendor/huawei/hdf/input/hdi:hdi_input", "features":[] },
  11.         { "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "features":[] },
  12. +        { "name": "hdf_sample_app", "dir": "//vendor/huawei/hdf/sample/platform/hdf-sample/app:hdf_sample_app", "features":[] }
  13.       ]
  14.     },
  15.   ]
  16. }
复制代码

3.4 应用编译
$ python build.py ipcamera_hi3516dv300 -b debug
$ vi out/ipcamera_hi3516dv300/build.log
  1. Done. Made 251 targets from 160 files in 907ms
  2. ninja: Entering directory `/home/ubuntu/harmony/sdk/out/ipcamera_hi3516dv300'
  3. [1/1346] STAMP obj/applications/sample/camera/communication/sample.stamp
  4. ...
  5. // 新增的hdf_sample_app
  6. [1163/1346] clang obj/vendor/huawei/hdf/sample/platform/hdf-sample/app/hdf_sample_app.o
  7. [1167/1346] LLVM LINK ./bin/hdf_sample_app
  8. // 可供参考的hello_uart
  9. [1160/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dev/hello_uart_dev.o
  10. [1161/1346] LLVM LINK ./bin/hello_uart
  11. [1169/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/uart_if.o
  12. [1170/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/hello_uart_dispatch.o
  13. [1171/1346] LLVM LINK ./bin/hello_uart_dispatch
  14. [1172/1346] STAMP obj/vendor/huawei/hdf/sample/platform/uart/hello_uart_sample.stamp
  15. ...
  16. [1344/1346] STAMP obj/build/lite/ohos.stamp
  17. [1345/1346] ACTION //build/lite:gen_rootfs(//build/lite/toolchain:linux_x86_64_clang)
  18. [1346/1346] STAMP obj/build/lite/gen_rootfs.stamp
  19. ohos ipcamera_hi3516dv300 build success!
复制代码
通过上面的编译日志可以找到hdf_sample_app应用放在了/bin目录:
  1. ./bin/hdf_sample_app
复制代码

4. 烧录
4.1 将系统bin文件拷贝到Windows共享目录中:
$ rm /mnt/hgfs/proj-harmony/images/out/ -RF
$ cp -rf out/ /mnt/hgfs/proj-harmony/images/
4.2 在HiTool工具中依次填入OHOS_Image.bin、rootfs.img、userfs.img的文件位置
bin_file.png
操作流程如下:
shunxu.png
修改一下分区长度:
part.png
点击“烧写”按键后,控制台会提示重启目标板,重启后,系统就自动进入烧写了。
单板初次启动需要修改启动参数,重新上电后登陆会进入uboot中,如果分区位置没有变化则不用执行下面修改启动参数的指令
hisilicon # setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x3000; go 0x80000000";
hisilicon # setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=7M rootsize=15M rw";
hisilicon # saveenv
hisilicon # reset
重启后进入系统。

注释:表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,“rootaddr=6M rootsize=14M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与新增rootfs.img文件时所填大小必须相同。

5. 测试
在根目录下,在命令行输入下面命令执行写入的demo程序,显示成功结果如下所示:
  1. OHOS # ./bin/hdf_sample_app
  2. // 用户空间给内核驱动发送数据
  3. [HDF:E/"sample_test"]user space send:
  4. [HDF:E/"sample_test"]cmd: 123, send data is: come from user sapce: default event info

  5. // 被驱动中用来处理用户态发下来的消息的函数HdfSampleDriverDispatch接收到
  6. [ERR][HDF:E/sample_driver]HdfSampleDriverDispatch: received cmd 123
  7. [ERR][HDF:E/sample_driver]HdfSampleDriverDispatch: read data is: come from user sapce: default event info

  8. // 默认:用户空间回调函数OnDevEventReceived的打印信息
  9. [HDF:E/"sample_test"]user space received:
  10. [HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info
  11. [HDF:E/"sample_test"]Get reply is: 2147483647
  12. [HDF:E/hdf_syscall_adapter]event listener task exit
  13. // 修改后:
  14. [HDF:E/"sample_test"]user space received:
  15. [HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info // 这了尝试打印出“change default event info”,但修改无效
  16. [HDF:E/"sample_test"]Get reply is: come from kernel sapce: default event info
  17. [HDF:E/hdf_syscall_adapter]event listener task exit
复制代码
打印结果的截图:

result.png

6. 吐槽:
1. 内核config的详细配置说明没有看到。
2. 编译报错位置定位麻烦,不会在编译出错的位置立马停下来,需要在编译日志中搜索关键字FAILED、failed、ERROR、error等。
3. 重新编译时未修改过的文件都要重新编译一遍;分区文件的内容大概是些什么也不清楚,如果只修改了驱动或文件系统,是否可以只烧写某一个分区;不知道在哪里设置多核并行编译等。
4. 驱动如何手动加载和卸载也无说明。
5. 发现华为对整个SDK框架介绍比较少,好在靠自己慢慢分析SDK结构、或者照着已有源码“依葫芦画瓢”勉强能解决大部分问题,不过这样走了很多弯路,希望华为能逐渐将关键细节都写到开发文档中,不用每个人都走一遍常见的问题。

7. 附件:
修改过后的diff文件:
ubuntu@ubuntu20:~/harmony/sdk$ git show > hdf_test.diff



本文结束,感谢您的阅读!



hdf_test.diff.zip

5.4 KB, 下载次数: 1, 下载积分: 积分 -1 分

绿波电龙 2020-12-7 09:44:32
这样的文章很好!
1 2回复

举报

  • 张浩 2020-12-20 20:09

    第一次被置顶,开心:)再弄一篇gpio的操作应该整个驱动框架就很清晰了。

  • 张浩 2020-12-20 20:10

    一直想开始弄摄像头相关的驱动和应用,可惜周末时间有限

评论

您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发文章