/**
 * @file adcs_comm_interface.c
 *
 * @brief This file contains communication interface abstracted ADCS functions
 *        Currently the abstraction supports only CAN interface
 *
 * @copyright Copyright 2023 Antaris, Inc.
 */
 /*********************************************************************/

#include "adcs_comm_interface.h"
#include "exo_hal_common.h"
#include "exo_hal_io_al_common.h"
#include "exo_io_al_can_common.h"
#include "exo_io_al_uart_common.h"
#include "stm32f7xx_hal_ext.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "adcs_bootloader.h"
#include "adcs_common.h"
#include "adcs_cubeacp.h"
#include "exo_common.h"
#include "exo_osal.h"
#include "adcs_bsk_sim.h"
#include "adcs_vapi_common.h"
#include "nvm.h"
#include "err_handler.h"

#define CAN_TX_MIN_WAIT_TIME_ON_FULL (50) ///< Delay time CAN TX ON FULL

#define ADCS_DEBUG_ENB // Disable ADCS power check

/// ADCS CAN structure
typedef struct adcs_can_struct
{
    iohal_can_rx_header adcs_rxHeader; /*!< CAN Received Header */
    uint8 adcs_rxData[8];              /*!< ADCS CAN Received data */
}adcs_can_data_s;

/// ADCS IPC related types
adcs_can_data_s adcs_can_data_struct;
os_queue_handle_ptr adcs_can_queue;
os_queue_handle_ptr adcs_uart_queue;
os_itc_msg_handle_t adcs_file_tm;
os_sem_handle_ptr adcs_tlm;
os_sem_handle_ptr adcs_sync_sem;
ahw_al_gen_info adcs_al_uart;

/*
 * UART interface selection based on the board
 */
#ifdef COREBOARD
extern ioal_uart_hdle ioal_huart5;
#define ADCS_UART_HDL ioal_huart5
#else
extern ioal_uart_hdle ioal_huart6;
#define ADCS_UART_HDL ioal_huart6
#endif

/*
 * CAN interface selection based on the board
 */
#ifdef COREBOARD
extern ioal_can_hdle ioal_hcan2;
#define ADCS_CAN_HDL ioal_hcan2
#else
extern ioal_can_hdle ioal_hcan3;
#define ADCS_CAN_HDL ioal_hcan3
#endif

/// CAN interface related global variables
iohal_can_tx_header adcs_txHeader;
iohal_can_rx_header adcs_rxHeader;
uint32_t adcs_txMailbox;
uint8 adcs_arr_can[512] = {0};
/// ADCS file download global parameters
uint16 adcs_global_fsize;
uint16 adcs_global_tlm_blksize;
// 20kb file data + 7 kb header 1 Kb Error handling
uint8 adcs_file_dwnld_buffer[28*1024]PLACE_IN_EXT_RAM_MEM = {0};

/**
 * @brief Global variables for Last communicated Radio Id
 */
extern uint8 adcs_radio_id;

// ADCS Primary interface selector
e_intrfc_typ adcs_primary_interface = ADCS_UART;

// ADCS Interface error counter
uint8 adcs_interface_err = 0;

/**
 * @brief This API verifies and add 0x1f(ESC character) if any ESC character
 * present in data_buffer. This verification based on ADCS firmware
 * reference manual.
 */
static uint8* adcs_esc_char_vrfy(uint8 *data_buffer,uint16 *len);

/**
 * @brief Function ADCS UART interrupt callback
 */
void adcs_uart_rx_cmplt_cb(void *args);

/**
 * @brief Function ADCS UART abort interrupt callback
 */
void adcs_uart_rx_abort_cmplt_cb(void *args);

/**
 * @brief Function to send telecommand to ADCS
 */
int8 adcs_send_cmd(uint8 tc_id, uint8 *msgBuffer, uint16 len)
{
    e_adcs_status status = OPERATION_FAILURE;

    if(PWR_ON == sat_dev_sts[HW_ADCS].hw_sts)
    {
        if(ral_success == os_sem_take(adcs_sync_sem, 5000))
        {
#ifndef ADCS_HW_SIM
            if(adcs_primary_interface == ADCS_UART)
            {
#if !defined(STM32F769xx) || defined(ADCS_UART6_ENB)
                status = adcs_sendtc_uart(tc_id, msgBuffer, len);
#endif
            }
            else
            {
                status = adcs_sendtc_can(tc_id, msgBuffer, len);
            }
#else
            status = adcs_sendtc_bsk(tc_id, msgBuffer, len);
#endif

            if(OPERATION_SUCCESS == status)
            {
                adcs_interface_err = 0;
            }
            else if(IO_INTERFACE_ERR == status)
            {
                // Increment error counter
                adcs_interface_err++;
                err_detected(ERR_GROUP_ADCS, ERR_IO_INTERFACE_ERR);
            }
            else if(OPERATION_FAILURE == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_OPERATION_FAILURE);
            }
            else if(TC_TM_ERROR == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_TC_TM_ERROR);
            }
            else if(INVLD_PWR_STATE == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_INVLD_PWR_STATE);
            }
            os_sem_give(adcs_sync_sem);
        }
        else
        {
            status = ADCS_SEM_TAKE_FAIL;
        }
    }
    else
    {
        status = INVLD_PWR_STATE;
    }
    return status;
}

/**
 * @brief Function to request Telemetry from ADCS
 */
int8 adcs_requesttlm(uint8 tlmId, uint8 *msgBuffer, uint16 len)
{
    e_adcs_status status = OPERATION_FAILURE;
    if(PWR_ON == sat_dev_sts[HW_ADCS].hw_sts)
    {
        if(ral_success == os_sem_take(adcs_sync_sem, 5000))
        {
#ifndef ADCS_HW_SIM
            if(adcs_primary_interface == ADCS_UART)
            {
#if !defined(STM32F769xx) || defined(ADCS_UART6_ENB)
                status = adcs_requesttlm_uart(tlmId, msgBuffer, len);
#endif
            }
            else
            {
                status = adcs_requesttlm_can(tlmId, msgBuffer, len);
            }
#else
            status = adcs_requesttlm_bsk(tlmId, msgBuffer, len);
#endif

            if(OPERATION_SUCCESS == status)
            {
                adcs_interface_err = 0;
            }
            else if(IO_INTERFACE_ERR == status)
            {
                // Increment error counter
                adcs_interface_err++;
                err_detected(ERR_GROUP_ADCS, ERR_IO_INTERFACE_ERR);
            }
            else if(OPERATION_FAILURE == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_OPERATION_FAILURE);
            }
            else if(TC_TM_ERROR == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_TC_TM_ERROR);
            }
            else if(INVLD_PWR_STATE == status)
            {
                err_detected(ERR_GROUP_ADCS, ERR_INVLD_PWR_STATE);
            }
            os_sem_give(adcs_sync_sem);
        }
        else
        {
            status = ADCS_SEM_TAKE_FAIL;
        }
    }
    else
    {
        status = INVLD_PWR_STATE;
    }
    return status;
}

/**
 * @brief Function to request download burst from ADCS
 */
int8 adcs_downldburst(uint8 *msgBuffer, uint8 *rx_buffer, uint16 tx_size,
                          uint16 *rx_size)
{
    int8 status = OPERATION_FAILURE;
    if(adcs_primary_interface == ADCS_UART)
    {
#if !defined(STM32F769xx) || defined(ADCS_UART6_ENB)
        status = adcs_downldburst_uart(msgBuffer, rx_buffer, tx_size, rx_size);
#endif
    }
    else
    {
        status = adcs_downldburst_can(msgBuffer, rx_buffer, tx_size, rx_size);
    }
    return status;
}

/**
 * @brief Function to request Telemetry from ADCS through CAN interface
 */
int8 adcs_requesttlm_can(uint8 tlmId, uint8 *msgBuffer, uint16 len)
{
    int8 status = -1;
    uint8 request = 1;
    uint16 idx = 0;
    uint8 packet = 0;
    uint8 last_packet = 0;
    uint16 pac_len = 7;

    io_hal_can_start(&ADCS_CAN_HDL);
    adcs_txHeader.DLC = 0;
    adcs_txHeader.ExtId = (0x0400ff01 | (0xff0000 & (tlmId << 16)));
    adcs_txHeader.IDE = CAN_ID_EXT;
    adcs_txHeader.RTR = CAN_RTR_DATA;
    adcs_txHeader.TransmitGlobalTime = DISABLE;

    io_hal_can_activate_notification(&ADCS_CAN_HDL,
                                     CAN_IT_RX_FIFO0_MSG_PENDING);
    if(io_hal_can_add_tx_message(&ADCS_CAN_HDL, &adcs_txHeader, NULL,
                                 &adcs_txMailbox) != HAL_SCS)
    {
        /*CAN error*/
    }
    else
    {
        while(io_hal_can_get_tx_mailboxes_free_level(&ADCS_CAN_HDL) != 3)
        {
            os_delay(CAN_TX_MIN_WAIT_TIME_ON_FULL);
        }

        while(request == 1)
        {
            /// TBD:Check whether this can be removed
            if(io_hal_can_get_rx_fifo_fill_level(&ADCS_CAN_HDL, \
                    CAN_RX_FIFO0) != 1)
            {
                /* no data in fifo  */
            }
            os_queue_receive(adcs_can_queue, &adcs_can_data_struct, 5000);
            status = ((adcs_can_data_struct.adcs_rxHeader.ExtId >> 24) & 0x1f);
            if(status == 0x5)
            {
                /* Normal CAN request */
                status = 0;
                request = 0;
                os_memcpy(msgBuffer, adcs_can_data_struct.adcs_rxData, len);
            }
            else if(status == 0x8)
            {
                /// Extended Telemetry Request
                packet++;
                os_memcpy(&msgBuffer[idx], adcs_can_data_struct.adcs_rxData,
                          pac_len);
                idx = idx + 7;

                if(last_packet)
                {
                    // Last packet received
                    request = 0;
                    status = 0;
                }
                else
                {
                    /*
                     * Iterate until the last packet is received
                     */
                }

                if(adcs_can_data_struct.adcs_rxData[7] == 1)
                {
                    // Set next packet as the last packet
                    pac_len = len - (packet * 7);
                    last_packet = 1;
                }
                else
                {
                    /*
                     * Iterate until the last packet is received
                     */
                }
            }
            else
            {
                /*
                 * Unexpected response
                 * Set request =0 and status as -1 and return
                 */
                request = 0;
                status = -1;
            }
            HAL_Delay(1);
        }
        HAL_Delay(1);
    }

    return status;
}

/**
 * @brief Function to send telecommand to ADCS through CAN interface
 */
int8 adcs_sendtc_can(uint8 tcId, uint8 *msgBuffer, uint16 len)
{
    int8 status = -1;
    uint8 send = 1;
    uint16 idx = 0;
    uint16 itr = 0;
    uint16 packets = 0;
    uint16 counter = 0;
    uint16 pack_len = 0;

    io_hal_can_start(&ADCS_CAN_HDL);
    memset(adcs_arr_can, 0, 512);
    io_hal_can_activate_notification(&ADCS_CAN_HDL,
                                     CAN_IT_RX_FIFO0_MSG_PENDING);
    if(len > 8)
    {
        /* extended TC request */
        adcs_txHeader.DLC = 8;
        adcs_txHeader.ExtId = (0x0700ff01 | (0xff0000 & (tcId << 16)));
        adcs_txHeader.IDE = CAN_ID_EXT;
        adcs_txHeader.RTR = CAN_RTR_DATA;
        adcs_txHeader.TransmitGlobalTime = DISABLE;
        if(len % 7 != 0)
        {
            packets = (len / 7) + 1;
        }
        else
        {
            packets = len / 7;
        }
        counter = packets;
        while(send)
        {
            counter = counter - 1;
            if(counter == 0)
            {
                pack_len = len - itr;
                adcs_txHeader.DLC = pack_len + 1;
                os_memcpy(&adcs_arr_can[idx], &msgBuffer[itr], pack_len);
                adcs_arr_can[pack_len] = counter;
                send = 0;
            }
            else
            {
                os_memcpy(&adcs_arr_can[idx], &msgBuffer[itr], 7);
                adcs_arr_can[idx + 7] = counter;
            }

            if(io_hal_can_add_tx_message(&ADCS_CAN_HDL, &adcs_txHeader,
                                         &adcs_arr_can[idx],
                                         &adcs_txMailbox) != HAL_SCS)
            {
                /*CAN error*/
            }
            else
            {
                while(io_hal_can_get_tx_mailboxes_free_level(&ADCS_CAN_HDL) !=
                      3)
                {
                    os_delay(CAN_TX_MIN_WAIT_TIME_ON_FULL);
                }

                idx = idx + 8;
                itr = itr + 7;

                HAL_Delay(1);
            }
        }
        if(io_hal_can_get_rx_fifo_fill_level(&ADCS_CAN_HDL, CAN_RX_FIFO0) != 1)
        {
            /* no data in fifo */
        }
        os_queue_receive(adcs_can_queue, &adcs_can_data_struct, 5000);
        status = ((adcs_can_data_struct.adcs_rxHeader.ExtId >> 24) & 0x1f);
        if(status == 0x02)
        {
            status = 0;
        }
    }
    else
    {
        adcs_txHeader.DLC = len;
        adcs_txHeader.ExtId = (0x0100ff01 | (0xff0000 & (tcId << 16)));
        adcs_txHeader.IDE = CAN_ID_EXT;
        adcs_txHeader.RTR = CAN_RTR_DATA;
        adcs_txHeader.TransmitGlobalTime = DISABLE;

        if(io_hal_can_add_tx_message(&ADCS_CAN_HDL, &adcs_txHeader, msgBuffer,
                                     &adcs_txMailbox) != HAL_SCS)
        {
            /*CAN error*/
        }
        while(io_hal_can_get_tx_mailboxes_free_level(&ADCS_CAN_HDL) != 3)
        {
            os_delay(CAN_TX_MIN_WAIT_TIME_ON_FULL);
        }
        if(io_hal_can_get_rx_fifo_fill_level(&ADCS_CAN_HDL, CAN_RX_FIFO0) != 1)
        {
            /* no data in fifo */
        }

        os_queue_receive(adcs_can_queue, &adcs_can_data_struct, 5000);
        status = ((adcs_can_data_struct.adcs_rxHeader.ExtId >> 24) & 0x1f);
        if(status == 0x02)
        {
            status = 0;
            // This need to be removed refer section 4.4.2 of commissioning manual
            //os_memcpy(msgBuffer, adcs_can_data_struct.adcs_rxData, len);
        }
        else
        {
            /*
             * Unexpected response
             * Set status as -1 and return
             */
            status = -1;
        }
    }

    return status;
}

/*
 * @brief This API downloads data in burst through CAN interface
 */
int8 adcs_downldburst_can(uint8 *msgBuffer, uint8 *rx_buffer, uint16 tx_size,
                          uint16 *rx_size)
{
    int8 status = -1;
    uint8 request = 1;
    uint16 b_idx = 0;
    uint16 pac_len = 7;
    ral_status_t q_sts;
    uint8 *blk_ptr = NULL;
    uint8 *adcs_file_tm_ptr = NULL;
    uint16 tlm_blksize;
    uint16 file_size = 0;
    uint16 seq_no = 0;
    uint16 pac_nums = 0;
    uint16 pkt_no = 0;
    uint8 last_burst = 0;
    uint8 tlm_sent = 0;
    io_hal_can_start(&ADCS_CAN_HDL);

    adcs_txHeader.DLC = tx_size;
    adcs_txHeader.ExtId = (0x0100ff01 | (0xff0000 & (119 << 16)));
    adcs_txHeader.IDE = CAN_ID_EXT;
    adcs_txHeader.RTR = CAN_RTR_DATA;
    adcs_txHeader.TransmitGlobalTime = DISABLE;

    io_hal_can_activate_notification(&ADCS_CAN_HDL,
                                     CAN_IT_RX_FIFO0_MSG_PENDING);
    if(io_hal_can_add_tx_message(&ADCS_CAN_HDL, &adcs_txHeader, &msgBuffer[1],
                                 &adcs_txMailbox) != HAL_SCS)
    {
        // CAN error
    }
    else
    {
        while(io_hal_can_get_tx_mailboxes_free_level(&ADCS_CAN_HDL) != 3)
        {
            os_delay(CAN_TX_MIN_WAIT_TIME_ON_FULL);
        }

        file_size = adcs_global_fsize;

        tlm_blksize = adcs_global_tlm_blksize;
        pac_nums = (file_size / 22) * 4;
        if((file_size % 22) != 0)
        {
            pac_nums = pac_nums + 4;
        }

        blk_ptr = os_malloc_eram(tlm_blksize);
        if(NULL == blk_ptr)
        {
            request = 0;
            status = -2;
        }
        while(request == 1)
        {
            if(io_hal_can_get_rx_fifo_fill_level(&ADCS_CAN_HDL, \
                    CAN_RX_FIFO0) != 1)
            {
                // No data in FIFO
            }
            q_sts =
                os_queue_receive(adcs_can_queue, &adcs_can_data_struct, 5000);
            if(ral_success == q_sts)
            {
                status =
                    ((adcs_can_data_struct.adcs_rxHeader.ExtId >> 24) & 0x1f);
                if(status == 0x8)
                {
                    os_memcpy(&blk_ptr[b_idx],
                            adcs_can_data_struct.adcs_rxData,pac_len);
                    pkt_no++;
                    b_idx = b_idx + pac_len;
                    tlm_sent = 0;
                    if((b_idx >= tlm_blksize) || (last_burst == 1))
                    {
                        // 3 bytes are added for seq_no(2) + tm_ctrl(1)
                        adcs_file_tm_ptr = os_malloc_eram(tlm_blksize + 4);
                        os_memcpy(adcs_file_tm_ptr, &seq_no, 2);
                        // 0x70 represents enable encryption, authentication
                        // and message type as response
                        adcs_file_tm_ptr[2] = 0x70;
                        adcs_file_tm_ptr[3] = adcs_radio_id;
                        os_memcpy(&adcs_file_tm_ptr[4], blk_ptr, tlm_blksize);
                        adcs_file_tm.src_entity = 130;
                        adcs_file_tm.pld.pld_ptr = adcs_file_tm_ptr;
                        adcs_file_tm.Msg_len = tlm_blksize;
                        adcs_file_tm.Msg_id = 119;
                        os_itc_msg_send(&adcs_file_tm, TM_STORAGE,
                                        os_send_itc_wait);
                        b_idx = 0;
                        seq_no++;
                        os_memset(blk_ptr, 0, tlm_blksize);
                        last_burst = 0;
                        tlm_sent = 1;
                    }
                }
                else
                {
                    /*
                     * Unexpected response
                     * Set request =0 and status as -1 and return
                     */
                    request = 0;
                    status = -1;
                }
            }
            else
            {
                request = 0;
                if(tlm_sent == 0)
                {
                    // 3 bytes are added for seq_no(2) + tm_ctrl(1)
                    adcs_file_tm_ptr = os_malloc_eram(tlm_blksize + 4);
                    os_memcpy(adcs_file_tm_ptr, &seq_no, 2);
                    // 0x70 represents enable encryption, authentication
                    // and message type as response
                    adcs_file_tm_ptr[2] = 0x70;
                    adcs_file_tm_ptr[3] = 0x01;
                    os_memcpy(&adcs_file_tm_ptr[4], blk_ptr, tlm_blksize);
                    adcs_file_tm.src_entity = 130;
                    adcs_file_tm.pld.pld_ptr = adcs_file_tm_ptr;
                    adcs_file_tm.Msg_len = tlm_blksize;
                    adcs_file_tm.Msg_id = 119;
                    os_itc_msg_send(&adcs_file_tm, TM_STORAGE,
                            os_send_itc_wait);
                    status = 0;
                }
            }
            os_delay(CAN_TX_MIN_WAIT_TIME_ON_FULL);
            HAL_Delay(1);
        }
        HAL_Delay(1);
    }
    *rx_size = 1;
    return status;
}

/**
 * @brief Function to send telecommand to ADCS through UART interface
 */
int8 adcs_sendtc_uart(uint8 tcId, uint8 *msgBuffer, uint16 len)
{
    uint8 *data_ptr = NULL;
    uint8 *vld_pld = NULL;
    uint16 uart_tx_len = 0;
    uint16 uart_rx_len = 0;
    uint8 uart_data[7] = {0};
    e_adcs_status status = OPERATION_FAILURE;
    os_status_t sem_status = ral_error;

    // uart_tx_len = 4 byte(SOF,EOF)+ 1 byte TC id + 1 byte status + 1 ESC char
    uart_rx_len = 6 + 1;

    vld_pld = adcs_esc_char_vrfy(msgBuffer,&len);

    // uart_tx_len = len + 4 byte(SOF,EOF)+ 1 byte TC id
    uart_tx_len = len + 5;
    data_ptr = os_malloc_eram(uart_tx_len);
    io_hal_uart_receive_it(&ADCS_UART_HDL, &adcs_al_uart, uart_data,
                           uart_rx_len);
    if(NULL != data_ptr)
    {
        adcs_encode_uart_tc(data_ptr, tcId, vld_pld, len);
        io_hal_uart_transmit(&ADCS_UART_HDL, data_ptr, uart_tx_len, 500);
        if(tcId == 1)
        {
            sem_status = os_sem_take(adcs_tlm, 1000);
        }
        else
        {
            sem_status = os_sem_take(adcs_tlm, 5000);
        }

        if(sem_status == ral_err_timeout)
        {
            io_hal_uart_abort_receive_it(&ADCS_UART_HDL);
            status = IO_INTERFACE_ERR;
        }
        else if((uart_data[0] == 0x1f) && (uart_data[1] == 0x7f) &&
           (uart_data[2] == tcId))
        {
            adcs_esc_char_dec(uart_data, uart_rx_len);
            status = uart_data[3];
        }
        else
        {
            status = TC_TM_ERROR;
        }
        os_free(data_ptr);
        if(vld_pld != msgBuffer)
        {
            os_free(vld_pld);
        }
    }

    return status;
}

/**
 * @brief This API encode the uart telecommand data.
 */
void adcs_encode_uart_tc(uint8 *buff, uint8 tcId, uint8 *msgBuffer,
                         uint16 len)
{
    uint16 size = 0;
    if((NULL != buff) && (NULL != msgBuffer))
    {
        buff[0] = 0x1F;
        buff[1] = 0x7F;
        buff[2] = tcId;
        memcpy(&buff[3], msgBuffer, len);
        size = 3 + len;
        buff[size] = 0x1F;
        buff[size + 1] = 0xFF;
    }
    else
    {
        //No Action
    }
}

/**
 * @brief Function to request telemetry to ADCS through UART interface
 */
int8 adcs_requesttlm_uart(uint8 tlmId, uint8 *msgBuffer, uint16 len)
{
    uint8 buff[5];
    e_adcs_status status = OPERATION_FAILURE;
    uint16 uart_len = 0;
    uint8 *uart_data = NULL;
    os_status_t sem_status = ral_error;

    //5 bytes = 4bytes of start/end message identifier + 
    //1byte TLM ID + 20 for ESC Char
    //Ref: Figure 4 UART Telemetry reply in CubeADCS Firmware Reference Manual
    uart_len = len + 5 + 20;

    adcs_encode_uart_tlm(tlmId, buff);
    uart_data = os_malloc_eram(uart_len);
    memset(uart_data, 0, uart_len);

    io_hal_uart_receive_it(&ADCS_UART_HDL, &adcs_al_uart, uart_data, uart_len);
    if((NULL != msgBuffer) && (NULL != uart_data))
    {
        io_hal_uart_transmit(&ADCS_UART_HDL, buff, 5, 500);
/*      DEBUG_CPRINT(("\n\rUART TX buffer 0 : %x", buff[0]));
        DEBUG_CPRINT(("\n\rUART TX buffer 1 : %x", buff[1]));
        DEBUG_CPRINT(("\n\rUART TX buffer 2 : %x", buff[2]));
        DEBUG_CPRINT(("\n\rUART TX buffer 3 : %x", buff[3]));
        DEBUG_CPRINT(("\n\rUART TX buffer 4 : %x", buff[4]));*/
        sem_status = os_sem_take(adcs_tlm, 5000);
        if(sem_status == ral_err_timeout)
        {
            io_hal_uart_abort_receive_it(&ADCS_UART_HDL);
            status = IO_INTERFACE_ERR;
        }
        else if((uart_data[0] == 0x1f) && (uart_data[1] == 0x7f) &&
           (uart_data[2] == tlmId))
        {
            adcs_esc_char_dec(uart_data, uart_len);
            status = OPERATION_SUCCESS;
            memcpy(msgBuffer, &uart_data[3], len);
        }
        else
        {
            status = TC_TM_ERROR;
        }
    }
    if(NULL != uart_data)
    {
        os_free(uart_data);
    }
    return status;
}

/**
 * @brief This API encode the uart telemtry data.
 */
void adcs_encode_uart_tlm(uint8 tlmId, uint8 *buff)
{
    buff[0] = 0x1F;
    buff[1] = 0x7F;
    buff[2] = tlmId;
    buff[3] = 0x1F;
    buff[4] = 0xFF;
}

/**
 * @brief ADCS CAN interface queue init api
 */
int8 adcs_can_init(void)
{
    os_status_t q_sts;
    q_sts = os_queue_create(&adcs_can_queue, 50, sizeof(adcs_can_data_s), "ADCS_CAN_QUEUE");
    return q_sts;
}

/**
 * @brief ADCS UART interface init api
 */
int8 adcs_uart_init(void)
{
    os_status_t s_sts;
    hal_ret_sts hal_sts = HAL_MAX_ERR;
    hal_sts = hal_sts;
    s_sts = os_sem_create_bin(&adcs_tlm, "ADCS_UART_SEM");
    os_sem_take(adcs_tlm, 5000);
#ifdef COREBOARD
    adcs_al_uart.io_intf_id = IOAL_INST_UART5;
#else
    adcs_al_uart.io_intf_id = IOAL_INST_UART6;
#endif
#if !defined(STM32F769xx) || defined(ADCS_UART6_ENB)
    ahal_register_io_cb(&adcs_al_uart, UART_RX_CPLT_CB, adcs_uart_rx_cmplt_cb,
                        HISR);
    ahal_register_io_cb(&adcs_al_uart, UART_RX_ABORT_CPLT_CB,
            adcs_uart_rx_abort_cmplt_cb, HISR);

    ahal_register_io_cb(&adcs_al_uart, UART_ERROR_CB, adcs_uart_rx_cmplt_cb,
                            HISR);
    // TODO - Timeout need to be profiled and configured
    io_hal_uart_receive_timeout_config(&ADCS_UART_HDL, 1152);
    hal_sts = io_hal_uart_receive_timeout_enable(&ADCS_UART_HDL);
#endif
    return s_sts;
}

/**
 * @brief ADCS interface init api
 */
int8 adcs_intf_init(void)
{
    os_status_t s_sts;
    // Resource sharing semaphore
    s_sts = os_sem_create_bin(&adcs_sync_sem, "ADCS_SYNC_SEM");
    // Interface selection
#if !defined(ADCS_HW_SIM) && !defined(LINUX_TEMP_PORT)
    // ADCS CAN init
    s_sts = adcs_can_init();
    // ADCS UART init
    s_sts = adcs_uart_init();
    // Primary interface read from NVM
    nvm_parameter_read(NVM_DATA, NVM_ADCS_DEFAULT_INTERFACE,
                        (uint8*)&adcs_primary_interface);
#else
    // Init ADCS Socket interface
    adcs_sock_init();
#endif
    return s_sts;
}

/**
 * @brief Function ADCS UART interrupt callback
 */
void adcs_uart_rx_cmplt_cb(void *args)
{
    os_sem_give(adcs_tlm);
}

/**
 * @brief Function ADCS UART abort interrupt callback
 */
void adcs_uart_rx_abort_cmplt_cb(void *args)
{

}

/**
 * @brief This API is used to get the queue receive handler of ADCS
 */
int8 adcs_get_can_queue_hdlr(os_queue_handle_ptr *hdlr)
{
    int8 status = 0;

    *hdlr = adcs_can_queue;

    return status;
}
/**
 * @brief This API to set the file download parameters
 */
int8 adcs_set_fdwnld_param(uint16 file_size, uint16 tlm_blk_size)
{
    int8 status = 0;

    adcs_global_fsize = file_size;
    adcs_global_tlm_blksize = tlm_blk_size;

    return status;
}

/*
 * @brief This API downloads data in burst through UART interface
 */
int8 adcs_downldburst_uart(uint8 *msgBuffer, uint8 *rx_buffer, uint16 tx_size,
        uint16 *rx_size)
{
    uint8 *data_ptr = NULL;
    uint16 uart_tx_len = 0;
    uint16 uart_rx_len = 0;
    uint8 *uart_data = NULL;
    uint16 file_size = 0;
    int8 status = -1;
    uint16 seq_no = 0;
    uint8 *adcs_file_tm_ptr = NULL;
    uint16 tlm_blksize = 0;
    uint16 tlm_length = 0;
    uint16 tlm_offset = 0;
    os_status_t sem_status = ral_error;
    sem_status = sem_status;

    // Assigning TM payload size for per packet.
    tlm_blksize = adcs_global_tlm_blksize;
    // uart_tx_len = len + 4 byte(SOF,EOF)+ 1 byte TC id
    uart_tx_len = tx_size + 5;

    // get block ready status and length by retrying 3 times with 1s polling
    status = adcs_get_download_block_status(&file_size);
    if(status == 0)
    {
        // Assigning user configured file size
        file_size = adcs_global_fsize;
        /*
         * UART frame contains 2byte SOF, 1byte Id, 2byte frame index,
         * 20 byte of Data and 2 byte EOF. Overall 27 bytes for 20 byte data.
         */
        if(file_size % 20)
        {
            /*
             * Partial frame also transfered in 27 byte packet with dummy
             * elements for reaming data length
             */
            uart_rx_len = 1;
        }
        /*
         * Per packet data 20 bytes, 2 byte SOF, 2 byte EOF, 2 byte packet
         * counter, 1 byte tc id and 20 bytes for ESC Char.
         */
        uart_rx_len = ((uart_rx_len + (file_size/20)) * 27);

        data_ptr = os_malloc_eram(uart_tx_len);
        uart_data = adcs_file_dwnld_buffer;
        memset(uart_data,0,uart_rx_len);
        memset(data_ptr,0,uart_tx_len);
        /*
         * Timeout configuration to 500 ms for File  downlaod
         */
        io_hal_uart_receive_timeout_config(&ADCS_UART_HDL, 57600);
        io_hal_uart_receive_timeout_enable(&ADCS_UART_HDL);
        // Interrupt will be triggered after uart_rx_len data received.
        io_hal_uart_receive_it(&ADCS_UART_HDL, &adcs_al_uart, uart_data,
                uart_rx_len + 20);

        adcs_encode_uart_tc(data_ptr, 119, msgBuffer, tx_size);

        if(NULL != msgBuffer)
        {
            io_hal_uart_transmit(&ADCS_UART_HDL, data_ptr, uart_tx_len, 500);

            /*
             * This 10s need to be tuned for download burst since upto
             * 20kb can be received.
             */
            sem_status = os_sem_take(adcs_tlm, 10000);
            adcs_esc_char_dec(uart_data, uart_rx_len + 20);
            DEBUG_CPRINT(("ADCS File Download Sem Take Status:- %d\n",\
                        sem_status));
            if((uart_data[0] == 0x1f) && (uart_data[1] == 0x7f) &&
                    (uart_data[2] == 119))
            {
                while(uart_rx_len>0)
                {
                    /*
                     * Verify remaining data length greater then maximum TM
                     * length for per packet if true assign maximum TM length
                     * for per packet else assign remaining length
                     */
                    if(uart_rx_len > tlm_blksize)
                    {
                        tlm_length = tlm_blksize;
                    }
                    else
                    {
                        tlm_length = uart_rx_len;
                    }

                    /*
                     *  4 bytes are added for seq_no(2) + tm_ctrl(1) +
                     *  radio id(1)
                     */
                    adcs_file_tm_ptr = os_malloc_eram(tlm_length + 4);
                    os_memcpy(adcs_file_tm_ptr, &seq_no, 2);
                    // 0x70 represents enable encryption, authentication
                    // and message type as response
                    adcs_file_tm_ptr[2] = 0x70;
                    adcs_file_tm_ptr[3] = adcs_radio_id;
                    os_memcpy(&adcs_file_tm_ptr[4], &uart_data[tlm_offset],
                            tlm_length);
                    adcs_file_tm.src_entity = 130;
                    adcs_file_tm.pld.pld_ptr = adcs_file_tm_ptr;
                    adcs_file_tm.Msg_len = tlm_length;
                    adcs_file_tm.Msg_id = 119;
                    os_itc_msg_send(&adcs_file_tm, TM_STORAGE,
                            os_send_itc_wait);

                    // data length and offset update for next packet
                    tlm_offset = tlm_offset + tlm_length;
                    uart_rx_len = uart_rx_len - tlm_length;
                    seq_no++;
                    status = 0;
                    os_delay(100);
                }
            }
        }
        /*
         * Timeout configuration to 10 ms for normal operations
         */
        io_hal_uart_receive_timeout_config(&ADCS_UART_HDL, 1152);
        io_hal_uart_receive_timeout_enable(&ADCS_UART_HDL);
        // Added to recover extra sem give because of timeout configuration
        os_sem_take(adcs_tlm, 1000);
    }
    *rx_size = 1;
    return status;
}

/*
 * @brief This API verify the download block status and update the block len
 */
int8 adcs_get_download_block_status(uint16_t *rx_size)
{
    int8 status = -1;
    uint8 buff[5] = {0};
    uint16 uart_tx_len = 5;
    uint16 uart_rx_len = 0;
    uint8 *uart_data = NULL;
    int8 rtry_cnt = 3;
    COMMON_DownloadBlockReady_t dwnld_blck_ready = {0};

    uart_rx_len = adcs_get_tlm_size(DownloadBlockReady);
    uart_rx_len += 5;

    uart_data = os_malloc_eram(uart_rx_len);
    os_memset(uart_data,0,uart_rx_len);

    adcs_encode_uart_tlm(DownloadBlockReady, buff);

    // Retry 3 time if block was not ready for download
    while(rtry_cnt)
    {
        if(NULL != uart_data)
        {
            io_hal_uart_receive_it(&ADCS_UART_HDL, &adcs_al_uart, uart_data,
                    uart_rx_len);

            io_hal_uart_transmit(&ADCS_UART_HDL, buff, uart_tx_len, 500);

            os_sem_take(adcs_tlm, 5000);

            if((uart_data[0] == 0x1f) && (uart_data[1] == 0x7f) &&
                    (uart_data[2] == DownloadBlockReady))
            {
                // Extract data stream to it structure
                COMMON_ExtractDownloadBlockReadyTlm(&uart_data[3],
                        &dwnld_blck_ready);
                // Verify block ready for download
                if(dwnld_blck_ready.ready == 1)
                {
                    // Assigning block length
                    *rx_size = dwnld_blck_ready.length;
                    status = 0;
                    break;
                }
                else
                {
                    // Added 1s delay before retrying
                    os_delay(1000);
                }
            }
            rtry_cnt--;
        }
        else
        {
            break;
        }
    }
    if(NULL != uart_data)
    {
        os_free(uart_data);
    }
    return status;
}

/**
 * @brief This API verifies and adds 0x1f (ESC character) if any ESC character
 * present in data_buffer. This verification is based on the ADCS firmware
 * reference manual.
 */
static uint8* adcs_esc_char_vrfy(uint8 *data_buffer, uint16 *len)
{
    uint16 length = *len;

    // Count the number of escape characters in the data_buffer
    uint16 count = 0;
    for (int i = 0; i < length; i++)
    {
        if (data_buffer[i] == 0x1f)
        {
            count++;
        }
    }

    // if no esc character found on the data_buffer return data_buffer
    if (count == 0)
    {
        return data_buffer;
    }

    // Allocate memory to add esc characters
    uint8 *vld_pld = os_malloc_eram(length + count);
    if (vld_pld != NULL)
    {
        // Loop through each element in the data_buffer
        for (int i = 0, j = 0; i < length; i++, j++)
        {
            if (data_buffer[i] == 0x1f)
            {
                // Add the escape character
                vld_pld[j++] = 0x1f;
            }

            // Copy the current element from the original payload
            vld_pld[j] = data_buffer[i];
        }
        // Update the length of the validated payload
        *len = length + count;
    }

    return vld_pld;
}

/**
 * @brief Function to send telecommand to ADCS through UART interface
 * without acknowledgment
 */
int8 adcs_sendtc_uart_nack(uint8 tcId, uint8 *msgBuffer, uint16 len)
{
    uint8 *data_ptr = NULL;
    uint8 *vld_pld = NULL;
    uint16 uart_tx_len = 0;
    int8 status = -1;

    vld_pld = adcs_esc_char_vrfy(msgBuffer,&len);
    // uart_tx_len = len + 4 byte(SOF,EOF)+ 1 byte TC id
    uart_tx_len = len + 5;
    data_ptr = os_malloc_eram(uart_tx_len);
    if(NULL != data_ptr)
    {
        adcs_encode_uart_tc(data_ptr, tcId, vld_pld, len);
        status = io_hal_uart_transmit(&ADCS_UART_HDL, data_ptr,\
                uart_tx_len, 500);
        //os_delay(1);

#if 0
        for(int i=0; i<uart_tx_len;i++)
        	DEBUG_CPRINT(("%.2x ",data_ptr[i]));
        DEBUG_CPRINT(("\n"));
#endif
        os_free(data_ptr);
        if(vld_pld != msgBuffer)
        {
            os_free(vld_pld);
        }
    }

    return status;
}

/**
 * @brief Function to configure the primary interface of ADCS
 */
int8 adcs_interface_cfg(uint8 adcs_interface)
{
    int8 status = OPERATION_FAILURE;
    if(ADCS_UART == adcs_interface || ADCS_CAN == adcs_interface)
    {
        adcs_primary_interface = adcs_interface;
        nvm_parameter_write(NVM_DATA, NVM_ADCS_DEFAULT_INTERFACE,
                (uint8*)&adcs_primary_interface);
        status = OPERATION_SUCCESS;
    }
    else
    {
        //No Action
    }
    return status;
}

/**
 * @brief Function to return the primary interface of ADCS
 */
int8 get_adcs_interface(void)
{
    return adcs_primary_interface;
}

/**
 * @brief Function to decode escape character in TM
 */
void adcs_esc_char_dec(uint8_t* data, uint16_t len)
{
    uint16_t i = 0;
    uint16_t count = 0;

    for (i = 0; i < len; i++)
    {
        if(data[i] == 0x1F && data[i+1] == 0x1F)
        {
            len = len - 1;
            memcpy(&data[i], &data[i+1], len-i);
            count++;
        }
    }
#if 0
    DEBUG_CPRINT(("No of 1F find:- %d\n", count));
    for(i = 0; i < len; i++)
        DEBUG_CPRINT(("%x ",data[i]));
#endif
}
