#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理

蓝初柳
发布于 2021-1-7 17:38
浏览
2收藏

参考文档
链接: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

    root {
        device_info {
            match_attr = "hdf_manager";
            template host {
                hostName = "";
                priority = 100;
                template device {
                    template deviceNode {
                        policy = 0;
                        priority = 100;
                        preload = 0;
                        permission = 0664;
                        moduleName = "";
                        serviceName = "";
                        deviceMatchAttr = "";
                    }
                }
            }
            ++++
            sample_host :: host {
                hostName = "sample_host"; // host名称,host节点是用来存放某一类驱动的容器
                sample_device :: device { // sample设备节点
                    device0 :: deviceNode { // sample驱动的DeviceNode节点
                        policy = 2; // 驱动服务发布的策略:驱动对内核态和用户态都发布服务
                        priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序
                        preload = 0; // 0=DEVICE_PRELOAD_ENABLE,系统启动过程中默认加载驱动。
                                     // 1=DEVICE_PRELOAD_DISABLE,系统启动过程中默认不加载驱动。
                        permission = 0664; // 驱动创建设备节点权限
                        moduleName = "sample_driver"; // 驱动和设备提供的服务相关联
                        serviceName = "sample_service"; // 用户空间通过服务名即可找到对应设备的驱动进行操作
                        //deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
                    }
                }
            }
            ++++
        }
    }

    #if 0
    // 补充:
    // 驱动有私有配置:
    root {
        SampLEDriverConfig {
            sample_version = 1;
            sample_bus = "I2C_0";
            match_attr = "sample_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
        }
    }
    #endif

HDF框架定了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:

    typedef enum {
        /* 驱动不提供服务 */
        SERVICE_POLICY_NONE = 0,
        /* 驱动对内核态发布服务 */
        SERVICE_POLICY_PUBLIC = 1,
        /* 驱动对内核态和用户态都发布服务 */
        SERVICE_POLICY_CAPACITY = 2,
        /* 驱动服务不对外发布服务,但可以被订阅 */
        SERVICE_POLICY_FRIENDLY = 3,
        /* 驱动私有服务不对外发布服务,也不能被订阅 */
        SERVICE_POLICY_PRIVATE = 4,
        /* 错误的服务策略 */
        SERVICE_POLICY_INVALID
    } 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

    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include "hdf_log.h"
    #include "hdf_base.h"
    #include "hdf_device_desc.h"

    #define HDF_LOG_TAG sample_driver

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

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

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

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

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

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

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

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

    驱动服务接口的实现
    int32_t SampleDriverServiceA(void)
    {
        // 驱动开发者实现业务逻辑
        return 0;
    }

    int32_t SampleDriverServiceB(uint32_t inputCode)
    {
        // 驱动开发者实现业务逻辑
        return 0;
    }

    int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
    {
        if (deviceObject == NULL) {
            return HDF_FAILURE;
        }
        static struct ISampleDriverService testService = {
            .ServiceA = SampleDriverServiceA,
            .ServiceB = SampleDriverServiceB,
        };
        deviceObject->service = &testService.ioService;
        return HDF_SUCCESS;
    }
    #endif

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

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

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

2.2 创建Kconfig


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

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

    config DRIVERS_HDF_PLATFORM_HDF_SAMPLE
        bool "Enable HDF platform HDF sample driver"
        default n
        depends on DRIVERS_HDF_PLATFORM
        help
          Answer Y to enable HDF platform HDF sample driver.

修改上层Kconfig
$ vi ~/harmony/sdk/vendor/huawei/hdf/Kconfig

    source "../../vendor/huawei/hdf/display/driver/Kconfig"
    +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

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

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

    MODULE_NAME := hdf_sample

    LOCAL_CFLAGS += $(HDF_INCLUDE)

    LOCAL_SRCS += hdf_sample.c

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

    include $(HDF_DRIVER)

2.4 将驱动模块加入编译


$ vi ~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk

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

    +LITEOS_BASELIB += -lhdf_sample
    +LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/hdf-sample

LITEOS_BASELIB中的hdf_sample为Makefile里的MODULE_NAME名字。

 

2.5 Makefile格式说明


驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。

    include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk #导入hdf预定义内容,必需
    MODULE_NAME :=    #生成的结果文件
    LOCAL_INCLUDE :=  #本驱动的头文件目录
    LOCAL_SRCS :=     #本驱动的源代码文件
    LOCAL_CFLAGS :=  #自定义的编译选项
    include $(HDF_DRIVER) #导入模板makefile完成编译

编译结果文件链接到内核镜像,添加到vendor目录下的hdf_vendor.mk里面,示例如下:

    LITEOS_BASELIB +=  -lxxx  #链接生成的静态库
    LIB_SUBDIRS    +=         #驱动代码Makefile的目录

2.6 驱动编译


2.6.1 编译报错问题

 

1:kernel deconfig报错,需要修改。
$ python build.py ipcamera_hi3516dv300 -b debug

    ...
    clean hi3516dv300 finish
    make[1]: Entering directory '/home/ubuntu/harmony/sdk/kernel/liteos_a'
    /home/ubuntu/harmony/sdk/kernel/liteos_a/tools/menuconfig/conf --silentoldconfig /home/ubuntu/harmony/sdk/kernel/liteos_a/Kconfig
    *
    * Restart config...
    *
    *
    * Driver
    *
    Enable Driver (DRIVERS) [Y/n/?] y
      Enable USB (DRIVERS_USB) [Y/n/?] y
        Enable USB HCD (DRIVERS_USB_HOST_DRIVER) [Y/n/?] y
          USB HCD
            1. Enable EHCI HCD (USB 2.0) (DRIVERS_USB_HOST_EHCI)
          > 2. Enable XHCI HCD (USB 3.0) (DRIVERS_USB_HOST_XHCI)
          choice[1-2?]: 2
    Enable USB Device Class Drivers (DRIVERS_USB_DEVICE_CLASS_DRIVERS) [Y/n/?] y
    Enable HDF manager (DRIVERS_HDF) [Y/n/?] y
      Enable HDF platform driver (DRIVERS_HDF_PLATFORM) [Y/n/?] y
        Enable HDF platform i2c driver (DRIVERS_HDF_PLATFORM_I2C) [Y/n/?] y
        Enable HDF platform spi driver (DRIVERS_HDF_PLATFORM_SPI) [Y/n/?] y
        Enable HDF platform gpio driver (DRIVERS_HDF_PLATFORM_GPIO) [Y/n/?] y
        Enable HDF platform watchdog driver (DRIVERS_HDF_PLATFORM_WATCHDOG) [Y/n/?] y
        Enable HDF platform sdio driver (DRIVERS_HDF_PLATFORM_SDIO) [Y/n/?] y
        Enable HDF platform rtc driver (DRIVERS_HDF_PLATFORM_RTC) [Y/n/?] y
        Enable HDF hisi sdk driver (DRIVERS_HDF_PLATFORM_HISI_SDK) [Y/n/?] y
      Enable HDF WiFi Host driver (DRIVERS_HDF_WIFI) [Y/n/?] y
        Enable Hi3881 Host driver (DRIVERS_HI3881) [Y/n/?] y
      Enable HDF input driver (DRIVERS_HDF_INPUT) [Y/n/?] y
        Enable HDF tp 5P5 gt911 driver (DRIVERS_HDF_TP_5P5_GT911) [Y/n/?] y
      Enable HDF Lcd driver (DRIVERS_HDF_LCD) [Y/n/?] y
        Enable HDF Lcd Icn9700 driver (DRIVERS_HDF_LCD_ICN9700) [Y/n/?] y
    Enable HDF platform HDF sample driver (DRIVERS_HDF_PLATFORM_HDF_SAMPLE) [N/y/?] (NEW) aborted!

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

    make[1]: *** [Makefile:129: genconfig] Error 1

处理:
通过搜索定位DRIVERS_HDF_LCD_ICN9700 的配置,然后参考其用法。
$ grep DRIVERS_HDF_LCD_ICN9700 . -nr

    ./kernel/liteos_a/.config:159:LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
    ./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

    LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
    +LOSCFG_DRIVERS_HDF_PLATFORM_HDF_SAMPLE=y
    LOSCFG_DRIVERS_HDF_USB=y

让修改生效:
$ cd ~/harmony/sdk/kernel/liteos_a/
$ ./build.sh hi3516dv300 clang debug

    sh param:hi3516dv300,clang,debug

然后重新编译,该问题解决通过。

 

2.6.2 编译

 

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

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

            .Dispatch = HdfSampleDriverDispatch,

处理:
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/hdf_sample.c

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

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

再次编译,该问题解决通过。
查看驱动模块hdf_sample存放位置:
~/harmony/sdk$ find -name hdf_sample

./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

    #include <fcntl.h>
    #include <unistd.h>
    #include "hdf_log.h"
    #include "hdf_sbuf.h"
    #include "hdf_io_service_if.h"

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

    #define SAMPLE_WRITE_READ 123
    #define SELECT_DEFAULT 0

    int g_replyFlag = 0;

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

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

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

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

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

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

        #if SELECT_DEFAULT
        // 默认将sbuf中获取到的int数据放到用户空间变量中。
        int replyData = 0;
        if (!HdfSbufReadInt32(reply, &replyData)) {
            HDF_LOGE("fail to get service call reply");
            ret = HDF_ERR_INVALID_OBJECT;
            goto out;
        }
        HDF_LOGE("Get reply is: %d", replyData);
        #else
        // 将sbuf中获取到的字符串数据放到用户空间变量中。
        const char *replyData = HdfSbufReadString(reply);
        if (replyData == NULL) {
            HDF_LOGE("fail to get service call reply");
            ret = HDF_ERR_INVALID_OBJECT;
            goto out;
        }
        HDF_LOGE("Get reply is: %s", replyData);
        #endif
    out:
        HdfSBufRecycle(data); // 释放用于数据中转的sbuf
        HdfSBufRecycle(reply); // 释放用于数据中转的sbuf
        return ret;
    }

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

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

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

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

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

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

        /* wait for event receive event finishing */
        while (g_replyFlag == 0) {
            sleep(1);
        }

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

        // 释放驱动服务。
        HdfIoServiceRecycle(serv);
        return HDF_SUCCESS;
    }

说明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项: 

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

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

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

    HDF_FRAMEWORKS = "//drivers/hdf/frameworks"

    executable("hdf_sample_app") {
        sources = [
            "hdf_sample_app.c"
        ]

        include_dirs = [
            "$HDF_FRAMEWORKS/ability/sbuf/include",
            "$HDF_FRAMEWORKS/core/shared/include",
            "$HDF_FRAMEWORKS/core/host/include",
            "$HDF_FRAMEWORKS/core/master/include",
            "$HDF_FRAMEWORKS/include/core",
            "$HDF_FRAMEWORKS/include/utils",
            "$HDF_FRAMEWORKS/utils/include",
            "$HDF_FRAMEWORKS/include/osal",
            "//third_party/bounds_checking_function/include",
        ]

        deps = [
            "//drivers/hdf/lite/manager:hdf_core",
            "//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal",
        ]

        public_deps = [
            "//third_party/bounds_checking_function:libsec_shared",
        ]
        defines = [
            "__USER__",
        ]

        cflags = [
            "-Wall",
            "-Wextra",
        ]
    }

注意,里面的对齐不能使用table键,否则编译会报错。

 

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

    {
      "subsystem": [
        {
          "name": "hdf",
          "component": [
            { "name": "posix", "dir": "//drivers/hdf/lite/posix:hdf_posix", "features":[] },
            { "name": "manager", "dir": "//drivers/hdf/lite/manager:hdf_manager", "features":[] },
            { "name": "wifi", "dir": "//vendor/huawei/hdf/wifi:wifi_firmware", "features":[] },
            { "name": "display", "dir": "//vendor/huawei/hdf/display/hdi:hdi_display", "features":[] },
            { "name": "input", "dir": "//vendor/huawei/hdf/input/hdi:hdi_input", "features":[] },
            { "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "features":[] },
    +        { "name": "hdf_sample_app", "dir": "//vendor/huawei/hdf/sample/platform/hdf-sample/app:hdf_sample_app", "features":[] }
          ]
        },
      ]
    }

 

3.4 应用编译


$ python build.py ipcamera_hi3516dv300 -b debug
$ vi out/ipcamera_hi3516dv300/build.log

    Done. Made 251 targets from 160 files in 907ms
    ninja: Entering directory `/home/ubuntu/harmony/sdk/out/ipcamera_hi3516dv300'
    [1/1346] STAMP obj/applications/sample/camera/communication/sample.stamp
    ...
    // 新增的hdf_sample_app
    [1163/1346] clang obj/vendor/huawei/hdf/sample/platform/hdf-sample/app/hdf_sample_app.o
    [1167/1346] LLVM LINK ./bin/hdf_sample_app
    // 可供参考的hello_uart
    [1160/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dev/hello_uart_dev.o
    [1161/1346] LLVM LINK ./bin/hello_uart
    [1169/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/uart_if.o
    [1170/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/hello_uart_dispatch.o
    [1171/1346] LLVM LINK ./bin/hello_uart_dispatch
    [1172/1346] STAMP obj/vendor/huawei/hdf/sample/platform/uart/hello_uart_sample.stamp
    ...
    [1344/1346] STAMP obj/build/lite/ohos.stamp
    [1345/1346] ACTION //build/lite:gen_rootfs(//build/lite/toolchain:linux_x86_64_clang)
    [1346/1346] STAMP obj/build/lite/gen_rootfs.stamp
    ohos ipcamera_hi3516dv300 build success!

通过上面的编译日志可以找到hdf_sample_app应用放在了/bin目录:

    ./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的文件位置

#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理-鸿蒙开发者社区

 操作流程如下:

#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理-鸿蒙开发者社区

 修改一下长度:

#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理-鸿蒙开发者社区

点击“烧写”按键后,控制台会提示重启目标板,重启后,系统就自动进入烧写了。
单板初次启动需要修改启动参数,重新上电后登陆会进入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程序,显示成功结果如下所示:

    OHOS # ./bin/hdf_sample_app
    // 用户空间给内核驱动发送数据
    [HDF:E/"sample_test"]user space send:
    [HDF:E/"sample_test"]cmd: 123, send data is: come from user sapce: default event info

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

    // 默认:用户空间回调函数OnDevEventReceived的打印信息
    [HDF:E/"sample_test"]user space received:
    [HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info
    [HDF:E/"sample_test"]Get reply is: 2147483647
    [HDF:E/hdf_syscall_adapter]event listener task exit
    // 修改后:
    [HDF:E/"sample_test"]user space received:
    [HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info // 这了尝试打印出“change default event info”,但修改无效
    [HDF:E/"sample_test"]Get reply is: come from kernel sapce: default event info
    [HDF:E/hdf_syscall_adapter]event listener task exit

打印结果的截图:

#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理-鸿蒙开发者社区

6. 吐槽:


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

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

 

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

分类
已于2021-1-7 17:38:16修改
收藏 2
回复
举报
回复
    相关推荐