1e41f4b71Sopenharmony_ci# DAC 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci## Overview 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci### DAC 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ciA digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics. 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ciThe DAC module supports development of digital-to-analog conversion. The DAC devices can be used to: 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci- Provide the output channel for the process control computer system and connect to the executor to implement automatic control of the production process. 12e41f4b71Sopenharmony_ci- Serve as an important module in the analog-to-digital converter using feedback technologies. 13e41f4b71Sopenharmony_ci 14e41f4b71Sopenharmony_ci### Basic Concepts 15e41f4b71Sopenharmony_ci 16e41f4b71Sopenharmony_ci- Resolution 17e41f4b71Sopenharmony_ci 18e41f4b71Sopenharmony_ci The number of binary bits that can be converted by a DAC. A greater number of bits indicates a higher resolution. 19e41f4b71Sopenharmony_ci 20e41f4b71Sopenharmony_ci- Conversion precision 21e41f4b71Sopenharmony_ci 22e41f4b71Sopenharmony_ci Difference between the actual output value of the DAC and the theoretical value when the maximum value is added to the input end. The conversion precision of a DAC converter varies depending on the structure of the chip integrated on the DAC and the interface circuit configuration. The ideal conversion precision value should be as small as possible. To achieve optimal DAC conversion precision, the DAC must have high resolution. In addition, errors in the devices or power supply of the interface circuits will affect the conversion precision. When the error exceeds a certain degree, a DAC conversion error will be caused. 23e41f4b71Sopenharmony_ci 24e41f4b71Sopenharmony_ci- Conversion speed 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci The conversion speed is determined by the setup time. The setup time is the period from the time the input suddenly changes from all 0s to all 1s to the time the output voltage remains within the FSR ± ½LSB (or FSR ± x%FSR). It is the maximum response time of the DAC, and hence used to measure the conversion speed. 27e41f4b71Sopenharmony_ci 28e41f4b71Sopenharmony_ci - The full scale range (FSR) is the maximum range of the output signal amplitude of a DAC. Different DACs have different FSRs, which can be limited by positive and negative currents or voltages. 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci - The least significant byte (LSB) refers to bit 0 (the least significant bit) in a binary number. 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci### Working Principles 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ciIn the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a device service is used as the DAC manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The DAC module uses the unified service mode, as shown in Figure 1. 35e41f4b71Sopenharmony_ci 36e41f4b71Sopenharmony_ciThe DAC module is divided into the following layers: 37e41f4b71Sopenharmony_ci- Interface layer: provides the capabilities of opening and closing a device and writing data. 38e41f4b71Sopenharmony_ci- Core layer: provides the capabilities of binding, initializing, and releasing devices. 39e41f4b71Sopenharmony_ci- Adaptation layer: implements hardware-related functions, such as controller initialization. 40e41f4b71Sopenharmony_ci 41e41f4b71Sopenharmony_ciIn the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. 42e41f4b71Sopenharmony_ci 43e41f4b71Sopenharmony_ci> **NOTE**<br/>The core layer can call the APIs of the interface layer and uses hooks to call APIs of the adaptation layer. In this way, the adaptation layer can indirectly call the APIs of the interface layer, but the interface layer cannot call the APIs of the adaptation layer. 44e41f4b71Sopenharmony_ci 45e41f4b71Sopenharmony_ci**Figure 1** Unified service mode 46e41f4b71Sopenharmony_ci 47e41f4b71Sopenharmony_ci 48e41f4b71Sopenharmony_ci 49e41f4b71Sopenharmony_ci### Constraints 50e41f4b71Sopenharmony_ci 51e41f4b71Sopenharmony_ciThe DAC module supports only the kernel (LiteOS-A) for mini and small systems. 52e41f4b71Sopenharmony_ci 53e41f4b71Sopenharmony_ci## Development Guidelines 54e41f4b71Sopenharmony_ci 55e41f4b71Sopenharmony_ci### When to Use 56e41f4b71Sopenharmony_ci 57e41f4b71Sopenharmony_ciThe DAC module is used for digital-to-analog conversion, audio output, and motor control. The DAC driver is used when the digital signals input by the DAC module are converted into analog signals to output. Before using DAC devices with OpenHarmony, you need to adapt the DAC driver to OpenHarmony. The following describes how to do it. 58e41f4b71Sopenharmony_ci 59e41f4b71Sopenharmony_ci### Available APIs 60e41f4b71Sopenharmony_ci 61e41f4b71Sopenharmony_ciTo enable the upper layer to successfully operate the hardware by calling the DAC APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/dac/dac_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. 62e41f4b71Sopenharmony_ci 63e41f4b71Sopenharmony_ciDefinitions of **DacMethod** and **DacLockMethod**: 64e41f4b71Sopenharmony_ci 65e41f4b71Sopenharmony_ci```c++ 66e41f4b71Sopenharmony_cistruct DacMethod { 67e41f4b71Sopenharmony_ci /* Hook used to write data. */ 68e41f4b71Sopenharmony_ci int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val); 69e41f4b71Sopenharmony_ci /* Hook used to start a DAC device. */ 70e41f4b71Sopenharmony_ci int32_t (*start)(struct DacDevice *device); 71e41f4b71Sopenharmony_ci /* Hook used to stop a DAC device. */ 72e41f4b71Sopenharmony_ci int32_t (*stop)(struct DacDevice *device); 73e41f4b71Sopenharmony_ci}; 74e41f4b71Sopenharmony_ci 75e41f4b71Sopenharmony_cistruct DacLockMethod { 76e41f4b71Sopenharmony_ci int32_t (*lock)(struct DacDevice *device); 77e41f4b71Sopenharmony_ci void (*unlock)(struct DacDevice *device); 78e41f4b71Sopenharmony_ci}; 79e41f4b71Sopenharmony_ci``` 80e41f4b71Sopenharmony_ciAt the adaptation layer, **DacMethod** must be implemented, and **DacLockMethod** can be implemented based on service requirements. The core layer provides the default **DacLockMethod**, in which a spinlock is used to protect the critical section. 81e41f4b71Sopenharmony_ci 82e41f4b71Sopenharmony_ci```c 83e41f4b71Sopenharmony_cistatic int32_t DacDeviceLockDefault(struct DacDevice *device) 84e41f4b71Sopenharmony_ci{ 85e41f4b71Sopenharmony_ci if (device == NULL) { 86e41f4b71Sopenharmony_ci HDF_LOGE("%s: device is null", __func__); 87e41f4b71Sopenharmony_ci return HDF_ERR_INVALID_OBJECT; 88e41f4b71Sopenharmony_ci } 89e41f4b71Sopenharmony_ci return OsalSpinLock(&device->spin); 90e41f4b71Sopenharmony_ci} 91e41f4b71Sopenharmony_ci 92e41f4b71Sopenharmony_cistatic void DacDeviceUnlockDefault(struct DacDevice *device) 93e41f4b71Sopenharmony_ci{ 94e41f4b71Sopenharmony_ci if (device == NULL) { 95e41f4b71Sopenharmony_ci HDF_LOGE("%s: device is null", __func__); 96e41f4b71Sopenharmony_ci return; 97e41f4b71Sopenharmony_ci } 98e41f4b71Sopenharmony_ci (void)OsalSpinUnlock(&device->spin); 99e41f4b71Sopenharmony_ci} 100e41f4b71Sopenharmony_ci 101e41f4b71Sopenharmony_cistatic const struct DacLockMethod g_dacLockOpsDefault = { 102e41f4b71Sopenharmony_ci .lock = DacDeviceLockDefault, 103e41f4b71Sopenharmony_ci .unlock = DacDeviceUnlockDefault, 104e41f4b71Sopenharmony_ci}; 105e41f4b71Sopenharmony_ci``` 106e41f4b71Sopenharmony_ci 107e41f4b71Sopenharmony_ciIf spinlock cannot be used, you can use another type of lock to implement **DacLockMethod**. The implemented **DacLockMethod** will replace the default **DacLockMethod**. 108e41f4b71Sopenharmony_ci 109e41f4b71Sopenharmony_ci**Table 1** Hook functions in **DacMethod** 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci| Function| Input Parameter | Output Parameter| Return Value | Description | 112e41f4b71Sopenharmony_ci| -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- | 113e41f4b71Sopenharmony_ci| write | **device**: structure pointer to the DAC controller at the core layer.<br>**channel**: channel ID, which is of the uint32_t type.<br>**val**: data to write, which is of the uint32_t type.| - | HDF_STATUS| Writes the target digit-to-analog (DA) value.| 114e41f4b71Sopenharmony_ci| start | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Starts a DAC device. | 115e41f4b71Sopenharmony_ci| stop | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Stops a DAC device. | 116e41f4b71Sopenharmony_ci 117e41f4b71Sopenharmony_ci**Table 2** Functions in **DacLockMethod** 118e41f4b71Sopenharmony_ci 119e41f4b71Sopenharmony_ci| Function| Input Parameter| Output Parameter| Return Value| Description| 120e41f4b71Sopenharmony_ci| -------- | -------- | -------- | -------- | -------- | 121e41f4b71Sopenharmony_ci| lock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Acquires the critical section lock.| 122e41f4b71Sopenharmony_ci| unlock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Releases the critical section lock.| 123e41f4b71Sopenharmony_ci 124e41f4b71Sopenharmony_ci 125e41f4b71Sopenharmony_ci### How to Develop 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_ciThe DAC module adaptation procedure is as follows: 128e41f4b71Sopenharmony_ci 129e41f4b71Sopenharmony_ci1. Instantiate the driver entry. 130e41f4b71Sopenharmony_ci2. Configure attribute files. 131e41f4b71Sopenharmony_ci3. Instantiate the core layer APIs. 132e41f4b71Sopenharmony_ci4. Debug the driver. 133e41f4b71Sopenharmony_ci 134e41f4b71Sopenharmony_ci### Example 135e41f4b71Sopenharmony_ci 136e41f4b71Sopenharmony_ciThe following uses the Hi3516D V300 driver **//device/soc/hisilicon/common/platform/dac/dac_hi35xx.c** as an example to describe how to perform the DAC driver adaptation. 137e41f4b71Sopenharmony_ci 138e41f4b71Sopenharmony_ci1. Instantiate the driver entry. 139e41f4b71Sopenharmony_ci 140e41f4b71Sopenharmony_ci The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the module name must be the same as that in **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. 141e41f4b71Sopenharmony_ci 142e41f4b71Sopenharmony_ci Generally, the HDF calls **Init()** to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. 143e41f4b71Sopenharmony_ci 144e41f4b71Sopenharmony_ci ```c++ 145e41f4b71Sopenharmony_ci static struct HdfDriverEntry g_dacDriverEntry = { 146e41f4b71Sopenharmony_ci .moduleVersion = 1, 147e41f4b71Sopenharmony_ci .Init = VirtualDacInit, 148e41f4b71Sopenharmony_ci .Release = VirtualDacRelease, 149e41f4b71Sopenharmony_ci .moduleName = "virtual_dac_driver",// (Mandatory) The value must be the same as that in the .hcs file. 150e41f4b71Sopenharmony_ci }; 151e41f4b71Sopenharmony_ci HDF_INIT(g_dacDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. 152e41f4b71Sopenharmony_ci ``` 153e41f4b71Sopenharmony_ci 154e41f4b71Sopenharmony_ci2. Configure attribute files. 155e41f4b71Sopenharmony_ci 156e41f4b71Sopenharmony_ci - Add the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. 157e41f4b71Sopenharmony_ci 158e41f4b71Sopenharmony_ci The device attribute values are closely related to the driver implementation and the default values or value ranges of the **DacDevice** members at the core layer, for example, the number of device channels and the maximum transmission speed. 159e41f4b71Sopenharmony_ci 160e41f4b71Sopenharmony_ci In the unified service mode, the first device node in the **device_info.hcs** file must be the DAC manager. The parameters must be set as follows: 161e41f4b71Sopenharmony_ci 162e41f4b71Sopenharmony_ci | Parameter | Value | 163e41f4b71Sopenharmony_ci | --------------- | ------------------------------------------------------------------- | 164e41f4b71Sopenharmony_ci | policy | **0**, which indicates that no service is published. | 165e41f4b71Sopenharmony_ci | priority | Driver startup priority. The value range is 0 to 200. A larger value indicates a lower priority. For the drivers with the same priority, the device loads them randomly.| 166e41f4b71Sopenharmony_ci | permission | Driver permission. | 167e41f4b71Sopenharmony_ci | moduleName | **HDF_PLATFORM_DAC_MANAGER** | 168e41f4b71Sopenharmony_ci | serviceName | **HDF_PLATFORM_DAC_MANAGER** | 169e41f4b71Sopenharmony_ci | deviceMatchAttr | Reserved. | 170e41f4b71Sopenharmony_ci 171e41f4b71Sopenharmony_ci Configure DAC controller information from the second node. This node specifies a type of DAC controllers rather than a specific DAC controller. In this example, there is only one DAC device. If there are multiple DAC devices, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **dac_config.hcs** file for each device. 172e41f4b71Sopenharmony_ci 173e41f4b71Sopenharmony_ci **device_info.hcs** example: 174e41f4b71Sopenharmony_ci 175e41f4b71Sopenharmony_ci ```hcs 176e41f4b71Sopenharmony_ci root { 177e41f4b71Sopenharmony_ci device_dac :: device { 178e41f4b71Sopenharmony_ci /* device0 is the DAC manager. */ 179e41f4b71Sopenharmony_ci device0 :: deviceNode { 180e41f4b71Sopenharmony_ci policy = 0; 181e41f4b71Sopenharmony_ci priority = 52; 182e41f4b71Sopenharmony_ci permission = 0644; 183e41f4b71Sopenharmony_ci serviceName = "HDF_PLATFORM_DAC_MANAGER"; 184e41f4b71Sopenharmony_ci moduleName = "HDF_PLATFORM_DAC_MANAGER"; 185e41f4b71Sopenharmony_ci } 186e41f4b71Sopenharmony_ci } 187e41f4b71Sopenharmony_ci /* dac_virtual is a DAC controller. */ 188e41f4b71Sopenharmony_ci dac_virtual :: deviceNode { 189e41f4b71Sopenharmony_ci policy = 0; 190e41f4b71Sopenharmony_ci priority = 56; 191e41f4b71Sopenharmony_ci permission = 0644; 192e41f4b71Sopenharmony_ci moduleName = "virtual_dac_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. 193e41f4b71Sopenharmony_ci serviceName = "VIRTUAL_DAC_DRIVER"; // (Mandatory) Unique name of the service published by the driver. 194e41f4b71Sopenharmony_ci deviceMatchAttr = "virtual_dac"; // (Mandatory) Controller private data, which must be same as that of the controller in dac_config.hcs. 195e41f4b71Sopenharmony_ci } 196e41f4b71Sopenharmony_ci } 197e41f4b71Sopenharmony_ci ``` 198e41f4b71Sopenharmony_ci 199e41f4b71Sopenharmony_ci - Configure the **dac_test_config.hcs** file. 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci Add a file to the directory of a product to configure driver parameters. For example, add the **vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** file for the hispark_taurus development board. 202e41f4b71Sopenharmony_ci 203e41f4b71Sopenharmony_ci The configuration parameters are as follows: 204e41f4b71Sopenharmony_ci 205e41f4b71Sopenharmony_ci ```hcs 206e41f4b71Sopenharmony_ci root { 207e41f4b71Sopenharmony_ci platform { 208e41f4b71Sopenharmony_ci dac_config { 209e41f4b71Sopenharmony_ci match_attr = "virtual_dac"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. 210e41f4b71Sopenharmony_ci template dac_device { 211e41f4b71Sopenharmony_ci deviceNum = 0; // Device number. 212e41f4b71Sopenharmony_ci validChannel = 0x1; // Valid channel 1. 213e41f4b71Sopenharmony_ci rate = 20000; // Transmission speed. 214e41f4b71Sopenharmony_ci } 215e41f4b71Sopenharmony_ci device_0 :: dac_device { 216e41f4b71Sopenharmony_ci deviceNum = 0; // Device number. 217e41f4b71Sopenharmony_ci validChannel = 0x2; // Valid channel 2. 218e41f4b71Sopenharmony_ci } 219e41f4b71Sopenharmony_ci } 220e41f4b71Sopenharmony_ci } 221e41f4b71Sopenharmony_ci } 222e41f4b71Sopenharmony_ci ``` 223e41f4b71Sopenharmony_ci 224e41f4b71Sopenharmony_ci After the **dac_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. 225e41f4b71Sopenharmony_ci 226e41f4b71Sopenharmony_ci For example, if the path of **dac_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs**, add the following statement to **hdf.hcs** of the product: 227e41f4b71Sopenharmony_ci 228e41f4b71Sopenharmony_ci ```c 229e41f4b71Sopenharmony_ci #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs" // Relative path of the file. 230e41f4b71Sopenharmony_ci ``` 231e41f4b71Sopenharmony_ci 232e41f4b71Sopenharmony_ci3. Instantiate the core layer APIs. 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci - Initialize the **DacDevice** object. 235e41f4b71Sopenharmony_ci 236e41f4b71Sopenharmony_ci Initialize **DacDevice** in the **VirtualDacParseAndInit** function. 237e41f4b71Sopenharmony_ci 238e41f4b71Sopenharmony_ci ```c++ 239e41f4b71Sopenharmony_ci /* Custom structure of the virtual driver. */ 240e41f4b71Sopenharmony_ci struct VirtualDacDevice { 241e41f4b71Sopenharmony_ci /* DAC device structure. */ 242e41f4b71Sopenharmony_ci struct DacDevice device; 243e41f4b71Sopenharmony_ci /* DAC device number. */ 244e41f4b71Sopenharmony_ci uint32_t deviceNum; 245e41f4b71Sopenharmony_ci /* Valid channel. */ 246e41f4b71Sopenharmony_ci uint32_t validChannel; 247e41f4b71Sopenharmony_ci /* DAC rate. */ 248e41f4b71Sopenharmony_ci uint32_t rate; 249e41f4b71Sopenharmony_ci }; 250e41f4b71Sopenharmony_ci /* Parse and initialize the **DacDevice** object of the core layer. */ 251e41f4b71Sopenharmony_ci static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) 252e41f4b71Sopenharmony_ci { 253e41f4b71Sopenharmony_ci /* Define the return values. */ 254e41f4b71Sopenharmony_ci int32_t ret; 255e41f4b71Sopenharmony_ci /* Pointer to the virtual DAC device. */ 256e41f4b71Sopenharmony_ci struct VirtualDacDevice *virtual = NULL; 257e41f4b71Sopenharmony_ci (void)device; 258e41f4b71Sopenharmony_ci /* Allocate space for this pointer. */ 259e41f4b71Sopenharmony_ci virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); 260e41f4b71Sopenharmony_ci if (virtual == NULL) { 261e41f4b71Sopenharmony_ci /* If the value is null, return an error code. */ 262e41f4b71Sopenharmony_ci HDF_LOGE("%s: Malloc virtual fail!", __func__); 263e41f4b71Sopenharmony_ci return HDF_ERR_MALLOC_FAIL; 264e41f4b71Sopenharmony_ci } 265e41f4b71Sopenharmony_ci /* Read the attribute file. */ 266e41f4b71Sopenharmony_ci ret = VirtualDacReadDrs(virtual, node); 267e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 268e41f4b71Sopenharmony_ci /* Failed to read the file. */ 269e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); 270e41f4b71Sopenharmony_ci /* Release the space for the virtual DAC device. */ 271e41f4b71Sopenharmony_ci OsalMemFree(virtual); 272e41f4b71Sopenharmony_ci /* Set the pointer to 0. */ 273e41f4b71Sopenharmony_ci virtual = NULL; 274e41f4b71Sopenharmony_ci return ret; 275e41f4b71Sopenharmony_ci } 276e41f4b71Sopenharmony_ci /* Initialize the pointer to the virtual DAC device. */ 277e41f4b71Sopenharmony_ci VirtualDacDeviceInit(virtual); 278e41f4b71Sopenharmony_ci /* Initialize the priv object in DacDevice. */ 279e41f4b71Sopenharmony_ci virtual->device.priv = (void *)node; 280e41f4b71Sopenharmony_ci /* Initialize the devNum object in DacDevice. */ 281e41f4b71Sopenharmony_ci virtual->device.devNum = virtual->deviceNum; 282e41f4b71Sopenharmony_ci /* Initialize the ops object in DacDevice. */ 283e41f4b71Sopenharmony_ci virtual->device.ops = &g_method; 284e41f4b71Sopenharmony_ci /* Add a DAC device. */ 285e41f4b71Sopenharmony_ci ret = DacDeviceAdd(&virtual->device); 286e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 287e41f4b71Sopenharmony_ci /* Failed to add the device. */ 288e41f4b71Sopenharmony_ci HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); 289e41f4b71Sopenharmony_ci /* Release the space for the virtual DAC device. */ 290e41f4b71Sopenharmony_ci OsalMemFree(virtual); 291e41f4b71Sopenharmony_ci /* Set this pointer to null. */ 292e41f4b71Sopenharmony_ci virtual = NULL; 293e41f4b71Sopenharmony_ci return ret; 294e41f4b71Sopenharmony_ci } 295e41f4b71Sopenharmony_ci 296e41f4b71Sopenharmony_ci return HDF_SUCCESS; 297e41f4b71Sopenharmony_ci } 298e41f4b71Sopenharmony_ci ``` 299e41f4b71Sopenharmony_ci 300e41f4b71Sopenharmony_ci 301e41f4b71Sopenharmony_ci 302e41f4b71Sopenharmony_ci - Define a custom structure. 303e41f4b71Sopenharmony_ci 304e41f4b71Sopenharmony_ci The custom structure holds parameters and data for the driver. Define the custom structure based on the function parameters of the device. The **DacTestReadConfig()** provided by the HDF reads the values in the **dac_config.hcs** file, and **DeviceResourceIface()** initializes the custom structure and passes some important parameters, such as the device number and bus number, to the **DacDevice** object at the core layer. 305e41f4b71Sopenharmony_ci 306e41f4b71Sopenharmony_ci ```c++ 307e41f4b71Sopenharmony_ci struct VirtualDacDevice { 308e41f4b71Sopenharmony_ci struct DacDevice device;// (Mandatory) Control object at the core layer. For details, see the description below. 309e41f4b71Sopenharmony_ci uint32_t deviceNum; // (Mandatory) Device number. 310e41f4b71Sopenharmony_ci uint32_t validChannel; // (Mandatory) Valid channel. 311e41f4b71Sopenharmony_ci uint32_t rate; // (Mandatory) Sampling rate. 312e41f4b71Sopenharmony_ci }; 313e41f4b71Sopenharmony_ci 314e41f4b71Sopenharmony_ci /* DacDevice is the core layer controller structure. The Init() function assigns values to the members of DacDevice. */ 315e41f4b71Sopenharmony_ci struct DacDevice { 316e41f4b71Sopenharmony_ci const struct DacMethod *ops; 317e41f4b71Sopenharmony_ci OsalSpinlock spin; // Spinlock. 318e41f4b71Sopenharmony_ci uint32_t devNum; // Device number. 319e41f4b71Sopenharmony_ci uint32_t chanNum; // Device channel number. 320e41f4b71Sopenharmony_ci const struct DacLockMethod *lockOps; 321e41f4b71Sopenharmony_ci void *priv; 322e41f4b71Sopenharmony_ci }; 323e41f4b71Sopenharmony_ci ``` 324e41f4b71Sopenharmony_ci 325e41f4b71Sopenharmony_ci 326e41f4b71Sopenharmony_ci 327e41f4b71Sopenharmony_ci - Instantiate **DacDevice** in **DacMethod**. 328e41f4b71Sopenharmony_ci 329e41f4b71Sopenharmony_ci The **VirtualDacWrite**, **VirtualDacStop**, and **VirtualDacStart** functions are instantiated in **dac_virtual.c**. 330e41f4b71Sopenharmony_ci 331e41f4b71Sopenharmony_ci ```c++ 332e41f4b71Sopenharmony_ci static const struct DacMethod g_method = { 333e41f4b71Sopenharmony_ci .write = VirtualDacWrite, // Write data to a DAC device. 334e41f4b71Sopenharmony_ci .stop = VirtualDacStop, // Stop a DAC device. 335e41f4b71Sopenharmony_ci .start = VirtualDacStart, // Start a DAC device. 336e41f4b71Sopenharmony_ci }; 337e41f4b71Sopenharmony_ci ``` 338e41f4b71Sopenharmony_ci 339e41f4b71Sopenharmony_ci > **NOTE**<br>For details about **DacMethod**, see [Available APIs](#available-apis). 340e41f4b71Sopenharmony_ci 341e41f4b71Sopenharmony_ci 342e41f4b71Sopenharmony_ci 343e41f4b71Sopenharmony_ci - Implement the **Init** function. 344e41f4b71Sopenharmony_ci 345e41f4b71Sopenharmony_ci Input parameter: 346e41f4b71Sopenharmony_ci 347e41f4b71Sopenharmony_ci **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 348e41f4b71Sopenharmony_ci 349e41f4b71Sopenharmony_ci Return value: 350e41f4b71Sopenharmony_ci 351e41f4b71Sopenharmony_ci **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. 352e41f4b71Sopenharmony_ci 353e41f4b71Sopenharmony_ci | Status | Description | 354e41f4b71Sopenharmony_ci | ---------------------- | -------------------------- | 355e41f4b71Sopenharmony_ci | HDF_ERR_INVALID_OBJECT | Invalid controller object. | 356e41f4b71Sopenharmony_ci | HDF_ERR_INVALID_PARAM | Invalid parameter. | 357e41f4b71Sopenharmony_ci | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | 358e41f4b71Sopenharmony_ci | HDF_ERR_IO | I/O error. | 359e41f4b71Sopenharmony_ci | HDF_SUCCESS | Transmission successful. | 360e41f4b71Sopenharmony_ci | HDF_FAILURE | Transmission failed. | 361e41f4b71Sopenharmony_ci 362e41f4b71Sopenharmony_ci 363e41f4b71Sopenharmony_ci 364e41f4b71Sopenharmony_ci Function description: 365e41f4b71Sopenharmony_ci 366e41f4b71Sopenharmony_ci Initializes the custom structure object and **DacDevice**, and calls the **DacDeviceAdd** function at the core layer. 367e41f4b71Sopenharmony_ci 368e41f4b71Sopenharmony_ci ```c++ 369e41f4b71Sopenharmony_ci static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) 370e41f4b71Sopenharmony_ci { 371e41f4b71Sopenharmony_ci /* Define the return values. */ 372e41f4b71Sopenharmony_ci int32_t ret; 373e41f4b71Sopenharmony_ci /* Pointer to the VirtualDacDevice structure. */ 374e41f4b71Sopenharmony_ci struct VirtualDacDevice *virtual = NULL; 375e41f4b71Sopenharmony_ci (void)device; 376e41f4b71Sopenharmony_ci /* Allocate memory of the specified size. */ 377e41f4b71Sopenharmony_ci virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); 378e41f4b71Sopenharmony_ci if (virtual == NULL) { 379e41f4b71Sopenharmony_ci /* Failed to allocate memory. */ 380e41f4b71Sopenharmony_ci HDF_LOGE("%s: Malloc virtual fail!", __func__); 381e41f4b71Sopenharmony_ci return HDF_ERR_MALLOC_FAIL; 382e41f4b71Sopenharmony_ci } 383e41f4b71Sopenharmony_ci /* Read the node parameters in the HCS. The function definition is as follows. */ 384e41f4b71Sopenharmony_ci ret = VirtualDacReadDrs(virtual, node); 385e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 386e41f4b71Sopenharmony_ci /* Failed to read the node data. */ 387e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); 388e41f4b71Sopenharmony_ci goto __ERR__; 389e41f4b71Sopenharmony_ci } 390e41f4b71Sopenharmony_ci /* Initialize the DAC device pointer. */ 391e41f4b71Sopenharmony_ci VirtualDacDeviceInit(virtual); 392e41f4b71Sopenharmony_ci /* Pass in the private data of the node. */ 393e41f4b71Sopenharmony_ci virtual->device.priv = (void *)node; 394e41f4b71Sopenharmony_ci /* Pass in the device number. */ 395e41f4b71Sopenharmony_ci virtual->device.devNum = virtual->deviceNum; 396e41f4b71Sopenharmony_ci /* Pass in the method. */ 397e41f4b71Sopenharmony_ci virtual->device.ops = &g_method; 398e41f4b71Sopenharmony_ci /* Add a DAC device. */ 399e41f4b71Sopenharmony_ci ret = DacDeviceAdd(&virtual->device); 400e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 401e41f4b71Sopenharmony_ci /* Failed to add the DAC device. */ 402e41f4b71Sopenharmony_ci HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); 403e41f4b71Sopenharmony_ci goto __ERR__; 404e41f4b71Sopenharmony_ci } 405e41f4b71Sopenharmony_ci /* The DAC device is added successfully. */ 406e41f4b71Sopenharmony_ci return HDF_SUCCESS; 407e41f4b71Sopenharmony_ci __ERR__: 408e41f4b71Sopenharmony_ci /* If the pointer is null */ 409e41f4b71Sopenharmony_ci if (virtual != NULL) { 410e41f4b71Sopenharmony_ci / Release the memory. */ 411e41f4b71Sopenharmony_ci OsalMemFree(virtual); 412e41f4b71Sopenharmony_ci /* Set this pointer to null. */ 413e41f4b71Sopenharmony_ci virtual = NULL; 414e41f4b71Sopenharmony_ci } 415e41f4b71Sopenharmony_ci 416e41f4b71Sopenharmony_ci return ret; 417e41f4b71Sopenharmony_ci } 418e41f4b71Sopenharmony_ci 419e41f4b71Sopenharmony_ci static int32_t VirtualDacInit(struct HdfDeviceObject *device) 420e41f4b71Sopenharmony_ci { 421e41f4b71Sopenharmony_ci /* Define return values. */ 422e41f4b71Sopenharmony_ci int32_t ret; 423e41f4b71Sopenharmony_ci /* Child node of the device structure. */ 424e41f4b71Sopenharmony_ci const struct DeviceResourceNode *childNode = NULL; 425e41f4b71Sopenharmony_ci /* Check the input parameter pointer. */ 426e41f4b71Sopenharmony_ci if (device == NULL || device->property == NULL) { 427e41f4b71Sopenharmony_ci /* The input parameter pointer is null. */ 428e41f4b71Sopenharmony_ci HDF_LOGE("%s: device or property is NULL", __func__); 429e41f4b71Sopenharmony_ci return HDF_ERR_INVALID_OBJECT; 430e41f4b71Sopenharmony_ci } 431e41f4b71Sopenharmony_ci /* The input parameter pointer is not null. */ 432e41f4b71Sopenharmony_ci ret = HDF_SUCCESS; 433e41f4b71Sopenharmony_ci DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 434e41f4b71Sopenharmony_ci /* Parse the child node. */ 435e41f4b71Sopenharmony_ci ret = VirtualDacParseAndInit(device, childNode); 436e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 437e41f4b71Sopenharmony_ci /* Failed to parse the child node. */ 438e41f4b71Sopenharmony_ci break; 439e41f4b71Sopenharmony_ci } 440e41f4b71Sopenharmony_ci } 441e41f4b71Sopenharmony_ci /* The child node is parsed. */ 442e41f4b71Sopenharmony_ci return ret; 443e41f4b71Sopenharmony_ci } 444e41f4b71Sopenharmony_ci 445e41f4b71Sopenharmony_ci static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node) 446e41f4b71Sopenharmony_ci { 447e41f4b71Sopenharmony_ci struct DeviceResourceIface *drsOps = NULL; 448e41f4b71Sopenharmony_ci 449e41f4b71Sopenharmony_ci /* Obtain the drsOps method. */ 450e41f4b71Sopenharmony_ci drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 451e41f4b71Sopenharmony_ci if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { 452e41f4b71Sopenharmony_ci HDF_LOGE("%s: Invalid drs ops fail!", __func__); 453e41f4b71Sopenharmony_ci return HDF_FAILURE; 454e41f4b71Sopenharmony_ci } 455e41f4b71Sopenharmony_ci /* Read the configuration parameters in sequence and fill them in the structure. */ 456e41f4b71Sopenharmony_ci if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) { 457e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read deviceNum fail!", __func__); 458e41f4b71Sopenharmony_ci return HDF_ERR_IO; 459e41f4b71Sopenharmony_ci } 460e41f4b71Sopenharmony_ci if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) { 461e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read validChannel fail!", __func__); 462e41f4b71Sopenharmony_ci return HDF_ERR_IO; 463e41f4b71Sopenharmony_ci } 464e41f4b71Sopenharmony_ci if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) { 465e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read rate fail!", __func__); 466e41f4b71Sopenharmony_ci return HDF_ERR_IO; 467e41f4b71Sopenharmony_ci } 468e41f4b71Sopenharmony_ci return HDF_SUCCESS; 469e41f4b71Sopenharmony_ci } 470e41f4b71Sopenharmony_ci ``` 471e41f4b71Sopenharmony_ci 472e41f4b71Sopenharmony_ci 473e41f4b71Sopenharmony_ci 474e41f4b71Sopenharmony_ci - Implement the **Release** function. 475e41f4b71Sopenharmony_ci 476e41f4b71Sopenharmony_ci Input parameter: 477e41f4b71Sopenharmony_ci 478e41f4b71Sopenharmony_ci **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 479e41f4b71Sopenharmony_ci 480e41f4b71Sopenharmony_ci Return value: 481e41f4b71Sopenharmony_ci 482e41f4b71Sopenharmony_ci No value is returned. 483e41f4b71Sopenharmony_ci 484e41f4b71Sopenharmony_ci Function description: 485e41f4b71Sopenharmony_ci 486e41f4b71Sopenharmony_ci Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. 487e41f4b71Sopenharmony_ci 488e41f4b71Sopenharmony_ci > **NOTE** <br> 489e41f4b71Sopenharmony_ci > 490e41f4b71Sopenharmony_ci >All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. 491e41f4b71Sopenharmony_ci 492e41f4b71Sopenharmony_ci 493e41f4b71Sopenharmony_ci 494e41f4b71Sopenharmony_ci ```c++ 495e41f4b71Sopenharmony_ci static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node) 496e41f4b71Sopenharmony_ci { 497e41f4b71Sopenharmony_ci /* Define return values. */ 498e41f4b71Sopenharmony_ci int32_t ret; 499e41f4b71Sopenharmony_ci /*Define the DAC device number. */ 500e41f4b71Sopenharmony_ci int16_t devNum; 501e41f4b71Sopenharmony_ci /* Pointer to the DacDevice structure. */ 502e41f4b71Sopenharmony_ci struct DacDevice *device = NULL; 503e41f4b71Sopenharmony_ci // Pointer to the VirtualDacDevice structure. */ 504e41f4b71Sopenharmony_ci struct VirtualDacDevice *virtual = NULL; 505e41f4b71Sopenharmony_ci /* Pointer to the DeviceResourceIface structure. */ 506e41f4b71Sopenharmony_ci struct DeviceResourceIface *drsOps = NULL; 507e41f4b71Sopenharmony_ci /* Obtain device resources through the instance entry. */ 508e41f4b71Sopenharmony_ci drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 509e41f4b71Sopenharmony_ci /* Check the input parameter pointer. */ 510e41f4b71Sopenharmony_ci if (drsOps == NULL || drsOps->GetUint32 == NULL) { 511e41f4b71Sopenharmony_ci /* The pointer is null. */ 512e41f4b71Sopenharmony_ci HDF_LOGE("%s: invalid drs ops fail!", __func__); 513e41f4b71Sopenharmony_ci return; 514e41f4b71Sopenharmony_ci } 515e41f4b71Sopenharmony_ci /* Obtain data of the devNum node. */ 516e41f4b71Sopenharmony_ci ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0); 517e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 518e41f4b71Sopenharmony_ci /* The information fails to be obtained. */ 519e41f4b71Sopenharmony_ci HDF_LOGE("%s: read devNum fail!", __func__); 520e41f4b71Sopenharmony_ci return; 521e41f4b71Sopenharmony_ci } 522e41f4b71Sopenharmony_ci /* Obtain the DAC device number. */ 523e41f4b71Sopenharmony_ci device = DacDeviceGet(devNum); 524e41f4b71Sopenharmony_ci /* Check whether the DAC device number and data are null. */ 525e41f4b71Sopenharmony_ci if (device != NULL && device->priv == node) { 526e41f4b71Sopenharmony_ci /* Release the DAC device number if the device data is null. */ 527e41f4b71Sopenharmony_ci DacDevicePut(device); 528e41f4b71Sopenharmony_ci /* Remove the DAC device number. */ 529e41f4b71Sopenharmony_ci DacDeviceRemove(device); 530e41f4b71Sopenharmony_ci virtual = (struct VirtualDacDevice *)device; 531e41f4b71Sopenharmony_ci /* Release the virtual pointer. */ 532e41f4b71Sopenharmony_ci OsalMemFree(virtual); 533e41f4b71Sopenharmony_ci } 534e41f4b71Sopenharmony_ci return; 535e41f4b71Sopenharmony_ci } 536e41f4b71Sopenharmony_ci 537e41f4b71Sopenharmony_ci static void VirtualDacRelease(struct HdfDeviceObject *device) 538e41f4b71Sopenharmony_ci { 539e41f4b71Sopenharmony_ci /* Define the child node structure pointer to the DeviceResourceNode. */ 540e41f4b71Sopenharmony_ci const struct DeviceResourceNode *childNode = NULL; 541e41f4b71Sopenharmony_ci /* Check whether the input parameter pointer is null. */ 542e41f4b71Sopenharmony_ci if (device == NULL || device->property == NULL) { 543e41f4b71Sopenharmony_ci /* The input parameter pointer is null. */ 544e41f4b71Sopenharmony_ci HDF_LOGE("%s: device or property is NULL", __func__); 545e41f4b71Sopenharmony_ci return; 546e41f4b71Sopenharmony_ci } 547e41f4b71Sopenharmony_ci 548e41f4b71Sopenharmony_ci DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 549e41f4b71Sopenharmony_ci /* Remove the DAC by node. */ 550e41f4b71Sopenharmony_ci VirtualDacRemoveByNode(childNode); 551e41f4b71Sopenharmony_ci } 552e41f4b71Sopenharmony_ci } 553e41f4b71Sopenharmony_ci ``` 554e41f4b71Sopenharmony_ci 555e41f4b71Sopenharmony_ci 556e41f4b71Sopenharmony_ci 557e41f4b71Sopenharmony_ci 558e41f4b71Sopenharmony_ci4. Debug the driver. 559e41f4b71Sopenharmony_ci 560e41f4b71Sopenharmony_ci (Optional) Verify the basic functions of the new driver, for example, check whether the test cases are successful after the driver is loaded. 561