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