• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Development Example for Platform Drivers<a name="EN-US_TOPIC_0000001157064271"></a>
2
3-   [Overview](#section194201316174215)
4-   [Preparations](#section6926133918422)
5-   [Development](#section65801539470)
6    -   [File Description](#section0708184454414)
7    -   [Instantiating the Driver Entry](#section85325864412)
8    -   [Setting Related Parameters](#section8155172019453)
9    -   [Adding a Controller](#section1335374114452)
10
11-   [Building Source Code and Burning Images](#section164824754712)
12
13## Overview<a name="section194201316174215"></a>
14
15This document uses the I2C driver as an example to describe how to develop platform drivers based on the hardware driver foundation \(HDF\).
16
17>![](../public_sys-resources/icon-caution.gif) **CAUTION:**
18>The sample code in this document is for reference only and cannot be directly used for commercial integration.
19
20The HDF provides a standard driver framework for common peripherals. To use the APIs provided by the HDF to perform operations on peripherals, you only need to adapt the specific driver to the HDF.
21
22In this example, an I2C driver is used.  [Figure 1](#fig148041484161)  shows the sequence diagram of the I2C driver.
23
24**Figure  1**  I2C driver sequence diagram<a name="fig148041484161"></a>
25
26
27![](figure/en-us_image_0000001169991055.png)
28
29-   User Business: business-triggered driver
30-   i2cManagerEntry: entry to the I2C manager, which is used to register the I2C manager with the HDF
31-   I2cManager: I2C manager, which manages the I2C controller
32-   I2cCntlr: I2C controller
33-   i2cDriverEntry: entry to the I2C controller, which is used to register the I2C controller with the HDF
34
35## Preparations<a name="section6926133918422"></a>
36
37Follow the instructions in  [Environment Setup for Standard System](../quick-start/quickstart-standard-overview.md).
38
39>![](../public_sys-resources/icon-notice.gif) **NOTICE:**
40>This development example applies to standard, small, and mini OpenHarmony systems. The following sections use the standard system as an example. You can refer to the specific guide for your system to set up the environment.
41
42## Development<a name="section65801539470"></a>
43
44### File Description<a name="section0708184454414"></a>
45
46The following table lists the files involved in this example and their paths.
47
48**Table  1**  File description
49
50<a name="table15887645104012"></a>
51<table><thead align="left"><tr id="row198881452404"><th class="cellrowborder" align="center" valign="top" width="13.489999999999998%" id="mcps1.2.4.1.1"><p id="p158742406488"><a name="p158742406488"></a><a name="p158742406488"></a>File</p>
52</th>
53<th class="cellrowborder" align="center" valign="top" width="68.52000000000001%" id="mcps1.2.4.1.2"><p id="p6975142717432"><a name="p6975142717432"></a><a name="p6975142717432"></a>File Path</p>
54</th>
55<th class="cellrowborder" align="center" valign="top" width="17.990000000000002%" id="mcps1.2.4.1.3"><p id="p98891454405"><a name="p98891454405"></a><a name="p98891454405"></a>Remarks</p>
56</th>
57</tr>
58</thead>
59<tbody><tr id="row1088914458407"><td class="cellrowborder" align="left" valign="top" width="13.489999999999998%" headers="mcps1.2.4.1.1 "><p id="p78741540104813"><a name="p78741540104813"></a><a name="p78741540104813"></a>Sample file</p>
60</td>
61<td class="cellrowborder" align="left" valign="top" width="68.52000000000001%" headers="mcps1.2.4.1.2 "><p id="p1066541692916"><a name="p1066541692916"></a><a name="p1066541692916"></a>/drivers/adapter/khdf/linux/platform/i2c/i2c_sample.c</p>
62</td>
63<td class="cellrowborder" align="left" valign="top" width="17.990000000000002%" headers="mcps1.2.4.1.3 "><p id="p208891445144012"><a name="p208891445144012"></a><a name="p208891445144012"></a>New file</p>
64</td>
65</tr>
66<tr id="row1388984594013"><td class="cellrowborder" align="left" valign="top" width="13.489999999999998%" headers="mcps1.2.4.1.1 "><p id="p88741840104811"><a name="p88741840104811"></a><a name="p88741840104811"></a>Device service file</p>
67</td>
68<td class="cellrowborder" align="left" valign="top" width="68.52000000000001%" headers="mcps1.2.4.1.2 "><p id="p486417183298"><a name="p486417183298"></a><a name="p486417183298"></a>/drivers/adapter/khdf/linux/hcs/device_info/device_info.hcs</p>
69</td>
70<td class="cellrowborder" rowspan="3" align="left" valign="top" width="17.990000000000002%" headers="mcps1.2.4.1.3 "><p id="p128898458401"><a name="p128898458401"></a><a name="p128898458401"></a></p>
71<p id="p168904455404"><a name="p168904455404"></a><a name="p168904455404"></a>New content will be added to these files.</p>
72<p id="p7890124516405"><a name="p7890124516405"></a><a name="p7890124516405"></a></p>
73</td>
74</tr>
75<tr id="row9889164513406"><td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.1 "><p id="p138741640124812"><a name="p138741640124812"></a><a name="p138741640124812"></a>Configuration file</p>
76</td>
77<td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.2 "><p id="p26905191293"><a name="p26905191293"></a><a name="p26905191293"></a>/drivers/adapter/khdf/linux/hcs/platform/i2c_config.hcs</p>
78</td>
79</tr>
80<tr id="row1189044513404"><td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.1 "><p id="p1687424074814"><a name="p1687424074814"></a><a name="p1687424074814"></a>Build file</p>
81</td>
82<td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.2 "><p id="p1885032192917"><a name="p1885032192917"></a><a name="p1885032192917"></a>/drivers/adapter/khdf/linux/platform/i2c/Makefile</p>
83</td>
84</tr>
85<tr id="row10890144564011"><td class="cellrowborder" align="left" valign="top" width="13.489999999999998%" headers="mcps1.2.4.1.1 "><p id="p118752040104810"><a name="p118752040104810"></a><a name="p118752040104810"></a>Dependency</p>
86</td>
87<td class="cellrowborder" align="left" valign="top" width="68.52000000000001%" headers="mcps1.2.4.1.2 "><p id="p15821718182916"><a name="p15821718182916"></a><a name="p15821718182916"></a>/drivers/framework/include/core/hdf_device_desc.h</p>
88</td>
89<td class="cellrowborder" rowspan="2" align="left" valign="top" width="17.990000000000002%" headers="mcps1.2.4.1.3 "><p id="p989012451401"><a name="p989012451401"></a><a name="p989012451401"></a>Header file to be included</p>
90<p id="p1890134594014"><a name="p1890134594014"></a><a name="p1890134594014"></a></p>
91</td>
92</tr>
93<tr id="row6890164564015"><td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.1 "><p id="p128756401484"><a name="p128756401484"></a><a name="p128756401484"></a>Core-layer header file</p>
94</td>
95<td class="cellrowborder" align="left" valign="top" headers="mcps1.2.4.1.2 "><p id="p47681122152918"><a name="p47681122152918"></a><a name="p47681122152918"></a>/drivers/framework/support/platform/include/i2c_core.h</p>
96</td>
97</tr>
98<tr id="row1499682234817"><td class="cellrowborder" align="left" valign="top" width="13.489999999999998%" headers="mcps1.2.4.1.1 "><p id="p1187513403487"><a name="p1187513403487"></a><a name="p1187513403487"></a>HCS configuration entry file</p>
99</td>
100<td class="cellrowborder" align="left" valign="top" width="68.52000000000001%" headers="mcps1.2.4.1.2 "><p id="p499818225487"><a name="p499818225487"></a><a name="p499818225487"></a>/drivers/adapter/khdf/linux/hcs/hdf.hcs</p>
101</td>
102<td class="cellrowborder" align="left" valign="top" width="17.990000000000002%" headers="mcps1.2.4.1.3 "><p id="p3998152254820"><a name="p3998152254820"></a><a name="p3998152254820"></a>Entry to HCS configuration files</p>
103</td>
104</tr>
105</tbody>
106</table>
107
108>![](../public_sys-resources/icon-caution.gif) **CAUTION:**
109>The file paths involved in this example are used for demonstration only. Determine the paths for storing the source files as required when developing your driver.
110
111### Instantiating the Driver Entry<a name="section85325864412"></a>
112
113Instantiate an  **HdfDriverEntry**  object as the driver entry. The driver entry must be a global variable of the  **HdfDriverEntry**  type \(which is defined in  **hdf\_device\_desc.h**\), and the value of  **moduleName**  must be the same as that in  **device\_info.hcs**. When loading the driver, the HDF calls the  **Bind**  function first and then the  **Init**  function. If an error occurred during the calling of the  **Init**  function, the HDF calls  **Release**  to release the driver resource and exit.
114
115The  **Bind**  function is not implemented in the I2C driver because the I2C controller is managed by the I2C manager and the  **Bind**  function has been implemented in the manager. Therefore, services do not need to be bound in the I2C driver.
116
117The sample code for instantiating the driver entry is as follows:
118
119```
120/* Define a driver entry object. It must be a global variable of the HdfDriverEntry type (defined in hdf_device_desc.h). */
121struct HdfDriverEntry g_sampleI2cDriverEntry = {
122    .moduleVersion = 1,
123    .Init = SampleI2cInit,
124    .Release = SampleI2cRelease,
125    .moduleName = "demo_i2c_driver",
126};
127/* Call HDF_INIT to register the driver entry with the HDF. */
128HDF_INIT(g_sampleI2cDriverEntry);
129```
130
131### Setting Related Parameters<a name="section8155172019453"></a>
132
133Configure the  **device\_info.hcs**  file and obtain and parse device configuration parameters from the HCS to ensure that the driver can be correctly loaded.
134
1351.  \(Mandatory\) Add a device service node.
136
137    Edit the  **device\_info.hcs**  file and add a driver device service node under  **device\_i2c :: device**. The following is an example:
138
139    ```
140     root {
141        device_info {
142            match_attr = "hdf_manager";
143                device_i2c :: device {                        // I2C devices
144    		device2 :: deviceNode {                   // Device node of an I2C driver
145                        policy = 0;                           // Policy for releasing the driver service
146                        priority = 55;                        // Driver startup priority
147                        permission = 0644;                    // Permission for the driver to create a device node
148                        moduleName = "demo_i2c_driver";       // Driver name. The value of this field must be the same as that of moduleName in the driver entry data structure.
149                        serviceName = "DEMO_I2C_DRIVER";      // Name of the service released by the driver. The name must be unique.
150                        deviceMatchAttr = "demo_i2c_config";  // Keyword matching the private data of the driver. The value must be the same as that of
151                                                              // match_attr in the private configuration data table of the driver.
152                    }
153                }
154        }
155    }
156
157    ```
158
159    >![](../public_sys-resources/icon-notice.gif) **NOTICE:**
160    >The  **priority**  attribute \(an integer ranging from 0 to 200\) in the configuration file indicates the priority of a host or drivers. Drivers in a host with a smaller priority value have a higher loading priority than those in other hosts. The driver with a smaller priority value in a host has a higher loading priority than the other drivers in the host. The loading sequence is random for drivers with the same priority.
161
1622.  \(Optional\) Add configuration parameters.
163
164    The driver may require private configuration information to ensure that the register configuration meets the requirements of different products. If private configuration data is required, you can add a driver configuration file to store some default configuration information about the driver. When loading the driver, the HDF obtains the specified configuration information, saves it in the  **property**  attribute of  **HdfDeviceObject**, and passes it to the driver via  **Bind**  and  **Init**. For details about how to use  **Bind**  and  **Init**, see  [Driver Development](../driver/driver-hdf-development.md). You can create a configuration file and reference it in the  **hdf.hcs**  file of the board-level driver. In this example, configuration parameters are directly added to the existing configuration file  **i2c\_config.hcs**.
165
166    The following configuration parameters are added to the  **i2c\_config.hcs**  file:
167
168    ```
169    root {
170        platform {
171                i2c_config_demo {
172                match_attr = "demo_i2c_config";        // The value of this field must be the same as that of deviceMatchAttr in device_info.hcs.
173
174                template i2c_controller {              // Parameter template
175                    bus = 0;
176                    reg_pbase = 0x120b0000;
177                    reg_size = 0xd1;
178                }
179
180                controller_demo_0 :: i2c_controller {  // Two sample I2C controllers
181                    bus = 8;
182                }
183                controller_demo_1 :: i2c_controller {
184                    bus = 9;
185                }
186            }
187        }
188    }
189    ```
190
191    The value of  **match\_attr**  must be the same as that of  **deviceMatch\_Attr**  in the  **device\_info.hcs**  file. The  **match\_attr**  attribute is used to match the configured parameters in the configuration file \(**i2c\_config.hcs**  in this example\) with the particular driver, so that the driver can call  **DeviceResourceGetIfaceInstance\(\)**  in the  **Bind**  or  **Init**  function to obtain these configuration parameters.
192
193    If you create a new configuration file to set parameters, reference this file in the board-level configuration entry file  **hdf.hcs**. For example:
194
195    ```
196    #include "device_info/device_info.hcs"
197    #include "i2c/i2c_config.hcs"
198    ```
199
200    In this development example, we use an existing configuration file  **i2c\_config.hcs**  to add parameters, and therefore do not need to add it to the board-level configuration entry file.
201
2023.  Enable the driver to obtain configuration parameters from the HCS.
203
204    In this example, the driver needs to obtain configuration parameters, such as the physical base address of the register, register size, and bus number, from the HCS to correctly configure the controller.
205
206    ```
207    /* Obtain configuration parameters from the HCS. */
208    static int32_t SampleI2cReadDrs(struct SampleI2cCntlr *sampleCntlr, const struct DeviceResourceNode *node)
209    {
210        int32_t ret;
211        struct DeviceResourceIface *drsOps = NULL;
212
213        drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
214        if (drsOps == NULL || drsOps->GetUint32 == NULL) {                         // Ensure that the GetUint32 function is available.
215            HDF_LOGE("%s: invalid drs ops fail!", __func__);
216            return HDF_FAILURE;
217        }
218
219        ret = drsOps->GetUint32(node, "reg_pbase", &sampleCntlr->regBasePhy, 0);   // Read the physical base address from the HCS.
220        if (ret != HDF_SUCCESS) {
221            HDF_LOGE("%s: read regBase fail!", __func__);
222            return ret;
223        }
224
225        ret = drsOps->GetUint16(node, "reg_size", &sampleCntlr->regSize, 0);       // Read the register size from the HCS.
226        if (ret != HDF_SUCCESS) {
227            HDF_LOGE("%s: read regsize fail!", __func__);
228            return ret;
229        }
230
231        ret = drsOps->GetUint16(node, "bus", (uint16_t *)&sampleCntlr->bus, 0);    // Read the bus number from the HCS.
232        if (ret != HDF_SUCCESS) {
233            HDF_LOGE("%s: read bus fail!", __func__);
234            return ret;
235        }
236
237        return HDF_SUCCESS;
238    }
239    ```
240
241
242### Adding a Controller<a name="section1335374114452"></a>
243
244Initialize the controller hardware, call core-layer APIs to add or delete devices to or from the core layer, and implement a hook.
245
2461.  Define an I2C controller structure, implement a hook, and assign the hook to the function pointer.
247
248    The  **I2cMethod**  structure is defined in the  **i2c\_core.h**  header file. This structure defines the functions to be implemented by the I2C driver by using function pointers. The  **SampleI2cTransfer**  function is a hook used for data transmission, which must be implemented in the driver and must be assigned to a function pointer.
249
250    The sample code is as follows:
251
252    ```
253    /* Custom device structure, which is inherited from I2cCntlr */
254    struct SampleI2cCntlr {
255        struct I2cCntlr cntlr;
256        OsalSpinlock spin;
257        volatile unsigned char  *regBase;
258        uint16_t regSize;
259        int16_t bus;
260        uint32_t regBasePhy;
261    };
262
263    /* Message structure, which is inherited from I2cMsg */
264    struct SampleTransferData {
265        struct I2cMsg *msgs;
266        int16_t index;
267        int16_t count;
268    };
269    /* Hook implementation */
270    static int32_t SampleI2cTransfer(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int16_t count)
271    {
272        int32_t ret = HDF_SUCCESS;
273        struct SampleI2cCntlr *sampleCntlr = NULL;
274        struct SampleTransferData td;
275
276        if (cntlr == NULL || cntlr->priv == NULL) {
277            HDF_LOGE("SampleI2cTransfer: cntlr lor sampleCntlr is null!");
278            return HDF_ERR_INVALID_OBJECT;
279        }
280        sampleCntlr = (struct SampleI2cCntlr *)cntlr;
281
282        if (msgs == NULL || count <= 0) {
283            HDF_LOGE("SampleI2cTransfer: err parms! count:%d", count);
284            return HDF_ERR_INVALID_PARAM;
285        }
286        td.msgs = msgs;
287        td.count = count;
288        td.index = 0;
289
290        HDF_LOGE("Successfully transmitted!");  // Data transmission is successful.
291
292        td.index = count;                       // The total number of sent messages is returned. The message handling process is not provided in this sample code.
293        return (td.index > 0) ? td.index : ret;
294    }
295    /* Assign the hook to a function pointer. */
296    static struct I2cMethod g_method = {
297        .transfer = SampleI2cTransfer,
298    };
299    ```
300
3012.  Write a driver initialization function.
302
303    This example uses  **SampleI2cInit**  as the name of the driver initialization function \(this function name is configurable\). This function must be assigned to the  **Init**  function in the driver entry structure so that the HDF can call it to initialize the driver. The driver initialization function needs to parse the configuration parameters obtained from the HCS and create a controller based on these parameters. The sample code is as follows:
304
305    ```
306    /* Parse parameters, apply for memory, and create a controller. */
307    static int32_t SampleI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
308    {
309        int32_t ret;
310        struct SampleI2cCntlr *sampleCntlr = NULL;
311        (void)device;
312
313        sampleCntlr = (struct SampleI2cCntlr *)OsalMemCalloc(sizeof(*sampleCntlr));
314        if (sampleCntlr == NULL) {
315            HDF_LOGE("%s: malloc sampleCntlr fail!", __func__);
316            return HDF_ERR_MALLOC_FAIL;
317        }
318
319        ret = SampleI2cReadDrs(sampleCntlr, node);              // Obtain configuration parameters from the HCS.
320        if (ret != HDF_SUCCESS) {
321            HDF_LOGE("%s: read drs fail! ret:%d", __func__, ret);
322            goto __ERR__;
323        }
324
325        sampleCntlr->regBase = OsalIoRemap(sampleCntlr->regBasePhy, sampleCntlr->regSize);
326        if (sampleCntlr->regBase == NULL) {
327            HDF_LOGE("%s: ioremap regBase fail!", __func__);
328            ret = HDF_ERR_IO;
329            goto __ERR__;
330        }
331
332        HDF_LOGE("The controller has been initialized!");       // The controller has been initialized successfully. (The initialization process is not provided here.)
333
334        sampleCntlr->cntlr.priv = (void *)node;
335        sampleCntlr->cntlr.busId = sampleCntlr->bus;
336        sampleCntlr->cntlr.ops = &g_method;
337        (void)OsalSpinInit(&sampleCntlr->spin);                 // Initialize the spin lock.
338        ret = I2cCntlrAdd(&sampleCntlr->cntlr);                 // Add a controller to the core layer.
339        if (ret != HDF_SUCCESS) {
340            HDF_LOGE("%s: add i2c controller fail:%d!", __func__, ret);
341            goto __ERR__;
342        }
343
344        return HDF_SUCCESS;
345    __ERR__:                                                    // Handle errors.
346        if (sampleCntlr != NULL) {
347            if (sampleCntlr->regBase != NULL) {
348                OsalIoUnmap((void *)sampleCntlr->regBase);      // Cancel address mapping.
349                sampleCntlr->regBase = NULL;
350            }
351            OsalMemFree(sampleCntlr);                           // Release the memory.
352            sampleCntlr = NULL;
353        }
354        return ret;
355    }
356    /* Driver initialization function */
357    static int32_t SampleI2cInit(struct HdfDeviceObject *device)
358    {
359        int32_t ret;
360        const struct DeviceResourceNode *childNode = NULL;
361
362        HDF_LOGE("%s: Enter", __func__);
363        if (device == NULL || device->property == NULL) {
364            HDF_LOGE("%s: device or property is NULL", __func__);
365            return HDF_ERR_INVALID_OBJECT;
366        }
367
368        ret = HDF_SUCCESS;
369        DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
370            ret = SampleI2cParseAndInit(device, childNode);    // Call the function for parsing parameters and creating a controller.
371            if (ret != HDF_SUCCESS) {
372                break;
373            }
374        }
375        return ret;
376    }
377    ```
378
3793.  Write a driver release function.
380
381    This example uses  **SampleI2cRelease**  as the name of the driver release function \(you can name your own function\). This function must be assigned to the  **Release**  function in the driver entry structure so that the HDF can call it to initialize the driver if the driver fails to be initialized via  **Init**. The driver release function must contain operations for releasing the memory and deleting the controller. The sample code is as follows:
382
383    ```
384    /* Function for deleting the controller */
385    static void SampleI2cRemoveByNode(const struct DeviceResourceNode *node)
386    {
387        int32_t ret;
388        int16_t bus;
389        struct I2cCntlr *cntlr = NULL;
390        struct SampleI2cCntlr *sampleCntlr = NULL;
391        struct DeviceResourceIface *drsOps = NULL;
392
393        drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
394        if (drsOps == NULL || drsOps->GetUint32 == NULL) {
395            HDF_LOGE("%s: invalid drs ops fail!", __func__);
396            return;
397        }
398
399        ret = drsOps->GetUint16(node, "bus", (uint16_t *)&bus, 0); // Obtain the I2C bus number from the HCS.
400        if (ret != HDF_SUCCESS) {
401            HDF_LOGE("%s: read bus fail!", __func__);
402            return;
403        }
404
405        cntlr = I2cCntlrGet(bus);
406        if (cntlr != NULL && cntlr->priv == node) {                // Delete the controller based on the I2C bus number.
407            I2cCntlrPut(cntlr);
408            I2cCntlrRemove(cntlr);
409            sampleCntlr = (struct SampleI2cCntlr *)cntlr;
410            OsalIoUnmap((void *)sampleCntlr->regBase);
411            OsalMemFree(sampleCntlr);
412        }
413        return;
414    }
415    /* Release resources. */
416    static void SampleI2cRelease(struct HdfDeviceObject *device)
417    {
418        const struct DeviceResourceNode *childNode = NULL;
419
420        HDF_LOGI("%s: enter", __func__);
421
422        if (device == NULL || device->property == NULL) {
423            HDF_LOGE("%s: device or property is NULL", __func__);
424            return;
425        }
426
427        DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
428            SampleI2cRemoveByNode(childNode);                       // Delete a controller.
429        }
430    }
431    ```
432
433
434## Building Source Code and Burning Images<a name="section164824754712"></a>
435
4361.  Edit the Makefile and add a source file to it, as shown in the following example:
437
438    ```
439    include drivers/hdf/khdf/platform/platform.mk
440
441    obj-y  += $(HDF_PLATFORM_FRAMEWORKS_ROOT)/src/i2c_core.o \
442              $(HDF_PLATFORM_FRAMEWORKS_ROOT)/src/i2c_if.o \
443              ./i2c_adapter.o \
444              ./i2c_sample.o
445    ```
446
447    **./i2c\_sample.o**  is the source file added to the Makefile in this example.
448
4492.  Build source code and burn images to the development board.
450
451    For details, see the related sections in  [Getting Started for Standard System](../quick-start/quickstart-standard.md).
452
453
454