#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "port.h"
#include "proto.h"
#include "spi_framing.h"
#include "spi_state.h"
#include "tz_log.h"
#include "tz_debug.h"
#include "sec_spi_info.h"
#include "sec_mw.h"

#ifdef USE_MOBICORE
#include "TlApi/TlApi.h"
#endif
#ifdef USE_BLOWFISH
#include <tee_spi.h>
#include <time.h>
#endif
#include "pearlv3_ese_sle97.h"

#ifdef USE_QSEE
#include <qsee_timer.h>
#endif

#define NO_SHIFT_VALUE          16

#define POLL_INTERVAL           2000
#define POLL_INTERVAL_MS        2

#define MAX_CRC_ERROR           10

#define DEBUG_MODE              1
#define NO_DEBUG_MODE           0


enum spi_state {
    SPI_APDU_INIT,
    SPI_START_AWAIT_RESPONSE,
    SPI_AWAIT_RESPONSE,
    SPI_TRANSFER_CHUNK,
    SPI_TRANSFER_FINAL_BLOCK,
};

enum spi_wait_state {
    SPI_WAIT_READ,
    SPI_WAIT,
};

uint8_t spi_frame_check_alive[] = {ESE_FRAME_CHECK_ALIVE};
uint8_t spi_frame_NULL[] =  {ESE_FRAME_NULL};
uint8_t spi_frame_ack[] = {ESE_FRAME_ACK_APDU};
uint8_t spi_frame_nack_crc[] = {ESE_FRAME_NACK_CRC_APDU};

uint8_t frame_out[SPI_MAX_FRAME_SIZE];
uint8_t frame_in[SPI_MAX_FRAME_SIZE];

uint8_t fragment_out[SPI_MAX_FRAME_SIZE];
uint8_t fragment_in[SPI_MAX_FRAME_SIZE];

//io_transfer_para data;
#ifdef USE_MOBICORE
spi_transfer_t dataTx;
spi_transfer_t dataRx;
#endif
#ifdef USE_QSEE
qsee_spi_transaction_info_t dataRx;
qsee_spi_transaction_info_t dataTx;
#endif
#ifdef USE_BLOWFISH
TEES_SPITransfer dataRx;
TEES_SPITransfer dataTx;
#endif

uint8_t shift = NO_SHIFT_VALUE ;


#if 0
/**
* this method can be used to assert CS
*/
void spi_enable_CS(int fd) {
    // in current implementation, CS state does not change during eSE transaction
    // hence the function does nothing
//    LOGD("enable CS");
//    ioctl(fd,P3_ENABLE_SPI_CLK);
//    ioctl(fd,P3_ENABLE_SPI_CS);
}


/**
* this method can be used to deassert CS
*/
void spi_disable_CS(int fd) {
    // in current implementation, CS state does not change during eSE transaction,
    // hence the function does nothing
//    LOGD("disable CS");
//    ioctl(fd,P3_DISABLE_SPI_CS);
//    ioctl(fd,P3_DISABLE_SPI_CLK);
}
#endif

#ifdef USE_QSEE
void setSpiConfiguration(qsee_spi_config_t* spi_config) {
    /**< SPI clock frequency */
    spi_config->max_freq = SPI_MAX_FREQ;
    if (iso7816_spi_mode0() == 1) {
        /**< Clock polarity  type to be used for the SPI core. */
        spi_config->spi_clk_polarity = QSEE_SPI_CLK_IDLE_LOW;
        /**< Shift mode(CPHA) type to be used for SPI core. */
        spi_config->spi_shift_mode = QSEE_SPI_INPUT_FIRST_MODE;
    } else {
        /**< Clock polarity  type to be used for the SPI core. */
        spi_config->spi_clk_polarity = QSEE_SPI_CLK_IDLE_HIGH;
        /**< Shift mode(CPHA) type to be used for SPI core. */
        spi_config->spi_shift_mode = QSEE_SPI_OUTPUT_FIRST_MODE;
    }
    /**< CS polarity type to be used for the SPI core. */
    spi_config->spi_cs_polarity = QSEE_SPI_CS_ACTIVE_LOW;
    /**< CS Mode to be used for the SPI core. */
    spi_config->spi_cs_mode = QSEE_SPI_CS_KEEP_ASSERTED;
    /**< Clock mode type to be used for the SPI core. */
    spi_config->spi_clk_always_on = QSEE_SPI_CLK_NORMAL;
    /**< SPI bits per word, any value from 3 to 31 */
    spi_config->spi_bits_per_word = 8;
#ifdef EXTENEDED_CONFIG
    /**< Put the Device in High Performance */
    spi_config->hs_mode = 0;
    /**< Put the Device in loop back test*/
    spi_config->loopback = 0;
#ifdef EXTENDED_NEW_CONFIG
    /**< Slave index from 0 to 3, if mulitple SPI devices are connected to the same master */
    spi_config->spi_slave_index = 0;
    /**< The minimum delay between two word(N-bit) transfers */
    spi_config->deassertion_time_ns = 0;
#endif
#endif

}
#endif

/**
* this method is used to transcieve data over the SPI interface
* int fd is the file descriptor corresponding the the Secure Element file in the file system
* uint8_t* spi_out is the buffer containing the frame to transmit
* uint8_t* spi_in is the buffer that will be received after the transcieve call
* uint16_t len is the number of byte to transcieve
* uint8_t debug is the indicator to display the data transcieved or not
* uint8_t fragmentSize the size of the fragment
*/
#if defined(EXYNOS_7420)
int spi_transcieve(uint8_t* spi_out, uint8_t* spi_in, uint16_t len, uint8_t debug) {
    uint16_t curOffset = 0;
    // send by 16bytes long blocks

    int retval = 0;
#ifdef USE_MOBICORE
    uint32_t deviceId = ESE_SPI_DEVICE_ID;
    spi_config_t spi_config;

    spi_config.delay_usecs = 100;
    spi_config.speed_hz = SPI_MAX_FREQ;
    spi_config.bits_per_word = 0;
    spi_config.dma_mode = 0;
#endif

#ifdef USE_QSEE
    qsee_spi_device_id_t deviceId = ESE_SPI_DEVICE_ID;
    qsee_spi_config_t spi_config;
    setSpiConfiguration(&spi_config);
#endif
    if (debug == DEBUG_MODE) {
        hex_print_tag_debug("[SEND]", spi_out, len);
#ifndef DEBUG_LOW
        if (len <10){
            hex_print_tag("[SEND]", spi_out, len);
        } else {
            hex_print_tag("[SEND]", spi_out, 10);
        }
#endif
    }
    if (len > 0) {
        dataTx.buf_addr = spi_out+curOffset;
        dataRx.buf_addr = spi_in+curOffset;
        dataTx.buf_len =len;
        dataRx.buf_len =len;
//        ioctl(fd, P3_RW_SPI_DATA, &data);
    }

#ifdef USE_MOBICORE
    if (0 != tlApiSecSPIWriteRead(deviceId, &spi_config, &dataTx, &dataRx)) {
        retval = -1;
        LOGE("tlApiSecSPIWriteRead fail! ret = %d",retval);
    }
#endif
#ifdef USE_QSEE
    if (0 != qsee_spi_full_duplex(deviceId, &spi_config, &dataTx, &dataRx)) {
        retval = -1;
        LOGE("qsee_spi_full_duplex fail! ret = %d",retval);
    }
#endif
    if (debug == DEBUG_MODE) {
        hex_print_tag_debug("[RECV]", spi_in, len+curOffset);
#ifndef DEBUG_LOW
        if (len <10){
            hex_print_tag("[RECV]", spi_in, len+curOffset);
        } else {
            hex_print_tag("[RECV]", spi_in, 10);
        }
#endif
    }
    return retval;
}

#else // defined(EXYNOS_7420)

int spi_transcieve(uint8_t* spi_out, uint8_t* spi_in, uint16_t len, uint8_t debug) {
    uint16_t curOffset = 0;
    // send by 16bytes long blocks

    int retval = 0;
#ifdef USE_MOBICORE
    uint32_t deviceId = ESE_SPI_DEVICE_ID;
    spi_config_t spi_config;

    spi_config.delay_usecs = 100;
    spi_config.speed_hz = SPI_MAX_FREQ;
    spi_config.bits_per_word = 0;
    spi_config.dma_mode = 0;
    spi_config.spi_mode = SPI_TX_MODE_0;
#endif

#ifdef USE_QSEE
    qsee_spi_device_id_t deviceId = ESE_SPI_DEVICE_ID;
    qsee_spi_config_t spi_config;
#ifdef GPIO_CONTROL
    unsigned char data_cson[4] = "cson";
    unsigned char data_csoff[5] = "csoff";
    ese_oem_req_t cmd;
    unsigned int output;
    cmd.cmd_id = ESE_CS_ON;
    cmd.data = data_cson;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS ON qsee_oem_process_cmd retval = %d", retval);
#endif
    setSpiConfiguration(&spi_config);
#endif
    if (debug == DEBUG_MODE) {
        hex_print_tag_debug("[SEND]", spi_out, len);
#ifndef DEBUG_LOW
        if (len <10){
            hex_print_tag("[SEND]", spi_out, len);
        } else {
            hex_print_tag("[SEND]", spi_out, 10);
        }
#endif
    }
    if (len > 0) {
#ifdef USE_BLOWFISH
        dataTx.bufAddr = spi_out+curOffset;
        dataRx.bufAddr = spi_in+curOffset;
        dataTx.bufLen = len;
        dataRx.bufLen = len;
#else
        dataTx.buf_addr = spi_out+curOffset;
        dataRx.buf_addr = spi_in+curOffset;
        dataTx.buf_len =len;
        dataRx.buf_len =len;
//        ioctl(fd, P3_RW_SPI_DATA, &data);
#endif
    }

#ifdef USE_MOBICORE
    if (0 != tlApiSecSPIWriteRead(deviceId, &spi_config, &dataTx, &dataRx)) {
        retval = -1;
        LOGE("tlApiSecSPIWriteRead fail! ret = %d",retval);
    }
#endif
#ifdef USE_QSEE
    if (0 != qsee_spi_full_duplex(deviceId, &spi_config, &dataTx, &dataRx)) {
        retval = -1;
        LOGE("qsee_spi_full_duplex fail! ret = %d",retval);
    }
#ifdef GPIO_CONTROL
    cmd.cmd_id = ESE_CS_OFF;
    cmd.data = data_csoff;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS OFF qsee_oem_process_cmd retval = %d", retval);
#endif
#endif
    if (debug == DEBUG_MODE) {
        hex_print_tag_debug("[RECV]", spi_in, len+curOffset);
#ifndef DEBUG_LOW
        if (len <10){
            hex_print_tag("[RECV]", spi_in, len+curOffset);
        } else {
            hex_print_tag("[RECV]", spi_in, 10);
        }
#endif
    }
    return retval;
}


#endif //defined(EXYNOS_7420)



/**
* this method can be used to detect bit shift.  It check the number of shift must be done on the parameter to see bit8 set.
* uint8_t c the byte to check whether bit are shifted
* return the number of bit to shift to the left is needed to see b8 set
*/
uint8_t spi_bitshift_detection(uint8_t c) {

    int i = 0, shift = 0;
    LOGD("shift detection on %x", c);

    for (i = 0 ; i < 8 ; i++) {
        if ((c & 0x0080) != 0x0080) {
            c = c<<1;
            shift++;
        }
    }

    if (shift != 0) {
        LOGD("shift detected %d", shift);
        return shift;
    }

    LOGD("no shift");
    return 0;
}


/**
* this method implements the acceptence algorithm for a received frame.
* currently, CRC error is the only critera to reject a frame
* uint8_t* spi_in is the buffer containing the frame that has been sent
* uint8_t* spi_out is the buffer containing the frame that has been received
* return NO_ERROR when the frame is accepted
*        CRC_ERROR when a CRC error is detected
*/
int spi_check_raw_frame(uint8_t* spi_in, uint8_t* spi_out) {
    // check CRC

    if (spi_check_frame(spi_out, spi_out[1]+4) == 1) {
//        LOGD("crc error");
        // check alive overwrite work around
        if (spi_out[0] == 0x0081) {
            int i = 0;
            //LOGD("engage workaround");

            for (i = 0; i < 6; i++) {
                if (spi_out[i] == 0x00C1 || spi_out[i] == 0x0001 || spi_out[i] == 0x0041) {
                    LOGD("recovered");
                    memcpy(spi_out, spi_out+i, spi_out[i+1]+4);
//                    hex_print_tag_debug("SEM", spi_out, spi_out[i]+4);
                    return NO_ERROR;
                }
            }
        }

#if 0
//        else if ((spi_out[spi_out[1]+3] == 0x00) && (spi_out[spi_out[1]] != 0x00)) {
        else if (spi_out[spi_out[1]+3] == 0x00) {
            int i = 0;
           LOGI("engage  workaround");

            if (spi_out[1] > 0x0F) {
                memcpy(spi_in, spi_out, 17);
                memcpy(spi_in+18, spi_out+17, spi_out[1]-13);

                for (i = 0; i < 256; i++) {
                    spi_in[17] = i;

                    if (spi_check_frame(spi_in, spi_in[1]+4) == 0) {
                        LOGD("recovered");
                        memcpy(spi_out, spi_in, spi_in[1]+4);
                        return NO_ERROR;
                    }
                }
            }

            if (spi_out[1] > 0x0E) {
                memcpy(spi_in, spi_out, 16);
                memcpy(spi_in+17, spi_out+16, spi_in[1]-12);

                for (i = 0; i < 256; i++) {
                    spi_in[16] = i;

                    if (spi_check_frame(spi_in, spi_in[1]+4) == 0) {
                        LOGD("recovered");
                        memcpy(spi_out, spi_in, spi_in[1]+4);
                        return NO_ERROR;
                    }
                }
            }

            if (spi_out[1] > 0x0D) {
                memcpy(spi_in, spi_out, 15);
                memcpy(spi_in+16, spi_out+15, spi_in[1]-11);

                for (i = 0; i < 256; i++) {
                    spi_in[15] = i;

                    if (spi_check_frame(spi_in, spi_in[1]+4) == 0) {
                        LOGD("recovered");
                        memcpy(spi_out, spi_in, spi_in[1]+4);
                        return NO_ERROR;
                    }
                }
            }
            if (spi_out[1] > 0x0C) {
                memcpy(spi_in, spi_out, 14);
                memcpy(spi_in+15, spi_out+14, spi_in[1]-10);

                for (i = 0; i < 256; i++) {
                    spi_in[14] = i;

                    if (spi_check_frame(spi_in, spi_in[1]+4) == 0) {
                        LOGD("recovered");
                        memcpy(spi_out, spi_in, spi_in[1]+4);
                        return NO_ERROR;
                    }
                }
            }
            return CRC_ERROR;
        }
#endif
        return CRC_ERROR;
    }

    return NO_ERROR;
}



/**
* This method implements command and response algorithm with active polling
* int fd is the file descriptor corresponding the the file representing the secure element in the file system
* uint8_t* spi_in is the buffer containing the buffer to transmit
* uint8_t* spi_out is the buffer that will contain the command received
* uint16_t len is the lenght of the buffer
* uint16_t timeout is the time out definition
* uint8_t mode is the mode the method shall be used :
*            MODE_TRANSCIEVE allows to receive and transmit at the same time
*
*/

int spi_send_raw_activepolling_mode (uint8_t* spi_in,  uint8_t* spi_out,uint16_t len, uint16_t timeout, uint8_t mode) {
#ifdef USE_MOBICORE
    timestamp_t startTime;
    timestamp_t currentTime;
#endif
#ifdef USE_QSEE
    unsigned long long startTime;
	unsigned long long currentTime;
#endif
    uint8_t garbage_tx = 0;
    uint8_t garbage_rx = 0;
    uint16_t curOutBuf = 0, i=0;
    int result = 0;

    if (iso7816_spi_mode0() == 1) {
        // send garbage data for frame guard time
        LOGI("garbage data transceive");
        result = spi_transcieve(&garbage_tx, &garbage_rx, 1,DEBUG_MODE);
        if (result != 0) {
            LOGE("spi_transcieve error");
            return ERROR_SPI_SECDRV;
        }
    }
    // enable CS
    // clean transmission buffer
    memset(frame_out, 0x00, SPI_MAX_FRAME_SIZE);
    memset(frame_in, 0x00, SPI_MAX_FRAME_SIZE);
    // copy incoming frame into working buffer
    memcpy(frame_out, spi_in, len);
    // transcieve the data
    result = spi_transcieve(frame_out, frame_in, len,DEBUG_MODE);
    if (result != 0) {
        LOGE("spi_transcieve error");
        return ERROR_SPI_SECDRV;
    }
    // mode transmit
    if (mode == 1) {
        memset(frame_in, 0x00, SPI_MAX_FRAME_SIZE); // forget about eSE response
    }
    // check if we get a response

    if (i!=len) {
        for (i = 0; i < len; i++) {
            if (frame_in[i] != 0) {
                // we pulled only one byte
                if (len-i == 1) {
                    // shift mode pulling procedure
                    spi_out[curOutBuf++] = frame_in[i];
                    memset(frame_out, 0x00, SPI_MAX_FRAME_SIZE);
                    // pull 1 byte
                    result  = spi_transcieve(frame_out, frame_in, SPI_MAX_FRAME_SIZE,DEBUG_MODE);
                    if (result != 0) {
                        LOGE("spi_transcieve error");
                        return ERROR_SPI_SECDRV;
                    }
                    memcpy(spi_out+curOutBuf, frame_in,spi_out[1]+4);
                    return spi_check_raw_frame(frame_out, spi_out)?CRC_FIRST_ERROR:NO_ERROR;
                } else {
                    int remain = frame_in[i+1]+2 - (len-i)+2;

                    // we need to check the number of byte to pull
                    if (remain <= 0) {
                        // complete frame has been received
                        memcpy(spi_out, frame_in+i, frame_in[i+1]+4);
                        // close SPI
                        return spi_check_raw_frame(frame_out,spi_out)?CRC_ERROR:NO_ERROR;
                    } else {
                        // we have received part of the frame, but not all
                        memcpy(spi_out, frame_in+i, len-i);
                        // pull the rest of the frame
                        curOutBuf = len-i;
                        memset(frame_out, 0, spi_out[1]+2);
                        result  = spi_transcieve(frame_out, frame_in, remain,DEBUG_MODE);
                        if (result != 0) {
                            LOGE("spi_transcieve error");
                            return ERROR_SPI_SECDRV;
                        }
                        // copy the rest of the frame in the output buffer
                        memcpy(spi_out+curOutBuf, frame_in,spi_out[1]+4);
                        // close SPI
                        return spi_check_raw_frame(frame_out, spi_out)?CRC_FIRST_ERROR:NO_ERROR;
                    }
                }
            }
        }
    }

    frame_out[0]=0;

    // nothing found .. enter active polling mode
    while (frame_in[0] == 0   && timeout > 0 ) {
        timeout= timeout - 1;
        frame_out[0]=0;
        // sleep for POLL INTERVAL
#ifdef USE_MOBICORE
        if (TLAPI_OK != tlApiGetSecureTimestamp (&startTime)) {
            return TIMER_ERROR;
		}
        do{
            if (TLAPI_OK != tlApiGetSecureTimestamp (&currentTime)) {
                LOGE("timer error");
                return TIMER_ERROR;
            }
        } while((startTime != 0) &&((currentTime-startTime) < POLL_INTERVAL));
#endif
#ifdef USE_QSEE
        startTime = qsee_get_uptime();
        if (startTime == 0) {
            return TIMER_ERROR;
        }
        do {
            currentTime = qsee_get_uptime();
            if (currentTime == 0) {
                return TIMER_ERROR;
            }
        } while((startTime != 0) && ((currentTime-startTime) < POLL_INTERVAL_MS));
#endif
        result = spi_transcieve(frame_out, frame_in, 1, NO_DEBUG_MODE);
        if (result != 0) {
            LOGE("spi_transcieve error");
            return ERROR_SPI_SECDRV;
        }
    }

    if (timeout == 0 && frame_in[0] == 0 ) {
        return TIME_OUT;
    }
    // pull frame length
    // we need to pull one byte more, and the rest of the frame
    spi_out[curOutBuf++] = frame_in[0];
    frame_out[0] = 0;
    // pull 1 byte for frame length
    result = spi_transcieve(frame_out, frame_in, 1, DEBUG_MODE);
    if (result != 0) {
        LOGE("spi_transcieve error");
        return ERROR_SPI_SECDRV;
    }
    // length retrieved
    spi_out[curOutBuf++] = frame_in[0];
    // pull the end of the frame
    memset(frame_out, 0, frame_in[0]+2);

    /* Workaround patch for "00 byte insertion issue(SPI+SWP) about CRC ERROR"
     * Apply the patch only for PEARL v3.0 product(2.0 = ZEN, NOBLE).
     */
#ifdef EXYNOS_7420
    {
        int offset = 0;
        for (i = spi_out[1] + 2; i > 14; i = i - 14) {
            result = spi_transcieve(frame_out, frame_in + offset, 14, DEBUG_MODE);
            if (result != 0) {
                LOGE("spi_transcieve error");
                return ERROR_SPI_SECDRV;
            }
            offset = offset + 14;
        }

        result = spi_transcieve(frame_out, frame_in + offset, i, DEBUG_MODE);
        if (result != 0) {
            LOGE("spi_transcieve error");
            return ERROR_SPI_SECDRV;
        }
    }
#else
    result = spi_transcieve(frame_out, frame_in,frame_in[0]+2, DEBUG_MODE);
    if (result != 0) {
        LOGE("spi_transcieve error");
        return ERROR_SPI_SECDRV;
    }
#endif /* Workaround patch for "00 byte insertion issue(SPI+SWP) about CRC ERROR" */

    // copy the rest of the frame in the output buffer
    memcpy(spi_out+curOutBuf, frame_in, spi_out[1]+4);
    // close SPI
    return spi_check_raw_frame(frame_out, spi_out);
}

/**
* this method is used to send raw frames and received raw frame, and automatically uses either:
* - the active polling method, or
* - the bitshift method
* int fd is the file descriptor corresponding the the file representing the secure element in the file system
* uint8_t* spi_in is the buffer containing the buffer to transmit
* uint8_t* spi_out is the buffer that will contain the command received
* uint16_t len is the lenght of the buffer
* uint16_t timeout is the time out definition
*/
int spi_send_raw (uint8_t* spi_in,  uint8_t* spi_out, uint16_t len, uint16_t timeout, uint8_t mode) {
#if defined FAKE_SPI_DRIVER
    new_line();
#endif
    int ret = 0;
    ret = spi_send_raw_activepolling_mode(spi_in, spi_out, len, timeout, mode);
    return ret;
    // bitshifting method is not implemented because workaroudn is implemented  in open method
}

#define ALIVE_UNKNOWN 0
#define ALIVE_AND_BUSY 1
#define NOT_ALIVE 2

#define MAX_TIME_OUT 3

/**
* this method is used to send frames at the APDU level
* int fd is the file decriptor corresponding to the file on the file system
* uint8_t* apdu is the buffer containing the c-apdu to be sent
* uint16_t size is the APDU size
* uint8_t* rsp is the buffer containing the r-apdu
* uint16_t len is the len
* uint16_t* retLen is the response length
* returns error status or ESE_OK
*/

int spi_process_send(uint8_t* apdu, uint16_t size, uint8_t *rsp, uint16_t* retLen) {
    uint16_t cnt, ret, offset = 0, outOffset = 0, curTimeOut = 0, prevSize = 0;
    int crcErrorCounter = 0;
    int result = 0;
#ifdef USE_MOBICORE
    timestamp_t startTime;
    timestamp_t currentTime;
#endif
#ifdef USE_QSEE
    unsigned long long startTime;
    unsigned long long currentTime;
#endif

//    int isAlive = ALIVE_UNKNOWN;
    *retLen = 0;
    // enable CS


    prevSize = 5;
    cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
//    LOGD("cnt %x", cnt);
    ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 0.5seconds timeout
    LOGD("data received");
//#if 1
    // analyze response frame
spi_process:
    // CRC error management

    if (ret == CRC_ERROR || ret == CRC_FIRST_ERROR) {
        if ((fragment_in[0] == 0xFF) && (fragment_in[1] == 0xFF)) {
            LOGI("eSE has been powered off");
            return ESE_NOT_POWERED;
        }
#ifdef USE_MOBICORE
        if (TLAPI_OK != tlApiGetSecureTimestamp (&startTime)) {
            return TIMER_ERROR;
        }
        do{
            if (TLAPI_OK != tlApiGetSecureTimestamp (&currentTime)) {
                LOGE("timer error");
                return TIMER_ERROR;
            }
        } while((startTime != 0) &&((currentTime-startTime) < 100000));
#endif

#ifdef USE_QSEE
        startTime = qsee_get_uptime();
        if (startTime == 0) {
            return TIMER_ERROR;
        }
        do {
            currentTime = qsee_get_uptime();
            if (currentTime == 0) {
                return TIMER_ERROR;
            }
        } while((startTime != 0) && ((currentTime-startTime) < 100));
#endif

#if 1 // handling CRC error coming from the eSE
        // purge incoming data
        memset(fragment_out, 0x00, 256);
        result = spi_transcieve(fragment_out, fragment_in, 256, DEBUG_MODE);
        if (result != 0) {
            LOGE("spi_transcieve error");
            return ERROR_SPI_SECDRV;
        }
        if ((fragment_in[0] == 0xFF) && (fragment_in[1] == 0xFF)) {
            LOGI("eSE has been powered off");
            return ESE_NOT_POWERED;
        }
        LOGE("CRC error");

        if (crcErrorCounter > 3) {
            return CRC_ERROR;
        }

        crcErrorCounter++;
        // send NACK
        ret = spi_send_raw(spi_frame_nack_crc, fragment_in, 5, 300, MODE_TRANSMIT); // 2 seconds timeout
        curTimeOut = MAX_TIME_OUT;
        // increase crc counter
        goto spi_process;
#else
        return CRC_ERROR;
#endif // 0
    }

    if (ret == TIME_OUT) { // time out management
        if (curTimeOut == MAX_TIME_OUT) {
#if 1
            if (crcErrorCounter) {
                return CRC_ERROR;
            }
#endif // 0
            LOGD("max time out reached");
            /// report error to upper layder
            return TIME_OUT;
        } else {
            curTimeOut++;
            ret = spi_send_raw(spi_frame_NULL, fragment_in, 1, 300, MODE_TRANSCIEVE); // 2 seconds timeout
            goto spi_process;
// check alive mecanism is not used in the version of middleware, because eSE will always ack the first fragment
#if 0

            if(isAlive == ALIVE_AND_BUSY) {
                // eSE is alive, need more time to process
                // wait some time, and be ready to receive
                LOGD("S");
                curTimeOut++;
//                usleep(10000);
                ret = spi_send_raw(spi_frame_NULL, fragment_in, 1, 300, MODE_TRANSCIEVE); // 2 seconds timeout
                goto spi_process;
            } else if(isAlive == ALIVE_UNKNOWN) {
                // send check alive
                ret = spi_send_raw(spi_frame_check_alive, fragment_in, 5, 300, MODE_TRANSMIT);
                goto spi_process;
            } else {
                // secure element is idle, last frame not received, retransmit
                cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                goto spi_process;
            }
#endif
        }
    } else {
        // LOGD("analyze frame");
        // handle APDU channel
        if (ESE_PCB_GET_CHANNEL(fragment_in[0]) == ESE_PCB_APDU_CHANNEL) {
            if (ESE_PCB_GET_TYPE(fragment_in[0]) == ESE_PCB_BLOCK_CONTROL) {
                if (fragment_in[1] == 0) {
                    // LOGD("ACK");
                    // process ACK
                    // transmit next frame
                    offset += cnt-4;
                    // LOGD("new offset %x", offset);
                    prevSize = 0;
                    cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                    ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                    goto spi_process;
                } else if(fragment_in[2] == 0x4E) { // NACK
                    // process nack
                    // LOGD("NACK");
                    // retransmit current frame
                    cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                    ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                    goto spi_process;
                } else if (fragment_in[2] == 0x43) { // check alive response
                    if( fragment_in[3] == 0x01) {
                        // process alive and busy
                        LOGD("alive and busy");
                        // isAlive =ALIVE_AND_BUSY;
                        // give more time
                        ret = spi_send_raw(spi_frame_NULL, fragment_in, 1, 300, MODE_TRANSCIEVE); // 2 seconds timeout
                        goto spi_process;
                    } else if(fragment_in[3] == 0x00) { // idle
                        LOGD("idle");
                        // isAlive = ALIVE_UNKNOWN;
                        // retransmit current frame
                        cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                        ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                        goto spi_process;
                    } else {
                        LOGD("error check alive");
                        // give some time
                        // isAlive = ALIVE_UNKNOWN;
                        // retransmit previous frame
                        cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                        ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                        goto spi_process;
                    }
                }
            } // end process control block
            else if (ESE_PCB_GET_TYPE(fragment_in[0]) == ESE_PCB_BLOCK_LAST_END) {
                // end processing
                // LOGD("end processing");
                // copy returned data to rsp)
                // LOGD("%x %x", outOffset, fragment_in[1]);
                memcpy(rsp+outOffset, fragment_in+2, fragment_in[1]);
                *retLen += fragment_in[1];
                // hex_print_tag_debug("SEM", rsp, *retLen);
                // return
            } else if (ESE_PCB_GET_TYPE(fragment_in[0]) == ESE_PCB_BLOCK_LAST) {
                LOGD("last block received");
                // copy returned data to rsp
                memcpy(rsp+outOffset, fragment_in+2, fragment_in[1]);
                *retLen += fragment_in[1];
                // return
            } else if (ESE_PCB_GET_TYPE(fragment_in[0]) == ESE_PCB_BLOCK_CHAINED) {
                LOGD("block received");
                // copy data
                memcpy(rsp+outOffset, fragment_in+2, fragment_in[1]);
                // send ACK
                outOffset+= fragment_in[1];
                ret = spi_send_raw(spi_frame_ack, fragment_in, 4, 300, MODE_TRANSMIT); // 2 seconds timeout
                *retLen += outOffset;
                goto spi_process;
            } else {
                // unknown frame
                LOGD("unknown frame Apdu");
                // give some time
                ret = spi_send_raw(spi_frame_nack_crc, fragment_in, 5, 300, MODE_TRANSMIT); // 2 seconds timeout
                goto spi_process;

            }
        } else if (ESE_PCB_GET_CHANNEL(fragment_in[0]) == ESE_PCB_SPI_CHANNEL) {
            if (ESE_PCB_GET_TYPE(fragment_in[0]) == ESE_PCB_BLOCK_CONTROL) {
                if (fragment_in[1] == 0x02 && fragment_in[2] == 0x4E) {
                    LOGD("NACK");
                    // nack
                    // retransmit current frame
                    cnt = next_apdu_frame(apdu, size, offset, fragment_out, prevSize);
                    ret = spi_send_raw(fragment_out, fragment_in, cnt, 300, MODE_TRANSMIT); // 2 seconds timeout
                    goto spi_process;
                }
            } else {
                // unknown frame
                LOGD("unknown frame");
                // give some time
                ret = spi_send_raw(spi_frame_nack_crc, fragment_in, 5, 300, MODE_TRANSMIT); // 2 seconds timeout
                goto spi_process;
            }
        } else {
            // unknown channel
            LOGD("unknown channel");
            // give some time
            ret = spi_send_raw(spi_frame_nack_crc, fragment_in, 5, 300, MODE_TRANSMIT); // 2 seconds timeout
            goto spi_process;
        }
    }
//#endif

    return 0;
}

