1# HDF Driver Development Process 2 3## Overview 4 5The Hardware Driver Foundation (HDF) provides a driver development framework to implement driver loading, driver service management, driver messaging mechanism, and configuration management. It provides a component-based driver model to normalize driver development and deployment. The HDF strives to build a unified driver platform to back up a more precise and efficient environment for one-time development for multi-device deployment. 6 7### Driver Loading 8 9The HDF allows loading of the drivers that match the configured device list. 10 11### Driver Service Management 12 13The HDF supports centralized management of driver services. You can obtain a driver service by using the APIs provided by the HDF. 14 15### Driver Messaging Mechanism 16 17The HDF provides a unified driver messaging mechanism, which allows messages to be exchanged between user-mode applications and kernel-mode drivers. 18 19### Configuration Management 20 21HDF Configuration Source (HCS) provides the source code of the HDF configuration in key-value (KV) pairs. It decouples the configuration code from the driver code, thereby facilitating configuration management. 22 23### Driver Model 24 25The device model involves the following concepts: 26 27- Host: In the HDF, the device drivers of the same type are placed in a host. The host manages the startup and loading of a group of devices. You can deploy the drivers that depend on each other to the same host, and deploy independent drivers to different hosts. 28- Device: A device corresponds to a physical device. 29- Device Node: A device node is a component of a device. A device has at least one device node. Each device node can publish a device service. Each device node corresponds to a unique driver to interact with the hardware. 30 31The following figure shows the HDF driver model. 32 33**Figure 1** HDF driver model 34 35 36 37## Driver Functions 38 39### Driver Loading 40 41The HDF implements loading of the drivers that match the configured device list. The drivers can be loaded on demand or in sequence. The **preload** field in the configuration file specifies the loading policy. The values of **preload** are as follows: 42 43```c 44typedef enum { 45 DEVICE_PRELOAD_ENABLE = 0, 46 DEVICE_PRELOAD_ENABLE_STEP2 = 1, 47 DEVICE_PRELOAD_DISABLE = 2, 48 DEVICE_PRELOAD_INVALID 49} DevicePreload; 50``` 51 52#### On-Demand Loading 53 54- **0** (**DEVICE_PRELOAD_ENABLE**): loads the driver during the system boot process. 55- **1** (**DEVICE_PRELOAD_ENABLE_STEP2**): loads the driver after a quick start is complete. If the system does not support quick start, the value **1** has the same meaning as **DEVICE_PRELOAD_ENABLE**. 56- **2** (**DEVICE_PRELOAD_DISABLE**): dynamically loads the driver after the system starts. If the driver service does not exist when a user-mode process attempts to obtain the driver service [messaging mechanism](#driver-messaging-mechanism), the HDF will dynamically load the driver. 57 58#### Sequential Loading (Default) 59 60The **priority** field (ranging from 0 to 200) in the configuration file determines the loading sequence of a host and a driver. For the drivers in different hosts, the driver with a smaller host priority is loaded first. For the drivers in the same host, the driver with a smaller priority is loaded first. 61 62#### Exception Recovery (User-Mode Driver) 63 64The policies for restoring from a driver service exception are as follows: 65- If **preload** is set to **0** (**DEVICE_PRELOAD_ENABLE**) or **1** (**DEVICE_PRELOAD_ENABLE_STEP2**) for the driver service, the startup module starts the host and reloads the service. 66- If **preload** is set to **2** (**DEVICE_PRELOAD_DISABLE**), the service module needs to register an HDF service status listener. When receiving a notification on service exit, the service module calls **LoadDevice()** to reload the service. 67 68### Driver Service Management 69 70Driver services, as capability objects externally provided by HDF driver devices, are managed by the HDF in a unified manner. Driver service management involves publishing and obtaining driver services. The **policy** field in the configuration file defines the service publishing policies. The values of this field are as follows: 71 72```c 73typedef enum { 74 /* The driver does not provide services. */ 75 SERVICE_POLICY_NONE = 0, 76 /* The driver publishes services only for kernel-mode processes. */ 77 SERVICE_POLICY_PUBLIC = 1, 78 /* The driver publishes services for both kernel- and user-mode processes. */ 79 SERVICE_POLICY_CAPACITY = 2, 80 /** The driver services are not published externally but can be subscribed to. */ 81 SERVICE_POLICY_FRIENDLY = 3, 82 /* The driver private services cannot be published externally or subscribed to. */ 83 SERVICE_POLICY_PRIVATE = 4, 84 /** Invalid service policy. */ 85 SERVICE_POLICY_INVALID 86} ServicePolicy; 87``` 88 89#### When to Use 90 91You need to implement HDF driver service management when your driver needs to provide external capabilities via APIs. 92 93#### Available APIs 94 95The following table describes the APIs for driver service management. 96 97**Table 1** APIs for driver service management 98 99| API | Description | 100| ------------------------------------------------------------ | ------------------------------------------------------------ | 101| int32_t (*Bind)(struct HdfDeviceObject *deviceObject) | Binds a service interface to the HDF. You need to implement **Bind**.| 102| const struct HdfObject *DevSvcManagerClntGetService(const char *svcName) | Obtains a driver service. | 103| int HdfDeviceSubscribeService( struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback) | Subscribes to a driver service. | 104 105 106### Driver Messaging Mechanism 107 108#### When to Use 109 110The HDF messaging mechanism implements interaction between the user-mode applications and kernel-mode drivers. 111 112#### Available APIs 113 114The messaging mechanism allows: 115- A user-mode application to send a message to a driver. 116- A user-mode application to receive events reported by a driver. 117 118**Table 2** APIs for the driver messaging mechanism 119 120| API | Description | 121| ------------------------------------------------------------ | ------------------------------------------------------------ | 122| struct HdfIoService *HdfIoServiceBind(const char *serviceName); | Obtains a driver service. After obtaining the driver service, the user-mode application calls **Dispatch()** in the driver service obtained to send messages to the driver. | 123| void HdfIoServiceRecycle(struct HdfIoService *service); | Releases a driver service. | 124| int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | Registers an event listener to receive events from the driver. | 125| int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data) | Sends an event. | 126 127 128 129### Configuration Management 130 131#### HDF Configuration Overview 132 133The HCS provides the HDF configuration source code in KV pairs. It decouples the configuration code from the driver code, thereby facilitating configuration management. You can use the HDF Configuration Generator (HC-GEN) to convert an HCS configuration file into a file that can be read by the software. 134 135- In a low-performance system on a chip (SoC), the HC-GEN tool converts an HCS configuration file into the source code or macro definitions of the configuration tree. The driver can obtain the configuration by calling the C code or macro-based APIs. 136- In a high-performance SoC, the tool converts an HCS configuration file into an HDF configuration binary (HCB) file. The driver can obtain the configuration by calling the configuration parsing APIs provided by the HDF. 137 138The following figure illustrates the configuration management process. 139 140**Figure 2** Configuration management process 141 142 143 144The HC-GEN converts the HCS into an HCB file. The HCS Parser module in the HDF rebuilds a configuration tree from the HCB file. The HDF driver obtains the configuration through the APIs provided by the HCS Parser. 145 146#### Configuration Syntax 147 148The following describes the HCS syntax. 149 150##### Keyword 151 152The following table describes the keywords used in the HCS syntax. 153 154**Table 3** Keywords used in the HCS syntax 155 156| Keyword | Description | Remarks | 157| ---------- | -------------------------- | ------------------------------------------ | 158| root | Sets the root node. | - | 159| include | Includes another HCS file. | - | 160| delete | Deletes a node or an attribute. | Applicable only to the configuration tree imported by using **include**. | 161| template | Defines a template node. | - | 162| match_attr | Marks the node attribute for matching.| During configuration parsing, the attribute value can be used to locate the corresponding node.| 163 164##### Basic Structs 165 166The HCS has two structs: **Attribute** and **Node**. 167 168**Attribute** 169 170**Attribute** is the minimum, independent configuration unit. The syntax is as follows: 171 172``` 173 attribute_name = value; 174``` 175 176- **attribute_name** is a case-sensitive string consisting of letters, digits, and underscores (\_) and must start with a letter or underscore (_). 177- The **value** can be in any of the following formats: 178 - Numeric constant. The value can be a binary, octal, decimal, or hexadecimal number. For details, see [Data Types](#data-types). 179 - String quoted by double quotation marks (""). 180 - Node reference. 181- An attribute key-value pair must end with a semicolon (;) and belong to a node. 182 183**Node** 184 185**Node** is a set of attributes. The syntax is as follows: 186 187``` 188 node_name { 189 module = "sample"; 190 ... 191 } 192``` 193 194- **node_name** is a case-sensitive string consisting of letters, digits, and underscores (\_) and must start with a letter or underscore (_). 195- No semicolon (;) is required after the curly brace ({) or (}). 196- The keyword **root** is used to declare the root node of a configuration table. Each configuration table must start with the root node. 197- The root node must contain a **module** attribute. The value is a string indicating the module to which the configuration belongs. 198- The **match_attr** attribute can be added to a node. Its value is a globally unique string. When parsing the configuration, the driver can use the value of this attribute as a parameter to call an API to locate the node that has this attribute. 199 200##### Data Types 201 202Attributes use built-in data types. You do not need to explicitly specify the data type for attribute values. Attributes support the following data types: 203 204**Integer** 205 206An integer can be in any of the following formats. The data type is assigned based on the actual data length and minimum space required. 207 208- Binary: prefixed with **0b**. For example, **0b1010**. 209- Octal: prefixed with **0**. For example, **0664**. 210- Decimal: signed or unsigned, without prefix. For example, **1024** or **+1024**. Negative integers can be read only via APIs with signed numbers. 211- Hexadecimal: prefixed with **0x**. For example, **0xff00** and **0xFF**. 212 213**String** 214 215A string is enclosed in double quotation marks (""). 216 217**Array** 218 219An array can hold either integers or strings, but not both of them. The mixed use of **uint32_t** and **uint64_t** in an integer array will cause typecasting to **uint64**. The following is an example of an integer array and a string array: 220 221``` 222attr_foo = [0x01, 0x02, 0x03, 0x04]; 223attr_bar = ["hello", "world"]; 224``` 225 226**Boolean** 227 228Boolean is a form of data with only two possible values: **true** and **false**. 229 230##### Preprocessing 231 232**include** 233 234The keyword **include** is used to import an HCS file. The syntax is as follows: 235 236``` 237#include "foo.hcs" 238#include "../bar.hcs" 239``` 240 241- The file name must be enclosed in double quotation marks (""). If the file to be included is in a different directory with the target file, use a relative path. The included file must be a valid HCS file. 242- If multiple HCS files included contain the same nodes, the same nodes will be overridden and other nodes are listed in sequence. 243 244##### Comments 245 246The following two comment formats are supported: 247 248- Single-line comment 249 250 ``` 251 // comment 252 ``` 253 254- Multi-line comment 255 256 ``` 257 /* 258 comment 259 */ 260 ``` 261 262 > **NOTE** 263 > 264 > Multi-line comments cannot be nested. 265 266##### Reference Modification 267 268You can reference the content of a node to modify the content of another node. The syntax is as follows: 269 270``` 271 node :& source_node 272``` 273 274In this statement, the content of **node** is referenced to modify the content of **source_node**. <br>Example: 275 276``` 277root { 278 module = "sample"; 279 foo { 280 foo_ :& root.bar{ 281 attr = "foo"; 282 } 283 foo1 :& foo2 { 284 attr = 0x2; 285 } 286 foo2 { 287 attr = 0x1; 288 } 289 } 290 291 bar { 292 attr = "bar"; 293 } 294} 295``` 296 297The configuration tree generated is as follows: 298 299``` 300root { 301 module = "sample"; 302 foo { 303 foo2 { 304 attr = 0x2; 305 } 306 } 307 bar { 308 attr = "foo"; 309 } 310} 311``` 312 313In this example, the value of **bar.attr** is changed to **foo** by referencing **foo.foo_**, and the value of **foo.foo2.attr** is changed to **0x2** by referencing **foo.foo1**. The **foo.foo_** and **foo.foo1** nodes are used to modify the content of the target nodes, and do not exist in the configuration tree generated. 314 315- A node of the same level can be referenced simply by using the node name. To reference a node of a different level, use the absolute path starting with **root**, and separate the node names using a period (.). **root** indicates the root node. For example, **root.foo.bar**. 316- If multiple modifications are made to the same attribute, only one modification takes effect and a warning will be displayed for you to confirm the result. 317 318##### Node Replication 319 320You can replicate a node to define a node with similar content. The syntax is as follows: 321 322``` 323 node : source_node 324``` 325 326This statement replicates the attributes of the **source_node** node to define **node**. <br>Example: 327 328``` 329root { 330 module = "sample"; 331 foo { 332 attr_0 = 0x0; 333 } 334 bar:foo { 335 attr_1 = 0x1; 336 } 337} 338``` 339 340The configuration tree generated is as follows: 341 342``` 343root { 344 module = "sample"; 345 foo { 346 attr_0 = 0x0; 347 } 348 bar { 349 attr_1 = 0x1; 350 attr_0 = 0x0; 351 } 352} 353``` 354 355In this example, the **bar** node contains **attr_0** and **attr_1** attributes, and the modification of the **attr_0** attribute in the **bar** node does not affect the **foo** node. 356 357You do not need to specify the path of the **foo** node if **foo** and **bar** are of the same level. Otherwise, you need to specify the absolute path of **foo** by using [Reference Modification](#reference-modification). 358 359##### Delete 360 361You can use the keyword **delete** to delete unnecessary nodes or attributes from the base configuration tree imported by using the **include** keyword. The following example includes the configuration in **sample2.hcs** to **sample1.hcs** and deletes the **attribute2** attribute and the **foo_2** node. <br>Example: 362 363``` 364// sample2.hcs 365root { 366 attr_1 = 0x1; 367 attr_2 = 0x2; 368 foo_2 { 369 t = 0x1; 370 } 371} 372 373// sample1.hcs 374#include "sample2.hcs" 375root { 376 attr_2 = delete; 377 foo_2 : delete { 378 } 379} 380``` 381 382The configuration tree generated is as follows: 383 384``` 385root { 386 attr_1 = 0x1; 387} 388``` 389 390> **NOTE**<br> 391> The keyword **delete** cannot be used to delete nodes or attributes in the same HCS file. In an HCS file, you can directly delete unnecessary attributes. 392 393##### Attribute References 394 395You can associate an attribute and a node so that the node can be quickly located when the attribute is read during configuration parsing. The syntax is as follows: 396 397``` 398 attribute = &node; 399``` 400 401In this statement, the value of **attribute** is a referenced to the node. During code parsing, you can quickly locate the node based on this **attribute**. <br>Example: 402 403``` 404node1 { 405 attributes; 406} 407node2 { 408 attr_1 = &root.node1; 409} 410``` 411 412or 413 414``` 415node2 { 416 node1 { 417 attributes; 418 } 419 attr_1 = &node1; 420} 421``` 422 423##### Template 424 425The template is used to generate nodes with consistent syntax, thereby facilitating the traverse and management of nodes of the same type. If a node is defined using the keyword **template**, its child nodes inherit from the node configuration through the double colon operator (::). The child nodes can modify or add but cannot delete attributes in **template**. The attributes not defined in the child nodes will use the attributes defined in **template** as the default values. <br>Example: 426 427``` 428root { 429 module = "sample"; 430 template foo { 431 attr_1 = 0x1; 432 attr_2 = 0x2; 433 } 434 435 bar :: foo { 436 } 437 438 bar_1 :: foo { 439 attr_1 = 0x2; 440 } 441} 442``` 443 444 The configuration tree generated is as follows: 445 446``` 447root { 448 module = "sample"; 449 bar { 450 attr_1 = 0x1; 451 attr_2 = 0x2; 452 } 453 bar_1 { 454 attr_1 = 0x2; 455 attr_2 = 0x2; 456 } 457} 458``` 459 460In this example, the **bar** and **bar_1** nodes inherit from the **foo** node. The structure of the generated configuration tree is the same as that of the **foo** node, except that the attribute values are different. 461 462#### Configuration Generation 463 464The HC-GEN tool checks the HCS configuration syntax and converts HCS source files into HCB files. 465 466**HC-GEN** 467 468HC-GEN options: 469 470``` 471Usage: hc-gen [Options] [File] 472options: 473 -o <file> output file name, default same as input 474 -a hcb align with four bytes 475 -b output binary output, default enable 476 -t output config in C language source file style 477 -m output config in macro source file style 478 -i output binary hex dump in C language source file style 479 -p <prefix> prefix of generated symbol name 480 -d decompile hcb to hcs 481 -V show verbose info 482 -v show version 483 -h show this help message 484``` 485 486Generate a .c or .h configuration file. 487 488``` 489hc-gen -o [OutputCFileName] -t [SourceHcsFileName] 490``` 491 492Generate an HCB file. 493 494``` 495hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName] 496``` 497 498Generate a macro definition file. 499 500``` 501hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName] 502``` 503 504Decompile an HCB file to an HCS file. 505 506``` 507hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName] 508``` 509 510## Development 511 512### When to Use 513 514During driver development, the driver cannot be loaded in the code compilation process without service management and messaging mechanism. The following describes the driver development process. 515 516### Driver Development Example 517 518The HDF-based driver development involves the following:<br>1. Implement the driver.<br>2. Write the driver build script.<br>3. Configure the driver. 519 520#### Implementing a Driver 521 522Write the driver code and register the driver entry with the HDF. 523 524- Write the driver service code.<br>Example: 525 526 ```c 527 #include "hdf_device_desc.h" // Include the driver development APIs provided by the HDF. 528 #include "hdf_log.h" // Include the log APIs provided by the HDF. 529 530 #define HDF_LOG_TAG "sample_driver" // Define the tag contained in logs. If no tag is defined, the default HDF_TAG is used. 531 532 // Bind the service capability interface provided by the driver to the HDF. 533 int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) 534 { 535 HDF_LOGD("Sample driver bind success"); 536 return HDF_SUCCESS; 537 } 538 539 // Initialize the driver service. 540 int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) 541 { 542 HDF_LOGD("Sample driver Init success"); 543 return HDF_SUCCESS; 544 } 545 546 // Release the driver resources. 547 void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) 548 { 549 HDF_LOGD("Sample driver release success"); 550 return; 551 } 552 ``` 553 554- Register the driver entry with the HDF. 555 556 ```c 557 // Define a driver entry object. It must be a global variable of the HdfDriverEntry type (defined in hdf_device_desc.h). 558 struct HdfDriverEntry g_sampleDriverEntry = { 559 .moduleVersion = 1, 560 .moduleName = "sample_driver", 561 .Bind = HdfSampleDriverBind, 562 .Init = HdfSampleDriverInit, 563 .Release = HdfSampleDriverRelease, 564 }; 565 566 // Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls Bind() and then Init(). If Init() fails to be called, the HDF will call Release() to release driver resources and exit the driver model. 567 HDF_INIT(g_sampleDriverEntry); 568 ``` 569 570#### Writing the Driver Compilation Script 571 572- ##### LiteOS 573 574 If a LiteOS is used, you need to modify **makefile** and **BUILD.gn** files. 575 576 - **Makefile**: 577 578 Use the **makefile** template provided by the HDF to compile the driver code. 579 580 ```makefile 581 include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # (Mandatory) Import the HDF predefined content. 582 MODULE_NAME := # File to be generated. 583 LOCAL_INCLUDE: = # Directory of the driver header files. 584 LOCAL_SRCS : = # Source code files of the driver. 585 LOCAL_CFLAGS : = # Custom compiler options. 586 include $(HDF_DRIVER) # Import the Makefile template to complete the build. 587 ``` 588 589 Add the path of the generated file to **hdf_lite.mk** in the **drivers/hdf_core/adapter/khdf/liteos** directory to link the file to the kernel image. 590 591 Example: 592 593 ```makefile 594 LITEOS_BASELIB += -lxxx # Static library generated by the link. 595 LIB_SUBDIRS += # Directory in which makefile is located. 596 ``` 597 598 - **BUILD.gn**: 599 600 Add the module **BUILD.gn**. 601 602 Example: 603 604 ``` 605 import("//build/lite/config/component/lite_component.gni") 606 import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni") 607 module_switch = defined(LOSCFG_DRIVERS_HDF_xxx) 608 module_name = "xxx" 609 hdf_driver(module_name) { 610 sources = [ 611 "xxx/xxx/xxx.c", # Source code to compile. 612 ] 613 public_configs = [ ":public" ] # Head file configuration of the dependencies. 614 } 615 config("public") { # Define the head file configuration of the dependencies. 616 include_dirs = [ 617 "xxx/xxx/xxx", # Directory of dependency header files. 618 ] 619 } 620 ``` 621 622 Add the **BUILD.gn** directory to **/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**. 623 624 ``` 625 group("liteos") { 626 public_deps = [ ":$module_name" ] 627 deps = [ 628 "xxx/xxx", # Directory of the new module BUILD.gn, /drivers/hdf_core/adapter/khdf/liteos 629 ] 630 } 631 ``` 632 633- ##### Linux 634 635 To define the driver control macro, add the **Kconfig** file to the driver directory **xxx** and add the path of the **Kconfig** file to **drivers/hdf_core/adapter/khdf/linux/Kconfig**. 636 637 ``` 638 source "drivers/hdf/khdf/xxx/Kconfig" # Kernel directory to which the HDF module is soft linked. 639 ``` 640 641 Add the driver directory to **drivers/hdf_core/adapter/khdf/linux/Makefile**. 642 643 ```makefile 644 obj-$(CONFIG_DRIVERS_HDF) += xxx/ 645 ``` 646 647 Add a **Makefile** to the driver directory **xxx** and add code compiling rules of the driver to the **Makefile** file. 648 649 ```makefile 650 obj-y += xxx.o 651 ``` 652 653#### Configuring the Driver 654 655The HDF uses HCS as the configuration description source code. For details about the HCS, see [HDF Configuration Overview](#hdf-configuration-overview). 656 657The driver configuration consists of the driver device description defined by the HDF and the private driver configuration. 658 659- (Mandatory) Set driver device information. 660 661 The HDF loads a driver based on the driver device description defined by the HDF. Therefore, the driver device description must be added to the **device_info.hcs** file defined by the HDF. 662 663 Example: 664 665 ``` 666 root { 667 device_info { 668 match_attr = "hdf_manager"; 669 template host { // Host template. If a node (for example, sample_host) uses the default values in this template, the node fields can be omitted. 670 hostName = ""; 671 priority = 100; 672 uid = ""; // User ID (UID) of the user-mode process. It is left empty by default. If you do not set the value, this parameter will be set to the value of hostName, which indicates a common user. 673 gid = ""; // Group ID (GID) of the user-mode process. It is left empty by default. If you do not set the value, this parameter will be set to the value of hostName, which indicates a common user group. 674 caps = [""]]; // Linux capabilities of the user-mode process. It is left empty by default. Set this parameter based on service requirements. 675 template device { 676 template deviceNode { 677 policy = 0; 678 priority = 100; 679 preload = 0; 680 permission = 0664; 681 moduleName = ""; 682 serviceName = ""; 683 deviceMatchAttr = ""; 684 } 685 } 686 } 687 sample_host :: host{ 688 hostName = "host0"; // Host name. The host node is used as a container to hold a type of drivers. 689 priority = 100; // Host startup priority (0-200). A smaller value indicates a higher priority. The default value 100 is recommended. The hosts with the same priority start based on the time when the priority was configured. The host configured first starts first. 690 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // Linux capabilities of a user-mode process. 691 device_sample :: device { // Sample device node. 692 device0 :: deviceNode { // DeviceNode of the sample driver. 693 policy = 1; // Policy for publishing the driver service. For details, see Driver Service Management. 694 priority = 100; // Driver startup priority (0-200). A smaller value indicates a higher priority. The default value 100 is recommended. The drivers with the same priority start based on the time when the priority was configured. The driver configured first starts first. 695 preload = 0; // The value 0 means to load the driver by default during the startup of the system. 696 permission = 0664; // Permission for the DeviceNode created. 697 moduleName = "sample_driver"; // Driver name. The value must be the same as that of moduleName in the HdfDriverEntry structure. 698 serviceName = "sample_service"; // Name of the service published by the driver. The service name must be unique. 699 deviceMatchAttr = "sample_config"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. 700 } 701 } 702 } 703 } 704 } 705 ``` 706 707 > **NOTE**<br> 708 > 709 > - **uid**, **gid**, and **caps** are startup parameters for user-mode drivers only. 710 > - According to the principle of least privilege for processes, **uid** and **gid** do not need to be configured for service modules. In the preceding example, **uid** and **gid** are left empty (granted with the common user rights) for sample_host. 711 > - If you need to set **uid** and **gid** to **system** or **root** due to service requirements, contact security experts for review. 712 > - The process UIDs are configured in **base/startup/init/services/etc/passwd**, and the process GIDs are configured in **base/startup/init/services/etc/group**. For details, see [Adding a System Service User Group]( https://gitee.com/openharmony/startup_init_lite/wikis). 713 > - The **caps** value is in the caps = ["xxx"] format. To configure **CAP_DAC_OVERRIDE**, set this parameter to **caps = ["DAC_OVERRIDE"]**. Do not set it to **caps = ["CAP_DAC_OVERRIDE"]**. 714 > - **preload** specifies the loading policy for the driver. 715 716- (Optional) Set driver private information. 717 718 If the driver has private configuration, add a driver configuration file to set default driver configuration. When loading the driver, the HDF obtains and saves the driver private information in **property** of **HdfDeviceObject**, and passes the information to the driver using **Bind()** and **Init()** (see [Implementing a Driver](implementing-a-driver)). 719 720 Example: 721 722 ``` 723 root { 724 SampleDriverConfig { 725 sample_version = 1; 726 sample_bus = "I2C_0"; 727 match_attr = "sample_config"; // The value must be the same as that of deviceMatchAttr in device_info.hcs. 728 } 729} 730 ``` 731 732 Add the configuration file to the **hdf.hcs** file. 733 734 Example: 735 736 ``` 737 #include "device_info/device_info.hcs" 738 #include "sample/sample_config.hcs" 739 ``` 740 741### Driver Messaging Mechanism Development 742 7431. Set the **policy** field in the driver configuration information to **2** (SERVICE_POLICY_CAPACITY). For details about the policy, see [Driver Service Management](#driver-service-management). 744 745 ``` 746 device_sample :: Device { 747 policy = 2; 748 ... 749 } 750 ``` 751 7522. Set permissions for the device node of the driver. By default, the **permission** field is set to **0666**. You can set it based on service requirements. 753 7543. Implement the **Dispatch()** method of **IDeviceIoService**. 755 756 ```c 757 // Dispatch() is used to process messages sent from the user-mode application. 758 int32_t SampleDriverDispatch(struct HdfDeviceIoClient *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) 759 { 760 HDF_LOGI("sample driver lite A dispatch"); 761 return HDF_SUCCESS; 762 } 763 int32_t SampleDriverBind(struct HdfDeviceObject *device) 764 { 765 HDF_LOGI("test for lite os sample driver A Open!"); 766 if (device == NULL) { 767 HDF_LOGE("test for lite os sample driver A Open failed!"); 768 return HDF_FAILURE; 769 } 770 static struct ISampleDriverService sampleDriverA = { 771 .ioService.Dispatch = SampleDriverDispatch, 772 .ServiceA = SampleDriverServiceA, 773 .ServiceB = SampleDriverServiceB, 774 }; 775 device->service = (struct IDeviceIoService *)(&sampleDriverA); 776 return HDF_SUCCESS; 777 } 778 ``` 779 7804. Define the cmd type in the message processing function. 781 782 ```c 783 #define SAMPLE_WRITE_READ 1 // Read and write operation 1 784 ``` 785 7865. Enable the user-mode application to obtain a service and send a message to the driver. 787 788 ```c 789 int SendMsg(const char *testMsg) 790 { 791 if (testMsg == NULL) { 792 HDF_LOGE("test msg is null"); 793 return HDF_FAILURE; 794 } 795 struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); 796 if (serv == NULL) { 797 HDF_LOGE("fail to get service"); 798 return HDF_FAILURE; 799 } 800 struct HdfSBuf *data = HdfSbufObtainDefaultSize(); 801 if (data == NULL) { 802 HDF_LOGE("fail to obtain sbuf data"); 803 return HDF_FAILURE; 804 } 805 struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); 806 if (reply == NULL) { 807 HDF_LOGE("fail to obtain sbuf reply"); 808 ret = HDF_DEV_ERR_NO_MEMORY; 809 goto out; 810 } 811 if (!HdfSbufWriteString(data, testMsg)) { 812 HDF_LOGE("fail to write sbuf"); 813 ret = HDF_FAILURE; 814 goto out; 815 } 816 int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply); 817 if (ret != HDF_SUCCESS) { 818 HDF_LOGE("fail to send service call"); 819 goto out; 820 } 821 out: 822 HdfSbufRecycle(data); 823 HdfSbbufRecycle(reply); 824 HdfIoServiceRecycle(serv); 825 return ret; 826 } 827 ``` 828 8296. Enable the user-mode process to receive messages from the driver. 830 831 1. Implement the method for the user-mode application to process the events reported by the driver. 832 833 ```c 834 static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) 835 { 836 OsalTimespec time; 837 OsalGetTime(&time); 838 HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec); 839 840 const char *string = HdfSbufReadString(data); 841 if (string == NULL) { 842 HDF_LOGE("fail to read string in event data"); 843 return HDF_FAILURE; 844 } 845 HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s", (char *)priv, id, string); 846 return HDF_SUCCESS; 847 } 848 ``` 849 850 2. Register the method for the user-mode application to receive messages from the driver. 851 852 ```c 853 int RegisterListen() 854 { 855 struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); 856 if (serv == NULL) { 857 HDF_LOGE("fail to get service"); 858 return HDF_FAILURE; 859 } 860 static struct HdfDevEventlistener listener = { 861 .callBack = OnDevEventReceived, 862 .priv ="Service0" 863 }; 864 if (HdfDeviceRegisterEventListener(serv, &listener) != 0) { 865 HDF_LOGE("fail to register event listener"); 866 return HDF_FAILURE; 867 } 868 ...... 869 HdfDeviceUnregisterEventListener(serv, &listener); 870 HdfIoServiceRecycle(serv); 871 return HDF_SUCCESS; 872 } 873 ``` 874 875 3. Enable the driver to report events. 876 877 ```c 878 int32_t SampleDriverDispatch(HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) 879 { 880 ... // process api call here 881 return HdfDeviceSendEvent(client->device, cmdCode, data); 882 } 883 ``` 884 885### Driver Service Management Development 886 887The development procedure is as follows: 888 889#### Defining the Services to be Published by the Driver 890 891```c 892// Define the driver service struct. 893struct ISampleDriverService { 894 struct IDeviceIoService ioService; // The first member must be of the IDeviceIoService type. 895 int32_t (*ServiceA)(void); // API of the first driver service. 896 int32_t (*ServiceB)(uint32_t inputCode); // API of the second driver service. You can add more as required. 897}; 898 899// Implement the driver service APIs. 900int32_t SampleDriverServiceA(void) 901{ 902 // You need to implement the service logic. 903 return HDF_SUCCESS; 904} 905 906int32_t SampleDriverServiceB(uint32_t inputCode) 907{ 908 // You need to implement the service logic. 909 return HDF_SUCCESS; 910} 911``` 912 913#### Binding Driver Services 914 915Implement the **Bind** pointer function, for example, **SampleDriverBind**, in **HdfDriverEntry** to bind the driver service to the HDF. 916 917```c 918int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject) 919{ 920 // deviceObject is a pointer to the device object created by the HDF for each driver. The device object holds private device data and service APIs. 921 if (deviceObject == NULL) { 922 HDF_LOGE("Sample device object is null!"); 923 return HDF_FAILURE; 924 } 925 static struct ISampleDriverService sampleDriverA = { 926 .ServiceA = SampleDriverServiceA, 927 .ServiceB = SampleDriverServiceB, 928 }; 929 deviceObject->service = &sampleDriverA.ioService; 930 return HDF_SUCCESS; 931} 932``` 933 934#### Obtaining Driver Services 935 936The driver service can be obtained by using either of the following methods: 937 938##### Using the API provided by the HDF 939 940If the service requester clearly knows when the driver is loaded, it can obtain the driver service by using the API provided by the HDF. The following is an example: 941 942```c 943const struct ISampleDriverService *sampleService = 944 (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver"); 945if (sampleService == NULL) { 946 return HDF_FAILURE; 947} 948sampleService->ServiceA(); 949sampleService->ServiceB(5); 950``` 951 952##### Using the subscription mechanism 953 954If the service requester is unaware of when the driver (in the same host) is loaded, it can use the subscription mechanism provided by the HDF to subscribe to the service. After the driver is loaded, the HDF publishes the driver service to the subscriber. The implementation is as follows: 955 956```c 957// Callback invoked to return the driver service after the subscribed driver is loaded. 958// object is the pointer to the private data of the subscriber, and service is the pointer to the subscribed service object. 959int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service) 960{ 961 const struct ISampleDriverService *sampleService = 962 (const struct ISampleDriverService *)service; 963 if (sampleService == NULL) { 964 return HDF_FAILURE; 965 } 966 sampleService->ServiceA(); 967 sampleService->ServiceB(5); 968} 969// Implement the subscription process. 970int32_t TestDriverInit(struct HdfDeviceObject *deviceObject) 971{ 972 if (deviceObject == NULL) { 973 HDF_LOGE("Test driver init failed, deviceObject is null!"); 974 return HDF_FAILURE; 975 } 976 struct SubscriberCallback callBack; 977 callBack.deviceObject = deviceObject; 978 callBack.OnServiceConnected = TestDriverSubCallBack; 979 int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack); 980 if (ret != HDF_SUCCESS) { 981 HDF_LOGE("Test driver subscribe sample driver failed!"); 982 } 983 return ret; 984} 985``` 986 987## HDF Development Example 988 989The following is a HDF-based driver development example. 990 991### Adding the Driver Configuration 992 993Add the driver configuration to the HDF configuration file, for example, **vendor/hisilicon/xxx/hdf_config/device_info**. 994 995``` 996root { 997 device_info { 998 match_attr = "hdf_manager"; 999 template host { 1000 hostName = ""; 1001 priority = 100; 1002 template device { 1003 template deviceNode { 1004 policy = 0; 1005 priority = 100; 1006 preload = 0; 1007 permission = 0664; 1008 moduleName = ""; 1009 serviceName = ""; 1010 deviceMatchAttr = ""; 1011 } 1012 } 1013 } 1014 sample_host :: host { 1015 hostName = "sample_host"; 1016 sample_device :: device { 1017 device0 :: deviceNode { 1018 policy = 2; 1019 priority = 100; 1020 preload = 1; 1021 permission = 0664; 1022 moduleName = "sample_driver"; 1023 serviceName = "sample_service"; 1024 } 1025 } 1026 } 1027 } 1028} 1029``` 1030 1031### Writing the Driver Code 1032 1033The sample driver code compiled based on the HDF framework is as follows: 1034 1035```c 1036#include <fcntl.h> 1037#include <sys/stat.h> 1038#include <sys/ioctl.h> 1039#include "hdf_log.h" 1040#include "hdf_base.h" 1041#include "hdf_device_desc.h" 1042 1043#define HDF_LOG_TAG sample_driver 1044 1045#define SAMPLE_WRITE_READ 123 1046 1047static int32_t HdfSampleDriverDispatch( 1048 struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply) 1049{ 1050 HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id); 1051 if (id == SAMPLE_WRITE_READ) { 1052 const char *readData = HdfSbufReadString(data); 1053 if (readData != NULL) { 1054 HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData); 1055 } 1056 if (!HdfSbufWriteInt32(reply, INT32_MAX)) { 1057 HDF_LOGE("%{public}s: reply int32 fail", __func__); 1058 } 1059 return HdfDeviceSendEvent(client->device, id, data); 1060 } 1061 return HDF_FAILURE; 1062} 1063 1064static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) 1065{ 1066 // Release resources here 1067 return; 1068} 1069 1070static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) 1071{ 1072 if (deviceObject == NULL) { 1073 return HDF_FAILURE; 1074 } 1075 static struct IDeviceIoService testService = { 1076 .Dispatch = HdfSampleDriverDispatch, 1077 }; 1078 deviceObject->service = &testService; 1079 return HDF_SUCCESS; 1080} 1081 1082static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) 1083{ 1084 if (deviceObject == NULL) { 1085 HDF_LOGE("%{public}s::ptr is null!", __func__); 1086 return HDF_FAILURE; 1087 } 1088 HDF_LOGI("Sample driver Init success"); 1089 return HDF_SUCCESS; 1090} 1091 1092static struct HdfDriverEntry g_sampleDriverEntry = { 1093 .moduleVersion = 1, 1094 .moduleName = "sample_driver", 1095 .Bind = HdfSampleDriverBind, 1096 .Init = HdfSampleDriverInit, 1097 .Release = HdfSampleDriverRelease, 1098}; 1099 1100HDF_INIT(g_sampleDriverEntry); 1101``` 1102 1103### Implementing Interaction Between the Application and the Driver 1104 1105Write the code for interaction between the user-mode application and the driver. Place the code in the **drivers/hdf_core/adapter/uhdf** directory for compilation. For details about **BUILD.gn**, see **drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn**. 1106 1107```c 1108#include <fcntl.h> 1109#include <sys/stat.h> 1110#include <sys/ioctl.h> 1111#include <unistd.h> 1112#include "hdf_log.h" 1113#include "hdf_sbuf.h" 1114#include "hdf_io_service_if.h" 1115 1116#define HDF_LOG_TAG sample_test 1117#define SAMPLE_SERVICE_NAME "sample_service" 1118 1119#define SAMPLE_WRITE_READ 123 1120 1121int g_replyFlag = 0; 1122 1123static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) 1124{ 1125 const char *string = HdfSbufReadString(data); 1126 if (string == NULL) { 1127 HDF_LOGE("fail to read string in event data"); 1128 g_replyFlag = 1; 1129 return HDF_FAILURE; 1130 } 1131 HDF_LOGI("%{public}s: dev event received: %{public}u %{public}s", (char *)priv, id, string); 1132 g_replyFlag = 1; 1133 return HDF_SUCCESS; 1134} 1135 1136static int SendEvent(struct HdfIoService *serv, char *eventData) 1137{ 1138 int ret = 0; 1139 struct HdfSBuf *data = HdfSbufObtainDefaultSize(); 1140 if (data == NULL) { 1141 HDF_LOGE("fail to obtain sbuf data"); 1142 return 1; 1143 } 1144 1145 struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); 1146 if (reply == NULL) { 1147 HDF_LOGE("fail to obtain sbuf reply"); 1148 ret = HDF_DEV_ERR_NO_MEMORY; 1149 goto out; 1150 } 1151 1152 if (!HdfSbufWriteString(data, eventData)) { 1153 HDF_LOGE("fail to write sbuf"); 1154 ret = HDF_FAILURE; 1155 goto out; 1156 } 1157 1158 ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply); 1159 if (ret != HDF_SUCCESS) { 1160 HDF_LOGE("fail to send service call"); 1161 goto out; 1162 } 1163 1164 int replyData = 0; 1165 if (!HdfSbufReadInt32(reply, &replyData)) { 1166 HDF_LOGE("fail to get service call reply"); 1167 ret = HDF_ERR_INVALID_OBJECT; 1168 goto out; 1169 } 1170 HDF_LOGI("Get reply is: %{public}d", replyData); 1171out: 1172 HdfSbufRecycle(data); 1173 HdfSbufRecycle(reply); 1174 return ret; 1175} 1176 1177int main() 1178{ 1179 char *sendData = "default event info"; 1180 struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME); 1181 if (serv == NULL) { 1182 HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME); 1183 return HDF_FAILURE; 1184 } 1185 1186 static struct HdfDevEventlistener listener = { 1187 .callBack = OnDevEventReceived, 1188 .priv ="Service0" 1189 }; 1190 1191 if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) { 1192 HDF_LOGE("fail to register event listener"); 1193 return HDF_FAILURE; 1194 } 1195 if (SendEvent(serv, sendData)) { 1196 HDF_LOGE("fail to send event"); 1197 return HDF_FAILURE; 1198 } 1199 1200 while (g_replyFlag == 0) { 1201 sleep(1); 1202 } 1203 1204 if (HdfDeviceUnregisterEventListener(serv, &listener)) { 1205 HDF_LOGE("fail to unregister listener"); 1206 return HDF_FAILURE; 1207 } 1208 1209 HdfIoServiceRecycle(serv); 1210 return HDF_SUCCESS; 1211} 1212``` 1213 1214> **NOTE** 1215> 1216> The user-mode application uses the message sending API of the HDF, and the compilation of the user-mode application depends on the dynamic libraries **hdf_core** and **osal** provided by the HDF. Therefore, you need to add the following dependencies to the .gn file: 1217> 1218> deps = [ 1219> 1220> "//drivers/hdf_core/adapter/uhdf/manager:hdf_core", 1221> 1222> "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal", 1223> 1224> ] 1225