基本结构

HCS主要分为属性(Attribute)和节点(Node)两种结构。

属性

属性即最小的配置单元,是一个独立的配置项。语法如下:

  attribute_name = value;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

节点

节点是一组属性的集合,语法如下:

  node_name {
      module = "sample";
      ...
  }
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
数据类型

在属性定义中使用自动数据类型,不显式指定类型,属性支持的数据类型如下:

整型

整型长度自动推断,根据实际数据长度给与最小空间占用的类型。

字符串

字符串使用双引号(“”)表示。

数组

数组元素支持整型、字符串,不支持混合类型。整型数组中uint32_t uint64_t混用会向上转型为uint64_t数组。整型数组与字符串数组示例如下:

attr_foo = [0x01, 0x02, 0x03, 0x04];
attr_bar = ["hello", "world"];
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

bool类型

bool类型中true表示真,false表示假。

预处理

include

用于导入其他HCS文件。语法示例如下:

#include "foo.hcs"
#include "../bar.hcs"
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
注释

支持两种注释风格。

    // comment
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
    /*
    comment
    */
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

说明: 多行注释不支持嵌套。

引用修改

引用修改的作用是在当前节点中修改另外任意一个节点的内容,语法为:

 node :& source_node
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

上述语句表示node中的内容是对source_node节点内容的修改。示例如下:

root {
    module = "sample";
    foo {
        foo_ :& root.bar{
            attr = "foo";
        }
        foo1 :& foo2 {
            attr = 0x2;
        }
        foo2 {
            attr = 0x1;
        }
    }

    bar {
        attr = "bar";
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

最终生成配置树为:

root {
    module = "sample";
    foo {
        foo2 {
            attr = 0x2;
        }
    }
    bar {
        attr = "foo";
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在以上示例中,可以看到foo.foo_节点通过引用将bar.attr属性的值修改为了"foo",foo.foo1节点通过引用将foo.foo2.attr属性的值修改为了0x2。foo.foo_以及foo.foo1节点表示对目标节点内容的修改,其自身并不会存在最终生成的配置树中。

节点复制

节点复制可以实现在节点定义时从另一个节点先复制内容,用于定义内容相似的节点。语法为:

 node : source_node
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

上述语句表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。示例如下:

root {
    module = "sample";
    foo {
        attr_0 = 0x0;
    }
    bar:foo {
        attr_1 = 0x1;
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

上述代码的最终生成配置树为:

root {
    module = "sample";
    foo {
        attr_0 = 0x0;
    }
    bar {
        attr_1 = 0x1;
        attr_0 = 0x0;
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在上述示例中,编译后bar节点既包含attr_0属性又包含attr_1属性,在bar中对attr_0的修改不会影响到foo。

当foo和bar在同级node中时可不指定foo的路径,否则需要使用绝对路径引用,绝对路径的介绍请参考 引用修改 。

删除

要对include导入的base配置树中不需要的节点或属性进行删除,可以使用delete关键字。下面的举例中sample1.hcs通过include导入了sample2.hcs中的配置内容,并使用delete删除了sample2.hcs中的attribute2属性和foo_2节点,示例如下:

// sample2.hcs
root {
    attr_1 = 0x1;
    attr_2 = 0x2;
    foo_2 {
        t = 0x1;
    }
}

// sample1.hcs
#include "sample2.hcs"
root {
    attr_2 = delete;
    foo_2 : delete {
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

上述代码在生成过程中将会删除root.foo_2节点与attr_2,最终生成配置树为:

root {
    attr_1 = 0x1;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

说明: 在同一个HCS文件中不允许使用delete,建议直接删除不需要的属性。

属性引用

为了在解析配置时快速定位到关联的节点,可以把节点作为属性的右值,通过读取属性查找到对应节点。语法为:

 attribute = &node;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

上述语句表示attribute的值是一个节点node的引用,在解析时可以用这个attribute快速定位到node,便于关联和查询其他node。示例如下:

node1 {
    attributes;
}
node2 {
    attr_1 = &root.node1;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

node2 {
    node1 {
        attributes;
    }
    attr_1 = &node1;
}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
模板

模板的用途在于生成严格一致的node结构,以便对同类型node进行遍历和管理。使用template关键字定义模板node,子node通过双冒号“::”声明继承关系。子节点可以改写或新增但不能删除template中的属性,子节点中没有定义的属性将使用template中的定义作为默认值。示例如下:

root {
    module = "sample";
    template foo {
        attr_1 = 0x1;
        attr_2 = 0x2;
    }

    bar :: foo {
    }

    bar_1 :: foo {
        attr_1 = 0x2;
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

生成配置树如下:

root {
    module = "sample";
    bar {
        attr_1 = 0x1;
        attr_2 = 0x2;
    }
    bar_1 {
        attr_1 = 0x2;
        attr_2 = 0x2;
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在上述示例中,bar和bar_1节点继承了foo节点,生成配置树节点结构与foo保持了完全一致,只是属性的值不同。

配置生成

hc-gen是配置生成的工具,可以对HCS配置语法进行检查并把HCS源文件转化成HCB二进制文件。

hc-gen介绍

hc-gen参数说明:

Usage: hc-gen [Options] [File]
options:
  -o    output file name, default same as input
  -a          hcb align with four bytes
  -b          output binary output, default enable
  -t          output config in C language source file style
  -m          output config in macro source file style
  -i          output binary hex dump in C language source file style
  -p  prefix of generated symbol name
  -d          decompile hcb to hcs
  -V          show verbose info
  -v          show version
  -h          show this help message
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

生成.c/.h配置文件方法:

hc-gen -o [OutputCFileName] -t [SourceHcsFileName]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

生成HCB配置文件方法:

hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

生成宏定义配置文件方法:

hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

反编译HCB文件为HCS方法:

hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

开发指导

场景介绍

关于驱动的开发我们主要目的是实现驱动代码的编写,但是驱动开发过程中需要服务管理、消息机制管理,才能使驱动在代码编译过程中进行加载。以下开发步骤中介绍了驱动开发、驱动消息机制管理开发、驱动服务管理开发的步骤。

驱动开发实例

基于HDF框架的驱动开发主要分为三个部分:驱动实现、驱动编译脚本编写和驱动配置。详细开发流程如下所示:

驱动实现

驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下:

    #include "hdf_device_desc.h"          // HDF框架对驱动开发相关能力接口的头文件
    #include "hdf_log.h"                  // HDF框架提供的日志接口头文件

    #define HDF_LOG_TAG sample_driver     // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。

    // 将驱动对外提供的服务能力接口绑定到HDF框架。
    int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
    {
        HDF_LOGD("Sample driver bind success");
        return HDF_SUCCESS;
    }

    // 驱动自身业务初始化的接口
    int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
    {
        HDF_LOGD("Sample driver Init success");
        return HDF_SUCCESS;
    }

    // 驱动资源释放的接口
    void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
    {
        HDF_LOGD("Sample driver release success");
        return;
    }
    c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
    // 定义驱动入口的对象,必须为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);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
驱动编译脚本编写
        include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # 【必需】导入hdf预定义内容
        MODULE_NAME :=        #生成的结果文件
        LOCAL_INCLUDE :=      #本驱动的头文件目录
        LOCAL_SRCS :=         #本驱动的源代码文件
        LOCAL_CFLAGS :=      #自定义的编译选项
        include $(HDF_DRIVER) #导入Makefile模板完成编译
        makefile
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

编译结果文件链接到内核镜像,添加到drivers/hdf_core/adapter/khdf/liteos目录下的hdf_lite.mk里面,示例如下:

        LITEOS_BASELIB +=  -lxxx  #链接生成的静态库
        LIB_SUBDIRS    +=         #驱动代码Makefile的目录
        makefile
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
       import("//build/lite/config/component/lite_component.gni")
       import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni")
       module_switch = defined(LOSCFG_DRIVERS_HDF_xxx)
       module_name = "xxx"
       hdf_driver(module_name) {
           sources = [
               "xxx/xxx/xxx.c",           #模块要编译的源码文件
           ]
           public_configs = [ ":public" ] #使用依赖的头文件配置
       }
       config("public") {                 #定义依赖的头文件配置
           include_dirs = [
               "xxx/xxx/xxx",             #依赖的头文件目录
           ]
       }
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

把新增模块的BUILD.gn所在的目录添加到**/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**里面:

        group("liteos") {
            public_deps = [ ":$module_name" ]
            deps = [
                "xxx/xxx",#新增模块BUILD.gn所在的目录,/drivers/hdf_core/adapter/khdf/liteos
            ]
        }
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

如果需要定义模块控制宏,需要在模块目录xxx里面添加Kconfig文件,并把Kconfig文件路径添加到drivers/hdf_core/adapter/khdf/linux/Kconfig里面:

    source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

添加模块目录到drivers/hdf_core/adapter/khdf/linux/Makefile

    obj-$(CONFIG_DRIVERS_HDF)  += xxx/
    makefile
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

在模块目录xxx里面添加Makefile文件,在Makefile文件里面添加模块代码编译规则:

    obj-y  += xxx.o
    makefile
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
驱动配置

HDF使用HCS作为配置描述源码,HCS详细介绍配置管理。

驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下:

    root {
        device_info {
            match_attr = "hdf_manager";
            template host {       // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省。
                hostName = "";
                priority = 100;
                uid = "";         // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。
                gid = "";         // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。
                caps = [""];      // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。
                template device {
                    template deviceNode {
                        policy = 0;
                        priority = 100;
                        preload = 0;
                        permission = 0664;
                        moduleName = "";
                        serviceName = "";
                        deviceMatchAttr = "";
                    }
                }
            }
            sample_host :: host{
                hostName = "host0";    // host名称,host节点是用来存放某一类驱动的容器。
                priority = 100;        // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。
                caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];   // 用户态进程Linux capabilities配置。
                device_sample :: device {        // sample设备节点
                    device0 :: deviceNode {      // sample驱动的DeviceNode节点
                        policy = 1;              // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。
                        priority = 100;          // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。
                        preload = 0;             // 驱动按需加载字段。
                        permission = 0664;       // 驱动创建设备节点权限
                        moduleName = "sample_driver";      // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
                        serviceName = "sample_service";    // 驱动对外发布服务的名称,必须唯一。
                        deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
                    }
                }
            }
        }
    }
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

说明:

如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息。HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init(参考 驱动实现 )传递给驱动。驱动的配置信息示例如下:

    root {
        SampleDriverConfig {
            sample_version = 1;
            sample_bus = "I2C_0";
            match_attr = "sample_config";   // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
        }
    }
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs,示例如下:

    #include "device_info/device_info.hcs"
    #include "sample/sample_config.hcs"
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

驱动消息机制管理开发

驱动服务管理的开发包括驱动服务的编写、绑定、获取或者订阅,详细步骤如下。

驱动服务编写
// 驱动服务结构的定义
struct ISampleDriverService {
    struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员。
    int32_t (*ServiceA)(void);               // 驱动的第一个服务接口。
    int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加。
};

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

int32_t SampleDriverServiceB(uint32_t inputCode)
{
    // 驱动开发者实现业务逻辑
    return HDF_SUCCESS;
}
c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
驱动服务绑定

开发者实现HdfDriverEntry中的Bind指针函数,如下的SampleDriverBind,把驱动服务绑定到HDF框架中。

int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{
    // deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
    if (deviceObject == NULL) {
        HDF_LOGE("Sample device object is null!");
        return HDF_FAILURE;
    }
    static struct ISampleDriverService sampleDriverA = {
        .ServiceA = SampleDriverServiceA,
        .ServiceB = SampleDriverServiceB,
    };
    deviceObject->service = &sampleDriverA.ioService;
    return HDF_SUCCESS;
}
c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
驱动服务获取

应用程序开发者获取驱动服务有两种方式:通过HDF接口直接获取和通过HDF提供的订阅机制获取。

通过HDF接口直接获取

当驱动服务获取者明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:

const struct ISampleDriverService *sampleService =
        (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
if (sampleService == NULL) {
    return HDF_FAILURE;
}
sampleService->ServiceA();
sampleService->ServiceB(5);
c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
通过HDF提供的订阅机制获取

当内核态驱动服务获取者对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动服务。当该驱动加载完成时,HDF框架会将被订阅的驱动服务发布给订阅者(驱动服务获取者),实现方式如下所示:

// 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用。
// object为订阅者的私有数据,service为被订阅的服务对象。
int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
{
    const struct ISampleDriverService *sampleService =
        (const struct ISampleDriverService *)service;
    if (sampleService == NULL) {
        return HDF_FAILURE;
    }
    sampleService->ServiceA();
    sampleService->ServiceB(5);
}
// 订阅过程的实现
int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("Test driver init failed, deviceObject is null!");
        return HDF_FAILURE;
    }
    struct SubscriberCallback callBack;
    callBack.deviceObject = deviceObject;
    callBack.OnServiceConnected = TestDriverSubCallBack;
    int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("Test driver subscribe sample driver failed!");
    }
    return ret;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

HDF开发实例

下面基于HDF框架,提供一个完整的样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。

添加配置

在HDF框架的配置文件(例如vendor/hisilicon/xxx/hdf_config/device_info)中添加该驱动的配置信息,如下所示:

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";
            sample_device :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 100;
                    preload = 1;
                    permission = 0664;
                    moduleName = "sample_driver";
                    serviceName = "sample_service";
                }
            }
        }
    }
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

编写驱动代码

基于HDF框架编写的sample驱动代码如下:

#include 
#include 
#include 
#include "hdf_log.h"
#include "hdf_base.h"
#include "hdf_device_desc.h"

#define HDF_LOG_TAG sample_driver

#define SAMPLE_WRITE_READ 123

static int32_t HdfSampleDriverDispatch(
    struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id);
    if (id == SAMPLE_WRITE_READ) {
        const char *readData = HdfSbufReadString(data);
        if (readData != NULL) {
            HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData);
        }
        if (!HdfSbufWriteInt32(reply, INT32_MAX)) {
            HDF_LOGE("%{public}s: reply int32 fail", __func__);
        }
        return HdfDeviceSendEvent(client->device, id, data);
    }
    return HDF_FAILURE;
}

static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
    // release resources here
    return;
}

static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        return HDF_FAILURE;
    }
    static struct IDeviceIoService testService = {
        .Dispatch = HdfSampleDriverDispatch,
    };
    deviceObject->service = &testService;
    return HDF_SUCCESS;
}

static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("%{public}s::ptr is null!", __func__);
        return HDF_FAILURE;
    }
    HDF_LOGI("Sample driver Init success");
    return HDF_SUCCESS;
}

static struct HdfDriverEntry g_sampleDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "sample_driver",
    .Bind = HdfSampleDriverBind,
    .Init = HdfSampleDriverInit,
    .Release = HdfSampleDriverRelease,
};

HDF_INIT(g_sampleDriverEntry);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

编写用户程序和驱动交互代码

基于HDF框架编写的用户态程序和驱动交互的代码如下(代码可以放在目录drivers/hdf_core/adapter/uhdf下面编译,BUILD.gn可以参考drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn):

#include 
#include 
#include 
#include 
#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

int g_replyFlag = 0;

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_LOGI("%{public}s: dev event received: %{public}u %{public}s",  (char *)priv, id, string);
    g_replyFlag = 1;
    return HDF_SUCCESS;
}

static int SendEvent(struct HdfIoService *serv, char *eventData)
{
    int ret = 0;
    struct HdfSBuf *data = HdfSbufObtainDefaultSize();
    if (data == NULL) {
        HDF_LOGE("fail to obtain sbuf data");
        return 1;
    }

    struct HdfSBuf *reply = HdfSbufObtainDefaultSize();
    if (reply == NULL) {
        HDF_LOGE("fail to obtain sbuf reply");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }

    if (!HdfSbufWriteString(data, eventData)) {
        HDF_LOGE("fail to write sbuf");
        ret = HDF_FAILURE;
        goto out;
    }

    ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("fail to send service call");
        goto out;
    }

    int replyData = 0;
    if (!HdfSbufReadInt32(reply, &replyData)) {
        HDF_LOGE("fail to get service call reply");
        ret = HDF_ERR_INVALID_OBJECT;
        goto out;
    }
    HDF_LOGI("Get reply is: %{public}d", replyData);
out:
    HdfSbufRecycle(data);
    HdfSbufRecycle(reply);
    return ret;
}

int main()
{
    char *sendData = "default event info";
    struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
    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 (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
        HDF_LOGE("fail to register event listener");
        return HDF_FAILURE;
    }
    if (SendEvent(serv, sendData)) {
        HDF_LOGE("fail to send event");
        return HDF_FAILURE;
    }

    while (g_replyFlag == 0) {
        sleep(1);
    }

    if (HdfDeviceUnregisterEventListener(serv, &listener)) {
        HDF_LOGE("fail to  unregister listener");
        return HDF_FAILURE;
    }

    HdfIoServiceRecycle(serv);
    return HDF_SUCCESS;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

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

deps = [

  ​ “//drivers/hdf_core/adapter/uhdf/manager:hdf_core”,

​   “//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal”,

  ]
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

如果大家想更加深入的学习 OpenHarmony(鸿蒙南向) 开发的全栈内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

系统架构分析:https://qr18.cn/CgxrRy

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/140992351","extend1":"pc","ab":"new"}">> id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension"> class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}"> class="blog_extension_card_left"> class="blog_extension_card_cont"> 鸿蒙开发学习资料领取!!! class="blog_extension_card_cont_r"> 微信名片
注:本文转载自blog.csdn.net的沧海一笑-dj的文章"https://blog.csdn.net/dengjin20104042056/article/details/99292814"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!