/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "host_hardware.h"
#include "fsl_device_registers.h"
#include "port/fsl_port.h"
#include "flexcan/fsl_flexcan.h"
#include "dspi/fsl_dspi.h"
#include "uart/fsl_uart.h"
#include "i2c/fsl_i2c.h"
#include "utilities/fsl_rtos_abstraction.h"

#include "pin_mux.h"
#include "board.h"
#include "disk.h"

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

#define k_uartBufferSize 256
#define HOST_UART_INSTANCE 2

/*!
 * @brief Internal driver state information.
 *
 * @note The contents of this structure are internal to the driver and should not be
 *      modified by users. Also, contents of the structure are subject to change in
 *      future releases.
 */
typedef struct FlexCANState
{
    volatile uint32_t rx_mb_idx; /*!< Index of the message buffer for receiving*/
    volatile uint32_t tx_mb_idx; /*!< Index of the message buffer for transmitting*/
    semaphore_t txIrqSync;       /*!< Used to wait for ISR to complete its TX business.*/
    semaphore_t rxIrqSync;       /*!< Used to wait for ISR to complete its RX business.*/
} flexcan_state_t;

/*! @brief FlexCAN data info from user*/
typedef struct FlexCANDataInfo
{
    flexcan_frame_format_t msg_id_type; /*!< Type of message ID (standard or extended)*/
    uint32_t data_length;               /*!< Length of Data in Bytes*/
} flexcan_data_info_t;

/*! @brief FlexCAN operation modes*/
typedef enum _flexcan_operation_modes
{
    kFlexCanNormalMode,     /*!< Normal mode or user mode*/
    kFlexCanListenOnlyMode, /*!< Listen-only mode*/
    kFlexCanLoopBackMode,   /*!< Loop-back mode*/
    kFlexCanFreezeMode,     /*!< Freeze mode*/
    kFlexCanDisableMode,    /*!< Module disable mode*/
} flexcan_operation_modes_t;

/*!
 * @brief flexCAN common information structure
 */
typedef struct _flexcan_transfer_info
{
    flexcan_state_t state;       //!< state
    flexcan_data_info_t rx_info; //!< tx info
    flexcan_data_info_t tx_info; //!< tx info
    uint32_t rxId;               //!< rx id
    uint32_t txId;               //!< tx id
    uint32_t rx_mailbox_num;     //!< rx mb number
    uint32_t tx_mailbox_num;     //!< tx mb number
    uint8_t rx_buf[64];          //!< rx buffer
    uint8_t rx_buf_write_index;  //!< rx buffer write index
    uint8_t rx_buf_read_index;   //!< rx buffer read index
} flexcan_transfer_info_t;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*!
 * @brief uart byte receive callback.
 */
static void s_uart_byte_receive_callback(uint8_t byte);

/*!
 * @brief I2C transfer callback.
 */
static void i2c_master_callback(I2C_Type *base, i2c_master_handle_t *handle, status_t status, void *userData);

/*!
 * @brief Return specified UART clock, LPUART0 is not supported here.
 */
static uint32_t get_uart_clock(uint32_t instance);

/*! @brief init uart functions. */
static void init_suarts(void);

/*! @brief dspi initialization. */
static void init_dspi(void);

/*! @brief i2c initialization. */
static void init_i2c(uint32_t instance);

/*! @brief flexcan initialization. */
static void init_flexcan(void);

static void read_can_data(uint8_t data, uint32_t instance);

static status_t FLEXCAN_Send(uint8_t instance,
                             uint32_t mb_idx,
                             flexcan_data_info_t *tx_info,
                             uint32_t msg_id,
                             uint8_t *mb_data,
                             uint32_t timeout_ms);

//! @brief flexCAN reset buffer process
static void reset_can_buffer(void);
/*******************************************************************************
 * Variables
 ******************************************************************************/

/*! @brief Variable for DSPI setup information */
static dspi_master_config_t g_dspiConfig;
static dspi_transfer_t g_dspi_masterXfer;
const static uint32_t g_dspiBaseAddr[] = SPI_BASE_ADDRS;
dspi_master_handle_t g_dspi_masterHandle;

/*! @brief Variable for I2C setup information */
static i2c_master_config_t s_i2cMasterConfig;
static i2c_master_transfer_t s_i2c_masterXfer;
const static uint32_t g_i2cBaseAddr[] = I2C_BASE_ADDRS;
i2c_master_handle_t g_i2c_masterHandle = { 0 };
volatile bool g_I2C_MasterCompletionFlag = false;

/* Uart1 */
static uint8_t *s_uart_rxData;
static uint32_t s_uart_bytesRx;
/* Uart1 buffer */
static uint8_t s_uartBuffer[k_uartBufferSize];
static uint8_t *s_uartPtr = s_uartBuffer;
static uint32_t s_uartBufferOffset = 0;

/* Array of UART peripheral base address. */
static UART_Type *const s_uartBase[] = UART_BASE_PTRS;
/* Array of UART IRQ number. */
static const IRQn_Type s_uartIRQ[] = UART_RX_TX_IRQS;

/* FlexCan */
const static uint32_t g_flexcanBaseAddr[] = CAN_BASE_ADDRS;

/* Tables to save CAN IRQ enum numbers defined in CMSIS header file. */
const IRQn_Type g_flexcanRxWarningIrqId[] = CAN_Rx_Warning_IRQS;
const IRQn_Type g_flexcanTxWarningIrqId[] = CAN_Tx_Warning_IRQS;
const IRQn_Type g_flexcanWakeUpIrqId[] = CAN_Wake_Up_IRQS;
const IRQn_Type g_flexcanErrorIrqId[] = CAN_Error_IRQS;
const IRQn_Type g_flexcanBusOffIrqId[] = CAN_Bus_Off_IRQS;
const IRQn_Type g_flexcanOredMessageBufferIrqId[] = CAN_ORed_Message_buffer_IRQS;

/*!
 * @brief flexCAN timing table (120Mhz)contains propseg, pseg1, pseg2, pre_divider, rjw
 */
flexcan_timing_config_t bit_rate_table[] = {
    { 47, 1, 3, 2, 1 }, /* 125 kHz */
    { 23, 1, 3, 2, 1 }, /* 250 kHz */
    { 11, 1, 3, 2, 1 }, /* 500 kHz */
    { 7, 1, 3, 2, 1 },  /* 750 kHz */
    { 5, 1, 3, 2, 1 },  /* 1   MHz */
};

/*!
 * @brief flexCAN instance used for bus pal
 */
uint8_t flexcanInstance = 0;

//! @brief Global state for the FlexCAN peripheral interface.
flexcan_transfer_info_t s_flexcanInfo;
/*******************************************************************************
 * Code
 ******************************************************************************/

void hardware_init(void)
{
    uint32_t count = 0;
    uint32_t regPrimask = 0U;

    /* Disable the MPU otherwise USB cannot access the bus */
    MPU->CESR = 0;

    BOARD_InitPins();
    BOARD_InitLED();
    BOARD_InitSwitch();

    microseconds_init();
    init_suarts();
    init_dspi();
    init_flexcan();
    init_i2c(I2C_INSTANCE);

    /* Wait to hardware stable */
    microseconds_delay(500000);
    microseconds_shutdown();

    for (uint32_t i = 0; i < 100; i++)
    {
        if (BOARD_ReadSwitch(kSwitch3))
        {
            count++;
        }
    }
    if (count > 60)
    {
        disk_init();
        FLASH_Init(&s_flashInstance);

        /* Disable IRQ, protect ring buffer. */
        regPrimask = __get_PRIMASK();
        __disable_irq();

        /* 1M flash region to store flash image */
        FLASH_Erase(&s_flashInstance, MSD_FLASH_BASE, 1024 * 1024, kFLASH_ApiEraseKey);

        /* Recover PRIMASK, enable IRQ if previously enabled. */
        __set_PRIMASK(regPrimask);
    }

    PORT_SetPinInterruptConfig(BOARD_SW2_PORT, BOARD_SW2_GPIO_PIN, kPORT_InterruptFallingEdge);
    EnableIRQ(BOARD_SW2_IRQ);
    PORT_SetPinInterruptConfig(BOARD_SW3_PORT, BOARD_SW3_GPIO_PIN, kPORT_InterruptFallingEdge);
    EnableIRQ(BOARD_SW3_IRQ);
}

uint32_t get_bus_clock(void)
{
    uint32_t busClockDivider = ((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV2_MASK) >> SIM_CLKDIV1_OUTDIV2_SHIFT) + 1;
    return (SystemCoreClock / busClockDivider);
}

uint32_t get_fast_peripheral_clock(void)
{
    uint32_t busClockDivider = ((SIM->CLKDIV1 & SIM_CLKDIV1_OUTDIV2_MASK) >> SIM_CLKDIV1_OUTDIV2_SHIFT) + 1;
    return (SystemCoreClock / busClockDivider);
}

bool usb_clock_init(void)
{
    SIM->CLKDIV2 = (uint32_t)0x0UL; /* Update USB clock prescalers */
                                    /* Select MCGPLLCLK clock */
    SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
    SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL(0x01U));
    SIM->CLKDIV2 = (uint32_t)0x09UL; /* Update USB clock prescalers */
    /* Enable USB-OTG IP clocking */
    SIM->SCGC4 |= (SIM_SCGC4_USBOTG_MASK);

    /* Configure enable USB regulator for device */
    SIM->SOPT1 |= SIM_SOPT1_USBREGEN_MASK;
    /* SIM_SOPT1: OSC32KSEL=0 */
    SIM->SOPT1 &=
        (uint32_t)~SIM_SOPT1_OSC32KSEL_MASK; /* System oscillator drives 32 kHz clock for various peripherals */

    return true;
}

static void init_suarts(void)
{
    /* UART2 for OpenSDA and UART4 for Kibble */
    uart_config_t config;

    UART_GetDefaultConfig(&config);
    config.baudRate_Bps = kUART_DEFAULT_BAUD;
    config.enableTx = true;
    config.enableRx = true;

    UART_Init(s_uartBase[HOST_UART_INSTANCE], &config, get_uart_clock(HOST_UART_INSTANCE));

    EnableIRQ(s_uartIRQ[UART_INSTANCE]);
    UART_Init(s_uartBase[UART_INSTANCE], &config, get_uart_clock(UART_INSTANCE));
    UART_EnableInterrupts(s_uartBase[UART_INSTANCE], kUART_RxDataRegFullInterruptEnable);
}

static uint32_t get_uart_clock(uint32_t instance)
{
    switch (instance)
    {
        case 0:
        case 1:
            /* UART0 and UART1 always use the system clock */
            return SystemCoreClock;
        case 2:
            return get_bus_clock();
        case 3:
        case 4:
            /* UART2-4 always use the bus clock. */
            return get_bus_clock();
        default:
            return 0;
    }
}

static void init_dspi(void)
{
    uint32_t baseAddr = g_dspiBaseAddr[SPI_INSTANCE];

    DSPI_MasterGetDefaultConfig(&g_dspiConfig);
    g_dspiConfig.whichCtar = kDSPI_Ctar1;
    g_dspiConfig.ctarConfig.cpol = kDSPI_ClockPolarityActiveLow;
    g_dspiConfig.ctarConfig.cpha = kDSPI_ClockPhaseSecondEdge;
    g_dspiConfig.ctarConfig.baudRate = 100000U;

    DSPI_MasterInit((SPI_Type *)baseAddr, &g_dspiConfig, get_bus_clock());

    g_dspi_masterXfer.configFlags = kDSPI_MasterCtar1 | kDSPI_MasterPcs0 | kDSPI_MasterPcsContinuous;
}

static void init_i2c(uint32_t instance)
{
    I2C_MasterGetDefaultConfig(&s_i2cMasterConfig);

    I2C_MasterInit((I2C_Type *)g_i2cBaseAddr[instance], &s_i2cMasterConfig, get_bus_clock());
    I2C_MasterTransferCreateHandle((I2C_Type *)g_i2cBaseAddr[instance], &g_i2c_masterHandle, i2c_master_callback, NULL);

    s_i2c_masterXfer.slaveAddress = 0x10;
    s_i2c_masterXfer.subaddress = 0;
    s_i2c_masterXfer.subaddressSize = 0;
    s_i2c_masterXfer.flags = kI2C_TransferDefaultFlag;
}

static void init_flexcan(void)
{
    flexcan_config_t config;
    uint32_t baseAddr = g_flexcanBaseAddr[FLEXCAN_INSTANCE];

    FLEXCAN_GetDefaultConfig(&config);
    config.clkSrc = kFLEXCAN_ClkSrcPeri;
    config.baudRate = 125000;

    /* Select mailbox number */
    s_flexcanInfo.rx_mailbox_num = 8;
    s_flexcanInfo.tx_mailbox_num = 9;

    /* make bootloader as node 'b' for testing */
    s_flexcanInfo.rxId = 0x123;
    s_flexcanInfo.txId = 0x321;

    s_flexcanInfo.rx_buf_write_index = 0;
    s_flexcanInfo.rx_buf_read_index = 0;

    /* Init the interrupt sync object.*/
    OSA_SemaCreate(&s_flexcanInfo.state.txIrqSync, 0);
    OSA_SemaCreate(&s_flexcanInfo.state.rxIrqSync, 0);
    s_flexcanInfo.state.rx_mb_idx = 8;
    s_flexcanInfo.state.tx_mb_idx = 9;

    /* also need to get clock selection config data */
    FLEXCAN_Init((CAN_Type *)baseAddr, &config, get_bus_clock());

    FLEXCAN_Enable((CAN_Type *)baseAddr, true);

    FLEXCAN_EnableInterrupts((CAN_Type *)baseAddr, kFLEXCAN_ErrorInterruptEnable);

    /* using setting table */
    FLEXCAN_SetTimingConfig((CAN_Type *)baseAddr, &bit_rate_table[0]);

    /* FlexCAN reveive config */
    s_flexcanInfo.rx_info.msg_id_type = kFLEXCAN_FrameFormatStandard;
    s_flexcanInfo.rx_info.data_length = 8;

    /* Configure RX MB fields */
    flexcan_rx_mb_config_t mbConfig;
    mbConfig.format = kFLEXCAN_FrameFormatStandard;
    mbConfig.id = CAN_ID_STD(s_flexcanInfo.rxId);
    mbConfig.type = kFLEXCAN_FrameTypeData;
    FLEXCAN_SetRxMbConfig((CAN_Type *)baseAddr, 8, &mbConfig, true);

    FLEXCAN_EnableMbInterrupts((CAN_Type *)baseAddr, 1 << 8);

    /* FlexCAN transfer config */
    s_flexcanInfo.tx_info.msg_id_type = kFLEXCAN_FrameFormatStandard;
    s_flexcanInfo.tx_info.data_length = 8;

    FLEXCAN_SetTxMbConfig((CAN_Type *)baseAddr, 9, false);

    NVIC_EnableIRQ(g_flexcanErrorIrqId[FLEXCAN_INSTANCE]);
    NVIC_EnableIRQ(g_flexcanBusOffIrqId[FLEXCAN_INSTANCE]);
    NVIC_EnableIRQ(g_flexcanOredMessageBufferIrqId[FLEXCAN_INSTANCE]);
}

bool wait_uart_char(uint8_t *data)
{
    /* If has data */
    if (s_uartBase[HOST_UART_INSTANCE]->RCFIFO)
    {
        *data = UART_ReadByte(s_uartBase[HOST_UART_INSTANCE]);
        return true;
    }
    else
    {
        return false;
    }
}

uint8_t wait_uart_char_blocking(void)
{
    uint8_t byte = 0;

    UART_ReadBlocking(s_uartBase[HOST_UART_INSTANCE], &byte, 1);

    return byte;
}

void configure_spi_speed(uint32_t speedkhz)
{
    uint32_t bitsPerSec = speedkhz * 1000;
    DSPI_MasterSetBaudRate((SPI_Type *)g_dspiBaseAddr[SPI_INSTANCE], kDSPI_Ctar1, bitsPerSec, get_bus_clock());
}

status_t send_spi_data(uint8_t *src, uint32_t writeLength)
{
    g_dspi_masterXfer.txData = src;
    g_dspi_masterXfer.rxData = NULL;
    g_dspi_masterXfer.dataSize = writeLength;

    DSPI_MasterTransferBlocking((SPI_Type *)g_dspiBaseAddr[SPI_INSTANCE], &g_dspi_masterXfer);

    return kStatus_Success;
}

status_t receive_spi_data(uint8_t *dest, uint32_t readLength)
{
    g_dspi_masterXfer.txData = NULL;
    g_dspi_masterXfer.rxData = dest;
    g_dspi_masterXfer.dataSize = readLength;

    DSPI_MasterTransferBlocking((SPI_Type *)g_dspiBaseAddr[SPI_INSTANCE], &g_dspi_masterXfer);

    return kStatus_Success;
}

void configure_i2c_speed(uint32_t speedkhz)
{
    I2C_MasterSetBaudRate((I2C_Type *)g_i2cBaseAddr[I2C_INSTANCE], speedkhz * 1000, get_bus_clock());
}

status_t send_i2c_data(uint8_t *src, uint32_t writeLength)
{
    s_i2c_masterXfer.direction = kI2C_Write;
    s_i2c_masterXfer.data = src;
    s_i2c_masterXfer.dataSize = writeLength;

    I2C_MasterTransferBlocking((I2C_Type *)g_i2cBaseAddr[I2C_INSTANCE], &s_i2c_masterXfer);

    return kStatus_Success;
}

static void i2c_master_callback(I2C_Type *base, i2c_master_handle_t *handle, status_t status, void *userData)
{
    /* Signal transfer success when received success status. */
    if (status == kStatus_Success)
    {
        g_I2C_MasterCompletionFlag = true;
    }
}

status_t receive_i2c_data(uint8_t *dest, uint32_t readLength)
{
    uint16_t timeout = UINT16_MAX;

    s_i2c_masterXfer.direction = kI2C_Read;
    s_i2c_masterXfer.data = dest;
    s_i2c_masterXfer.dataSize = readLength;

    I2C_MasterTransferNonBlocking((I2C_Type *)g_i2cBaseAddr[I2C_INSTANCE], &g_i2c_masterHandle, &s_i2c_masterXfer);

    /*  Reset master completion flag to false. */
    g_I2C_MasterCompletionFlag = false;

    /*  Wait for transfer completed. */
    while ((!g_I2C_MasterCompletionFlag) && (--timeout))
    {
    }
    if (timeout == 0)
    {
        return kStatus_Timeout;
    }

    g_I2C_MasterCompletionFlag = false;

    return kStatus_Success;
}

void configure_can_speed(uint32_t speedkhz)
{
    /* Just support 125K, 250K, 500K, 750K, and 1000K */
    uint32_t speed_index = 0;

    switch (speedkhz)
    {
        case kFLEXCAN_125K:
            speed_index = 0;
            break;
        case kFLEXCAN_250K:
            speed_index = 1;
            break;
        case kFLEXCAN_500K:
            speed_index = 2;
            break;
        case kFLEXCAN_750K:
            speed_index = 3;
            break;
        case kFLEXCAN_1000K:
            speed_index = 4;
            break;
    }

    if (speed_index < 5)
    {
        /* using setting table */
        FLEXCAN_SetTimingConfig((CAN_Type *)g_flexcanBaseAddr[FLEXCAN_INSTANCE], &bit_rate_table[speed_index]);
    }
}

status_t send_can_data(uint8_t *src, uint32_t writeLength)
{
    uint32_t sentCnt = 0;
    uint8_t *sendPtr = src;

    while (sentCnt < writeLength)
    {
        if ((writeLength - sentCnt) <= 8)
        {
            /* number of bytes to be sent */
            s_flexcanInfo.tx_info.data_length = writeLength - sentCnt;
            sentCnt += writeLength - sentCnt;
        }
        else
        {
            /* number of bytes to be sent */
            s_flexcanInfo.tx_info.data_length = 8;
            sentCnt += 8;
        }

        FLEXCAN_Send(FLEXCAN_INSTANCE, 9, &s_flexcanInfo.tx_info, s_flexcanInfo.txId, (uint8_t *)sendPtr, 1000);

        sendPtr += s_flexcanInfo.tx_info.data_length;
    }

    reset_can_buffer();

    return kStatus_Success;
}

status_t FLEXCAN_Send(uint8_t instance,
                      uint32_t mb_idx,
                      flexcan_data_info_t *tx_info,
                      uint32_t msg_id,
                      uint8_t *mb_data,
                      uint32_t timeout_ms)
{
    uint32_t baseAddr = g_flexcanBaseAddr[instance];
    osa_status_t syncStatus;
    uint8_t i;
    flexcan_frame_t frame;
    flexcan_mb_transfer_t xfer;
    xfer.mbIdx = mb_idx;
    frame.format = tx_info->msg_id_type;
    frame.length = tx_info->data_length;
    frame.id = CAN_ID_STD(msg_id);
    frame.type = kFLEXCAN_FrameTypeData;
    xfer.frame = &frame;

    /* Copy user's buffer into the message buffer data area*/
    if (mb_data != NULL)
    {
        xfer.frame->dataWord0 = 0x0;
        xfer.frame->dataWord1 = 0x0;

        for (i = 0; i < tx_info->data_length; i++)
        {
            uint32_t temp, temp1;
            temp1 = (*(mb_data + i));
            if (i < 4)
            {
                temp = temp1 << ((3 - i) * 8);
                xfer.frame->dataWord0 |= temp;
            }
            else
            {
                temp = temp1 << ((7 - i) * 8);
                xfer.frame->dataWord1 |= temp;
            }
        }
    }

    if (kStatus_Success == FLEXCAN_WriteTxMb((CAN_Type *)baseAddr, mb_idx, xfer.frame))
    {
        /* Enable Message Buffer Interrupt. */
        FLEXCAN_EnableMbInterrupts((CAN_Type *)baseAddr, 1 << mb_idx);

        do
        {
            syncStatus = OSA_SemaWait(&s_flexcanInfo.state.txIrqSync, timeout_ms);
        } while (syncStatus == kStatus_OSA_Idle);

        /* Disable message buffer interrupt*/
        FLEXCAN_DisableMbInterrupts((CAN_Type *)baseAddr, 1 << mb_idx);

        /* Wait for the interrupt*/
        if (syncStatus != kStatus_OSA_Success)
        {
            return kStatus_Timeout;
        }
        else
        {
            return kStatus_Success;
        }
    }
    else
    {
        return kStatus_Fail;
    }
}

void FLEXCAN_IRQHandler(uint8_t instance)
{
    volatile uint32_t flag_reg;
    uint32_t temp;
    CAN_Type *baseAddr = (CAN_Type *)g_flexcanBaseAddr[instance];

    /* Get the interrupts that are enabled and ready */
    flag_reg = (baseAddr->IFLAG1 & baseAddr->IMASK1);

    /* Check Tx/Rx interrupt flag and clear the interrupt */
    if (flag_reg)
    {
        temp = (1 << s_flexcanInfo.state.rx_mb_idx);
        if (flag_reg & temp)
        {
            OSA_SemaPost(&s_flexcanInfo.state.rxIrqSync);

            flexcan_frame_t rxFrame;
            /* Get RX MB field values*/
            if (!FLEXCAN_ReadRxMb(baseAddr, s_flexcanInfo.state.rx_mb_idx, &rxFrame))
            {
                uint8_t i;
                uint8_t sink_byte = 0;
                for (i = 0; i < rxFrame.length; i++)
                {
                    switch (i)
                    {
                        case 0:
                            sink_byte = rxFrame.dataByte0;
                            break;
                        case 1:
                            sink_byte = rxFrame.dataByte1;
                            break;
                        case 2:
                            sink_byte = rxFrame.dataByte2;
                            break;
                        case 3:
                            sink_byte = rxFrame.dataByte3;
                            break;
                        case 4:
                            sink_byte = rxFrame.dataByte4;
                            break;
                        case 5:
                            sink_byte = rxFrame.dataByte5;
                            break;
                        case 6:
                            sink_byte = rxFrame.dataByte6;
                            break;
                        case 7:
                            sink_byte = rxFrame.dataByte7;
                            break;
                        default:
                            break;
                    }
                    read_can_data(sink_byte, instance);
                }
            }
        }

        temp = (1 << s_flexcanInfo.state.tx_mb_idx);
        if (flag_reg & temp)
        {
            OSA_SemaPost(&s_flexcanInfo.state.txIrqSync);
        }

        baseAddr->IFLAG1 = flag_reg;
    }

    /* Clear all other interrupts in ERRSTAT register (Error, Busoff, Wakeup) */
    FLEXCAN_ClearStatusFlags(baseAddr, kFLEXCAN_ErrorFlag | CAN_ESR1_ERRINT_MASK);

    return;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLEXCAN_IRQBusoffHandler
 * Description   : Busoff interrupt handler
 *
 *END**************************************************************************/
void FLEXCAN_IRQBusoffHandler(uint8_t instance)
{
    uint32_t baseAddr = g_flexcanBaseAddr[instance];
    FLEXCAN_ClearStatusFlags((CAN_Type *)baseAddr, kFLEXCAN_ErrorFlag | CAN_ESR1_ERRINT_MASK);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLEXCAN_DRV_IRQ_Error_Handler
 * Description   : flexCAN general error interrupt handler
 *
 *END**************************************************************************/
void FLEXCAN_IRQErrorHandler(uint8_t instance)
{
    uint32_t baseAddr = g_flexcanBaseAddr[instance];
    FLEXCAN_ClearStatusFlags((CAN_Type *)baseAddr, kFLEXCAN_ErrorFlag | CAN_ESR1_ERRINT_MASK);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : receive_can_data
 * Description   : flexCAN receiving data process
 *
 *END**************************************************************************/
static void read_can_data(uint8_t data, uint32_t instance)
{
    s_flexcanInfo.rx_buf[s_flexcanInfo.rx_buf_write_index++] = data;
    s_flexcanInfo.rx_buf_write_index &= 0x3f;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : reset_can_buffer
 * Description   : flexCAN reset buffer process
 *
 *END**************************************************************************/
static void reset_can_buffer(void)
{
    s_flexcanInfo.rx_buf_write_index = 0;
    s_flexcanInfo.rx_buf_read_index = 0;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : read_can_data
 * Description   : flexCAN read data process
 *
 *END**************************************************************************/
status_t receive_can_data(uint8_t *dest, uint32_t readLength)
{
    uint8_t received_cnt = 0;
    uint16_t timeout = UINT16_MAX;

    while ((received_cnt < readLength) && (--timeout))
    {
        if (s_flexcanInfo.rx_buf_read_index != s_flexcanInfo.rx_buf_write_index)
        {
            dest[received_cnt++] = s_flexcanInfo.rx_buf[s_flexcanInfo.rx_buf_read_index++];
            s_flexcanInfo.rx_buf_read_index &= 0x3f;
        }
    }

    if (timeout == 0)
    {
        init_flexcan();
        return kStatus_Timeout;
    }

    return kStatus_Success;
}

void configure_uart_speed(uint32_t baud)
{
    uart_config_t config;

    UART_GetDefaultConfig(&config);
    config.baudRate_Bps = baud;
    config.enableTx = true;
    config.enableRx = true;

    UART_Init(s_uartBase[UART_INSTANCE], &config, get_bus_clock());
}

status_t send_uart_data(uint8_t *src, uint32_t writeLength)
{
    UART_WriteBlocking(s_uartBase[UART_INSTANCE], src, writeLength);

    return kStatus_Success;
}

status_t receive_uart_data(uint8_t *dest, uint32_t readLength)
{
    uint16_t timeout = UINT16_MAX;

    while ((((s_uart_bytesRx + k_uartBufferSize - s_uartBufferOffset) % k_uartBufferSize) < readLength) && (--timeout))
    {
    }

    if (timeout == 0)
    {
        return kStatus_Timeout;
    }

    for (uint32_t i = 0; i < readLength; i++)
    {
        dest[i] = s_uartPtr[(s_uartBufferOffset + i) % k_uartBufferSize];
    }
    s_uartBufferOffset = (s_uartBufferOffset + sizeof(uint8_t) * readLength) % k_uartBufferSize;

    return kStatus_Success;
}

void UART4_RX_TX_IRQHandler(void)
{
    if (UART4->S1 & UART_S1_RDRF_MASK)
    {
        s_uart_byte_receive_callback(UART4->D);
    }
}

static void s_uart_byte_receive_callback(uint8_t byte)
{
    s_uart_rxData = s_uartPtr;
    if (s_uart_rxData)
    {
        s_uart_rxData[s_uart_bytesRx] = byte;
        s_uart_bytesRx = (s_uart_bytesRx + 1) % k_uartBufferSize;
    }
    s_uart_rxData = 0;
}

void CAN0_ORed_Message_buffer_IRQHandler(void)
{
    FLEXCAN_IRQHandler(0);
}

/* Implementation of CAN0 handler named in startup code. */
void CAN0_Bus_Off_IRQHandler(void)
{
    FLEXCAN_IRQBusoffHandler(0);
}

/* Implementation of CAN0 handler named in startup code. */
void CAN0_Error_IRQHandler(void)
{
    FLEXCAN_IRQErrorHandler(0);
}

void CAN0_Wake_Up_IRQHandler(void)
{
    FLEXCAN_IRQHandler(0);
}

#if __ICCARM__

size_t __write(int handle, const unsigned char *buf, size_t size)
{
    while (size--)
    {
        UART_WriteBlocking(s_uartBase[HOST_UART_INSTANCE], (uint8_t const *)buf++, 1);
    }
    return size;
}

#endif /* __ICCARM__ */
