• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 平台驱动开发示例<a name="ZH-CN_TOPIC_0000001157064271"></a>
2
3## 概述<a name="section194201316174215"></a>
4
5本文档将以I2C驱动为例,介绍如何基于HDF驱动框架完成平台驱动开发。
6
7>![](../public_sys-resources/icon-caution.gif) **注意:**
8>本例仅作为平台驱动开发示例参考,开发者不可直接用于商用集成。
9
10HDF驱动框架为常用外围设备提供了标准的驱动框架,驱动开发者只需将驱动适配至HDF驱动框架,即可通过HDF驱动框架提供的接口操作外围设备。
11
12本文以I2C为例。其时序流程如[图1](#fig9596628607)所示。
13
14**图 1**  I2C时序流程图<a name="fig9596628607"></a>
15
16![](figures/I2C时序流程图.png "I2C时序流程图")
17
18## 环境准备<a name="section6926133918422"></a>
19
20环境准备具体操作请参考[Hi3516标准系统入门](../quick-start/quickstart-appendix-hi3516-ide.md)完成环境搭建。
21
22>![](../public_sys-resources/icon-notice.gif) **须知:**
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>![](../public_sys-resources/icon-caution.gif) **注意:**
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    >![](../public_sys-resources/icon-notice.gif) **须知:**
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