1# HDF驱动开发流程
2
3## 概述
4
5HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路,让驱动开发和部署更加规范,旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的驱动管理的开发环境,力求做到一次开发,多系统部署。
6
7### 驱动加载
8
9HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能。
10
11### 驱动服务管理
12
13HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。
14
15### 驱动消息机制
16
17HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。
18
19### 配置管理
20
21HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。
22
23### 驱动模型
24
25HDF框架将一类设备驱动放在同一个Host(设备容器)里面,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在不同的Host,主要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host里面,否则部署到独立的Host中是更好的选择。Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。HDF驱动模型如下图所示:
26
27**图1** HDF驱动模型
28
29![](figures/HDF驱动模型.png)
30
31## 功能描述
32
33### 驱动加载
34
35HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能,支持按需加载和按序加载两种策略,具体设备的加载策略由配置文件中的preload字段来控制,配置值参考如下:
36
37```c
38typedef enum {
39    DEVICE_PRELOAD_ENABLE = 0,
40    DEVICE_PRELOAD_ENABLE_STEP2 = 1,
41    DEVICE_PRELOAD_DISABLE = 2,
42    DEVICE_PRELOAD_INVALID
43} DevicePreload;
44```
45
46#### 按需加载
47
48- preload字段配置为0(DEVICE_PRELOAD_ENABLE),则系统启动过程中默认加载。
49- preload字段配置为1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同。
50- preload字段配置为2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务[消息机制](#驱动消息机制管理)时,如果驱动服务不存在,HDF框架会尝试动态加载该驱动。
51
52#### 按序加载(默认加载策略)
53
54配置文件中的priority(取值范围为整数0到200)是用来表示host(驱动容器)和驱动的优先级的。不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。
55
56#### 异常恢复(用户态驱动)
57
58当驱动服务异常退出时,恢复策略如下:
59- preload字段配置为0(DEVICE_PRELOAD_ENABLE)或1(DEVICE_PRELOAD_ENABLE_STEP2)的驱动服务,由启动模块拉起host并重新加载服务。
60- preload字段配置为2(DEVICE_PRELOAD_DISABLE)的驱动服务,需业务模块注册HDF的服务状态监听器,当收到服务退出消息时,业务模块调用LoadDevice重新加载服务。
61
62### 驱动服务管理
63
64驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取。HDF框架定义了驱动对外发布服务的策略,由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:
65
66```c
67typedef enum {
68    /* 驱动不提供服务 */
69    SERVICE_POLICY_NONE = 0,
70    /* 驱动对内核态发布服务 */
71    SERVICE_POLICY_PUBLIC = 1,
72    /* 驱动对内核态和用户态都发布服务 */
73    SERVICE_POLICY_CAPACITY = 2,
74    /* 驱动服务不对外发布服务,但可以被订阅 */
75    SERVICE_POLICY_FRIENDLY = 3,
76    /* 驱动私有服务不对外发布服务,也不能被订阅 */
77    SERVICE_POLICY_PRIVATE = 4,
78    /* 错误的服务策略 */
79    SERVICE_POLICY_INVALID
80} ServicePolicy;
81```
82
83#### 使用场景
84
85当驱动需要以接口的形式对外提供能力时,可以使用HDF框架的驱动服务管理能力。
86
87#### 接口说明
88
89针对驱动服务管理功能,HDF框架开放了以下接口供开发者调用,如下表所示:
90
91**表1** 服务管理接口
92
93| 方法                                                         | 描述                                                         |
94| ------------------------------------------------------------ | ------------------------------------------------------------ |
95| int32_t (*Bind)(struct HdfDeviceObject *deviceObject)        | 需要驱动开发者实现Bind函数,将自己的服务接口绑定到HDF框架中。 |
96| const struct HdfObject *DevSvcManagerClntGetService(const char *svcName) | 获取驱动的服务。                                             |
97| int HdfDeviceSubscribeService( struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback) | 订阅驱动的服务。                                             |
98
99
100### 驱动消息机制管理
101
102#### 使用场景
103
104当用户态应用和内核态驱动需要交互时,可以使用HDF框架的消息机制来实现。
105
106#### 接口说明
107
108消息机制的功能主要有以下两种:
109- 用户态应用发送消息到驱动。
110- 用户态应用接收驱动主动上报事件。
111
112**表2** 消息机制接口
113
114| 方法                                                         | 描述                                                         |
115| ------------------------------------------------------------ | ------------------------------------------------------------ |
116| struct HdfIoService *HdfIoServiceBind(const char *serviceName); | 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。 |
117| void HdfIoServiceRecycle(struct HdfIoService *service);      | 释放驱动服务。                                               |
118| int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | 用户态程序注册接收驱动上报事件的操作方法。                   |
119| int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data) | 驱动主动上报事件接口。                                       |
120
121
122
123### 配置管理
124
125#### 配置概述
126
127HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:
128
129-   在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。
130-   在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。
131
132以下是使用HCB模式的典型应用场景:
133
134**图2** 配置使用流程图
135
136![](figures/配置使用流程图.png)
137
138HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。
139
140#### 配置语法
141
142HCS的语法介绍如下:
143
144##### 关键字
145
146HCS配置语法保留了以下关键字。
147
148**表3** HCS配置语法保留关键字
149
150| 关键字     | 用途                       | 说明                                       |
151| ---------- | -------------------------- | ------------------------------------------ |
152| root       | 配置根节点                 | -                                          |
153| include    | 引用其他HCS配置文件        | -                                          |
154| delete     | 删除节点或属性             | 只能用于操作include导入的配置树            |
155| template   | 定义模板节点               | -                                          |
156| match_attr | 用于标记节点的匹配查找属性 | 解析配置时可以使用该属性的值查找到对应节点 |
157
158##### 基本结构
159
160HCS主要分为属性(Attribute)和节点(Node)两种结构。
161
162**属性**
163
164属性即最小的配置单元,是一个独立的配置项。语法如下:
165
166```
167  attribute_name = value;
168```
169
170-   attribute_name是**字母、数字、下划线**的组合且必须以字母或下划线开头,字母区分大小写。
171-   value的可用格式如下:
172    -   数字常量,支持二进制、八进制、十进制、十六进制数,具体[数据类型](#数据类型)章节。
173    -   字符串,内容使用双引号("")引用。
174    -   节点引用。
175-   attribute必须以分号(;)结束且必须属于一个node。
176
177**节点**
178
179节点是一组属性的集合,语法如下:
180
181```
182  node_name {
183      module = "sample";
184      ...
185  }
186```
187
188-   node_name是**字母、数字、下划线**的组合且必须以字母或下划线开头,字母区分大小写。
189-   大括号后无需添加结束符“;”。
190-   root为保留关键字,用于声明配置表的根节点。每个配置表必须以root节点开始。
191-   root节点中必须包含module属性,其值应该为一个字符串,用于表征该配置所属模块。
192-   节点中可以增加match_attr属性,其值为一个全局唯一的字符串。当驱动程序在解析配置时可以以该属性的值为参数调用查找接口查找到包含该属性的节点。
193
194##### 数据类型
195
196在属性定义中使用自动数据类型,不显式指定类型,属性支持的数据类型如下:
197
198**整型**
199
200整型长度自动推断,根据实际数据长度给与最小空间占用的类型。
201
202-   二进制,0b前缀,示例:0b1010。
203-   八进制,0前缀,示例:0664。
204-   十进制 ,无前缀,且支持有符号与无符号,示例:1024,+1024均合法。驱动程序在读取负值时注意使用有符号数读取接口。
205-   十六进制,0x前缀,示例:0xff00、0xFF。
206
207**字符串**
208
209字符串使用双引号("")表示。
210
211**数组**
212
213数组元素支持整型、字符串,不支持混合类型。整型数组中uint32_t uint64_t混用会向上转型为uint64_t数组。整型数组与字符串数组示例如下:
214
215```
216attr_foo = [0x01, 0x02, 0x03, 0x04];
217attr_bar = ["hello", "world"];
218```
219
220**bool类型**
221
222bool类型中**true**表示真,**false**表示假。
223
224##### 预处理
225
226**include**
227
228用于导入其他HCS文件。语法示例如下:
229
230```
231#include "foo.hcs"
232#include "../bar.hcs"
233```
234
235-   文件名必须使用双引号(""),不在同一目录使用相对路径引用。被include文件也必须是合法的HCS文件。
236-   多个include,如果存在相同的节点,后者覆盖前者,其余的节点依次展开。
237
238##### 注释
239
240支持两种注释风格。
241
242-   单行注释。
243
244    ```
245    // comment
246    ```
247
248-   多行注释。
249
250    ```
251    /*
252    comment
253    */
254    ```
255
256    >   ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
257    >   多行注释不支持嵌套。
258
259##### 引用修改
260
261引用修改的作用是在当前节点中修改另外任意一个节点的内容,语法为:
262
263```
264 node :& source_node
265```
266
267上述语句表示node中的内容是对source_node节点内容的修改。示例如下:
268
269```
270root {
271    module = "sample";
272    foo {
273        foo_ :& root.bar{
274            attr = "foo";
275        }
276        foo1 :& foo2 {
277            attr = 0x2;
278        }
279        foo2 {
280            attr = 0x1;
281        }
282    }
283
284    bar {
285        attr = "bar";
286    }
287}
288```
289
290最终生成配置树为:
291
292```
293root {
294    module = "sample";
295    foo {
296        foo2 {
297            attr = 0x2;
298        }
299    }
300    bar {
301        attr = "foo";
302    }
303}
304```
305
306在以上示例中,可以看到foo.foo_节点通过引用将bar.attr属性的值修改为了"foo",foo.foo1节点通过引用将foo.foo2.attr属性的值修改为了0x2。foo.foo_以及foo.foo1节点表示对目标节点内容的修改,其自身并不会存在最终生成的配置树中。
307
308-   引用同级node,可以直接使用node名称,否则被引用的节点必须使用绝对路径,节点间使用“.”分隔,root表示根节点,格式为root开始的节点路径序列,例如root.foo.bar即为一个合法的绝对路径。
309-   如果出现修改冲突(即多处修改同一个属性),编译器将提示warning,因为这种情况下只会生效某一个修改而导致最终结果不确定。
310
311##### 节点复制
312
313节点复制可以实现在节点定义时从另一个节点先复制内容,用于定义内容相似的节点。语法为:
314
315```
316 node : source_node
317```
318
319上述语句表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。示例如下:
320
321```
322root {
323    module = "sample";
324    foo {
325        attr_0 = 0x0;
326    }
327    bar:foo {
328        attr_1 = 0x1;
329    }
330}
331```
332
333上述代码的最终生成配置树为:
334
335```
336root {
337    module = "sample";
338    foo {
339        attr_0 = 0x0;
340    }
341    bar {
342        attr_1 = 0x1;
343        attr_0 = 0x0;
344    }
345}
346```
347
348在上述示例中,编译后bar节点既包含attr_0属性又包含attr_1属性,在bar中对attr_0的修改不会影响到foo。
349
350当foo和bar在同级node中时可不指定foo的路径,否则需要使用绝对路径引用,绝对路径的介绍请参考[引用修改](#引用修改)。
351
352##### 删除
353
354要对include导入的base配置树中不需要的节点或属性进行删除,可以使用delete关键字。下面的举例中sample1.hcs通过include导入了sample2.hcs中的配置内容,并使用delete删除了sample2.hcs中的attribute2属性和foo_2节点,示例如下:
355
356```
357// sample2.hcs
358root {
359    attr_1 = 0x1;
360    attr_2 = 0x2;
361    foo_2 {
362        t = 0x1;
363    }
364}
365
366// sample1.hcs
367#include "sample2.hcs"
368root {
369    attr_2 = delete;
370    foo_2 : delete {
371    }
372}
373```
374
375上述代码在生成过程中将会删除root.foo_2节点与attr_2,最终生成配置树为:
376
377```
378root {
379    attr_1 = 0x1;
380}
381```
382
383>   ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
384>   在同一个HCS文件中不允许使用delete,建议直接删除不需要的属性。
385
386##### 属性引用
387
388为了在解析配置时快速定位到关联的节点,可以把节点作为属性的右值,通过读取属性查找到对应节点。语法为:
389
390```
391 attribute = &node;
392```
393
394上述语句表示attribute的值是一个节点node的引用,在解析时可以用这个attribute快速定位到node,便于关联和查询其他node。示例如下:
395
396```
397node1 {
398    attributes;
399}
400node2 {
401    attr_1 = &root.node1;
402}
403```
404
405406
407```
408node2 {
409    node1 {
410        attributes;
411    }
412    attr_1 = &node1;
413}
414```
415
416##### 模板
417
418模板的用途在于生成严格一致的node结构,以便对同类型node进行遍历和管理。使用template关键字定义模板node,子node通过双冒号“::”声明继承关系。子节点可以改写或新增但不能删除template中的属性,子节点中没有定义的属性将使用template中的定义作为默认值。示例如下:
419
420```
421root {
422    module = "sample";
423    template foo {
424        attr_1 = 0x1;
425        attr_2 = 0x2;
426    }
427
428    bar :: foo {
429    }
430
431    bar_1 :: foo {
432        attr_1 = 0x2;
433    }
434}
435```
436
437​        生成配置树如下:
438
439```
440root {
441    module = "sample";
442    bar {
443        attr_1 = 0x1;
444        attr_2 = 0x2;
445    }
446    bar_1 {
447        attr_1 = 0x2;
448        attr_2 = 0x2;
449    }
450}
451```
452
453在上述示例中,bar和bar_1节点继承了foo节点,生成配置树节点结构与foo保持了完全一致,只是属性的值不同。
454
455#### 配置生成
456
457​        hc-gen是配置生成的工具,可以对HCS配置语法进行检查并把HCS源文件转化成HCB二进制文件。
458
459**hc-gen介绍**
460
461​        hc-gen参数说明:
462
463```
464Usage: hc-gen [Options] [File]
465options:
466  -o <file>   output file name, default same as input
467  -a          hcb align with four bytes
468  -b          output binary output, default enable
469  -t          output config in C language source file style
470  -m          output config in macro source file style
471  -i          output binary hex dump in C language source file style
472  -p <prefix> prefix of generated symbol name
473  -d          decompile hcb to hcs
474  -V          show verbose info
475  -v          show version
476  -h          show this help message
477```
478
479生成.c/.h配置文件方法:
480
481```
482hc-gen -o [OutputCFileName] -t [SourceHcsFileName]
483```
484
485生成HCB配置文件方法:
486
487```
488hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]
489```
490
491生成宏定义配置文件方法:
492
493```
494hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]
495```
496
497反编译HCB文件为HCS方法:
498
499```
500hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
501```
502
503## 开发指导
504
505### 场景介绍
506
507关于驱动的开发我们主要目的是实现驱动代码的编写,但是驱动开发过程中需要服务管理、消息机制管理,才能使驱动在代码编译过程中进行加载。以下开发步骤中介绍了驱动开发、驱动消息机制管理开发、驱动服务管理开发的步骤。
508
509### 驱动开发实例
510
511基于HDF框架的驱动开发主要分为三个部分:驱动实现、驱动编译脚本编写和驱动配置。详细开发流程如下所示:
512
513#### 驱动实现
514
515驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下:
516
517-   驱动业务代码
518
519    ```c
520    #include "hdf_device_desc.h"          // HDF框架对驱动开发相关能力接口的头文件
521    #include "hdf_log.h"                  // HDF框架提供的日志接口头文件
522
523    #define HDF_LOG_TAG sample_driver     // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。
524
525    // 将驱动对外提供的服务能力接口绑定到HDF框架。
526    int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
527    {
528        HDF_LOGD("Sample driver bind success");
529        return HDF_SUCCESS;
530    }
531
532    // 驱动自身业务初始化的接口
533    int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
534    {
535        HDF_LOGD("Sample driver Init success");
536        return HDF_SUCCESS;
537    }
538
539    // 驱动资源释放的接口
540    void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
541    {
542        HDF_LOGD("Sample driver release success");
543        return;
544    }
545    ```
546
547-   驱动入口注册到HDF框架
548
549    ```c
550    // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。
551    struct HdfDriverEntry g_sampleDriverEntry = {
552        .moduleVersion = 1,
553        .moduleName = "sample_driver",
554        .Bind = HdfSampleDriverBind,
555        .Init = HdfSampleDriverInit,
556        .Release = HdfSampleDriverRelease,
557    };
558
559    // 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
560    HDF_INIT(g_sampleDriverEntry);
561    ```
562
563#### 驱动编译脚本编写
564
565-   ##### LiteOS
566
567    涉及Makefile和BUILD.gn修改:
568
569    -   Makefile部分:
570
571        驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。
572
573        ```makefile
574        include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # 【必需】导入hdf预定义内容
575        MODULE_NAME :=        #生成的结果文件
576        LOCAL_INCLUDE :=      #本驱动的头文件目录
577        LOCAL_SRCS :=         #本驱动的源代码文件
578        LOCAL_CFLAGS :=      #自定义的编译选项
579        include $(HDF_DRIVER) #导入Makefile模板完成编译
580        ```
581
582        编译结果文件链接到内核镜像,添加到**drivers/hdf_core/adapter/khdf/liteos**目录下的**hdf_lite.mk**里面,示例如下:
583
584        ```makefile
585        LITEOS_BASELIB +=  -lxxx  #链接生成的静态库
586        LIB_SUBDIRS    +=         #驱动代码Makefile的目录
587        ```
588
589    -   BUILD.gn部分:
590
591        添加模块BUILD.gn,可参考如下示例:
592
593        ```
594        import("//build/lite/config/component/lite_component.gni")
595        import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni")
596        module_switch = defined(LOSCFG_DRIVERS_HDF_xxx)
597        module_name = "xxx"
598        hdf_driver(module_name) {
599            sources = [
600                "xxx/xxx/xxx.c",           #模块要编译的源码文件
601            ]
602            public_configs = [ ":public" ] #使用依赖的头文件配置
603        }
604        config("public") {                 #定义依赖的头文件配置
605            include_dirs = [
606                "xxx/xxx/xxx",             #依赖的头文件目录
607            ]
608        }
609        ```
610
611        把新增模块的BUILD.gn所在的目录添加到**/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**里面:
612
613        ```
614        group("liteos") {
615            public_deps = [ ":$module_name" ]
616            deps = [
617                "xxx/xxx",#新增模块BUILD.gn所在的目录,/drivers/hdf_core/adapter/khdf/liteos
618            ]
619        }
620        ```
621
622-   ##### Linux
623
624    如果需要定义模块控制宏,需要在模块目录xxx里面添加Kconfig文件,并把Kconfig文件路径添加到**drivers/hdf_core/adapter/khdf/linux/Kconfig**里面:
625
626    ```
627    source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录
628    ```
629
630    添加模块目录到**drivers/hdf_core/adapter/khdf/linux/Makefile**:
631
632    ```makefile
633    obj-$(CONFIG_DRIVERS_HDF)  += xxx/
634    ```
635
636    在模块目录xxx里面添加Makefile文件,在Makefile文件里面添加模块代码编译规则:
637
638    ```makefile
639    obj-y  += xxx.o
640    ```
641
642#### 驱动配置
643
644HDF使用HCS作为配置描述源码,HCS详细介绍[配置管理](#配置概述)。
645
646驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下:
647
648-   驱动设备描述(必选)
649
650    HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述。驱动的设备描述填写如下所示:
651
652    ```
653    root {
654        device_info {
655            match_attr = "hdf_manager";
656            template host {       // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省。
657                hostName = "";
658                priority = 100;
659                uid = "";         // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。
660                gid = "";         // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。
661                caps = [""];      // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。
662                template device {
663                    template deviceNode {
664                        policy = 0;
665                        priority = 100;
666                        preload = 0;
667                        permission = 0664;
668                        moduleName = "";
669                        serviceName = "";
670                        deviceMatchAttr = "";
671                    }
672                }
673            }
674            sample_host :: host{
675                hostName = "host0";    // host名称,host节点是用来存放某一类驱动的容器。
676                priority = 100;        // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。
677                caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];   // 用户态进程Linux capabilities配置。
678                device_sample :: device {        // sample设备节点
679                    device0 :: deviceNode {      // sample驱动的DeviceNode节点
680                        policy = 1;              // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。
681                        priority = 100;          // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。
682                        preload = 0;             // 驱动按需加载字段。
683                        permission = 0664;       // 驱动创建设备节点权限
684                        moduleName = "sample_driver";      // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
685                        serviceName = "sample_service";    // 驱动对外发布服务的名称,必须唯一。
686                        deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
687                    }
688                }
689            }
690        }
691    }
692    ```
693
694    >   ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
695    >
696    >   -   uid、gid、caps等配置项是用户态驱动的启动配置,内核态不用配置。
697    >   -   根据进程权限最小化设计原则,业务模块uid、gid不用配置,如上面的sample_host,使用普通用户权限,即uid和gid被定义为hostName的定义值。
698    >   -   如果普通用户权限不能满足业务要求,需要把uid、gid定义为system或者root权限时,请找安全专家进行评审。
699    >   -   进程的uid在文件**base/startup/init/services/etc/passwd**中配置,进程的gid在文件**base/startup/init/services/etc/group**中配置,进程uid和gid配置参考:[系统服务用户组添加方法](https://gitee.com/openharmony/startup_init_lite/wikis)700    >   -   caps值:格式为caps = ["xxx"],如果要配置CAP_DAC_OVERRIDE,此处需要填写caps = ["DAC_OVERRIDE"],不能填写为caps = ["CAP_DAC_OVERRIDE"]。
701    >   -   preload:驱动按需加载字段。
702
703-   驱动私有配置信息(可选)
704
705    如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息。HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init(参考[驱动实现](#驱动实现))传递给驱动。驱动的配置信息示例如下:
706
707    ```
708    root {
709        SampleDriverConfig {
710            sample_version = 1;
711            sample_bus = "I2C_0";
712            match_attr = "sample_config";   // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
713        }
714    }
715    ```
716
717    配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs,示例如下:
718
719    ```
720    #include "device_info/device_info.hcs"
721    #include "sample/sample_config.hcs"
722    ```
723
724### 驱动消息机制管理开发
725
7261.  将驱动配置信息中服务策略policy字段设置为2(SERVICE_POLICY_CAPACITY,[policy定义](#驱动服务管理))。
727
728    ```
729    device_sample :: Device {
730        policy = 2;
731        ...
732    }
733    ```
734
7352.  配置驱动信息中的服务设备节点权限(permission字段)是框架给驱动创建设备节点的权限,默认是0666,驱动开发者根据驱动的实际使用场景配置驱动设备节点的权限。
736
7373.  在服务实现过程中,实现服务基类成员IDeviceIoService中的Dispatch方法。
738
739    ```c
740    // Dispatch是用来处理用户态发下来的消息
741    int32_t SampleDriverDispatch(struct HdfDeviceIoClient *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
742    {
743        HDF_LOGI("sample driver lite A dispatch");
744        return HDF_SUCCESS;
745    }
746    int32_t SampleDriverBind(struct HdfDeviceObject *device)
747    {
748        HDF_LOGI("test for lite os sample driver A Open!");
749        if (device == NULL) {
750            HDF_LOGE("test for lite os sample driver A Open failed!");
751            return HDF_FAILURE;
752        }
753        static struct ISampleDriverService sampleDriverA = {
754            .ioService.Dispatch = SampleDriverDispatch,
755            .ServiceA = SampleDriverServiceA,
756            .ServiceB = SampleDriverServiceB,
757        };
758        device->service = (struct IDeviceIoService *)(&sampleDriverA);
759        return HDF_SUCCESS;
760    }
761    ```
762
7634.  驱动定义消息处理函数中的cmd类型。
764
765    ```c
766    #define SAMPLE_WRITE_READ 1    // 读写操作码1
767    ```
768
7695.  用户态获取服务接口并发送消息到驱动。
770
771    ```c
772    int SendMsg(const char *testMsg)
773    {
774        if (testMsg == NULL) {
775            HDF_LOGE("test msg is null");
776            return HDF_FAILURE;
777        }
778        struct HdfIoService *serv = HdfIoServiceBind("sample_driver");
779        if (serv == NULL) {
780            HDF_LOGE("fail to get service");
781            return HDF_FAILURE;
782        }
783        struct HdfSBuf *data = HdfSbufObtainDefaultSize();
784        if (data == NULL) {
785            HDF_LOGE("fail to obtain sbuf data");
786            return HDF_FAILURE;
787        }
788        struct HdfSBuf *reply = HdfSbufObtainDefaultSize();
789        if (reply == NULL) {
790            HDF_LOGE("fail to obtain sbuf reply");
791            ret = HDF_DEV_ERR_NO_MEMORY;
792            goto out;
793        }
794        if (!HdfSbufWriteString(data, testMsg)) {
795            HDF_LOGE("fail to write sbuf");
796            ret = HDF_FAILURE;
797            goto out;
798        }
799        int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
800        if (ret != HDF_SUCCESS) {
801            HDF_LOGE("fail to send service call");
802            goto out;
803        }
804    out:
805        HdfSbufRecycle(data);
806        HdfSbbufRecycle(reply);
807        HdfIoServiceRecycle(serv);
808        return ret;
809    }
810    ```
811
8126.  用户态接收该驱动上报的消息。
813
814    1.  用户态编写驱动上报消息的处理函数。
815
816        ```c
817        static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
818        {
819            OsalTimespec time;
820            OsalGetTime(&time);
821            HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec);
822
823            const char *string = HdfSbufReadString(data);
824            if (string == NULL) {
825                HDF_LOGE("fail to read string in event data");
826                return HDF_FAILURE;
827            }
828            HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s",  (char *)priv, id, string);
829            return HDF_SUCCESS;
830        }
831        ```
832
833    2.  用户态注册接收驱动上报消息的操作方法。
834
835        ```c
836        int RegisterListen()
837        {
838            struct HdfIoService *serv = HdfIoServiceBind("sample_driver");
839            if (serv == NULL) {
840                HDF_LOGE("fail to get service");
841                return HDF_FAILURE;
842            }
843            static struct HdfDevEventlistener listener = {
844                .callBack = OnDevEventReceived,
845                .priv ="Service0"
846            };
847            if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
848                HDF_LOGE("fail to register event listener");
849                return HDF_FAILURE;
850            }
851            ......
852            HdfDeviceUnregisterEventListener(serv, &listener);
853            HdfIoServiceRecycle(serv);
854            return HDF_SUCCESS;
855        }
856        ```
857
858    3.  驱动上报事件。
859
860        ```c
861        int32_t SampleDriverDispatch(HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
862        {
863            ... // process api call here
864            return HdfDeviceSendEvent(client->device, cmdCode, data);
865        }
866        ```
867
868### 驱动服务管理开发
869
870驱动服务管理的开发包括驱动服务的编写、绑定、获取或者订阅,详细步骤如下。
871
872#### 驱动服务编写
873
874```c
875// 驱动服务结构的定义
876struct ISampleDriverService {
877    struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员。
878    int32_t (*ServiceA)(void);               // 驱动的第一个服务接口。
879    int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加。
880};
881
882// 驱动服务接口的实现
883int32_t SampleDriverServiceA(void)
884{
885    // 驱动开发者实现业务逻辑
886    return HDF_SUCCESS;
887}
888
889int32_t SampleDriverServiceB(uint32_t inputCode)
890{
891    // 驱动开发者实现业务逻辑
892    return HDF_SUCCESS;
893}
894```
895
896#### 驱动服务绑定
897
898开发者实现HdfDriverEntry中的Bind指针函数,如下的SampleDriverBind,把驱动服务绑定到HDF框架中。
899
900```c
901int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
902{
903    // deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。
904    if (deviceObject == NULL) {
905        HDF_LOGE("Sample device object is null!");
906        return HDF_FAILURE;
907    }
908    static struct ISampleDriverService sampleDriverA = {
909        .ServiceA = SampleDriverServiceA,
910        .ServiceB = SampleDriverServiceB,
911    };
912    deviceObject->service = &sampleDriverA.ioService;
913    return HDF_SUCCESS;
914}
915```
916
917#### 驱动服务获取
918
919应用程序开发者获取驱动服务有两种方式:通过HDF接口直接获取和通过HDF提供的订阅机制获取。
920
921##### 通过HDF接口直接获取
922
923当驱动服务获取者明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:
924
925```c
926const struct ISampleDriverService *sampleService =
927        (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
928if (sampleService == NULL) {
929    return HDF_FAILURE;
930}
931sampleService->ServiceA();
932sampleService->ServiceB(5);
933```
934
935##### 通过HDF提供的订阅机制获取
936
937当内核态驱动服务获取者对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动服务。当该驱动加载完成时,HDF框架会将被订阅的驱动服务发布给订阅者(驱动服务获取者),实现方式如下所示:
938
939```c
940// 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用。
941// object为订阅者的私有数据,service为被订阅的服务对象。
942int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
943{
944    const struct ISampleDriverService *sampleService =
945        (const struct ISampleDriverService *)service;
946    if (sampleService == NULL) {
947        return HDF_FAILURE;
948    }
949    sampleService->ServiceA();
950    sampleService->ServiceB(5);
951}
952// 订阅过程的实现
953int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
954{
955    if (deviceObject == NULL) {
956        HDF_LOGE("Test driver init failed, deviceObject is null!");
957        return HDF_FAILURE;
958    }
959    struct SubscriberCallback callBack;
960    callBack.deviceObject = deviceObject;
961    callBack.OnServiceConnected = TestDriverSubCallBack;
962    int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);
963    if (ret != HDF_SUCCESS) {
964        HDF_LOGE("Test driver subscribe sample driver failed!");
965    }
966    return ret;
967}
968```
969
970## HDF开发实例
971
972下面基于HDF框架,提供一个完整的样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。
973
974### 添加配置
975
976在HDF框架的配置文件(例如vendor/hisilicon/xxx/hdf_config/device_info)中添加该驱动的配置信息,如下所示:
977
978```
979root {
980    device_info {
981        match_attr = "hdf_manager";
982        template host {
983            hostName = "";
984            priority = 100;
985            template device {
986                template deviceNode {
987                    policy = 0;
988                    priority = 100;
989                    preload = 0;
990                    permission = 0664;
991                    moduleName = "";
992                    serviceName = "";
993                    deviceMatchAttr = "";
994                }
995            }
996        }
997        sample_host :: host {
998            hostName = "sample_host";
999            sample_device :: device {
1000                device0 :: deviceNode {
1001                    policy = 2;
1002                    priority = 100;
1003                    preload = 1;
1004                    permission = 0664;
1005                    moduleName = "sample_driver";
1006                    serviceName = "sample_service";
1007                }
1008            }
1009        }
1010    }
1011}
1012```
1013
1014### 编写驱动代码
1015
1016基于HDF框架编写的sample驱动代码如下:
1017
1018```c
1019#include <fcntl.h>
1020#include <sys/stat.h>
1021#include <sys/ioctl.h>
1022#include "hdf_log.h"
1023#include "hdf_base.h"
1024#include "hdf_device_desc.h"
1025
1026#define HDF_LOG_TAG sample_driver
1027
1028#define SAMPLE_WRITE_READ 123
1029
1030static int32_t HdfSampleDriverDispatch(
1031    struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
1032{
1033    HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id);
1034    if (id == SAMPLE_WRITE_READ) {
1035        const char *readData = HdfSbufReadString(data);
1036        if (readData != NULL) {
1037            HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData);
1038        }
1039        if (!HdfSbufWriteInt32(reply, INT32_MAX)) {
1040            HDF_LOGE("%{public}s: reply int32 fail", __func__);
1041        }
1042        return HdfDeviceSendEvent(client->device, id, data);
1043    }
1044    return HDF_FAILURE;
1045}
1046
1047static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
1048{
1049    // release resources here
1050    return;
1051}
1052
1053static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
1054{
1055    if (deviceObject == NULL) {
1056        return HDF_FAILURE;
1057    }
1058    static struct IDeviceIoService testService = {
1059        .Dispatch = HdfSampleDriverDispatch,
1060    };
1061    deviceObject->service = &testService;
1062    return HDF_SUCCESS;
1063}
1064
1065static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
1066{
1067    if (deviceObject == NULL) {
1068        HDF_LOGE("%{public}s::ptr is null!", __func__);
1069        return HDF_FAILURE;
1070    }
1071    HDF_LOGI("Sample driver Init success");
1072    return HDF_SUCCESS;
1073}
1074
1075static struct HdfDriverEntry g_sampleDriverEntry = {
1076    .moduleVersion = 1,
1077    .moduleName = "sample_driver",
1078    .Bind = HdfSampleDriverBind,
1079    .Init = HdfSampleDriverInit,
1080    .Release = HdfSampleDriverRelease,
1081};
1082
1083HDF_INIT(g_sampleDriverEntry);
1084```
1085
1086### 编写用户程序和驱动交互代码
1087
1088基于HDF框架编写的用户态程序和驱动交互的代码如下(代码可以放在目录drivers/hdf_core/adapter/uhdf下面编译,BUILD.gn可以参考drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn):
1089
1090```c
1091#include <fcntl.h>
1092#include <sys/stat.h>
1093#include <sys/ioctl.h>
1094#include <unistd.h>
1095#include "hdf_log.h"
1096#include "hdf_sbuf.h"
1097#include "hdf_io_service_if.h"
1098
1099#define HDF_LOG_TAG sample_test
1100#define SAMPLE_SERVICE_NAME "sample_service"
1101
1102#define SAMPLE_WRITE_READ 123
1103
1104int g_replyFlag = 0;
1105
1106static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
1107{
1108    const char *string = HdfSbufReadString(data);
1109    if (string == NULL) {
1110        HDF_LOGE("fail to read string in event data");
1111        g_replyFlag = 1;
1112        return HDF_FAILURE;
1113    }
1114    HDF_LOGI("%{public}s: dev event received: %{public}u %{public}s",  (char *)priv, id, string);
1115    g_replyFlag = 1;
1116    return HDF_SUCCESS;
1117}
1118
1119static int SendEvent(struct HdfIoService *serv, char *eventData)
1120{
1121    int ret = 0;
1122    struct HdfSBuf *data = HdfSbufObtainDefaultSize();
1123    if (data == NULL) {
1124        HDF_LOGE("fail to obtain sbuf data");
1125        return 1;
1126    }
1127
1128    struct HdfSBuf *reply = HdfSbufObtainDefaultSize();
1129    if (reply == NULL) {
1130        HDF_LOGE("fail to obtain sbuf reply");
1131        ret = HDF_DEV_ERR_NO_MEMORY;
1132        goto out;
1133    }
1134
1135    if (!HdfSbufWriteString(data, eventData)) {
1136        HDF_LOGE("fail to write sbuf");
1137        ret = HDF_FAILURE;
1138        goto out;
1139    }
1140
1141    ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
1142    if (ret != HDF_SUCCESS) {
1143        HDF_LOGE("fail to send service call");
1144        goto out;
1145    }
1146
1147    int replyData = 0;
1148    if (!HdfSbufReadInt32(reply, &replyData)) {
1149        HDF_LOGE("fail to get service call reply");
1150        ret = HDF_ERR_INVALID_OBJECT;
1151        goto out;
1152    }
1153    HDF_LOGI("Get reply is: %{public}d", replyData);
1154out:
1155    HdfSbufRecycle(data);
1156    HdfSbufRecycle(reply);
1157    return ret;
1158}
1159
1160int main()
1161{
1162    char *sendData = "default event info";
1163    struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
1164    if (serv == NULL) {
1165        HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);
1166        return HDF_FAILURE;
1167    }
1168
1169    static struct HdfDevEventlistener listener = {
1170        .callBack = OnDevEventReceived,
1171        .priv ="Service0"
1172    };
1173
1174    if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
1175        HDF_LOGE("fail to register event listener");
1176        return HDF_FAILURE;
1177    }
1178    if (SendEvent(serv, sendData)) {
1179        HDF_LOGE("fail to send event");
1180        return HDF_FAILURE;
1181    }
1182
1183    while (g_replyFlag == 0) {
1184        sleep(1);
1185    }
1186
1187    if (HdfDeviceUnregisterEventListener(serv, &listener)) {
1188        HDF_LOGE("fail to  unregister listener");
1189        return HDF_FAILURE;
1190    }
1191
1192    HdfIoServiceRecycle(serv);
1193    return HDF_SUCCESS;
1194}
1195```
1196
1197>   ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
1198>   用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:
1199>
1200>   deps = [
1201>
1202>   ​        "//drivers/hdf_core/adapter/uhdf/manager:hdf_core",
1203>
1204>   ​        "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal",
1205>
1206>   ]
1207