1e41f4b71Sopenharmony_ci# I3C 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci## Introduction 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci### Function 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ciImproved Inter-Integrated Circuit (I3C) is a simple and cost-efficient two-wire bidirectional synchronous serial bus protocol developed by the Mobile Industry Processor Interface (MIPI) Alliance. 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ciI3C is a two-wire bidirectional serial bus, optimized for multiple sensor target devices and controlled by only one I3C controller at a time. It is backward compatible with Inter-Integrated circuit (I2C) target devices, but features higher speed and lower power consumption and supports in-band interrupts (IBIs), hot-joins of target devices, and controller switchover. 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ciThe IBIs over the serial bus eliminates the need for an extra interrupt line to complete interrupts in I2C. I2C devices, I3C target devices, and the I3C secondary controller can co-exist on the same I3C bus. 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci### Basic Concepts 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ci- IBI 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci When there is no start signal on the serial clock (SCL) line, the I3C target device can pull down the serial data (SDA) line to make the controller send an SCL start signal, which initiates an IBI request. If multiple target devices send interrupt requests at the same time, the I3C controller arbitrates the requests based on the target device addresses. The request with a lower address is responded first. 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ci- Dynamic Address Assignment (DAA) 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ci The I3C controller can dynamically allocate addresses to target devices to avoid address conflicts. Before addresses are allocated, each I3C device connected to an I3C bus must be uniquely identified in either of the following ways: 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ci - The device has an I2C compliant static address that can be used by the host. 24e41f4b71Sopenharmony_ci - The device has a 48-bit temporary ID. 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci The host must use a 48-bit temporary ID unless the device has a static IP address. 27e41f4b71Sopenharmony_ci 28e41f4b71Sopenharmony_ci- Common Command Code (CCC) 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci All I3C devices support CCC. The CCC can be sent to an I3C target device or all I3C target devices. 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci- Bus Characteristic Register (BCR) 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ci Each I3C device connected to an I3C bus has a read-only BCR, which describes the I3C compliant device's role and capabilities for use in DAA and CCC. 35e41f4b71Sopenharmony_ci 36e41f4b71Sopenharmony_ci- Device Characteristic Register (DCR) 37e41f4b71Sopenharmony_ci 38e41f4b71Sopenharmony_ci Each I3C device connected to an I3C bus has a read-only DCR, which describes the I3C compliant device type (such as accelerometers, gyroscope, and others) for use in DAA and DCC. 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ci 41e41f4b71Sopenharmony_ci### Working Principles 42e41f4b71Sopenharmony_ci 43e41f4b71Sopenharmony_ciIn the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than 10 I3C controllers. 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 following figure illustrtes the unified service mode. 44e41f4b71Sopenharmony_ci 45e41f4b71Sopenharmony_ciThe I3C module is divided into the following layers: 46e41f4b71Sopenharmony_ci 47e41f4b71Sopenharmony_ci- Interface layer: provides the capabilities of opening a device, writing data, and closing a device. 48e41f4b71Sopenharmony_ci- Core layer: binds services, initializes and releases the PlatformManager, and provides the capabilities of adding, deleting, and obtaining controllers. The core layer also provides capabilities of adding, deleting, and obtaining the devices connected to the I3C bus and interrupt callbacks. 49e41f4b71Sopenharmony_ci- Adaptation layer: implements hardware-related functions, such as controller initialization. 50e41f4b71Sopenharmony_ci 51e41f4b71Sopenharmony_ciIn the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. 52e41f4b71Sopenharmony_ci 53e41f4b71Sopenharmony_ci **Figure 1** Unified service mode 54e41f4b71Sopenharmony_ci 55e41f4b71Sopenharmony_ci 56e41f4b71Sopenharmony_ci 57e41f4b71Sopenharmony_ci### Constraints 58e41f4b71Sopenharmony_ci 59e41f4b71Sopenharmony_ciThe I3C module supports only the kernel (LiteOS-A) for mini and small systems. 60e41f4b71Sopenharmony_ci 61e41f4b71Sopenharmony_ci## Development Guidelines 62e41f4b71Sopenharmony_ci 63e41f4b71Sopenharmony_ci### When to Use 64e41f4b71Sopenharmony_ci 65e41f4b71Sopenharmony_ciI3C can connect to one or more I3C or I2C target devices. It is used to: 66e41f4b71Sopenharmony_ci 67e41f4b71Sopenharmony_ci- Communicate with sensors, such as gyroscopes, barometers, and image sensors that support the I3C protocol. 68e41f4b71Sopenharmony_ci- Communicate with devices with other ports (such as UART serial ports) through software or hardware protocols. 69e41f4b71Sopenharmony_ci 70e41f4b71Sopenharmony_ciBefore using I3C devices with OpenHarmony, you need to adapt the I3C driver to OpenHarmony. The following describes how to do it. 71e41f4b71Sopenharmony_ci 72e41f4b71Sopenharmony_ci### Available APIs 73e41f4b71Sopenharmony_ci 74e41f4b71Sopenharmony_ciTo enable the upper layer to successfully operate the hardware by calling the I3C APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. 75e41f4b71Sopenharmony_ci 76e41f4b71Sopenharmony_ci**I3cMethod**: 77e41f4b71Sopenharmony_ci```c 78e41f4b71Sopenharmony_cistruct I3cMethod { 79e41f4b71Sopenharmony_ci int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc); 80e41f4b71Sopenharmony_ci int32_t (*transfer)(struct I3cCntlr *cntlr, struct I3cMsg *msgs, int16_t count); 81e41f4b71Sopenharmony_ci int32_t (*i2cTransfer)(struct I3cCntlr *cntlr, struct I3cMsg *msgs, int16_t count); 82e41f4b71Sopenharmony_ci int32_t (*setConfig)(struct I3cCntlr *cntlr, struct I3cConfig *config); 83e41f4b71Sopenharmony_ci int32_t (*getConfig)(struct I3cCntlr *cntlr, struct I3cConfig *config); 84e41f4b71Sopenharmony_ci int32_t (*requestIbi)(struct I3cDevice *dev); 85e41f4b71Sopenharmony_ci void (*freeIbi)(struct I3cDevice *dev); 86e41f4b71Sopenharmony_ci}; 87e41f4b71Sopenharmony_ci``` 88e41f4b71Sopenharmony_ci 89e41f4b71Sopenharmony_ci**Table 1** Hook functions in **I3cMethod** 90e41f4b71Sopenharmony_ci|Function|Input Parameter|Output Parameter|Return Value|Description| 91e41f4b71Sopenharmony_ci|-|-|-|-|-| 92e41f4b71Sopenharmony_ci|sendCccCmd| **cntlr**: structure pointer to an I3C controller at the core layer.<br>**ccc**: pointer to the CCC to send.| **ccc**: pointer to the CCC sent.| HDF_STATUS|Sends a CCC.| 93e41f4b71Sopenharmony_ci|Transfer | **cntlr**: structure pointer to an I3C controller at the core layer.<br>**msgs**: structure pointer to the messages to transfer.<br>**count**: number of messages to transfer, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS| Transfers user messages in I3C mode.| 94e41f4b71Sopenharmony_ci|i2cTransfer | **cntlr**: structure pointer to an I3C controller at the core layer.<br>**msgs**: structure pointer to the messages to transfer.<br>**count**: number of messages to transfer, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS| Transfers user messages in I2C mode.| 95e41f4b71Sopenharmony_ci|setConfig| **cntlr**: structure pointer to an I3C controller at the core layer.<br>**config**: pointer to the controller configuration.| –| HDF_STATUS| Sets an I3C controller.| 96e41f4b71Sopenharmony_ci|getConfig| **cntlr**: structure pointer to an I3C controller at the core layer.| **config**: pointer to the controller configuration.| HDF_STATUS| Obtains the I3C controller configuration.| 97e41f4b71Sopenharmony_ci|requestIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS| Requests an IBI for an I3C device.| 98e41f4b71Sopenharmony_ci|freeIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS| Releases the IBI for an I3C device.| 99e41f4b71Sopenharmony_ci 100e41f4b71Sopenharmony_ci### How to Develop 101e41f4b71Sopenharmony_ci 102e41f4b71Sopenharmony_ciThe I3C module adaptation involves the following steps: 103e41f4b71Sopenharmony_ci 104e41f4b71Sopenharmony_ci1. Instantiate the driver entry. 105e41f4b71Sopenharmony_ci 106e41f4b71Sopenharmony_ci - Instantiate the **HdfDriverEntry** structure. 107e41f4b71Sopenharmony_ci - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. 108e41f4b71Sopenharmony_ci 109e41f4b71Sopenharmony_ci2. Configure attribute files. 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci - Add the **deviceNode** information to the **device_info.hcs** file. 112e41f4b71Sopenharmony_ci - (Optional) Add the **i3c_config.hcs** file. 113e41f4b71Sopenharmony_ci 114e41f4b71Sopenharmony_ci3. Instantiate the I3C controller object. 115e41f4b71Sopenharmony_ci 116e41f4b71Sopenharmony_ci - Initialize **I3cCntlr**. 117e41f4b71Sopenharmony_ci - Instantiate **I3cMethod** in **I3cCntlr**. For details, see the description of **I3cMethod** below. 118e41f4b71Sopenharmony_ci 119e41f4b71Sopenharmony_ci4. Register an interrupt handler. 120e41f4b71Sopenharmony_ci 121e41f4b71Sopenharmony_ci Registers an interrupt handler for the controller to implement the device hot-join and IBI features. 122e41f4b71Sopenharmony_ci 123e41f4b71Sopenharmony_ci### Example 124e41f4b71Sopenharmony_ci 125e41f4b71Sopenharmony_ci1. Instantiate the driver entry. 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_ci The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **//drivers/hdf_core/framework/include/core/hdf_device_desc.h**), and the module name 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. 128e41f4b71Sopenharmony_ci 129e41f4b71Sopenharmony_ci 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. 130e41f4b71Sopenharmony_ci 131e41f4b71Sopenharmony_ci I3C driver entry example: 132e41f4b71Sopenharmony_ci 133e41f4b71Sopenharmony_ci >  **NOTE** 134e41f4b71Sopenharmony_ci > 135e41f4b71Sopenharmony_ci > Multiple devices may connect to the I3C controller. In the HDF, a manager object needs to be created for this type of devices, and a manager service is published to handle external access requests uniformly. When a device needs to be started, the manager service locates the target device based on the specified parameters. 136e41f4b71Sopenharmony_ci > 137e41f4b71Sopenharmony_ci > You do not need to implement the driver of the I3C manager, which is implemented by the core layer. However, the **I3cCntlrAdd** function of the core layer must be invoked in the **Init** function to implement the related features. 138e41f4b71Sopenharmony_ci 139e41f4b71Sopenharmony_ci ```c 140e41f4b71Sopenharmony_ci static struct HdfDriverEntry g_virtualI3cDriverEntry = { 141e41f4b71Sopenharmony_ci .moduleVersion = 1, 142e41f4b71Sopenharmony_ci .Init = VirtualI3cInit, 143e41f4b71Sopenharmony_ci .Release = VirtualI3cRelease, 144e41f4b71Sopenharmony_ci .moduleName = "virtual_i3c_driver", // (Mandatory) The value must be the same as that in the .hcs file. 145e41f4b71Sopenharmony_ci }; 146e41f4b71Sopenharmony_ci HDF_INIT(g_virtualI3cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. 147e41f4b71Sopenharmony_ci 148e41f4b71Sopenharmony_ci /* Driver entry of the i3c_core.c manager service at the core layer. */ 149e41f4b71Sopenharmony_ci struct HdfDriverEntry g_i3cManagerEntry = { 150e41f4b71Sopenharmony_ci .moduleVersion = 1, 151e41f4b71Sopenharmony_ci .Init = I3cManagerInit, 152e41f4b71Sopenharmony_ci .Release = I3cManagerRelease, 153e41f4b71Sopenharmony_ci .moduleName = "HDF_PLATFORM_I3C_MANAGER", // The value must be the same as that of device0 in the device_info.hcs file. 154e41f4b71Sopenharmony_ci }; 155e41f4b71Sopenharmony_ci HDF_INIT(g_i3cManagerEntry); 156e41f4b71Sopenharmony_ci ``` 157e41f4b71Sopenharmony_ci 158e41f4b71Sopenharmony_ci2. Configure attribute files. 159e41f4b71Sopenharmony_ci 160e41f4b71Sopenharmony_ci Add the **deviceNode** information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file and configure the device attributes in **i3c_config.hcs**. 161e41f4b71Sopenharmony_ci 162e41f4b71Sopenharmony_ci The **deviceNode** information is related to the driver entry registration. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **I3cCntlr** members at the core layer. 163e41f4b71Sopenharmony_ci 164e41f4b71Sopenharmony_ci In the unified service mode, the first device node in the **device_info.hcs** file must be the I3C manager. The I3C manager parameters must be set as follows: 165e41f4b71Sopenharmony_ci 166e41f4b71Sopenharmony_ci |Parameter|Value| 167e41f4b71Sopenharmony_ci |-|-| 168e41f4b71Sopenharmony_ci |moduleName |HDF_PLATFORM_I3C_MANAGER| 169e41f4b71Sopenharmony_ci |serviceName|Reserved.| 170e41f4b71Sopenharmony_ci |policy|0| 171e41f4b71Sopenharmony_ci |cntlrMatchAttr| Reserved.| 172e41f4b71Sopenharmony_ci 173e41f4b71Sopenharmony_ci Configure I3C controller information from the second node. This node specifies a type of I3C controllers rather than a specific I3C controller. In this example, there is only one I3C controller. If there are multiple I3C controllers, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **i3c_config** file for each controller. 174e41f4b71Sopenharmony_ci 175e41f4b71Sopenharmony_ci - **device_info.hcs** example 176e41f4b71Sopenharmony_ci 177e41f4b71Sopenharmony_ci ```c 178e41f4b71Sopenharmony_ci root { 179e41f4b71Sopenharmony_ci device_i3c :: device { 180e41f4b71Sopenharmony_ci device0 :: deviceNode { 181e41f4b71Sopenharmony_ci policy = 0; 182e41f4b71Sopenharmony_ci priority = 52; 183e41f4b71Sopenharmony_ci permission = 0644; 184e41f4b71Sopenharmony_ci serviceName = "HDF_PLATFORM_I3C_MANAGER"; 185e41f4b71Sopenharmony_ci moduleName = "HDF_PLATFORM_I3C_MANAGER"; 186e41f4b71Sopenharmony_ci } 187e41f4b71Sopenharmony_ci } 188e41f4b71Sopenharmony_ci i3c_virtual :: deviceNode { 189e41f4b71Sopenharmony_ci policy = 0; // The value 0 indicates that no service is published. 190e41f4b71Sopenharmony_ci priority = 56; // Driver startup priority. 191e41f4b71Sopenharmony_ci permission = 0644; // Permission for the device node created. 192e41f4b71Sopenharmony_ci moduleName = "virtual_i3c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. 193e41f4b71Sopenharmony_ci serviceName = "VIRTUAL_I3C_DRIVER"; // (Mandatory) Unique name of the service published by the driver. 194e41f4b71Sopenharmony_ci deviceMatchAttr = "virtual_i3c"; // (Mandatory) Controller private data, which must be same as that of the controller in i3c_config.hcs. 195e41f4b71Sopenharmony_ci } // The specific controller information is in i3c_config.hcs. 196e41f4b71Sopenharmony_ci } 197e41f4b71Sopenharmony_ci ``` 198e41f4b71Sopenharmony_ci 199e41f4b71Sopenharmony_ci - i3c_config.hcs example 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci ```c 202e41f4b71Sopenharmony_ci root { 203e41f4b71Sopenharmony_ci platform { 204e41f4b71Sopenharmony_ci i3c_config { 205e41f4b71Sopenharmony_ci match_attr = "virtual_i3c"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. 206e41f4b71Sopenharmony_ci template i3c_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes. 207e41f4b71Sopenharmony_ci busId = 0; // (Mandatory) I3C bus number. 208e41f4b71Sopenharmony_ci busMode = 0x0; // Bus mode, which can be 0x0 (pure), 0x1 (mixed-fast), 0x2 (mixed-limited), or 0x3 (mixed-slow). 209e41f4b71Sopenharmony_ci regBasePhy = 0x120b0000; // (Mandatory) Physical base address. 210e41f4b71Sopenharmony_ci regSize = 0xd1; // (Mandatory) Register bit width. 211e41f4b71Sopenharmony_ci IrqNum = 20; // (Mandatory) Interrupt request (IRQ) number. 212e41f4b71Sopenharmony_ci i3cMaxRate = 12900000; // (Optional) Maximum clock rate in I3C mode. 213e41f4b71Sopenharmony_ci i3cRate = 12500000; // (Optional) Clock rate in I3C mode. 214e41f4b71Sopenharmony_ci i2cFmRate = 1000000; // (Optional) Clock rate in I2C FM mode. 215e41f4b71Sopenharmony_ci i2cFmPlusRate = 400000; // (Optional) Clock rate in I2C FM+ mode. 216e41f4b71Sopenharmony_ci } 217e41f4b71Sopenharmony_ci controller_0 :: i3c_controller { 218e41f4b71Sopenharmony_ci busId = 18; 219e41f4b71Sopenharmony_ci IrqNum = 20; 220e41f4b71Sopenharmony_ci } 221e41f4b71Sopenharmony_ci } 222e41f4b71Sopenharmony_ci } 223e41f4b71Sopenharmony_ci } 224e41f4b71Sopenharmony_ci ``` 225e41f4b71Sopenharmony_ci After the **i3c_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. 226e41f4b71Sopenharmony_ci 227e41f4b71Sopenharmony_ci For example, if the path of **i3c_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs**, add the following statement to **hdf.hcs** of the product: 228e41f4b71Sopenharmony_ci 229e41f4b71Sopenharmony_ci ```c 230e41f4b71Sopenharmony_ci #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs" // Relative path of the file. 231e41f4b71Sopenharmony_ci ``` 232e41f4b71Sopenharmony_ci 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci 235e41f4b71Sopenharmony_ci3. Instantiate the I3C controller object. 236e41f4b71Sopenharmony_ci 237e41f4b71Sopenharmony_ci 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). 238e41f4b71Sopenharmony_ci 239e41f4b71Sopenharmony_ci Instantiate **I3cMethod** in **I3cCntlr**.<br>The **I3cLockMethod** hook function structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in **Init()**. 240e41f4b71Sopenharmony_ci 241e41f4b71Sopenharmony_ci - Define a custom structure. 242e41f4b71Sopenharmony_ci 243e41f4b71Sopenharmony_ci >  **NOTE** 244e41f4b71Sopenharmony_ci > 245e41f4b71Sopenharmony_ci > 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. 246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci ```c 248e41f4b71Sopenharmony_ci struct VirtualI3cCntlr { 249e41f4b71Sopenharmony_ci struct I3cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description. 250e41f4b71Sopenharmony_ci volatile unsigned char *regBase; // (Mandatory) Register base address. 251e41f4b71Sopenharmony_ci uint32_t regBasePhy // (Mandatory) Physical base address of the register. 252e41f4b71Sopenharmony_ci uint32_t regSize; // (Mandatory) Register bit width. 253e41f4b71Sopenharmony_ci uint16_t busId; // (Mandatory) Bus number. 254e41f4b71Sopenharmony_ci uint16_t busMode; 255e41f4b71Sopenharmony_ci uint16_t IrqNum; 256e41f4b71Sopenharmony_ci uint32_t i3cMaxRate; 257e41f4b71Sopenharmony_ci uint32_t i3cRate; 258e41f4b71Sopenharmony_ci uint32_t i2cFmRate; 259e41f4b71Sopenharmony_ci uint32_t i2cFmPlusRate; 260e41f4b71Sopenharmony_ci }; 261e41f4b71Sopenharmony_ci 262e41f4b71Sopenharmony_ci /* I3cCntlr is the controller structure at the core layer. The Init function assigns values to the members of I3cCntlr. */ 263e41f4b71Sopenharmony_ci struct I3cCntlr { 264e41f4b71Sopenharmony_ci OsalSpinlock lock; 265e41f4b71Sopenharmony_ci void *owner; 266e41f4b71Sopenharmony_ci int16_t busId; 267e41f4b71Sopenharmony_ci struct I3cConfig config; 268e41f4b71Sopenharmony_ci uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16]; 269e41f4b71Sopenharmony_ci struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX]; 270e41f4b71Sopenharmony_ci const struct I3cMethod *ops; 271e41f4b71Sopenharmony_ci const struct I3cLockMethod *lockOps; 272e41f4b71Sopenharmony_ci void *priv; 273e41f4b71Sopenharmony_ci }; 274e41f4b71Sopenharmony_ci ``` 275e41f4b71Sopenharmony_ci 276e41f4b71Sopenharmony_ci - Implement the **Init** function. 277e41f4b71Sopenharmony_ci 278e41f4b71Sopenharmony_ci **Input parameter**: 279e41f4b71Sopenharmony_ci 280e41f4b71Sopenharmony_ci **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 281e41f4b71Sopenharmony_ci 282e41f4b71Sopenharmony_ci **Return value**: 283e41f4b71Sopenharmony_ci 284e41f4b71Sopenharmony_ci **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. 285e41f4b71Sopenharmony_ci 286e41f4b71Sopenharmony_ci 287e41f4b71Sopenharmony_ci |Status|Description| 288e41f4b71Sopenharmony_ci |:-|:-:| 289e41f4b71Sopenharmony_ci |HDF_ERR_INVALID_OBJECT|Invalid controller object.| 290e41f4b71Sopenharmony_ci |HDF_ERR_INVALID_PARAM |Invalid parameter.| 291e41f4b71Sopenharmony_ci |HDF_ERR_MALLOC_FAIL |Failed to allocate the memory.| 292e41f4b71Sopenharmony_ci |HDF_ERR_IO |I/O error.| 293e41f4b71Sopenharmony_ci |HDF_SUCCESS |Transmission successful.| 294e41f4b71Sopenharmony_ci |HDF_FAILURE |Transmission failed.| 295e41f4b71Sopenharmony_ci 296e41f4b71Sopenharmony_ci **Function description**: 297e41f4b71Sopenharmony_ci 298e41f4b71Sopenharmony_ci Initializes the custom structure object and **I3cCntlr**, and calls the **I3cCntlrAdd** function to add the I3C controller to the core layer. 299e41f4b71Sopenharmony_ci 300e41f4b71Sopenharmony_ci ```c 301e41f4b71Sopenharmony_ci static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) 302e41f4b71Sopenharmony_ci { 303e41f4b71Sopenharmony_ci int32_t ret; 304e41f4b71Sopenharmony_ci struct VirtualI3cCntlr *virtual = NULL; // (Mandatory) Custom structure object. 305e41f4b71Sopenharmony_ci (void)device; 306e41f4b71Sopenharmony_ci 307e41f4b71Sopenharmony_ci virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // (Mandatory) Allocate memory. 308e41f4b71Sopenharmony_ci if (virtual == NULL) { 309e41f4b71Sopenharmony_ci HDF_LOGE("%s: Malloc virtual fail!", __func__); 310e41f4b71Sopenharmony_ci return HDF_ERR_MALLOC_FAIL; 311e41f4b71Sopenharmony_ci } 312e41f4b71Sopenharmony_ci 313e41f4b71Sopenharmony_ci ret = VirtualI3cReadDrs(virtual, node); // (Mandatory) Use the default values in the i3c_config file to fill in the structure. For details about the function definition, see the following. 314e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 315e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); 316e41f4b71Sopenharmony_ci goto __ERR__; 317e41f4b71Sopenharmony_ci } 318e41f4b71Sopenharmony_ci ... 319e41f4b71Sopenharmony_ci virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);// (Mandatory) Address mapping. 320e41f4b71Sopenharmony_ci ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); // (Mandatory) Register an interrupt handler. 321e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 322e41f4b71Sopenharmony_ci HDF_LOGE("%s: register irq failed!", __func__); 323e41f4b71Sopenharmony_ci return ret; 324e41f4b71Sopenharmony_ci } 325e41f4b71Sopenharmony_ci ... 326e41f4b71Sopenharmony_ci VirtualI3cCntlrInit(virtual); // (Mandatory) Initialize the I3C device. 327e41f4b71Sopenharmony_ci virtual->cntlr.priv = (void *)node; // (Mandatory) Set the storage device attributes. 328e41f4b71Sopenharmony_ci virtual->cntlr.busId = virtual->busId; // (Mandatory) Initialize I3cCntlr. 329e41f4b71Sopenharmony_ci virtual->cntlr.ops = &g_method; // (Mandatory) Attach the I3cMethod instance. 330e41f4b71Sopenharmony_ci (void)OsalSpinInit(&virtual->spin); 331e41f4b71Sopenharmony_ci ret = I3cCntlrAdd(&virtual->cntlr); // (Mandatory) Call this function to add the controller to the core layer. The driver can access the platform core layer only when a success signal is returned. 332e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 333e41f4b71Sopenharmony_ci HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret); 334e41f4b71Sopenharmony_ci (void)OsalSpinDestroy(&virtual->spin); 335e41f4b71Sopenharmony_ci goto __ERR__; 336e41f4b71Sopenharmony_ci } 337e41f4b71Sopenharmony_ci 338e41f4b71Sopenharmony_ci return HDF_SUCCESS; 339e41f4b71Sopenharmony_ci __ERR__: // If the controller fails to be added, deinitialize related functions. 340e41f4b71Sopenharmony_ci if (virtual != NULL) { 341e41f4b71Sopenharmony_ci OsalMemFree(virtual); 342e41f4b71Sopenharmony_ci virtual = NULL; 343e41f4b71Sopenharmony_ci } 344e41f4b71Sopenharmony_ci 345e41f4b71Sopenharmony_ci return ret; 346e41f4b71Sopenharmony_ci } 347e41f4b71Sopenharmony_ci 348e41f4b71Sopenharmony_ci static int32_t VirtualI3cInit(struct HdfDeviceObject *device) 349e41f4b71Sopenharmony_ci { 350e41f4b71Sopenharmony_ci int32_t ret; 351e41f4b71Sopenharmony_ci const struct DeviceResourceNode *childNode = NULL; 352e41f4b71Sopenharmony_ci 353e41f4b71Sopenharmony_ci if (device == NULL || device->property == NULL) { 354e41f4b71Sopenharmony_ci HDF_LOGE("%s: device or property is NULL", __func__); 355e41f4b71Sopenharmony_ci return HDF_ERR_INVALID_OBJECT; 356e41f4b71Sopenharmony_ci } 357e41f4b71Sopenharmony_ci 358e41f4b71Sopenharmony_ci DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 359e41f4b71Sopenharmony_ci ret = VirtualI3cParseAndInit(device, childNode); 360e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 361e41f4b71Sopenharmony_ci break; 362e41f4b71Sopenharmony_ci } 363e41f4b71Sopenharmony_ci } 364e41f4b71Sopenharmony_ci 365e41f4b71Sopenharmony_ci return ret; 366e41f4b71Sopenharmony_ci } 367e41f4b71Sopenharmony_ci 368e41f4b71Sopenharmony_ci static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node) 369e41f4b71Sopenharmony_ci { 370e41f4b71Sopenharmony_ci struct DeviceResourceIface *drsOps = NULL; 371e41f4b71Sopenharmony_ci 372e41f4b71Sopenharmony_ci /* Obtain the drsOps method. */ 373e41f4b71Sopenharmony_ci drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 374e41f4b71Sopenharmony_ci if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { 375e41f4b71Sopenharmony_ci HDF_LOGE("%s: Invalid drs ops fail!", __func__); 376e41f4b71Sopenharmony_ci return HDF_FAILURE; 377e41f4b71Sopenharmony_ci } 378e41f4b71Sopenharmony_ci /* Read the configuration parameters in sequence and fill them in the structure. */ 379e41f4b71Sopenharmony_ci if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) { 380e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read busId fail!", __func__); 381e41f4b71Sopenharmony_ci return HDF_ERR_IO; 382e41f4b71Sopenharmony_ci } 383e41f4b71Sopenharmony_ci if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) { 384e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read busMode fail!", __func__); 385e41f4b71Sopenharmony_ci return HDF_ERR_IO; 386e41f4b71Sopenharmony_ci } 387e41f4b71Sopenharmony_ci if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) { 388e41f4b71Sopenharmony_ci HDF_LOGE("%s: Read IrqNum fail!", __func__); 389e41f4b71Sopenharmony_ci return HDF_ERR_IO; 390e41f4b71Sopenharmony_ci } 391e41f4b71Sopenharmony_ci ··· 392e41f4b71Sopenharmony_ci return HDF_SUCCESS; 393e41f4b71Sopenharmony_ci } 394e41f4b71Sopenharmony_ci ``` 395e41f4b71Sopenharmony_ci 396e41f4b71Sopenharmony_ci - Implement the **Release** function. 397e41f4b71Sopenharmony_ci 398e41f4b71Sopenharmony_ci **Input parameter**: 399e41f4b71Sopenharmony_ci 400e41f4b71Sopenharmony_ci **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. 401e41f4b71Sopenharmony_ci 402e41f4b71Sopenharmony_ci **Return value**: 403e41f4b71Sopenharmony_ci 404e41f4b71Sopenharmony_ci No value is returned. 405e41f4b71Sopenharmony_ci 406e41f4b71Sopenharmony_ci **Function description**: 407e41f4b71Sopenharmony_ci 408e41f4b71Sopenharmony_ci Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. 409e41f4b71Sopenharmony_ci 410e41f4b71Sopenharmony_ci >  **NOTE** 411e41f4b71Sopenharmony_ci > 412e41f4b71Sopenharmony_ci > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. 413e41f4b71Sopenharmony_ci 414e41f4b71Sopenharmony_ci ```c 415e41f4b71Sopenharmony_ci static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node) 416e41f4b71Sopenharmony_ci { 417e41f4b71Sopenharmony_ci int32_t ret; 418e41f4b71Sopenharmony_ci int16_t busId; 419e41f4b71Sopenharmony_ci struct I3cCntlr *cntlr = NULL; 420e41f4b71Sopenharmony_ci struct VirtualI3cCntlr *virtual = NULL; 421e41f4b71Sopenharmony_ci struct DeviceResourceIface *drsOps = NULL; 422e41f4b71Sopenharmony_ci 423e41f4b71Sopenharmony_ci drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); 424e41f4b71Sopenharmony_ci if (drsOps == NULL || drsOps->GetUint32 == NULL) { 425e41f4b71Sopenharmony_ci HDF_LOGE("%s: invalid drs ops fail!", __func__); 426e41f4b71Sopenharmony_ci return; 427e41f4b71Sopenharmony_ci } 428e41f4b71Sopenharmony_ci 429e41f4b71Sopenharmony_ci ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0); 430e41f4b71Sopenharmony_ci if (ret != HDF_SUCCESS) { 431e41f4b71Sopenharmony_ci HDF_LOGE("%s: read busId fail!", __func__); 432e41f4b71Sopenharmony_ci return; 433e41f4b71Sopenharmony_ci } 434e41f4b71Sopenharmony_ci ... 435e41f4b71Sopenharmony_ci /* Call I3cCntlrGet() to obtain the I3cCntlr object based on the cntlrNum of the device, and then call I3cCntlrRemove() to release the I3cCntlr object. */ 436e41f4b71Sopenharmony_ci cntlr = I3cCntlrGet(busId); 437e41f4b71Sopenharmony_ci if (cntlr != NULL && cntlr->priv == node) { 438e41f4b71Sopenharmony_ci I3cCntlrPut(cntlr); 439e41f4b71Sopenharmony_ci I3cCntlrRemove(cntlr); // (Mandatory) Remove the I3cCntlr object from the manager driver. 440e41f4b71Sopenharmony_ci virtual = (struct VirtualI3cCntlr *)cntlr; // (Mandatory) Obtain the custom object through a forced conversion and perform the release operation. 441e41f4b71Sopenharmony_ci (void)OsalSpinDestroy(&virtual->spin); 442e41f4b71Sopenharmony_ci OsalMemFree(virtual); 443e41f4b71Sopenharmony_ci } 444e41f4b71Sopenharmony_ci return; 445e41f4b71Sopenharmony_ci } 446e41f4b71Sopenharmony_ci 447e41f4b71Sopenharmony_ci static void VirtualI3cRelease(struct HdfDeviceObject *device) 448e41f4b71Sopenharmony_ci { 449e41f4b71Sopenharmony_ci const struct DeviceResourceNode *childNode = NULL; 450e41f4b71Sopenharmony_ci 451e41f4b71Sopenharmony_ci HDF_LOGI("%s: enter", __func__); 452e41f4b71Sopenharmony_ci 453e41f4b71Sopenharmony_ci if (device == NULL || device->property == NULL) { 454e41f4b71Sopenharmony_ci HDF_LOGE("%s: device or property is NULL", __func__); 455e41f4b71Sopenharmony_ci return; 456e41f4b71Sopenharmony_ci } 457e41f4b71Sopenharmony_ci ... 458e41f4b71Sopenharmony_ci /* Traverse and parse all nodes in i3c_config.hcs and perform the release operation on each node. */ 459e41f4b71Sopenharmony_ci DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { 460e41f4b71Sopenharmony_ci VirtualI3cRemoveByNode(childNode); // See the description of VirtualI3cRemoveByNode for more details. 461e41f4b71Sopenharmony_ci } 462e41f4b71Sopenharmony_ci } 463e41f4b71Sopenharmony_ci ``` 464e41f4b71Sopenharmony_ci 465e41f4b71Sopenharmony_ci4. Register an interrupt handler. 466e41f4b71Sopenharmony_ci 467e41f4b71Sopenharmony_ci The interrupt handler performs an IBI or hot-join based on the address where the interrupt is generated. 468e41f4b71Sopenharmony_ci 469e41f4b71Sopenharmony_ci ```c 470e41f4b71Sopenharmony_ci static int32_t VirtualI3cReservedAddrWorker(struct VirtualI3cCntlr *virtual, uint16_t addr) 471e41f4b71Sopenharmony_ci { 472e41f4b71Sopenharmony_ci (void)virtual; 473e41f4b71Sopenharmony_ci switch (addr) { 474e41f4b71Sopenharmony_ci case I3C_HOT_JOIN_ADDR: 475e41f4b71Sopenharmony_ci VirtualI3cHotJoin(virtual); 476e41f4b71Sopenharmony_ci break; 477e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H3E: 478e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H5E: 479e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H6E: 480e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H76: 481e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H7A: 482e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H7C: 483e41f4b71Sopenharmony_ci case I3C_RESERVED_ADDR_7H7F: 484e41f4b71Sopenharmony_ci /* All single-bit errors in the broadcast address */ 485e41f4b71Sopenharmony_ci HDF_LOGW("%s: broadcast Address single bit error!", __func__); 486e41f4b71Sopenharmony_ci break; 487e41f4b71Sopenharmony_ci default: 488e41f4b71Sopenharmony_ci HDF_LOGD("%s: Reserved address which is not supported!", __func__); 489e41f4b71Sopenharmony_ci break; 490e41f4b71Sopenharmony_ci } 491e41f4b71Sopenharmony_ci 492e41f4b71Sopenharmony_ci return HDF_SUCCESS; 493e41f4b71Sopenharmony_ci } 494e41f4b71Sopenharmony_ci 495e41f4b71Sopenharmony_ci static int32_t I3cIbiHandle(uint32_t irq, void *data) 496e41f4b71Sopenharmony_ci { 497e41f4b71Sopenharmony_ci struct VirtualI3cCntlr *virtual = NULL; 498e41f4b71Sopenharmony_ci struct I3cDevice *device = NULL; 499e41f4b71Sopenharmony_ci uint16_t ibiAddr; 500e41f4b71Sopenharmony_ci char *testStr = "Hello I3C!"; 501e41f4b71Sopenharmony_ci 502e41f4b71Sopenharmony_ci (void)irq; 503e41f4b71Sopenharmony_ci if (data == NULL) { 504e41f4b71Sopenharmony_ci HDF_LOGW("%s: data is NULL!", __func__); 505e41f4b71Sopenharmony_ci return HDF_ERR_INVALID_PARAM; 506e41f4b71Sopenharmony_ci } 507e41f4b71Sopenharmony_ci virtual = (struct VirtualI3cCntlr *)data; 508e41f4b71Sopenharmony_ci /* (Mandatory) Obtain the address where the interrupt is generated. Use the CHECK_RESERVED_ADDR macro to determine whether the address is an I3C address. */ 509e41f4b71Sopenharmony_ci ibiAddr = VirtualI3cGetIbiAddr(); 510e41f4b71Sopenharmony_ci if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) { 511e41f4b71Sopenharmony_ci HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__); 512e41f4b71Sopenharmony_ci return VirtualI3cReservedAddrWorker(virtual, ibiAddr); 513e41f4b71Sopenharmony_ci } else { 514e41f4b71Sopenharmony_ci HDF_LOGD("%s: Calling I3cCntlrIbiCallback...", __func__); 515e41f4b71Sopenharmony_ci device = GetDeviceByAddr(&virtual->cntlr, ibiAddr); 516e41f4b71Sopenharmony_ci if (device == NULL) { 517e41f4b71Sopenharmony_ci HDF_LOGE("func:%s device is NULL!",__func__); 518e41f4b71Sopenharmony_ci return HDF_ERR_MALLOC_FAIL; 519e41f4b71Sopenharmony_ci } 520e41f4b71Sopenharmony_ci if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) { 521e41f4b71Sopenharmony_ci /* Place the string "Hello I3C!" into the IBI buffer. */ 522e41f4b71Sopenharmony_ci *device->ibi->data = *testStr; 523e41f4b71Sopenharmony_ci } 524e41f4b71Sopenharmony_ci /* Invoke the IBI callback based on the I3C device that generates the IBI. */ 525e41f4b71Sopenharmony_ci return I3cCntlrIbiCallback(device); 526e41f4b71Sopenharmony_ci } 527e41f4b71Sopenharmony_ci 528e41f4b71Sopenharmony_ci return HDF_SUCCESS; 529e41f4b71Sopenharmony_ci } 530e41f4b71Sopenharmony_ci ``` 531