/*
 * Copyright (C)
 *
 * Handle T=1 Protocol according to ISO 7816-3 specification
 *
 *
 */


#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include "lrc.h"
#include "spi_common.h"
#include "spi_communication.h"
#include "tz_debug.h"
#include "tz_utils.h"
#include "spi_protocol.h"

#include "sec_spi_info.h"


/**
 * Global Variable to save sequence number
*/
uint8_t gIseqNum = 0x1;
uint8_t gRseqNum = 0;
uint8_t gImoreData = 0;

void resetSequenceNum(void) {
    /* */
    gIseqNum = 0x1;
    gRseqNum = 0x0;
}


/**
 * Check PCB byte of received information
 * Input param pcb contain PCB byte
 *             checkIBlock, if TRUE then comare with available I-Block
 *
 * Return ESESTATUS_SUCCESS if PCB value is valid
 *        ESESTATUS_INVALID_DATA if it is invalid
 *
*/
ESESTATUS checkPCBvalue(uint8_t pcb, uint8_t checkIBlock) {
    /* is it valid S-Block or R-Block */
    switch(pcb) {
    /* Available PCB value for R-block */
    case 0x80:
    case 0x82:
    case 0x90:
    case 0x92:

    /* Valid PCB value for S-block */
    case S_RESYNCH_RSP:
    case S_WTX_REQ:
    case S_WARM_RESET_RSP:
    case S_SLEEP_ENABLE_RSP:
    case S_SET_CONF_RSP:
    case S_RECOVERY_RSP:
        return ESESTATUS_SUCCESS;
//        break;

    default:
        break;
    }

    /* is it valid I-Block */
    if (checkIBlock == TRUE) {
        if ((pcb == 0x00) || (pcb == 0x20) || (pcb == 0x40) || (pcb == 0x60))
            return ESESTATUS_SUCCESS;
    }

    LOGD("[%s] incorrect pcb value(0x%x), checkIBlock(0x%x)", __func__, pcb, checkIBlock);
    return ESESTATUS_INVALID_DATA;
}


/**
 *
 * It send/receive SPI T=1 Frame.
 * This function will be called when try to send I-Block.
 * If the I-Block is longer than MAX_INF_FIELD_LEN, then split the frame
 * Input param p_data(data to send), data_len(len of data)
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - ESESTATUS_DRIVER_ERROR : device driver error
 *   - else proper error code
*/
ESESTATUS transceiveIFrame(uint8_t *p_inData, uint32_t len_inData,
                           uint8_t *p_outData, uint32_t *plen_outData ) {
    ESESTATUS status = ESESTATUS_SUCCESS;
    T1PROT_STATUS prev_t1status = T1PROT_ALL_FINISHED;
    uint32_t remain_len = len_inData;
    uint32_t inData_offset = 0;
    uint8_t tmpOutBuf[MAX_FRAME_BUF_LEN] = {0x0, };
    uint8_t cntBWT = 0;
    uint8_t cntWTX = 0;
    uint8_t wtx_inf_value = 0;
    uint8_t cntRblocksending = 0;
    uint8_t exitInnerLoop = TRUE;
    uint8_t exitOuttuerLoop = TRUE;
    uint8_t cntInnerLoop = 0;
    uint8_t rcvRseqNum;
    uint8_t isSeqMismatch = FALSE;
    *plen_outData = 0;

    // SEND command to wake up the eSE. It necessary in case of sleep mode.
    // status = send_NullByte();
    // LOGD("%s null byte sent", __func__);
    // if (ESESTATUS_SUCCESS != status) {
    // return status;
    // }

    //hex_print_tag_debug("[enter: transceiveIFrame]", p_inData, len_inData);
    do {
        if (remain_len > MAX_INF_FIELD_LEN) {
            gImoreData = 0x1;
            gIseqNum = (0x01^gIseqNum);
            prev_t1status = T1PROT_SEND_I_BLOCK_CHAINED;
            status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, MAX_INF_FIELD_LEN);
            exitOuttuerLoop = FALSE;
        } else {
            // it is last I-frame
            gImoreData = 0x0;
            gIseqNum = (0x01^gIseqNum);
            prev_t1status = T1PROT_SEND_I_BLOCK_LAST;
            status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, remain_len);
            exitOuttuerLoop =  TRUE;
        }

        if (ESESTATUS_SUCCESS != status) {
            LOGE("[%s] sending failed(0x%x)", __func__, status);
            return status;
        } else { /* SENDING SUCCEED */
            // Inner loop
            exitInnerLoop = FALSE;
            cntInnerLoop = 0;
            cntRblocksending = 0;
            cntWTX = 0;

            do {
                cntInnerLoop++;
                status = receive_Frame(tmpOutBuf);

                /* Check the status */
                /* CASE : input parameter is wrong or device return error */
                if ((ESESTATUS_INVALID_INPUT_PARAM == status) ||
                        (ESESTATUS_DEVICE_ERROR == status)) {
                    /* EXIT the function */
                    LOGE("[%s] receive_Frame fail. status is 0x%x", __func__, status);
                    return status;
                }

                /* CASE : To prevent infinite loop, check loop count. */
                if (cntInnerLoop > MAX_INNER_LOOP_CNT) {
                    /* EXIT the function */
                    status = ESESTATUS_INNER_LOOP_MAX;
                    LOGE("[%s] retry count(%d) exceed the max(%d)",
                         __func__, cntInnerLoop, MAX_INNER_LOOP_CNT);
                    return status;
                }

                /* CASE : No Response from the eSE */
                if (ESESTATUS_POLLING_TIME_OUT == status) {
                    cntBWT++;
                    LOGD("[%s] BWT time-out occurred [%d]", __func__, cntBWT);

                    if (cntBWT >= MAX_BWT_COUNT) {
                        /* BWT time-out occurred "MAX_BWT_COUNT" times. Return failure */
                        return ESESTATUS_BWT_TIMEOUT;
                    }

                    /* Send RESYNCH frame to resynchronize the transmission protocol */
                    status = send_SFrame(S_RESYNCH_REQ, NULL, 0);
                    prev_t1status = T1PROT_SEND_S_RESYNC_REQ;

                    if (ESESTATUS_SUCCESS != status) {
                        return status;
                    }

                    continue;
                }

                /* Receive the Frame successfully */
                cntBWT = 0;

                /* Check the data of Prologue and Epilog */
                /* CASE : received data is incorrect */
                if ( (tmpOutBuf[0] != NODE_ADDR_HOST) || \
                        (checkLRC(tmpOutBuf, tmpOutBuf[2]+4) != 0) || \
                        (checkPCBvalue(tmpOutBuf[1], TRUE) != ESESTATUS_SUCCESS)) {
                    // check retry counter.
                    if ( cntRblocksending > MAX_R_BLOCK_SENDING) {
                        /* Retry count reach to maximum value */
                        status = ESESTATUS_R_BLOCK_MAX;
                        return status;
                    }

                    /* SPI interface : communication error is very critical. Need to recover the problem */
                    /* Send 256 00 byte to flush the SPI buffer.
                       Send FFFFFFFF data to reset the interface
                       waiting S_RECOVERY_RSP from eSE. */
                    /* status = send_RawData(null_bytes, sizeof(null_bytes));
                    if (ESESTATUS_SUCCESS != status) {
                        return status;
                    }
                    */
                    /* There is communication error.
                       T=1 Protocol : send R(R_SEQNUM) */
                    LOGD("%s : prog or lrc incorrect", __func__);

                    if (prev_t1status == T1PROT_SEND_S_RESYNC_REQ)
                        status = send_SFrame(S_RESYNCH_REQ, NULL, 0);
                    else
                        status = send_RFrame(gRseqNum, R_ERROR_OTHER);

                    if (ESESTATUS_SUCCESS != status) {
                        return status;
                    }

                    cntRblocksending++;
                    continue;
                }

                /* CASE : R-block ACK */
                if ((tmpOutBuf[1] & 0xC0) == 0x80) {
                    LOGD("[%s] R-block(0x%x), prev_t1status(0x%x)", __func__, tmpOutBuf[1], prev_t1status);
                    rcvRseqNum = (tmpOutBuf[1] & 0x10) >> 4;
                    status = ESESTATUS_SUCCESS;

                    /* eSE didn't received the S-block. re-send the S-block */
                    if (prev_t1status == T1PROT_SEND_S_WTX_RSP) {
                        status = send_SFrame(S_WTX_RSP, &wtx_inf_value, 1);
                    } else if (prev_t1status == T1PROT_SEND_I_BLOCK_CHAINED) {
                        /* eSE didn't received the I-block successfully. */
                        if((gIseqNum == rcvRseqNum) && (isSeqMismatch == FALSE)) {
                            status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, MAX_INF_FIELD_LEN);
                            isSeqMismatch = TRUE;
                        } else if(gIseqNum != rcvRseqNum) {
                            exitInnerLoop = TRUE;
                            remain_len = remain_len - MAX_INF_FIELD_LEN;
                            inData_offset = inData_offset + MAX_INF_FIELD_LEN;
                        } else {
                            /* Send RESYNCH frame to resynchronize the transmission protocol */
                            status = send_SFrame(S_RESYNCH_REQ, NULL, 0);
                            prev_t1status = T1PROT_SEND_S_RESYNC_REQ;
                        }
                    } else if (prev_t1status == T1PROT_SEND_I_BLOCK_LAST) {
                        /* eSE didn't received the I-block successfully. */
                        if((gIseqNum == rcvRseqNum) && (isSeqMismatch == FALSE)) {
                            status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, remain_len);
                            isSeqMismatch = TRUE;
                            /* the Sequence Counter is incorrect. */
                        } else {
                            /* Send RESYNCH frame to resynchronize the transmission protocol */
                            status = send_SFrame(S_RESYNCH_REQ, NULL, 0);
                            prev_t1status = T1PROT_SEND_S_RESYNC_REQ;
                        }
                    } else if (prev_t1status == T1PROT_RECEIVE_I_BLOCK_CHAINED) {
                        /* AP didn't received the R-block successfully. */
                        status = send_RFrame(gRseqNum, R_ERROR_OTHER);
                    } else {
                        LOGE("[%s] unexpected case", __func__);
                        status = send_RFrame(gRseqNum, R_ERROR_OTHER);
                    }

                    if (ESESTATUS_SUCCESS != status) {
                        return status;
                    }

                    /* CASE : I-block received */
                } else if ((gImoreData == 0x0) && ((tmpOutBuf[1] & 0x80) == 0x0)) {
                    memcpy(p_outData + (*plen_outData), tmpOutBuf + 3, tmpOutBuf[2]);
                    *plen_outData = (*plen_outData) + tmpOutBuf[2];
                    /* Received I(N, x) successfully. So next expected block is N+1 */
                    gRseqNum = (tmpOutBuf[1] & 0x40) >> 6;
                    gRseqNum = (0x01^gRseqNum);

                    /* if I Block has more bit then send R(seq_num_recv) */
                    if((tmpOutBuf[1] & 0x20) == 0x20) {
                        prev_t1status = T1PROT_RECEIVE_I_BLOCK_CHAINED;
                        /* I(0, 1) received then send R(1) to eSE */
                        status = send_RFrame(gRseqNum, R_ERROR_FREE);

                        if (ESESTATUS_SUCCESS != status)
                            return status;
                    } else {
                        /* if I block is last one then return from this function */
                        /* I block send/receive finished, don't need to set t1status */
                        exitInnerLoop = TRUE;
                    }

                    /* CASE : S-Block S_WTX_REQ received. */
                } else if (tmpOutBuf[1] == S_WTX_REQ) {
                    /* inner loop not finished */
                    cntWTX++;
                    wtx_inf_value = tmpOutBuf[3];
                    LOGD("[%s] Receive WTX Request, current count[%d], value[%d]",
                         __func__, cntWTX, wtx_inf_value);

                    if (cntWTX >= MAX_WTX_COUNT) {
                        LOGE("[%s] WTX Request[%d] reach to MAX_WTX_COUNT[%d]",
                             __func__, cntWTX, MAX_WTX_COUNT);
                        /* Reach to MAX_WTX_COUNT */
                        /* if AP didn��?t send S_WTX_RSP to eSE, then eSE will answer with a new S_WTX_REQ
                           to any new received frame, except for re-init/reset commands */
                        status = send_SFrame(S_WARM_RESET_REQ, NULL, 0);

                        if (ESESTATUS_SUCCESS != status)
                            return status;

                        status = receive_Frame(tmpOutBuf);

                        if (ESESTATUS_SUCCESS != status)
                            return status;

                        return ESESTATUS_ESE_LONG_PROCESSING;
                    }

                    prev_t1status = T1PROT_SEND_S_WTX_RSP;
                    status = send_SFrame(S_WTX_RSP, &wtx_inf_value, 1);

                    if (ESESTATUS_SUCCESS != status)
                        return status;

                    /* CASE : S-Block S_RESYNCH_RSP received. Retransmit it from the beginning */
                } else if(tmpOutBuf[1] == S_RESYNCH_RSP) {
                    /* Receive S_RESYNCH_RSP */
                    resetSequenceNum();
                    inData_offset = 0;
                    remain_len =  len_inData;
                    isSeqMismatch = FALSE;
                    *plen_outData = 0;

                    /* Send I-frame from beginning */
                    if (remain_len > MAX_INF_FIELD_LEN) {
                        gImoreData = 0x1;
                        gIseqNum = (0x01^gIseqNum);
                        prev_t1status = T1PROT_SEND_I_BLOCK_CHAINED;
                        status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, MAX_INF_FIELD_LEN);
                        exitOuttuerLoop = FALSE;
                    } else {
                        // it is last I-frame
                        gImoreData = 0x0;
                        gIseqNum = (0x01^gIseqNum);
                        prev_t1status = T1PROT_SEND_I_BLOCK_LAST;
                        status = send_IFrame(gIseqNum, gImoreData, p_inData + inData_offset, remain_len);
                        exitOuttuerLoop =  TRUE;
                    }

                    /* S-Block cycle finished */
                } else {
                    LOGE("[%s] unexpected cases", __func__);
                    return ESESTATUS_CRITICAL;
                }
            } while (exitInnerLoop == FALSE);
        }
    } while (exitOuttuerLoop == FALSE);

    return status;
}


/**
 *
 * It send/receive SPI T=1 Frame.
 * This function will be called when try to send S-frame.
 * Input param p_data(data to send), data_len(len of data)
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - ESESTATUS_DRIVER_ERROR : device driver error
 *   - else proper error code
*/
ESESTATUS transceiveSFrame(uint8_t controlbyte, uint8_t *p_data, uint32_t data_len,
                           uint8_t *p_outData, uint32_t *plen_outData) {
    ESESTATUS status = ESESTATUS_SUCCESS;
    uint8_t tmpOutBuf[MAX_FRAME_BUF_LEN] = {0x0, };
    uint8_t cntBWT = 0;
    uint8_t cntWTX = 0;
    uint8_t wtx_inf_value = 0;
    uint8_t exitInnerLoop = TRUE;
    uint8_t cntInnerLoop = 0;
    uint8_t recover_req[4] = {0xff, 0xff, 0xff, 0xff};

    if (controlbyte == S_SET_CONF_REQ) {
        if ((NULL == p_outData) || (NULL == p_data))
            return ESESTATUS_INVALID_INPUT_PARAM;

        if (NULL == plen_outData)
            return ESESTATUS_INVALID_INPUT_PARAM;

        *plen_outData = 0;
    }

    // SEND command to wake up the eSE. checn driver error.
    // status = send_NullByte();
    // if (ESESTATUS_SUCCESS != status)
    // {
    // return status;
    // }

    // in case of S_SET_CONF_REQ, data is necessary.
    if (controlbyte == S_RECOVERY_REQ) {
        status = send_RawData(recover_req, sizeof(recover_req));
        // set status
    } else {
        status = send_SFrame(controlbyte, p_data, data_len);
    }

    if (ESESTATUS_SUCCESS == status) {
        // loop
        exitInnerLoop = FALSE;
        cntWTX = 0;

        do {
            cntInnerLoop++;
            status = receive_Frame(tmpOutBuf);

            /* CASE : To prevent infinite loop, check loop count. */
            if (cntInnerLoop > MAX_INNER_LOOP_CNT) {
                /* EXIT the function */
                status = ESESTATUS_INNER_LOOP_MAX;
                LOGE("[%s] retry count(%d) exceed the max(%d)",
                     __func__, cntInnerLoop, MAX_INNER_LOOP_CNT);
                return status;
            }

            if (ESESTATUS_SUCCESS == status) {
                cntBWT = 0;

                if ( (tmpOutBuf[0] != NODE_ADDR_HOST) || \
                        (checkLRC(tmpOutBuf, tmpOutBuf[2]+4) != 0) || \
                        (checkPCBvalue(tmpOutBuf[1], FALSE) != ESESTATUS_SUCCESS)) {
                    if (controlbyte == S_RECOVERY_REQ) {
                        status = send_RawData(recover_req, sizeof(recover_req));
                        // set status
                    } else {
                        status = send_SFrame(controlbyte, p_data, data_len);
                    }

                    /* There is communication error.
                      T=1 Protocol : send R((R_SEQNUM) and increase counter
                      But communication error is very critical SPI interface.
                      So need to recover the problem */
                    /* Send 256 00 byte to flush the SPI buffer.
                       Send FFFFFFFF data to reset the interface
                       waiting S_RECOVERY_RSP from eSE. */
                    /* increase the fail counter */
                } else if ((tmpOutBuf[1] & 0xC0) == 0xC0) {
                    /* S-block received : return this function */
                    if (tmpOutBuf[1] == S_SET_CONF_RSP) {
                        /* save response at p_outData */
                        memcpy(p_outData, tmpOutBuf + 3, tmpOutBuf[2]);
                        *plen_outData = (*plen_outData) + tmpOutBuf[2];
                    } else if (tmpOutBuf[1] == S_RESYNCH_RSP ) {
                        resetSequenceNum();
                    }

                    exitInnerLoop = TRUE;
                } else if ((tmpOutBuf[1] & 0xC0) == 0x80) {
                    /* R-block received : resend S-Block */
                    if (controlbyte == S_RECOVERY_REQ) {
                        status = send_RawData(recover_req, sizeof(recover_req));
                        // set status
                    } else {
                        status = send_SFrame(controlbyte, p_data, data_len);
                    }
                } else if (tmpOutBuf[1] == S_WTX_REQ) {
                    /* CASE : S-Block S_WTX_REQ received */
                    cntWTX++;
                    wtx_inf_value = tmpOutBuf[3];
                    LOGD("[%s] Receive WTX Request, current count[%d], value[%d]",
                         __func__, cntWTX, wtx_inf_value);

                    if (cntWTX >= MAX_WTX_COUNT) {
                        return ESESTATUS_ESE_LONG_PROCESSING;
                    }

                    status = send_SFrame(S_WTX_RSP, &wtx_inf_value, 1);
                }
            } else if (ESESTATUS_POLLING_TIME_OUT == status) {
                cntBWT++;

                if (cntBWT >= MAX_BWT_COUNT) {
                    return ESESTATUS_BWT_TIMEOUT;
                } else {
                    /* Re-send S-frame */
                    if (controlbyte == S_RECOVERY_REQ) {
                        status = send_RawData(recover_req, sizeof(recover_req));
                    } else {
                        status = send_SFrame(controlbyte, p_data, data_len);
                    }
                }
            }

            /* if there is communication problem, then exit the funciton */
            if (ESESTATUS_SUCCESS != status)
                return status;
        } while (exitInnerLoop == FALSE);
    } else {
        return status;
    }

    return status;
}



/**
 *
 * This function will do transceive APDU
 * The caller should prepare the buffer for response.
 * Input param cpdu(apdu to send), cpduLen(len of apdu), rpdu(buff to save response)
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - ESESTATUS_HAL_INVALID_PARAM : invalid input parameter
 *   - else proper error code
*/
ESESTATUS phOTEse_Transceive_apdu( uint8_t *cpdu, int cpduLen,
                                   p_secEse_7816_rpdu_t pRsp ) {
    ESESTATUS   ret;
    uint8_t *p_outData;
    uint32_t len_outData;
	LOGD("[enter] %s\n", __func__);

    /* check input parameter */
    if((cpdu == NULL) || (cpduLen < 1) || (pRsp == NULL)) {
        LOGE("%s : input parameter is wrong\n", __func__);
        return ESESTATUS_HAL_INVALID_PARAM;
    }

    p_outData = tz_malloc(LEN_NORMAL_APDU+3);

    if(p_outData == NULL) {
        return ESESTATUS_HAL_MALLOC_FAIL;
    }

    ret = transceiveIFrame(cpdu, cpduLen, p_outData, &len_outData);

    if (ret == ESESTATUS_SUCCESS) {
        if (len_outData < 2) {
            ret = ESESTATUS_HAL_RESPONSE_INCORRECT;
            LOGE("%s : received data is invalid", __func__);
        } else {
            pRsp->sw1 = p_outData[len_outData-2];
            pRsp->sw2 = p_outData[len_outData-1];
            memcpy(pRsp->pdata, p_outData, len_outData-2);
            pRsp->len = len_outData-2;
        }
    } else {
        LOGE("%s : transceiveIFrame faild error code : 0x%04X", __func__, ret);
    }

    tz_free(p_outData);

    LOGD("[exit] %s : ret 0x%x", __func__, ret);

    return ret;
}

/**
 *
 * This function will do transceive APDU
 * The caller should prepare the buffer for response.
 * Input param pCmd(data to send), pRsp(buff to save response)
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - ESESTATUS_HAL_INVALID_PARAM : invalid input parameter
 *   - ESESTATUS_HAL_MALLOC_FAIL : malloc failed
 *   - ESESTATUS_HAL_RESPONSE_INCORRECT : response length is incorrect
 *   - else proper error code
*/
ESESTATUS phOTEse_Transceive(p_secEse_7816_cpdu_t pCmd, p_secEse_7816_rpdu_t pRsp) {
    ESESTATUS ret;
    uint8_t *p_inData;
    uint8_t *p_outData;
    uint32_t inDataLen;
    uint32_t outDataLen;
    uint32_t len_outData;
    uint32_t offset;
    uint8_t isCase3E4E = FALSE;
    LOGD("[enter] %s\n", __func__);

    /* check input parameter */
    if((pCmd == NULL) || (pRsp == NULL)) {
        LOGE("%s : pCmd or pRsp is null\n", __func__);
        return ESESTATUS_HAL_INVALID_PARAM;
    }

    if ((pCmd->lc > 0) && (pCmd->cpdu_type > 1)) {
        LOGE("%s : lc > 0 and cpdu_type > 1 \n", __func__);
        return ESESTATUS_HAL_INVALID_PARAM;
    }

    if((pCmd->lc > 0) && (pCmd->pdata == NULL)) {
        LOGE("%s : lc > 0 but pdata is null\n", __func__);
        return ESESTATUS_HAL_INVALID_PARAM;
    }

    if (pCmd->le_type == 0) {
        outDataLen = 3;
    } else {
		if (pRsp->pdata == NULL) {
			LOGE("%s : pRsp pdata is null\n", __func__);
			return ESESTATUS_HAL_INVALID_PARAM;
		}

	    if (pCmd->le_type == 1) {
	        outDataLen = LEN_NORMAL_APDU + 3;
	    } else if (pCmd->le_type == 2) {
	        outDataLen = LEN_EXTEND_APDU + 3;
	    } else {
	        LOGE("%s : pCmd le_type is invalid(%d)\n", __func__, pCmd->le_type);
	        return ESESTATUS_HAL_INVALID_PARAM;
	    }
    }

    inDataLen = pCmd->lc + 10;
    //LOGD("%s : allocate memory. inDataLen = %d", __func__, inDataLen);
    p_inData = (uint8_t *)tz_malloc(inDataLen);

    if(p_inData == NULL)
        return ESESTATUS_HAL_MALLOC_FAIL;

    //LOGD("%s : allocate memory. outDataLen = %d", __func__, outDataLen);
	p_outData = (uint8_t *)tz_malloc(outDataLen);
    if(p_outData == NULL) {
        tz_free(p_inData);
        return ESESTATUS_HAL_MALLOC_FAIL;
    }

    offset = 0;
    p_inData[offset++] = pCmd->cla;
    p_inData[offset++] = pCmd->ins;
    p_inData[offset++] = pCmd->p1;
    p_inData[offset++] = pCmd->p2;

    /* see ISO7816-4, 5.2 Table 1 */
    if (pCmd->lc > 0) {
        if (pCmd->cpdu_type == 1) {
            isCase3E4E = TRUE;
            p_inData[offset++] = 0;
            p_inData[offset++] = (pCmd->lc & 0xFF00) >> 8;
            p_inData[offset++] = pCmd->lc & 0xFF;
        } else {
            p_inData[offset++] = pCmd->lc;
        }

        memcpy(p_inData + offset, pCmd->pdata, pCmd->lc);
        offset += pCmd->lc;
    }

    if (pCmd->le_type) {
        if (pCmd->le_type == 1) {
            p_inData[offset++] = pCmd->le;
        } else if (pCmd->le_type == 2 || pCmd->le_type == 3) {
            if (isCase3E4E == FALSE) p_inData[offset++] = 0x00;

            p_inData[offset++] = (pCmd->le == 0) ? 0 : (pCmd->le & 0xFF00) >> 8;
            p_inData[offset++] = (pCmd->le == 0) ? 0 : pCmd->le & 0xFF;
        }
    }

    ret = transceiveIFrame(p_inData, offset, p_outData, &len_outData);

    if (ret == ESESTATUS_SUCCESS) {
        if (len_outData < 2) {
            ret = ESESTATUS_HAL_RESPONSE_INCORRECT;
            LOGE("%s : received data is invalid", __func__);
        } else {
            pRsp->sw1 = p_outData[len_outData-2];
            pRsp->sw2 = p_outData[len_outData-1];
			if (pRsp->pdata != NULL)
            	memcpy(pRsp->pdata, p_outData, len_outData-2);
            pRsp->len = len_outData-2;
        }
    } else {
        LOGE("%s : transceiveIFrame faild error code : 0x%04X", __func__, ret);
    }
    tz_free(p_inData);
    tz_free(p_outData);

    LOGD("[exit] %s : ret 0x%x", __func__, ret);

    return ret;
}
/**
 *
 * This function send S_RESYNCH_REQ to SE to reset sequence number.
 * Input param NONE
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - else proper error code
*/
ESESTATUS phOTEse_Init(void) {
    ESESTATUS status;
    status = transceiveSFrame(S_RESYNCH_REQ, NULL, 0x0, NULL, 0x0);

    if (status != ESESTATUS_SUCCESS) {
        LOGE("%s : S_RESYNCH_REQ transceive failed", __func__);
    }

    return status;
}


/**
 *
 * This function send S_WARM_RESET_REQ to SE to trigger warm reset.
 * Input param NONE
 * Return the status of ESESTATUS.
 *   - ESESTATUS_SUCCESS for success
 *   - else proper error code
*/
ESESTATUS phOTEse_WarmReset(void) {
    ESESTATUS status;
    status = transceiveSFrame(S_WARM_RESET_REQ, NULL, 0x0, NULL, 0x0);

    if (status != ESESTATUS_SUCCESS) {
        LOGE("%s : S_WARM_RESET_REQ transceive failed", __func__);
    }

    return status;
}

#ifdef USE_QSEE
void setSpiConfiguration(qsee_spi_config_t* spi_config) {
    /**< SPI clock frequency */
    spi_config->max_freq = SPI_MAX_FREQ;
    /**< 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;
    /**< 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
}

int spi_write(const void *buf, size_t count) {
    qsee_spi_device_id_t deviceId;
    qsee_spi_config_t spi_config;
	qsee_spi_transaction_info_t spi_rx;
	qsee_spi_transaction_info_t spi_tx;
    int ret = 0;

    uint8_t rx_buffer[258] = {0,};
#ifdef GPIO_CONTROL
    int retval = 0;
    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
#ifdef CS_CONTROL
    eSEGpioCSEnable();
#endif

    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfiguration(&spi_config);

    spi_tx.buf_addr = (uint8_t*) buf;
    spi_tx.buf_len = count;
	spi_rx.buf_addr = rx_buffer;
	spi_rx.buf_len = count;

    ret = qsee_spi_full_duplex(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.buf_len;

#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif

#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
#ifdef CS_CONTROL
    eSEGpioCSDisable();
#endif

    return ret;
}

int spi_read(void *buf, size_t count) {
    qsee_spi_device_id_t deviceId;
    qsee_spi_config_t spi_config;
	qsee_spi_transaction_info_t spi_rx;
	qsee_spi_transaction_info_t spi_tx;
    int ret = 0;

    uint8_t tx_buffer[258] = {0,};

#ifdef GPIO_CONTROL
    int retval = 0;
    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
#ifdef CS_CONTROL
    eSEGpioCSEnable();
#endif

    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfiguration(&spi_config);

    spi_tx.buf_addr = tx_buffer;
    spi_tx.buf_len = count;
	spi_rx.buf_addr = (uint8_t*) buf;
	spi_rx.buf_len = count;

    ret = qsee_spi_full_duplex(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.buf_len;

#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif


#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
#ifdef CS_CONTROL
    eSEGpioCSDisable();
#endif

    return ret;
}
#endif

#ifdef USE_MOBICORE
void setSpiConfiguration(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;
}

int spi_write(const void *buf, size_t count) {
    uint32_t deviceId;
    spi_config_t spi_config;
	spi_transfer_t spi_rx;
	spi_transfer_t spi_tx;
    int ret = 0;

    uint8_t rx_buffer[258] = {0,};
    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfiguration(&spi_config);

    spi_tx.buf_addr = (uint8_t*) buf;
    spi_tx.buf_len = count;
	spi_rx.buf_addr = rx_buffer;
	spi_rx.buf_len = count;
    ret = tlApiSecSPIWriteRead(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.buf_len;
#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

int spi_read(void *buf, size_t count) {
    uint32_t deviceId;
    spi_config_t spi_config;
	spi_transfer_t spi_rx;
	spi_transfer_t spi_tx;
    int ret = 0;

    uint8_t tx_buffer[258] = {0,};
    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfiguration(&spi_config);

    spi_tx.buf_addr = tx_buffer;
    spi_tx.buf_len = count;
	spi_rx.buf_addr = (uint8_t*) buf;
	spi_rx.buf_len = count;
    ret = tlApiSecSPIWriteRead(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.buf_len;
#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

#endif

#ifdef USE_BLOWFISH

int spi_write(const void *buf, size_t count) {
    uint8_t rx_buffer[258] = {0,};
    TEE_Result ret = -1;

    TEES_SPITransfer spi_tx = {
        .bufAddr = (uint8_t*) buf,
        .bufLen = count,
        .transferredLen = 0
    };

    TEES_SPITransfer spi_rx = {
        .bufAddr = rx_buffer,
        .bufLen = count,
        .transferredLen = 0
    };

    ret = TEES_SPIWriteRead(handler, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.bufLen;
#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

int spi_read(void *buf, size_t count) {
    uint8_t tx_buffer[258] = {0,};
   TEE_Result ret = -1;

   TEES_SPITransfer spi_tx = {
        .bufAddr = tx_buffer,
        .bufLen = count,
        .transferredLen = 0
    };

    TEES_SPITransfer spi_rx = {
        .bufAddr = (uint8_t*) buf,
        .bufLen = count,
        .transferredLen = 0
    };

    ret = TEES_SPIWriteRead(handler, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.bufLen;
#ifdef DEBUG_LOW
    hex_print_tag_debug("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

#endif

