/**
 * @file adcs_vapi_fw.c
 *
 * @brief This file contains OS abstracted ADCS functions for ADCS firmware
 * upload related functions
 *
 * @copyright Copyright 2023 Antaris, Inc.
 */

#include "adcs_vapi_fw.h"
#include "adcs_bootloader.h"
#include "adcs_common.h"
#include "adcs_cube_bypass.h"
#include "adcs_cubeacp.h"
#include "adcs_comm_interface.h"
#include "nvm.h"
#include "exo_osal.h"
#include "logger.h"

#define FILE_PACKET_SIZE    (20)    ///< ADCS File upload per packet size
#define ADCS_FIRMWARE_STORAGE_MEM (512*1024)
/// ADCS Firmware upload global parameters
uint8 adcs_firmware_destination;

#ifndef LINUX_TEMP_PORT
/* Allocated SDRAM memory for ADCS module 512 KB*/
static uint8 adcs_ftm_file_dwnlod_storage[ADCS_FIRMWARE_STORAGE_MEM] PLACE_IN_EXT_RAM_MEM;

#ifdef COREBOARD
/* Allocated FMC NOR flash memory for ADCS module 512 KB*/
static uint8 adcs_fw_storage[ADCS_FIRMWARE_STORAGE_MEM] PLACE_IN_EXT_FMC_FLASH_MEM;

#else
/* Allocated QSPI NOR for EPS module  1mb, if board is discovery*/
static uint8 adcs_fw_storage[ADCS_FIRMWARE_STORAGE_MEM] PLACE_IN_EXT_QSPI_FLASH_MEM;

#endif

/**
 * @brief This API return the SDRAM storage address for ADCS firmware
 */
uint8* adcs_get_sdram_storage_address(void)
{
    return adcs_ftm_file_dwnlod_storage;
}

/**
 * @brief This API return the NOR Flash storage address for ADCS firmware
 */
uint8* adcs_get_nor_flash_address(void)
{
    return adcs_fw_storage;
}
#endif

/**
 * @brief This API Reset the file upload hole map
 *
 * @return Return value of the interface send api
 * @retval 0 -> Success
 *         Other values -> Error
 */
static int8 adcs_reset_hole_map();

/**
 * @brief This API verify and switch ADCS node to bootloader from CUBE ACP
 * based on node type in identification telemetry.
 *
 * @return Return value of mode switch status based on ADCS node
 * @retval 0 -> Success
 *        -1 -> Error
 */
static int8 adcs_switch_boot_mode();

/**
 * @brief This API poll until uploaded block was successfully write
 * to flash maximum timeout was 10 seconds
 *
 * @return Return value of ADCS internal flash copy status
 * @retval 0 -> Success
 *        -1 -> Error
 */
static int8 adcs_upload_blck_complete();

/**
 * @brief This API poll until Internal flash copy was successfully completed.
 *
 * @return Return value internal flash complete status.
 * @retval 0 -> Success
 *         Other values -> Error
 */
static int8 adcs_cpy_int_flsh_complete();

/**
 * @brief This API Reset the file upload hole map
 */
static int8 adcs_reset_hole_map()
{
    int8 status = -1;
    uint8 tcid = 117;
    uint16 tclen = 0;
    uint8 *tcbuffer = NULL;

    status = adcs_send_cmd(tcid,tcbuffer,tclen);

    return status;
}

/**
 * @brief This API verify and switch ADCS node to bootloader from CUBE ACP
 * based on node type in identification telemetry.
 */
static int8 adcs_switch_boot_mode()
{
    int8 status = -1;
    uint16 size = 0;
    COMMON_Identification_t id_stat;
    int8 result = -1;
    uint8_t tcBuffer[3];

    result = adcs_request_identification_tlm(&id_stat);
    if(TLM_OK == result && id_stat.nodeType == 10)
    {
        for(int i = 0; i < 5; i++)
        {
            size = COMMON_FormatResetCmd(tcBuffer, 0x5A);
            result = adcs_send_cmd(tcBuffer[0], &tcBuffer[1], size-1);
            //if(TCMD_OK == result)
            os_delay(2000);
            result = adcs_request_identification_tlm(&id_stat);
            if(TLM_OK == result && id_stat.nodeType == 110)
            {
                status = OPERATION_SUCCESS;
                break;
            }
            else
            {
                os_delay(1000);
                result = adcs_request_identification_tlm(&id_stat);
                if(TLM_OK == result && id_stat.nodeType == 110)
                {
                    status = OPERATION_SUCCESS;
                    break;
                }
                else
                {
                    status = OPERATION_FAILURE;
                }
            }
        }
    }
    else if(id_stat.nodeType == 110)
    {
        status = OPERATION_SUCCESS;
    }
    else
    {
        status = OPERATION_FAILURE;
    }
    return status;
}

/**
 * @brief This API poll until uploaded block was successfully write
 * to flash maximum timeout was 10 seconds
 */
static int8 adcs_upload_blck_complete()
{
    int8 status = -1;
    uint8 max_rtry_cnt = 5;
    uint8 *tlmbuffer = NULL;
    int16 tlmlen = 0;
    COMMON_UploadBlockComplete_t upld_blck_complete = {0};

    /*
     * Maximum write time was 10 seconds so wait for 5 seconds and
     * retry 5 with 1 second delay in between.
     */
    os_delay(5000);
    tlmlen = adcs_get_tlm_size(UploadBlockComplete);
    tlmbuffer = os_malloc(tlmlen);
    do
    {
        /*
         * Request Upload Block Complete message to verify Finalize
         * Upload Block Completed
         */
        adcs_requesttlm(UploadBlockComplete,tlmbuffer,tlmlen);
        COMMON_ExtractUploadBlockCompleteTlm(tlmbuffer, &upld_blck_complete);

        // If busy flag and error flag was false finalize upload was success
        if(upld_blck_complete.busy == FALSE &&
                upld_blck_complete.error == FALSE)
        {
            DEBUG_CPRINT(("Finalize upload block success\n"));
            log_id(LOG_INFO_L1,LOG_ADCS,FINALIZE_UPLOAD_BLOCK_SUCCESS);
            status = 0;
            break;
        }
        // else wait for 1 second and and retry request
        else
        {
            os_delay(1000);
        }
        max_rtry_cnt--;
    }while(max_rtry_cnt);
    os_free(tlmbuffer);
    return status;
}

/**
 * @brief This API poll until Internal flash copy was successfully completed.
 */
static int8 adcs_cpy_int_flsh_complete()
{
    int8 status = -1;
    uint8 max_rtry_cnt = 5;
    uint8 *tlmbuffer = NULL;
    int16 tlmlen = 0;
    BOOTLOADER_CopyToIntFlashProgress_t int_flsh_complete = {0};

    tlmlen = BOOTLOADER_GetTlmLen(CopyToIntFlashProgress);
    tlmbuffer = os_malloc(tlmlen);
    do
    {
        /*
         * Request Copy internal flash Complete message to verify
         * copy completed.
         */
        status = adcs_requesttlm(CopyToIntFlashProgress, tlmbuffer, tlmlen);
        if(status == OPERATION_SUCCESS)
        {
            BOOTLOADER_ExtractCopyToIntFlashProgressTlm(tlmbuffer,\
                    &int_flsh_complete);

            /*
             * If busy flag and error flag was false finalize
             * upload was success
             */
            if(int_flsh_complete.busy == FALSE &&
                    int_flsh_complete.error == FALSE)
            {
                DEBUG_CPRINT(("Internal Flash copy success\n"));
                log_id(LOG_INFO_L1,LOG_ADCS,INTERNAL_FLASH_COPY_SUCCESS);
                status = OPERATION_SUCCESS;
                break;
            }
            // else wait for 1 second and and retry request
            else
            {
                os_delay(1000);
            }
        }
        else
        {
            os_delay(1000);
        }
        max_rtry_cnt--;
    }while(max_rtry_cnt);
    os_free(tlmbuffer);
    return status;
}

/**
 * @brief This API calculate checksum for given address and length.
 * CRC calculation method referred from Cube ADCS reference manual.
 */
uint16 adcs_crc_calc(uint8 *start, uint32 len)
{
    uint16 crc = 0;
    uint8 *data;
    uint8 *end = start + len;
    for (data = start; data < end; data++)
    {
        crc = (crc >> 8) | (crc << 8);
        crc ^= *data;
        crc ^= (crc & 0xff) >> 4;
        crc ^= crc << 12;
        crc ^= (crc & 0xff) << 5;
    }
    return crc;
}

/**
 * @brief This API erase the selected destination block and return the status
 */
int8 adcs_initiate_upload(uint8 *data)
{
    int8 status = -1;
    uint8 *tmbuffer = NULL;
    int16 tclen = 2;
    int16 tmlen = 0;
    uint8 busy = 0;
    uint8 rtry_cnt = 3;

    // Copy firmware destination to global variable
    adcs_firmware_destination = data[0];
    DEBUG_CPRINT(("Firmware Upload initialized flash index:- %d\n",\
                            adcs_firmware_destination));
    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,\
    Firmware_upload_initialized_flash_index__u8_,adcs_firmware_destination);

    adcs_send_cmd(114, data, tclen);
    tmlen = adcs_get_tlm_size(InitializeUploadComplete);
    tmbuffer = os_malloc(tmlen);

    /*
     * Block erase required 8 seconds to complete
     */
    os_delay(8000);

    // Poll until flash erase complete
    while(rtry_cnt)
    {
        // Request flash erase status
        adcs_requesttlm(InitializeUploadComplete, tmbuffer, tmlen);
        COMMON_ExtractInitializeUploadCompleteTlm(tmbuffer, (Boolean*)&busy);

        // Verify flash erase completed
        if(busy == 0)
        {
            DEBUG_CPRINT(("Flash erase success\n"));
            log_id(LOG_INFO_L1,LOG_ADCS,Flash_Erase_Success);
            status = 0;
            break;
        }
        else
        {
            rtry_cnt--;
        }
        os_delay(1000);
    }
    os_free(tmbuffer);
    return status;
}

/**
 * @brief This API upload the data_buffer to ADCS by transferring 20kb
 * every time and write to flash upto data_len
 */
int8 adcs_file_upload(uint8 *data_buffer, uint32 data_len)
{
    int8 status = 0;
    uint8 *tcbuffer = NULL;
    int16 tclen = 0;
    int16 tlmlen = 0;
    int32 remaining_len = 0;
    uint16 data_offset = 0;
    uint16 pkt_no = 0, miss_pkt_no = 0;
    uint8 hole_map[129] = {0};
    uint8 finalize_blck[8] = {0};
    uint32 count = 0;
    uint32 block_offset = 0, block_length = 0;
    uint8 missing_rtry_cnt = 3;
    int i = 0, j =0;

    // Reset hole map for file upload
    adcs_reset_hole_map();

    // 20 file bytes + 2 byte packet number
    tclen = 20 + 2;
    // 1 byte Id + tclen
    tcbuffer = os_malloc(tclen + 1);
    remaining_len = data_len;

    DEBUG_CPRINT(("File Upload Initiated\n"));
    log_id(LOG_INFO_L1,LOG_ADCS,Flash_Upload_Initiated);
    // Continue this loop until remaining_len was 0 or status return fail
    while((remaining_len > 0) && (status == 0))
    {
        missing_rtry_cnt = 3;
        os_memset(tcbuffer, 0, tclen);
        COMMON_FormatFileUploadCmd(tcbuffer, pkt_no,\
                &data_buffer[data_offset]);
#ifndef LINUX_TEMP_PORT
        // Transfer data packet to ADCS
        adcs_sendtc_uart_nack(tcbuffer[0], &tcbuffer[1], tclen);
#endif
        // Offset address and remaining length
        data_offset = data_offset + FILE_PACKET_SIZE;
        remaining_len = remaining_len - FILE_PACKET_SIZE;
        // increment packet number for next packet
        pkt_no++;

        // Finalize packet for every 20kb or no remaining length
        if(pkt_no == 1024 || remaining_len <= 0)
        {
            tlmlen = adcs_get_tlm_size(HoleMap1);
            do
            {
                count = 0;
                // Request hole map for transmitted 1024 packets
                adcs_requesttlm(HoleMap1,\
                        &hole_map[(HoleMap1 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap2,\
                        &hole_map[(HoleMap2 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap3,\
                        &hole_map[(HoleMap3 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap4,\
                        &hole_map[(HoleMap4 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap5,\
                        &hole_map[(HoleMap5 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap6,\
                        &hole_map[(HoleMap6 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap7,\
                        &hole_map[(HoleMap7 % HoleMap1) * 16], tlmlen);
                adcs_requesttlm(HoleMap8,\
                        &hole_map[(HoleMap8 % HoleMap1) * 16], tlmlen);

                DEBUG_CPRINT(("\nHole Map:-"));
                for(j = 0; j<128;j++)
                    DEBUG_CPRINT(("%x ",hole_map[j]));
                DEBUG_CPRINT(("\n"));

                hole_map[128] = '\0';
                log_id(LOG_INFO_L1, LOG_ADCS,Hole_Map);
                log_string(LOG_INFO_L1, LOG_ADCS,(char*)&hole_map[0]);
                log_string(LOG_INFO_L1, LOG_ADCS,(char*)&hole_map[50]);
                log_string(LOG_INFO_L1, LOG_ADCS,(char*)&hole_map[100]);

                // Retransmit the missing packet
                for(i = 0; i < pkt_no; i++)
                {
                    if ((hole_map[i >> 3] & (1 << (i & 0x07))) == 0)
                    {
                        miss_pkt_no = i;
                        data_offset = i * FILE_PACKET_SIZE;

                        os_memset(tcbuffer, 0, tclen);
                        COMMON_FormatFileUploadCmd(tcbuffer, miss_pkt_no,\
                                &data_buffer[data_offset]);

#ifndef LINUX_TEMP_PORT
                        status = adcs_sendtc_uart_nack(tcbuffer[0],\
                                &tcbuffer[1], tclen);
#endif
                        count++;
                    }
                    else
                    {
                        // No Action
                    }
                }
                DEBUG_CPRINT(("Missing Pckt Count:- %d\n",count));
                log_id_with_one_arg(LOG_WARNING,LOG_ADCS,\
                        FT_Missing_PCKT_COUNT__u32_,count);
                os_memset(hole_map, 0, 128);
                os_delay(500);
                missing_rtry_cnt--;
            }while((count != 0) && (missing_rtry_cnt > 0));

            /*
             * If there is no missing packet finalize the transfered data
             * to flash memory
             */
            if(count == 0)
            {
                // calculate block length 20kb or remaining length
                if(remaining_len > 0)
                {
                    block_length = FILE_PACKET_SIZE * pkt_no;
                }
                else
                {
                    block_length = (FILE_PACKET_SIZE * pkt_no) + remaining_len;
                }
                DEBUG_CPRINT(("block length:- %d Remaning length;- %d\n",\
                                                block_length, remaining_len));
                log_id_with_two_arg(LOG_INFO_L2,LOG_ADCS,\
                        Block_length__u32__Remaining_length__u32_,\
                        block_length, remaining_len);
                COMMON_FormatFinalizeUploadBlockCmd(finalize_blck,\
                        adcs_firmware_destination, block_offset, block_length);
                adcs_send_cmd(finalize_blck[0], &finalize_blck[1], 7);

                // Update block offest for next block
                block_offset = block_offset + FILE_PACKET_SIZE * 1024;
                /*
                 * Verify uploaded block was successfully copied
                 * to flash memory
                 */
                status = adcs_upload_blck_complete();
            }
            else
            {
                status = -1;
                break;
            }
            // Reset hole map for file upload
            adcs_reset_hole_map();

            // Reset variables for next block
            pkt_no = 0;
            data_offset = 0;
            data_buffer = data_buffer + FILE_PACKET_SIZE * 1024;
        }
        else
        {
            // No Action
        }
    }
    if(remaining_len <= 0)
    {
        status = 0;
    }
    else
    {
        DEBUG_CPRINT(("Firmware upload failed\n"));
        log_id(LOG_ERROR,LOG_ADCS,Firmware_upload_failed);
    }
    os_free(tcbuffer);
    return status;
}

/**
 * @brief This API Copy uploaded firmware to internal flash of ADCS
 * and reset the MCU to run the new firmware.
 */
int8 adcs_frimware_boot(uint8* data)
{
    COMMON_ExtendedIdentification_t boot_sts = {0};
    COMMON_BootIndexStatus_t boot_indx_sts = {0};
    uint16 expected_boot_ctr = 0;
    int8 status = -1;
    uint16 tclen = 3;
    uint8 *tcbuffer = NULL;
    tcbuffer = os_malloc(tclen);

    status = adcs_switch_boot_mode();
    if(status == OPERATION_SUCCESS)
    {
        // Send internal flash copy command
        tclen = BOOTLOADER_FormatCopyToInternalFlashCmd(tcbuffer, *data, 0);
        status = adcs_send_cmd(tcbuffer[0], &tcbuffer[1], tclen-1);

        // Verify internal flash copy completed
        status = adcs_cpy_int_flsh_complete();
        if(status == OPERATION_SUCCESS)
        {
            tclen = BOOTLOADER_FormatBootIndexCmd(tcbuffer,\
                    BootSetProgramsList_RunInternalFlashProgram);
            status = adcs_send_cmd(tcbuffer[0], &tcbuffer[1], tclen-1);

            os_delay(1000);
            // Request boot status telemetry
            status = adcs_request_boot_prog_status_tlm(&boot_sts);
            if(TLM_OK == status)
            {
                expected_boot_ctr = (boot_sts.bootCounter) + 1;
                // Send Reset command run new firmware
                tclen = COMMON_FormatResetCmd(tcbuffer, 0x5A);
                status = adcs_send_cmd(tcbuffer[0], &tcbuffer[1], tclen-1);
                //if(TCMD_OK == status)
                {
                    os_delay(10000);
                    // Request boot status telemetry
                    status = adcs_request_boot_prog_status_tlm(&boot_sts);
                    if(TLM_OK == status)
                    {
                        // Verify expected and actual boot counter are same
                        if(boot_sts.bootCounter == expected_boot_ctr)
                        {
                            DEBUG_CPRINT(("Boot Success\n"));
                            log_id(LOG_INFO_L1,LOG_ADCS,Boot_Success);
                            status = OPERATION_SUCCESS;
                        }
                        else
                        {
                            tclen = COMMON_GetTlmLen(BootIndexStatus);
                            status = adcs_requesttlm(BootIndexStatus, tcbuffer, tclen);
                            if(OPERATION_SUCCESS == status)
                            {
                                COMMON_ExtractBootIndexStatusTlm(tcbuffer, &boot_indx_sts);
                                if(BootStatus_BootSuccess == boot_indx_sts.bootStatus)
                                {
                                    DEBUG_CPRINT(("Boot Success\n"));
                                    log_id(LOG_INFO_L1,LOG_ADCS,Boot_Success);
                                    status = OPERATION_SUCCESS;
                                }
                                else
                                {
                                    DEBUG_CPRINT(("Boot Failed\n"));
                                    log_id(LOG_ERROR,LOG_ADCS,Boot_Failed);
                                    status = OPERATION_FAILURE;
                                }
                            }
                            else
                            {
                                DEBUG_CPRINT(("Boot Failed\n"));
                                log_id(LOG_ERROR,LOG_ADCS,Boot_Failed);
                                status = OPERATION_FAILURE;
                            }
                        }
                    }
                    else
                    {
                        // No Action
                    }
                }
            }
            else
            {
                // No Action
            }
        }
        else
        {
            // No Action
        }
    }
    else
    {
        // No Action
    }
    return status;
}

/**
 * @brief This API Verify firmware availability, upload the firmware to ADCS
 * and run uploaded firmware.
 */
int8 adcs_firmware_upload(uint8 *data)
{
    int8 status = OPERATION_FAILURE, ret_sts = 0;
    ret_sts = ret_sts;
    uint32 adcs_firmware_len = 0;
    uint16 adcs_firmware_crc = 0, adcs_sdram_crc = 0, adcs_fmc_nor_crc = 0;
    uint8 adcs_firmware_status = 0;
    // Read firmware length, status and CRC from NVM
    nvm_parameter_read(NVM_DATA, NVM_ADCS_FW_SIZE,\
            (uint8_t *)&adcs_firmware_len);
    nvm_parameter_read(NVM_DATA, NVM_ADCS_FW_STATUS,\
            (uint8_t *)&adcs_firmware_status);
    nvm_parameter_read(NVM_DATA, NVM_ADCS_FW_CRC,\
            (uint8_t *)&adcs_firmware_crc);

    if(adcs_firmware_status == ADCS_FW_AVAILABLE)
    {
#ifndef LINUX_TEMP_PORT
        // Calculate CRC for firmware in SDRAM
        adcs_sdram_crc = adcs_crc_calc(adcs_get_sdram_storage_address(),\
                adcs_firmware_len);
#endif
        // Compare nvm CRC and calculated CRC are same
        if(adcs_sdram_crc == adcs_firmware_crc)
        {
            status = adcs_switch_boot_mode();
            if(status == OPERATION_SUCCESS)
            {
                // Erase destination and return status
                status = adcs_initiate_upload(data);
                if(status == OPERATION_SUCCESS)
                {
#ifndef LINUX_TEMP_PORT
                    // Upload firmware to ADCS destination location
                    status = adcs_file_upload(adcs_get_sdram_storage_address(),
                            adcs_firmware_len);
#endif
                    if(status == OPERATION_SUCCESS)
                    {
                        // If firmware upload success run the new firmware
                        status = adcs_frimware_boot(data);
                        // Update firmware update status
                        adcs_firmware_status = ADCS_FW_UPLOAD_SUCCESS;
                    }
                    else
                    {
                        status = FW_UPLOAD_FAILURE;
                        // Update firmware update status
                        adcs_firmware_status = ADCS_FW_UPLOAD_FAILURE;
                    }
                }
                else
                {
                    status = FW_UPLOAD_FAILURE;
                    // Update firmware update status
                    adcs_firmware_status = ADCS_FW_UPLOAD_FAILURE;
                }
                if(status != OPERATION_SUCCESS)
                {
                    /*
                     * Error recovery if firmware upload failed run the
                     * old firmware stored in internal flash1.
                     */
                    do
                    {
                        uint8 old_fw_destination = ProgramsList_ExtFlash1;
                        status = adcs_frimware_boot(&old_fw_destination);
                    }while(status != OPERATION_SUCCESS);
                    status = FW_ADCS_BCK_FW_UPLD_SCS;
                    // Update firmware update status
                    adcs_firmware_status = ADCS_BCKUP_FW_UPLD_SCS;
                }
                else
                {
                    status = FW_UPLOAD_SUCCESS;
                    // Update firmware update status
                    adcs_firmware_status = ADCS_FW_UPLOAD_SUCCESS;
                }
            }
            nvm_parameter_write(NVM_DATA, NVM_ADCS_FW_STATUS,\
                    (uint8_t *)&adcs_firmware_status);
        }
        else
        {
            status = FW_SDRAM_CRC_ERR;
            // Copy firmware from flash to SDRAM
#ifndef LINUX_TEMP_PORT
            ret_sts = os_memcpy_extmem(adcs_get_sdram_storage_address(),\
                    adcs_get_nor_flash_address(), adcs_firmware_len,\
                    NULL, 1,1);
            // Calculate and compare CRC
            adcs_fmc_nor_crc = adcs_crc_calc(adcs_get_sdram_storage_address(),\
                    adcs_firmware_len);
#endif
            if(adcs_fmc_nor_crc != adcs_firmware_crc)
            {
                status = FW_FMC_NOR_CRC_ERR;
                // Update firmware available status false
                adcs_firmware_status = ADCS_FW_NOT_AVAILABLE;
                nvm_parameter_write(NVM_DATA, NVM_ADCS_FW_STATUS,\
                        (uint8_t *)&adcs_firmware_status);
            }
            else
            {
                // No Action
            }
        }
    }
    else
    {
        status = FW_NOT_AVAILABLE;
    }
    return status;
}

/**
 * @brief This API return the ADCS firmware update status
 */
int8 get_adcs_fw_upd_sts()
{
    int8 status = OPERATION_FAILURE;
    uint8 adcs_firmware_status = 0;
    // Read firmware status from NVM
    nvm_parameter_read(NVM_DATA, NVM_ADCS_FW_STATUS,\
            (uint8_t *)&adcs_firmware_status);

    switch(adcs_firmware_status)
    {
    case ADCS_FW_NOT_AVAILABLE:
    {
        status = FW_NOT_AVAILABLE;
    }
    break;
    case ADCS_FW_UPLOAD_SUCCESS:
    {
        status = FW_UPLOAD_SUCCESS;
    }
    break;
    case ADCS_FW_UPLOAD_FAILURE:
    {
        status = FW_UPLOAD_FAILURE;
    }
    break;
    case ADCS_BCKUP_FW_UPLD_SCS:
    {
        status = FW_ADCS_BCK_FW_UPLD_SCS;
    }
    break;
    }
    return status;
}
