/*
 * Copyright (C) 2019 SAMSUNG S.LSI
 *
 * 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.
 */

#include <qsee_sync.h>

#ifdef ESE_U_BOOT_BUILD
#include <linux/string.h>
#include <malloc.h>
#else
#include <stdlib.h>
#include <string.h>
#endif

#include <ese_log.h>
#include <ese_util.h>
#include "ese_hal.h"
#include "ese_protocol.h"

#define LOG_TAG "ESE_PROTOCOL"

#define MASTER_ADDRESS			0x12
#define SLAVE_ADDRESS			0x21
#define MAX_POLL_COUNT			200
#define RESPONSE_BUFFER_SIZE	260

#define ESE_UDELAY				qsee_spin


static protocol_t *protocol = NULL;;

static uint8_t eseProtocol_computeLRC(unsigned char *p_buff, uint32_t offset, uint32_t length)
{
	uint32_t LRC = 0, i = 0;
	for (i = offset; i < offset + length; i++) {
		LRC = LRC ^ p_buff[i];
	}

	return (uint8_t) LRC;
}

static ESE_STATUS eseProtocol_checkLRC(uint8_t* p_data, uint32_t data_len)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	uint8_t calc_crc = 0;
	uint8_t recv_crc = 0;

	recv_crc = p_data[data_len - 1];

	/* calculate the CRC after excluding CRC  */
	calc_crc = eseProtocol_computeLRC(p_data, 0, (data_len - 1));

	if (recv_crc != calc_crc) {
		status = ESESTATUS_FAILED;
	}
	return status;
}

static ESE_STATUS eseProtocol_resetProtoParams(void)
{
	unsigned long int tmpWTXCountlimit = 0;
	unsigned long int tmpRNACKCountlimit = 0;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	tmpWTXCountlimit = protocol->wtx_counter_limit;
	tmpRNACKCountlimit = protocol->rnack_retry_limit;
	memset(protocol, 0x00, sizeof(protocol_t));
	protocol->wtx_counter_limit = tmpWTXCountlimit;
	protocol->rnack_retry_limit = tmpRNACKCountlimit;
	protocol->CurrentState = IDLE;
	protocol->nextTransceiveState = IDLE_STATE;
	protocol->Rx_Cntx.lastRcvdFrameType = INVALID;
	protocol->NextTx_Cntx.FrameType = INVALID;
	protocol->NextTx_Cntx.IframeInfo.maxDataLen = PROTOCOL_SEND_SIZE;
	protocol->NextTx_Cntx.IframeInfo.p_data = NULL;
	protocol->LastTx_Cntx.FrameType = INVALID;
	protocol->LastTx_Cntx.IframeInfo.maxDataLen = PROTOCOL_SEND_SIZE;
	protocol->LastTx_Cntx.IframeInfo.p_data = NULL;
	/* Initialized with sequence number of the last I-frame sent */
	protocol->NextTx_Cntx.IframeInfo.seqNo = 0x01;
	/* Initialized with sequence number of the last I-frame received */
	protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo = 0x01;
	/* Initialized with sequence number of the last I-frame received */
	protocol->LastTx_Cntx.IframeInfo.seqNo = 0x01;
	protocol->recoveryCounter = 0;
	protocol->timeoutCounter = 0;
	protocol->wtx_counter = 0;
	/* This update is helpful in-case a R-NACK is transmitted from the MW */
	protocol->lastSentNonErrorframeType = UNKNOWN;
	protocol->rnack_retry_counter = 0;
	protocol->recv_data.next = NULL;
	protocol->recv_data.data.buffer = NULL;
	protocol->recv_data.data.len = 0;
	return ESESTATUS_SUCCESS;
}

static ESE_STATUS eseProtocol_setFirstIframeContxt(void)
{
	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	protocol->NextTx_Cntx.IframeInfo.dataOffset = 0;
	protocol->NextTx_Cntx.FrameType = IFRAME;
	protocol->NextTx_Cntx.IframeInfo.seqNo = protocol->LastTx_Cntx.IframeInfo.seqNo ^ 1;
	protocol->nextTransceiveState = SEND_IFRAME;

	if (protocol->NextTx_Cntx.IframeInfo.totalDataLen > protocol->NextTx_Cntx.IframeInfo.maxDataLen) {
		protocol->NextTx_Cntx.IframeInfo.isChained = 1;
		protocol->NextTx_Cntx.IframeInfo.sendDataLen = protocol->NextTx_Cntx.IframeInfo.maxDataLen;
		protocol->NextTx_Cntx.IframeInfo.totalDataLen =
			protocol->NextTx_Cntx.IframeInfo.totalDataLen - protocol->NextTx_Cntx.IframeInfo.maxDataLen;
	} else {
		protocol->NextTx_Cntx.IframeInfo.sendDataLen = protocol->NextTx_Cntx.IframeInfo.totalDataLen;
		protocol->NextTx_Cntx.IframeInfo.isChained = 0;
	}

	return ESESTATUS_SUCCESS;
}

static ESE_STATUS eseProtocol_setNextIframeContxt(void)
{
	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	/* Expecting to reach here only after first of chained I-frame is sent and
	* before the last chained is sent */
	protocol->NextTx_Cntx.FrameType = IFRAME;
	protocol->nextTransceiveState = SEND_IFRAME;

	protocol->NextTx_Cntx.IframeInfo.seqNo = protocol->LastTx_Cntx.IframeInfo.seqNo ^ 1;
	protocol->NextTx_Cntx.IframeInfo.dataOffset =
		protocol->LastTx_Cntx.IframeInfo.dataOffset + protocol->LastTx_Cntx.IframeInfo.maxDataLen;
	protocol->NextTx_Cntx.IframeInfo.p_data = protocol->LastTx_Cntx.IframeInfo.p_data;
	protocol->NextTx_Cntx.IframeInfo.maxDataLen = protocol->LastTx_Cntx.IframeInfo.maxDataLen;

	// if  chained
	if (protocol->LastTx_Cntx.IframeInfo.totalDataLen > protocol->LastTx_Cntx.IframeInfo.maxDataLen) {
		protocol->NextTx_Cntx.IframeInfo.isChained = 1;
		protocol->NextTx_Cntx.IframeInfo.sendDataLen = protocol->LastTx_Cntx.IframeInfo.maxDataLen;
		protocol->NextTx_Cntx.IframeInfo.totalDataLen =
			protocol->LastTx_Cntx.IframeInfo.totalDataLen - protocol->LastTx_Cntx.IframeInfo.maxDataLen;
	} else {
		protocol->NextTx_Cntx.IframeInfo.isChained = 0;
		protocol->NextTx_Cntx.IframeInfo.sendDataLen = protocol->LastTx_Cntx.IframeInfo.totalDataLen;
	}
	return ESESTATUS_SUCCESS;
}

static ESE_STATUS eseProtocol_recoverySteps(void)
{
	/*CC
	 * TODO
	 */
	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

#if 1
	ESELOG_E("recovery step finish");
	protocol->nextTransceiveState = IDLE_STATE;
	protocol->recoveryCounter = PROTOCOL_ZERO;
#else
	if (protocol->recoveryCounter <= PROTOCOL_FRAME_RETRY_COUNT) {
		protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = INTF_RESET_REQ;
		protocol->NextTx_Cntx.FrameType = SFRAME;
		protocol->NextTx_Cntx.SframeInfo.sFrameType = INTF_RESET_REQ;
		protocol->nextTransceiveState = SEND_S_INTF_RST;
	} else { /* If recovery fails */
		protocol->nextTransceiveState = IDLE_STATE;
	}
#endif

	return ESESTATUS_SUCCESS;
}

#ifdef ESE_PROTOCOL_SFRAME
static ESE_STATUS eseProtocol_sendSFrame(sFrameInfo_t sFrameData)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	int frame_len = 0;
	uint8_t* p_framebuff = NULL;
	uint8_t pcb_byte = 0;
	int ret_bytes = 0;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	//ESELOG_D("Enter %s", __FUNCTION__);

	/* This update is helpful in-case a R-NACK is transmitted from the MW */
	protocol->lastSentNonErrorframeType = SFRAME;

	frame_len = (PROTOCOL_HEADER_LEN + PROTOCOL_LRC_LEN);
	p_framebuff = ESE_MALLOC(frame_len);
	if (NULL == p_framebuff) {
		ESELOG_E("malloc fail");
		return ESESTATUS_FAILED;
	}

	if ((sFrameData.sFrameType & 0x20 ) == 0) {
		//ESELOG_I("SFrame Req : %d", sFrameData.sFrameType);
		p_framebuff[2] = 0;
		p_framebuff[3] = 0x00;

		pcb_byte |= PROTOCOL_S_BLOCK_REQ; /* PCB */
		pcb_byte |= sFrameData.sFrameType;

	} else if ((sFrameData.sFrameType & 0x20) == 0x20) {
		//ESELOG_I("SFrame Res : %d", sFrameData.sFrameType);
		p_framebuff[2] = 0x01;
		p_framebuff[3] = 0x01;

		pcb_byte |= PROTOCOL_S_BLOCK_RSP;
		pcb_byte |= (sFrameData.sFrameType & 0x0f);
	} else {
		ESELOG_E("Invalid SFrame");
		ESE_FREE(p_framebuff);
		return ESESTATUS_INVALID_FORMAT;
	}

	/* frame the packet */
	p_framebuff[0] = MASTER_ADDRESS;     /* NAD Byte */
	p_framebuff[1] = pcb_byte; /* PCB */

	//ESELOG_I("Make SFrame  : [0x%x] [0x%x]", p_framebuff[0], p_framebuff[1]);

	p_framebuff[frame_len - 1] =
			eseProtocol_computeLRC(p_framebuff, 0, (frame_len - 1));
	ret_bytes = eseHal_Send(p_framebuff, frame_len);
	if (ret_bytes != frame_len) {
		status = ESESTATUS_WRITE_FAILED;
	}
	ESE_FREE(p_framebuff);
	//ESELOG_D("Exit %s Status 0x%x", __FUNCTION__, status);
	return status;
}
#endif

static ESE_STATUS eseProtocol_sendRframe(rFrameTypes_t rFrameType)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	uint8_t recv_ack[4] = {0x00, 0x80, 0x00, 0x00};
	int ret_bytes = 0;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	if (rFrameType == RNACK) {/* R-NACK */
		recv_ack[1] = 0x82;
	} else {/* R-ACK*/
		/* This update is helpful in-case a R-NACK is transmitted from the MW */
		protocol->lastSentNonErrorframeType = RFRAME;
	}
	recv_ack[0] = MASTER_ADDRESS;
	recv_ack[1] |= ((protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo ^ 1) << 4);
	recv_ack[3] = eseProtocol_computeLRC(recv_ack, 0x00, (sizeof(recv_ack) - 1));
	//ESELOG_I("%s recv_ack[1]:0x%x", __FUNCTION__, recv_ack[1]);
	ret_bytes = eseHal_Send(recv_ack, sizeof(recv_ack));
	if (ret_bytes != sizeof(recv_ack)) {
		status = ESESTATUS_WRITE_FAILED;
	}

	return status;
}
static ESE_STATUS eseProtocol_sendIframe(iFrameInfo_t iFrameData)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	int frame_len = 0;
	uint8_t* p_framebuff = NULL;
	uint8_t pcb_byte = 0;
	int ret_bytes = 0;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	if (iFrameData.sendDataLen == 0) {
		ESELOG_E("Iframe Len is 0, INVALID");
		return ESESTATUS_FAILED;
	}
	/* This update is helpful in-case a R-NACK is transmitted from the MW */
	protocol->lastSentNonErrorframeType = IFRAME;
	frame_len = (iFrameData.sendDataLen + PROTOCOL_HEADER_LEN + PROTOCOL_LRC_LEN);
	p_framebuff = ESE_MALLOC(frame_len);
	if (p_framebuff == NULL) {
		ESELOG_E("Heap allocation failed");
		return ESESTATUS_FAILED;
	}

	/* frame the packet */
	p_framebuff[0] = MASTER_ADDRESS; /* NAD Byte */

	if (iFrameData.isChained) { /* make B6 (M) bit high */
		pcb_byte |= PROTOCOL_CHAINING;
	}

	/* Update the send seq no */
	pcb_byte |= (protocol->NextTx_Cntx.IframeInfo.seqNo << 6);

	/* store the pcb byte */
	p_framebuff[1] = pcb_byte;
	/* store I frame length */
	p_framebuff[2] = iFrameData.sendDataLen;
	/* store I frame */
	memcpy(&(p_framebuff[3]),
			iFrameData.p_data + iFrameData.dataOffset, iFrameData.sendDataLen);

	p_framebuff[frame_len - 1] =
		eseProtocol_computeLRC(p_framebuff, 0, (frame_len - 1));

	ret_bytes = eseHal_Send(p_framebuff, frame_len);
	if (ret_bytes != frame_len) {
		status = ESESTATUS_WRITE_FAILED;
	}

	ESE_FREE(p_framebuff);

	return status;
}

static ESE_STATUS eseProtocol_decodeFrame(uint8_t* p_data, uint32_t data_len)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	uint8_t pcb;
	PCB_bits_t pcb_bits;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	pcb = p_data[PROTOCOL_PCB_OFFSET];
	memset(&pcb_bits, 0x00, sizeof(PCB_bits_t));
	memcpy(&pcb_bits, &pcb, sizeof(uint8_t));

	if (pcb_bits.msb == 0x00) {/* I-FRAME decoded should come here */
		//ESELOG_I("I-Frame Received");
		protocol->wtx_counter = 0;
		protocol->Rx_Cntx.lastRcvdFrameType = IFRAME;
		if (protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo != pcb_bits.bit7) {  //   != pcb_bits->bit7)
			//ESELOG_I("I-Frame lastRcvdIframeInfo.seqNo : 0x%x", pcb_bits.bit7);
			protocol->recoveryCounter = 0;
			protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo = 0x00;
			protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo |= pcb_bits.bit7;

			if (pcb_bits.bit6) {
				protocol->Rx_Cntx.lastRcvdIframeInfo.isChained = 1;
				protocol->NextTx_Cntx.FrameType = RFRAME;
				protocol->NextTx_Cntx.RframeInfo.errCode = NO_ERROR;
				status = eseData_storeData(&(protocol->recv_data), &p_data[3], data_len - 4, 1);
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}

				protocol->nextTransceiveState = SEND_R_ACK;
			} else {
				protocol->Rx_Cntx.lastRcvdIframeInfo.isChained = 1;
				protocol->nextTransceiveState = IDLE_STATE;
				status = eseData_storeData(&(protocol->recv_data), &p_data[3], data_len - 4, 1);
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
			}
		} else { //error
			ESE_UDELAY(DELAY_ERROR_RECOVERY);
			if (protocol->recoveryCounter < PROTOCOL_FRAME_RETRY_COUNT) {
				protocol->NextTx_Cntx.FrameType = RFRAME;
				protocol->NextTx_Cntx.RframeInfo.errCode = OTHER_ERROR;
				protocol->nextTransceiveState = SEND_R_NACK;
				protocol->recoveryCounter++;
			} else {
				status = eseProtocol_recoverySteps();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->recoveryCounter++;
			}
		}
	} else if ((pcb_bits.msb == 0x01) && (pcb_bits.bit7 == 0x00)) { /* R-FRAME decoded should come here */
		//ESELOG_I("R-Frame Received");
		protocol->wtx_counter = 0;
		protocol->Rx_Cntx.lastRcvdFrameType = RFRAME;
		protocol->Rx_Cntx.lastRcvdRframeInfo.seqNo = 0;  // = 0;
		protocol->Rx_Cntx.lastRcvdRframeInfo.seqNo |= pcb_bits.bit5;

		if ((pcb_bits.lsb == 0x00) && (pcb_bits.bit2 == 0x00)) {
			protocol->Rx_Cntx.lastRcvdRframeInfo.errCode = NO_ERROR;
			protocol->recoveryCounter = 0;
			if (protocol->Rx_Cntx.lastRcvdRframeInfo.seqNo !=
				protocol->LastTx_Cntx.IframeInfo.seqNo) {
				status = eseProtocol_setNextIframeContxt();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->nextTransceiveState = SEND_IFRAME;
			} else {
				// error handling.
				status = eseProtocol_recoverySteps();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->recoveryCounter++;
			}
		} else if (((pcb_bits.lsb == 0x01) && (pcb_bits.bit2 == 0x00)) ||/* Error handling 1 : Parity error */
			((pcb_bits.lsb == 0x00) && (pcb_bits.bit2 == 0x01))) { /* Error handling 2: Other indicated error */
			ESE_UDELAY(DELAY_ERROR_RECOVERY);
			if ((pcb_bits.lsb == 0x00) && (pcb_bits.bit2 == 0x01))
				protocol->Rx_Cntx.lastRcvdRframeInfo.errCode = OTHER_ERROR;
			else
				protocol->Rx_Cntx.lastRcvdRframeInfo.errCode = PARITY_ERROR;
			if (protocol->recoveryCounter < PROTOCOL_FRAME_RETRY_COUNT) {
				if (protocol->LastTx_Cntx.FrameType == IFRAME) {
					memcpy(&protocol->NextTx_Cntx,
						&protocol->LastTx_Cntx, sizeof(NextTx_Info_t));
					protocol->nextTransceiveState = SEND_IFRAME;
					protocol->NextTx_Cntx.FrameType = IFRAME;
				} else if (protocol->LastTx_Cntx.FrameType == RFRAME) {
					/* Usecase to reach the below case:
					I-frame sent first, followed by R-NACK and we receive a R-NACK with
					last sent I-frame sequence number*/
					if ((protocol->Rx_Cntx.lastRcvdRframeInfo.seqNo ==
						protocol->LastTx_Cntx.IframeInfo.seqNo) &&
						(protocol->lastSentNonErrorframeType == IFRAME)) {
							memcpy(&protocol->NextTx_Cntx,
								&protocol->LastTx_Cntx, sizeof(NextTx_Info_t));
							protocol->nextTransceiveState = SEND_IFRAME;
							protocol->NextTx_Cntx.FrameType = IFRAME;
					}
					/* Usecase to reach the below case:
					R-frame sent first, followed by R-NACK and we receive a R-NACK with
					next expected I-frame sequence number*/
					else if ((protocol->Rx_Cntx.lastRcvdRframeInfo.seqNo !=
							protocol->LastTx_Cntx.IframeInfo.seqNo) &&
							(protocol->lastSentNonErrorframeType == RFRAME)) {
						protocol->NextTx_Cntx.FrameType = RFRAME;
						protocol->NextTx_Cntx.RframeInfo.errCode = NO_ERROR;
						protocol->nextTransceiveState = SEND_R_ACK;
					}
					/* Usecase to reach the below case:
					I-frame sent first, followed by R-NACK and we receive a R-NACK with
					next expected I-frame sequence number + all the other unexpected
					scenarios */
					else {
						protocol->NextTx_Cntx.FrameType = RFRAME;
						protocol->NextTx_Cntx.RframeInfo.errCode = OTHER_ERROR;
						protocol->nextTransceiveState = SEND_R_NACK;
					}
				} else if (protocol->LastTx_Cntx.FrameType == SFRAME) {
					/* Copy the last S frame sent */
					memcpy(&protocol->NextTx_Cntx,
						&protocol->LastTx_Cntx, sizeof(LastTx_Info_t));
				}
				protocol->recoveryCounter++;
			} else {
				status = eseProtocol_recoverySteps();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->recoveryCounter++;
			}
			// resend previously send I frame
		} else if ((pcb_bits.lsb == 0x01) && (pcb_bits.bit2 == 0x01)) { /* Error handling 3 */
			ESE_UDELAY(DELAY_ERROR_RECOVERY);
			if (protocol->recoveryCounter < PROTOCOL_FRAME_RETRY_COUNT) {
				protocol->Rx_Cntx.lastRcvdRframeInfo.errCode = SOF_MISSED_ERROR;
				protocol->NextTx_Cntx = protocol->LastTx_Cntx;
				protocol->recoveryCounter++;
			} else {
				status = eseProtocol_recoverySteps();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->recoveryCounter++;
			}
		} else { /* Error handling 4 */
			ESE_UDELAY(DELAY_ERROR_RECOVERY);
			if (protocol->recoveryCounter < PROTOCOL_FRAME_RETRY_COUNT) {
				protocol->Rx_Cntx.lastRcvdRframeInfo.errCode = UNDEFINED_ERROR;
				protocol->recoveryCounter++;
			} else {
				status = eseProtocol_recoverySteps();
				if (status != ESESTATUS_SUCCESS) {
					return status;
				}
				protocol->recoveryCounter++;
			}
		}
#ifdef ESE_PROTOCOL_SFRAME
	} else if ((pcb_bits.msb == 0x01) && (pcb_bits.bit7 == 0x01)) {/* S-FRAME decoded should come here */
		//ESELOG_I("S-Frame Received");
		int32_t frameType = (int32_t)(pcb & 0x3F); /*discard upper 2 bits */
		protocol->Rx_Cntx.lastRcvdFrameType = SFRAME;
		if (frameType != WTX_REQ) {
			protocol->wtx_counter = 0;
		}
		switch (frameType) {
		case RESYNCH_REQ:
			protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = RESYNCH_REQ;
			break;
		case RESYNCH_RSP:
			protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = RESYNCH_RSP;
			protocol->NextTx_Cntx.FrameType = UNKNOWN;
			protocol->nextTransceiveState = IDLE_STATE;
			break;
		case ABORT_REQ:
			protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = ABORT_REQ;
			break;
		case ABORT_RES:
			protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = ABORT_RES;
			protocol->NextTx_Cntx.FrameType = UNKNOWN;
			protocol->nextTransceiveState = IDLE_STATE;
			break;
		case WTX_REQ:
			protocol->wtx_counter++;
			//ESELOG_I("wtx_counter : %lu wtx_counter_limit : %lu", protocol->wtx_counter, protocol->wtx_counter_limit);
			/* Previous sent frame is some S-frame but not WTX response S-frame */
			if (protocol->LastTx_Cntx.SframeInfo.sFrameType != WTX_RSP &&
				protocol->LastTx_Cntx.FrameType == SFRAME) { /* Goto recovery if it keep coming here for more than recovery counter max. value */
				if (protocol->recoveryCounter < PROTOCOL_FRAME_RETRY_COUNT) { /* Re-transmitting the previous sent S-frame */
					protocol->NextTx_Cntx = protocol->LastTx_Cntx;
					protocol->recoveryCounter++;
				} else {
					status = eseProtocol_recoverySteps();
					if (status != ESESTATUS_SUCCESS) {
						return status;
					}
					protocol->recoveryCounter++;
				}
			} else { /* Checking for WTX counter with max. allowed WTX count */
				if (protocol->wtx_counter == protocol->wtx_counter_limit) {
					#if 0 //Need to check
					protocol->wtx_counter = 0;
					protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = INTF_RESET_REQ;
					protocol->NextTx_Cntx.FrameType = SFRAME;
					protocol->NextTx_Cntx.SframeInfo.sFrameType = INTF_RESET_REQ;
					protocol->nextTransceiveState = SEND_S_INTF_RST;
					#endif
				} else {
					ESE_UDELAY(DELAY_ERROR_RECOVERY);
					protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = WTX_REQ;
					protocol->NextTx_Cntx.FrameType = SFRAME;
					protocol->NextTx_Cntx.SframeInfo.sFrameType = WTX_RSP;
					protocol->nextTransceiveState = SEND_S_WTX_RSP;
				}
			}
			break;
		case WTX_RSP:
			protocol->Rx_Cntx.lastRcvdSframeInfo.sFrameType = WTX_RSP;
			break;
		default:
			break;
		}
#endif
	} else {
		ESELOG_E("Wrong-Frame Received");
	}
	return status;
}

static ESE_STATUS eseProtocol_processResponse(void)
{
	uint32_t data_len = 0, rec_len = 0, ret_len = 0;
	uint32_t poll_cnt = MAX_POLL_COUNT;
	uint8_t p_data[RESPONSE_BUFFER_SIZE] = {0x00,};
	ESE_STATUS status = ESESTATUS_SUCCESS;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	do {
		ESE_UDELAY(2 * 1000);
		ret_len = eseHal_Receive(p_data, 1);
		if (ret_len != 1) {
			ESELOG_E("mismatch receive size %u, %u", 1, ret_len);
			status = ESESTATUS_FAILED;
			goto error;
		}
		poll_cnt--;
	} while (p_data[0] == 0x0 && poll_cnt > 0);

	if (poll_cnt == 0) {
		ESELOG_E("response timeout");
		status = ESESTATUS_FAILED;
		goto error;
	}
	data_len += ret_len;

	if (p_data[0] != SLAVE_ADDRESS) {
		ESELOG_E("invalid slave address : 0x%02x", p_data[0]);
		status = ESESTATUS_FAILED;
		goto error;
	}

	ret_len = eseHal_Receive(p_data + 1, 2);
	if (ret_len != 2) {
		ESELOG_E("mismatch receive size %u, %u", 2, ret_len);
		status = ESESTATUS_FAILED;
		goto error;
	}
	data_len += ret_len;

	rec_len = p_data[2] + 1;
	ret_len = eseHal_Receive(p_data + 3, rec_len);
	if (ret_len != rec_len) {
		ESELOG_E("mismatch receive size %u, %u", rec_len, ret_len);
		status = ESESTATUS_FAILED;
		goto error;
	}
	data_len += ret_len;

	ESELOG_I("p_data len -> 0x%x", data_len);

error:
	if (data_len > 0) {
		/* Resetting the timeout counter */
		protocol->timeoutCounter = PROTOCOL_ZERO;
		/* LRC check followed */
		if (status == ESESTATUS_SUCCESS) {
			status = eseProtocol_checkLRC(p_data, data_len);
			if (status != ESESTATUS_SUCCESS) {
				goto flush;
			}
		}
		if (status == ESESTATUS_SUCCESS) {
			/* Resetting the RNACK retry counter */
			protocol->rnack_retry_counter = PROTOCOL_ZERO;
			status = eseProtocol_decodeFrame(p_data, data_len);
			if (status != ESESTATUS_SUCCESS) {
				goto flush;
			}
		} else {
			ESELOG_E("LRC Check failed");
			if (protocol->rnack_retry_counter < protocol->rnack_retry_limit) {
				protocol->Rx_Cntx.lastRcvdFrameType = INVALID;
				protocol->NextTx_Cntx.FrameType = RFRAME;
				protocol->NextTx_Cntx.RframeInfo.errCode = PARITY_ERROR;
				protocol->NextTx_Cntx.RframeInfo.seqNo =
					(!protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo) << 4;
				protocol->nextTransceiveState = SEND_R_NACK;
				protocol->rnack_retry_counter++;
			} else {
				protocol->rnack_retry_counter = PROTOCOL_ZERO;
				/* Re-transmission failed completely, Going to exit */
				protocol->nextTransceiveState = IDLE_STATE;
				protocol->timeoutCounter = PROTOCOL_ZERO;
			}
		}
	} else {
		ESELOG_E("eseHal_Receive failed");
#ifdef ESE_PROTOCOL_SFRAME
		if ((protocol->LastTx_Cntx.FrameType == SFRAME)
			&& ((protocol->LastTx_Cntx.SframeInfo.sFrameType == WTX_RSP) ||
			(protocol->LastTx_Cntx.SframeInfo.sFrameType == RESYNCH_RSP))) {
			if (protocol->rnack_retry_counter < protocol->rnack_retry_limit) {
				protocol->Rx_Cntx.lastRcvdFrameType = INVALID;
				protocol->NextTx_Cntx.FrameType = RFRAME;
				protocol->NextTx_Cntx.RframeInfo.errCode = OTHER_ERROR;
				protocol->NextTx_Cntx.RframeInfo.seqNo =
					(!protocol->Rx_Cntx.lastRcvdIframeInfo.seqNo) << 4;
				protocol->nextTransceiveState = SEND_R_NACK;
				protocol->rnack_retry_counter++;
			} else {
				protocol->rnack_retry_counter = PROTOCOL_ZERO;
				/* Re-transmission failed completely, Going to exit */
				protocol->nextTransceiveState = IDLE_STATE;
				protocol->timeoutCounter = PROTOCOL_ZERO;
			}
		} else
#endif
		{
			ESE_UDELAY(DELAY_ERROR_RECOVERY);
			/* re transmit the frame */
			if (protocol->timeoutCounter < PROTOCOL_TIMEOUT_RETRY_COUNT) {
				protocol->timeoutCounter++;
				ESELOG_E("re-transmitting the previous frame");
				protocol->NextTx_Cntx = protocol->LastTx_Cntx;
			} else {
				/* Re-transmission failed completely, Going to exit */
				protocol->nextTransceiveState = IDLE_STATE;
				protocol->timeoutCounter = PROTOCOL_ZERO;
				status = eseData_storeData(&(protocol->recv_data), p_data, data_len, 1);
				if (status != ESESTATUS_SUCCESS) {
					goto flush;
				}
			}
		}
	}
	return status;

flush:
	ESELOG_E("buffer flush");
	ret_len = eseHal_Receive(p_data, 258);
	if (ret_len != 258) {
		ESELOG_E("mismatch receive size %u, %u", 258, ret_len);
		status = ESESTATUS_FAILED;
	}	
	return status;
}

static ESE_STATUS eseProtocol_transceiveProcess(void)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
#ifdef ESE_PROTOCOL_SFRAME
	sFrameInfo_t sFrameInfo;
#endif

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	while (protocol->nextTransceiveState != IDLE_STATE) {
		switch (protocol->nextTransceiveState) {
			case SEND_IFRAME:
				status = eseProtocol_sendIframe(
				protocol->NextTx_Cntx.IframeInfo);
				break;
			case SEND_R_ACK:
				status = eseProtocol_sendRframe(RACK);
				break;
			case SEND_R_NACK:
				status = eseProtocol_sendRframe(RNACK);
				break;
#ifdef ESE_PROTOCOL_SFRAME
			case SEND_S_RSYNC:
				sFrameInfo.sFrameType = RESYNCH_REQ;
				status = eseProtocol_sendSFrame(sFrameInfo);
				break;
			case SEND_S_WTX_RSP:
				sFrameInfo.sFrameType = WTX_RSP;
				status = eseProtocol_sendSFrame(sFrameInfo);
				break;
#endif
			default:
				protocol->nextTransceiveState = IDLE_STATE;
			break;
		}

		if (status == ESESTATUS_SUCCESS) {
			memcpy(&protocol->LastTx_Cntx, &protocol->NextTx_Cntx, sizeof(NextTx_Info_t));
			status = eseProtocol_processResponse();
		} else {
			//Insert Log
			ESELOG_E("Transceive send failed, going to recovery!");
			protocol->nextTransceiveState = IDLE_STATE;
		}
	}
	return status;
}

ESE_STATUS eseProtocol_transceive(ese_data* pCmd, ese_data* pRsp)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	ESE_STATUS wStatus = ESESTATUS_SUCCESS;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	if ((NULL == pCmd) || (NULL == pRsp) || (protocol->CurrentState != IDLE))
		return status;

	/* Updating the transceive information to the protocol stack */
	protocol->CurrentState = TRANSCEIVE;
	protocol->NextTx_Cntx.IframeInfo.p_data = pCmd->p_data;
	protocol->NextTx_Cntx.IframeInfo.totalDataLen = pCmd->len;

	status = eseProtocol_setFirstIframeContxt();
	if (status != ESESTATUS_SUCCESS) {
		return status;
	}
	status = eseProtocol_transceiveProcess();
	if (status == ESESTATUS_FAILED) {
		/* ESE hard reset to be done */
		wStatus = eseData_getData(&(protocol->recv_data), &pRsp->p_data, &pRsp->len);
		if (wStatus != ESESTATUS_SUCCESS) {
			 status = ESESTATUS_FAILED;
		}
	} else {
		// fetch the data info and report to upper layer.
		wStatus = eseData_getData(&(protocol->recv_data), &pRsp->p_data, &pRsp->len);
		if (ESESTATUS_SUCCESS != wStatus) {
			status = ESESTATUS_FAILED;
		}
	}
	protocol->CurrentState = IDLE;
	return status;
}

ESE_STATUS eseProtocol_open(void)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	ESE_HAL_STATUS hStatus = ESE_HAL_STATUS_SUCCESS;

	if (protocol == NULL) {
		protocol = ESE_MALLOC(sizeof(protocol_t));
		if (protocol == NULL) {
			return ESESTATUS_INSUFFICIENT_RESOURCES;
		}
	}

	hStatus = eseHal_Open();
	if (hStatus != ESE_HAL_STATUS_SUCCESS) {
		ESELOG_E("Fail eseHal_Open %d", hStatus);
		if (hStatus == ESE_HAL_STATUS_BUSY) {
			status = ESESTATUS_BUSY;
		} else if (hStatus == ESE_HAL_STATUS_ALREADY_OPENED) {
			status = ESESTATUS_ALREADY_OPENED;
		} else {
			status = ESESTATUS_INVALID_DEVICE;
		}
		return status;
	}

	status = eseProtocol_resetProtoParams();
	protocol->wtx_counter_limit = PROTOCOL_WTX_DEFAULT_COUNT;
	protocol->rnack_retry_limit = PROTOCOL_MAX_RNACK_RETRY_LIMIT;
	//protocol->NextTx_Cntx.IframeInfo.maxDataLen = ESE_T1_MAXLEN;

	return status;
}

ESE_STATUS eseProtocol_close (void)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;
	ESE_HAL_STATUS hStatus = ESE_HAL_STATUS_SUCCESS;

	if (protocol == NULL) {
		return ESESTATUS_ALREADY_CLOSED;
	}

	if (protocol->CurrentState != IDLE)
		return status;

	protocol->recoveryCounter = 0;
	protocol->wtx_counter = 0;

	ESE_FREE(protocol);
	protocol = NULL;

	hStatus = eseHal_Close();
	if (hStatus != ESE_HAL_STATUS_SUCCESS) {
		ESELOG_E("Fail eseHal_Close %d", hStatus);
		return ESESTATUS_INVALID_DEVICE;
	}

	ESE_PRINT_LIST();

	return status;
}

#ifdef ESE_PROTOCOL_SFRAME
static ESE_STATUS eseProtocol_RSync(void)
{
	ESE_STATUS status = ESESTATUS_FAILED;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	protocol->CurrentState = TRANSCEIVE;
  /* send the end of session s-frame */
	protocol->NextTx_Cntx.FrameType = SFRAME;
	protocol->NextTx_Cntx.SframeInfo.sFrameType = RESYNCH_REQ;
	protocol->nextTransceiveState = SEND_S_RSYNC;
	status = eseProtocol_transceiveProcess();
	protocol->CurrentState = IDLE;
	return status;
}
#endif

ESE_STATUS eseProtocol_reset (void)
{
	ESE_STATUS status = ESESTATUS_SUCCESS;

	if (protocol == NULL) {
		return ESESTATUS_NOT_INITIALISED;
	}

	status = eseProtocol_resetProtoParams();
	protocol->wtx_counter_limit = PROTOCOL_WTX_DEFAULT_COUNT;
	protocol->rnack_retry_limit = PROTOCOL_MAX_RNACK_RETRY_LIMIT;
	//protocol->NextTx_Cntx.IframeInfo.maxDataLen = ESE_T1_MAXLEN;

#ifdef ESE_PROTOCOL_SFRAME
	 status = eseProtocol_RSync();
#endif
	return status;
}
