1# 驱动开发 2 3 4## 驱动模型介绍 5 6HDF(Hardware Driver Foundation)框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个Host(设备容器)里面,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在不同的Host,主要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host里面,否则部署到独立的Host中是更好的选择。Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。HDF驱动模型如下图所示: 7 8 **图1** HDF驱动模型 9 10 ![HDF驱动模型](figures/HDF驱动模型.png) 11 12 13## 驱动开发步骤 14 15基于HDF框架的驱动开发主要分为三个部分:驱动实现、驱动编译脚本编写和驱动配置。详细开发流程如下所示: 16 171. 驱动实现 18 19 驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下: 20 21 - 驱动业务代码 22 23 ```c 24 #include "hdf_device_desc.h" // HDF框架对驱动开发相关能力接口的头文件 25 #include "hdf_log.h" // HDF框架提供的日志接口头文件 26 27 #define HDF_LOG_TAG sample_driver // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。 28 29 // 将驱动对外提供的服务能力接口绑定到HDF框架。 30 int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) 31 { 32 HDF_LOGD("Sample driver bind success"); 33 return HDF_SUCCESS; 34 } 35 36 // 驱动自身业务初始化的接口 37 int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) 38 { 39 HDF_LOGD("Sample driver Init success"); 40 return HDF_SUCCESS; 41 } 42 43 // 驱动资源释放的接口 44 void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) 45 { 46 HDF_LOGD("Sample driver release success"); 47 return; 48 } 49 ``` 50 - 驱动入口注册到HDF框架 51 52 ```c 53 // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。 54 struct HdfDriverEntry g_sampleDriverEntry = { 55 .moduleVersion = 1, 56 .moduleName = "sample_driver", 57 .Bind = HdfSampleDriverBind, 58 .Init = HdfSampleDriverInit, 59 .Release = HdfSampleDriverRelease, 60 }; 61 62 // 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 63 HDF_INIT(g_sampleDriverEntry); 64 ``` 65 662. 驱动编译脚本编写 67 68 - LiteOS 69 70 涉及Makefile和BUILD.gn修改: 71 72 - Makefile部分: 73 74 驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。 75 76 77 ```c 78 include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # 【必需】导入hdf预定义内容 79 MODULE_NAME := #生成的结果文件 80 LOCAL_INCLUDE := #本驱动的头文件目录 81 LOCAL_SRCS := #本驱动的源代码文件 82 LOCAL_CFLAGS := #自定义的编译选项 83 include $(HDF_DRIVER) #导入Makefile模板完成编译 84 ``` 85 86 编译结果文件链接到内核镜像,添加到**drivers/hdf_core/adapter/khdf/liteos**目录下的**hdf_lite.mk**里面,示例如下: 87 88 89 ```c 90 LITEOS_BASELIB += -lxxx #链接生成的静态库 91 LIB_SUBDIRS += #驱动代码Makefile的目录 92 ``` 93 94 - BUILD.gn部分: 95 96 添加模块BUILD.gn,可参考如下示例: 97 98 99 ```c 100 import("//build/lite/config/component/lite_component.gni") 101 import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni") 102 module_switch = defined(LOSCFG_DRIVERS_HDF_xxx) 103 module_name = "xxx" 104 hdf_driver(module_name) { 105 sources = [ 106 "xxx/xxx/xxx.c", #模块要编译的源码文件 107 ] 108 public_configs = [ ":public" ] #使用依赖的头文件配置 109 } 110 config("public") { #定义依赖的头文件配置 111 include_dirs = [ 112 "xxx/xxx/xxx", #依赖的头文件目录 113 ] 114 } 115 ``` 116 117 把新增模块的BUILD.gn所在的目录添加到**/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**里面: 118 119 120 ```c 121 group("liteos") { 122 public_deps = [ ":$module_name" ] 123 deps = [ 124 "xxx/xxx", #新增模块BUILD.gn所在的目录,目录结构相对于/drivers/hdf_core/adapter/khdf/liteos 125 ] 126 } 127 ``` 128 - Linux 129 130 如果需要定义模块控制宏,需要在模块目录xxx里面添加Kconfig文件,并把Kconfig文件路径添加到**drivers/hdf_core/adapter/khdf/linux/Kconfig**里面: 131 132 133 ```c 134 source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录 135 ``` 136 137 添加模块目录到**drivers/hdf_core/adapter/khdf/linux/Makefile**: 138 139 140 ```c 141 obj-$(CONFIG_DRIVERS_HDF) += xxx/ 142 ``` 143 144 在模块目录xxx里面添加Makefile文件,在Makefile文件里面添加模块代码编译规则: 145 146 147 ```c 148 obj-y += xxx.o 149 ``` 150 1513. 驱动配置 152 153 HDF使用HCS作为配置描述源码,HCS详细介绍参考[配置管理](../driver/driver-hdf-manage.md)介绍。 154 155 驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下: 156 157 - 驱动设备描述(必选) 158 159 HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述。驱动的设备描述填写如下所示: 160 161 162 ``` 163 root { 164 device_info { 165 match_attr = "hdf_manager"; 166 template host { // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省。 167 hostName = ""; 168 priority = 100; 169 uid = ""; // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。 170 gid = ""; // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。 171 caps = [""]; // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。 172 template device { 173 template deviceNode { 174 policy = 0; 175 priority = 100; 176 preload = 0; 177 permission = 0664; 178 moduleName = ""; 179 serviceName = ""; 180 deviceMatchAttr = ""; 181 } 182 } 183 } 184 sample_host :: host{ 185 hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器。 186 priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。 187 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // 用户态进程Linux capabilities配置。 188 device_sample :: device { // sample设备节点 189 device0 :: deviceNode { // sample驱动的DeviceNode节点 190 policy = 1; // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。 191 priority = 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。 192 preload = 0; // 驱动按需加载字段。 193 permission = 0664; // 驱动创建设备节点权限 194 moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 195 serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一。 196 deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。 197 } 198 } 199 } 200 } 201 } 202 ``` 203 204 > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br> 205 > 206 > - uid、gid、caps等配置项是用户态驱动的启动配置,内核态不用配置。 207 > 208 > - 根据进程权限最小化设计原则,业务模块uid、gid不用配置,如上面的sample_host,使用普通用户权限,即uid和gid被定义为hostName的定义值。 209 > 210 > - 如果普通用户权限不能满足业务要求,需要把uid、gid定义为system或者root权限时,请找安全专家进行评审。 211 > 212 > - 进程的uid在文件**base/startup/init/services/etc/passwd**中配置,进程的gid在文件**base/startup/init/services/etc/group**中配置,进程uid和gid配置参考:[系统服务用户组添加方法](https://gitee.com/openharmony/startup_init_lite/wikis)。 213 > 214 > - caps值:格式为caps = ["xxx"],如果要配置CAP_DAC_OVERRIDE,此处需要填写caps = ["DAC_OVERRIDE"],不能填写为caps = ["CAP_DAC_OVERRIDE"]。 215 > 216 > - preload:驱动按需加载字段,参考[驱动加载](../driver/driver-hdf-load.md)。 217 218 219 - 驱动私有配置信息(可选) 220 221 如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息。HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init(参考步骤1)传递给驱动。驱动的配置信息示例如下: 222 223 224 ``` 225 root { 226 SampleDriverConfig { 227 sample_version = 1; 228 sample_bus = "I2C_0"; 229 match_attr = "sample_config"; // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致 230 } 231 } 232 ``` 233 234 配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs,示例如下: 235 236 237 ``` 238 #include "device_info/device_info.hcs" 239 #include "sample/sample_config.hcs" 240 ``` 241 242