Harmony鸿蒙南向驱动开发流程

宁睿  金牌会员 | 2024-8-12 00:28:49 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 876|帖子 876|积分 2638

概述

HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包罗驱动加载、驱动服务管理、驱动消息机制和设置管理。并以组件化驱动模型作为核心设计思路,让驱动开发和部署更加规范,旨在构建同一的驱动架构平台,为驱动开发者提供更精准、更高效的驱动管理的开发情况,力图做到一次开发,多体系部署。
驱动加载

HDF驱动框架提供把和设置的设备列表匹配成功的驱动程序加载起来的功能。
驱动服务管理

HDF框架可以会合管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。
驱动消息机制

HDF框架提供同一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。
设置管理

HCS(HDF Configuration Source)是HDF驱动框架的设置描述源码,内容以Key-Value为重要形式。它实现了设置代码与驱动代码解耦,便于开发者举行设置管理。
驱动模型

HDF框架将一类设备驱动放在同一个Host(设备容器)内里,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在差别的Host,重要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host内里,否则部署到独立的Host中是更好的选择。Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。HDF驱动模型如下图所示:
图1 HDF驱动模型


功能描述

驱动加载

HDF驱动框架提供把和设置的设备列表匹配成功的驱动程序加载起来的功能,支持按需加载和按序加载两种战略,具体设备的加载战略由设置文件中的preload字段来控制,设置值参考如下:
  1. typedef enum {
  2.     DEVICE_PRELOAD_ENABLE = 0,
  3.     DEVICE_PRELOAD_ENABLE_STEP2 = 1,
  4.     DEVICE_PRELOAD_DISABLE = 2,
  5.     DEVICE_PRELOAD_INVALID
  6. } DevicePreload;
复制代码
按需加载



  • preload字段设置为0(DEVICE_PRELOAD_ENABLE),则体系启动过程中默认加载。
  • preload字段设置为1(DEVICE_PRELOAD_ENABLE_STEP2),当体系支持快速启动的时间,则在体系完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同。
  • preload字段设置为2(DEVICE_PRELOAD_DISABLE),则体系启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务消息机制时,如果驱动服务不存在,HDF框架会实验动态加载该驱动。
按序加载(默认加载战略)

设置文件中的priority(取值范围为整数0到200)是用来表示host(驱动容器)和驱动的优先级的。差别的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。
非常规复(用户态驱动)

当驱动服务非常退出时,规复战略如下:


  • preload字段设置为0(DEVICE_PRELOAD_ENABLE)或1(DEVICE_PRELOAD_ENABLE_STEP2)的驱动服务,由启动模块拉起host并重新加载服务。
  • preload字段设置为2(DEVICE_PRELOAD_DISABLE)的驱动服务,需业务模块注册HDF的服务状态监听器,当收到服务退出消息时,业务模块调用LoadDevice重新加载服务。
驱动服务管理

驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架同一管理。驱动服务管理重要包含驱动服务的发布和获取。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;
复制代码
使用场景

当驱动必要以接口的形式对外提供能力时,可以使用HDF框架的驱动服务管理能力。
接口阐明

针对驱动服务管理功能,HDF框架开放了以下接口供开发者调用,如下表所示:
表1 服务管理接口
方法描述int32_t (*Bind)(struct HdfDeviceObject *deviceObject)必要驱动开发者实现Bind函数,将自己的服务接口绑定到HDF框架中。const struct HdfObject *DevSvcManagerClntGetService(const char *svcName)获取驱动的服务。int HdfDeviceSubscribeService( struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback)订阅驱动的服务。 驱动消息机制管理

使用场景

当用户态应用和内核态驱动必要交互时,可以使用HDF框架的消息机制来实现。
接口阐明

消息机制的功能重要有以下两种:


  • 用户态应用发送消息到驱动。
  • 用户态应用吸收驱动主动上报事件。
表2 消息机制接口
方法描述struct HdfIoService *HdfIoServiceBind(const char *serviceName);用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。void HdfIoServiceRecycle(struct HdfIoService *service);释放驱动服务。int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener);用户态程序注册吸收驱动上报事件的利用方法。int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data)驱动主动上报事件接口。 设置管理

设置概述

HCS(HDF Configuration Source)是HDF驱动框架的设置描述源码,内容以Key-Value为重要形式。它实现了设置代码与驱动代码解耦,便于开发者举行设置管理。HC-GEN(HDF Configuration Generator)是HCS设置转换工具,可以将HDF设置文件转换为软件可读取的文件格式:


  • 在弱性能情况中,转换为设置树源码或设置树宏定义,驱动可直接调用C代码或宏式APIs获取设置。
  • 在高性能情况中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的设置剖析接口获取设置。
以下是使用HCB模式的典范应用场景:
图2 设置使用流程图


HCS颠末HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重修设置树,HDF驱动模块使用HCS Parser提供的设置读取接口获取设置内容。
设置语法

HCS的语法介绍如下:
关键字

HCS设置语法保留了以下关键字。
表3 HCS设置语法保留关键字
关键字用途阐明root设置根节点-include引用其他HCS设置文件-delete删除节点或属性只能用于利用include导入的设置树template定义模板节点-match_attr用于标志节点的匹配查找属性剖析设置时可以使用该属性的值查找到对应节点 基本结构

HCS重要分为属性(Attribute)和节点(Node)两种结构。
属性
属性即最小的设置单元,是一个独立的设置项。语法如下:
  1.   attribute_name = value;
复制代码


  • attribute_name是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。
  • value的可用格式如下:

    • 数字常量,支持二进制、八进制、十进制、十六进制数,具体数据范例章节。
    • 字符串,内容使用双引号("")引用。
    • 节点引用。

  • attribute必须以分号(;)竣事且必须属于一个node。
节点
节点是一组属性的集合,语法如下:
  1.   node_name {
  2.       module = "sample";
  3.       ...
  4.   }
复制代码


  • node_name是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。
  • 大括号后无需添加竣事符“;”。
  • root为保留关键字,用于声明设置表的根节点。每个设置表必须以root节点开始。
  • root节点中必须包含module属性,其值应该为一个字符串,用于表征该设置所属模块。
  • 节点中可以增长match_attr属性,其值为一个全局唯一的字符串。当驱动程序在剖析设置时可以以该属性的值为参数调用查找接口查找到包含该属性的节点。
数据范例

在属性定义中使用主动数据范例,不显式指定范例,属性支持的数据范例如下:
整型
整型长度主动推断,根据现实数据长度给与最小空间占用的范例。


  • 二进制,0b前缀,示例:0b1010。
  • 八进制,0前缀,示例:0664。
  • 十进制 ,无前缀,且支持有符号与无符号,示例:1024,+1024均合法。驱动程序在读取负值时注意使用有符号数读取接口。
  • 十六进制,0x前缀,示例:0xff00、0xFF。
字符串
字符串使用双引号("")表示。
数组
数组元素支持整型、字符串,不支持混淆范例。整型数组中uint32_t uint64_t混用会向上转型为uint64_t数组。整型数组与字符串数组示例如下:
  1. attr_foo = [0x01, 0x02, 0x03, 0x04];
  2. attr_bar = ["hello", "world"];
复制代码
bool范例
bool范例中true表示真,false表示假。
预处理

include
用于导入其他HCS文件。语法示例如下:
  1. #include "foo.hcs"
  2. #include "../bar.hcs"
复制代码


  • 文件名必须使用双引号(""),不在同一目录使用相对路径引用。被include文件也必须是合法的HCS文件。
  • 多个include,如果存在相同的节点,后者覆盖前者,其余的节点依次展开。
解释

支持两种解释风格。


  • 单行解释。
    1. // comment
    复制代码
  • 多行解释。
    1. /*
    2. comment
    3. */
    复制代码
          阐明: 多行解释不支持嵌套。
引用修改

引用修改的作用是在当前节点中修改另外恣意一个节点的内容,语法为:
  1. node :& source_node
复制代码
上述语句表示node中的内容是对source_node节点内容的修改。示例如下:
  1. root {
  2.     module = "sample";
  3.     foo {
  4.         foo_ :& root.bar{
  5.             attr = "foo";
  6.         }
  7.         foo1 :& foo2 {
  8.             attr = 0x2;
  9.         }
  10.         foo2 {
  11.             attr = 0x1;
  12.         }
  13.     }
  14.     bar {
  15.         attr = "bar";
  16.     }
  17. }
复制代码
终极生成设置树为:
  1. root {
  2.     module = "sample";
  3.     foo {
  4.         foo2 {
  5.             attr = 0x2;
  6.         }
  7.     }
  8.     bar {
  9.         attr = "foo";
  10.     }
  11. }
复制代码
在以上示例中,可以看到foo.foo_节点通过引用将bar.attr属性的值修改为了"foo",foo.foo1节点通过引用将foo.foo2.attr属性的值修改为了0x2。foo.foo_以及foo.foo1节点表示对目标节点内容的修改,其自身并不会存在终极生成的设置树中。


  • 引用同级node,可以直接使用node名称,否则被引用的节点必须使用绝对路径,节点间使用“.”分隔,root表示根节点,格式为root开始的节点路径序列,例如root.foo.bar即为一个合法的绝对路径。
  • 如果出现修改冲突(即多处修改同一个属性),编译器将提示warning,因为这种情况下只会生效某一个修改而导致终极结果不确定。
节点复制

节点复制可以实现在节点定义时从另一个节点先复制内容,用于定义内容相似的节点。语法为:
  1. node : source_node
复制代码
上述语句表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。示例如下:
  1. root {
  2.     module = "sample";
  3.     foo {
  4.         attr_0 = 0x0;
  5.     }
  6.     bar:foo {
  7.         attr_1 = 0x1;
  8.     }
  9. }
复制代码
上述代码的终极生成设置树为:
  1. root {
  2.     module = "sample";
  3.     foo {
  4.         attr_0 = 0x0;
  5.     }
  6.     bar {
  7.         attr_1 = 0x1;
  8.         attr_0 = 0x0;
  9.     }
  10. }
复制代码
在上述示例中,编译后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节点,示例如下:
  1. // sample2.hcs
  2. root {
  3.     attr_1 = 0x1;
  4.     attr_2 = 0x2;
  5.     foo_2 {
  6.         t = 0x1;
  7.     }
  8. }
  9. // sample1.hcs
  10. #include "sample2.hcs"
  11. root {
  12.     attr_2 = delete;
  13.     foo_2 : delete {
  14.     }
  15. }
复制代码
上述代码在生成过程中将会删除root.foo_2节点与attr_2,终极生成设置树为:
  1. root {
  2.     attr_1 = 0x1;
  3. }
复制代码
  阐明: 在同一个HCS文件中不允许使用delete,发起直接删除不必要的属性。
  属性引用

为了在剖析设置时快速定位到关联的节点,可以把节点作为属性的右值,通过读取属性查找到对应节点。语法为:
  1. attribute = &node;
复制代码
上述语句表示attribute的值是一个节点node的引用,在剖析时可以用这个attribute快速定位到node,便于关联和查询其他node。示例如下:
  1. node1 {
  2.     attributes;
  3. }
  4. node2 {
  5.     attr_1 = &root.node1;
  6. }
复制代码

  1. node2 {
  2.     node1 {
  3.         attributes;
  4.     }
  5.     attr_1 = &node1;
  6. }
复制代码
模板

模板的用途在于生成严格同等的node结构,以便对同范例node举行遍历和管理。使用template关键字定义模板node,子node通过双冒号“::”声明继承关系。子节点可以改写或新增但不能删除template中的属性,子节点中没有定义的属性将使用template中的定义作为默认值。示例如下:
  1. root {
  2.     module = "sample";
  3.     template foo {
  4.         attr_1 = 0x1;
  5.         attr_2 = 0x2;
  6.     }
  7.     bar :: foo {
  8.     }
  9.     bar_1 :: foo {
  10.         attr_1 = 0x2;
  11.     }
  12. }
复制代码
​ 生成设置树如下:
  1. root {
  2.     module = "sample";
  3.     bar {
  4.         attr_1 = 0x1;
  5.         attr_2 = 0x2;
  6.     }
  7.     bar_1 {
  8.         attr_1 = 0x2;
  9.         attr_2 = 0x2;
  10.     }
  11. }
复制代码
在上述示例中,bar和bar_1节点继承了foo节点,生成设置树节点结构与foo保持了完全同等,只是属性的值差别。
设置生成

​ hc-gen是设置生成的工具,可以对HCS设置语法举行检查并把HCS源文件转化成HCB二进制文件。
hc-gen介绍
​ hc-gen参数阐明:
  1. Usage: hc-gen [Options] [File]
  2. options:
  3.   -o <file>   output file name, default same as input
  4.   -a          hcb align with four bytes
  5.   -b          output binary output, default enable
  6.   -t          output config in C language source file style
  7.   -m          output config in macro source file style
  8.   -i          output binary hex dump in C language source file style
  9.   -p <prefix> prefix of generated symbol name
  10.   -d          decompile hcb to hcs
  11.   -V          show verbose info
  12.   -v          show version
  13.   -h          show this help message
复制代码
生成.c/.h设置文件方法:
  1. hc-gen -o [OutputCFileName] -t [SourceHcsFileName]
复制代码
生成HCB设置文件方法:
  1. hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]
复制代码
生成宏定义设置文件方法:
  1. hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]
复制代码
反编译HCB文件为HCS方法:
  1. hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
复制代码
开发指导

场景介绍

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

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

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


  • 驱动业务代码
    1. #include "hdf_device_desc.h"          // HDF框架对驱动开发相关能力接口的头文件
    2. #include "hdf_log.h"                  // HDF框架提供的日志接口头文件
    3. #define HDF_LOG_TAG sample_driver     // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。
    4. // 将驱动对外提供的服务能力接口绑定到HDF框架。
    5. int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
    6. {
    7.     HDF_LOGD("Sample driver bind success");
    8.     return HDF_SUCCESS;
    9. }
    10. // 驱动自身业务初始化的接口
    11. int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
    12. {
    13.     HDF_LOGD("Sample driver Init success");
    14.     return HDF_SUCCESS;
    15. }
    16. // 驱动资源释放的接口
    17. void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
    18. {
    19.     HDF_LOGD("Sample driver release success");
    20.     return;
    21. }
    复制代码
  • 驱动入口注册到HDF框架
    1. // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。
    2. struct HdfDriverEntry g_sampleDriverEntry = {
    3.     .moduleVersion = 1,
    4.     .moduleName = "sample_driver",
    5.     .Bind = HdfSampleDriverBind,
    6.     .Init = HdfSampleDriverInit,
    7.     .Release = HdfSampleDriverRelease,
    8. };
    9. // 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
    10. HDF_INIT(g_sampleDriverEntry);
    复制代码
驱动编译脚本编写



  • LiteOS

    涉及Makefile和BUILD.gn修改:

    • Makefile部分:
      驱动代码的编译必须要使用HDF框架提供的Makefile模板举行编译。
      1. include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # 【必需】导入hdf预定义内容
      2. MODULE_NAME :=        #生成的结果文件
      3. LOCAL_INCLUDE :=      #本驱动的头文件目录
      4. LOCAL_SRCS :=         #本驱动的源代码文件
      5. LOCAL_CFLAGS :=      #自定义的编译选项
      6. include $(HDF_DRIVER) #导入Makefile模板完成编译
      复制代码
      编译结果文件链接到内核镜像,添加到drivers/hdf_core/adapter/khdf/liteos目录下的hdf_lite.mk内里,示例如下:
      1. LITEOS_BASELIB +=  -lxxx  #链接生成的静态库
      2. LIB_SUBDIRS    +=         #驱动代码Makefile的目录
      复制代码
    • BUILD.gn部分:
      添加模块BUILD.gn,可参考如下示例:
      1. import("//build/lite/config/component/lite_component.gni")
      2. import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni")
      3. module_switch = defined(LOSCFG_DRIVERS_HDF_xxx)
      4. module_name = "xxx"
      5. hdf_driver(module_name) {
      6.     sources = [
      7.         "xxx/xxx/xxx.c",           #模块要编译的源码文件
      8.     ]
      9.     public_configs = [ ":public" ] #使用依赖的头文件配置
      10. }
      11. config("public") {                 #定义依赖的头文件配置
      12.     include_dirs = [
      13.         "xxx/xxx/xxx",             #依赖的头文件目录
      14.     ]
      15. }
      复制代码
      把新增模块的BUILD.gn地点的目录添加到**/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**内里:
      1. group("liteos") {
      2.     public_deps = [ ":$module_name" ]
      3.     deps = [
      4.         "xxx/xxx",#新增模块BUILD.gn所在的目录,/drivers/hdf_core/adapter/khdf/liteos
      5.     ]
      6. }
      复制代码

  • Linux

    如果必要定义模块控制宏,必要在模块目录xxx内里添加Kconfig文件,并把Kconfig文件路径添加到drivers/hdf_core/adapter/khdf/linux/Kconfig内里:
    1. source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录
    复制代码
    添加模块目录到drivers/hdf_core/adapter/khdf/linux/Makefile
    1. obj-$(CONFIG_DRIVERS_HDF)  += xxx/
    复制代码
    在模块目录xxx内里添加Makefile文件,在Makefile文件内里添加模块代码编译规则:
    1. obj-y  += xxx.o
    复制代码
驱动设置

HDF使用HCS作为设置描述源码,HCS具体介绍设置管理。
驱动设置包含两部分,HDF框架定义的驱动设备描述和驱动的私有设置信息,具体写法如下:


  • 驱动设备描述(必选)
    HDF框架加载驱动所必要的信息泉源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs设置文件中添加对应的设备描述。驱动的设备描述填写如下所示:
    1. root {
    2.     device_info {
    3.         match_attr = "hdf_manager";
    4.         template host {       // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省。
    5.             hostName = "";
    6.             priority = 100;
    7.             uid = "";         // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。
    8.             gid = "";         // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。
    9.             caps = [""];      // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。
    10.             template device {
    11.                 template deviceNode {
    12.                     policy = 0;
    13.                     priority = 100;
    14.                     preload = 0;
    15.                     permission = 0664;
    16.                     moduleName = "";
    17.                     serviceName = "";
    18.                     deviceMatchAttr = "";
    19.                 }
    20.             }
    21.         }
    22.         sample_host :: host{
    23.             hostName = "host0";    // host名称,host节点是用来存放某一类驱动的容器。
    24.             priority = 100;        // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。
    25.             caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];   // 用户态进程Linux capabilities配置。
    26.             device_sample :: device {        // sample设备节点
    27.                 device0 :: deviceNode {      // sample驱动的DeviceNode节点
    28.                     policy = 1;              // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。
    29.                     priority = 100;          // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。
    30.                     preload = 0;             // 驱动按需加载字段。
    31.                     permission = 0664;       // 驱动创建设备节点权限
    32.                     moduleName = "sample_driver";      // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
    33.                     serviceName = "sample_service";    // 驱动对外发布服务的名称,必须唯一。
    34.                     deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
    35.                 }
    36.             }
    37.         }
    38.     }
    39. }
    复制代码
          阐明:

    • uid、gid、caps等设置项是用户态驱动的启动设置,内核态不用设置。
    • 根据进程权限最小化设计原则,业务模块uid、gid不用设置,如上面的sample_host,使用普通用户权限,即uid和gid被定义为hostName的定义值。
    • 如果普通用户权限不能满足业务要求,必要把uid、gid定义为system大概root权限时,请找安全专家举行评审。
    • 进程的uid在文件base/startup/init/services/etc/passwd中设置,进程的gid在文件base/startup/init/services/etc/group中设置,进程uid和gid设置参考:体系服务用户组添加方法。
    • caps值:格式为caps = ["xxx"],如果要设置CAP_DAC_OVERRIDE,此处必要填写caps = ["DAC_OVERRIDE"],不能填写为caps = ["CAP_DAC_OVERRIDE"]。
    • preload:驱动按需加载字段。
       
  • 驱动私有设置信息(可选)
    如果驱动有私有设置,则可以添加一个驱动的设置文件,用来填写一些驱动的默认设置信息。HDF框架在加载驱动的时间,会将对应的设置信息获取并生存在HdfDeviceObject中的property内里,通过Bind和Init(参考驱动实现)传递给驱动。驱动的设置信息示例如下:
    1. root {
    2.     SampleDriverConfig {
    3.         sample_version = 1;
    4.         sample_bus = "I2C_0";
    5.         match_attr = "sample_config";   // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
    6.     }
    7. }
    复制代码
    设置信息定义之后,必要将该设置文件添加到板级设置入口文件hdf.hcs,示例如下:
    1. #include "device_info/device_info.hcs"
    2. #include "sample/sample_config.hcs"
    复制代码
驱动消息机制管理开发


  • 将驱动设置信息中服务战略policy字段设置为2(SERVICE_POLICY_CAPACITY,policy定义)。
    1. device_sample :: Device {
    2.     policy = 2;
    3.     ...
    4. }
    复制代码
  • 设置驱动信息中的服务设备节点权限(permission字段)是框架给驱动创建立备节点的权限,默认是0666,驱动开发者根据驱动的现实使用场景设置驱动设备节点的权限。
  • 在服务实现过程中,实现服务基类成员IDeviceIoService中的Dispatch方法。
    1. // Dispatch是用来处理用户态发下来的消息
    2. int32_t SampleDriverDispatch(struct HdfDeviceIoClient *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
    3. {
    4.     HDF_LOGI("sample driver lite A dispatch");
    5.     return HDF_SUCCESS;
    6. }
    7. int32_t SampleDriverBind(struct HdfDeviceObject *device)
    8. {
    9.     HDF_LOGI("test for lite os sample driver A Open!");
    10.     if (device == NULL) {
    11.         HDF_LOGE("test for lite os sample driver A Open failed!");
    12.         return HDF_FAILURE;
    13.     }
    14.     static struct ISampleDriverService sampleDriverA = {
    15.         .ioService.Dispatch = SampleDriverDispatch,
    16.         .ServiceA = SampleDriverServiceA,
    17.         .ServiceB = SampleDriverServiceB,
    18.     };
    19.     device->service = (struct IDeviceIoService *)(&sampleDriverA);
    20.     return HDF_SUCCESS;
    21. }
    复制代码
  • 驱动定义消息处理函数中的cmd范例。
    1. #define SAMPLE_WRITE_READ 1    // 读写操作码1
    复制代码
  • 用户态获取服务接口并发送消息到驱动。
    1. int SendMsg(const char *testMsg)
    2. {
    3.     if (testMsg == NULL) {
    4.         HDF_LOGE("test msg is null");
    5.         return HDF_FAILURE;
    6.     }
    7.     struct HdfIoService *serv = HdfIoServiceBind("sample_driver");
    8.     if (serv == NULL) {
    9.         HDF_LOGE("fail to get service");
    10.         return HDF_FAILURE;
    11.     }
    12.     struct HdfSBuf *data = HdfSbufObtainDefaultSize();
    13.     if (data == NULL) {
    14.         HDF_LOGE("fail to obtain sbuf data");
    15.         return HDF_FAILURE;
    16.     }
    17.     struct HdfSBuf *reply = HdfSbufObtainDefaultSize();
    18.     if (reply == NULL) {
    19.         HDF_LOGE("fail to obtain sbuf reply");
    20.         ret = HDF_DEV_ERR_NO_MEMORY;
    21.         goto out;
    22.     }
    23.     if (!HdfSbufWriteString(data, testMsg)) {
    24.         HDF_LOGE("fail to write sbuf");
    25.         ret = HDF_FAILURE;
    26.         goto out;
    27.     }
    28.     int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
    29.     if (ret != HDF_SUCCESS) {
    30.         HDF_LOGE("fail to send service call");
    31.         goto out;
    32.     }
    33. out:
    34.     HdfSbufRecycle(data);
    35.     HdfSbbufRecycle(reply);
    36.     HdfIoServiceRecycle(serv);
    37.     return ret;
    38. }
    复制代码
  • 用户态吸收该驱动上报的消息。

    • 用户态编写驱动上报消息的处理函数。
      1. static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
      2. {
      3.     OsalTimespec time;
      4.     OsalGetTime(&time);
      5.     HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec);
      6.     const char *string = HdfSbufReadString(data);
      7.     if (string == NULL) {
      8.         HDF_LOGE("fail to read string in event data");
      9.         return HDF_FAILURE;
      10.     }
      11.     HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s",  (char *)priv, id, string);
      12.     return HDF_SUCCESS;
      13. }
      复制代码
    • 用户态注册吸收驱动上报消息的利用方法。
      1. int RegisterListen()
      2. {
      3.     struct HdfIoService *serv = HdfIoServiceBind("sample_driver");
      4.     if (serv == NULL) {
      5.         HDF_LOGE("fail to get service");
      6.         return HDF_FAILURE;
      7.     }
      8.     static struct HdfDevEventlistener listener = {
      9.         .callBack = OnDevEventReceived,
      10.         .priv ="Service0"
      11.     };
      12.     if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
      13.         HDF_LOGE("fail to register event listener");
      14.         return HDF_FAILURE;
      15.     }
      16.     ......
      17.     HdfDeviceUnregisterEventListener(serv, &listener);
      18.     HdfIoServiceRecycle(serv);
      19.     return HDF_SUCCESS;
      20. }
      复制代码
    • 驱动上报事件。
      1. int32_t SampleDriverDispatch(HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
      2. {
      3.     ... // process api call here
      4.     return HdfDeviceSendEvent(client->device, cmdCode, data);
      5. }
      复制代码

驱动服务管理开发

驱动服务管理的开发包罗驱动服务的编写、绑定、获取大概订阅,具体步骤如下。
驱动服务编写

  1. // 驱动服务结构的定义
  2. struct ISampleDriverService {
  3.     struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员。
  4.     int32_t (*ServiceA)(void);               // 驱动的第一个服务接口。
  5.     int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加。
  6. };
  7. // 驱动服务接口的实现
  8. int32_t SampleDriverServiceA(void)
  9. {
  10.     // 驱动开发者实现业务逻辑
  11.     return HDF_SUCCESS;
  12. }
  13. int32_t SampleDriverServiceB(uint32_t inputCode)
  14. {
  15.     // 驱动开发者实现业务逻辑
  16.     return HDF_SUCCESS;
  17. }
复制代码
驱动服务绑定

开发者实现HdfDriverEntry中的Bind指针函数,如下的SampleDriverBind,把驱动服务绑定到HDF框架中。
  1. int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
  2. {
  3.     // deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
  4.     if (deviceObject == NULL) {
  5.         HDF_LOGE("Sample device object is null!");
  6.         return HDF_FAILURE;
  7.     }
  8.     static struct ISampleDriverService sampleDriverA = {
  9.         .ServiceA = SampleDriverServiceA,
  10.         .ServiceB = SampleDriverServiceB,
  11.     };
  12.     deviceObject->service = &sampleDriverA.ioService;
  13.     return HDF_SUCCESS;
  14. }
复制代码
驱动服务获取

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

当驱动服务获取者明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:
  1. const struct ISampleDriverService *sampleService =
  2.         (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
  3. if (sampleService == NULL) {
  4.     return HDF_FAILURE;
  5. }
  6. sampleService->ServiceA();
  7. sampleService->ServiceB(5);
复制代码
通过HDF提供的订阅机制获取

当内核态驱动服务获取者对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动服务。当该驱动加载完成时,HDF框架会将被订阅的驱动服务发布给订阅者(驱动服务获取者),实现方式如下所示:
  1. // 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用。
  2. // object为订阅者的私有数据,service为被订阅的服务对象。
  3. int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
  4. {
  5.     const struct ISampleDriverService *sampleService =
  6.         (const struct ISampleDriverService *)service;
  7.     if (sampleService == NULL) {
  8.         return HDF_FAILURE;
  9.     }
  10.     sampleService->ServiceA();
  11.     sampleService->ServiceB(5);
  12. }
  13. // 订阅过程的实现
  14. int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
  15. {
  16.     if (deviceObject == NULL) {
  17.         HDF_LOGE("Test driver init failed, deviceObject is null!");
  18.         return HDF_FAILURE;
  19.     }
  20.     struct SubscriberCallback callBack;
  21.     callBack.deviceObject = deviceObject;
  22.     callBack.OnServiceConnected = TestDriverSubCallBack;
  23.     int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);
  24.     if (ret != HDF_SUCCESS) {
  25.         HDF_LOGE("Test driver subscribe sample driver failed!");
  26.     }
  27.     return ret;
  28. }
复制代码
HDF开发实例

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

在HDF框架的设置文件(例如vendor/hisilicon/xxx/hdf_config/device_info)中添加该驱动的设置信息,如下所示:
  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.         sample_host :: host {
  20.             hostName = "sample_host";
  21.             sample_device :: device {
  22.                 device0 :: deviceNode {
  23.                     policy = 2;
  24.                     priority = 100;
  25.                     preload = 1;
  26.                     permission = 0664;
  27.                     moduleName = "sample_driver";
  28.                     serviceName = "sample_service";
  29.                 }
  30.             }
  31.         }
  32.     }
  33. }
复制代码
编写驱动代码

基于HDF框架编写的sample驱动代码如下:
  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. #define SAMPLE_WRITE_READ 123
  9. static int32_t HdfSampleDriverDispatch(
  10.     struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
  11. {
  12.     HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id);
  13.     if (id == SAMPLE_WRITE_READ) {
  14.         const char *readData = HdfSbufReadString(data);
  15.         if (readData != NULL) {
  16.             HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData);
  17.         }
  18.         if (!HdfSbufWriteInt32(reply, INT32_MAX)) {
  19.             HDF_LOGE("%{public}s: reply int32 fail", __func__);
  20.         }
  21.         return HdfDeviceSendEvent(client->device, id, data);
  22.     }
  23.     return HDF_FAILURE;
  24. }
  25. static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
  26. {
  27.     // release resources here
  28.     return;
  29. }
  30. static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
  31. {
  32.     if (deviceObject == NULL) {
  33.         return HDF_FAILURE;
  34.     }
  35.     static struct IDeviceIoService testService = {
  36.         .Dispatch = HdfSampleDriverDispatch,
  37.     };
  38.     deviceObject->service = &testService;
  39.     return HDF_SUCCESS;
  40. }
  41. static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
  42. {
  43.     if (deviceObject == NULL) {
  44.         HDF_LOGE("%{public}s::ptr is null!", __func__);
  45.         return HDF_FAILURE;
  46.     }
  47.     HDF_LOGI("Sample driver Init success");
  48.     return HDF_SUCCESS;
  49. }
  50. static struct HdfDriverEntry g_sampleDriverEntry = {
  51.     .moduleVersion = 1,
  52.     .moduleName = "sample_driver",
  53.     .Bind = HdfSampleDriverBind,
  54.     .Init = HdfSampleDriverInit,
  55.     .Release = HdfSampleDriverRelease,
  56. };
  57. HDF_INIT(g_sampleDriverEntry);
复制代码
编写用户程序和驱动交互代码

基于HDF框架编写的用户态程序和驱动交互的代码如下(代码可以放在目录drivers/hdf_core/adapter/uhdf下面编译,BUILD.gn可以参考drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn):
  1. #include <fcntl.h>
  2. #include <sys/stat.h>
  3. #include <sys/ioctl.h>
  4. #include <unistd.h>
  5. #include "hdf_log.h"
  6. #include "hdf_sbuf.h"
  7. #include "hdf_io_service_if.h"
  8. #define HDF_LOG_TAG sample_test
  9. #define SAMPLE_SERVICE_NAME "sample_service"
  10. #define SAMPLE_WRITE_READ 123
  11. int g_replyFlag = 0;
  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_LOGI("%{public}s: dev event received: %{public}u %{public}s",  (char *)priv, id, string);
  21.     g_replyFlag = 1;
  22.     return HDF_SUCCESS;
  23. }
  24. static int SendEvent(struct HdfIoService *serv, char *eventData)
  25. {
  26.     int ret = 0;
  27.     struct HdfSBuf *data = HdfSbufObtainDefaultSize();
  28.     if (data == NULL) {
  29.         HDF_LOGE("fail to obtain sbuf data");
  30.         return 1;
  31.     }
  32.     struct HdfSBuf *reply = HdfSbufObtainDefaultSize();
  33.     if (reply == NULL) {
  34.         HDF_LOGE("fail to obtain sbuf reply");
  35.         ret = HDF_DEV_ERR_NO_MEMORY;
  36.         goto out;
  37.     }
  38.     if (!HdfSbufWriteString(data, eventData)) {
  39.         HDF_LOGE("fail to write sbuf");
  40.         ret = HDF_FAILURE;
  41.         goto out;
  42.     }
  43.     ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
  44.     if (ret != HDF_SUCCESS) {
  45.         HDF_LOGE("fail to send service call");
  46.         goto out;
  47.     }
  48.     int replyData = 0;
  49.     if (!HdfSbufReadInt32(reply, &replyData)) {
  50.         HDF_LOGE("fail to get service call reply");
  51.         ret = HDF_ERR_INVALID_OBJECT;
  52.         goto out;
  53.     }
  54.     HDF_LOGI("Get reply is: %{public}d", replyData);
  55. out:
  56.     HdfSbufRecycle(data);
  57.     HdfSbufRecycle(reply);
  58.     return ret;
  59. }
  60. int main()
  61. {
  62.     char *sendData = "default event info";
  63.     struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
  64.     if (serv == NULL) {
  65.         HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);
  66.         return HDF_FAILURE;
  67.     }
  68.     static struct HdfDevEventlistener listener = {
  69.         .callBack = OnDevEventReceived,
  70.         .priv ="Service0"
  71.     };
  72.     if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
  73.         HDF_LOGE("fail to register event listener");
  74.         return HDF_FAILURE;
  75.     }
  76.     if (SendEvent(serv, sendData)) {
  77.         HDF_LOGE("fail to send event");
  78.         return HDF_FAILURE;
  79.     }
  80.     while (g_replyFlag == 0) {
  81.         sleep(1);
  82.     }
  83.     if (HdfDeviceUnregisterEventListener(serv, &listener)) {
  84.         HDF_LOGE("fail to  unregister listener");
  85.         return HDF_FAILURE;
  86.     }
  87.     HdfIoServiceRecycle(serv);
  88.     return HDF_SUCCESS;
  89. }
复制代码
   阐明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中必要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:
  deps = [
  ​ "//drivers/hdf_core/adapter/uhdf/manager:hdf_core",
  ​ "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal",
  ]
  最后

有很多小同伴不知道学习哪些鸿蒙开发技术?不知道必要重点把握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,终极浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必把握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
渴望这一份鸿蒙学习资料可以或许给大家带来帮助,有必要的小同伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(HarmonyOS NEXT)最新学习路线




  •  HarmonOS底子技能



  • HarmonOS就业必备技能 

  •  HarmonOS多媒体技术



  • 鸿蒙NaPi组件进阶



  • HarmonOS高级技能



  • 初识HarmonOS内核 

  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份团结鸿蒙官方发布笔记整理收纳的一套体系性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》


《鸿蒙生态应用开发V2.0白皮书》


《鸿蒙 (OpenHarmony)开发底子到实战手册》

OpenHarmony北向、南向开发情况搭建

 《鸿蒙开发底子》



  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

 《鸿蒙开发进阶》



  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 使命管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙体系移植和裁剪定制
  • ……

《鸿蒙进阶实战》



  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个时机。只有积极应对变化,不断学习和提拔自己,他们才能在这个厘革的期间中立于不败之地。 


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表