#include <errno.h>
|
#include <string.h>
|
#include <stdlib.h>
|
|
#include "T1.h"
|
#include "phdriver.h"
|
#include "se_api.h"
|
#include "mk_misc.h"
|
#include "mk_trace.h"
|
|
static uint8_t gp_data_tx[270];
|
static uint8_t gp_data_tx_bck[270];
|
uint8_t gp_data_rx[270];
|
static T1_params g_T1_params;
|
static uint8_t g_all_data_buf[1024];
|
|
atr_info g_atr = {
|
.len = 0x13,
|
.vendor_ID = {0x00},
|
.dll_IC = 0x01,
|
.bgt = {0x00, 0x01},
|
.bwt = {0x03, 0xE8},
|
.max_freq = {0x4E, 0x20},
|
.checksum = 0x00,
|
.default_IFSC = 0xFE,
|
.num_channels = 0x01,
|
.max_IFSC = {0x00, 0xFF},
|
.capbilities = {0x00, 0x14}, // Extended frame length feature doesn't support
|
};
|
|
uint32_t g_recv_total_len = 0;
|
|
static uint8_t t1_calculate_LRC(uint8_t *p_data, uint16_t offset, uint16_t len);
|
static uint16_t t1_calculate_CRC(uint8_t *p_data, uint16_t offset, uint16_t len);
|
static void t1_set_epilogue(uint8_t *p_data, uint16_t offset, uint16_t len);
|
|
static ESESTATUS t1_read(uint16_t *p_data_len);
|
|
static uint8_t set_info_len(uint8_t *p_data, uint16_t info_len);
|
static uint16_t get_info_len(uint8_t *p_data);
|
static ESESTATUS decode_ATR(uint8_t *p_data, bool is_CIP);
|
static void free_tx_part_mem(void);
|
static void reset_for_resync_rsp(void);
|
|
static ESESTATUS do_T1_recovery_at_decode_Iframe(void);
|
static ESESTATUS do_T1_recovery_at_decode_Sframe(void);
|
static ESESTATUS do_T1_recovery_at_decode_Rframe(void);
|
static ESESTATUS t1_recv_data_store_in_list(uint8_t *p_data, uint16_t data_len, uint16_t total_len);
|
static void t1_recv_data_list_destroy(void);
|
static void reset_ATR(void);
|
|
/******************************************************************************
|
* @Function Transmit_receive_process
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS Transmit_receive_process(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
g_ese_ctx.is_read_done = false;
|
|
g_recv_total_len = 0;
|
while (STATE_IDEL != g_ese_ctx.next_T1_state)
|
{
|
switch (g_ese_ctx.p_T1_params->tx_frame_type)
|
{
|
case IFRAME:
|
{
|
status = t1_send_Iframe();
|
break;
|
}
|
|
case RFRAME:
|
{
|
status = t1_send_Rframe();
|
break;
|
}
|
|
case SFRAME:
|
{
|
status = t1_send_Sframe();
|
break;
|
}
|
case FRAME_TYPE_INVALID_MAX:
|
case FRAME_TYPE_INVALID_MIN:
|
{
|
status = ESESTATUS_INVALID_PARAMETER;
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid T1 frame type: %d\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->tx_frame_type);
|
}
|
}
|
|
if (ESESTATUS_SUCCESS == status)
|
{
|
#ifdef DBG_LEVEL_STACK
|
gDumpStackFlag = false;
|
#endif
|
g_ese_ctx.p_T1_params->blk_retry_cnt = 0;
|
status = response_process();
|
// Do not need handle the status value
|
}
|
else
|
{
|
if (((ESESTATUS_PH_IO != status) && (ESESTATUS_MEM_EXCEPTION != status)) || (g_ese_ctx.p_T1_params->blk_retry_cnt >= g_ese_ctx.max_blk_retry_cnt))
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: send frame failed, frameType = %d, status = %d, retryCnt = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_frame_type, status, g_ese_ctx.max_blk_retry_cnt);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
else
|
{
|
// resend
|
g_ese_ctx.p_T1_params->blk_retry_cnt++;
|
}
|
}
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function response_process
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS response_process(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
uint16_t data_len = 0;
|
|
// LOG_INFO(TRACE_MODULE_SE, "%s: enter\r\n", __FUNCTION__);
|
status = t1_read(&data_len);
|
|
if (ESESTATUS_SUCCESS == status)
|
{
|
status = t1_chk_epilogue(gp_data_rx, 0, data_len);
|
if (status == ESESTATUS_SUCCESS)
|
{
|
status = t1_decode_frame(data_len);
|
|
if (ESESTATUS_SUCCESS == status)
|
{
|
}
|
}
|
else
|
{
|
do_T1_recovery_at_undecode();
|
}
|
}
|
else if (ESESTATUS_FATAL_ERROR == status)
|
{
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
else
|
{
|
do_T1_recovery_at_undecode();
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_send_Sframe
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
#if defined(__GNUC__)
|
#pragma GCC diagnostic push
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
#endif
|
ESESTATUS t1_send_Sframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
int ret = -1;
|
uint8_t frame_len = 0;
|
|
if (SFRAME_TYPE_INVALID == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid S-block frame type: %d\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->tx_sub_Sframe_type);
|
return ESESTATUS_INVALID_PARAMETER;
|
}
|
else
|
{
|
frame_len = get_header_len(true);
|
}
|
|
// memset(gp_data_tx, 0, sizeof(gp_data_tx));
|
switch (g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
{
|
case RESYNC_REQ:
|
{
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | RESYNC_REQ);
|
break;
|
}
|
|
case IFS_REQ:
|
{
|
uint8_t len_offset = 0, info_len = 0x02;
|
frame_len += info_len;
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | IFS_REQ);
|
len_offset = set_info_len(gp_data_tx, info_len);
|
gp_data_tx[len_offset + 1] = (uint8_t)(g_ese_ctx.max_IFSD >> 8);
|
gp_data_tx[len_offset + 2] = (uint8_t)(g_ese_ctx.max_IFSD & 0xFF);
|
break;
|
}
|
|
case IFS_RSP:
|
{
|
uint8_t len_offset = 0, info_len = 0;
|
info_len = (uint8_t)get_info_len(gp_data_rx);
|
frame_len += info_len;
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | IFS_REQ);
|
len_offset = set_info_len(gp_data_tx, info_len);
|
memcpy(&gp_data_tx[len_offset + 1], &gp_data_rx[len_offset + 1], info_len);
|
break;
|
}
|
|
case ABORT_REQ:
|
{
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | ABORT_REQ);
|
break;
|
}
|
case WTX_RSP:
|
{
|
uint8_t len_offset = 0, info_len = 1;
|
frame_len += info_len;
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | WTX_RSP);
|
len_offset = set_info_len(gp_data_tx, info_len);
|
gp_data_tx[len_offset + 1] = g_ese_ctx.p_T1_params->wtx_info;
|
break;
|
}
|
|
case CIP_REQ:
|
{
|
/* Note:T1 CIP_REQ for ATR, the frame's INFO length should be 1 byte,
|
*/
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | CIP_REQ);
|
break;
|
}
|
|
case PROP_END_APDU_REQ:
|
{
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | PROP_END_APDU_REQ);
|
break;
|
}
|
|
case HARD_RESET_REQ:
|
{
|
break;
|
}
|
|
case ATR_REQ:
|
{
|
gp_data_tx[T1_PCB_OFFSET] = (T1_S_BLOCK_REQ_MASK | ATR_REQ);
|
break;
|
}
|
|
default:
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid S-block frame type: %X\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->tx_sub_Sframe_type);
|
break;
|
}
|
}
|
|
gp_data_tx[T1_NAD_OFFSET] = NAD_D2C;
|
// caculate LRC or CRC and set it to pFramebuff
|
t1_set_epilogue(gp_data_tx, 0, frame_len);
|
|
ret = phdriver_write(gp_data_tx, frame_len);
|
if (-1 == ret)
|
{
|
status = ESESTATUS_PH_IO;
|
LOG_INFO(TRACE_MODULE_SE, "write fail: %s, errno = %d, ret = %d\r\n", __FUNCTION__, errno, ret);
|
}
|
|
return status;
|
}
|
#if defined(__GNUC__)
|
#pragma GCC diagnostic pop
|
#endif
|
|
/******************************************************************************
|
* @Function t1_send_Iframe
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_send_Iframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
int ret = -1;
|
uint16_t frame_len = 0;
|
uint8_t *p_frame_buf = NULL;
|
uint16_t len_offset = 0;
|
uint16_t send_len = 0;
|
uint16_t tx_data_offset = 0;
|
|
if (g_ese_ctx.p_T1_params->recovery_cnt > g_ese_ctx.max_recovery_cnt)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s fatal error, recovery_cnt = %d\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->recovery_cnt);
|
status = ESESTATUS_FATAL_ERROR;
|
goto cleanup;
|
}
|
else if ((g_ese_ctx.p_T1_params->p_data_tx_part != NULL))
|
{
|
// LOG_INFO(TRACE_MODULE_SE, "%s retry_send_part, tx_len_part = %u\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->tx_len_part);
|
p_frame_buf = g_ese_ctx.p_T1_params->p_data_tx_part;
|
frame_len = g_ese_ctx.p_T1_params->tx_len_part;
|
goto retry_send_part;
|
}
|
|
tx_data_offset = g_ese_ctx.p_T1_params->tx_data_offset;
|
|
frame_len = (g_ese_ctx.is_T1_ext_hdr_len ? T1_EXT_HEADER_LEN : T1_HEADER_LEN) + ((LRC == g_atr.checksum) ? T1_LRC_LEN : T1_CRC_LEN);
|
|
if ((g_ese_ctx.p_T1_params->tx_len - tx_data_offset) > g_ese_ctx.max_IFSC)
|
{
|
g_ese_ctx.p_T1_params->is_device_chaining = true;
|
send_len = g_ese_ctx.max_IFSC;
|
tx_data_offset += g_ese_ctx.max_IFSC;
|
}
|
else
|
{
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
send_len = g_ese_ctx.p_T1_params->tx_len - tx_data_offset;
|
tx_data_offset += send_len;
|
}
|
|
frame_len += send_len;
|
// memset(gp_data_tx, 0, sizeof(gp_data_tx));
|
p_frame_buf = gp_data_tx;
|
|
p_frame_buf[T1_NAD_OFFSET] = NAD_D2C;
|
len_offset = set_info_len(p_frame_buf, send_len);
|
memcpy((p_frame_buf + len_offset + 1), (g_ese_ctx.p_T1_params->p_data_tx + g_ese_ctx.p_T1_params->tx_data_offset), send_len);
|
g_ese_ctx.p_T1_params->tx_data_offset = tx_data_offset;
|
|
// memset(gp_data_tx_bck, 0, sizeof(gp_data_tx_bck));
|
memcpy(gp_data_tx_bck, p_frame_buf, frame_len);
|
g_ese_ctx.p_T1_params->p_data_tx_part = gp_data_tx_bck;
|
g_ese_ctx.p_T1_params->tx_len_part = frame_len;
|
|
retry_send_part:
|
p_frame_buf[T1_PCB_OFFSET] = 0x00;
|
if (g_ese_ctx.p_T1_params->is_device_chaining)
|
{
|
p_frame_buf[T1_PCB_OFFSET] |= T1_CHAINING_MASK;
|
}
|
p_frame_buf[T1_PCB_OFFSET] |= (g_ese_ctx.seq_num_device << 6);
|
|
t1_set_epilogue(p_frame_buf, 0, frame_len);
|
|
ret = phdriver_write(p_frame_buf, frame_len);
|
if (-1 == ret)
|
{
|
status = ESESTATUS_PH_IO;
|
LOG_INFO(TRACE_MODULE_SE, "write fail: %s, errno = %d, ret = %d\r\n", __FUNCTION__, errno, ret);
|
}
|
|
cleanup:
|
|
if (S_ABORT_RSP == g_ese_ctx.next_T1_state)
|
{
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
free_tx_part_mem();
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_send_Rframe
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_send_Rframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
int ret = -1;
|
uint8_t frame_len = 0;
|
uint8_t *p_frame_buff = gp_data_tx;
|
|
if (RNACK_INVALID_ERROR == g_ese_ctx.p_T1_params->tx_sub_Rframe_type)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid R-block frame type: %d\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->tx_sub_Sframe_type);
|
return ESESTATUS_INVALID_PARAMETER;
|
}
|
else
|
{
|
frame_len = get_header_len(true);
|
}
|
|
// memset(gp_data_tx, 0, sizeof(gp_data_tx));
|
// pFramebuff = gp_data_tx;
|
|
if (RNACK_PARITY_ERROR == g_ese_ctx.p_T1_params->tx_sub_Rframe_type)
|
{
|
p_frame_buff[1] = 0x81;
|
}
|
else if (RNACK_OTHER_ERROR == g_ese_ctx.p_T1_params->tx_sub_Rframe_type)
|
{
|
p_frame_buff[1] = 0x82;
|
}
|
else
|
{ // if (RACK == g_ese_ctx.p_T1_params->subRFrameType) {
|
p_frame_buff[1] = 0x80;
|
}
|
|
p_frame_buff[1] |= (g_ese_ctx.seq_num_card << 4);
|
|
p_frame_buff[T1_NAD_OFFSET] = NAD_D2C;
|
// caculate LRC or CRC and set it to pFramebuff
|
t1_set_epilogue(p_frame_buff, 0, frame_len);
|
|
ret = phdriver_write(p_frame_buff, frame_len);
|
if (-1 == ret)
|
{
|
status = ESESTATUS_PH_IO;
|
|
LOG_INFO(TRACE_MODULE_SE, "write fail: %s, errno = %d, ret = %d\r\n", __FUNCTION__, errno, ret);
|
}
|
else
|
{
|
// LOG_INFO(TRACE_MODULE_SE, "\nTx", 2, pFramebuff, frameLen);
|
// LOG_INFO(TRACE_MODULE_SE, "\n");
|
}
|
|
if (NULL == p_frame_buff)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s failed, frame buffer malloc memory failed\r\n", __FUNCTION__);
|
status = ESESTATUS_MEM_EXCEPTION;
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_calculate_LRC
|
* @Param p_data - data to compute the LRC over.
|
* offset - calculate start offset.
|
* len - data length, not include the epilogue byte.
|
* @Returns On success return the LRC of the data.
|
******************************************************************************/
|
static uint8_t t1_calculate_LRC(uint8_t *p_data, uint16_t offset, uint16_t len)
|
{
|
uint8_t LRC = 0;
|
uint16_t i = 0;
|
for (i = offset; i < len; i++)
|
{
|
LRC = LRC ^ p_data[i];
|
}
|
return LRC;
|
}
|
|
/******************************************************************************
|
* @Function t1_calculate_CRC
|
* @Param p_data - data to compute the CRC over.
|
* offset - calculate start offset.
|
* len - data length, not include the epilogue bytes..
|
* @Returns On success return the CRC of the data.
|
******************************************************************************/
|
static uint16_t t1_calculate_CRC(uint8_t *p_data, uint16_t offset, uint16_t len)
|
{
|
uint16_t crc;
|
|
crc = (uint16_t)CRC_PRESET;
|
|
int i, j;
|
for (i = offset; i < len; i++)
|
{
|
crc = crc ^ ((unsigned short)p_data[i]);
|
for (j = 0; j < 8; j++)
|
{
|
if ((crc & 0x0001) == 0x0001)
|
{
|
crc = (crc >> 1) ^ CRC_POLYNOMIAL;
|
}
|
else
|
{
|
crc = crc >> 1;
|
}
|
}
|
}
|
crc = ~crc;
|
|
return crc;
|
}
|
|
/******************************************************************************
|
* @Function t1_set_epilogue
|
* @Param p_data - data to compute the LRC or CRC over.
|
* offset - calculate start offset.
|
* len - data length, include the epilogue byte(s).
|
******************************************************************************/
|
static void t1_set_epilogue(uint8_t *p_data, uint16_t offset, uint16_t len)
|
{
|
if ((LRC == g_atr.checksum) ||
|
(CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/
|
|| (CIP_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/)
|
{
|
// LRC
|
p_data[len - 1] = t1_calculate_LRC(p_data, offset, len - 1);
|
}
|
else
|
{
|
const uint16_t CRC = t1_calculate_CRC(p_data, offset, len - 2);
|
|
p_data[len - 2] = (uint8_t)(CRC >> 8);
|
p_data[len - 1] = (uint8_t)CRC;
|
}
|
}
|
|
/******************************************************************************
|
* @Function t1_chk_epilogue
|
* @Param p_data - data to compute the LRC or CRC over.
|
* offset - calculate start offset.
|
* len - data length, include the epilogue byte(s).
|
* @Returns ESESTATUS_SUCCESS if caculate result is equal to response's epilogue,
|
* else ESESTATUS_PARITY_ERROR.
|
******************************************************************************/
|
ESESTATUS t1_chk_epilogue(uint8_t *p_data, uint16_t offset, uint16_t len)
|
{
|
ESESTATUS status = ESESTATUS_PARITY_ERROR;
|
|
/* Note:T1 CIP_REQ for ATR, the frame's INFO length should be 1 byte,
|
* do not support 2 bytes length.
|
* And epiLogue field should use LRC (1 byte).
|
*/
|
if ((LRC == g_atr.checksum) ||
|
(CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/
|
|| (CIP_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/)
|
{
|
// LRC
|
const uint8_t LRC = t1_calculate_LRC(p_data, offset, len - 1);
|
if (LRC == p_data[len - 1])
|
{
|
status = ESESTATUS_SUCCESS;
|
}
|
}
|
else
|
{
|
uint16_t rspCRC = (uint16_t)(((uint16_t)p_data[len - 2]) << 8);
|
rspCRC |= p_data[len - 1];
|
const uint16_t CRC = t1_calculate_CRC(p_data, offset, len - 2);
|
if (CRC == rspCRC)
|
{
|
status = ESESTATUS_SUCCESS;
|
}
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_read
|
* @Param p_data_len - point to data length.
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_xxx.
|
******************************************************************************/
|
static ESESTATUS t1_read(uint16_t *p_data_len)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
int ret = -1;
|
const uint32_t sof_max_cnt = g_ese_ctx.max_read_retry_cnt;
|
uint32_t sof_cnt = 0;
|
uint16_t total_cnt = 0, num_Bytes_to_read = 0, read_index = 0;
|
|
#ifdef USE_SPI_SPLIT_READ
|
const uint8_t hdrLen = get_header_len(false) + 1;
|
#else
|
const uint8_t hdrLen = g_ese_ctx.max_IFSD;
|
#endif
|
|
#ifdef DBG_LEVEL_STACK
|
if (gDumpStackFlag)
|
{
|
dumpCallstack();
|
}
|
gDumpStackFlag = true;
|
#endif
|
|
// memset(gp_data_rx, 0x00, sizeof(gp_data_rx));
|
do
|
{
|
sys_timer_delay_ms(1); // 10
|
|
ret = phdriver_read(gp_data_rx, 1);
|
|
if ((ret < 0) || (NAD_C2DT != gp_data_rx[0]))
|
{
|
sof_cnt++;
|
continue;
|
}
|
else
|
{
|
phdriver_read(gp_data_rx + 1, (hdrLen - 1));
|
|
if (g_ese_ctx.is_T1_ext_hdr_len)
|
{
|
num_Bytes_to_read = (uint16_t)(((uint16_t)gp_data_rx[T1_FRAME_LEN_OFFSET]) << 8);
|
num_Bytes_to_read |= gp_data_rx[T1_FRAME_LEN_OFFSET2];
|
|
if (CRC == g_atr.checksum)
|
{
|
num_Bytes_to_read += 1;
|
}
|
}
|
else
|
{
|
num_Bytes_to_read = gp_data_rx[T1_FRAME_LEN_OFFSET];
|
}
|
read_index = hdrLen;
|
total_cnt += hdrLen;
|
// LOG_INFO(TRACE_MODULE_SE, "_spi_read() [HDR] read data right!! \r\n");
|
break;
|
}
|
} while (sof_cnt < sof_max_cnt);
|
|
/*SOF Read timeout happened, go for frame retransmission*/
|
if (sof_cnt == sof_max_cnt)
|
{
|
if (ret >= 0)
|
{
|
ret = -1;
|
status = ESESTATUS_PH_IOR_INVALID_DATA;
|
}
|
else
|
{
|
status = ESESTATUS_PH_IO;
|
}
|
// LOG_INFO(TRACE_MODULE_SE, "_spi_read() [HDR] execption, ret = %d, sofCnt = %X\r\n", ret, sofCnt);
|
}
|
|
#ifdef USE_SPI_SPLIT_READ
|
if (ret < 0)
|
{
|
if ((-1 == ret) && (4 == errno))
|
{
|
// Interrupted system call, is NFC_DLD_FLUSH
|
status = ESESTATUS_FATAL_ERROR;
|
}
|
}
|
else
|
{
|
if (0 != num_Bytes_to_read)
|
{
|
ret = phdriver_read(&gp_data_rx[read_index], num_Bytes_to_read);
|
if (ret < 0)
|
{
|
status = ESESTATUS_PH_IO;
|
LOG_INFO(TRACE_MODULE_SE, "%s _spi_read() [payload] execption, ret = %x, errno = %d\r\n", __FUNCTION__, ret, errno);
|
}
|
else
|
{
|
// LOG_INFO(TRACE_MODULE_SE, " _spi_read() [payload] right!!!!!!!!!!!!!!\r\n");
|
total_cnt += num_Bytes_to_read;
|
}
|
}
|
}
|
#endif
|
|
if (ESESTATUS_SUCCESS != status)
|
{
|
*p_data_len = 0;
|
// LOG_INFO(TRACE_MODULE_SE, "RxFail\r\n");
|
// LOG_INFO(TRACE_MODULE_SE, "read fail: %s, errno = %d, ret = %d\r\n", __FUNCTION__, errno, ret);
|
}
|
else
|
{
|
*p_data_len = total_cnt;
|
|
// LOG_INFO(TRACE_MODULE_SE, "Rx\r\n");
|
}
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_decode_frame
|
* @Param data_len - data length.
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_xxx.
|
******************************************************************************/
|
ESESTATUS t1_decode_frame(uint16_t data_len)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
uint8_t pcb = gp_data_rx[T1_PCB_OFFSET];
|
T1_pcb_bits *p_pcb_bits;
|
|
if (g_ese_ctx.p_T1_params)
|
{
|
p_pcb_bits = &g_ese_ctx.p_T1_params->last_rx_pcb_bits;
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "p_T1_params is NULL\r\n");
|
return ESESTATUS_INVALID_PCB;
|
}
|
|
// memset(p_pcb_bits, 0x00, sizeof(T1_pcb_bits));
|
memcpy(p_pcb_bits, &pcb, sizeof(uint8_t));
|
uint16_t infoLen = get_info_len(gp_data_rx);
|
uint8_t dataOffset = get_header_len(false);
|
|
if (0x00 == p_pcb_bits->msb)
|
{
|
// I-FRAME decoded
|
g_ese_ctx.p_T1_params->last_rx_frame_type = IFRAME;
|
g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
|
g_ese_ctx.p_T1_params->wtx_cnt = 0;
|
|
if (g_ese_ctx.is_seq_num_dr && (p_pcb_bits->bit7 != g_ese_ctx.seq_num_card))
|
{
|
g_ese_ctx.seq_num_card ^= 0x01;
|
}
|
g_ese_ctx.is_seq_num_dr = false;
|
|
if (p_pcb_bits->bit7 == g_ese_ctx.seq_num_card)
|
{
|
g_ese_ctx.p_T1_params->recovery_cnt = 0;
|
g_ese_ctx.p_T1_params->resync_CIP_req_cnt = 0;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
|
if (p_pcb_bits->bit6)
|
{
|
g_ese_ctx.p_T1_params->is_card_chaining = true;
|
g_ese_ctx.seq_num_card = (g_ese_ctx.seq_num_card ^ 0x01);
|
|
status = t1_recv_data_store_in_list(&gp_data_rx[dataOffset], infoLen, data_len);
|
|
if (ESESTATUS_SUCCESS != status)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s, t1_recv_data_store_in_list failed, terminate transmission, status = %d\r\n", __FUNCTION__, status);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
free_tx_part_mem();
|
}
|
else
|
{
|
// Card response chaining and successfully, then request the next segments data.
|
g_ese_ctx.next_T1_state = R_ACK;
|
g_ese_ctx.p_T1_params->tx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RACK;
|
}
|
}
|
else
|
{
|
g_ese_ctx.p_T1_params->is_card_chaining = false;
|
status = t1_recv_data_store_in_list(&gp_data_rx[dataOffset], infoLen, data_len);
|
if (ESESTATUS_SUCCESS != status)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s, t1_recv_data_store_in_list failed, terminate transmission, status = %d\r\n", __FUNCTION__, status);
|
}
|
else
|
{
|
// Not chaining, and response success.
|
}
|
|
g_ese_ctx.seq_num_device = (g_ese_ctx.seq_num_device ^ 0x01);
|
g_ese_ctx.seq_num_card = (g_ese_ctx.seq_num_card ^ 0x01);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
free_tx_part_mem();
|
}
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s, received invalid seqNum = %02X, seq_num_card = %02X\r\n", __FUNCTION__, p_pcb_bits->bit7, g_ese_ctx.seq_num_card);
|
status = do_T1_recovery_at_decode_Iframe();
|
}
|
}
|
else if ((0x01 == p_pcb_bits->msb) && (0x00 == p_pcb_bits->bit7))
|
{
|
// R-FRAME decoded
|
g_ese_ctx.p_T1_params->last_rx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
|
if ((0x00 == p_pcb_bits->lsb) && (0x00 == p_pcb_bits->bit2))
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RACK;
|
if ((NULL != g_ese_ctx.p_T1_params->p_data_tx) && g_ese_ctx.p_T1_params->is_device_chaining)
|
{
|
if (g_ese_ctx.seq_num_device != p_pcb_bits->bit5)
|
{
|
// Device chaining, valid card request the next segments of device data to send.
|
// seq_num_device should changed, but the seq_num_card unchanged until received card I-block.
|
g_ese_ctx.seq_num_device = p_pcb_bits->bit5;
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
free_tx_part_mem();
|
}
|
else
|
{
|
status = do_T1_recovery_at_decode_Rframe();
|
}
|
}
|
else if (NULL != g_ese_ctx.p_T1_params->p_data_tx)
|
{
|
status = ESESTATUS_INVALID_PARAMETER;
|
LOG_INFO(TRACE_MODULE_SE, "%s: Device initiates unchaining I-block cannot receive RACK, terminate transmit.\r\n", __FUNCTION__);
|
// terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
else
|
{
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
}
|
else
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
if ((0x01 == p_pcb_bits->lsb) && (0x00 == p_pcb_bits->bit2))
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RNACK_PARITY_ERROR;
|
status = ESESTATUS_PARITY_ERROR;
|
}
|
else if ((0x00 == p_pcb_bits->lsb) && (0x01 == p_pcb_bits->bit2))
|
{
|
// Other error
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RNACK_OTHER_ERROR;
|
status = ESESTATUS_UNKNOWN_ERROR;
|
}
|
else
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
status = ESESTATUS_UNKNOWN_ERROR;
|
}
|
|
// LOG_INFO(TRACE_MODULE_SE, "%s: PCB = 0x%02X, recovery_cnt = %d, max_recovery_cnt = %d\r\n", __FUNCTION__, pcb,
|
// g_ese_ctx.p_T1_params->recovery_cnt,
|
// g_ese_ctx.max_recovery_cnt);
|
do_T1_recovery_at_decode_Rframe();
|
}
|
}
|
else if ((0x01 == p_pcb_bits->msb) && (0x01 == p_pcb_bits->bit7))
|
{
|
g_ese_ctx.p_T1_params->last_rx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
|
LOG_INFO(TRACE_MODULE_SE, "%s S-Frame Received\r\n", __FUNCTION__);
|
uint8_t subFrameType = pcb & T1_S_BLOCK_SUB_TYPE_MASK;
|
if (RESYNC_RSP != subFrameType)
|
{
|
g_ese_ctx.is_resync_recovery_flag = false;
|
}
|
|
switch (subFrameType)
|
{
|
case RESYNC_RSP:
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type = RESYNC_RSP;
|
status = do_T1_recovery_at_decode_Sframe();
|
break;
|
}
|
|
case IFS_REQ:
|
{
|
uint16_t IFSC = 0;
|
if (2 == infoLen)
|
{
|
IFSC = (uint16_t)(((uint16_t)gp_data_rx[dataOffset]) << 8);
|
IFSC |= gp_data_rx[dataOffset + 1];
|
LOG_INFO(TRACE_MODULE_SE, "%s IFS_RSP Received, IFSD = %04X\r\n", __FUNCTION__, IFSC);
|
}
|
else if (1 == infoLen)
|
{
|
IFSC = (uint16_t)gp_data_rx[dataOffset];
|
LOG_INFO(TRACE_MODULE_SE, "%s IFS_RSP Received, IFSD = %02X\r\n", __FUNCTION__, IFSC);
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s Invalid IFS_RSP Received\r\n", __FUNCTION__);
|
}
|
g_ese_ctx.max_IFSC = IFSC;
|
g_ese_ctx.next_T1_state = S_IFS_RSP;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = IFS_RSP;
|
break;
|
}
|
|
case IFS_RSP:
|
{
|
g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type = IFS_RSP;
|
uint16_t IFSD = 0;
|
uint8_t offset = get_header_len(false);
|
if (2 == infoLen)
|
{
|
IFSD = (uint16_t)(((uint16_t)gp_data_rx[offset]) << 8);
|
IFSD |= gp_data_rx[offset + 1];
|
LOG_INFO(TRACE_MODULE_SE, "%s IFS_RSP Received, IFSD = %04X\r\n", __FUNCTION__, IFSD);
|
}
|
else if (1 == infoLen)
|
{
|
IFSD = (uint16_t)gp_data_rx[offset];
|
LOG_INFO(TRACE_MODULE_SE, "%s IFS_RSP Received, IFSD = %02X\r\n", __FUNCTION__, IFSD);
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s Invalid IFS_RSP Received\r\n", __FUNCTION__);
|
}
|
if (g_ese_ctx.max_IFSD != IFSD)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid IFS receive paylod, receivedIFSD = %X, sentIFSD = %X\r\n", __FUNCTION__, IFSD, g_ese_ctx.max_IFSD);
|
}
|
status = do_T1_recovery_at_decode_Sframe();
|
break;
|
}
|
|
case ABORT_REQ:
|
case ABORT_RSP:
|
{
|
// Rule 9: only chaining cmd or rsp will send (receive) ABORT_REQ
|
if (g_ese_ctx.p_T1_params->is_card_chaining || g_ese_ctx.p_T1_params->is_device_chaining)
|
{
|
t1_recv_data_list_destroy();
|
}
|
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
|
case WTX_REQ:
|
{
|
if (g_ese_ctx.p_T1_params->wtx_cnt <= g_ese_ctx.max_wtx_cnt)
|
{
|
g_ese_ctx.p_T1_params->wtx_cnt++;
|
|
g_ese_ctx.p_T1_params->wtx_info = gp_data_rx[get_header_len(false)];
|
g_ese_ctx.next_T1_state = S_WTX_RSP;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = WTX_RSP;
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : failed, WTX_REQ counter larger than maxWtxCnt\r\n", __FUNCTION__);
|
status = ESESTATUS_FAILED;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
break;
|
}
|
|
case CIP_RSP:
|
{
|
status = decode_ATR(gp_data_rx, true);
|
reset_for_resync_rsp();
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
|
case PROP_END_APDU_RSP:
|
{
|
status = ESESTATUS_SUCCESS;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
|
case HARD_RESET_RSP:
|
{
|
status = ESESTATUS_SUCCESS;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
|
case ATR_RSP:
|
{
|
status = decode_ATR(gp_data_rx, false);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
break;
|
}
|
|
default:
|
{
|
status = ESESTATUS_SUCCESS;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid S-block frame type: %X\r\n", __FUNCTION__, subFrameType);
|
}
|
}
|
}
|
else
|
{
|
// LOG_ERROR(TRACE_MODULE_SE, "[RX]Nothing\r\n");
|
LOG_INFO(TRACE_MODULE_SE, "%s Wrong-Frame Received, PCB = %X\r\n", __FUNCTION__, pcb);
|
status = ESESTATUS_INVALID_PCB;
|
}
|
|
return status;
|
}
|
|
/** Contains the Prologue and (or not) Epilogue field length **/
|
uint8_t get_header_len(bool contains_epilogue)
|
{
|
uint8_t len = 0;
|
bool is_T1_ext_hdr_len = g_ese_ctx.is_T1_ext_hdr_len;
|
uint8_t checksum = g_atr.checksum;
|
if ((SFRAME == g_ese_ctx.p_T1_params->tx_frame_type) && ((CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/))
|
{
|
is_T1_ext_hdr_len = false;
|
checksum = 0;
|
}
|
|
len = (is_T1_ext_hdr_len ? T1_EXT_HEADER_LEN : T1_HEADER_LEN);
|
if (contains_epilogue)
|
{
|
len += ((checksum == 0) ? T1_LRC_LEN : T1_CRC_LEN);
|
}
|
|
return len;
|
}
|
|
static uint8_t set_info_len(uint8_t *p_data, uint16_t info_len)
|
{
|
uint8_t len_offset = 0;
|
bool is_T1_ext_hdr_len = g_ese_ctx.is_T1_ext_hdr_len;
|
if ((SFRAME == g_ese_ctx.p_T1_params->tx_frame_type) && ((CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/))
|
{
|
is_T1_ext_hdr_len = false;
|
}
|
|
if (is_T1_ext_hdr_len)
|
{
|
p_data[T1_FRAME_LEN_OFFSET] = (uint8_t)(info_len >> 8);
|
p_data[T1_FRAME_LEN_OFFSET2] = (uint8_t)(info_len & 0xFF);
|
len_offset = T1_FRAME_LEN_OFFSET2;
|
}
|
else
|
{
|
p_data[T1_FRAME_LEN_OFFSET] = (uint8_t)(info_len & 0xFF);
|
len_offset = T1_FRAME_LEN_OFFSET;
|
}
|
return len_offset;
|
}
|
|
static uint16_t get_info_len(uint8_t *p_data)
|
{
|
uint16_t info_len = 0;
|
bool is_T1_ext_hdr_len = g_ese_ctx.is_T1_ext_hdr_len;
|
if ((SFRAME == g_ese_ctx.p_T1_params->tx_frame_type) && ((CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)
|
/*|| (ATR_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)*/))
|
{
|
is_T1_ext_hdr_len = false;
|
}
|
|
if (is_T1_ext_hdr_len)
|
{
|
info_len = (uint16_t)(((uint16_t)p_data[T1_FRAME_LEN_OFFSET]) << 8);
|
info_len |= p_data[T1_FRAME_LEN_OFFSET2];
|
}
|
else
|
{
|
info_len = p_data[T1_FRAME_LEN_OFFSET];
|
}
|
return info_len;
|
}
|
|
/******************************************************************************
|
* @Function decode_ATR
|
* @Param p_data - received data and its must be PCB = 0xE4 or 0xE7.
|
* is_CIP - true if received data's PCB = 0xE4.
|
* @Return ESESTATUS_SUCCESS if decode ATR success, else ESESTATUS_INVALID_T1_LEN.
|
******************************************************************************/
|
static ESESTATUS decode_ATR(uint8_t *p_data, bool is_CIP)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
uint16_t info_len = get_info_len(p_data);
|
uint8_t *p_start = &p_data[T1_FRAME_LEN_OFFSET];
|
if (!is_CIP && g_ese_ctx.is_T1_ext_hdr_len)
|
{
|
// Currently, ATR length is 0x13, exttended frame length high byte should be 0x00
|
p_start++;
|
}
|
|
if (sizeof(atr_info) == (info_len + 1))
|
{
|
memcpy(&g_atr.len, p_start, sizeof(atr_info));
|
}
|
else if (0 == p_data[T1_FRAME_LEN_OFFSET])
|
{
|
// Do nothing, keep befor ATR value.
|
reset_ATR();
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s : invalid ATR response AtrInfoLen = %02X\r\n", __FUNCTION__, (uint8_t)sizeof(atr_info));
|
|
status = ESESTATUS_INVALID_T1_LEN;
|
}
|
|
if (ESESTATUS_SUCCESS == status)
|
{
|
g_ese_ctx.max_IFSC = (uint16_t)(((uint16_t)g_atr.max_IFSC[0]) << 8);
|
g_ese_ctx.max_IFSC |= g_atr.max_IFSC[1];
|
g_ese_ctx.is_T1_ext_hdr_len = (g_atr.capbilities[1] & 0x08) != 0;
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function init_Iframe_from_cmd_apdu
|
* @Param p_cmd_apdu - 7816-4 APDU that shall be sent.
|
* cmd_len - Length of the apduCmd to be sent.
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS init_Iframe_from_cmd_apdu(uint8_t *p_cmd_apdu, uint16_t cmd_len)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
|
g_ese_ctx.p_T1_params = &g_T1_params;
|
g_ese_ctx.p_T1_params->is_device_chaining = (cmd_len > g_ese_ctx.max_IFSC ? true : false);
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
g_ese_ctx.p_T1_params->p_data_tx = p_cmd_apdu;
|
g_ese_ctx.p_T1_params->tx_len = cmd_len;
|
|
g_ese_ctx.p_T1_params->tx_data_offset = 0;
|
g_ese_ctx.p_T1_params->tx_len_part = 0;
|
g_ese_ctx.p_T1_params->p_data_tx_part = NULL;
|
|
g_ese_ctx.p_T1_params->resync_CIP_req_cnt = 0;
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function deinit_Iframe_from_cmd_apdu
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
void deinit_Iframe_from_cmd_apdu(void)
|
{
|
if (NULL != g_ese_ctx.p_T1_params)
|
{
|
g_ese_ctx.p_T1_params->p_data_tx = NULL;
|
|
if (NULL != g_ese_ctx.p_T1_params->p_data_tx_part)
|
{
|
g_ese_ctx.p_T1_params->p_data_tx_part = NULL;
|
}
|
|
g_ese_ctx.p_T1_params = NULL;
|
}
|
}
|
|
/******************************************************************************
|
* @Function free_tx_part_mem
|
******************************************************************************/
|
static void free_tx_part_mem(void)
|
{
|
if (NULL != g_ese_ctx.p_T1_params)
|
{
|
if (NULL != g_ese_ctx.p_T1_params->p_data_tx_part)
|
{
|
g_ese_ctx.p_T1_params->p_data_tx_part = NULL;
|
}
|
}
|
}
|
|
/******************************************************************************
|
* @Function reset_for_resync_rsp
|
******************************************************************************/
|
static void reset_for_resync_rsp(void)
|
{
|
g_ese_ctx.seq_num_card = 0;
|
g_ese_ctx.seq_num_device = 0;
|
g_ese_ctx.p_T1_params->recovery_cnt = 0;
|
g_ese_ctx.p_T1_params->resync_CIP_req_cnt = 0;
|
g_ese_ctx.p_T1_params->wtx_cnt = 0;
|
g_ese_ctx.p_T1_params->tx_data_offset = 0;
|
if (g_ese_ctx.p_T1_params->is_card_chaining)
|
{
|
t1_recv_data_list_destroy();
|
}
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
g_ese_ctx.p_T1_params->is_card_chaining = false;
|
}
|
|
/******************************************************************************
|
* @Function do_T1_recovery_at_undecode
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_FATAL_ERROR.
|
******************************************************************************/
|
ESESTATUS do_T1_recovery_at_undecode(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
if (g_ese_ctx.p_T1_params->recovery_cnt < g_ese_ctx.max_recovery_cnt)
|
{
|
g_ese_ctx.p_T1_params->recovery_cnt++;
|
|
if ((SFRAME != g_ese_ctx.p_T1_params->tx_frame_type) || (WTX_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type))
|
{
|
g_ese_ctx.next_T1_state = R_PARITY_ERR;
|
g_ese_ctx.p_T1_params->tx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_PARITY_ERROR;
|
// Should use the N(S), that is g_ese_ctx.seq_num_device
|
}
|
}
|
else
|
{
|
if (((SFRAME != g_ese_ctx.p_T1_params->tx_frame_type) || (WTX_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type)) && !g_ese_ctx.is_resync_recovery_flag)
|
{
|
g_ese_ctx.is_resync_recovery_flag = true;
|
|
// Do case 4
|
g_ese_ctx.next_T1_state = S_RESYNC_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = RESYNC_REQ;
|
}
|
else if (g_ese_ctx.is_resync_recovery_flag)
|
{
|
if (g_ese_ctx.p_T1_params->resync_CIP_req_cnt < g_ese_ctx.max_recovery_cnt)
|
{
|
g_ese_ctx.p_T1_params->resync_CIP_req_cnt++;
|
|
// Do case 5
|
g_ese_ctx.next_T1_state = S_CIP_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = CIP_REQ;
|
}
|
else
|
{
|
status = ESESTATUS_FATAL_ERROR;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
}
|
else
|
{
|
// Chip hard reset if warm reset failed.
|
if ((SFRAME == g_ese_ctx.p_T1_params->tx_frame_type) && (CIP_REQ == g_ese_ctx.p_T1_params->tx_sub_Sframe_type))
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: warm reset failed, TODO need chip hard reset to recover\r\n", __FUNCTION__);
|
// need to chip hard reset
|
status = ESESTATUS_FATAL_ERROR;
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: S(... REQ) failed, warm reset to recover lastTxFrameType = %d, lastTxSFrameType = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_frame_type, g_ese_ctx.p_T1_params->tx_sub_Sframe_type);
|
|
g_ese_ctx.next_T1_state = S_CIP_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = CIP_REQ;
|
}
|
}
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function do_T1_recovery_at_decode_Iframe
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_FATAL_ERROR.
|
******************************************************************************/
|
static ESESTATUS do_T1_recovery_at_decode_Iframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
T1_frame_types last_rx_frame_type = g_ese_ctx.p_T1_params->last_rx_frame_type;
|
T1_pcb_bits *p_pcb_bits = &g_ese_ctx.p_T1_params->last_rx_pcb_bits;
|
|
if (IFRAME == last_rx_frame_type)
|
{
|
if (g_ese_ctx.p_T1_params->recovery_cnt < g_ese_ctx.max_recovery_cnt)
|
{
|
g_ese_ctx.p_T1_params->recovery_cnt++;
|
if (p_pcb_bits->bit7 != g_ese_ctx.seq_num_card)
|
{
|
g_ese_ctx.next_T1_state = R_OTHER_ERR;
|
g_ese_ctx.p_T1_params->tx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_OTHER_ERROR;
|
}
|
else
|
{
|
// Cannot be occured this case
|
LOG_INFO(TRACE_MODULE_SE,
|
"%s: an I-block received, seqNum exception, terminate transmission. lastTxSeqNum = %d, lastRxExpectedNum = %d, lastRxSeqNum = %d\r\n",
|
__FUNCTION__, g_ese_ctx.seq_num_device, g_ese_ctx.seq_num_card, p_pcb_bits->bit7);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
status = ESESTATUS_INVALID_PARAMETER;
|
free_tx_part_mem();
|
}
|
}
|
else
|
{
|
// Should transmit RESYNC_REQ to recover.
|
LOG_INFO(TRACE_MODULE_SE, "%s: lastRecvFrameType = %d, recovery_cnt = %d, max_recovery_cnt = %d RESYNC_REQ should be sent to recover\r\n",
|
__FUNCTION__, last_rx_frame_type, g_ese_ctx.p_T1_params->recovery_cnt, g_ese_ctx.max_recovery_cnt);
|
// RESYNC_REQ only send once, recovery_cnt should not be reset to zero.
|
g_ese_ctx.is_resync_recovery_flag = true;
|
|
g_ese_ctx.next_T1_state = S_RESYNC_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = RESYNC_REQ;
|
}
|
}
|
else
|
{
|
status = ESESTATUS_INVALID_PARAMETER;
|
LOG_INFO(TRACE_MODULE_SE,
|
"%s: should be called when received an invalid IFrame, but non-IFrame received, lastRecvFrameType = %d, recovery_cnt = %d, max_recovery_cnt = "
|
"%d\r\n",
|
__FUNCTION__, last_rx_frame_type, g_ese_ctx.p_T1_params->recovery_cnt, g_ese_ctx.max_recovery_cnt);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
free_tx_part_mem();
|
}
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function do_T1_recovery_at_decode_Sframe
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_FATAL_ERROR.
|
******************************************************************************/
|
static ESESTATUS do_T1_recovery_at_decode_Sframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
T1_frame_types last_rx_frame_type = g_ese_ctx.p_T1_params->last_rx_frame_type;
|
Sframe_types last_rx_sub_Sframe_type = g_ese_ctx.p_T1_params->last_rx_sub_Sframe_type;
|
|
if ((SFRAME == last_rx_frame_type) && (NULL != g_ese_ctx.p_T1_params->p_data_tx))
|
{
|
// recovery_cnt will be ignored for RESYNC_RSP and IFS_RSP
|
if (RESYNC_RSP == last_rx_sub_Sframe_type)
|
{
|
reset_for_resync_rsp();
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
}
|
else if (IFS_RSP == last_rx_sub_Sframe_type)
|
{
|
if (g_ese_ctx.p_T1_params->is_card_chaining)
|
{
|
g_ese_ctx.next_T1_state = R_ACK;
|
g_ese_ctx.p_T1_params->tx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RACK;
|
}
|
else if (g_ese_ctx.p_T1_params->is_device_chaining)
|
{
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_INVALID_ERROR;
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block I-block is unchaining, terminate transmit when received IFS_RSP\r\n", __FUNCTION__);
|
// terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block is an I-block, lastTxFrameType = %d, but device received R-block, cannot be occurred.\r\n",
|
__FUNCTION__, last_rx_sub_Sframe_type);
|
// terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
}
|
else
|
{
|
status = ESESTATUS_INVALID_PARAMETER;
|
LOG_INFO(TRACE_MODULE_SE,
|
"%s: should be called when received an SFrame, but non-SFrame received, lastRecvFrameType = %d, recovery_cnt = %d, max_recovery_cnt = %d\r\n",
|
__FUNCTION__, last_rx_frame_type, g_ese_ctx.p_T1_params->recovery_cnt, g_ese_ctx.max_recovery_cnt);
|
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function do_T1_recovery_at_decode_Rframe
|
* @Returns On success return ESESTATUS_SUCCESS, else ESESTATUS_FATAL_ERROR.
|
******************************************************************************/
|
static ESESTATUS do_T1_recovery_at_decode_Rframe(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
T1_frame_types last_rx_frame_type = g_ese_ctx.p_T1_params->last_rx_frame_type;
|
Rframe_types last_rx_sub_Rframe_type = g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type;
|
|
if (RFRAME == last_rx_frame_type)
|
{
|
if (g_ese_ctx.p_T1_params->recovery_cnt < g_ese_ctx.max_recovery_cnt)
|
{
|
g_ese_ctx.p_T1_params->recovery_cnt++;
|
|
if ((NULL != g_ese_ctx.p_T1_params->p_data_tx))
|
{
|
// device initiates block is an I-block
|
if (IFRAME == g_ese_ctx.p_T1_params->tx_frame_type)
|
{
|
if (RACK == last_rx_sub_Rframe_type)
|
{
|
g_ese_ctx.next_T1_state = R_OTHER_ERR;
|
g_ese_ctx.p_T1_params->tx_frame_type = RFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type = RNACK_OTHER_ERROR;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = SFRAME_TYPE_INVALID;
|
LOG_INFO(TRACE_MODULE_SE, "%s: RACK for RNACK_OTHER_ERROR\r\n", __FUNCTION__);
|
}
|
else if (RNACK_PARITY_ERROR == last_rx_sub_Rframe_type)
|
{
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
// LOG_INFO(TRACE_MODULE_SE, "%s: RNACK_PARITY_ERROR, resend IFRAME\r\n", __FUNCTION__);
|
}
|
else if (RNACK_OTHER_ERROR == last_rx_sub_Rframe_type)
|
{
|
g_ese_ctx.seq_num_device ^= 0x01;
|
g_ese_ctx.is_seq_num_dr = true;
|
g_ese_ctx.next_T1_state = I_BLK;
|
g_ese_ctx.p_T1_params->tx_frame_type = IFRAME;
|
// LOG_INFO(TRACE_MODULE_SE, "%s: RNACK_OTHER_ERROR, resend IFRAME\r\n", __FUNCTION__);
|
}
|
else
|
{
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
LOG_INFO(
|
TRACE_MODULE_SE,
|
"%s: lastSendIFrame, lastRecvRFrame recovery failed, unkonwn lastRFrameType = %d, recovery_cnt = %d, max_recovery_cnt = %d\r\n",
|
__FUNCTION__, g_ese_ctx.p_T1_params->last_rx_sub_Rframe_type, g_ese_ctx.p_T1_params->recovery_cnt, g_ese_ctx.max_recovery_cnt);
|
}
|
}
|
else if (SFRAME == g_ese_ctx.p_T1_params->tx_frame_type)
|
{
|
if ((WTX_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type) || (IFS_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type) ||
|
(ABORT_RSP == g_ese_ctx.p_T1_params->tx_sub_Sframe_type))
|
{
|
LOG_INFO(TRACE_MODULE_SE,
|
"%s: device initiates block is an I-block, lastTxSubSFrameType = %d, but device received R-block, cannot be occurred.\r\n",
|
__FUNCTION__, g_ese_ctx.p_T1_params->tx_sub_Sframe_type);
|
// terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block is I-block, lastTxSubSFrameType = %d, recovery_cnt = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type, g_ese_ctx.p_T1_params->recovery_cnt);
|
}
|
}
|
else if (RFRAME == g_ese_ctx.p_T1_params->tx_frame_type)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block is I-block, lastTxSubRFrameType = %d, recovery_cnt = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_sub_Rframe_type, g_ese_ctx.p_T1_params->recovery_cnt);
|
}
|
else
|
{
|
// unknown R-block type, terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
LOG_INFO(TRACE_MODULE_SE, "%s: lastSend unknown frame type, recovery_cnt = %d\r\n", __FUNCTION__, g_ese_ctx.p_T1_params->recovery_cnt);
|
}
|
}
|
else if (SFRAME == g_ese_ctx.p_T1_params->tx_frame_type)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block is S-block, lastTxSubSFrameType = %d, recovery_cnt = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type, g_ese_ctx.p_T1_params->recovery_cnt);
|
}
|
else
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: device initiates block cannot be an R-block, lastTxFrameType = %d\r\n", __FUNCTION__,
|
g_ese_ctx.p_T1_params->tx_frame_type);
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
g_ese_ctx.is_resync_recovery_flag = false;
|
}
|
}
|
else
|
{
|
if (!g_ese_ctx.is_resync_recovery_flag && (NULL != g_ese_ctx.p_T1_params->p_data_tx))
|
{
|
g_ese_ctx.is_resync_recovery_flag = true;
|
g_ese_ctx.next_T1_state = S_RESYNC_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = RESYNC_REQ;
|
}
|
else if (g_ese_ctx.is_resync_recovery_flag && (NULL != g_ese_ctx.p_T1_params->p_data_tx))
|
{
|
free_tx_part_mem();
|
g_ese_ctx.is_resync_recovery_flag = false;
|
g_ese_ctx.next_T1_state = S_CIP_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = CIP_REQ;
|
}
|
else
|
{
|
status = ESESTATUS_INVALID_PARAMETER;
|
LOG_INFO(TRACE_MODULE_SE,
|
"%s: should be called when received an RFrame, but non-RFrame received, lastRecvFrameType = %d, recovery_cnt = %d, max_recovery_cnt = "
|
"%d\r\n",
|
__FUNCTION__, last_rx_frame_type, g_ese_ctx.p_T1_params->recovery_cnt, g_ese_ctx.max_recovery_cnt);
|
// terminate transmit
|
g_ese_ctx.next_T1_state = STATE_IDEL;
|
}
|
}
|
}
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_recv_data_store_in_list
|
* @Param p_data - 7816-4 APDU response that shall be stored.
|
* data_len - Length of the APDU response.
|
* total_len - T=1 total length, contains prologue and epilogue.
|
* @Returns On Success ESESTATUS_SUCCESS else ESESTATUS error.
|
******************************************************************************/
|
static ESESTATUS t1_recv_data_store_in_list(uint8_t *p_data, uint16_t data_len, uint16_t total_len)
|
{
|
if ((0 == data_len) || (data_len != (total_len - get_header_len(true))))
|
{
|
// Other error, do nothing
|
return ESESTATUS_INVALID_T1_LEN;
|
}
|
|
// if (g_recv_total_len == 0)
|
// {
|
// memset(g_all_data_buf, 0, sizeof(g_all_data_buf));
|
// }
|
memcpy(g_all_data_buf + g_recv_total_len, p_data, data_len);
|
g_recv_total_len += data_len;
|
return ESESTATUS_SUCCESS;
|
}
|
|
/******************************************************************************
|
* @Function t1_recv_data_list_destroy
|
******************************************************************************/
|
static void t1_recv_data_list_destroy(void)
|
{
|
g_recv_total_len = 0;
|
}
|
|
static void reset_ATR(void)
|
{
|
atr_info atr = {
|
.len = 0x13,
|
.vendor_ID = {0x00},
|
.dll_IC = 0x01,
|
.bgt = {0x00, 0x01},
|
.bwt = {0x03, 0xE8},
|
.max_freq = {0x4E, 0x20},
|
.checksum = 0x00,
|
.default_IFSC = 0xFE,
|
.num_channels = 0x01,
|
.max_IFSC = {0x00, 0xFF},
|
.capbilities = {0x00, 0x14}, // Extended frame length feature doesn't support
|
};
|
|
g_atr = atr;
|
}
|
|
/******************************************************************************
|
* @Function t1_transmit_receive_apdu
|
* @Param p_cmd_apdu - 7816-4 APDU that shall be sent.
|
* cmd_len - Length of the apduCmd to be sent.
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_transmit_receive_apdu(uint8_t *p_cmd_apdu, uint16_t cmd_len)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
// LOG_INFO(TRACE_MODULE_SE, "%s: Enter\r\n", __FUNCTION__);
|
status = init_Iframe_from_cmd_apdu(p_cmd_apdu, cmd_len);
|
if (ESESTATUS_SUCCESS != status)
|
{
|
goto cleanup;
|
}
|
|
status = Transmit_receive_process();
|
|
if (ESESTATUS_SUCCESS != status)
|
{
|
LOG_INFO(TRACE_MODULE_SE, "%s: t1_transmit_receive_apdu failed, status = %d\r\n", __FUNCTION__, status);
|
}
|
|
cleanup:
|
deinit_Iframe_from_cmd_apdu();
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_CIP_req
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_CIP_req(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
// LOG_INFO(TRACE_MODULE_SE, "t1CIPReq() enter\r\n");
|
|
g_ese_ctx.p_T1_params = &g_T1_params;
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
g_ese_ctx.next_T1_state = S_CIP_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = CIP_REQ;
|
|
status = Transmit_receive_process();
|
g_ese_ctx.p_T1_params = NULL;
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_ATR_req
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_ATR_req(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
|
g_ese_ctx.p_T1_params = &g_T1_params;
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
g_ese_ctx.next_T1_state = S_ATR_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = ATR_REQ;
|
|
status = Transmit_receive_process();
|
g_ese_ctx.p_T1_params = NULL;
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_IFSD_req
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_IFSD_req(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
|
g_ese_ctx.p_T1_params = &g_T1_params;
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
g_ese_ctx.next_T1_state = S_IFS_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = IFS_REQ;
|
|
status = Transmit_receive_process();
|
g_ese_ctx.p_T1_params = NULL;
|
|
return status;
|
}
|
|
/******************************************************************************
|
* @Function t1_prop_end_apdu_req
|
* @Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_prop_end_apdu_req(void)
|
{
|
ESESTATUS status = ESESTATUS_SUCCESS;
|
|
g_ese_ctx.p_T1_params = &g_T1_params;
|
g_ese_ctx.p_T1_params->is_device_chaining = false;
|
g_ese_ctx.next_T1_state = S_PROP_END_APDU_REQ;
|
g_ese_ctx.p_T1_params->tx_frame_type = SFRAME;
|
g_ese_ctx.p_T1_params->tx_sub_Sframe_type = PROP_END_APDU_REQ;
|
|
status = Transmit_receive_process();
|
g_ese_ctx.p_T1_params = NULL;
|
|
return status;
|
}
|
|
/******************************************************************************
|
* Function t1_receive_data_and_get
|
* @Param pp_Data - a pointer address to the received data.
|
* data_len - a pointer to the length of the received data.
|
* Returns On success return ESESTATUS_SUCCESS or else ESESTATUS error.
|
******************************************************************************/
|
ESESTATUS t1_receive_data_and_get(uint8_t **pp_Data, uint16_t *data_len)
|
{
|
*data_len = (uint16_t)g_recv_total_len;
|
*pp_Data = g_all_data_buf;
|
return ESESTATUS_SUCCESS;
|
}
|