driver-hdf-development.md 11.5 KB
Newer Older
D
duangavin123 已提交
1
# 驱动开发
W
wenjun 已提交
2

N
NEEN 已提交
3

D
duangavin123 已提交
4
## 驱动模型介绍
W
wenjun 已提交
5

6
HDF(Hardware Driver Foundation)框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node。HDF驱动模型如下图所示:
W
wenjun 已提交
7

D
duangavin123 已提交
8
  **图1** HDF驱动模型
W
wenjun 已提交
9

10
  ![HDF驱动模型](figures/HDF驱动模型.png)
W
wenjun 已提交
11 12


D
duangavin123 已提交
13
## 驱动开发步骤
W
wenjun 已提交
14

15
基于HDF框架进行驱动的开发主要分为两个部分,驱动实现和驱动配置。详细开发流程如下所示:
W
wenjun 已提交
16

D
duangavin123 已提交
17
1. 驱动实现
18

D
duangavin123 已提交
19
   驱动实现包含驱动业务代码和驱动入口注册,具体写法如下:
W
wenjun 已提交
20

D
duangavin123 已提交
21
   - 驱动业务代码
W
wenjun 已提交
22
        
D
duangavin123 已提交
23 24
      ```
      #include "hdf_device_desc.h"  // HDF框架对驱动开发相关能力接口的头文件
25
      #include "hdf_log.h"          // HDF框架提供的日志接口头文件
D
duangavin123 已提交
26 27 28
      
      #define HDF_LOG_TAG "sample_driver"   // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签
      
29
      // 驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
D
duangavin123 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
      int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
      {
          HDF_LOGD("Sample driver bind success");
          return 0;
      }
      
      // 驱动自身业务初始的接口
      int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
      {
          HDF_LOGD("Sample driver Init success");
          return 0;
      }
      
      // 驱动资源释放的接口
      void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
      {
          HDF_LOGD("Sample driver release success");
          return;
      }
      ```
   - 驱动入口注册到HDF框架
W
wenjun 已提交
51
        
D
duangavin123 已提交
52 53 54 55 56 57 58 59 60 61
      ```
      // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
      struct HdfDriverEntry g_sampleDriverEntry = {
          .moduleVersion = 1,
          .moduleName = "sample_driver",
          .Bind = HdfSampleDriverBind,
          .Init = HdfSampleDriverInit,
          .Release = HdfSampleDriverRelease,
      };
      
62
      // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出
D
duangavin123 已提交
63 64
      HDF_INIT(g_sampleDriverEntry);
      ```
W
wenjun 已提交
65

Z
zhang 已提交
66
2. 驱动编译
Z
zhang 已提交
67
   - liteos
68

D
duangavin123 已提交
69 70 71
      涉及makefile和BUILD.gn修改:

      - makefile部分:
72 73

        驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。
D
duangavin123 已提交
74 75 76 77 78 79 80 81

           
         ```
         include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk #导入hdf预定义内容,必需
         MODULE_NAME :=    #生成的结果文件
         LOCAL_INCLUDE :=  #本驱动的头文件目录
         LOCAL_SRCS :=     #本驱动的源代码文件
         LOCAL_CFLAGS :=  #自定义的编译选项
82
         include $(HDF_DRIVER) #导入Makefile模板完成编译
D
duangavin123 已提交
83 84
         ```

85
        编译结果文件链接到内核镜像,添加到drivers/adapter/khdf/liteos目录下的hdf_lite.mk里面,示例如下:
D
duangavin123 已提交
86 87 88 89 90 91 92 93

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

      - BUILD.gn部分:
94 95

        添加模块BUILD.gn参考定义如下内容:
D
duangavin123 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

           
         ```
         import("//build/lite/config/component/lite_component.gni")
         import("//drivers/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", #依赖的头文件目录
             ]
         }
         ```

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

           
         ```
         group("liteos") {
                public_deps = [ ":$module_name" ]
                    deps = [
                       "xxx/xxx",   #新增模块BUILD.gn所在的目录,目录结构相对于/drivers/adapter/khdf/liteos
                   ]
         }
         ```
   - linux
128

D
duangavin123 已提交
129
      如果需要定义模块控制宏,需要在模块目录xxx里面添加Kconfig文件,并把Kconfig文件路径添加到drivers/adapter/khdf/linux/Kconfig里面:
Z
zhang 已提交
130

D
duangavin123 已提交
131 132 133 134
        
      ```
      source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录
      ```
Z
zhang 已提交
135

D
duangavin123 已提交
136
      添加模块目录到drivers/adapter/khdf/linux/Makefile:
Z
zhang 已提交
137

D
duangavin123 已提交
138 139 140 141 142 143
        
      ```
      obj-$(CONFIG_DRIVERS_HDF)  += xxx/
      ```

      在模块目录xxx里面添加Makefile文件,在Makefile文件里面添加模块代码编译规则:
Z
zhang 已提交
144

D
duangavin123 已提交
145 146 147 148
        
      ```
      obj-y  += xxx.o
      ```
Z
zhang 已提交
149

D
duangavin123 已提交
150
3. 驱动配置
151

D
duangavin123 已提交
152
   HDF使用HCS作为配置描述源码,HCS详细介绍参考[配置管理](../driver/driver-hdf-manage.md)介绍。
Z
zhang 已提交
153

D
duangavin123 已提交
154
   驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下:
Z
zhang 已提交
155

D
duangavin123 已提交
156
   - 驱动设备描述(必选)
157 158

     HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述,驱动的设备描述填写如下所示:
D
duangavin123 已提交
159 160 161 162 163 164 165 166 167

        
      ```
      root {
          device_info {
              match_attr = "hdf_manager";
              template host {       // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省
                  hostName = "";
                  priority = 100;
168 169 170
                  uid = "";        // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户
                  gid = "";        // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组
                  caps = [""];     // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置
D
duangavin123 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
                  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的加载顺序
186
                  caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];   // 用户态进程Linux capabilities配置
D
duangavin123 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
                  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值相等
                      }
                  }
              }
          }
      }
      ```
202 203 204 205 206 207 208 209 210 211 212 213
      说明:

      uid、gid、caps等配置项是用户态驱动的启动配置,内核态不用配置。

      根据进程权限最小化设计原则,业务模块uid、gid不用配置,如上面的sample_host,使用普通用户权限,即uid和gid被定义为hostName的定义值。

      如果普通用户权限不能满足业务要求,需要把uid、gid定义为system或者root权限时,请找安全专家进行评审。

      进程的uid在文件base/startup/init_lite/services/etc/passwd中配置,进程的gid在文件base/startup/init_lite/services/etc/group中配置,进程uid和gid配置参考:[系统服务用户组添加方法](https://gitee.com/openharmony/startup_init_lite/wikis)。

      caps值:比如业务模块要配置CAP_DAC_OVERRIDE,此处需要填写 caps = ["DAC_OVERRIDE"],不能填写为caps = ["CAP_DAC_OVERRIDE"]。

D
duangavin123 已提交
214
   - 驱动私有配置信息(可选)
215

216
     如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init(参考步骤1)传递给驱动,驱动的配置信息示例如下:
D
duangavin123 已提交
217 218 219 220 221 222 223

        
      ```
      root {
          SampleDriverConfig {
              sample_version = 1;
              sample_bus = "I2C_0";
224
              match_attr = "sample_config";   // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
D
duangavin123 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237
          }
      }
      ```

      配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs(这一块可以通过OpenHarmony驱动子系统在DevEco集成驱动开发套件工具一键式配置,具体使用方法参考驱动开发套件中的介绍),示例如下:

        
      ```
      #include "device_info/device_info.hcs"
      #include "sample/sample_config.hcs"
      ```


238
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
D
duangavin123 已提交
239 240 241 242 243 244 245 246 247 248 249 250 251
> 驱动加载方式支持按需加载和按序加载两种方式,具体使用方法如下:
> 
> - 按需加载
>     
>   ```
>   typedef enum {
>       DEVICE_PRELOAD_ENABLE = 0,
>       DEVICE_PRELOAD_ENABLE_STEP2,
>       DEVICE_PRELOAD_DISABLE,
>       DEVICE_PRELOAD_INVALID
>   } DevicePreload;
>   ```
> 
252
>   配置文件中preload字段配成0(DEVICE_PRELOAD_ENABLE),则系统启动过程中默认加载;配成1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快启的时候,则在系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同;配成2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务(参考[消息机制](../driver/driver-hdf-message-management.md))时,如果驱动服务不存在,HDF框架会尝试动态加载该驱动。
D
duangavin123 已提交
253 254
> 
> - 按序加载(需要驱动为默认加载)
255
>   配置文件中的priority(取值范围为整数0到200)是用来表示host和驱动的优先级。不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。