blob: a4ca4943590c646031d80d8056b307e7332fe3a9 [file] [log] [blame]
/**
* @file sci2c.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2016,2020 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* This file implements the SCI2C Protocol Specification.
*
*****************************************************************************/
#include "smCom.h"
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include "sm_printf.h"
#include "sm_timer.h"
#include "i2c_a7.h"
#include "sci2c.h"
#include "sci2c_cfg.h"
#ifdef __gnu_linux__
#include <fcntl.h>
#include <sys/stat.h>
#include <linux/i2c-dev.h>
#include <unistd.h>
#endif
#include <time.h>
#if defined(SSS_USE_FTR_FILE)
#include "fsl_sss_ftr.h"
#else
#include "fsl_sss_ftr_default.h"
#endif
#include "nxLog_smCom.h"
#include "nxEnsure.h"
// #define LOG_I2C
// #define LOG_SCI2C_PARAMETER_EXCHANGE
#define DELAY_MSEC (1)
#define RX_LEN_SOFT_RESET (2)
#define CDB_ATS_MAX (31)
#define MAX_ATR_RESPONSE_LENGTH (CDB_ATS_MAX + 3) /* add length and PCB byte */
#define RX_LEN_PARAM_EXCH (2)
#define RX_LEN_BIND_SELECT (1)
#define RX_LEN_GET_STATUS (2)
#define DEFAULT_FWI (9) /* default frame waiting integer (15.1.3) */
#define APDU_GET_RESPONSE (0x02)
#define SLAVE_STATUS (0x7)
#define SLAVE_STATUS_BUSY ((0x1<<4) | SLAVE_STATUS)
#define SCI2C_M2S_OK 0x00
#define SCI2C_M2S_WRITE_BLOCK_FAILED 0x01
#define SCI2C_M2S_GET_STATUS_TIME_OUT 0x02
#define SCI2C_S2M_OK 0x03
#define SCI2C_S2M_READ_BLOCK_FAILED 0x04
#define SCI2C_tFW_DEF_ms 600 // Waiting time extension is assumed to be 600 ms
static U8 gSeqCtr = 0;
static uint8_t rxData[270];
static uint8_t txData[270];
static uint8_t * pRx = rxData;
static U8 GetCounter(void);
static U8 sci2c_WaitForStatusOkay(void* conn_ctx, U32 msec);
static i2c_status_t sci2c_SendByte(void* conn_ctx, sci2c_Data_t * pSci2cData);
static i2c_status_t sci2c_WriteBlock(void* conn_ctx, sci2c_Data_t * pSci2cData);
static i2c_status_t sci2c_ReadBlock(void* conn_ctx, sci2c_Data_t * pSci2cData, U8 * pRead, U16 * pReadLen);
static U16 sci2c_MasterToSlaveDataTx(void* conn_ctx, tSCI2C_Data_t * pSCI2C);
static U16 sci2c_SlaveToMasterDataTx(void* conn_ctx, tSCI2C_Data_t * pSCI2C);
static eSci2c_Error_t sci2c_Wakeup(void* conn_ctx);
static eSci2c_Error_t sci2c_SoftReset(void* conn_ctx, U8 * pRx, U16 * pRxLen);
static eSci2c_Error_t sci2c_ReadAnswerToReset(void* conn_ctx, U8 * pAtrResponse, U16 * pRxLen);
static eSci2c_Error_t sci2c_ParameterExchange(void* conn_ctx, sci2c_maxDataBytesS2M_t maxData, sci2c_maxDataBytesM2S_t * pResponse);
static eSci2c_Error_t sci2c_GetStatus(void* conn_ctx, U8* pStatus);
static eSci2c_Error_t sci2c_DataExchange(void* conn_ctx, tSCI2C_Data_t * pSCI2C);
/**
* @function sci2c_Init
* @description Initializes the SCI2C module.
* @param SCI2Catr IN: Pointer to the buffer that will contain ATR
* @param SCI2CatrLen IN: Pointer to length of provided buffer; OUT: Actual length of retrieved ATR
* @return
*/
eSci2c_Error_t sci2c_Init(void* conn_ctx, U8 *SCI2Catr, U16 *SCI2CatrLen)
{
uint8_t atr[64];
#if SSS_HAVE_A71XX
U8 status = SCI2C_STATUS_UNDEFINED; // SCI2C_STATUS_UNDEFINED allows to detect the case no A71CH is connected
int nGetStatus = 0;
#endif
uint16_t len = 0;
eSci2c_Error_t rv = eSci2c_Error;
uint16_t nrRead = 0;
sci2c_maxDataBytesM2S_t maxCommandLength;
ENSURE_OR_GO_EXIT(SCI2Catr != NULL);
ENSURE_OR_GO_EXIT(SCI2CatrLen != NULL);
#define GET_STATUS_MAX 70
gSeqCtr = 0; /* (re)set sequence counter */
#if SSS_HAVE_A71XX
// Keep on getting the status until the A7 is ready. Fetching the status
// will implicitly wake up the A7, so no explicit wake-up command is required.
//
// GET_STATUS_MAX puts an upperlimit on the amount of re-tries. This prevents
// the sci2c_Init call not returning in case e.g. no A7 is connected.
//
// Time-out for a (physically) not connected A7:
// GET_STATUS_MAX * T_WNCMD_ACTUAL = 7 sec
//
while (status != SCI2C_STATUS_NORMAL_READY)
{
sci2c_GetStatus(conn_ctx, &status);
if ((++nGetStatus) > GET_STATUS_MAX) {
sm_printf(DBGOUT, "Error: Failed to retrieve ready status.\r\n");
return eSci2c_Error;
}
if (status == SCI2C_STATUS_NORMAL_BUSY) {
sm_sleep(T_WNCMD_ACTUAL);
}
else if (status == SCI2C_STATUS_NORMAL_READY) {
// Will drop out of while loop
}
else if (status == SCI2C_STATUS_UNDEFINED) {
sm_sleep(T_WNCMD_ACTUAL);
}
else {
sm_printf(DBGOUT, "Recover from Status=0x%02X by sci2c_SoftReset.\r\n", status);
rv = sci2c_SoftReset(conn_ctx, pRx, &nrRead);
if (rv != eSci2c_No_Error)
{
sm_printf(DBGOUT, "Soft reset failed\r\n");
}
}
}
// sci2c_Wakeup();
// sm_sleep(T_WNCMD_ACTUAL); // Maximum value is 200 ms
#endif
rv = sci2c_SoftReset(conn_ctx, pRx, &nrRead);
if (rv != eSci2c_No_Error)
{
sm_printf(DBGOUT, "Soft reset failed\r\n");
return eSci2c_Error;
}
sm_sleep(5 * DELAY_MSEC);
/* --- read ATR --- */
// sm_printf(DBGOUT, "reading ATR\r\n");
rv = sci2c_ReadAnswerToReset(conn_ctx, atr, &len);
if ((rv != eSci2c_No_Error) || (len == 0))
{
*SCI2CatrLen = 0;
sm_printf(DBGOUT, "Error reading ATR\r\n");
return eSci2c_Error;
}
else
{
// Copy ATR
*SCI2CatrLen = (len < (*SCI2CatrLen)) ? len : (*SCI2CatrLen);
memcpy(SCI2Catr, atr, *SCI2CatrLen);
}
/* --- param exchange --- */
#ifdef LOG_SCI2C_PARAMETER_EXCHANGE
printf("\r\n-----------sci2c_ParameterExchange ------------------\r\n");
#endif
rv = sci2c_ParameterExchange(conn_ctx, SMCOM_MAX_BYTES, &maxCommandLength);
/* next types are casted to U32 for comparison; if types do not match, this comparison needs to be changed */
if ((rv != eSci2c_No_Error) || ((U32) maxCommandLength != (U32) SMCOM_MAX_BYTES))
{
sm_printf(DBGOUT, "Error setting parameter exchange\r\n");
return eSci2c_Error;
}
#ifdef LOG_SCI2C_PARAMETER_EXCHANGE
printf("\r\n-----------done------------------\r\n");
#endif
exit:
return rv;
}
void sci2c_SetSequenceCounter(U8 seqCounter)
{
gSeqCtr = seqCounter;
}
U8 sci2c_GetSequenceCounter()
{
return gSeqCtr;
}
/**
* Terminates I2C connection without breaking SCI2C link.
*
* @param[in] full Can be either 0 or 1. A value of 0 corresponds to a 'light-weight' terminate.
* This parameter is basically a hack to enable the Bootloader to Host-OS SCP03 key handover scenario with the
* Raspberry Pi's GPIO based I2C master scenario.
* To allow for this scenario the 'full' parameter must be set to '0'.
*/
void sci2c_TerminateI2C(U8 full)
{
axI2CTerm(NULL, full);
}
static eSci2c_Error_t sci2c_Wakeup(void* conn_ctx)
{
i2c_status_t i2cErr;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
pSci2cData->pcb = PCB_WAKEUP;
pSci2cData->dataLen = 0;
#if AX_EMBEDDED
axI2CResetBackoffDelay();
#endif
i2cErr = sci2c_SendByte(conn_ctx, pSci2cData);
// Depending on the speed of the I2C master controller
// an extra delay may have to be inserted here to ensure
// the secure module is awake by the next command.
// The SCI2C specification mandates a minimum delay of 180 microsec between the
// end of the wakeup command and the start of the next command.
#if AX_EMBEDDED
if (i2cErr == i2c_NoAddrAck ) {
sm_usleep(SCI2C_T_CMDG);
return eSci2c_No_Error;
}
else
#endif
if (i2cErr != i2c_Okay)
{
return eSci2c_Error;
}
else
{
return eSci2c_No_Error;
}
}
/**
* @function sci2c_SoftReset
* @description
* @param pRx Answer to reset command.
* @param pRxLen number of bytes read in response to reset command.
* @return
*/
static eSci2c_Error_t sci2c_SoftReset(void* conn_ctx, U8 * pRx, U16 * pRxLen)
{
U8 i = 0;
eSci2c_Error_t rv = eSci2c_Protocol_Error;
i2c_status_t i2cErr;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
ENSURE_OR_GO_EXIT(pRx != NULL);
ENSURE_OR_GO_EXIT(pRxLen != NULL);
pSci2cData->pcb = PCB_SOFT_RESET;
pSci2cData->dataLen = 0;
for (i = 0; i < N_RETRY_SRST; i++)
{
i2cErr = sci2c_ReadBlock(conn_ctx, pSci2cData, pRx, pRxLen);
if (i2cErr == i2c_Okay)
{
rv = eSci2c_No_Error;
break;
}
// else if (i2cErr != i2c_NoAddrAck) // retry only if NAK on address
// {
// rv = eSci2c_Protocol_Error;
// break;
// }
}
/* spec: wait (at least 5 msec) */
sm_sleep(T_RSTG);
exit:
return rv;
}
/**
* @function sci2c_ReadAnswerToReset
* @description
* @param *pAtrResponse Pointer to struct containing the ATR response.
* @param pRxLen number of bytes read in response to reset command.
* @return
*/
static eSci2c_Error_t sci2c_ReadAnswerToReset(void* conn_ctx, U8 * pAtrResponse, U16 * pRxLen)
{
i2c_status_t i2cErr = i2c_Okay;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
U16 nrRead = 0;
eSci2c_Error_t rv = eSci2c_Error;
ENSURE_OR_GO_EXIT(pAtrResponse != NULL);
ENSURE_OR_GO_EXIT(pRxLen != NULL);
pSci2cData->pcb = PCB_READ_ATR;
pSci2cData->dataLen = 0;
i2cErr = sci2c_ReadBlock(conn_ctx, pSci2cData, pAtrResponse, &nrRead);
if ((nrRead > MAX_ATR_RESPONSE_LENGTH) || (i2cErr == i2c_Failed) || (i2cErr == i2c_Sci2cException))
{
return eSci2c_Error;
}
else if (i2cErr == i2c_NoAddrAck)
{
sm_printf(DBGOUT, "no addr ack\r\n");
return eSci2c_Error;
}
/* strip the 1st (=length) and 2nd (=PCB) byte */
memmove(pAtrResponse, pAtrResponse + 2, nrRead - 2);
memset(&pAtrResponse[nrRead - 1], 0x00, 2); // clear the data in memory after copying
nrRead -= 2;
*pRxLen = nrRead;
rv = eSci2c_No_Error;
exit:
return rv;
}
/**
* @function sci2c_ParameterExchange
* @description
* @param maxData
* @return
*/
static eSci2c_Error_t sci2c_ParameterExchange(void* conn_ctx, sci2c_maxDataBytesS2M_t maxData, sci2c_maxDataBytesM2S_t * pResponse)
{
sci2c_Data_t sci2cData;
sci2c_Data_t *pSci2cData = (sci2c_Data_t *)&sci2cData;
eSci2c_Error_t rv = eSci2c_Error;
U8 i = 0;
U16 nrRead = 0;
i2c_status_t i2cErr;
ENSURE_OR_GO_EXIT(pResponse != NULL);
pSci2cData->pcb = (U8)(PCB_PARAM_EXCH | (maxData << 6));
pSci2cData->dataLen = 0;
for (i = 0; i < N_RETRY_PE; i++) /* try N_RETRY_PE times if not okay */
{
i2cErr = sci2c_ReadBlock(conn_ctx, pSci2cData, pRx, &nrRead);
if ((i2cErr == i2c_Okay) && (((pRx[1] & 0x0C) >> 2) == ((~pRx[1] & 0x30) >> 4)) &&
(((pRx[1] & 0xC0) >> 6) == maxData)) {
*pResponse = (sci2c_maxDataBytesM2S_t)((pRx[1] & 0xC0) >> 6);
rv = eSci2c_No_Error;
break;
}
else {
printf("\r\nagain\r\n");
}
}
exit:
return rv;
}
/**
* @function sci2c_GetStatus
* @description Issues a Status command and will return the status of the slave.
* @param pStatus The status of the slave.
* @return
*/
static eSci2c_Error_t sci2c_GetStatus(void* conn_ctx, U8* pStatus)
{
eSci2c_Error_t rv = eSci2c_Error;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
U16 nrRead = 0;
i2c_status_t i2cErr;
ENSURE_OR_GO_EXIT(pStatus != NULL);
pSci2cData->pcb = PCB_STATUS;
pSci2cData->dataLen = 0;
i2cErr = sci2c_ReadBlock(conn_ctx, pSci2cData, pRx, &nrRead);
if ( (i2cErr == i2c_Okay) || (i2cErr == i2c_Sci2cException) )
{
if ((nrRead == 2) && /* 2 bytes read: LEN and PCB */
((pRx[1] & 0x0F) == 0x07)) /* PCB indicates status response */
{
*pStatus = ((pRx[1] & 0xF0) >> 4);
if ((*pStatus == SCI2C_STATUS_NORMAL_READY) || (*pStatus == SCI2C_STATUS_NORMAL_BUSY))
{
rv = eSci2c_No_Error;
}
else
{
/* the specification requires to indicate a protocol exception. */
rv = eSci2c_Protocol_Error;
}
}
}
// NOTE-1: The underlying driver doesn't return a value of i2c_NoAddrAck
else if (i2cErr == i2c_NoAddrAck)
{
// printf("sci2c_GetStatus: i2c_NoAddrAck\r\n");
rv = eSci2c_NackOnAddr;
}
else
// (Refer to NOTE-1 above) If the slave did not acknowledge the I2C address, eSci2c_Error is returned as well
{
rv = eSci2c_Error;
}
exit:
return rv;
}
static U16 sci2c_MasterToSlaveDataTx(void* conn_ctx, tSCI2C_Data_t * pSCI2C)
{
U8 maxI2CPacketLen = 253; /* limited by the I2C driver */
U16 remaining = 0;
U16 alreadyParsed = 0;
U8 nrBytesToTx = 0;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
i2c_status_t i2cErr;
U16 nStatus = SCI2C_M2S_WRITE_BLOCK_FAILED;
ENSURE_OR_GO_EXIT(pSCI2C != NULL);
nStatus = SCI2C_M2S_OK;
if (pSCI2C->buflen > maxI2CPacketLen) /* chaining */
{
remaining = pSCI2C->buflen;
while (remaining > 0)
{
pSci2cData->pcb = (U8)((GetCounter() << 4) | pSCI2C->edc | pSCI2C->dir);
if (remaining > maxI2CPacketLen)
{
nrBytesToTx = maxI2CPacketLen;
pSci2cData->pcb |= (0x1 << 7); /* set the More bit */
}
else
{
nrBytesToTx = (U8) remaining; /* cast safe: remaining is always < maxI2CPacketLen */
}
pSci2cData->pData = &pSCI2C->pBuf[alreadyParsed];
pSci2cData->dataLen = nrBytesToTx;
remaining -= nrBytesToTx;
i2cErr = sci2c_WriteBlock(conn_ctx, pSci2cData);
// i2c_Okay, /* the command has been transmitted succesfully */
// i2c_NoAddrAck, /* the slave does not acknowledge the address byte */
// i2c_Failed,
if (i2cErr != i2c_Okay)
{
sm_printf(DBGOUT, "sci2c_MasterToSlaveDataTx (line=%d): sci2c_WriteBlock failed with %d.\r\n", __LINE__, i2cErr);
nStatus = SCI2C_M2S_WRITE_BLOCK_FAILED;
break;
}
alreadyParsed += nrBytesToTx;
if ((pSci2cData->pcb & 0x80) != 0) /* more bit is set => check status and wait for okay */
{
/* wait until the status is okay after a packet with the More bit set */
sm_usleep(T_CMDG_USec);
U8 okay = sci2c_WaitForStatusOkay(conn_ctx, APP_SCI2C_TIMEOUT_ms);
if (!okay)
{
sm_printf(DBGOUT, "sci2c_MasterToSlaveDataTx: stack specific timeout expired waiting for status\r\n");
nStatus = SCI2C_M2S_GET_STATUS_TIME_OUT;
}
}
}
}
else
{
pSci2cData->pcb = (U8)((GetCounter() << 4) | pSCI2C->edc | pSCI2C->dir);
pSci2cData->pData = pSCI2C->pBuf;
pSci2cData->dataLen = (U8) pSCI2C->buflen; /* cast safe: pSCI2C->buflen is always < 255 */
i2cErr = sci2c_WriteBlock(conn_ctx, pSci2cData);
if (i2cErr != i2c_Okay)
{
sm_printf(DBGOUT, "sci2c_MasterToSlaveDataTx (line=%d): sci2c_WriteBlock failed with %d.\r\n", __LINE__, i2cErr);
nStatus = SCI2C_M2S_WRITE_BLOCK_FAILED;
}
}
exit:
return nStatus;
}
static U16 sci2c_SlaveToMasterDataTx(void* conn_ctx, tSCI2C_Data_t * pSCI2C)
{
U8 reTxBit = 0;
U16 nrRead = 0;
U16 alreadyParsed = 0;
sci2c_Data_t sci2cData;
sci2c_Data_t * pSci2cData = (sci2c_Data_t *) &sci2cData;
i2c_status_t i2cErr = i2c_Failed;
U8 moreBitSet = 1; // Ensures we also retry after a NACK on the first Slave to Master Data transmission command
U16 nStatus = SCI2C_S2M_READ_BLOCK_FAILED;
#define MAX_IGNORE_NACK 250
U16 nack_count = 0;
// here we add the more bit in order to get it running
ENSURE_OR_GO_EXIT(pSCI2C != NULL);
#if SSS_HAVE_A71XX
pSci2cData->pcb = (U8)((reTxBit<<7) | pSCI2C->dir | 0x80);
/* On SE050 based IC
* pSci2cData->pcb = (U8)((reTxBit<<7) | pSCI2C->dir); */
#endif
pSci2cData->dataLen = 0;
do
{
i2cErr = sci2c_ReadBlock(conn_ctx, pSci2cData, pRx, &nrRead);
switch(i2cErr)
{
case i2c_Okay:
if (!((nrRead == 2) && (pRx[1] == SLAVE_STATUS_BUSY)))
{
/* The slave is not busy, check the more bit */
nack_count = 0;
moreBitSet = (pRx[1] & (0x1 << 7)) >> 7;
memcpy(&pSCI2C->pBuf[alreadyParsed], &pRx[2], nrRead - 2);
alreadyParsed += (nrRead - 2);
}
// The last data transmission command shall always have the More bit set to 0.
pSci2cData->pcb &= 0x7F;
break;
case i2c_Failed:
// Fall through on purpose
case i2c_Sci2cException:
break;
case i2c_NoAddrAck:
if (nack_count > MAX_IGNORE_NACK) {
i2cErr = i2c_Failed;
}
else {
nack_count++;
sm_sleep(2);
}
break;
default:
sm_printf(DBGOUT, "sci2c_SlaveToMasterDataTx: sci2c_ReadBlock returned %x\r\n", i2cErr);
break;
}
} while ( (moreBitSet == 1) && (i2cErr != i2c_Failed) );
pSCI2C->buflen = alreadyParsed;
if (i2cErr == i2c_Okay)
{
nStatus = SCI2C_S2M_OK;
}
else
{
nStatus = SCI2C_S2M_READ_BLOCK_FAILED;
}
exit:
return nStatus;
}
/**
* @function sci2c_DataExchange
* @description Used for sending and receiving the response after the status is okay.
* @param pSCI2C Pointer to the SCI2C data structure
*/
static eSci2c_Error_t sci2c_DataExchange(void* conn_ctx, tSCI2C_Data_t * pSCI2C)
{
U16 nStatus = SCI2C_M2S_OK;
U8 okay = 0;
eSci2c_Error_t rv = eSci2c_Error;
ENSURE_OR_GO_EXIT(pSCI2C != NULL);
if (pSCI2C->dir == eSci2c_DirectionM2S) /* master to slave */
{
nStatus = sci2c_MasterToSlaveDataTx(conn_ctx, pSCI2C);
if (nStatus != SCI2C_M2S_OK)
{
return eSci2c_Error;
}
/* wait until status is okay after sending the complete payload stream */
sm_usleep(T_CMDG_USec);
okay = sci2c_WaitForStatusOkay(conn_ctx, APP_SCI2C_TIMEOUT_ms);
if (!okay)
{
sm_printf(DBGOUT, "timeout expired waiting for status (3)\r\n");
return eSci2c_Error;
}
}
else /* slave to master */
{
nStatus = sci2c_SlaveToMasterDataTx(conn_ctx, pSCI2C);
if (nStatus != SCI2C_S2M_OK)
{
return eSci2c_Error;
}
}
rv = eSci2c_No_Error;
exit:
return rv;
}
/* -------------------------- local functions ----------------------------- */
static U8 GetCounter(void)
{
if (gSeqCtr >= 8)
{
gSeqCtr = 0;
}
return gSeqCtr++;
}
/* sci2c_WaitForStatusOkay
* Returns 1 if the status is okay before the timeout expires.
*/
static U8 sci2c_WaitForStatusOkay(void* conn_ctx, U32 msec)
{
U8 status = SCI2C_STATUS_EXCEPTION_OTHER;
eSci2c_Error_t rv = eSci2c_Error;
U32 time = 0;
U32 waitingOnStatusResponse = 0;
// Get the status until the status indicates 'slave ready'
// - In case the Waiting Time Extension Period has expired: Return with an error
while (time < msec)
{
rv = sci2c_GetStatus(conn_ctx, &status);
if ( (rv == eSci2c_No_Error) && (status == SCI2C_STATUS_NORMAL_READY) )
{
break;
}
else if ( (rv == eSci2c_No_Error) && (status == SCI2C_STATUS_NORMAL_BUSY) )
{
// Status busy resets timer for Waiting Time Extension violation.
waitingOnStatusResponse = 0;
}
// else if ( (rv == eSci2c_Error) || (rv == eSci2c_Protocol_Error) )
// {
// printf("sci2c_WaitForStatusOkay: sci2c_GetStatus returned %d (status == 0x%02X)\r\n", rv, status);
// return 0;
// }
waitingOnStatusResponse += SCI2C_tMD_ms;
if ( waitingOnStatusResponse > SCI2C_tFW_DEF_ms)
{
// Violation on Waiting Time extension
return 0;
}
time += SCI2C_tMD_ms;
sm_sleep(SCI2C_tMD_ms);
}
if (time >= msec)
{
return 0;
}
else
{
return 1;
}
}
/* ---------------------- */
/**
* Packages and sends the APDU command via the SCI2C protocol.
* Receives and unwraps the APDU response via the SCI2C protocol.
* @param[in,out] pApdu APDU structure.
* @retval ::SMCOM_OK Successful execution
* @retval ::SMCOM_SND_FAILED
* @retval ::SMCOM_RCV_FAILED
*/
U32 sci2c_Transceive(void* conn_ctx, apdu_t * pApdu)
{
uint16_t expectedLen = 0;
U8 tx[1] = { APDU_GET_RESPONSE };
tSCI2C_Data_t oSCI2C;
tSCI2C_Data_t * pSCI2C = (tSCI2C_Data_t *) &oSCI2C;
eSci2c_Error_t sci2c_Error = eSci2c_No_Error;
U32 rv = SMCOM_SND_FAILED;
ENSURE_OR_GO_EXIT(pApdu != NULL);
pSCI2C->dir = eSci2c_DirectionM2S;
pSCI2C->edc = eEdc_NoErrorDetection;
pSCI2C->pBuf = (U8*) pApdu->pBuf;
pSCI2C->buflen = pApdu->buflen;
sci2c_Wakeup(conn_ctx);
sci2c_Error = sci2c_DataExchange(conn_ctx, pSCI2C);
if (sci2c_Error != eSci2c_No_Error)
{
return SMCOM_SND_FAILED;
}
pSCI2C->dir = eSci2c_DirectionS2M;
pSCI2C->edc = eEdc_NoErrorDetection;
pSCI2C->pBuf = (U8*) &tx;
pSCI2C->buflen = 1;
// default Rx buffer
if (pApdu->pBuf == NULL)
{
pApdu->pBuf = pSCI2C->pBuf;
}
pSCI2C->pBuf = pApdu->pBuf;
pSCI2C->buflen = expectedLen;
sci2c_Error = sci2c_DataExchange(conn_ctx, pSCI2C);
pApdu->rxlen = pSCI2C->buflen;
// reset offset for subsequent response parsing
pApdu->offset = 0;
if (sci2c_Error != eSci2c_No_Error)
{
return SMCOM_RCV_FAILED;
}
rv = SMCOM_OK;
exit:
return rv;
}
/**
* Packages and sends the \p pTx byte array via the SCI2C protocol.
* Receives and unwraps the APDU response via the SCI2C protocol and stores the result in \p pRx.
* @param[in] pTx The input buffer
* @param[in] txLen The input buffer length.
* @param[in,out] pRx The output buffer.
* @param[in,out] pRxLen The output buffer length.
* @retval ::SMCOM_OK Successful execution
* @retval ::SMCOM_SND_FAILED
* @retval ::SMCOM_RCV_FAILED
*/
U32 sci2c_TransceiveRaw(void* conn_ctx, U8 * pTx, U16 txLen, U8 * pRx, U32 * pRxLen)
{
U8 tx[1] = { APDU_GET_RESPONSE };
tSCI2C_Data_t oSCI2C;
tSCI2C_Data_t * pSCI2C = (tSCI2C_Data_t *) &oSCI2C;
eSci2c_Error_t sci2c_Error = eSci2c_No_Error;
pSCI2C->dir = eSci2c_DirectionM2S;
pSCI2C->edc = eEdc_NoErrorDetection;
pSCI2C->pBuf = pTx;
pSCI2C->buflen = txLen;
sci2c_Wakeup(conn_ctx);
sci2c_Error = sci2c_DataExchange(conn_ctx, pSCI2C);
if (sci2c_Error != eSci2c_No_Error)
{
return SMCOM_SND_FAILED;
}
pSCI2C->dir = eSci2c_DirectionS2M;
pSCI2C->edc = eEdc_NoErrorDetection;
pSCI2C->pBuf = (U8*) &tx;
pSCI2C->buflen = 1;
pSCI2C->pBuf = pRx;
sci2c_Error = sci2c_DataExchange(conn_ctx, pSCI2C);
*pRxLen = pSCI2C->buflen;
if (sci2c_Error != eSci2c_No_Error)
{
return SMCOM_RCV_FAILED;
}
return SMCOM_OK;
}
static i2c_status_t sci2c_SendByte(void* conn_ctx, sci2c_Data_t * pSci2cData)
{
i2c_error_t status = I2C_FAILED;
ENSURE_OR_GO_EXIT(pSci2cData != NULL);
txData[0] = pSci2cData->pcb;
if (pSci2cData->dataLen > 0) /* add length byte + data */
{
return i2c_Failed;
}
/* only write 1 byte (the PCB) */
#ifdef PLATFORM_IMX
status = axI2CWriteByte(conn_ctx, I2C_BUS_0, SMCOM_I2C_ADDRESS, (U8 *) &txData);
#else
status = axI2CWrite(conn_ctx, I2C_BUS_0, SMCOM_I2C_ADDRESS, (U8 *) &txData, 1);
#endif
if (status == I2C_OK)
{
return i2c_Okay;
}
else if (status == I2C_NACK_ON_ADDRESS)
{
return i2c_NoAddrAck;
}
else
{
// NOTE: An error code of 0x0D may be the (legitimate) return code
// for an SCI2C wakeup command.
#ifndef PLATFORM_IMX
sm_printf(DBGOUT, "I2C status: 0x%02X\r\n", status);
#endif
return i2c_Failed;
}
exit:
return i2c_Failed;
}
static i2c_status_t sci2c_WriteBlock(void* conn_ctx, sci2c_Data_t * pSci2cData)
{
U16 write_len = 1; /* PCB byte */
#ifdef LOG_I2C
int i = 0;
#endif
i2c_error_t status = I2C_FAILED;
ENSURE_OR_GO_EXIT(pSci2cData != NULL);
txData[0] = pSci2cData->pcb;
if (pSci2cData->dataLen > 0)
{
txData[1] = pSci2cData->dataLen;
memcpy(&txData[2], pSci2cData->pData, pSci2cData->dataLen);
write_len += pSci2cData->dataLen + 1; /* add LEN byte + data length */
}
#ifdef LOG_I2C
printf("\r\n/send ");
for (i = 0; i< write_len; i++)
{
printf("%02x", txData[i]);
}
printf("\r\n");
#endif
status = axI2CWrite(conn_ctx, I2C_BUS_0, SMCOM_I2C_ADDRESS, (U8 *) &txData, write_len);
#ifdef LOG_I2C
printf("WRITE BLOCK done: %d\r\n", status);
#endif
if (status == I2C_OK)
{
return i2c_Okay;
}
else if (status == I2C_STARTED)
{
return i2c_Okay;
}
else if (status == I2C_NACK_ON_ADDRESS)
{
return i2c_NoAddrAck;
}
else
{
sm_printf(DBGOUT, "I2C status %x\r\n", status);
return i2c_Failed;
}
exit:
return i2c_Failed;
}
//
// The values returned by axI2CWriteRead depend on the driver implementation.
//
static i2c_status_t sci2c_ReadBlock(void* conn_ctx, sci2c_Data_t * pSci2cData, U8 * pRead, U16 * pReadLen)
{
U16 readlen = 0;
i2c_error_t status = I2C_FAILED;
U16 write_len = 1; /* LEN byte */
i2c_status_t rv = i2c_Failed;
ENSURE_OR_GO_EXIT(pSci2cData != NULL);
ENSURE_OR_GO_EXIT(pRead != NULL);
ENSURE_OR_GO_EXIT(pReadLen != NULL);
#ifdef LOG_I2C
int i = 0;
#endif
sm_sleep(DELAY_MSEC);
txData[0] = pSci2cData->pcb;
status = axI2CWriteRead(conn_ctx, I2C_BUS_0, SMCOM_I2C_ADDRESS, (U8 *) &txData, write_len, pRead, (U16 *) &readlen);
if (status != I2C_OK)
{
// (Try) not to report NAKs on address: This approach (currently) does not work on iMX Linux driver
// so a nack ripples up as an i2c_failed!
if (status != I2C_NACK_ON_ADDRESS)
{
return i2c_Failed;
}
else
{
return i2c_NoAddrAck;
}
}
else if (readlen < 2)
{
// should not happen
sm_printf(DBGOUT,"Error: %d bytes read\r\n", readlen);
return i2c_Failed;
}
else if ((pRead[0] + 1) != readlen) /* the LEN byte does not match the number of read bytes */
{
sm_printf(DBGOUT,"Wrong length %02X %02X\r\n", pRead[0], pRead[1]);
return i2c_Failed;
}
else if ((pRead[1] & 0x8F) == 0x87) /* the slave indicates an exception */
{
sm_printf(DBGOUT,"Protocol exception %02X %02X\r\n", pRead[0], pRead[1]);
*pReadLen = readlen;
sm_sleep(DELAY_MSEC);
return i2c_Sci2cException;
}
#ifdef LOG_I2C
printf("\r\n/rcv ");
for (i = 0; i< readlen; i++)
{
printf("%02x", pRead[i]);
}
printf("\r\n");
#endif
sm_sleep(DELAY_MSEC);
*pReadLen = readlen;
rv = i2c_Okay;
exit:
return rv;
}