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> **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 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> **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> **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 > **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