1# 平台驱动开发示例<a name="ZH-CN_TOPIC_0000001157064271"></a> 2 3## 概述<a name="section194201316174215"></a> 4 5本文档将以I2C驱动为例,介绍如何基于HDF驱动框架完成平台驱动开发。 6 7> **注意:** 8>本例仅作为平台驱动开发示例参考,开发者不可直接用于商用集成。 9 10HDF驱动框架为常用外围设备提供了标准的驱动框架,驱动开发者只需将驱动适配至HDF驱动框架,即可通过HDF驱动框架提供的接口操作外围设备。 11 12本文以I2C为例。其时序流程如[图1](#fig9596628607)所示。 13 14**图 1** I2C时序流程图<a name="fig9596628607"></a> 15 16 17 18## 环境准备<a name="section6926133918422"></a> 19 20环境准备具体操作请参考[Hi3516标准系统入门](../quick-start/quickstart-appendix-hi3516-ide.md)完成环境搭建。 21 22> **须知:** 23>本示例针对OpenHarmony轻量系统、小型系统、标准系统都适用,本文以标准系统为例。其他系统的开发者可参考对应系统的指导文档进行环境搭建。 24 25## 开发<a name="section65801539470"></a> 26 27### 文件说明<a name="section0708184454414"></a> 28 29本例中涉及的文件及路径如下表: 30 31**表 1** 文件说明 32 33| 说明 | 文件路径 | 操作 | 34| --------- | ------------| -------- | 35| 示例文件 | /drivers/adapter/khdf/linux/platform/i2c/i2c_sample.c | 新增文件 | 36| 设备服务文件 | /drivers/adapter/khdf/linux/hcs/device_info/device_info.hcs | 追加内容 | 37| 配置参数文件 | /drivers/adapter/khdf/linux/hcs/platform/i2c_config.hcs | 追加内容 | 38| 编译文件 | /drivers/adapter/khdf/linux/platform/i2c/Makefile | 追加内容 | 39| 依赖头文件 | /drivers/framework/include/core/hdf_device_desc.h | 作为头文件引用 | 40| 核心层头文件 | /drivers/framework/support/platform/include/i2c_core.h | 作为头文件引用 | 41| HCS配置入口文件 | /drivers/adapter/khdf/linux/hcs/hdf.hcs | HCS配置文件总入口 | 42 43 44> **注意:** 45>本例程涉及的文件路径均作为演示,驱动开发者应根据实际情况确定具体的源文件存放位置。 46 47### 实例化驱动入口<a name="section85325864412"></a> 48 49实例化一个HdfDriverEntry 对象作为驱动入口。驱动入口必须为HdfDriverEntry(在hdf\_device\_desc.h中定义)类型的全局变量,且moduleName要和device\_info.hcs中保持一致。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 50 51I2C驱动中没有实现Bind方法,因为I2C控制器由manager管理,而在manager中已经实现了Bind方法,因此I2C驱动中无需再绑定服务。 52 53实例化驱动入口的示例代码如下: 54 55``` 56/* 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量 */ 57struct HdfDriverEntry g_sampleI2cDriverEntry = { 58 .moduleVersion = 1, 59 .Init = SampleI2cInit, 60 .Release = SampleI2cRelease, 61 .moduleName = "demo_i2c_driver", 62}; 63/* 调用HDF_INIT将驱动入口注册到HDF框架中 */ 64HDF_INIT(g_sampleI2cDriverEntry); 65``` 66 67### 设置相关参数<a name="section8155172019453"></a> 68 69通过配置device\_info.hcs,并从HCS获取并解析设备的配置参数以确保驱动能够正确加载。 70 711. 添加设备服务节点(必选)。 72 73 编辑device\_info.hcs,在device\_i2c :: device下添加驱动设备服务节点,示例如下: 74 75 ``` 76 root { 77 device_info { 78 match_attr = "hdf_manager"; 79 device_i2c :: device { // i2c设备节点。 80 device2 :: deviceNode { // i2c驱动的DeviceNode节点。 81 policy = 0; // policy字段是驱动服务发布的策略。 82 priority = 55; // 驱动启动优先级。 83 permission = 0644; // 驱动创建设备节点权限。 84 moduleName = "demo_i2c_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 85 serviceName = "DEMO_I2C_DRIVER"; // 驱动对外发布服务的名称,必须唯一。 86 deviceMatchAttr = "demo_i2c_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的。 87 // match_attr值相等。 88 } 89 } 90 } 91 } 92 93 ``` 94 95 > **须知:** 96 >配置文件中的priority(取值范围为整数0到200)是用来表示host和驱动的优先级,不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高,驱动的priority值相同则不保证加载顺序。 97 982. 添加配置参数(可选)。 99 100 有时驱动可能会需要私有配置信息,以确保寄存器的配置可以满足不同产品的需求。如需要私有配置信息,则可以添加一个驱动的配置文件,用来存放一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init(参考[驱动开发](../driver/driver-overview-foundation.md))传递给驱动。驱动开发者可新建配置文件,并在板级驱动hdf.hcs中引用新建的配置文件,本例中直接在原有的配置文件i2c\_config.hcs内添加配置参数。 101 102 本例中编辑i2c\_config.hcs,添加配置参数: 103 104 ``` 105 root { 106 platform { 107 i2c_config_demo { 108 match_attr = "demo_i2c_config"; // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致。 109 110 template i2c_controller { // 参数模板。 111 bus = 0; 112 reg_pbase = 0x120b0000; 113 reg_size = 0xd1; 114 } 115 116 controller_demo_0 :: i2c_controller { // 两个I2C示例控制器。 117 bus = 8; 118 } 119 controller_demo_1 :: i2c_controller { 120 bus = 9; 121 } 122 } 123 } 124 } 125 ``` 126 127 match\_attr字段必须与device\_info.hcs中的deviceMatch\_Attr保持一致,在此文件中配置驱动需要的参数,通过match\_attr可匹配至对应的驱动,该驱动即可在Bind或Init中调用DeviceResourceGetIfaceInstance\(\)函数获取这些配置参数。 128 129 若配置文件为新文件,则需要在板级配置入口文件hdf.hcs中引用该配置文件,例如: 130 131 ``` 132 #include "device_info/device_info.hcs" 133 #include "i2c/i2c_config.hcs" 134 ``` 135 136 由于本例中在原有的i2c\_config.hcs内添加配置参数,没有新建配置文件,因此无需再将i2c\_config.hcs添加至板级配置入口文件中。 137 1383. 驱动从HCS获取配置参数。 139 140 在本例中,驱动需要通过HCS获取寄存器物理基地址、寄存器大小、总线号等参数,从而对控制器进行正确配置。 141 142 ``` 143 /* 从HCS获取配置参数 */ 144 static int32_t SampleI2cReadDrs(struct SampleI2cCntlr *sampleCntlr, const struct DeviceResourceNode *node) 145 { 146 int32_t ret; 147 struct DeviceResourceIface *drsOps = NULL; 148 149 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 150 if (drsOps == NULL || drsOps->GetUint32 == NULL) { // 确保GetUint32方法可用。 151 HDF_LOGE("%s: invalid drs ops fail!", __func__); 152 return HDF_FAILURE; 153 } 154 155 ret = drsOps->GetUint32(node, "reg_pbase", &sampleCntlr->regBasePhy, 0); // 从HCS读取物理基地址reg_pbase。 156 if (ret != HDF_SUCCESS) { 157 HDF_LOGE("%s: read regBase fail!", __func__); 158 return ret; 159 } 160 161 ret = drsOps->GetUint16(node, "reg_size", &sampleCntlr->regSize, 0); // 从HCS读取寄存器大小reg_size。 162 if (ret != HDF_SUCCESS) { 163 HDF_LOGE("%s: read regsize fail!", __func__); 164 return ret; 165 } 166 167 ret = drsOps->GetUint16(node, "bus", (uint16_t *)&sampleCntlr->bus, 0); // 从HCS读取总线号bus。 168 if (ret != HDF_SUCCESS) { 169 HDF_LOGE("%s: read bus fail!", __func__); 170 return ret; 171 } 172 173 return HDF_SUCCESS; 174 } 175 ``` 176 177 178### 添加控制器<a name="section1335374114452"></a> 179 180初始化控制器硬件,并调用核心层接口完成向核心层添加、删除设备,以及钩子函数的实现等。 181 1821. 定义结构体,实现钩子函数并赋值至函数指针。 183 184 I2cMethod结构体在i2c\_core.h中定义,其中通过函数指针的方式定义了I2C需要实现的方法,transfer方法为用于传输的钩子函数,在驱动中需要做具体实现并对函数指针赋值。 185 186 示例代码如下: 187 188 ``` 189 /* 自定义设备结构体,继承父类I2cCntlr */ 190 struct SampleI2cCntlr { 191 struct I2cCntlr cntlr; 192 OsalSpinlock spin; 193 volatile unsigned char *regBase; 194 uint16_t regSize; 195 int16_t bus; 196 uint32_t regBasePhy; 197 }; 198 199 /* 消息结构体,继承父类I2cMsg */ 200 struct SampleTransferData { 201 struct I2cMsg *msgs; 202 int16_t index; 203 int16_t count; 204 }; 205 /* 钩子函数实现 */ 206 static int32_t SampleI2cTransfer(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int16_t count) 207 { 208 int32_t ret = HDF_SUCCESS; 209 struct SampleI2cCntlr *sampleCntlr = NULL; 210 struct SampleTransferData td; 211 212 if (cntlr == NULL || cntlr->priv == NULL) { 213 HDF_LOGE("SampleI2cTransfer: cntlr lor sampleCntlr is null!"); 214 return HDF_ERR_INVALID_OBJECT; 215 } 216 sampleCntlr = (struct SampleI2cCntlr *)cntlr; 217 218 if (msgs == NULL || count <= 0) { 219 HDF_LOGE("SampleI2cTransfer: err parms! count:%d", count); 220 return HDF_ERR_INVALID_PARAM; 221 } 222 td.msgs = msgs; 223 td.count = count; 224 td.index = 0; 225 226 HDF_LOGE("Successfully transmitted!"); // 表示此处传输成功。 227 228 td.index = count; // 经过处理,最后实际发送msg个数等于count,返回已发送个数,此句代替已省略的处理过程。 229 return (td.index > 0) ? td.index : ret; 230 } 231 /* 钩子函数赋值 */ 232 static struct I2cMethod g_method = { 233 .transfer = SampleI2cTransfer, 234 }; 235 ``` 236 2372. 编写驱动初始化函数。 238 239 本例中使用SampleI2cInit作为驱动初始化函数的函数名(函数名称可由驱动开发者确定),该函数需要在驱动入口结构体中赋值给Init,以供HDF驱动框架调用从而达到初始化驱动的目的。该函数中需要对从HCS获取的配置参数进行解析,并按照这些参数创建控制器。示例如下: 240 241 ``` 242 /* 解析参数,申请内存并创建控制器 */ 243 static int32_t SampleI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) 244 { 245 int32_t ret; 246 struct SampleI2cCntlr *sampleCntlr = NULL; 247 (void)device; 248 249 sampleCntlr = (struct SampleI2cCntlr *)OsalMemCalloc(sizeof(*sampleCntlr)); 250 if (sampleCntlr == NULL) { 251 HDF_LOGE("%s: malloc sampleCntlr fail!", __func__); 252 return HDF_ERR_MALLOC_FAIL; 253 } 254 255 ret = SampleI2cReadDrs(sampleCntlr, node); // 从HCS获取配置参数。 256 if (ret != HDF_SUCCESS) { 257 HDF_LOGE("%s: read drs fail! ret:%d", __func__, ret); 258 goto __ERR__; 259 } 260 261 sampleCntlr->regBase = OsalIoRemap(sampleCntlr->regBasePhy, sampleCntlr->regSize); 262 if (sampleCntlr->regBase == NULL) { 263 HDF_LOGE("%s: ioremap regBase fail!", __func__); 264 ret = HDF_ERR_IO; 265 goto __ERR__; 266 } 267 268 HDF_LOGE("The controller has been initialized!"); // 表示此处省略的控制器初始化操作已经成功。 269 270 sampleCntlr->cntlr.priv = (void *)node; 271 sampleCntlr->cntlr.busId = sampleCntlr->bus; 272 sampleCntlr->cntlr.ops = &g_method; 273 (void)OsalSpinInit(&sampleCntlr->spin); // 初始化自旋锁。 274 ret = I2cCntlrAdd(&sampleCntlr->cntlr); // 向核心层添加控制器。 275 if (ret != HDF_SUCCESS) { 276 HDF_LOGE("%s: add i2c controller fail:%d!", __func__, ret); 277 goto __ERR__; 278 } 279 280 return HDF_SUCCESS; 281 __ERR__: // 错误处理。 282 if (sampleCntlr != NULL) { 283 if (sampleCntlr->regBase != NULL) { 284 OsalIoUnmap((void *)sampleCntlr->regBase); // 取消地址映射。 285 sampleCntlr->regBase = NULL; 286 } 287 OsalMemFree(sampleCntlr); // 释放内存。 288 sampleCntlr = NULL; 289 } 290 return ret; 291 } 292 /* 驱动入口初始化函数 */ 293 static int32_t SampleI2cInit(struct HdfDeviceObject *device) 294 { 295 int32_t ret; 296 const struct DeviceResourceNode *childNode = NULL; 297 298 HDF_LOGE("%s: Enter", __func__); 299 if (device == NULL || device->property == NULL) { 300 HDF_LOGE("%s: device or property is NULL", __func__); 301 return HDF_ERR_INVALID_OBJECT; 302 } 303 304 ret = HDF_SUCCESS; 305 DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 306 ret = SampleI2cParseAndInit(device, childNode); // 调用解析参数和创建控制器的函数。 307 if (ret != HDF_SUCCESS) { 308 break; 309 } 310 } 311 return ret; 312 } 313 ``` 314 3153. 编写驱动释放函数。 316 317 本例中使用SampleI2cRelease作为驱动释放函数的函数名(函数名称可由驱动开发者确定),该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,将调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。示例如下: 318 319 ``` 320 /* 删除控制器函数 */ 321 static void SampleI2cRemoveByNode(const struct DeviceResourceNode *node) 322 { 323 int32_t ret; 324 int16_t bus; 325 struct I2cCntlr *cntlr = NULL; 326 struct SampleI2cCntlr *sampleCntlr = NULL; 327 struct DeviceResourceIface *drsOps = NULL; 328 329 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 330 if (drsOps == NULL || drsOps->GetUint32 == NULL) { 331 HDF_LOGE("%s: invalid drs ops fail!", __func__); 332 return; 333 } 334 335 ret = drsOps->GetUint16(node, "bus", (uint16_t *)&bus, 0); // 从HCS获取I2C总线号。 336 if (ret != HDF_SUCCESS) { 337 HDF_LOGE("%s: read bus fail!", __func__); 338 return; 339 } 340 341 cntlr = I2cCntlrGet(bus); 342 if (cntlr != NULL && cntlr->priv == node) { // 根据I2C总线号删除控制器。 343 I2cCntlrPut(cntlr); 344 I2cCntlrRemove(cntlr); 345 sampleCntlr = (struct SampleI2cCntlr *)cntlr; 346 OsalIoUnmap((void *)sampleCntlr->regBase); 347 OsalMemFree(sampleCntlr); 348 } 349 return; 350 } 351 /* 释放资源 */ 352 static void SampleI2cRelease(struct HdfDeviceObject *device) 353 { 354 const struct DeviceResourceNode *childNode = NULL; 355 356 HDF_LOGI("%s: enter", __func__); 357 358 if (device == NULL || device->property == NULL) { 359 HDF_LOGE("%s: device or property is NULL", __func__); 360 return; 361 } 362 363 DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 364 SampleI2cRemoveByNode(childNode); // 调用删除控制器函数。 365 } 366 } 367 ``` 368 369 370## 编译及烧录<a name="section164824754712"></a> 371 3721. 编辑Makefile,添加源文件: 373 374 ``` 375 include drivers/hdf/khdf/platform/platform.mk 376 377 obj-y += $(HDF_PLATFORM_FRAMEWORKS_ROOT)/src/i2c_core.o \ 378 $(HDF_PLATFORM_FRAMEWORKS_ROOT)/src/i2c_if.o \ 379 ./i2c_adapter.o \ 380 ./i2c_sample.o 381 ``` 382 383 "./i2c\_sample.o"为本示例中在Makefile中追加的内容。 384 3852. 编译及烧录。 386 387 - 安装包方式具体操作请参考标准系统快速入门[编译](../quick-start/quickstart-appendix-hi3516-pkg.md#编译)和[烧录](../quick-start/quickstart-appendix-hi3516-pkg.md#烧录)。 388 389 - IDE方式具体操作请参考标准系统快速入门[编译](../quick-start/quickstart-appendix-hi3516-ide.md#编译)和[烧录](../quick-start/quickstart-appendix-hi3516-ide.md#烧录)。 390 391 392