/*
 * Copyright 2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_component_i3c.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/
void I3C_BusSetAddrSlot(i3c_bus_t *bus, uint8_t addr, i3c_addr_slot_status_t status)
{
    uint16_t bitPos = (uint16_t)addr * I3C_BUS_ADDR_SLOTWIDTH;
    uint32_t *slotPtr;

    if (addr > I3C_BUS_MAX_ADDR)
    {
        return;
    }

    slotPtr = &bus->addrSlots[bitPos / I3C_BUS_ADDR_SLOTDEPTH];
    *slotPtr &= ~((uint32_t)I3C_BUS_ADDR_SLOTMASK << (bitPos % I3C_BUS_ADDR_SLOTDEPTH));
    *slotPtr |= (uint32_t)status << (bitPos % I3C_BUS_ADDR_SLOTDEPTH);
}

static void I3C_BusInitAddrSlots(i3c_bus_t *bus)
{
    uint8_t i;

    /* Reserve address 0x0 to 0x7, reserve I3C broadcast address. */
    for (i = 0; i < 8U; i++)
    {
        I3C_BusSetAddrSlot(bus, i, kI3C_Bus_AddrSlot_Reserved);
    }

    I3C_BusSetAddrSlot(bus, I3C_BUS_BROADCAST_ADDR, kI3C_Bus_AddrSlot_Reserved);
}

static uint8_t I3C_BusGetAddrSlotStatus(i3c_bus_t *bus, uint8_t checkAddr)
{
    uint16_t bitPos    = (uint16_t)checkAddr * 2U;
    uint32_t useStatus = 0;

    if (checkAddr > I3C_BUS_MAX_ADDR)
    {
        return (uint8_t)kI3C_Bus_AddrSlot_Reserved;
    }

    useStatus = bus->addrSlots[bitPos / I3C_BUS_ADDR_SLOTDEPTH];
    useStatus >>= bitPos % I3C_BUS_ADDR_SLOTDEPTH;

    return (uint8_t)(useStatus & (uint8_t)I3C_BUS_ADDR_SLOTMASK);
}

static status_t I3C_CheckBusMasterOps(i3c_device_hw_ops_t *ops)
{
    if ((ops == NULL) || (ops->Init == NULL) || (ops->DoI3CTransfer == NULL) || (ops->DoI2CTransfer == NULL) ||
        (ops->TransmitCCC == NULL) || (ops->ProceedDAA == NULL))
    {
        return kStatus_I3CBus_MasterOpsMissing;
    }

    return kStatus_Success;
}

static status_t I3C_BusMasterGetMaxReadLength(i3c_device_t *master, i3c_device_information_t *info)
{
    i3c_ccc_cmd_t getMRLCmd = {0};
    status_t result         = kStatus_Success;

    getMRLCmd.isRead   = true;
    getMRLCmd.cmdId    = I3C_BUS_CCC_GETMRL;
    getMRLCmd.destAddr = info->dynamicAddr;
    getMRLCmd.data     = malloc(3U);
    getMRLCmd.dataSize = 3U;

    /*
     * When the device does not have IBI payload GETMRL only returns 2
     * bytes of data.
     */
    if ((info->bcr & I3C_BUS_DEV_BCR_IBI_PAYLOAD_MASK) == 0U)
    {
        getMRLCmd.dataSize -= 1U;
    }

    result = I3C_BusMasterSendCCC(master, &getMRLCmd);

    uint8_t *pData = getMRLCmd.data;
    if ((info->bcr & I3C_BUS_DEV_BCR_IBI_PAYLOAD_MASK) != 0U)
    {
        info->maxIBILength = pData[2];
    }

    info->maxReadLength = (uint16_t)pData[0] << 8UL | (uint16_t)pData[1];

    free(getMRLCmd.data);

    return result;
}

static status_t I3C_BusMasterGetMaxWriteLength(i3c_device_t *master, i3c_device_information_t *info)
{
    i3c_ccc_cmd_t getMWLCmd = {0};
    status_t result         = kStatus_Success;

    uint16_t writelen;
    getMWLCmd.isRead   = true;
    getMWLCmd.cmdId    = I3C_BUS_CCC_GETMWL;
    getMWLCmd.destAddr = info->dynamicAddr;
    getMWLCmd.data     = &writelen;
    getMWLCmd.dataSize = 2U;

    result = I3C_BusMasterSendCCC(master, &getMWLCmd);

    info->maxWriteLength = writelen;

    return result;
}

static status_t I3C_BusMasterGetHDRCapability(i3c_device_t *master, i3c_device_information_t *info)
{
    i3c_ccc_cmd_t getHDRCapCmd = {0};
    status_t result            = kStatus_Success;

    uint8_t hdrMode;
    getHDRCapCmd.isRead   = true;
    getHDRCapCmd.cmdId    = I3C_BUS_CCC_GETHDRCAP;
    getHDRCapCmd.destAddr = info->dynamicAddr;
    getHDRCapCmd.data     = &hdrMode;
    getHDRCapCmd.dataSize = 1U;

    result = I3C_BusMasterSendCCC(master, &getHDRCapCmd);

    info->hdrMode = hdrMode;

    return result;
}

static status_t I3C_BusMasterGetPID(i3c_device_t *master, i3c_device_information_t *info)
{
    uint8_t pid[6];
    i3c_ccc_cmd_t getPidCmd = {0};
    status_t result         = kStatus_Success;

    getPidCmd.isRead   = true;
    getPidCmd.cmdId    = I3C_BUS_CCC_GETPID;
    getPidCmd.destAddr = info->dynamicAddr;
    getPidCmd.data     = pid;
    getPidCmd.dataSize = 6U;

    result = I3C_BusMasterSendCCC(master, &getPidCmd);

    info->vendorID   = (((uint16_t)pid[0] << 8U | (uint16_t)pid[1]) & 0xFFFEU) >> 1U;
    info->partNumber = ((uint32_t)pid[2] << 24U | (uint32_t)pid[3] << 16U | (uint32_t)pid[4] << 8U | (uint32_t)pid[5]);

    return result;
}

static status_t I3C_BusMasterGetBCR(i3c_device_t *master, i3c_device_information_t *info)
{
    uint8_t bcr;
    i3c_ccc_cmd_t getBCRCmd = {0};
    status_t result         = kStatus_Success;

    getBCRCmd.isRead   = true;
    getBCRCmd.cmdId    = I3C_BUS_CCC_GETBCR;
    getBCRCmd.destAddr = info->dynamicAddr;
    getBCRCmd.data     = &bcr;
    getBCRCmd.dataSize = 1U;

    result = I3C_BusMasterSendCCC(master, &getBCRCmd);

    info->bcr = bcr;

    return result;
}

static status_t I3C_BusMasterGetDCR(i3c_device_t *master, i3c_device_information_t *info)
{
    uint8_t dcr;
    i3c_ccc_cmd_t getDCRCmd = {0};
    status_t result         = kStatus_Success;

    getDCRCmd.isRead   = true;
    getDCRCmd.cmdId    = I3C_BUS_CCC_GETDCR;
    getDCRCmd.destAddr = info->dynamicAddr;
    getDCRCmd.data     = &dcr;
    getDCRCmd.dataSize = 1U;

    result = I3C_BusMasterSendCCC(master, &getDCRCmd);

    info->dcr = dcr;

    return result;
}

static status_t I3C_BusMasterAssignDevDynamicAddr(i3c_device_t *masterDev)
{
    i3c_bus_t *i3cBus = masterDev->bus;
    status_t result   = kStatus_Success;

    list_handle_t i3cDevList = &(i3cBus->i3cDevList);

    for (list_element_handle_t listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        i3c_device_t *tmpDev = (i3c_device_t *)(void *)listItem;
        if (tmpDev == masterDev)
        {
            continue;
        }
        else if ((tmpDev->initDynamicAddr != 0U) && (tmpDev->info.staticAddr != 0U) && (tmpDev->info.dynamicAddr == 0U))
        {
            result =
                I3C_BusMasterSetDynamicAddrFromStaticAddr(masterDev, tmpDev->info.staticAddr, tmpDev->initDynamicAddr);

            if (result != kStatus_Success)
            {
                return result;
            }

            tmpDev->info.dynamicAddr = tmpDev->initDynamicAddr;
            I3C_BusSetAddrSlot(i3cBus, tmpDev->info.dynamicAddr, kI3C_Bus_AddrSlot_I3CDev);

            /* Retrieve device information. */
            result = I3C_BusMasterGetDeviceInfo(masterDev, tmpDev->info.dynamicAddr, &tmpDev->info);
            if (result != kStatus_Success)
            {
                return result;
            }
        }
        else
        {
            /*Empty else to eliminate MISRA 15.7*/
        }
    }

    return result;
}

static status_t I3C_BusMasterAddExistingI3CDevs(i3c_device_t *masterDev)
{
    i3c_bus_t *i3cBus = masterDev->bus;
    status_t result   = kStatus_Success;

    list_handle_t i3cDevList = &(i3cBus->i3cDevList);

    for (list_element_handle_t listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        i3c_device_t *tmpDev = (i3c_device_t *)(void *)listItem;
        if (tmpDev == masterDev)
        {
            continue;
        }
        else if (tmpDev->ibiInfo != NULL)
        {
            result = I3C_BusMasterRegisterDevIBI(masterDev, tmpDev, tmpDev->ibiInfo);
            if (result != kStatus_Success)
            {
                return result;
            }
        }
    }

    return result;
}

/*!
 * brief Add exist I3C device in bus to the bus device list.
 *
 * This function will simply add the device to the bus device list and set the related address slot.
 *
 * param bus Pointer to bus structure.
 * param dev Pointer to I3C device.
 */
void I3C_BusAddI3CDev(i3c_bus_t *bus, i3c_device_t *dev)
{
    /* Chain device into i3c_device_list */
    list_handle_t i3cDevList = &bus->i3cDevList;
    (void)LIST_AddTail(i3cDevList, &dev->listNode);

    if (dev->info.dynamicAddr != 0U)
    {
        /* Set slot status I3C device */
        I3C_BusSetAddrSlot(bus, dev->info.dynamicAddr, kI3C_Bus_AddrSlot_I3CDev);
    }

    if (dev->info.staticAddr != 0U)
    {
        /* Set slot status I2C device */
        I3C_BusSetAddrSlot(bus, dev->info.staticAddr, kI3C_Bus_AddrSlot_I2CDev);
    }

    dev->bus = bus;
}

/*!
 * brief Add exist I2C device in bus to the bus device list.
 *
 * This function will simply add the device to the bus device list and set the related address slot.
 *
 * param bus Pointer to bus structure.
 * param dev Pointer to I2C device.
 */
void I3C_BusAddI2CDev(i3c_bus_t *bus, i2c_device_t *dev)
{
    /* Chain device into i2c_device_list */
    dev->bus                 = bus;
    list_handle_t i2cDevList = &bus->i2cDevList;
    (void)LIST_AddTail(i2cDevList, &dev->listNode);
    /* Set slot status I2C device */
    I3C_BusSetAddrSlot(bus, dev->staticAddr, kI3C_Bus_AddrSlot_I2CDev);
    dev->bus = bus;
}

/*!
 * brief Get valid address slot in the I3C bus.
 *
 * This function will search for available address lot in the I3C bus address pool, the search starts from
 * the startAddr specified by user input, to end address defined in @ref I3C_BUS_MAX_ADDR. Will return the
 * available address if the related address slot is valid.
 *
 * param bus Pointer to bus structure.
 * param startAddr Start address for address slot searching, end address is defined in @ref I3C_BUS_MAX_ADDR.
 * return Available address in the bus address pool.
 */
uint8_t I3C_BusGetValidAddrSlot(i3c_bus_t *bus, uint8_t startAddr)
{
    uint8_t validAddr = 0xFF;

    for (validAddr = startAddr; validAddr < I3C_BUS_MAX_ADDR; validAddr++)
    {
        if (I3C_BusGetAddrSlotStatus(bus, validAddr) == (uint8_t)kI3C_Bus_AddrSlot_Free)
        {
            return validAddr;
        }
    }

    return validAddr;
}

/*!
 * brief Gets the default configuration structure.
 *
 * This function initializes the bus configuration structure to a default value. The default
 * values are:
 *   busConfig->busMode = kI3C_Bus_PureMode;
 *   busConfig->i2cBaudRate = 400000U;
 *   busConfig->i3cOpenDrainBaudRate = 1500000U;
 *   busConfig->i3cPushPullBaudRate = 4000000U;
 *
 * param config Pointer to a configuration structure.
 */
void I3C_BusGetDefaultBusConfig(i3c_bus_config_t *busConfig)
{
    busConfig->busMode              = kI3C_Bus_PureMode;
    busConfig->i2cBaudRate          = 400000U;
    busConfig->i3cOpenDrainBaudRate = 1500000U;
    busConfig->i3cPushPullBaudRate  = 4000000U;
}

/*!
 * brief Creates I3C bus structure.
 *
 * This function creates the bus structure with input bus configuration. Address pool set up and device
 * list initialize process will be also done in this function call.
 *
 * param bus Pointer to bus structure.
 * param busConfig Pointer to the bus configuration structure.
 */
void I3C_BusCreate(i3c_bus_t *bus, const i3c_bus_config_t *busConfig)
{
    assert(bus != NULL);

    (void)memset(bus, 0, sizeof(*bus));

    LIST_Init(&bus->i2cDevList, 0);
    LIST_Init(&bus->i3cDevList, 0);
    I3C_BusInitAddrSlots(bus);
    bus->busMode              = busConfig->busMode;
    bus->i2cBaudRate          = busConfig->i2cBaudRate;
    bus->i3cOpenDrainBaudRate = busConfig->i3cOpenDrainBaudRate;
    bus->i3cPushPullBaudRate  = busConfig->i3cPushPullBaudRate;
}

/*!
 * brief Create I3C master structure on bus.
 *
 * This function will create I3C master bus structure, initialize the master according to bus characteristics, install
 * device information and device control information.
 *
 * param masterDev Pointer to device structure creating as master.
 * param bus Pointer to bus structure.
 * param devInfo Pointer to device information structure.
 * param masterControlInfo Pointer to master control information structure.
 */
status_t I3C_BusMasterCreate(i3c_device_t *masterDev,
                             i3c_bus_t *bus,
                             i3c_device_information_t *devInfo,
                             i3c_device_control_info_t *masterControlInfo)
{
    status_t result = kStatus_Success;

    (void)memset(masterDev, 0, sizeof(*masterDev));

    masterDev->info = *devInfo;
    result          = I3C_CheckBusMasterOps(masterControlInfo->funcs);

    if (result != kStatus_Success)
    {
        return result;
    }

    masterDev->devControlInfo = masterControlInfo;
    masterDev->bus            = bus;

    /* call master init to initialize master */
    result = masterControlInfo->funcs->Init(masterDev);

    if (result != kStatus_Success)
    {
        return result;
    }

    if (!masterControlInfo->isSecondary)
    {
        bus->currentMaster = masterDev;
        /*Add masterDev to bus i3c device list */
        I3C_BusAddI3CDev(bus, masterDev);

        /* Execute reset DAA CCC command to reset all i3c device dynamic address */
        result = I3C_BusMasterResetDAA(masterDev, I3C_BUS_BROADCAST_ADDR);
        if (result != kStatus_Success)
        {
            return result;
        }

        /* Master assign slave with init dynamic address. */
        result = I3C_BusMasterAssignDevDynamicAddr(masterDev);
        if (result != kStatus_Success)
        {
            return result;
        }

        result = I3C_BusMasterAddExistingI3CDevs(masterDev);
        if (result != kStatus_Success)
        {
            return result;
        }

        /* Disable all events before start doing DAA. */
        result = I3C_BusMasterDisableEvents(masterDev, I3C_BUS_BROADCAST_ADDR,
                                            ((uint8_t)kI3C_EventMR | (uint8_t)kI3C_EventHJ | (uint8_t)kI3C_EventIBI));
        if (result != kStatus_Success)
        {
            return result;
        }

        /* Start to do DAA */
        result = I3C_BusMasterDoDAA(masterDev);
        if (result != kStatus_Success)
        {
            return result;
        }

        /* Enable all events before start doing DAA. */
        result = I3C_BusMasterEnableEvents(masterDev, I3C_BUS_BROADCAST_ADDR,
                                           ((uint8_t)kI3C_EventMR | (uint8_t)kI3C_EventHJ | (uint8_t)kI3C_EventIBI));
        if (result != kStatus_Success)
        {
            return result;
        }
    }

    return result;
}

/*!
 * brief Bus master transfer CCC frame.
 *
 * Bus master call this function to transfer CCC frame, CCC frame command and data is prepared in @ref i3c_ccc_cmd_t
 * structure.
 *
 * param masterDev Pointer to I3C master device.
 * param command Pointer to ccc frame.
 */
status_t I3C_BusMasterSendCCC(i3c_device_t *masterDev, i3c_ccc_cmd_t *command)
{
    i3c_device_control_info_t *masterControlInfo = masterDev->devControlInfo;

    if (masterDev != masterDev->bus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    if (masterControlInfo->isSecondary)
    {
        return kStatus_I3CBus_Success;
    }

    if (NULL == masterControlInfo->funcs->TransmitCCC)
    {
        return kStatus_I3CBus_MasterOpsUnsupport;
    }

    return masterControlInfo->funcs->TransmitCCC(masterDev, command);
}

/*!
 * brief Bus master reset dynamic assigned address.
 *
 * Bus master call this function to reset dynamic assigned address, the operation could be done to all connected I3C
 * devices by using slave address I3C_BUS_BROADCAST_ADDR or to a specific connected device by using the device's dynamic
 * address.
 *
 * param masterDev Pointer to I3C master device.
 * param slaveAddr Slave address, use I3C_BUS_BROADCAST_ADDR as broadcast address.
 */
status_t I3C_BusMasterResetDAA(i3c_device_t *masterDev, uint8_t slaveAddr)
{
    uint8_t addrStat;
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != masterDev->bus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    addrStat = I3C_BusGetAddrSlotStatus(i3cBus, slaveAddr);
    if ((slaveAddr != I3C_BUS_BROADCAST_ADDR) && (addrStat != (uint8_t)kI3C_Bus_AddrSlot_I3CDev))
    {
        return kStatus_I3CBus_MasterOpsFailure;
    }

    i3c_ccc_cmd_t rstDaaCmd = {0};

    rstDaaCmd.isRead   = false;
    rstDaaCmd.destAddr = slaveAddr;
    rstDaaCmd.cmdId    = I3C_BUS_CCC_RSTDAA((slaveAddr != I3C_BUS_BROADCAST_ADDR));

    return I3C_BusMasterSendCCC(masterDev, &rstDaaCmd);
}

/*!
 * brief Bus master do dynamic address assignment.
 *
 * Bus master call this function to do dynamic address assignment to the I3C devices connected on bus.
 *
 * param masterDev Pointer to I3C master device.
 */
status_t I3C_BusMasterDoDAA(i3c_device_t *masterDev)
{
    i3c_device_control_info_t *masterControlInfo = masterDev->devControlInfo;
    i3c_bus_t *i3cBus                            = masterDev->bus;
    status_t result                              = kStatus_Success;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    result = masterControlInfo->funcs->ProceedDAA(masterDev);
    if (result != kStatus_Success)
    {
        return result;
    }

    /* Send defslvs command on bus if there's secondary master */
    bool isSend              = false;
    list_handle_t i3cDevList = &(i3cBus->i3cDevList);

    for (list_element_handle_t listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        i3c_device_t *tmpDev = (i3c_device_t *)(void *)listItem;
        if (tmpDev == masterDev)
        {
            continue;
        }
        else if ((tmpDev->info.bcr & I3C_BUS_DEV_BCR_DEV_ROLE_MASK) ==
                 I3C_BUS_DEV_BCR_DEV_ROLE(I3C_BUS_DEV_BCR_DEV_MASTER))
        {
            isSend = true;
            break;
        }
        else
        {
            /*Empty else to eliminate MISRA 15.7*/
        }
    }

    if (isSend)
    {
        result = I3C_BusMasterSendSlavesList(masterDev);
    }

    return result;
}

/*!
 * brief Bus master set device dynamic address from static address.
 *
 * Bus master call this function to execute SETDASA CCC command to set device dynamic address from static address.
 *
 * param masterDev Pointer to I3C master device.
 * param staticAddr Device static address.
 * param initDynamicAddr Device initialized dynamic address.
 */
status_t I3C_BusMasterSetDynamicAddrFromStaticAddr(i3c_device_t *master, uint8_t staticAddr, uint8_t initDynamicAddr)
{
    i3c_ccc_cmd_t setdasaCCC = {0};
    status_t result          = kStatus_Success;
    uint8_t dynamicAddr      = initDynamicAddr << 1;

    setdasaCCC.cmdId    = I3C_BUS_CCC_SETDASA;
    setdasaCCC.destAddr = staticAddr;
    setdasaCCC.data     = &dynamicAddr;
    setdasaCCC.dataSize = 1U;
    setdasaCCC.isRead   = false;

    result = I3C_BusMasterSendCCC(master, &setdasaCCC);

    return result;
}

/*!
 * brief Bus master send slave list on bus.
 *
 * Bus master call this function to send slave list on bus to notify the secondary master.
 *
 * param masterDev Pointer to I3C master device.
 */
status_t I3C_BusMasterSendSlavesList(i3c_device_t *masterDev)
{
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    list_element_handle_t listItem;
    list_handle_t i2cDevList = &(i3cBus->i2cDevList);
    list_handle_t i3cDevList = &(i3cBus->i3cDevList);

    i3c_ccc_cmd_t defSlavesCmd = {0};
    defSlavesCmd.isRead        = false;
    defSlavesCmd.destAddr      = I3C_BUS_BROADCAST_ADDR;
    defSlavesCmd.cmdId         = I3C_BUS_CCC_DEFSLVS;
    uint8_t devCount           = 0;
    status_t result            = kStatus_Success;

    for (listItem = i2cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        devCount++;
    }

    for (listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        devCount++;
    }

    defSlavesCmd.dataSize = sizeof(i3c_ccc_dev_t) * (uint16_t)devCount + 1U;
    defSlavesCmd.data     = malloc(defSlavesCmd.dataSize);

    uint8_t *pData = defSlavesCmd.data;

    *pData                    = devCount;
    i3c_ccc_dev_t *masterInfo = (i3c_ccc_dev_t *)(void *)&pData[1];
    masterInfo->bcr           = masterDev->info.bcr;
    masterInfo->dcr           = masterDev->info.dcr;
    masterInfo->dynamicAddr   = masterDev->info.dynamicAddr << 1U;
    masterInfo->staticAddr    = (I3C_BUS_BROADCAST_ADDR << 1U);

    i3c_ccc_dev_t *slaveInfo = (i3c_ccc_dev_t *)((uint32_t)&pData[1] + sizeof(i3c_ccc_dev_t));
    for (listItem = i2cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        slaveInfo->lvr        = ((i2c_device_t *)(void *)listItem)->lvr;
        slaveInfo->staticAddr = (((i2c_device_t *)(void *)listItem)->staticAddr << 1U);
        slaveInfo++;
    }

    for (listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        i3c_device_t *i3cDev = (i3c_device_t *)(void *)listItem;
        if (i3cDev == masterDev)
        {
            continue;
        }

        slaveInfo->dcr         = i3cDev->info.dcr;
        slaveInfo->dynamicAddr = i3cDev->info.dynamicAddr << 1U;
        slaveInfo->bcr         = i3cDev->info.bcr;
        slaveInfo->staticAddr  = i3cDev->info.staticAddr << 1U;
        slaveInfo++;
    }

    result = I3C_BusMasterSendCCC(masterDev, &defSlavesCmd);

    free(defSlavesCmd.data);

    return result;
}

/*!
 * brief Bus master enable events.
 *
 * Bus master call this function to enable events on bus.
 *
 * param masterDev Pointer to I3C master device.
 * param slaveAddr Slave address, use I3C_BUS_BROADCAST_ADDR as broadcast address.
 * param busEvents Logic OR or member in @ref _i3c_bus_events.
 */
status_t I3C_BusMasterEnableEvents(i3c_device_t *masterDev, uint8_t slaveAddr, uint8_t busEvents)
{
    i3c_ccc_cmd_t enEventsCmd = {0};
    status_t result           = kStatus_Success;

    enEventsCmd.isRead   = false;
    enEventsCmd.cmdId    = I3C_BUS_CCC_ENEC((slaveAddr != I3C_BUS_BROADCAST_ADDR));
    enEventsCmd.destAddr = slaveAddr;
    enEventsCmd.data     = &busEvents;
    enEventsCmd.dataSize = 1U;

    result = I3C_BusMasterSendCCC(masterDev, &enEventsCmd);

    return result;
}

/*!
 * brief Bus master disable events.
 *
 * Bus master call this function to disable events on bus.
 *
 * param masterDev Pointer to I3C master device.
 * param slaveAddr Slave address, use I3C_BUS_BROADCAST_ADDR as broadcast address.
 * param busEvents Logic OR or member in @ref _i3c_bus_events.
 */
status_t I3C_BusMasterDisableEvents(i3c_device_t *masterDev, uint8_t slaveAddr, uint8_t busEvents)
{
    i3c_ccc_cmd_t disEventsCmd = {0};
    status_t result            = kStatus_Success;

    disEventsCmd.isRead   = false;
    disEventsCmd.cmdId    = I3C_BUS_CCC_DISEC((slaveAddr != I3C_BUS_BROADCAST_ADDR));
    disEventsCmd.destAddr = slaveAddr;
    disEventsCmd.data     = &busEvents;
    disEventsCmd.dataSize = 1U;

    result = I3C_BusMasterSendCCC(masterDev, &disEventsCmd);

    return result;
}

/*!
 * brief Bus master get device information for a specific I3C device.
 *
 * Bus master call this function to get device information for a specific I3C device.
 *
 * param masterDev Pointer to I3C master device.
 * param slaveAddr Slave address, dynamic assigned address for a device.
 * param devInfo Input pointer to structure i3c_device_information_t to store the read out device information.
 */
status_t I3C_BusMasterGetDeviceInfo(i3c_device_t *masterDev, uint8_t slaveAddr, i3c_device_information_t *devInfo)
{
    status_t result = kStatus_Success;

    devInfo->dynamicAddr = slaveAddr;

    result = I3C_BusMasterGetPID(masterDev, devInfo);
    if (result != kStatus_Success)
    {
        return result;
    }

    result = I3C_BusMasterGetBCR(masterDev, devInfo);
    if (result != kStatus_Success)
    {
        return result;
    }

    result = I3C_BusMasterGetDCR(masterDev, devInfo);
    if (result != kStatus_Success)
    {
        return result;
    }

    result = I3C_BusMasterGetMaxReadLength(masterDev, devInfo);
    if (result != kStatus_Success)
    {
        return result;
    }
    result = I3C_BusMasterGetMaxWriteLength(masterDev, devInfo);
    if (result != kStatus_Success)
    {
        return result;
    }

    if ((devInfo->bcr & I3C_BUS_DEV_BCR_MODE_MASK) != 0U)
    {
        result = I3C_BusMasterGetHDRCapability(masterDev, devInfo);
        if (result != kStatus_Success)
        {
            return result;
        }
    }

    return result;
}

status_t I3C_BusMasterRegisterDevIBI(i3c_device_t *masterDev, i3c_device_t *i3cDev, i3c_device_ibi_info_t *devIbiInfo)
{
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    i3c_device_control_info_t *masterControlInfo = masterDev->devControlInfo;
    i3cDev->ibiInfo                              = devIbiInfo;

    if (I3C_BusGetAddrSlotStatus(i3cBus, i3cDev->info.dynamicAddr) == (uint8_t)kI3C_Bus_AddrSlot_I3CDev)
    {
        masterControlInfo->funcs->RegisterIBI(masterDev, i3cDev->info.dynamicAddr);
    }
    else
    {
        return kStatus_I3CBus_InvalidOps;
    }

    return kStatus_Success;
}
/*!
 * brief Bus master handle in-band-interrupt event.
 *
 * Bus master call this function to handle in-band-interrupt event.
 *
 * param masterDev Pointer to I3C master device.
 * param ibiAddress slave address which requests the IBI.
 * param ibiData Pointer to the ibi data buffer.
 * param payloadSize ibi data buffer size.
 */
status_t I3C_BusMasterHandleIBI(i3c_device_t *masterDev, uint8_t ibiAddress, void *ibiData, uint32_t payloadSize)
{
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    list_element_handle_t listItem;
    list_handle_t i3cDevList = &(i3cBus->i3cDevList);
    i3c_device_t *i3cDev     = NULL;

    for (listItem = i3cDevList->head; listItem != NULL; listItem = listItem->next)
    {
        i3cDev = (i3c_device_t *)(void *)listItem;
        if (i3cDev == masterDev)
        {
            continue;
        }

        if (i3cDev->info.dynamicAddr == ibiAddress)
        {
            break;
        }
    }

    if (i3cDev != NULL)
    {
        assert(i3cDev->ibiInfo != NULL);
        assert(i3cDev->ibiInfo->maxPayloadLength >= payloadSize);
        assert(i3cDev->ibiInfo->ibiHandler != NULL);

        i3cDev->ibiInfo->ibiHandler(i3cDev, ibiData, payloadSize);
    }

    return kStatus_Success;
}

/*!
 * brief Bus master do data transfer to I2C device.
 *
 * Bus master call this function to transfer data to connected I2C device.
 *
 * param masterDev Pointer to I3C master device.
 * param i2cDev Pointer to the I2C device master do transfer.
 * param xfer Pointer to bus transfer.
 */
status_t I3C_BusMasterDoTransferToI2CDev(i3c_device_t *masterDev, i2c_device_t *i2cDev, i3c_bus_transfer_t *xfer)
{
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    status_t result                              = kStatus_Success;
    i3c_device_control_info_t *masterControlInfo = masterDev->devControlInfo;

    if (I3C_BusGetAddrSlotStatus(i3cBus, i2cDev->staticAddr) == (uint8_t)kI3C_Bus_AddrSlot_I2CDev)
    {
        result = masterControlInfo->funcs->DoI2CTransfer(i2cDev, xfer);
    }
    else
    {
        result = kStatus_I3CBus_InvalidOps;
    }

    return result;
}

/*!
 * brief Bus master do data transfer to I3C device.
 *
 * Bus master call this function to transfer data to connected I3C device.
 *
 * param masterDev Pointer to I3C master device.
 * param i3cDev Pointer to the I3C device master do transfer.
 * param xfer Pointer to bus transfer.
 */
status_t I3C_BusMasterDoTransferToI3CDev(i3c_device_t *masterDev, i3c_device_t *i3cDev, i3c_bus_transfer_t *xfer)
{
    i3c_bus_t *i3cBus = masterDev->bus;

    if (masterDev != i3cBus->currentMaster)
    {
        return kStatus_I3CBus_NotCurrentMaster;
    }

    status_t result                              = kStatus_Success;
    i3c_device_control_info_t *masterControlInfo = masterDev->devControlInfo;

    if (I3C_BusGetAddrSlotStatus(i3cBus, i3cDev->info.dynamicAddr) == (uint8_t)kI3C_Bus_AddrSlot_I3CDev)
    {
        result = masterControlInfo->funcs->DoI3CTransfer(i3cDev, xfer);
    }
    else
    {
        result = kStatus_I3CBus_InvalidOps;
    }

    return result;
}

/*!
 * brief Create I2C slave structure on bus.
 *
 * This function will create I2C device bus structure, initialize the slave according to bus characteristics, install
 * device information including static address, lvr and device control information.
 *
 * param i2cDev Pointer to device structure creating as I2C device.
 * param bus Pointer to bus structure.
 * param staticAddr Static address of the I2C device
 * param lvr legacy virtual register value of the device.
 * param devControlInfo Pointer to device control information structure.
 */
status_t I3C_BusI2CSlaveCreate(
    i2c_device_t *i2cDev, i3c_bus_t *bus, uint8_t staticAddr, uint8_t lvr, i2c_device_control_info_t *devControlInfo)
{
    (void)memset(i2cDev, 0, sizeof(*i2cDev));

    i2cDev->bus            = bus;
    i2cDev->staticAddr     = staticAddr;
    i2cDev->lvr            = lvr;
    i2cDev->devControlInfo = devControlInfo;

    i3c_device_t *masterDev = NULL;
    status_t result         = kStatus_Success;

    if (bus != NULL)
    {
        masterDev = (i3c_device_t *)bus->currentMaster;
    }

    if (masterDev != NULL)
    {
        /*Add device to bus i2c device list */
        I3C_BusAddI2CDev(bus, i2cDev);
    }

    if ((devControlInfo != NULL) && (devControlInfo->funcs != NULL) && (devControlInfo->funcs->Init != NULL))
    {
        result = devControlInfo->funcs->Init(i2cDev);
    }

    return result;
}

/*!
 * brief Create I3C slave structure on bus.
 *
 * This function will create I3C device bus structure, initialize the slave according to bus characteristics, install
 * device information and device control information.
 *
 * param i3cDev Pointer to device structure creating as I3C device.
 * param bus Pointer to bus structure.
 * param devInfo Pointer to device information structure
 * param devControlInfo Pointer to device control information structure.
 */
status_t I3C_BusI3CSlaveCreate(i3c_device_t *i3cDev,
                               i3c_bus_t *bus,
                               i3c_device_information_t *devInfo,
                               i3c_device_ibi_info_t *ibiInfo,
                               i3c_device_control_info_t *devControlInfo)
{
    (void)memset(i3cDev, 0, sizeof(*i3cDev));

    i3cDev->bus            = bus;
    i3cDev->ibiInfo        = ibiInfo;
    i3cDev->info           = *devInfo;
    i3cDev->devControlInfo = devControlInfo;
    status_t result        = kStatus_Success;

    i3c_device_t *masterDev = NULL;

    if (bus != NULL)
    {
        masterDev = (i3c_device_t *)bus->currentMaster;
    }

    if (masterDev != NULL)
    {
        /*Add device to bus i3c device list */
        I3C_BusAddI3CDev(bus, i3cDev);
    }

    if ((devControlInfo != NULL) && (devControlInfo->funcs != NULL) && (devControlInfo->funcs->Init != NULL))
    {
        result = devControlInfo->funcs->Init(i3cDev);
    }

    return result;
}

/*!
 * brief I3C slave device request Hot Join on bus.
 *
 * param i3cDev Pointer to device structure creating as I3C device.
 */
status_t I3C_BusSlaveRequestHotJoin(i3c_device_t *i3cDev)
{
    status_t result = kStatus_Success;

    if ((i3cDev->devControlInfo != NULL) && (i3cDev->devControlInfo->funcs != NULL) &&
        (i3cDev->devControlInfo->funcs->HotJoin != NULL))
    {
        i3cDev->devControlInfo->funcs->HotJoin(i3cDev);
    }
    else
    {
        result = kStatus_I3CBus_SlaveOpsNotSupported;
    }

    return result;
}

/*!
 * brief I3C slave device request mastership on bus.
 *
 * param i3cDev Pointer to device structure creating as I3C device.
 */
status_t I3C_BusSlaveRequestMasterShip(i3c_device_t *i3cDev)
{
    status_t result = kStatus_Success;

    if ((i3cDev->devControlInfo != NULL) && (i3cDev->devControlInfo->funcs != NULL) &&
        (i3cDev->devControlInfo->funcs->RequestMastership != NULL))
    {
        i3cDev->devControlInfo->funcs->RequestMastership(i3cDev);
    }
    else
    {
        result = kStatus_I3CBus_SlaveOpsNotSupported;
    }

    return result;
}

/*!
 * brief I3C slave request IBI events.
 *
 * param i3cDev Pointer to device structure creating as I3C device.
 * param data Pointer to data buffer to be sent in IBI event.
 * param dataSize data size of IBI data.
 */
status_t I3C_BusSlaveRequestIBI(i3c_device_t *i3cDev, void *data, size_t dataSize)
{
    status_t result = kStatus_Success;

    if ((i3cDev->devControlInfo != NULL) && (i3cDev->devControlInfo->funcs != NULL) &&
        (i3cDev->devControlInfo->funcs->RequestMastership != NULL))
    {
        i3cDev->devControlInfo->funcs->RequestIBI(i3cDev, data, dataSize);
    }
    else
    {
        result = kStatus_I3CBus_SlaveOpsNotSupported;
    }

    return result;
}
