提交 97d9f9e6 编写于 作者: A Annie_wang

update docs

Signed-off-by: NAnnie_wang <annie.wangli@huawei.com>
上级 aff329ec
# ADC<a name="EN-US_TOPIC_0000001153677754"></a>
# ADC
## Overview<a name="section268031773165048"></a>
The analog-to-digital converter \(ADC\) is a device that converts analog signals into digital signals. In the Hardware Driver Foundation \(HDF\), the ADC module uses the unified service mode for API adaptation. In this mode, a device service is used as the ADC manager to handle external access requests in a unified manner, which is reflected in the configuration file. The unified service mode applies to the scenario where there are many device objects of the same type, for example, when the ADC has more than 10 controllers. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services.
## Overview
**Figure 1** Unified service mode<a name="fig14423182615525"></a>
![](figures/unified-service-mode.png "ADC-unified-service-mode")
An analog-to-digital converter (ADC) is a device that converts analog signals into digital signals. In the Hardware Driver Foundation (HDF), the ADC module uses the unified service mode for API adaptation. In this mode, a device service is used as the ADC manager to handle external access requests in a unified manner, which is reflected in the configuration file. The unified service mode applies to the scenario where there are many device objects of the same type, for example, when the ADC has more than 10 controllers. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services.
## Available APIs<a name="section752964871810"></a>
**Figure 1** Unified service mode
![](figures/unified-service-mode.png "ADC Unified Service Mode")
## Available APIs
**AdcMethod**:
### AdcMethod<a name="section1618135285210"></a>
```
struct AdcMethod {
......@@ -19,396 +23,310 @@ struct AdcMethod {
};
```
**Table 1** Callbacks for the members in the AdcMethod structure
<a name="table1943202316536"></a>
<table><thead align="left"><tr id="row2451223135315"><th class="cellrowborder" valign="top" width="20%" id="mcps1.2.6.1.1"><p id="p845123185313"><a name="p845123185313"></a><a name="p845123185313"></a>Callback</p>
</th>
<th class="cellrowborder" valign="top" width="20%" id="mcps1.2.6.1.2"><p id="p345192315311"><a name="p345192315311"></a><a name="p345192315311"></a>Input Parameter</p>
</th>
<th class="cellrowborder" valign="top" width="20%" id="mcps1.2.6.1.3"><p id="p74512313536"><a name="p74512313536"></a><a name="p74512313536"></a>Output Parameter</p>
</th>
<th class="cellrowborder" valign="top" width="20%" id="mcps1.2.6.1.4"><p id="p174542313531"><a name="p174542313531"></a><a name="p174542313531"></a>Return Value</p>
</th>
<th class="cellrowborder" valign="top" width="20%" id="mcps1.2.6.1.5"><p id="p445112315535"><a name="p445112315535"></a><a name="p445112315535"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row545102316536"><td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.1 "><p id="p14562345312"><a name="p14562345312"></a><a name="p14562345312"></a>read</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.2 "><p id="p474720389561"><a name="p474720389561"></a><a name="p474720389561"></a><strong id="b167472038115611"><a name="b167472038115611"></a><a name="b167472038115611"></a>device</strong>: structure pointer to the ADC controller at the core layer.</p>
<p id="p14459238536"><a name="p14459238536"></a><a name="p14459238536"></a><strong id="b179891252205514"><a name="b179891252205514"></a><a name="b179891252205514"></a>channel</strong>: channel ID of the uint32_t type.</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.3 "><p id="p746423195320"><a name="p746423195320"></a><a name="p746423195320"></a><strong id="b626111125816"><a name="b626111125816"></a><a name="b626111125816"></a>val</strong>: pointer to the signal data to be transmitted. It is of the uint32_t type.</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.4 "><p id="p1746192315311"><a name="p1746192315311"></a><a name="p1746192315311"></a>HDF_STATUS</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.5 "><p id="p146152311537"><a name="p146152311537"></a><a name="p146152311537"></a>Reads the signal data sampled by the ADC.</p>
</td>
</tr>
<tr id="row6461236531"><td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.1 "><p id="p446182316538"><a name="p446182316538"></a><a name="p446182316538"></a>stop</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.2 "><p id="p1446112318536"><a name="p1446112318536"></a><a name="p1446112318536"></a><strong id="b345886135912"><a name="b345886135912"></a><a name="b345886135912"></a>device</strong>: structure pointer to the ADC controller at the core layer.</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.3 "><p id="p4461823125317"><a name="p4461823125317"></a><a name="p4461823125317"></a></p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.4 "><p id="p1346122314530"><a name="p1346122314530"></a><a name="p1346122314530"></a>HDF_STATUS</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.5 "><p id="p1346223185318"><a name="p1346223185318"></a><a name="p1346223185318"></a>Stops the ADC device.</p>
</td>
</tr>
<tr id="row194622375313"><td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.1 "><p id="p144642305313"><a name="p144642305313"></a><a name="p144642305313"></a>start</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.2 "><p id="p17461323185314"><a name="p17461323185314"></a><a name="p17461323185314"></a><strong id="b1988212105110"><a name="b1988212105110"></a><a name="b1988212105110"></a>device</strong>: structure pointer to the ADC controller at the core layer.</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.3 "><p id="p1146623105317"><a name="p1146623105317"></a><a name="p1146623105317"></a></p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.4 "><p id="p546132311535"><a name="p546132311535"></a><a name="p546132311535"></a>HDF_STATUS</p>
</td>
<td class="cellrowborder" valign="top" width="20%" headers="mcps1.2.6.1.5 "><p id="p1471623195311"><a name="p1471623195311"></a><a name="p1471623195311"></a>Starts the ADC device.</p>
</td>
</tr>
</tbody>
</table>
## How to Develop<a name="section100579767165048"></a>
**Table 1** Description of the callback functions in AdcMethod
The ADC module adaptation involves the following steps:
| Function| Input Parameter| Output Parameter| Return Value| Description|
| -------- | -------- | -------- | -------- | -------- |
| read | **device**: structure pointer to the ADC controller at the core layer.<br>**channel**: channel ID, which is of the uint32_t type.| **val**: pointer to the signal data to be transmitted. It is of the uint32_t type.| HDF_STATUS| Reads the signal data sampled by the ADC.|
| stop | **device**: structure pointer to the ADC controller at the core layer.| –| HDF_STATUS| Stops an ADC device.|
| start | **device**: structure pointer to the ADC controller at the core layer.| –| HDF_STATUS| Starts an ADC device.|
1. Instantiate the driver entry.
- Instantiate the **HdfDriverEntry** structure.
- Call **HDF\_INIT** to register the **HdfDriverEntry** instance with the HDF.
2. Configure attribute files.
- Add the **deviceNode** information to the **device\_info.hcs** file.
- \(Optional\) Add the **adc\_config.hcs** file.
3. Instantiate the ADC controller object.
- Initialize **AdcDevice**.
- Instantiate **AdcMethod** in the **AdcDevice** object.
For details, see [Available APIs](#available-apis).
4. \(Optional\) Debug the driver.
For new drivers, verify basic functions, for example, verify the information returned after the connect operation and whether the signal collection is successful.
## Development Example<a name="section1745221471165048"></a>
The following uses **adc\_hi35xx.c** as an example to present the contents that need to be provided by the vendor to implement device functions.
1. Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type \(defined in **hdf\_device\_desc.h**\), and the value of **moduleName** must be the same as that in **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.
Generally, HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, HDF calls **Release** to release driver resources and exits.
- ADC driver entry reference
Many devices may connect to the ADC. In the HDF, a manager object needs to be created for the ADC. When a device needs to be started, the manager object locates the device based on the specified parameters.
The driver of the ADC manager is implemented by the core layer. Vendors do not need to pay attention to the implementation of this part. However, when they implement the **Init** function, the **AdcDeviceAdd** function of the core layer must be called to implement the corresponding features.
```
static struct HdfDriverEntry g_hi35xxAdcDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxAdcInit,
.Release = Hi35xxAdcRelease,
.moduleName = "hi35xx_adc_driver",// (Mandatory) This parameter must be the same as that in the .hcs file.
};
HDF_INIT(g_hi35xxAdcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
// Driver entry of the adc_core.c manager service at the core layer
struct HdfDriverEntry g_adcManagerEntry = {
.moduleVersion = 1,
.Init = AdcManagerInit,
.Release = AdcManagerRelease,
.moduleName = "HDF_PLATFORM_ADC_MANAGER",// This parameter corresponds to device0 in the device_info file.
};
HDF_INIT(g_adcManagerEntry);
```
2. Add the **deviceNode** information to the **device\_info.hcs** file and configure the device attributes in the **adc\_config.hcs** file. The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **AdcDevice** members at the core layer.
In the unified service mode, the first device node in the **device\_info** file must be the ADC manager, and the parameters must be set as follows:
<a name="table1344068233165048"></a>
<table><thead align="left"><tr id="row1551612465165048"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="entry1856185125165048p0"><a name="entry1856185125165048p0"></a><a name="entry1856185125165048p0"></a>Member</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="entry720672143165048p0"><a name="entry720672143165048p0"></a><a name="entry720672143165048p0"></a>Value</p>
</th>
</tr>
</thead>
<tbody><tr id="row583452627165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry747665129165048p0"><a name="entry747665129165048p0"></a><a name="entry747665129165048p0"></a>moduleName</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry912596825165048p0"><a name="entry912596825165048p0"></a><a name="entry912596825165048p0"></a>It has a fixed value of <strong id="b581184091716"><a name="b581184091716"></a><a name="b581184091716"></a>HDF_PLATFORM_ADC_MANAGER</strong>.</p>
</td>
</tr>
<tr id="row218211231165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry568759156165048p0"><a name="entry568759156165048p0"></a><a name="entry568759156165048p0"></a>serviceName</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry1349637957165048p0"><a name="entry1349637957165048p0"></a><a name="entry1349637957165048p0"></a>–</p>
</td>
</tr>
<tr id="row1166331861165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry1142726988165048p0"><a name="entry1142726988165048p0"></a><a name="entry1142726988165048p0"></a>policy</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry781016408165048p0"><a name="entry781016408165048p0"></a><a name="entry781016408165048p0"></a>The value is <strong id="b229121291813"><a name="b229121291813"></a><a name="b229121291813"></a>0</strong>, which indicates that no service is published.</p>
</td>
</tr>
<tr id="row1822624516165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry982991296165048p0"><a name="entry982991296165048p0"></a><a name="entry982991296165048p0"></a>deviceMatchAttr</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry367170471165048p0"><a name="entry367170471165048p0"></a><a name="entry367170471165048p0"></a>This parameter is not used.</p>
</td>
</tr>
</tbody>
</table>
Configure ADC controller information from the second node. This node specifies a type of ADC controllers rather than an ADC controller. In this example, there is only one ADC device. If there are multiple ADC devices, you need to add the **deviceNode** information to the **device\_info** file and add the corresponding device attributes to the **adc\_config** file.
- **device\_info.hcs** configuration reference
```
root {
device_info {
platform :: host {
device_adc :: device {
device0 :: deviceNode {
policy = 0;
priority = 50;
permission = 0644;
moduleName = "HDF_PLATFORM_ADC_MANAGER";
serviceName = "HDF_PLATFORM_ADC_MANAGER";
}
device1 :: deviceNode {
policy = 0; // The value 0 indicates that no service needs to be published.
priority = 55; // Driver startup priority
permission = 0644; // Permission to create device nodes for the driver
moduleName = "hi35xx_adc_driver"; // (Mandatory) Driver name, which must be the same as the moduleName in the driver entry.
serviceName = "HI35XX_ADC_DRIVER"; // (Mandatory) Unique name of the service published by the driver
deviceMatchAttr = "hisilicon_hi35xx_adc";// (Mandatory) Used to configure the private data of the controller. The value must be the same as the controller in adc_config.hcs.
} // The specific controller information is in adc_config.hcs.
}
}
}
}
```
- adc\_config.hcs configuration reference
```
root {
platform {
adc_config_hi35xx {
match_attr = "hisilicon_hi35xx_adc";
template adc_device {
regBasePhy = 0x120e0000;// Physical base address of the register
regSize = 0x34; // Bit width of the register
deviceNum = 0; // Device number
validChannel = 0x1; // Valid channel
dataWidth = 10; // Data bit width of received signals
scanMode = 1; // Scan mode
delta = 0; // Delta parameter
deglitch = 0;
glitchSample = 5000;
rate = 20000;
}
device_0 :: adc_device {
deviceNum = 0;
validChannel = 0x2;
}
}
}
}
```
3. Initialize the **AdcDevice** object at the core layer, including initializing the vendor custom structure \(transferring parameters and data\), instantiating **AdcMethod** \(used to call underlying functions of the driver\) in **AdcDevice**, and implementing the **HdfDriverEntry** member functions \(**Bind**, **Init**, and **Release**\).
- Custom structure reference
To the driver, the custom structure carries parameters and data. The values in the **adc\_config.hcs** file are read by HDF, and the structure members are initialized through **DeviceResourceIface**. Some important values, such as the device number and bus number, are also passed to the **AdcDevice** object at the core layer.
```
struct Hi35xxAdcDevice {
struct AdcDevice device;// (Mandatory) Control object of the core layer. For details, see the description of AdcDevice.
volatile unsigned char *regBase;// (Mandatory) Base address of the register
volatile unsigned char *pinCtrlBase;
uint32_t regBasePhy; // (Mandatory) Physical base address of the register
uint32_t regSize; // (mandatory) Bit width of the register
uint32_t deviceNum; // (Mandatory) Device number
uint32_t dataWidth; // (Mandatory) Data bit width of received signals
uint32_t validChannel; // (Mandatory) Valid channel
uint32_t scanMode; // (Mandatory) Scan mode
uint32_t delta;
uint32_t deglitch;
uint32_t glitchSample;
uint32_t rate; // (Mandatory) Sampling rate
};
// AdcDevice is the core layer controller structure. Its members are assigned with values by using the Init function.
struct AdcDevice {
const struct AdcMethod *ops;
OsalSpinlock spin;
uint32_t devNum;
uint32_t chanNum;
const struct AdcLockMethod *lockOps;
void *priv;
};
```
- Instantiate the callback function structure **AdcMethod** in **AdcDevice**. The **AdcLockMethod** callback function structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in the **Init** function.
```
static const struct AdcMethod g_method = {
.read = Hi35xxAdcRead,
.stop = Hi35xxAdcStop,
.start = Hi35xxAdcStart,
};
```
- Init function
Input parameters:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
Return values:
HDF\_STATUS \(The following table lists some status. For details about other status, see **HDF\_STATUS** in the **//drivers/framework/include/utils/hdf\_base.h** file.\)
<a name="table127573104165048"></a>
<table><thead align="left"><tr id="row1932243367165048"><th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.1"><p id="entry405408385165048p0"><a name="entry405408385165048p0"></a><a name="entry405408385165048p0"></a>Status (Value)</p>
</th>
<th class="cellrowborder" valign="top" width="50%" id="mcps1.1.3.1.2"><p id="entry407875094165048p0"><a name="entry407875094165048p0"></a><a name="entry407875094165048p0"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row1845195554165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry144793493165048p0"><a name="entry144793493165048p0"></a><a name="entry144793493165048p0"></a>HDF_ERR_INVALID_OBJECT</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry1933449399165048p0"><a name="entry1933449399165048p0"></a><a name="entry1933449399165048p0"></a>Invalid controller object</p>
</td>
</tr>
<tr id="row1203086670165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry766973904165048p0"><a name="entry766973904165048p0"></a><a name="entry766973904165048p0"></a>HDF_ERR_INVALID_PARAM</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry999036326165048p0"><a name="entry999036326165048p0"></a><a name="entry999036326165048p0"></a>Invalid parameter</p>
</td>
</tr>
<tr id="row1147526196165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry271913224165048p0"><a name="entry271913224165048p0"></a><a name="entry271913224165048p0"></a>HDF_ERR_MALLOC_FAIL</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry1349459344165048p0"><a name="entry1349459344165048p0"></a><a name="entry1349459344165048p0"></a>Failed to allocate memory</p>
</td>
</tr>
<tr id="row42206428165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry826579070165048p0"><a name="entry826579070165048p0"></a><a name="entry826579070165048p0"></a>HDF_ERR_IO</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry740637684165048p0"><a name="entry740637684165048p0"></a><a name="entry740637684165048p0"></a>I/O error</p>
</td>
</tr>
<tr id="row456623770165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry2126491887165048p0"><a name="entry2126491887165048p0"></a><a name="entry2126491887165048p0"></a>HDF_SUCCESS</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry1585614415165048p0"><a name="entry1585614415165048p0"></a><a name="entry1585614415165048p0"></a>Transmission successful</p>
</td>
</tr>
<tr id="row2048732992165048"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.1 "><p id="entry131232818165048p0"><a name="entry131232818165048p0"></a><a name="entry131232818165048p0"></a>HDF_FAILURE</p>
</td>
<td class="cellrowborder" valign="top" width="50%" headers="mcps1.1.3.1.2 "><p id="entry42178503165048p0"><a name="entry42178503165048p0"></a><a name="entry42178503165048p0"></a>Transmission failed</p>
</td>
</tr>
</tbody>
</table>
Function description:
Initializes the custom structure object and **AdcDevice**, and calls the **AdcDeviceAdd** function at the core layer.
```
static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct DeviceResourceNode *childNode = NULL;
...
// Traverse and parse all nodes in adc_config.hcs and call the Hi35xxAdcParseInit function to initialize the devices separately.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = Hi35xxAdcParseInit(device, childNode);// For details about the function definition, see the following description.
...
}
return ret;
}
static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node)
{
int32_t ret;
struct Hi35xxAdcDevice *hi35xx = NULL; // (Mandatory) Custom structure object
(void)device;
hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); // (Mandatory) Memory allocation
...
ret = Hi35xxAdcReadDrs(hi35xx, node); // (Mandatory) Fill the default values in the adc_config file to the structure.
...
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);// (Mandatory) Address mapping
...
hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE);
...
Hi35xxAdcDeviceInit(hi35xx); // (Mandatory) Initialize the ADC.
hi35xx->device.priv = (void *)node; // (Mandatory) Store device attributes.
hi35xx->device.devNum = hi35xx->deviceNum;// (Mandatory) Initialize the AdcDevice members.
hi35xx->device.ops = &g_method; // (Mandatory) Mount the AdcMethod instance object.
ret = AdcDeviceAdd(&hi35xx->device); // (Mandatory and important) Call this function to set the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned.
...
return HDF_SUCCESS;
__ERR__:
if (hi35xx != NULL) { // If the operation fails, you need to execute the initialization function reversely.
if (hi35xx->regBase != NULL) {
OsalIoUnmap((void *)hi35xx->regBase);
hi35xx->regBase = NULL;
}
AdcDeviceRemove(&hi35xx->device);
OsalMemFree(hi35xx);
}
return ret;
}
```
- Release function
Input parameters:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
Return values:
## How to Develop
Function description:
Releases the memory and deletes the controller. This function assigns a value to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the corresponding value assignment operations.
The ADC module adaptation involves the following steps:
```
static void Hi35xxAdcRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
...
// Traverse and parse all nodes in adc_config.hcs and perform the release operation on each node.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
Hi35xxAdcRemoveByNode(childNode);// The function definition is as follows:
}
}
static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node)
{
int32_t ret;
int32_t deviceNum;
struct AdcDevice *device = NULL;
struct Hi35xxAdcDevice *hi35xx = NULL;
struct DeviceResourceIface *drsOps = NULL;
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
...
ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0);
...
// You can call the AdcDeviceGet function to obtain the AdcDevice object based on deviceNum and call the AdcDeviceRemove function to release the AdcDevice object.
device = AdcDeviceGet(deviceNum);
if (device != NULL && device->priv == node) {
AdcDevicePut(device);
AdcDeviceRemove(device); // (Mandatory) Remove the AdcDevice object from the driver manager.
hi35xx = (struct Hi35xxAdcDevice *)device;// (Mandatory) Obtain the custom object through forced conversion and release the object.
OsalIoUnmap((void *)hi35xx->regBase);
OsalMemFree(hi35xx);
1. Instantiate the driver entry.
- Instantiate the **HdfDriverEntry** structure.
- Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF.
2. Configure attribute files.
- Add the **deviceNode** information to the **device_info.hcs** file.
- (Optional) Add the **adc_config.hcs** file.
3. Instantiate the ADC controller object.
- Initialize **AdcDevice**.
- Instantiate **AdcMethod** in the **AdcDevice** object.
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> For details, see [Available APIs](#available-apis).
4. Debug the driver.
(Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and check whether signals are successfully collected.
## Development Example
The following uses **adc_hi35xx.c** as an example to present the information required for implementing device functions.
1. Instantiate the driver entry.
The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **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.
Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
ADC driver entry example:
Many devices may connect to the ADC. In the HDF, a manager object needs to be created for the ADC. When a device needs to be started, the manager object locates the target device based on the specified parameters.
The driver of the ADC manager is implemented by the core layer. The **Init** function of the driver layer calls **AdcDeviceAdd** of the core layer to add devices to the ADC manager. Therefore, you do not need to implement this part.
```
static struct HdfDriverEntry g_hi35xxAdcDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxAdcInit,
.Release = Hi35xxAdcRelease,
.moduleName = "hi35xx_adc_driver", // (Mandatory) This parameter must be the same as that in the .hcs file.
};
HDF_INIT(g_hi35xxAdcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
// Driver entry of the adc_core.c manager service at the core layer
struct HdfDriverEntry g_adcManagerEntry = {
.moduleVersion = 1,
.Init = AdcManagerInit,
.Release = AdcManagerRelease,
.moduleName = "HDF_PLATFORM_ADC_MANAGER", // The value is device0 in the device_info file.
};
HDF_INIT(g_adcManagerEntry);
```
2. Add **deviceNode** to the **device_info.hcs** file, and configure the device attributes in the **adc_config.hcs** file.
The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or restriction ranges of the **AdcDevice** members at the core layer.
In the unified service mode, the first device node in the **device_info** file must be the ADC manager. The parameters must be set as follows:
| Parameter| Value|
| -------- | -------- |
| moduleName | **HDF_PLATFORM_ADC_MANAGER**|
| serviceName | –|
| policy | **0**, which indicates that no service is published.|
| deviceMatchAttr | Reserved.|
Configure ADC controller information from the second node. This node specifies a type of ADC controllers rather than an ADC controller. In this example, there is only one ADC device. If there are multiple ADC devices, add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **adc_config** file for each device.
- **device_info.hcs** configuration example
```
root {
device_info {
platform :: host {
device_adc :: device {
device0 :: deviceNode {
policy = 0;
priority = 50;
permission = 0644;
moduleName = "HDF_PLATFORM_ADC_MANAGER";
serviceName = "HDF_PLATFORM_ADC_MANAGER";
}
device1 :: deviceNode {
policy = 0; // The value 0 indicates that no service is published.
priority = 55; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
moduleName = "hi35xx_adc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
serviceName = "HI35XX_ADC_DRIVER"; // (Mandatory) Unique name of the service published by the driver.
deviceMatchAttr = "hisilicon_hi35xx_adc";// (Mandatory) Private data of the controller. The value must be the same as that of the corresponding controller in adc_config.hcs.
// The specific controller information is configured in adc_config.hcs.
}
}
}
}
return
```
\ No newline at end of file
}
```
- **adc_config.hcs** configuration example
```
root {
platform {
adc_config_hi35xx {
match_attr = "hisilicon_hi35xx_adc";
template adc_device {
regBasePhy = 0x120e0000;// Physical base address of the register.
regSize = 0x34; // Bit width of the register.
deviceNum = 0; // Device number.
validChannel = 0x1; // Valid channel.
dataWidth = 10; // Bit width of the received signal.
scanMode = 1; // Scan mode.
delta = 0; // Delta parameter.
deglitch = 0;
glitchSample = 5000;
rate = 20000;
}
device_0 :: adc_device {
deviceNum = 0;
validChannel = 0x2;
}
}
}
}
```
3. Initialize the **AdcDevice** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **AdcMethod** in **AdcDevice** (so that the underlying driver functions can be called).
- Defining a custom structure
To the driver, the custom structure holds parameters and data. The **DeviceResourceIface()** function provided by the HDF reads **adc_config.hcs** to initialize the custom structure and passes some important parameters, such as the device number and bus number, to the **AdcDevice** object at the core layer.
```
struct Hi35xxAdcDevice {
struct AdcDevice device; // (Mandatory) Control object at the core layer. For details, see the description of AdcDevice.
volatile unsigned char *regBase; // (Mandatory) Register base address.
volatile unsigned char *pinCtrlBase;
uint32_t regBasePhy; // (Mandatory) Physical base address of the register.
uint32_t regSize; // (Mandatory) Bit width of the register.
uint32_t deviceNum; // (Mandatory) Device number.
uint32_t dataWidth; // (Mandatory) Data bit width of received signals.
uint32_t validChannel; // (Mandatory) Valid channel.
uint32_t scanMode; // (Mandatory) Scan mode.
uint32_t delta;
uint32_t deglitch;
uint32_t glitchSample;
uint32_t rate; // (Mandatory) Sampling rate.
};
// AdcDevice is the core layer controller structure. The **Init()** function assigns values to the members of AdcDevice.
struct AdcDevice {
const struct AdcMethod *ops;
OsalSpinlock spin;
uint32_t devNum;
uint32_t chanNum;
const struct AdcLockMethod *lockOps;
void *priv;
};
```
- Instantiating **AdcMethod** in **AdcDevice**<br>This example does not provide the implementation of the **AdcLockMethod** callback. For details, see I2C driver development. Other members are initialized by the **Init** function.
```
static const struct AdcMethod g_method = {
.read = Hi35xxAdcRead,
.stop = Hi35xxAdcStop,
.start = Hi35xxAdcStart,
};
```
- **Init** function
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
**Return value**:
**HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
| Status| Description|
| -------- | -------- |
| HDF_ERR_INVALID_OBJECT | Invalid controller object.|
| HDF_ERR_INVALID_PARAM | Invalid parameter.|
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
| HDF_ERR_IO | I/O error.|
| HDF_SUCCESS | Transmission successful.|
| HDF_FAILURE | Transmission failed.|
**Function description**:
Initializes the custom structure object and **AdcDevice**, and calls the **AdcDeviceAdd** function at the core layer.
```
static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct DeviceResourceNode *childNode = NULL;
...
// Traverse and parse all nodes in adc_config.hcs and call the **Hi35xxAdcParseInit** function to initialize the devices separately.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = Hi35xxAdcParseInit(device, childNode);// The function definition is as follows:
...
}
return ret;
}
static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node)
{
int32_t ret;
struct Hi35xxAdcDevice *hi35xx = NULL; // (Mandatory) Custom structure object.
(void)device;
hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); // (Mandatory) Allocate memory.
...
ret = Hi35xxAdcReadDrs(hi35xx, node); // (Mandatory) Fill the default values in the adc_config file to the structure.
...
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);// (Mandatory) Address mapping.
...
hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE);
...
Hi35xxAdcDeviceInit(hi35xx); // (Mandatory) Initialize the ADC.
hi35xx->device.priv = (void *)node; // (Mandatory) Store device attributes.
hi35xx->device.devNum = hi35xx->deviceNum;// (Mandatory) Initialize AdcDevice members.
hi35xx->device.ops = &g_method; // (Mandatory) Attach the AdcMethod instance object.
ret = AdcDeviceAdd(&hi35xx->device); // (Mandatory) Call this function to set the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned.
...
return HDF_SUCCESS;
__ERR__:
if (hi35xx != NULL) { // If the operation fails, execute the initialization function reversely.
if (hi35xx->regBase != NULL) {
OsalIoUnmap((void *)hi35xx->regBase);
hi35xx->regBase = NULL;
}
AdcDeviceRemove(&hi35xx->device);
OsalMemFree(hi35xx);
}
return ret;
}
```
- **Release** function
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
**Return value**:
No value is returned.
**Function description**:
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.
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
```
static void Hi35xxAdcRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
...
// Traverse and parse all nodes in adc_config.hcs and perform the release operation on each node.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
Hi35xxAdcRemoveByNode(childNode);// The function definition is as follows:
}
}
static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node)
{
int32_t ret;
int32_t deviceNum;
struct AdcDevice *device = NULL;
struct Hi35xxAdcDevice *hi35xx = NULL;
struct DeviceResourceIface *drsOps = NULL;
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
...
ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0);
...
// You can call the AdcDeviceGet function to obtain the AdcDevice object based on deviceNum and call the AdcDeviceRemove function to release the AdcDevice object.
device = AdcDeviceGet(deviceNum);
if (device != NULL && device->priv == node) {
AdcDevicePut(device);
AdcDeviceRemove(device); // (Mandatory) Remove the AdcDevice object from the driver manager.
hi35xx = (struct Hi35xxAdcDevice *)device;// (Mandatory) Obtain the custom object through forced conversion and release the operation.
OsalIoUnmap((void *)hi35xx->regBase);
OsalMemFree(hi35xx);
}
return
```
# DAC
## Overview
## Overview<a name="1"></a>
### DAC<a name="2"></a>
### DAC
A digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics.
### Basic Concepts<a name="3"></a>
The DAC module supports development of digital-to-analog conversion. The DAC module provides the output channel for the process control computer system. It connects to the executor to implement automatic control of the production process. It is also an important module in the analog-to-digital converter using feedback technologies.
### Basic Concepts
- Resolution
The number of binary bits that can be converted by a DAC. A greater number of bits indicates a higher resolution.
......@@ -23,47 +22,43 @@ The DAC module supports development of digital-to-analog conversion. The DAC mod
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.
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.
- 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.
The least significant byte (LSB) refers to bit 0 (the least significant bit) in a binary number.
- The least significant byte (LSB) refers to bit 0 (the least significant bit) in a binary number.
### Working Principles<a name="4"></a>
### Working Principles
In 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, more device nodes need to be configured and memory resources will be consumed by services. The figure below shows the unified service mode.
In 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 figure below shows the unified service mode.
The DAC module is divided into the following layers:
- The interface layer provides APIs for opening or closing a device and writing data.
- The core layer provides the capabilities of binding, initializing, and releasing devices.
- The adaptation layer implements other functions.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br>
>The core layer can call the functions of the interface layer and uses the hook to call functions of the adaptation layer. In this way, the adaptation layer can indirectly call the functions of the interface layer, but the interface layer cannot call the functions of the adaptation layer.
**Figure 1** Unified service mode<a name="fig14423182615525"></a>
![](figures/unified-service-mode.png "DAC unified service mode")
- Interface layer: provides APIs for opening or closing a device and writing data.
- Core layer: provides the capabilities of binding, initializing, and releasing devices.
- Adaptation layer: implements other functions.
![](../public_sys-resources/icon-note.gif) **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.
**Figure 1** Unified service mode
![](figures/unified-service-mode.png "DAC unified service mode")
### Constraints<a name="5"></a>
### Constraints
Currently, the DAC module supports only the kernels (LiteOS) of mini and small systems.
Currently, the DAC module supports only the kernels (LiteOS) of mini and small systems.
## Development Guidelines<a name="6"></a>
## Development Guidelines
### When to Use<a name="7"></a>
### When to Use
The 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.
### Available APIs<a name="8"></a>
### Available APIs
The **DacMethod** is used to call the DAC driver functions.
The **DacMethod** structure is used to call the DAC driver APIs.
**DacMethod** definition:
**DacMethod**:
```
```c++
struct DacMethod {
// Hook used to write data.
int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val);
......@@ -76,39 +71,39 @@ struct DacMethod {
**Table 1** Description of the DacMethod structure
<a name="table27410339187"></a>
| Function| Input Parameter | Output Parameter| Return Value | Description |
| -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- |
| write | **device**: 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.|
| start | **device**: pointer to the DAC controller at the core layer. | - | HDF_STATUS| Starts a DAC device. |
| stop | **device**: pointer to the DAC controller at the core layer. | - | HDF_STATUS| Stops a device. |
| 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.|
| start | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Starts a DAC device. |
| stop | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Stops a DAC device. |
### How to Develop
The DAC module adaptation procedure is as follows:
### How to Develop<a name="9"></a>
1. Instantiate the driver entry.
2. Configure attribute files.
3. Instantiate the core layer APIs.
4. Debug the driver.
The DAC module adaptation procedure is as follows:
### Development Example
- Instantiate the driver entry.
- Configure attribute files.
- Instantiate the APIs of the core layer.
- Debug the driver.
The following presents the information required for implementing device functions.
1. Instantiate the driver entry.
Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers are collected to form a segment address space similar to an array for the upper layer to invoke.
The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **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.
Generally, the HDF calls the **Init()** function to load the driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit.
Generally, the HDF calls the **Init()** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
```
```c++
static struct HdfDriverEntry g_dacDriverEntry = {
.moduleVersion = 1,
.Init = VirtualDacInit,
.Release = VirtualDacRelease,
.moduleName = "virtual_dac_driver",// (Mandatory) The value must be the same as that in the .hcs file.
};
HDF_INIT(g_dacDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
HDF_INIT(g_dacDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
```
2. Configure attribute files.
......@@ -119,23 +114,23 @@ The DAC module adaptation procedure is as follows:
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:
| Member | Value |
| --------------- | ------------------------------------------------------------ |
| policy | **0**, which indicates that no service is published.|
| priority | Driver startup priority. The value range is 0 to 200. A larger value indicates a lower priority. If the priorities are the same, the device loading sequence is not ensured.|
| permission | Driver permission.|
| moduleName | The value is **HDF_PLATFORM_DAC_MANAGER**.|
| serviceName | The value is **HDF_PLATFORM_DAC_MANAGER**.|
| deviceMatchAttr | Reserved.|
| Parameter | Value |
| --------------- | ------------------------------------------------------------------- |
| policy | **0**, which indicates that no service is published. |
| 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.|
| permission | Driver permission. |
| moduleName | **HDF_PLATFORM_DAC_MANAGER** |
| serviceName | **HDF_PLATFORM_DAC_MANAGER** |
| deviceMatchAttr | Reserved. |
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, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **dac_config** file.
**device_info.hcs** configuration reference
**device_info.hcs** configuration example:
```
```hcs
root {
device_dac :: device {
// device0 is the DAC manager.
// device0 is a DAC manager.
device0 :: deviceNode {
policy = 0;
priority = 52;
......@@ -144,7 +139,7 @@ The DAC module adaptation procedure is as follows:
moduleName = "HDF_PLATFORM_DAC_MANAGER";
}
}
// dac_virtual is the DAC controller.
// dac_virtual is a DAC controller.
dac_virtual :: deviceNode {
policy = 0;
priority = 56;
......@@ -157,34 +152,35 @@ The DAC module adaptation procedure is as follows:
```
- Configure the **dac_test_config.hcs** file.
Add a file, for example, **vendor/vendor_hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** and configure driver parameters.
```
Add a file, for example, **vendor/vendor_hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** and configure driver parameters.
```hcs
root {
platform {
dac_config {
match_attr = "virtual_dac"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
template dac_device {
deviceNum = 0; // Device number.
validChannel = 0x1; // Valid channel 1.
rate = 20000; // Transmission speed.
deviceNum = 0; // Device number.
validChannel = 0x1; // Valid channel 1.
rate = 20000; // Transmission speed.
}
device_0 :: dac_device {
deviceNum = 0; // Device number.
validChannel = 0x2; // Valid channel 2.
deviceNum = 0; // Device number.
validChannel = 0x2; // Valid channel 2.
}
}
}
}
```
3. Instantiate the APIs of the core layer.
3. Instantiate the core layer APIs.
- Initialize the **DacDevice** object.
- Initializing the **DacDevice** object
Initialize the **DacDevice** member in the **VirtualDacParseAndInit** function.
```
```c++
// Custom structure of the virtual driver
struct VirtualDacDevice {
// DAC device structure
......@@ -201,28 +197,28 @@ The DAC module adaptation procedure is as follows:
{
// Define the return values.
int32_t ret;
// Virtual pointer to the DAC device
// Pointer to the virtual DAC device
struct VirtualDacDevice *virtual = NULL;
(void)device;
// Allocate space for the virtual pointer.
// Allocate space for this pointer.
virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual));
if (virtual == NULL) {
// If the value is null, return an error code.
HDF_LOGE("%s: Malloc virtual fail!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
// Read the configuration of the attribute file.
// Read the content of the attribute file.
ret = VirtualDacReadDrs(virtual, node);
if (ret != HDF_SUCCESS) {
// Failed to read the file.
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
// Release the virtual space.
// Release the space for the virtual DAC device.
OsalMemFree(virtual);
// Set the pointer to 0.
virtual = NULL;
return ret;
}
// Initialize the virtual pointer.
// Initialize the pointer to the virtual DAC device.
VirtualDacDeviceInit(virtual);
// Initialize the priv object in DacDevice.
virtual->device.priv = (void *)node;
......@@ -235,9 +231,9 @@ The DAC module adaptation procedure is as follows:
if (ret != HDF_SUCCESS) {
// Failed to add the device.
HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret);
// Release the virtual space.
// Release the space for the virtual DAC device.
OsalMemFree(virtual);
// Set the virtual pointer to null.
// Set this pointer to null.
virtual = NULL;
return ret;
}
......@@ -245,73 +241,69 @@ The DAC module adaptation procedure is as follows:
return HDF_SUCCESS;
}
```
- Custom structure reference
- Defining a custom structure
The custom structure holds parameters and data for the driver. Define the custom structure based on the function parameters of the device. The HDF reads the values in the **dac_config.hcs** file using the **DacTestReadConfig()** function and initializes the structure members using **DeviceResourceIface()**. Some important values, such as the device number and bus number, are also passed to the **DacDevice** object at the core layer.
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.
```
```c++
struct VirtualDacDevice {
struct DacDevice device;// (Mandatory) Control object at the core layer. For details, see the description below.
uint32_t deviceNum; // (Mandatory) Device number.
uint32_t validChannel; // (Mandatory) Valid channel.
uint32_t deviceNum; // (Mandatory) Device number.
uint32_t validChannel; // (Mandatory) Valid channel.
uint32_t rate; // (Mandatory) Sampling rate.
};
// DacDevice is the core layer controller structure. Its members are assigned with values in the Init() function.
// DacDevice is the core layer controller structure. The Init() function assigns values to the members of DacDevice.
struct DacDevice {
const struct DacMethod *ops;
OsalSpinlock spin; // Spinlock.
uint32_t devNum; // Device number.
uint32_t chanNum; // Device channel number.
OsalSpinlock spin; // Spinlock.
uint32_t devNum; // Device number.
uint32_t chanNum; // Device channel number.
const struct DacLockMethod *lockOps;
void *priv;
};
```
- Instantiate the **DacDevice** in **DacMethod**.
- Instantiating **DacDevice** in **DacMethod**.
The **VirtualDacWrite**, **VirtualDacStop**, and **VirtualDacStart** functions are instantiated in **dac_virtual.c**.
```
```c++
static const struct DacMethod g_method = {
.write = VirtualDacWrite, // Write data to a DAC device.
.stop = VirtualDacStop, // Stop a DAC device.
.stop = VirtualDacStop, // Stop a DAC device.
.start = VirtualDacStart, // Start a DAC device.
};
```
>![](../public_sys-resources/icon-note.gif) **NOTE**<br>
>For details about **DacMethod**, see [Available APIs](#available-apis).
![](../public_sys-resources/icon-note.gif) **NOTE**<br>
For details about **DacMethod**, see [Available APIs](#available-apis).
- **Init** function
Input parameters:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
Return value:
**Return value**:
HDF_STATUS (The table below lists some status. For details about other status, see HDF_STATUS in the /drivers/framework/include/utils/hdf_base.h file.)
**HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
| State | Description |
| ---------------------- | -------------- |
| Status | Description |
| ---------------------- | ------------- |
| HDF_ERR_INVALID_OBJECT | Invalid controller object.|
| HDF_ERR_INVALID_PARAM | Invalid parameter. |
| HDF_ERR_INVALID_PARAM | Invalid parameter. |
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory. |
| HDF_ERR_IO | I/O error. |
| HDF_SUCCESS | Transmission successful. |
| HDF_FAILURE | Transmission failed. |
| HDF_ERR_IO | I/O error. |
| HDF_SUCCESS | Transmission successful. |
| HDF_FAILURE | Transmission failed. |
Function description:
**Function description**:
Initializes the custom structure object and **DacDevice**, and calls the **AdcDeviceAdd** function at the core layer.
```
```c++
static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
// Define return value parameters.
......@@ -355,7 +347,7 @@ The DAC module adaptation procedure is as follows:
if (virtual != NULL) {
// Release the memory.
OsalMemFree(virtual);
// Set the pointer to null.
// Set this pointer to null.
virtual = NULL;
}
......@@ -389,32 +381,36 @@ The DAC module adaptation procedure is as follows:
}
```
- **Release** function
- **Release** function
Input parameters:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
Return value
**Return value**:
This function returns no value.
No value is returned.
Function description:
**Function description**:
Releases memory and deletes the controller. This function assigns a value to the **Release** API 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. All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the corresponding value assignment operations.
```
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.
>![](../public_sys-resources/icon-note.gif) **NOTE**
>
>All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
```c++
static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node)
{
// Define return value parameters.
int32_t ret;
// Define the DAC device number.
int16_t devNum;
// Pointer of the DacDevice structure.
// Pointer to the DacDevice structure.
struct DacDevice *device = NULL;
// Pointer of the VirtualDacDevice structure.
// Pointer to the VirtualDacDevice structure.
struct VirtualDacDevice *virtual = NULL;
// Pointer of the DeviceResourceIface structure.
// Pointer to the DeviceResourceIface structure.
struct DeviceResourceIface *drsOps = NULL;
// Obtain device resources through the instance entry.
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
......@@ -423,47 +419,47 @@ The DAC module adaptation procedure is as follows:
// The pointer is null.
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
// Obtain data of the devNum node.
ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0);
if (ret != HDF_SUCCESS) {
// Failed to obtain node data.
HDF_LOGE("%s: read devNum fail!", __func__);
return;
}
// Obtain the DAC device number.
device = DacDeviceGet(devNum);
// Check whether the DAC device number and data are null.
if (device != NULL && device->priv == node) {
// Release the DAC device number if the device data is null.
DacDevicePut(device);
// Remove the DAC device number.
DacDeviceRemove(device);
virtual = (struct VirtualDacDevice *)device;
// Release the virtual pointer.
OsalMemFree(virtual);
}
// Obtain data of the devNum node.
ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0);
if (ret != HDF_SUCCESS) {
// Failed to obtain node data.
HDF_LOGE("%s: read devNum fail!", __func__);
return;
}
// Obtain the DAC device number.
device = DacDeviceGet(devNum);
// Check whether the DAC device number and data are null.
if (device != NULL && device->priv == node) {
// Release the DAC device number if the device data is null.
DacDevicePut(device);
// Remove the DAC device number.
DacDeviceRemove(device);
virtual = (struct VirtualDacDevice *)device;
// Release the virtual pointer.
OsalMemFree(virtual);
}
return;
}
static void VirtualDacRelease(struct HdfDeviceObject *device)
{
// Define the child node structure pointer of the DeviceResourceNode.
// Define the child node structure pointer to the DeviceResourceNode.
const struct DeviceResourceNode *childNode = NULL;
// Check whether the input parameter pointer is null.
if (device == NULL || device->property == NULL) {
// The input parameter pointer is null.
HDF_LOGE("%s: device or property is NULL", __func__);
return;
// The input parameter pointer is null.
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
// Remove the DAC through the node.
VirtualDacRemoveByNode(childNode);
}
}
}
```
4. Debug the driver.
(Optional) Verify the basic functions of the new driver, for example, whether the test cases are successful after the driver is loaded.
\ No newline at end of file
(Optional) Verify the basic functions of the new driver, for example, check whether the test cases are successful after the driver is loaded.
......@@ -79,7 +79,7 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf\_device\_desc.h**), and the value of **moduleName** must be the same as that in **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.
Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
GPIO driver entry example:
GPIO driver entry example:
```
struct HdfDriverEntry g_gpioDriverEntry = {
......@@ -108,9 +108,9 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
priority = 50;
device_gpio :: device {
device0 :: deviceNode {
policy = 0; // No service is published.
priority = 10; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
policy = 0; // No service is published.
priority = 10; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
moduleName = "hisi_pl061_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
deviceMatchAttr = "hisilicon_hi35xx_pl061"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs.
// Add private information about all controllers in this file.
......@@ -128,12 +128,12 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
gpio_config {
controller_0x120d0000 {
match_attr = "hisilicon_hi35xx_pl061"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
groupNum = 12; // (Mandatory) GPIO group number.
bitNum = 8; // (Mandatory) Number of GPIO pins in each group.
regBase = 0x120d0000; // (Mandatory) Physical base address.
regStep = 0x1000; // (Mandatory) Register offset step.
irqStart = 48; // (Mandatory) Enable interrupts.
irqShare = 0; // (Mandatory) Whether to share an interrupt.
groupNum = 12; // (Mandatory) GPIO group number.
bitNum = 8; // (Mandatory) Number of GPIO pins in each group.
regBase = 0x120d0000; // (Mandatory) Physical base address.
regStep = 0x1000; // (Mandatory) Register offset step.
irqStart = 48; // (Mandatory) Enable interrupts.
irqShare = 0; // (Mandatory) Whether to share an interrupt.
}
}
}
......@@ -201,13 +201,13 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
- **Init** function
Input parameter:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
Return value:
**Return value**:
HDF_STATUS<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
**HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
**Table 2** HDF_STATUS
......@@ -220,7 +220,7 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
| HDF_SUCCESS | Initialization successful.|
| HDF_FAILURE | Initialization failed.|
Function description:
**Function description**:
Initializes the custom structure object and **GpioCntlr**, calls the **GpioCntlrAdd** function at the core layer, and (optional) connects to the virtual file system (VFS).
......@@ -241,11 +241,11 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
...
ret = Pl061GpioInitCntlrMem(pl061); // Allocate memory.
...
pl061->cntlr.count = pl061->groupNum x pl061->bitNum;// (Mandatory) Calculate the number of pins.
pl061->cntlr.priv = (void *)device->property; // (Mandatory) Store device attributes.
pl061->cntlr.ops = &g_method; // (Mandatory) Attach the GpioMethod instance.
pl061->cntlr.device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and GpioCntlr.
ret = GpioCntlrAdd(&pl061->cntlr); // (Mandatory) Call this function to fill the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned.
pl061->cntlr.count = pl061->groupNum * pl061->bitNum;// (Mandatory) Calculate the number of pins.
pl061->cntlr.priv = (void *)device->property; // (Mandatory) Store device attributes.
pl061->cntlr.ops = &g_method; // (Mandatory) Attach the GpioMethod instance.
pl061->cntlr.device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and GpioCntlr.
ret = GpioCntlrAdd(&pl061->cntlr); // (Mandatory) Call this function to fill the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned.
...
Pl061GpioDebugCntlr(pl061);
#ifdef PL061_GPIO_USER_SUPPORT // (Optional) Access the user-level VFS if it is supported.
......@@ -258,19 +258,22 @@ The following uses **gpio_hi35xx.c** as an example to present the information re
```
- **Release** function
Input parameter:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
Return value:
**Return value**:
No value is returned.
Function description:
**Function description**:
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. All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
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.
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
```
static void Pl061GpioRelease(struct HdfDeviceObject *device)
{
......
......@@ -165,12 +165,12 @@ The HDMI module adaptation involves the following steps:
platform :: host {
device_hdmi :: device {
device0 :: deviceNode {
policy = 2; // Publish services.
priority = 20; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
serviceName = "HDF_PLATFORM_HDMI_0"; // (Mandatory) Unique name of the service published by the driver.
moduleName = "hdmi_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
deviceMatchAttr = "adapter_hdmi_driver"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in hdmi_config.hcs.
policy = 2; // Publish services.
priority = 20; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
serviceName = "HDF_PLATFORM_HDMI_0"; // (Mandatory) Unique name of the service published by the driver.
moduleName = "hdmi_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
deviceMatchAttr = "adapter_hdmi_driver"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in hdmi_config.hcs.
} // The specific controller information is in hdmi_config.hcs.
}
}
......@@ -183,12 +183,12 @@ The HDMI module adaptation involves the following steps:
root {
platform {
hdmi_config {
template hdmi_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes.
match_attr = ""; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
template hdmi_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes.
match_attr = ""; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
index = 0; // (Mandatory) HDMI controller number.
regBasePhy = 0x10100000; // (Mandatory) Physical base address of the register.
regBasePhy = 0x10100000; // (Mandatory) Physical base address of the register.
regSize = 0xd1; // (Mandatory) Register bit width.
irqNum = 100; //(Mandatory) Interrupt request (IRQ) number.
irqNum = 100; //(Mandatory) Interrupt request (IRQ) number.
maxTmdsClock = 300;
videoTiming = 10;
quantization = 1;
......@@ -241,7 +241,7 @@ The HDMI module adaptation involves the following steps:
struct HdmiAdapterHost {
struct HdmiCntlr *cntlr; // (Mandatory) Control object at the core layer. The details are as follows:
volatile unsigned char *regBase;// (Mandatory) Register base address.
uint32_t regBasePhy // (Mandatory) Physical base address of the register.
uint32_t regBasePhy; // (Mandatory) Physical base address of the register.
uint32_t regSize; // (Mandatory) Register bit width.
uint32_t irqNum; // (Mandatory) IRQ number.
};
......@@ -321,7 +321,7 @@ The HDMI module adaptation involves the following steps:
**Return value**:
HDF_STATUS
**HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
......@@ -356,17 +356,17 @@ The HDMI module adaptation involves the following steps:
HDF_LOGE("%s: malloc host failed!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
cntlr->priv = (void *)host; // (Mandatory) Store host to the private data of cntlr.
cntlr->ops = &g_hdmiHostOps; // (Mandatory) Connect to the HdmiCntlrOps instance.
cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and HdmiCntlr.
obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and HdmiCntlr.
cntlr->priv = (void *)host; // (Mandatory) Store host to the private data of cntlr.
cntlr->ops = &g_hdmiHostOps; // (Mandatory) Connect to the HdmiCntlrOps instance.
cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and HdmiCntlr.
obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and HdmiCntlr.
ret = HdmiAdapterCntlrParse(cntlr, obj); // (Mandatory) Initialize cntlr. If the operation fails, execute goto __ERR.
...
ret = HdmiAdapterHostParse(host, obj); // (Mandatory) Initialize the attributes of the host object. If the operation fails, execute goto__ERR.
ret = HdmiAdapterHostParse(host, obj); // (Mandatory) Initialize the attributes of the host object. If the operation fails, execute goto__ERR.
...
ret = HdmiAdapterHostInit(host, cntlr); // Initialize the custom structure. If the operation fails, execute goto __ERR.
...
ret = HdmiCntlrAdd(cntlr); // Call the functions at the core layer. If the operation fails, execute goto__ERR.
ret = HdmiCntlrAdd(cntlr); // Call the functions at the core layer. If the operation fails, execute goto__ERR.
...
HDF_LOGD("HdmiAdapterBind: success.");
return HDF_SUCCESS;
......@@ -385,7 +385,7 @@ The HDMI module adaptation involves the following steps:
**Return value**:
HDF_STATUS
**HDF_STATUS**
**Function description**:
......@@ -412,16 +412,16 @@ The HDMI module adaptation involves the following steps:
Releases the memory and deletes the controller. This function assigns values to the **Release** callback 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.
> ![](../public_sys-resources/icon-note.gif) **NOTE**<br>
> All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the corresponding value assignment operations.
```c
static void HdmiAdapterRelease(struct HdfDeviceObject *obj)
{
struct HdmiCntlr *cntlr = NULL;
...
cntlr = (struct MmcCntlr *)obj->service;// Forcibly convert HdfDeviceObject to HdmiCntlr by using service. For details about the value assignment, see the Bind function.
cntlr = (struct HdmiCntlr *)obj->service;// Forcibly convert HdfDeviceObject to HdmiCntlr by using service. For details about the value assignment, see the Bind function.
...
HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);// Customized memory release function. A forced conversion from HdmiCntlr to HimciAdapterHost is involved in the process.
}
```
> ![](../public_sys-resources/icon-note.gif) **NOTE**<br>
> All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the corresponding value assignment operations.
......@@ -5,7 +5,7 @@
The Inter-Integrated Circuit (I2C) bus is a simple and bidirectional two-wire synchronous serial bus developed by Philips. In the Hardware Driver Foundation (HDF), the I2C module uses the unified service mode for API adaptation. In this mode, a device service is used as the I2C manager to handle external access requests in a unified manner, which is reflected in the configuration file. The unified service mode applies to the scenario where there are many device objects of the same type, for example, when the I2C module has more than 10 controllers. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services.
**Figure 1** Unified service mode
**Figure 1** Unified service mode
![image](figures/unified-service-mode.png "I2C Unified Service Mode")
......@@ -59,42 +59,46 @@ The I2C module adaptation involves the following steps:
The following uses **i2c_hi35xx.c** as an example to present the information required for implementing device functions.
1. Instantiate the driver entry.<br/>The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **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.
1. Instantiate the driver entry.
The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **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.
Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit.
I2C driver entry example:
An I2C controller may be connected with multiple devices. You need to create a manager object in the HDF and publish a manager service to handle external access requests in a unified manner. Before a device is used, the manager service must be obtained first. Then, the manager service locates the target device based on the specified parameters.
The driver of the I2C manager is implemented by the core layer. Vendors do not need to care about the implementation of this part. However, the **Init** function must call the **I2cCntlrAdd** function of the core layer to implement corresponding features.
```
struct HdfDriverEntry g_i2cDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxI2cInit,
.Release = Hi35xxI2cRelease,
.moduleName = "hi35xx_i2c_driver",// (Mandatory) The value must be the same as that in the config.hcs file.
};
HDF_INIT(g_i2cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
// Driver entry of the i2c_core.c manager service at the core layer
struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1,
.Bind = I2cManagerBind,
.Init = I2cManagerInit,
.Release = I2cManagerRelease,
.moduleName = "HDF_PLATFORM_I2C_MANAGER",// This parameter corresponds to device0 in the device_info file.
.moduleVersion = 1,
.Init = Hi35xxI2cInit,
.Release = Hi35xxI2cRelease,
.moduleName = "hi35xx_i2c_driver", // (Mandatory) The value must be the same as that in the config.hcs file.
};
HDF_INIT(g_i2cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
// Driver entry of the i2c_core.c manager service at the core layer
struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1,
.Bind = I2cManagerBind,
.Init = I2cManagerInit,
.Release = I2cManagerRelease,
.moduleName = "HDF_PLATFORM_I2C_MANAGER", // This parameter corresponds to device0 in the device_info file.
};
HDF_INIT(g_i2cManagerEntry);
HDF_INIT(g_i2cManagerEntry);
```
2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **i2c_config.hcs** file.
The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **I2cCntlr** members at the core layer.
In the unified service mode, the first device node in the **device_info** file must be the I2C manager. The table below lists the settings of its parameters.
**Table 2** Settings of the I2C manager
| Parameter| Value|
......@@ -102,12 +106,12 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
| moduleName | **HDF_PLATFORM_I2C_MANAGER**|
| serviceName | **HDF_PLATFORM_I2C_MANAGER**|
| policy | **1** or **2**, depending on whether the service is published to the user mode.|
| deviceMatchAttr | This parameter is reserved.|
| deviceMatchAttr | Reserved|
Configure I2C controller information from the second node. This node specifies a type of I2C controllers rather than a specific I2C controller. The controllers are distinguishes by **busID** and **reg_pbase**, which can be seen in the **i2c_config** file.
Configure I2C controller information from the second node. This node specifies a type of I2C controllers rather than a specific I2C controller. The controllers are distinguishes by **busID** and **reg_pbase**, which can be seen in the **i2c_config** file.
- **device_info.hcs** configuration example
```
root {
......@@ -123,21 +127,21 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
deviceMatchAttr = "hdf_platform_i2c_manager";
}
device1 :: deviceNode {
policy = 0; // The value 0 indicates that no service needs to be published.
priority = 55; // Driver startup priority.
permission = 0644; // Permission for the driver to create a device node.
policy = 0; // The value 0 indicates that no service needs to be published.
priority = 55; // Driver startup priority.
permission = 0644; // Permission for the driver to create a device node.
moduleName = "hi35xx_i2c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
serviceName = "HI35XX_I2C_DRIVER"; // (Mandatory) Unique name of the service published by the driver.
deviceMatchAttr = "hisilicon_hi35xx_i2c"; // (Mandatory) Used to configure the private data of the controller. The value must be the same as the controller information in i2c_config.hcs.
//The specific controller information is stored in i2c_config.hcs.
//The specific controller information is stored in i2c_config.hcs.
}
}
}
}
```
- **i2c_config.hcs** configuration example
```
root {
......@@ -182,7 +186,7 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
uint32_t clk; // (Optional) Customized.
uint32_t freq; // (Optional) Customized.
uint32_t irq; // (Optional) Customized.
uint32_t regBasePhy // (Mandatory) Physical base address of the register.
uint32_t regBasePhy; // (Mandatory) Physical base address of the register.
};
// I2cCntlr is a controller structure at the core layer. The Init function assigns values to the members of I2cCntlr.
......@@ -205,19 +209,19 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
};
static const struct I2cLockMethod g_lockOps = {
.lock = Hi35xxI2cLock, // Lock function
.unlock = Hi35xxI2cUnlock,// Unlock function
.lock = Hi35xxI2cLock, // Lock function
.unlock = Hi35xxI2cUnlock, // Unlock function
};
```
- **Init** function
Input parameter:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
Return value:
**Return value**:
HDF_STATUS<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
**HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
**Table 3** HDF_STATUS
......@@ -230,7 +234,7 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
| HDF_SUCCESS | Transmission successful.|
| HDF_FAILURE | Transmission failed.|
Function description:
**Function description**:
Initializes the custom structure object and **I2cCntlr**, calls the **I2cCntlrAdd** function at the core layer, and mounts the VFS (optional).
......@@ -255,20 +259,20 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
...
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // Address mapping.
...
Hi35xxI2cCntlrInit(hi35xx); // (Mandatory) Initialize the I2C device.
Hi35xxI2cCntlrInit(hi35xx); // (Mandatory) Initialize the I2C device.
hi35xx->cntlr.priv = (void *)node; // (Mandatory) Device attributes.
hi35xx->cntlr.busId = hi35xx->bus; // (Mandatory) Initialize busId in I2cCntlr.
hi35xx->cntlr.ops = &g_method; // (Mandatory) Hook the I2cMethod instance.
hi35xx->cntlr.lockOps = &g_lockOps; // (Mandatory) Hook the I2cLockMethod instance.
(void)OsalSpinInit(&hi35xx->spin); // (Mandatory) Initialize the lock.
hi35xx->cntlr.priv = (void *)node; // (Mandatory) Device attributes.
hi35xx->cntlr.busId = hi35xx->bus; // (Mandatory) Initialize busId in I2cCntlr.
hi35xx->cntlr.ops = &g_method; // (Mandatory) Hook the I2cMethod instance.
hi35xx->cntlr.lockOps = &g_lockOps; // (Mandatory) Hook the I2cLockMethod instance.
(void)OsalSpinInit(&hi35xx->spin); // (Mandatory) Initialize the lock.
ret = I2cCntlrAdd(&hi35xx->cntlr); // (Mandatory) Call this function to set the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned.
...
#ifdef USER_VFS_SUPPORT
(void)I2cAddVfsById(hi35xx->cntlr.busId);// (Optional) Mount the user-level VFS if required.
#endif
return HDF_SUCCESS;
__ERR__: // If the operation fails, execute the initialization functions reversely.
__ERR__: // If the operation fails, execute the initialization functions reversely.
if (hi35xx != NULL) {
if (hi35xx->regBase != NULL) {
OsalIoUnmap((void *)hi35xx->regBase);
......@@ -282,15 +286,15 @@ The following uses **i2c_hi35xx.c** as an example to present the information req
```
- **Release** function
Input parameter:
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
Return value:
**Return value**:
No value is returned.
Function description:
**Function description**:
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.
......
......@@ -42,9 +42,9 @@ In the Hardware Driver Foundation (HDF), the I3C module uses the unified service
The I3C module is divided into the following layers:
- The interface layer provides APIs for opening or closing a controller, transmitting messages, and obtaining and setting controller parameters.
- The core layer provides the capabilities of binding, initializing, and releasing devices.
- The adaptation layer implements other functions.
- Interface layer: provides APIs for opening or closing a controller, transmitting messages, and obtaining and setting controller parameters.
- Core layer: provides the capabilities of binding, initializing, and releasing devices.
- Adaptation layer: implements other functions.
**Figure 1** Unified service mode
......@@ -126,9 +126,11 @@ The I3C module adaptation involves the following steps:
I3C driver entry example:
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> The system may have multiple I3C controllers. Therefore, you need to create a manager object in the HDF and publish a manager service to uniformly handle external access requests. Then, the manager service locates the controller to open based on the specified parameters.
>
> The core layer implements the driver of the I3C manager service. Vendors do not need to care about the implementation. However, the **I3cCntlrAdd()** function at the core layer must be called in the implementation of **Init()** to implement related features.
> The core layer implements the driver of the I3C manager service. You do not need to care about the implementation. However, the **I3cCntlrAdd()** function at the core layer must be called in the implementation of **Init()** to implement related features.
```c
static struct HdfDriverEntry g_virtualI3cDriverEntry = {
......@@ -137,7 +139,7 @@ The I3C module adaptation involves the following steps:
.Release = VirtualI3cRelease,
.moduleName = "virtual_i3c_driver",// (Mandatory) The value must be the same as that in the .hcs file.
};
HDF_INIT(g_virtualI3cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
HDF_INIT(g_virtualI3cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
/* Driver entry of the i3c_core.c manager service at the core layer */
struct HdfDriverEntry g_i3cManagerEntry = {
......@@ -155,7 +157,7 @@ The I3C module adaptation involves the following steps:
In the unified service mode, the first device node in the **device_info** file must be the I3C manager. The I3C manager parameters must be set as follows.
|Name|Value|
|Parameter|Value|
|-|-|
|moduleName |HDF_PLATFORM_I3C_MANAGER|
|serviceName|Reserved|
......@@ -178,13 +180,13 @@ The I3C module adaptation involves the following steps:
}
}
i3c_virtual :: deviceNode {
policy = 0; // The value 0 indicates that no service is published.
priority = 56; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
moduleName = "virtual_i3c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
policy = 0; // The value 0 indicates that no service is published.
priority = 56; // Driver startup priority.
permission = 0644; // Permission to create device nodes for the driver.
moduleName = "virtual_i3c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
serviceName = "VIRTUAL_I3C_DRIVER"; // (Mandatory) Unique name of the service published by the driver.
deviceMatchAttr = "virtual_i3c"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in i3c_config.hcs.
} // The specific controller information is in i3c_config.hcs.
deviceMatchAttr = "virtual_i3c"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in i3c_config.hcs.
} // The specific controller information is in i3c_config.hcs.
}
```
......@@ -194,17 +196,17 @@ The I3C module adaptation involves the following steps:
root {
platform {
i3c_config {
match_attr = "virtual_i3c"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
match_attr = "virtual_i3c"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs.
template i3c_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes.
busId = 0; // (Mandatory) I3C bus number.
busMode = 0x0; // Bus mode, which can be 0x0 (pure), 0x1 (mixed-fast), 0x2 (mixed-limited), or 0x3 (mixed-slow).
busMode = 0x0; // Bus mode, which can be 0x0 (pure), 0x1 (mixed-fast), 0x2 (mixed-limited), or 0x3 (mixed-slow).
regBasePhy = 0x120b0000; // (Mandatory) Physical base address.
regSize = 0xd1; // (Mandatory) Register bit width.
IrqNum = 20; // (Mandatory) Interrupt request (IRQ) number.
i3cMaxRate = 12900000; // (Optional) Maximum clock rate in I3C mode.
regSize = 0xd1; // (Mandatory) Register bit width.
IrqNum = 20; // (Mandatory) Interrupt request (IRQ) number.
i3cMaxRate = 12900000; // (Optional) Maximum clock rate in I3C mode.
i3cRate = 12500000; // (Optional) Clock rate in I3C mode.
i2cFmRate = 1000000; // (Optional) Clock rate in I2C FM mode.
i2cFmPlusRate = 400000; // (Optional) Clock rate in I2C FM+ mode.
i2cFmRate = 1000000; // (Optional) Clock rate in I2C FM mode.
i2cFmPlusRate = 400000; // (Optional) Clock rate in I2C FM+ mode.
}
controller_0 :: i3c_controller {
busId = 18;
......@@ -219,199 +221,204 @@ The I3C module adaptation involves the following steps:
Initialize the **I3cCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **I3cMethod** in **I3cCntlr** (so that the underlying driver functions can be called).
- Define a custom structure.
Instantiate **I3cMethod** in **I3cCntlr**. The **I3cLockMethod** callback structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in **Init()**.
- Defining a custom structure
To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **i3c_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **I3cCntlr** object at the core layer.
```c
struct VirtualI3cCntlr {
struct I3cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description.
volatile unsigned char *regBase; //(Mandatory) Register base address.
uint32_t regBasePhy; // (Mandatory) Physical base address of the register.
uint32_t regSize; // (Mandatory) Bit width of the register.
uint16_t busId; // (Mandatory) Bus number.
uint16_t busMode;
uint16_t IrqNum;
uint32_t i3cMaxRate;
uint32_t i3cRate;
uint32_t i2cFmRate;
uint32_t i2cFmPlusRate;
};
/* I3cCntlr is the controller structure at the core layer. The Init function assigns values to the members of I3cCntlr.
struct I3cCntlr {
OsalSpinlock lock;
void *owner;
int16_t busId;
struct I3cConfig config;
uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16];
struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX];
const struct I3cMethod *ops;
const struct I3cLockMethod *lockOps;
void *priv;
};
```
- **Init** function
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
**Return value**:
**HDF_STATUS**<br>The table below lists some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
| Status| Description|
| -------- | -------- |
| HDF_ERR_INVALID_OBJECT | Invalid controller object.|
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
| HDF_ERR_INVALID_PARAM | Invalid parameter.|
| HDF_ERR_IO | I/O error.|
| HDF_SUCCESS | Transmission successful.|
| HDF_FAILURE | Transmission failed.|
**Function description**:
Initializes the custom structure object and **I3cCntlr**, and calls the **I3cCntlrAdd** function to add the I3C controller to the core layer.
```c
static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
int32_t ret;
struct VirtualI3cCntlr *virtual = NULL; // (Mandatory) Custom structure object.
(void)device;
virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // (Mandatory) Allocate memory.
if (virtual == NULL) {
HDF_LOGE("%s: Malloc virtual fail!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
ret = VirtualI3cReadDrs(virtual, node); // (Mandatory) Fill the default values defined in the i3c_config file to the structure.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
goto __ERR__;
}
...
virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);// (Mandatory) Address mapping.
ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); // (Mandatory) Register an interrupt handler.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: register irq failed!", __func__);
return ret;
}
...
VirtualI3cCntlrInit(virtual); // (Mandatory) Initialize the I3C device.
virtual->cntlr.priv = (void *)node; // (Mandatory) Set the storage device attributes.
virtual->cntlr.busId = virtual->busId; // (Mandatory) Initialize I3cCntlr.
virtual->cntlr.ops = &g_method; // (Mandatory) Attach the I3cMethod instance.
(void)OsalSpinInit(&virtual->spin);
ret = I3cCntlrAdd(&virtual->cntlr); // (Mandatory) Call this function to add the controller to the core layer. If a success signal is returned, the driver is completely connected to the core layer.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret);
(void)OsalSpinDestroy(&virtual->spin);
goto __ERR__;
}
return HDF_SUCCESS;
__ERR__: // If the controller fails to be added, deinitialize related functions.
if (virtual != NULL) {
OsalMemFree(virtual);
virtual = NULL;
}
return ret;
}
static int32_t VirtualI3cInit(struct HdfDeviceObject *device)
{
int32_t ret;
const struct DeviceResourceNode *childNode = NULL;
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return HDF_ERR_INVALID_OBJECT;
}
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = VirtualI3cParseAndInit(device, childNode);
if (ret != HDF_SUCCESS) {
break;
}
}
return ret;
}
```
- **Release** function
**Input parameter**:
> To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **i3c_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **I3cCntlr** object at the core layer.
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
```c
struct VirtualI3cCntlr {
struct I3cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description.
volatile unsigned char *regBase;// (Mandatory) Register base address.
uint32_t regBasePhy; // (Mandatory) Physical base address of the register.
uint32_t regSize; // (Mandatory) Bit width of the register.
uint16_t busId; // (Mandatory) Bus number.
uint16_t busMode;
uint16_t IrqNum;
uint32_t i3cMaxRate;
uint32_t i3cRate;
uint32_t i2cFmRate;
uint32_t i2cFmPlusRate;
};
/* I3cCntlr is the controller structure at the core layer. The Init function assigns values to the members of I3cCntlr.
struct I3cCntlr {
OsalSpinlock lock;
void *owner;
int16_t busId;
struct I3cConfig config;
uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16];
struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX];
const struct I3cMethod *ops;
const struct I3cLockMethod *lockOps;
void *priv;
};
```
**Return value**:
No value is returned.
> Instantiate **I3cMethod** in **I3cCntlr**. The **I3cLockMethod** callback structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in **Init()**.
**Function description**:
- **Init** function
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.
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information.
**Return value**:
HDF_STATUS<br>The table below lists some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
| Status| Description|
| -------- | -------- |
| HDF_ERR_INVALID_OBJECT | Invalid controller object.|
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
| HDF_ERR_INVALID_PARAM | Invalid parameter.|
| HDF_ERR_IO | I/O error.|
| HDF_SUCCESS | Transmission successful.|
| HDF_FAILURE | Transmission failed.|
**Function description**:
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
```c
static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node)
{
int32_t ret;
int16_t busId;
struct I3cCntlr *cntlr = NULL;
struct VirtualI3cCntlr *virtual = NULL;
struct DeviceResourceIface *drsOps = NULL;
Initializes the custom structure object and **I3cCntlr**, and calls the **I3cCntlrAdd** function to add the I3C controller to the core layer.
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
```c
static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
int32_t ret;
struct VirtualI3cCntlr *virtual = NULL; // (Mandatory) Custom structure object.
(void)device;
virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // (Mandatory) Allocate memory.
if (virtual == NULL) {
HDF_LOGE("%s: Malloc virtual fail!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
ret = VirtualI3cReadDrs(virtual, node); // (Mandatory) Fill the default values defined in the i3c_config file to the structure.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
goto __ERR__;
}
...
virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);// (Mandatory) Address mapping.
ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); // (Mandatory) Register an interrupt handler.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: register irq failed!", __func__);
return ret;
}
...
VirtualI3cCntlrInit(virtual); // (Mandatory) Initialize the I3C device.
virtual->cntlr.priv = (void *)node; // (Mandatory) Set the storage device attributes.
virtual->cntlr.busId = virtual->busId; // (Mandatory) Initialize I3cCntlr.
virtual->cntlr.ops = &g_method; // (Mandatory) Attach the I3cMethod instance.
(void)OsalSpinInit(&virtual->spin);
ret = I3cCntlrAdd(&virtual->cntlr); // (Mandatory) Call this function to add the controller to the core layer. If a success signal is returned, the driver is completely connected to the core layer.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret);
(void)OsalSpinDestroy(&virtual->spin);
goto __ERR__;
}
return HDF_SUCCESS;
__ERR__: // If the controller fails to be added, deinitialize related functions.
if (virtual != NULL) {
OsalMemFree(virtual);
virtual = NULL;
}
return ret;
}
static int32_t VirtualI3cInit(struct HdfDeviceObject *device)
{
int32_t ret;
const struct DeviceResourceNode *childNode = NULL;
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return HDF_ERR_INVALID_OBJECT;
}
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = VirtualI3cParseAndInit(device, childNode);
if (ret != HDF_SUCCESS) {
break;
}
}
return ret;
}
```
- **Release** function
ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: read busId fail!", __func__);
return;
}
...
/* Call I3cCntlrGet() to obtain the I3cCntlr object based on the cntlrNum of the device, and then call I3cCntlrRemove() to release the I3cCntlr object. */
cntlr = I3cCntlrGet(busId);
if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr);
I3cCntlrRemove(cntlr); // (Mandatory) Remove the I3cCntlr object from the manager driver.
virtual = (struct VirtualI3cCntlr *)cntlr; // (Mandatory) Obtain the custom object through a forced conversion and perform the release operation.
(void)OsalSpinDestroy(&virtual->spin);
OsalMemFree(virtual);
}
return;
}
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
**Return value**:
No value is returned.
**Function description**:
static void VirtualI3cRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
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. All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
HDF_LOGI("%s: enter", __func__);
```c
static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node)
{
int32_t ret;
int16_t busId;
struct I3cCntlr *cntlr = NULL;
struct VirtualI3cCntlr *virtual = NULL;
struct DeviceResourceIface *drsOps = NULL;
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: read busId fail!", __func__);
return;
}
...
/* Call I3cCntlrGet() to obtain the I3cCntlr object based on the cntlrNum of the device, and then call I3cCntlrRemove() to release the I3cCntlr object. */
cntlr = I3cCntlrGet(busId);
if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr);
I3cCntlrRemove(cntlr); // (Mandatory) Remove the I3cCntlr object from the manager driver.
virtual = (struct VirtualI3cCntlr *)cntlr; // (Mandatory) Obtain the custom object through a forced conversion and perform the release operation.
(void)OsalSpinDestroy(&virtual->spin);
OsalMemFree(virtual);
}
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
static void VirtualI3cRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
HDF_LOGI("%s: enter", __func__);
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
...
// Traverse and parse all nodes in i3c_config.hcs and perform the release operation on each node.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); // See the description of VirtualI3cRemoveByNode for more details.
}
...
// Traverse and parse all nodes in i3c_config.hcs and perform the release operation on each node.
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); // See the description of VirtualI3cRemoveByNode for more details.
}
```
}
```
4. Register an interrupt handler.
The interrupt handler performs an IBI or hot-join based on the address where the interrupt is generated.
......
......@@ -42,31 +42,34 @@ struct MipiCsiCntlrMethod {
| enableSensorClock | **cntlr**: structure pointer to the MIPI CSI controller.<br>**snsClkSource**: number of the clock signal cable of the sensor, which is of the uint8_t type.| – | HDF_STATUS| Enables the MIPI sensor clock. |
| disableSensorClock | **cntlr**: structure pointer to the MIPI CSI controller.<br>**snsClkSource**: number of the clock signal cable of the sensor, which is of the uint8_t type.| – | HDF_STATUS| Disables the MIPI sensor clock. |
| resetSensor | **cntlr**: structure pointer to the MIPI CSI controller.<br>**snsClkSource**: number of the clock signal cable of the sensor, which is of the uint8_t type.| – | HDF_STATUS| Resets a sensor. |
| unresetSensor | **cntlr**: structure pointer to the MIPI CSI controller.<br>**snsClkSource**: number of the clock signal cable of the sensor, which is of the uint8_t type.| – | HDF_STATUS| Deasserts the reset of a sensor. |
| unresetSensor | **cntlr**: structure pointer to the MIPI CSI controller.<br>**snsClkSource**: number of the clock signal cable of the sensor, which is of the uint8_t type.| – | HDF_STATUS| Deasserts the reset of a sensor. |
## How to Develop
The MIPI CSI module adaptation involves the following steps:
1. Instantiate the driver entry.
- Instantiate the **HdfDriverEntry** structure.
- Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF.
2. Configure attribute files.
1. Configure attribute files.
- Add the **deviceNode** information to the **device_info.hcs** file.
- (Optional) Add the **mipicsi_config.hcs** file.
2. Instantiate the driver entry.
- Instantiate the **HdfDriverEntry** structure.
- Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF.
3. Instantiate the MIPI CSI controller object.
- Initialize **MipiCsiCntlr**.
- Instantiate **MipiCsiCntlrMethod** in the **MipiCsiCntlr** object.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br>
>![](../public_sys-resources/icon-note.gif) **NOTE**<br>
>For details about the functions in **MipiCsiCntlrMethod**, see [Available APIs](#available-apis).
4. Debug the driver.
- (Optional) For new drivers, verify the basic functions, for example, the data transmission and the information returned after the **MipiCsiCntlrMethod** instance is attached.
(Optional) For new drivers, verify the basic functions, for example, the data transmission and the information returned after the **MipiCsiCntlrMethod** instance is attached.
## Development Example
The following uses **mipi_rx_hi35xx.c** as an example to present the information required for implementing device functions.
......@@ -76,246 +79,266 @@ The following uses **mipi_rx_hi35xx.c** as an example to present the information
The device attribute values are closely related to the default values or value range of the **MipiCsiCntlr** members at the core layer. The **deviceNode** information is related to the driver entry registration.
In this example, the MIPI controller attributes are defined in the source file. If required, add the **deviceMatchAttr** information to **deviceNode** in the **device_info** file and add the **mipicsi_config.hcs** file.
- **device_info.hcs** configuration example
```c
root {
device_info {
match_attr = "hdf_manager";
platform :: host {
hostName = "platform_host";
priority = 50;
device_mipi_csi:: device {
device0 :: deviceNode {
policy = 0;
priority = 160;
permission = 0644;
moduleName = "HDF_MIPI_RX"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
serviceName = "HDF_MIPI_RX"; // (Mandatory) Unique name of the service published by the driver.
}
}
}
}
}
```
2. Instantiate the driver entry.<br/>The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. The function pointer members in the **HdfDriverEntry** structure are filled by the vendors' operation functions. 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.
Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
- MIPI CSI driver entry example
```c
struct HdfDriverEntry g_mipiCsiDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxMipiCsiInit, // See the Init function.
.Release = Hi35xxMipiCsiRelease, //See the Release function.
.moduleName = "HDF_MIPI_RX", // (Mandatory) The value must be the same as that in the device_info.hcs file.
};
HDF_INIT(g_mipiCsiDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
```
3. Initialize the **MipiCsiCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **MipiCsiCntlrMethod** in **MipiCsiCntlr** (so that the underlying driver functions can be called).
- Defining a custom structure
To the driver, the custom structure hols parameters and data. The values in the **config** file are used to initialize the structure members. In this example, the MIPI CSI attributes are defined in the source file. Therefore, the basic member structure is similar to that of **MipiCsiCntlr**.
```c
typedef struct {
/** The data type can be 8-, 10-, 12-, 14-, or 16-bit. */
DataType inputDataType;
/** MIPI WDM mode */
MipiWdrMode wdrMode;
/** laneId: -1 - disabled */
short laneId[MIPI_LANE_NUM];
union {
/** Used for HI_MIPI_WDR_MODE_DT */
short dataType[WDR_VC_NUM];
};
} MipiDevAttr;
typedef struct {
/** Device number */
uint8_t devno;
/** Input mode, which can be MIPI, LVDS, sub-LVDS, HiSPi, or DC. */
InputMode inputMode;
MipiDataRate dataRate;
/** Crop area of the MIPI RX device (same as the size of the sensor input image) */
ImgRect imgRect;
union {
MipiDevAttr mipiAttr;
LvdsDevAttr lvdsAttr;
};
} ComboDevAttr;
// MipiCsiCntlr is the core layer controller structure. The Init function assigns values to the members of MipiCsiCntlr.
struct MipiCsiCntlr {
/** Send the service provided by this controller when the driver is bound to the HDF. */
struct IDeviceIoService service;
/** Pass the pointer to the device when the driver is bound to the HDF. */
struct HdfDeviceObject *device;
/** Device number */
unsigned int devNo;
/** All APIs provided by the controller */
struct MipiCsiCntlrMethod *ops;
/** All APIs for controller debugging. Set it to null if the driver is not implemented. */
struct MipiCsiCntlrDebugMethod *debugs;
/** Controller context variable. */
MipiDevCtx ctx;
/** Spinlock used when the controller context variable is accessed. */
OsalSpinlock ctxLock;
/** Lock method when the controller is managed */
struct OsalMutex lock;
/** Pointer to the anonymous structure that holds the CSI device data */
void *priv;
};
```
- Instantiating **MipiCsiCntlrMethod** in **MipiCsiCntlr** (other members are initialized by **Init**)
```c
static struct MipiCsiCntlrMethod g_method = {
.setComboDevAttr = Hi35xxSetComboDevAttr,
.setPhyCmvmode = Hi35xxSetPhyCmvmode,
.setExtDataType = Hi35xxSetExtDataType,
.setHsMode = Hi35xxSetHsMode,
.enableClock = Hi35xxEnableClock,
.disableClock = Hi35xxDisableClock,
.resetRx = Hi35xxResetRx,
.unresetRx = Hi35xxUnresetRx,
.enableSensorClock = Hi35xxEnableSensorClock,
.disableSensorClock = Hi35xxDisableSensorClock,
.resetSensor = Hi35xxResetSensor,
.unresetSensor = Hi35xxUnresetSensor
};
```
- Init function
**Input parameter**:
>![](../public_sys-resources/icon-note.gif) **NOTE**<br>
>In this example, the MIPI controller attributes are defined in the source file. If required, add the **deviceMatchAttr** information to **deviceNode** in the **device_info** file and add the **mipicsi_config.hcs** file.
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
- **device_info.hcs** configuration example
**Return value**:
```c
root {
device_info {
match_attr = "hdf_manager";
platform :: host {
hostName = "platform_host";
priority = 50;
device_mipi_csi:: device {
device0 :: deviceNode {
policy = 0;
priority = 160;
permission = 0644;
moduleName = "HDF_MIPI_RX"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry.
serviceName = "HDF_MIPI_RX"; // (Mandatory) Unique name of the service published by the driver.
}
}
}
}
}
```
HDF_STATUS<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
2. Instantiate the driver entry.
| Status | Description |
| :--------------------- | :----------: |
| HDF_ERR_INVALID_OBJECT | Invalid object. |
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
| HDF_ERR_INVALID_PARAM | Invalid parameter. |
| HDF_ERR_IO | I/O error. |
| HDF_SUCCESS | Operation successful. |
| HDF_FAILURE | Operation failed. |
**Function description**:
Attaches the **MipiCsiCntlrMethod** instance, calls **MipiCsiRegisterCntlr**, and initializes the custom structure.
```c
static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device)
{
int32_t ret;
HDF_LOGI("%s: enter!", __func__);
g_mipiCsi.priv = NULL; // g_mipiTx is a global variable defined.
// static struct MipiCsiCntlr g_mipiCsi = {
// .devNo = 0
//};
g_mipiCsi.ops = &g_method; // Attach the MipiCsiCntlrMethod instance.
#ifdef CONFIG_HI_PROC_SHOW_SUPPORT
g_mipiCsi.debugs = &g_debugMethod;
#endif
ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // (Mandatory) Call the function at the core layer and g_mipiTx to initialize global variables at the core layer.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__);
return ret;
}
ret = MipiRxDrvInit(); // (Mandatory) Device initialization customized by the vendor.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__);
return ret;
}
#ifdef MIPICSI_VFS_SUPPORT
ret = MipiCsiDevModuleInit(g_mipiCsi.devNo);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__);
return ret;
}
#endif
OsalSpinInit(&g_mipiCsi.ctxLock);
HDF_LOGI("%s: load mipi csi driver success!", __func__);
return ret;
}
// mipi_csi_core.c file
int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device)
{
...
// Global variable static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT];
if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) {
(void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock);
(void)OsalMutexInit(&(cntlr->lock));
g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // Initialize MipiCsiHandle.
g_mipiCsihandle[cntlr->devNo].priv = NULL;
cntlr->device = device; // Prerequisites for conversion between HdfDeviceObject and MipiCsiHandle
device->service = &(cntlr->service); // Prerequisites for conversion between HdfDeviceObject and MipiCsiHandle.
cntlr->priv = NULL;
HDF_LOGI("%s: success.", __func__);
return HDF_SUCCESS;
}
HDF_LOGE("%s: cntlr already exists.", __func__);
return HDF_FAILURE;
}
```
- Release function
**Input parameter**:
The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. The function pointer members in the **HdfDriverEntry** structure are filled by the vendors' operation functions. 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.
Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit.
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
- MIPI CSI driver entry example
**Return value**:
No value is returned.
```c
struct HdfDriverEntry g_mipiCsiDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxMipiCsiInit, // See the Init function.
.Release = Hi35xxMipiCsiRelease, // See the Release function.
.moduleName = "HDF_MIPI_RX", // (Mandatory) The value must be the same as that in the device_info.hcs file.
};
HDF_INIT(g_mipiCsiDriverEntry); // Call HDF_INIT to register the driver entry with the HDF.
```
**Function description**:
Releases the memory and deletes the controller. This function assigns values to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources.
All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
```c
static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device)
{
struct MipiCsiCntlr *cntlr = NULL;
...
cntlr = MipiCsiCntlrFromDevice(device); // A forced conversion from HdfDeviceObject to MipiCsiCntlr is involved.
// return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service;
...
OsalSpinDestroy(&cntlr->ctxLock);
#ifdef MIPICSI_VFS_SUPPORT
MipiCsiDevModuleExit(cntlr->devNo);
#endif
MipiRxDrvExit(); // (Mandatory) Release the resources occupied by vendor devices.
MipiCsiUnregisterCntlr(&g_mipiCsi); // Null function
g_mipiCsi.priv = NULL;
HDF_LOGI("%s: unload mipi csi driver success!", __func__);
}
```
3. Initialize the **MipiCsiCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **MipiCsiCntlrMethod** in **MipiCsiCntlr** (so that the underlying driver functions can be called).
- Defining a custom structure
To the driver, the custom structure hols parameters and data. The values in the **config** file are used to initialize the structure members. In this example, the MIPI CSI attributes are defined in the source file. Therefore, the basic member structure is similar to that of **MipiCsiCntlr**.
```c
typedef struct {
/** The data type can be 8-, 10-, 12-, 14-, or 16-bit. */
DataType inputDataType;
/** MIPI WDM mode */
MipiWdrMode wdrMode;
/** laneId: -1 - disabled */
short laneId[MIPI_LANE_NUM];
union {
/** Used for HI_MIPI_WDR_MODE_DT */
short dataType[WDR_VC_NUM];
};
} MipiDevAttr;
typedef struct {
/** Device number */
uint8_t devno;
/** Input mode, which can be MIPI, LVDS, sub-LVDS, HiSPi, or DC. */
InputMode inputMode;
MipiDataRate dataRate;
/** Crop area of the MIPI RX device (same as the size of the sensor input image) */
ImgRect imgRect;
union {
MipiDevAttr mipiAttr;
LvdsDevAttr lvdsAttr;
};
} ComboDevAttr;
// MipiCsiCntlr is the core layer controller structure. The Init function assigns values to the members of MipiCsiCntlr.
struct MipiCsiCntlr {
/** Send the service provided by this controller when the driver is bound to the HDF. */
struct IDeviceIoService service;
/** Pass the pointer to the device when the driver is bound to the HDF. */
struct HdfDeviceObject *device;
/** Device number */
unsigned int devNo;
/** All APIs provided by the controller */
struct MipiCsiCntlrMethod *ops;
/** All APIs for controller debugging. Set it to null if the driver is not implemented. */
struct MipiCsiCntlrDebugMethod *debugs;
/** Controller context variable. */
MipiDevCtx ctx;
/** Spinlock used when the controller context variable is accessed. */
OsalSpinlock ctxLock;
/** Lock method when the controller is managed */
struct OsalMutex lock;
/** Pointer to the anonymous structure that holds the CSI device data */
void *priv;
};
```
- Instantiating **MipiCsiCntlrMethod** in **MipiCsiCntlr**
>![](../public_sys-resources/icon-note.gif)
>
>Other members are initialized by **Init**.
```c
static struct MipiCsiCntlrMethod g_method = {
.setComboDevAttr = Hi35xxSetComboDevAttr,
.setPhyCmvmode = Hi35xxSetPhyCmvmode,
.setExtDataType = Hi35xxSetExtDataType,
.setHsMode = Hi35xxSetHsMode,
.enableClock = Hi35xxEnableClock,
.disableClock = Hi35xxDisableClock,
.resetRx = Hi35xxResetRx,
.unresetRx = Hi35xxUnresetRx,
.enableSensorClock = Hi35xxEnableSensorClock,
.disableSensorClock = Hi35xxDisableSensorClock,
.resetSensor = Hi35xxResetSensor,
.unresetSensor = Hi35xxUnresetSensor
};
```
- **Init** function
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
**Return value**:
**HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
| Status | Description |
| :--------------------- | :------------------------: |
| HDF_ERR_INVALID_OBJECT | Invalid object. |
| HDF_ERR_MALLOC_FAIL | Failed to allocate memory. |
| HDF_ERR_INVALID_PARAM | Invalid parameter. |
| HDF_ERR_IO | I/O error. |
| HDF_SUCCESS | Operation successful. |
| HDF_FAILURE | Operation failed. |
**Function description**:
Attaches the **MipiCsiCntlrMethod** instance, calls **MipiCsiRegisterCntlr**, and initializes the custom structure.
```c
static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device)
{
int32_t ret;
HDF_LOGI("%s: enter!", __func__);
g_mipiCsi.priv = NULL; // g_mipiTx is a global variable defined.
// static struct MipiCsiCntlr g_mipiCsi = {
// .devNo = 0
//};
g_mipiCsi.ops = &g_method; // Attach the MipiCsiCntlrMethod instance.
#ifdef CONFIG_HI_PROC_SHOW_SUPPORT
g_mipiCsi.debugs = &g_debugMethod;
#endif
ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // (Mandatory) Call the function at the core layer and g_mipiTx to initialize global variables at the core layer.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__);
return ret;
}
ret = MipiRxDrvInit(); // (Mandatory) Device initialization customized by the vendor.
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__);
return ret;
}
#ifdef MIPICSI_VFS_SUPPORT
ret = MipiCsiDevModuleInit(g_mipiCsi.devNo);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__);
return ret;
}
#endif
OsalSpinInit(&g_mipiCsi.ctxLock);
HDF_LOGI("%s: load mipi csi driver success!", __func__);
return ret;
}
// mipi_csi_core.c file
int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device)
{
...
// Global variable static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT];
if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) {
(void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock);
(void)OsalMutexInit(&(cntlr->lock));
g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // Initialize MipiCsiHandle.
g_mipiCsihandle[cntlr->devNo].priv = NULL;
cntlr->device = device; // Prerequisites for conversion between HdfDeviceObject and MipiCsiHandle
device->service = &(cntlr->service); // Prerequisites for conversion between HdfDeviceObject and MipiCsiHandle.
cntlr->priv = NULL;
HDF_LOGI("%s: success.", __func__);
return HDF_SUCCESS;
}
HDF_LOGE("%s: cntlr already exists.", __func__);
return HDF_FAILURE;
}
```
- **Release** function
**Input parameter**:
**HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
**Return value**:
No value is returned.
**Function description**:
Releases the memory and deletes the controller. This function assigns values to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources.
>![](../public_sys-resources/icon-note.gif) **NOTE**
>
>All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations.
```c
static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device)
{
struct MipiCsiCntlr *cntlr = NULL;
...
cntlr = MipiCsiCntlrFromDevice(device); // A forced conversion from HdfDeviceObject to MipiCsiCntlr is involved.
// return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service;
...
OsalSpinDestroy(&cntlr->ctxLock);
#ifdef MIPICSI_VFS_SUPPORT
MipiCsiDevModuleExit(cntlr->devNo);
#endif
MipiRxDrvExit(); // (Mandatory) Release the resources occupied by vendor devices.
MipiCsiUnregisterCntlr(&g_mipiCsi); // Null function
g_mipiCsi.priv = NULL;
HDF_LOGI("%s: unload mipi csi driver success!", __func__);
}
```
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册