/*
 * Copyright (C) 2012-2014 NXP Semiconductors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /**
 * \addtogroup spi_t1_protocol_implementation
 *
 * @{ */
#include <phNxpEseProtocol.h>
#include "tz_debug.h"
#include "tz_utils.h"

const uint8_t PH_SCAL_T1_MAXLEN = 254;
const uint8_t PH_SCAL_T1_HEADER_LEN = 0x03;
const uint8_t PH_SCAL_T1_CRC_LEN = 0x01;
const uint8_t PH_SCAL_T1_PCB_OFFSET = 0x01;
const uint8_t PH_SCAL_T1_DATA_LEN_OFFSET = 0x02;
uint8_t recv_chained_frame = 0x00;
extern phNxpEseP61_Control_t nxpesehal_ctrl;
STATIC ESESTATUS phNxpEseP61_SendFrame(uint8_t mode,uint32_t data_len, uint8_t *p_data);
STATIC uint8_t phNxpEseP61_ComputeLRC(unsigned char *p_buff, uint32_t offset,
        uint32_t length);
STATIC ESESTATUS phNxpEseP61_ProcessChainedFrame(uint32_t data_len, uint8_t *p_data);
STATIC ESESTATUS phNxpEseP61_ProcessAck(int32_t data_len, uint8_t *p_data);
STATIC void phNxpEseP61_SendtoUpper(ESESTATUS status, void *data);
STATIC ESESTATUS phNxpEseP61_CheckPCB(uint8_t pcb);
STATIC void phNxpEseP61_DecodePcb(uint8_t pcb, struct PCB_BITS *pcbbits);
STATIC ESESTATUS phNxpEseP61_DecodeStatus(PH_SCAL_T1_FRAME_T frame_type, uint8_t pcb,
        struct PCB_BITS *pcb_bits);
STATIC ESESTATUS phNxpEseP61_CheckLRC(uint32_t data_len, uint8_t *p_data);
STATIC ESESTATUS phNxpEseP61_SendRawFrame(uint32_t data_len, uint8_t *p_data);
STATIC void phNxpEseP61_ResetRecovery(void);
STATIC ESESTATUS phNxpEseP61_read_process(void);
void ese_stack_data_callback(ESESTATUS status, phNxpEseP61_data *eventData);


/**
 * \ingroup spi_t1_protocol_implementation
 * \brief It is used to process the request from JNI.
 * Based on the length, it will decide whether to processed as chained frame or as normal frame.
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

ESESTATUS phNxpEseP61_ProcessData(uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;
    if (data_len > PH_SCAL_T1_MAXLEN)
    {
        LOGD("Process Chained Frame");
        status = phNxpEseP61_ProcessChainedFrame(data_len, p_data);
    }
    else
    {
        LOGD("Process Single Frame");
        status = phNxpEseP61_SendFrame(PH_SCAL_T1_SINGLE_FRAME, data_len, p_data);
        if (ESESTATUS_SUCCESS == status)
        {
            nxpesehal_ctrl.current_operation = PH_SCAL_I_FRAME;
            status = phNxpEseP61_read_process();
        }
    }
    return status;

}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function is used to send the frame to TML.
 * It calculates the frame length and also updates frame with the sequence number, PCB field & data length etc.
 * \param[in]       uint8_t
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_SendFrame(uint8_t mode, uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;
    uint32_t frame_len = 0;
    uint8_t p_framebuff[MAX_DATA_LEN] = {0,};
    uint8_t pcb_byte = 0;
#ifdef DEBUG_LOW
	int i;
#endif
	//LOGD("phNxpEseP61_SendFrame start");

    if (0 == data_len || NULL == p_data)
    {
        LOGE("%s Error in buffer/len", __FUNCTION__);
        return status;
    }

    frame_len = (data_len + PH_SCAL_T1_HEADER_LEN + PH_SCAL_T1_CRC_LEN);

    /* frame the packet */
    p_framebuff[0] = 0x00; /* NAD Byte */

    if (PH_SCAL_T1_CHAINING == mode)
    {
        /* make B6 (M) bit high */
        pcb_byte |= PH_SCAL_T1_CHAINING;
    }

    /* Update the send seq no */
	LOGD("phNxpEseP61_SendFrame nxpesehal_ctrl.seq_counter = %02x", nxpesehal_ctrl.seq_counter);
    nxpesehal_ctrl.seq_counter = (nxpesehal_ctrl.seq_counter ^ 0x01);
	LOGD("phNxpEseP61_SendFrame nxpesehal_ctrl.seq_counter = %02x", nxpesehal_ctrl.seq_counter);
    pcb_byte |= (nxpesehal_ctrl.seq_counter << 6);
	LOGD("phNxpEseP61_SendFrame pcb_byte = %02x", pcb_byte);

    /* store the pcb byte */
    p_framebuff[1] = pcb_byte;
    /* store I frame length */
    p_framebuff[2] = data_len;
    /* store I frame */
    memcpy(&(p_framebuff[3]), p_data, data_len);
#ifdef DEBUG_LOW
	for (i=0 ; i<data_len ; i++)
	{
		LOGD("phNxpEseP61_SendFrame p_data[%d] = %02x", i,p_data[i]);
	}
#endif
    p_framebuff[frame_len - 1] = phNxpEseP61_ComputeLRC(p_framebuff, 0,
            (frame_len - 1));
    /* store last frame */
    nxpesehal_ctrl.last_frame_len = frame_len;
    memcpy(nxpesehal_ctrl.p_last_frame, p_framebuff, frame_len);

    status = phNxpEseP61_WriteFrame(frame_len, p_framebuff);
    if (ESESTATUS_SUCCESS != status)
    {
        LOGE("%s Error phNxpEseP61_WriteFrame", __FUNCTION__);
    }
    LOGD("%s Exiting %d", __FUNCTION__, status);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function is used to calculate the LRC (one byte).
 * LRC will be part the epilogue field conveys the error detection code of the block.
 * When LRC is used, exclusive-oring of all the bytes of the block from NAD to LRC inclusive shall give '00'. Any other value is invalid.
 *
 * \param[in]       unsigned char*
 * \param[in]       uint32_t
 * \param[in]       uint32_t
 *
 * \retval Calculated LRC (uint8_t)
 *
*/

STATIC uint8_t phNxpEseP61_ComputeLRC(unsigned char *p_buff, uint32_t offset,
        uint32_t length)
{
    uint32_t LRC = 0, i = 0;

    for (i = offset; i < length; i++)
    {
        LRC = LRC ^ p_buff[i];
    }
    return (uint8_t) LRC;
}

STATIC ESESTATUS phNxpEseP61_read_process(void)
{
    ESESTATUS wstatus = ESESTATUS_FAILED;
    int read_data_lenght = -1;
    int wtx_req_cnt = 0;

    nxpesehal_ctrl.read_buff_len = MAX_DATA_LEN;
    memset(&nxpesehal_ctrl.p_read_buff[0], 0x00, MAX_DATA_LEN);

    //LOGD("phNxpEseP61_read_process start");

again:
    read_data_lenght = phNxpEseP61_read(nxpesehal_ctrl.read_buff_len, &nxpesehal_ctrl.p_read_buff[0]);
    if(read_data_lenght == -ESESTATUS_RESPONSE_TIMEOUT)
    {
        phNxpEseP61_SendtoUpper(ESESTATUS_FAILED, NULL);
        wstatus = ESESTATUS_RESPONSE_TIMEOUT;
        LOGE("%s phNxpEseP61_read failed ESESTATUS_RESPONSE_TIMEOUT", __FUNCTION__);
    }
    else if (read_data_lenght < 4)
    {
        LOGE("%s phNxpEseP61_read failed read_data_lenght = %d", __FUNCTION__, read_data_lenght);
    }
    else
    {
        wstatus = phNxpEseP61_ProcessResponse(read_data_lenght, &nxpesehal_ctrl.p_read_buff[0]);
        if (wstatus == ESESTATUS_WTX_REQ) {
            LOGD("Call phNxpEseP61_read for further seq, wstatus = 0x%x", wstatus);
            if(wtx_req_cnt >= 2)
            {
                phNxpEseP61_SendtoUpper(ESESTATUS_FAILED, NULL);
                wstatus = ESESTATUS_RESPONSE_TIMEOUT;
                LOGE("%s phNxpEseP61_read failed ESESTATUS_RESPONSE_TIMEOUT by WTX\n", __FUNCTION__);
            }
            else
            {
                wtx_req_cnt++;
                goto again;
            }
        } else if (ESESTATUS_MORE_FRAME == wstatus
                || ESESTATUS_REVOCERY_STARTED == wstatus || wstatus == ESESTATUS_FRAME_RESEND
                || ESESTATUS_FRAME_RESEND_R_FRAME == wstatus || ESESTATUS_RESET_SEQ_COUNTER_FRAME_RESEND == wstatus)

        {
            LOGD("Call phNxpEseP61_read for further seq");
            goto again;
        }
    }
	//LOGD("phNxpEseP61_read_process end");
    return wstatus;
}
/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function is used to process chained frame. 
 * It will process the frame in a loop by sending max of 254 bytes at a time.
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_ProcessChainedFrame(uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;
    uint32_t total_length = data_len;
    uint32_t offset = 0;

    LOGD("%s Enter data_len %d", __FUNCTION__, data_len);

    do
    {
        nxpesehal_ctrl.current_operation = PH_SCAL_I_CHAINED_FRAME;

        status = phNxpEseP61_SendFrame(PH_SCAL_T1_CHAINING,
                PH_SCAL_T1_MAXLEN, (p_data + offset));
        if (ESESTATUS_SUCCESS == status)
        {
            LOGD("%s phNxpEseP61_SendFrame Success", __FUNCTION__);
            status = phNxpEseP61_read_process();
            LOGD("%s phNxpEseP61_read_process status = %d", __FUNCTION__, status);
            if (ESESTATUS_SEND_NEXT_FRAME != status)
            {
                LOGE("%s phNxpEseP61_read_process Failed", __FUNCTION__);
                break;
            }
            else
            {
                status = ESESTATUS_SUCCESS;
            }
        }
        else
        {
            LOGE("%s Error status %x", __FUNCTION__, status);
            break;
        }

        total_length = total_length - PH_SCAL_T1_MAXLEN;
        offset = offset + PH_SCAL_T1_MAXLEN;

    } while (total_length > PH_SCAL_T1_MAXLEN);

    if (ESESTATUS_SUCCESS == status)
    {
        nxpesehal_ctrl.current_operation = PH_SCAL_I_FRAME;
        LOGD("%s Sending last frame", __FUNCTION__);
        status = phNxpEseP61_SendFrame(PH_SCAL_T1_SINGLE_FRAME,
                total_length, (p_data + offset));
        if (ESESTATUS_SUCCESS != status)
        {
            LOGE("%s Error in last phNxpEseP61_SendFrame", __FUNCTION__);
        }
        else
        {
            status = phNxpEseP61_read_process();
        }
    }

    LOGD("%s status = 0x%x", __FUNCTION__, status);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function checks the LRC byte of the received frame.
 * It is for error detection.
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 * ESESTATUS_CRC_ERROR ---> if received LRC is different from calculated LRC
 *
*/

STATIC ESESTATUS phNxpEseP61_CheckLRC(uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_SUCCESS;
    uint8_t calc_crc = 0;
    uint8_t recv_crc = 0;

    recv_crc = p_data[data_len - 1];

    LOGD("%s Received CRC   0x%X", __FUNCTION__, recv_crc);
    /* calculate the CRC after excluding CRC  */
    calc_crc = phNxpEseP61_ComputeLRC(p_data, 0, (data_len -1));

    LOGD("%s Calculated CRC 0x%X", __FUNCTION__, calc_crc);
    if (recv_crc != calc_crc)
    {
        LOGE("%s Error CRC Check", __FUNCTION__);
        status = ESESTATUS_CRC_ERROR;
    }
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function decodes the status byte
 *
 * I-blocks are denoted as follows.
 * I(N(S),M)     I-block where N(S) is the send-sequence number and M is
 *               the more-data bit (see 11.6.2.2)
 * Na(S),Nb(S)   send-sequence numbers of I-blocks where indices a and b
 *              distinguish sources A and B
 *
 * R-blocks are denoted as follows.
 * R(N(R))       R-block where N(R) is the send-sequence number of the expected I-block
 *
 * S-blocks are denoted as follows. 
 * S(RESYNCH request)  S-block requesting a resynchronization. 
 * S(RESYNCH response) S-block acknowledging the resynchronization. 
 * S(IFS request)      S-block offering a maximum size of the information field. 
 * S(IFS response)     S-block acknowledging IFS. 
 * S(ABORT request)    S-block requesting a chain abortion. 
 * S(ABORT response)   S-block acknowledging the chain abortion. 
 * S(WTX request)      S-block requesting a waiting time extension. 
 * S(WTX response)     S-block acknowledging the waiting time extension.
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_DecodeStatus(PH_SCAL_T1_FRAME_T frame_type, uint8_t pcb,
        struct PCB_BITS *pcb_bits)
{
    ESESTATUS status = ESESTATUS_SUCCESS;
    int32_t error_code = (int32_t)(pcb & 0x3F); /*discard upper 2 bits */

    switch (frame_type)
    {
    case PH_SCAL_I_FRAME:
        LOGD("Recv Seq bit  = 0x%x", pcb_bits->bit7);
                recv_chained_frame = 0x00 | pcb_bits->bit7;

        LOGD("More Data bit = 0x%x", pcb_bits->bit6);
        if (pcb_bits->bit6)
        {
            LOGD("Chained Frame - RECV");
            status = ESESTATUS_MORE_FRAME;
        } else
        {
            LOGD("Last Frame");
            status = ESESTATUS_LAST_FRAME;
        }
        if (ESE_STATUS_RECOVERY == nxpesehal_ctrl.halStatus)
        {
            phNxpEseP61_ResetRecovery();
        }
        break;

    case PH_SCAL_R_FRAME:
        LOGD("Don't care  = 0x%x", pcb_bits->bit6);
        LOGD("N(R)  = 0x%x", pcb_bits->bit5);

        nxpesehal_ctrl.recv_seq_counter = 0;
        nxpesehal_ctrl.recv_seq_counter |= pcb_bits->bit5;

        LOGD("nxpesehal_ctrl.recv_seq_counter = 0x%x",nxpesehal_ctrl.recv_seq_counter);
        LOGD("nxpesehal_ctrl.seq_counter = 0x%x", nxpesehal_ctrl.seq_counter);
        if ((pcb_bits->lsb == 0x00) && (pcb_bits->bit2 == 0x00))
        {
            LOGD("%s Received ACK", __FUNCTION__);
        }
        else if ((pcb_bits->lsb == 0x01) && (pcb_bits->bit2 == 0x00))
        {
            LOGE(" Error : redundancy code error or a character parity = 0x%x", pcb_bits->lsb);
            status = ESESTATUS_PARITY_ERROR;
        }
        else if ((pcb_bits->lsb == 0x00) && (pcb_bits->bit2 == 0x01))
        {
            LOGE(" Error : any other error = 0x%x", pcb_bits->bit2);
            status = ESESTATUS_FAILED;
        }
        else
        {
            LOGE(" Error : Undefined");
            status = ESESTATUS_UNKNOWN_ERROR;
        }

        if (nxpesehal_ctrl.recv_seq_counter == nxpesehal_ctrl.seq_counter)
        {
            status = ESESTATUS_FRAME_RESEND;
            LOGE(" Resend the last frame as rec_seq counter is equal to send_seq_counter");

        }
        else if (PH_SCAL_I_CHAINED_FRAME == nxpesehal_ctrl.current_operation
                && PH_SCAL_R_FRAME != nxpesehal_ctrl.last_state
                && nxpesehal_ctrl.recv_seq_counter != nxpesehal_ctrl.seq_counter)
        {
            /* if chained send is going on move to next frame */
                LOGD("Send Next Chained frame");
                status = ESESTATUS_SEND_NEXT_FRAME;
        }
        else if (PH_SCAL_R_FRAME == nxpesehal_ctrl.last_state
                && nxpesehal_ctrl.recv_seq_counter != nxpesehal_ctrl.seq_counter)
        {
            LOGD("R frame Response received ");
            if (PH_SCAL_I_CHAINED_FRAME == nxpesehal_ctrl.current_operation)
            {
                nxpesehal_ctrl.last_state = PH_SCAL_I_FRAME;
                LOGD(" Send Next Chained frame");
                status = ESESTATUS_SEND_NEXT_FRAME;
            }
            else
            {
                if (nxpesehal_ctrl.halStatus == ESE_STATUS_RECOVERY)
                {
                    LOGD(" R Frame Received Resent the Last I Frame");
                    status = ESESTATUS_FRAME_RESEND;

                }
                else
                {
                    LOGD(" Resend R frame");
                    status = ESESTATUS_FRAME_RESEND_R_FRAME;
                }
            }

        }
        else
        {
            LOGE("Undefined behaviour");
        }
        if ((nxpesehal_ctrl.halStatus == ESE_STATUS_RECOVERY) &&
                ((status == ESESTATUS_FRAME_RESEND) || ESESTATUS_SEND_NEXT_FRAME == status))
        {
            LOGD(" Recovery is done");
            phNxpEseP61_ResetRecovery();
        }

        break;

    case PH_SCAL_S_FRAME:
    {
        LOGE("%s 0x%x", __FUNCTION__, error_code);
        switch (error_code)
        {
            case RESYNCH_REQ:
                LOGE("RESYNCH_REQ");
                status = ESESTATUS_RESYNCH_REQ;
            break;

            case RESYNCH_RES:
                LOGE("RESYNCH_RES");
                status = ESESTATUS_RESYNCH_RES;
            break;

            case IFS_REQ:
                LOGE("IFS_REQ");
                status = ESESTATUS_IFS_REQ;
            break;

            case IFS_RES:
                LOGE("IFS_RES");
                status = ESESTATUS_IFS_RES;
            break;

            case ABORT_REQ:
                LOGE("ABORT_REQ");
                status = ESESTATUS_ABORT_REQ;
            break;

            case ABORT_RES:
                LOGE("ABORT_RES");
                status = ESESTATUS_ABORT_RES;
            break;

            case WTX_REQ:
                LOGE("WTX_REQ");
                if (ESE_STATUS_RECOVERY == nxpesehal_ctrl.halStatus)
                {
                    phNxpEseP61_ResetRecovery();
                }

                status = ESESTATUS_WTX_REQ;
            break;

            case WTX_RES:
                LOGE("WTX_RES");
                status = ESESTATUS_WTX_RES;
            break;

            default:
                LOGE("ERROR Undefined");
                status = ESESTATUS_UNKNOWN_ERROR;
            break;
        }
    }

    break;

    default:
        LOGE("%s Wrong Frame ", __FUNCTION__);
        status = ESESTATUS_INVALID_PARAMETER;
        break;
    }
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function decodes PCB byte in bit-field. 
 * PCB is used in identifying the frame type.
 *
 * \param[in]       uint8_t
 * \param[in]       struct PCB_BITS*
 *
 * \retval void
 *
*/

STATIC void phNxpEseP61_DecodePcb(uint8_t pcb, struct PCB_BITS *pcbbits)
{
    memcpy(pcbbits, &pcb, sizeof(uint8_t));
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief It used to find out the frame type based on PCB field
 *
 * \param[in]       uint8_t
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_CheckPCB(uint8_t pcb)
{
    ESESTATUS status = ESESTATUS_SUCCESS;
    PH_SCAL_T1_FRAME_T frame_type;
    struct PCB_BITS pcb_bits;

    /*An information block (I-block) is used to convey information for use by the application layer.
     *  In addition, it conveys a positive or negative acknowledgment.*/

    /*A receive ready block (R-block) is used to convey a positive or negative acknowledgment.
     *  Its information field shall be absent.*/

    /*A supervisory block (S-block) is used to exchange control information between the
     * interface device and the card. Its information field may be present depending on
     * its controlling function.*/

    /*
     * Frame Type       MSB     B7      B6      B5  B4  B3  B2  B1
     *
     * I :              0       N(S)    M bit   (Future use)
     *
     * R :              1       0
     */

    /*Below function will store MSB in pcb_buff[1] and B7 in pcb_buff[0] */
    memset(&pcb_bits, 0x00, sizeof(struct PCB_BITS));
    LOGD("PCB Byte = 0x%X", pcb);
    phNxpEseP61_DecodePcb(pcb, &pcb_bits);

    if (0x00 == pcb_bits.msb)
    {
        LOGD("%s I-Frame Received", __FUNCTION__);
        frame_type = PH_SCAL_I_FRAME;

    }
    else if ((0x01 == pcb_bits.msb) && (0x00 == pcb_bits.bit7))
    {
        LOGD("%s R-Frame Received", __FUNCTION__);
        frame_type = PH_SCAL_R_FRAME;
    }
    else if ((0x01 == pcb_bits.msb) && (0x01 == pcb_bits.bit7))
    {
        LOGD("%s S-Frame Received", __FUNCTION__);
        frame_type = PH_SCAL_S_FRAME;
    }
    else
    {
        LOGE("%s Wrong-Frame Received", __FUNCTION__);
        status = ESESTATUS_INVALID_PARAMETER;
    }
    if (ESESTATUS_SUCCESS == status)
    {
        status = phNxpEseP61_DecodeStatus(frame_type, pcb, &pcb_bits);
    }
    return status;
}
/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function invokes the registered JNI callback with status and data
 *
 * \param[in]       ESESTATUS
 * \param[in]       void*
 *
 * \retval void
 *
*/

STATIC void phNxpEseP61_SendtoUpper(ESESTATUS status, void *data)
{

    LOGD("%s status 0x%x", __FUNCTION__, status);
    nxpesehal_ctrl.halStatus = ESE_STATUS_IDLE;
    nxpesehal_ctrl.recovery_counter = 0;
    //(nxpesehal_ctrl.p_ese_stack_data_cback)(status, data);
    ese_stack_data_callback(status, data);
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function processes the frame received from TML
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval void
 *
*/

ESESTATUS phNxpEseP61_ProcessResponse(uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status;
    static int retry_cnt = 0;
    memset(&nxpesehal_ctrl.p_rsp_data[0], 0x00,
            sizeof(nxpesehal_ctrl.p_rsp_data));

    nxpesehal_ctrl.rsp_len = data_len;
    memcpy(&nxpesehal_ctrl.p_rsp_data[0], p_data, data_len);

    LOGD("%s data len = %d", __FUNCTION__, data_len);
    /* Make NAD byte zero*/
    nxpesehal_ctrl.p_rsp_data[0] = 0x00;

    status = phNxpEseP61_ProcessAck(nxpesehal_ctrl.rsp_len, &nxpesehal_ctrl.p_rsp_data[0]);

    if (ESESTATUS_FRAME_RESEND == status || ESESTATUS_RESET_SEQ_COUNTER_FRAME_RESEND == status)
    {
        retry_cnt ++;
        if(retry_cnt > 2)
        {
            LOGE("%s Bad SMX State ", __FUNCTION__);
            retry_cnt = 0;
            phNxpEseP61_Action(ESESTATUS_ABORTED, 0, NULL);
            status = ESESTATUS_ABORTED;
        }
        else
        {
            if (ESESTATUS_RESET_SEQ_COUNTER_FRAME_RESEND == status)
            {
                /* reset the send seq counter */
                nxpesehal_ctrl.p_last_frame[1] &= 0xBF;
                nxpesehal_ctrl.seq_counter = 0x01;
            }

            LOGE("%s Resending frame count %d", __FUNCTION__, retry_cnt);
            status = phNxpEseP61_SendRawFrame(nxpesehal_ctrl.last_frame_len, nxpesehal_ctrl.p_last_frame);
			if(status == ESESTATUS_SUCCESS)
				status = ESESTATUS_RESET_SEQ_COUNTER_FRAME_RESEND;
        }
    }
    else
    {
        retry_cnt = 0;
    }
    LOGD("%s Exiting %d", __FUNCTION__, status);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief It takes appropriate action based on the error. 
 * Like for ESESTATUS_RESPONSE_TIMEOUT ----> Send/Resend R-Frame	
 *			ESESTATUS_WRITE_TIMEOUT ---> Chained Frame Ack timeout leads to recovery (abort) 
 *			ESESTATUS_INVALID_PARAMETER ----> Frame Resend 	
 *			ESESTATUS_CRC_ERROR/ESESTATUS_INVALID_PARAMETER ---> Frame Resend 
 *			ESESTATUS_MORE_FRAME ----> set chain flag to TRUE	
 * ......
 *
 * \param[in]       ESESTATUS
 * \param[in]       uint32_t
 * \param[in]       uint8_t *
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

ESESTATUS phNxpEseP61_Action(ESESTATUS action_evt, uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;
    STATIC bool_t chained_flag = FALSE;
    phNxpEseP61_data pRes;
    uint8_t recv_ack[4] = {0x00, 0x80, 0x00, 0x00};

    LOGD("%s - Enter action 0x%X - data_len %d !!!", __FUNCTION__, action_evt, data_len);
    if (nxpesehal_ctrl.recovery_counter > 3)
    {
        LOGD("%s Recovery counter reached max (abort)", __FUNCTION__);
        if (PH_SCAL_I_CHAINED_FRAME == nxpesehal_ctrl.current_operation)
        {
            LOGD("%s Chained Frame Ack timeout", __FUNCTION__);
        }
        status = ESESTATUS_ABORTED;
    }

    switch(action_evt)
    {
    case ESESTATUS_RESPONSE_TIMEOUT:
    case ESESTATUS_FRAME_RESEND_R_FRAME:

        nxpesehal_ctrl.halStatus = ESE_STATUS_RECOVERY;
        status = ESESTATUS_REVOCERY_STARTED;
        if (ESESTATUS_FRAME_RESEND_R_FRAME != action_evt)
        {
            LOGD("%s Send the R Frame with New Seq No", __FUNCTION__);
            /* Change the Seq counter of R Frame */
            nxpesehal_ctrl.seq_counter = (!nxpesehal_ctrl.seq_counter);
        }

        recv_ack[1] |= ((nxpesehal_ctrl.seq_counter) << 4);
        if (nxpesehal_ctrl.recovery_counter < 3)
        {
            nxpesehal_ctrl.recovery_counter++;
            recv_ack[3] = phNxpEseP61_ComputeLRC(recv_ack, 0x00, (sizeof(recv_ack) -1));
            nxpesehal_ctrl.last_state = PH_SCAL_R_FRAME;
            phNxpEseP61_SendRawFrame(sizeof(recv_ack), recv_ack);
        }
        else
        {
            LOGE("Recovery is not possible ");
            status = ESESTATUS_ABORTED;
        }

        break;

    case ESESTATUS_WRITE_TIMEOUT:
        if (PH_SCAL_I_CHAINED_FRAME == nxpesehal_ctrl.current_operation)
        {
            LOGD("%s Chained Frame Ack timeout", __FUNCTION__);
        }
        status = ESESTATUS_ABORTED;
        break;

    case ESESTATUS_CRC_ERROR:
        status = ESESTATUS_FRAME_RESEND;
        /* CRC byte is not equal to received */
        break;
    case ESESTATUS_INVALID_PARAMETER:
        status = ESESTATUS_FRAME_RESEND;
        /* if received wrong pcb type*/
        break;
    case ESESTATUS_MORE_FRAME:

        chained_flag = TRUE;
        /* acknowledge the frame and save data in linked list
         *
         */
        if (ESESTATUS_SUCCESS != phNxpEseP61_StoreDatainList(data_len, p_data))
        {
            LOGE("%s - Error storing chained data in list", __FUNCTION__);
        }
        else
        {
            //recv_ack[1] |= ((nxpesehal_ctrl.seq_counter) << 4);
            recv_ack[1] |= ((!recv_chained_frame) << 4);
            LOGD("%s Send ACK for chained frame 0x%x", __FUNCTION__, recv_ack[1]);
            recv_ack[3] = phNxpEseP61_ComputeLRC(recv_ack, 0x00, (sizeof(recv_ack) -1));
            status = phNxpEseP61_SendRawFrame(sizeof(recv_ack), recv_ack);
            if (ESESTATUS_SUCCESS == status)
            {
                status = ESESTATUS_MORE_FRAME;
            }
            else
            {
                status = ESESTATUS_FAILED;
            }

        }

        break;
    case ESESTATUS_LAST_FRAME:
        if (TRUE == chained_flag)
        {
            if (data_len > 0)
            {
                if (ESESTATUS_SUCCESS != phNxpEseP61_StoreDatainList(data_len, p_data))
                {
                    LOGE("%s - Error storing chained data in list", __FUNCTION__);
                }

                else
                {
                    status = ESESTATUS_SUCCESS;
                }
            }
        }
        else
        {
            status = ESESTATUS_SUCCESS;
        }
        break;

    case ESESTATUS_FRAME_RESEND:
        status = ESESTATUS_FRAME_RESEND;
        break;
    case ESESTATUS_SEND_NEXT_FRAME:
        if (PH_SCAL_I_CHAINED_FRAME == nxpesehal_ctrl.current_operation)
        {
            LOGD("%s Chained Frame Ack", __FUNCTION__);
            status = ESESTATUS_SEND_NEXT_FRAME;
        }
        break;
    case ESESTATUS_PARITY_ERROR:
        status = ESESTATUS_FRAME_RESEND;
        break;

    case ESESTATUS_FAILED:
        break;

    case ESESTATUS_UNKNOWN_ERROR:
        status =  ESESTATUS_ABORTED;
        break;

    case ESESTATUS_RESYNCH_REQ:
        status = ESESTATUS_FAILED;
        break;

    case ESESTATUS_RESYNCH_RES:
        status = ESESTATUS_FAILED;
        break;

    case ESESTATUS_IFS_REQ:
        status = ESESTATUS_IFS_REQ;
        break;

    case ESESTATUS_IFS_RES:
        break;

    case ESESTATUS_ABORT_REQ:
    case ESESTATUS_ABORT_RES:
        status = ESESTATUS_ABORTED;
        break;

    case ESESTATUS_WTX_REQ:
        status = ESESTATUS_WTX_REQ;
        break;

    case ESESTATUS_WTX_RES:
        status = ESESTATUS_WTX_RES;
        break;

    default:
        status = ESESTATUS_ABORTED;
        LOGE("%s Undefined Error code !!!", __FUNCTION__);
        break;
    }

    if ((ESESTATUS_FAILED == status) || (ESESTATUS_ABORTED == status))
    {
        LOGD("%s Error Resoponse", __FUNCTION__);
        phNxpEseP61_SendtoUpper(status, NULL);
        status = ESESTATUS_FAILED;
    }
    else if (TRUE == chained_flag && ESESTATUS_SUCCESS == status)
    {
        LOGD("%s Chained Frame Resoponse", __FUNCTION__);
        status = phNxpEseP61_GetData(&pRes.len, &pRes.p_data);
        if (ESESTATUS_SUCCESS == status)
        {
            LOGD("%s DataLen = %d", __FUNCTION__, pRes.len);
            phNxpEseP61_SendtoUpper(status, &pRes);
            tz_free(pRes.p_data);
        }
        else
        {
            LOGE("%s phNxpEseP61_GetData failed 0x%X", __FUNCTION__, status);
            phNxpEseP61_SendtoUpper(ESESTATUS_FAILED, NULL);
        }
        chained_flag = FALSE;

    }
    else if (FALSE == chained_flag && ESESTATUS_SUCCESS == status)
    {
        LOGD("%s Single Frame Resoponse", __FUNCTION__);
        pRes.len = data_len;
        pRes.p_data = p_data;
        phNxpEseP61_SendtoUpper(status, &pRes);

    }
    else if (ESESTATUS_WTX_REQ == status)
    {
        uint8_t wtx_frame[] = {0x00, 0xE3, 0x01, 0x01, 0xE3};
        LOGD("%s WTX Request", __FUNCTION__);
        status = phNxpEseP61_SendRawFrame(sizeof(wtx_frame), wtx_frame);
        if (ESESTATUS_SUCCESS == status)
        {
            status = ESESTATUS_WTX_REQ;
        }
        /* Triggern SPI service to re-set timer */
        phNxpEseP61_SendtoUpper(status, NULL);

    }

    LOGD("%s - Exit status 0x%x !!!", __FUNCTION__, status);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function checks the CRC and PCB byte
 *
 * \param[in]       int32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_ProcessAck(int32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;

    status = phNxpEseP61_CheckLRC(data_len, p_data);
    if (ESESTATUS_SUCCESS != status)
    {
        LOGE("%s ERROR in CRC", __FUNCTION__);
        goto clean_and_return;
    }

    status = phNxpEseP61_CheckPCB(p_data[PH_SCAL_T1_PCB_OFFSET]);

    clean_and_return:
    /* extract the first 3 byte and last CRC byte */
    status = phNxpEseP61_Action(status, (data_len - 4), &p_data[3]);
    LOGD("%s Exiting %d", __FUNCTION__, status);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function is used to send WTX to P61. 
 * If the card requires more than BWT (block waiting time) to process the previously received I-block, it transmits S(WTX request)
 * where INF (information field) conveys one byte encoding an integer multiplier of the BWT value. The interface device
 * shall acknowledge by S(WTX response) with the same INF.
 *
 * \param[in]       uint32_t
 * \param[in]       uint8_t*
 *
 * \retval On Success ESESTATUS_SUCCESS else proper error code
 *
*/

STATIC ESESTATUS phNxpEseP61_SendRawFrame(uint32_t data_len, uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;
    status = phNxpEseP61_WriteFrame(data_len, p_data);
    return status;
}

/**
 * \ingroup spi_t1_protocol_implementation
 * \brief This function resets the all recovery related flags like counter & halstatus (Open/Busy/Recovery/Idle/Close).
 *
 * \param[in]       void
 *
 * \retval void
 *
*/

STATIC void phNxpEseP61_ResetRecovery(void)
{
    LOGD("%s enter", __FUNCTION__);
    nxpesehal_ctrl.halStatus = ESE_STATUS_BUSY;
    nxpesehal_ctrl.recovery_counter = 0;
    LOGD("%s exit", __FUNCTION__);
}
/** @} */
