The Hardware Driver Foundation (HDF) is designed upon a component-based driver model. This model enables refined driver management and streamlines driver development and deployment. In the HDF, the same type of device drivers are placed in a host. You can develop and deploy the drivers separately. One driver can have multiple nodes.
The Hardware Driver Foundation (HDF) is designed based on a modular driver model to enable refined driver management and streamline driver development and deployment. The HDF allows the same type of device drivers to be 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.
The figure below shows the HDF driver model.
The figure below shows the HDF driver model. A device refers to a physical device. A DeviceNode is a component of a device. A device has at least one DeviceNode. Each DeviceNode can publish a device service. Each DevicdNode has a unique driver to interact with the hardware.
**Figure 1** HDF driver model
...
...
@@ -14,17 +14,15 @@ The figure below shows the HDF driver model.
## How to Develop
The HDF-based driver development process involves driver implementation, build, and configuration. The procedure is as follows:
The HDF-based driver development involves driver implementation, write of the driver compilation script, and driver configuration. The procedure is as follows:
1. Implement a driver.
Write the driver code and register the driver entry with the HDF.
- Write the driver service code.
- Write the driver service code. <br>The following is an example:
The following is an example:
```
```c
#include "hdf_device_desc.h" // Header file that defines the driver development APIs provided by the HDF.
#include "hdf_log.h" // Header file that defines the log APIs provided by the HDF.
...
...
@@ -34,14 +32,14 @@ The HDF-based driver development process involves driver implementation, build,
@@ -51,10 +49,9 @@ The HDF-based driver development process involves driver implementation, build,
return;
}
```
- Register the driver entry with the HDF.
```
```c
// Define a driver entry object. It must be a global variable of the HdfDriverEntry type (defined in hdf_device_desc.h).
structHdfDriverEntryg_sampleDriverEntry={
.moduleVersion=1,
...
...
@@ -64,36 +61,36 @@ The HDF-based driver development process involves driver implementation, build,
.Release=HdfSampleDriverRelease,
};
// Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls the Bind() function and then the Init() function. If the Init() function fails to be called, the HDF will call Release() to release driver resources and exit the driver model.
// 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.
HDF_INIT(g_sampleDriverEntry);
```
2.Build the driver.
2.Write the driver compilation script.
- LiteOS
Modify **Makefile** and **BUILD.gn** files.
Modify **makefile** and **BUILD.gn** files.
- **Makefile**:
Use the **Makefile** template provided by the HDF to compile the driver code.
Use the **makefile** template provided by the HDF to compile the driver code.
```
include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk # (Mandatory) Import the HDF predefined content.
```c
include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # (Mandatory) Import the HDF predefined content.
MODULE_NAME := # File to be generated.
LOCAL_INCLUDE: = # Directory of the driver header files.
LOCAL_SRCS : = # Source code files of the driver.
LOCAL_CFLAGS : = # Custom build options.
include $(HDF_DRIVER) # Import the Makefile template to complete the build.
include $(HDF_DRIVER) # Import the makefile template to complete the build.
```
Add the path of the generated file to **hdf_lite.mk** in the **drivers/adapter/khdf/liteos** directory to link the file to the kernel image. The following is an example:
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. <br>The following is an example:
```
```c
LITEOS_BASELIB += -lxxx # Static library generated by the link.
LIB_SUBDIRS += # Directory in which Makefile is located.
LIB_SUBDIRS += # Directory in which makefile is located.
```
- **BUILD.gn**:
...
...
@@ -101,9 +98,9 @@ The HDF-based driver development process involves driver implementation, build,
Add **BUILD.gn**. The content of **BUILD.gn** is as follows:
@@ -114,42 +111,42 @@ The HDF-based driver development process involves driver implementation, build,
}
config("public") { # Define the head file configuration of the dependencies.
include_dirs = [
"xxx/xxx/xxx", # Directory of dependency header files.
"xxx/xxx/xxx", # Directory of dependency header files.
]
}
```
Add the **BUILD.gn** directory to **/drivers/adapter/khdf/liteos/BUILD.gn**.
Add the **BUILD.gn** directory to **/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**.
```
```c
group("liteos") {
public_deps = [ ":$module_name" ]
deps = [
"xxx/xxx", # Directory where the BUILD.gn of the driver is located. It is a relative path to /drivers/adapter/khdf/liteos.
"xxx/xxx", # Directory where the BUILD.gn of the driver is located. It is a relative path to /drivers/hdf_core/adapter/khdf/liteos.
]
}
```
- Linux
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/adapter/khdf/linux/Kconfig**.
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**.
```
```c
source "drivers/hdf/khdf/xxx/Kconfig" # Kernel directory to which the HDF module is soft linked.
```
Add the driver directory to **drivers/adapter/khdf/linux/Makefile**.
Add the driver directory to **drivers/hdf_core/adapter/khdf/linux/Makefile**.
```
```c
obj-$(CONFIG_DRIVERS_HDF) += xxx/
```
Add a **Makefile** to the driver directory **xxx** and add code compiling rules of the driver to the **Makefile** file.
```
```c
obj-y += xxx.o
```
...
...
@@ -161,16 +158,14 @@ The HDF-based driver development process involves driver implementation, build,
- (Mandatory) Set driver device information.
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 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. <br>The following is an example:
The following is an example:
```
root {
device_info {
match_attr = "hdf_manager";
template host { // Host template. If a node (for example, sample_host) uses the default values in this template, the node fields can be omitted.
template host { // Host template. If a node (for example, sample_host) uses the default values in this template, the node fields can be omitted.
hostName = "";
priority = 100;
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.
...
...
@@ -191,14 +186,14 @@ The HDF-based driver development process involves driver implementation, build,
sample_host :: host{
hostName = "host0"; // Host name. The host node is used as a container to hold a type of drivers.
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.
caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // Linux capabilities of the user-mode process.
caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // Linux capabilities of a user-mode process.
device_sample :: device { // Sample device node.
device0 :: deviceNode { // DeviceNode of the sample driver.
policy = 1; // Policy for publishing the driver service. For details, see Driver Service Management.
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.
preload = 0; // The driver is loaded on demand. For details, see "NOTE" at the end of this document.
permission = 0664; // Permission for the driver to create a device node.
moduleName = "sample_driver"; // Driver name. The value of this field must be the same as that of moduleName in the HdfDriverEntry structure.
preload = 0; // The value 0 means to load the driver by default during the startup of the system.
permission = 0664; // Permission for the DeviceNode created.
moduleName = "sample_driver"; // Driver name. The value must be the same as that of moduleName in the HdfDriverEntry structure.
serviceName = "sample_service"; // Name of the service published by the driver. The service name must be unique.
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.
}
...
...
@@ -218,59 +213,30 @@ The HDF-based driver development process involves driver implementation, build,
>
> - The process UIDs are configured in **base/startup/init_lite/services/etc/passwd**, and the process GIDs are configured in **base/startup/init_lite/services/etc/group**. For details, see [Adding a System Service User Group]( https://gitee.com/openharmony/startup_init_lite/wikis).
>
> - If CAP_DAC_OVERRIDE needs to be configured for a service module, enter **caps = ["DAC_OVERRIDE"]** instead of **caps = ["CAP_DAC_OVERRIDE"]**.
> - 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"]**.
>
> - **preload** specifies the driver loading policy. For details, see [Driver Loading](../driver/driver-hdf-load.md).
- (Optional) Set driver private information.
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 step 1).
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 step 1). <br>The following is an example of the driver configuration:
The following is an example of the driver private configuration:
```
root {
```
root {
SampleDriverConfig {
sample_version = 1;
sample_bus = "I2C_0";
match_attr = "sample_config"; // The value must be the same as that of deviceMatchAttr in device_info.hcs.
}
}
```
After the configuration, add the configuration file to the board-level configuration entry file **hdf.hcs**. (You can use DevEco to perform one-click configuration. For details, see the description about the driver development suite.)
The following is an example:
```
#include "device_info/device_info.hcs"
#include "sample/sample_config.hcs"
```
```
Add the configuration file to the **hdf.hcs** file. <br>The following is an example:
> - If **preload** in the configuration file is set to **0** (**DEVICE_PRELOAD_ENABLE**), the driver is loaded by default during the system boot process.
>
> - If **preload** is set to **1** (**DEVICE_PRELOAD_ENABLE_STEP2**), the driver is loaded 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**.
>
> - If **preload** is set to **2** (**DEVICE_PRELOAD_DISABLE**), the driver service is dynamically loaded. When a user-mode process requests the driver service, the HDF attempts to dynamically load the driver if the driver service is not available. > For more details, see [Driver Messaging Mechanism](../driver/driver-hdf-message-management.md).
>
> - Sequential loading (**preload** set to **0 (DEVICE_PRELOAD_ENABLE)**)
>
> In the configuration file, the **priority** field (ranging from 0 to 200) determines the loading sequence of a host and a driver. For drivers in different hosts, a smaller host priority value indicates a higher driver loading priority; for drivers in the same host, a smaller driver priority value indicates a higher driver loading priority.
The HDF loads the drivers that match the configured device list.
## Driver Loading Policies
The HDF supports on-demand loading and sequential loading of drivers. The **preload** field in the configuration file (see [Driver Development](../driver/driver-hdf-development.md)) specifies the loading policy of a driver. The values are as follows:
```c
typedefenum{
DEVICE_PRELOAD_ENABLE=0,
DEVICE_PRELOAD_ENABLE_STEP2=1,
DEVICE_PRELOAD_DISABLE=2,
DEVICE_PRELOAD_INVALID
}DevicePreload;
```
### On-Demand Loading
- The value **0** (**DEVICE_PRELOAD_ENABLE**) means to load the driver by default during the system boot process.
- The value **1** (**DEVICE_PRELOAD_ENABLE_STEP2**) means to load 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**.
- The value **2** (**DEVICE_PRELOAD_DISABLE**) means to load the driver dynamically. When a user-mode process requests the driver service, the HDF attempts to dynamically load the driver if the driver service is not available. For more details, see [Driver Messaging Mechanism](../driver/driver-hdf-message-management.md).
### Sequential Loading (Default)
The **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 drivers in the host with a smaller priority value are loaded first. For the drivers in the same host, the driver with a smaller priority value is loaded first.
HDF Configuration Source (HCS) is the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, simplifying configuration management.
HDF Configuration Source (HCS) provides the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, thereby facilitating configuration management.
HDF Configuration Generator (HC-GEN) is a tool for converting an HDF configuration file into a file that can be read by the software.
...
...
@@ -31,24 +31,24 @@ The table below describes the keywords used in the HCS syntax.
**Table 1** Keywords used in HCS syntax
| Keyword| Description| Remarks|
| Keyword| Description| Remarks|
| -------- | -------- | -------- |
| root | Sets the root node.| - |
| include | References other HCS files.| - |
| delete | Deletes a node or an attribute.| Applicable only to the configuration tree referenced by **include**.|
| template | Defines a template node.| - |
| match_attr | Marks the node attribute for matching.| During configuration parsing, the attribute value can be used to locate the corresponding node.|
| root | Sets the root node.| - |
| include | References other HCS files.| - |
| delete | Deletes a node or an attribute.| Applicable only to the configuration tree referenced by **include**.|
| template | Defines a template node.| - |
| match_attr | Marks the node attribute for matching.| When parsing the configuration, the driver can use the attribute value as a parameter to call an API to locate the node that has this attribute. |
### Basic Structures
The HCS has two structures: attribute and node.
**Attribute**
Attribute
An attribute is the minimum, independent configuration unit. The syntax is as follows:
```
attribute_name = value;
```
...
...
@@ -57,7 +57,7 @@ An attribute is the minimum, independent configuration unit. The syntax is as fo
- The **value** can be in any of the following formats:
- A binary, octal, decimal, or hexadecimal integer. For details, see the **Data Types** section.
- A binary, octal, decimal, or hexadecimal integer. For details, see [Data Types](#data-types).
- String quoted by double quotation marks ("").
- Node reference.
...
...
@@ -67,7 +67,7 @@ An attribute is the minimum, independent configuration unit. The syntax is as fo
A node is a set of attributes. The syntax is as follows:
```
node_name {
module = "sample";
...
...
@@ -97,7 +97,7 @@ Attributes automatically use built-in data types, including integer, string, arr
- Octal: prefixed with **0**, for example, **0664**.
- Decimal: signed or unsigned, without prefix, for example, **1024** or **+1024**. Negative integers can be read only via signed interfaces.
- Decimal: signed or unsigned, without prefix, for example, **1024** or **+1024**. Negative integers can be read only via APIs with signed numbers.
- Hexadecimal: prefixed with **0x**, for example, **0xff00** and **0xFF**.
...
...
@@ -109,13 +109,13 @@ A string is enclosed in double quotation marks ("").
An array can hold either integers or strings, but not a mixture 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:
```
attr_foo = [0x01, 0x02, 0x03, 0x04];
attr_bar = ["hello", "world"];
```
**Boolean**
Boolean
Boolean data type is a form of data with only two possible values: **true** and **false**.
...
...
@@ -126,7 +126,7 @@ Boolean data type is a form of data with only two possible values: **true** and
The keyword **include** is used to import other HCS files. The syntax is as follows:
```
#include "foo.hcs"
#include "../bar.hcs"
...
...
@@ -143,14 +143,14 @@ The following two comment formats are supported:
- Single-line comment
```
// comment
```
- Multi-line comment
```
/*
comment
...
...
@@ -165,13 +165,13 @@ The following two comment formats are supported:
You can reference the content of a node to modify the content of another node. The syntax is as follows:
```
node :& source_node
```
In this statement, the content of **node** is referenced to modify the content of **source_node**.
Example:
```
root {
...
...
@@ -196,7 +196,7 @@ root {
The configuration tree generated is as follows:
```
root {
module = "sample";
...
...
@@ -222,7 +222,7 @@ In this example, the value of **bar.attr** is changed to **foo** by referencing
You can replicate a node to define a node with similar content. The syntax is as follows:
```
node : source_node
```
...
...
@@ -230,7 +230,7 @@ You can replicate a node to define a node with similar content. The syntax is as
This statement replicates the attributes of the **source_node** node to define **node**.
Example:
```
root {
module = "sample";
...
...
@@ -245,7 +245,7 @@ root {
The configuration tree generated is as follows:
```
root {
module = "sample";
...
...
@@ -269,7 +269,7 @@ You do not need to specify the path of the **foo** node if the **foo** node and
You 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.
Example:
```
// sample2.hcs
root {
...
...
@@ -291,7 +291,7 @@ root {
The configuration tree generated is as follows:
```
root {
attr_1 = 0x1;
...
...
@@ -306,7 +306,7 @@ root {
You 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:
```
attribute = &node;
```
...
...
@@ -314,7 +314,7 @@ You can associate an attribute and a node so that the node can be quickly locate
In 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**.
Example:
```
node1 {
attributes;
...
...
@@ -324,9 +324,9 @@ node2 {
}
```
or
Or
```
node2 {
node1 {
...
...
@@ -344,7 +344,7 @@ The template is used to generate nodes with consistent syntax, thereby facilitat
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.
Example:
```
root {
module = "sample";
...
...
@@ -364,7 +364,7 @@ root {
The configuration tree generated is as follows:
```
root {
module = "sample";
...
...
@@ -382,7 +382,7 @@ root {
In 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.
## **Configuration Generation**
## Configuration Generation
The HC-GEN tool checks the HCS configuration syntax and converts HCS source files into HCB files.
...
...
@@ -391,7 +391,7 @@ The HC-GEN tool checks the HCS configuration syntax and converts HCS source file
@@ -10,23 +10,23 @@ The HDF messaging mechanism implements the interaction between the user-mode app
The messaging mechanism allows:
1.User-mode applications to send messages to drivers.
1.A user-mode application to send a message to a driver.
2.User-mode applications to receive events from drivers.
2.A user-mode application to receive events reported by a driver.
**Table 1** APIs for the driver messaging mechanism
| API| Description|
| -------- | -------- |
| struct HdfIoService \*HdfIoServiceBind(const char \*serviceName); | Obtains a driver service. The **Dispatch()** method in the driver service obtained sends messages to the driver.|
| void HdfIoServiceRecycle(struct HdfIoService \*service); | Releases a driver service.|
| int HdfDeviceRegisterEventListener(struct HdfIoService \*target, struct HdfDevEventlistener \*listener); | Registers the method for receiving events reported by the driver.|
| struct HdfIoService \*HdfIoServiceBind(const char \*serviceName); | Obtains a driver service. After obtaining the driver service, the user-mode application uses **Dispatch()** in the driver service obtained to send messages to the driver.|
| void HdfIoServiceRecycle(struct HdfIoService \*service); | Releases a driver service.|
| int HdfDeviceRegisterEventListener(struct HdfIoService \*target, struct HdfDevEventlistener \*listener); | Registers the method for receiving events reported by the driver.|
1. In the driver configuration, set **policy** to **2**. For more details, see [policy](../driver/driver-hdf-servicemanage.md).
1. In the driver configuration file, set **policy** to **2**. For more details, see [Driver Service Management](../driver/driver-hdf-servicemanage.md).
```
device_sample :: Device {
...
...
@@ -35,23 +35,23 @@ The messaging mechanism allows:
}
```
2. Set the driver permission. By default, the **permission** field is set to **0666**, which allows the driver to create device nodes. You can set this field based on service requirements.
2. 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.
3. Implement the **Dispatch()** method of **IDeviceIoService**.
```
// Dispatch messages sent from the user-mode application.
The Hardware Driver Foundation (HDF) provides driver framework capabilities including driver loading, driver service management, and driver messaging mechanism. It strives to build a unified driver architecture platform to provide a more precise and efficient development environment, where you can perform one-time development for multi-device deployment.
The Hardware Driver Foundation (HDF) provides driver framework capabilities, such as driver loading, driver service management, driver messaging mechanism, and configuration management, for driver developers. It strives to provide a more precise and efficient driver development environment, where you can perform one-time development for multi-device deployment.
## Driver Loading
...
...
@@ -12,18 +12,23 @@ The HDF supports the following loading modes:
- On-demand loading
The driver is loaded by default during the operating system (OS) boot process or dynamically loaded after OS is started.
The HDF allows drivers to be loaded by default during the operating system (OS) boot process or dynamically loaded after the OS is started.
- Sequential loading
The driver is loaded based on its priority during the OS boot process.
The HDF allows drivers to be loaded based on their priorities during the OS boot process.
## Driver Service Management
The HDF allows centralized management of driver services. You can obtain a driver service by using the API provided by the HDF.
The HDF supports centralized management of driver services. You can obtain a driver service by using the API provided by the HDF.
## Driver messaging mechanism
## Driver Messaging Mechanism
The HDF provides a unified driver messaging mechanism, which allows messages to be exchanged between user-mode applications and kernel-mode drivers.
## Configuration Management
HDF Configuration Source (HCS) provides the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, simplifying configuration management.
## Implementing Interaction Between the Application and the Driver
Write the code for interaction between the user-mode application and the driver. Place the code in the **drivers/adapter/uhdf** directory for compilation. For details about **build.gn**, see **drivers/framework/sample/platform/uart/dev/build.gn**.
Write 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**.
```
```c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
...
...
@@ -134,7 +134,7 @@ Write the code for interaction between the user-mode application and the driver.
@@ -7,8 +7,8 @@ Driver services are objects of capabilities provided by HDF driver devices to ex
The HDF uses the **policy** field in the configuration file to define policies for a driver to provide services externally. The values this field are as follows:
```
```c
typedefenum{
/* The driver does not provide services. */
SERVICE_POLICY_NONE=0,
...
...
@@ -28,7 +28,7 @@ typedef enum {
## When to Use
The driver service management capability can be used if the driver provides capabilities using APIs.
You can use the driver service management capability of the HDF when the driver needs to provide capabilities via APIs.
## Available APIs
...
...
@@ -37,7 +37,7 @@ The table below describes the APIs for driver service management.
**Table 1** APIs for driver service management
| API| Description|
| API| Description|
| -------- | -------- |
| int32_t (\*Bind)(struct HdfDeviceObject \*deviceObject) | Binds a service API to the HDF. You need to implement the **Bind()** function.|
If the service requester clearly knows the time when the driver is loaded, use the API provided by the HDF to obtain the driver service. The following is an example:
If the kernel mode is unaware of the time for loading drivers on the same host, use the subscription mechanism provided by the HDF to subscribe to the drivers. After the drivers are loaded, the HDF publishes the driver services to the subscriber.
The implementation is as follows:
```
// Callback invoked to return the driver services after the subscribed driver is loaded.
// object is the pointer to the private data of the subscriber, and service is the pointer to the subscribed service object.
If the service requester is unaware of the time when the driver (in the same host) is loaded, 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:
```c
// Callback invoked to return the driver service after the subscribed driver is loaded.
// object is the pointer to the private data of the subscriber, and service is the pointer to the subscribed service object.