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