mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-27 06:50:21 +00:00
bde3a47078
* Git: set git attributes to automatically manage line endings. * Git: cleanup gitignore file
853 lines
35 KiB
C
Executable file
853 lines
35 KiB
C
Executable file
|
|
/******************************************************************************
|
|
* \attention
|
|
*
|
|
* <h2><center>© COPYRIGHT 2020 STMicroelectronics</center></h2>
|
|
*
|
|
* Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License");
|
|
* You may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* www.st.com/myliberty
|
|
*
|
|
* 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,
|
|
* AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* PROJECT: ST25R391x firmware
|
|
* Revision:
|
|
* LANGUAGE: ISO C99
|
|
*/
|
|
|
|
/*! \file rfal_nfca.c
|
|
*
|
|
* \author Gustavo Patricio
|
|
*
|
|
* \brief Provides several NFC-A convenience methods and definitions
|
|
*
|
|
* It provides a Poller (ISO14443A PCD) interface and as well as
|
|
* some NFC-A Listener (ISO14443A PICC) helpers.
|
|
*
|
|
* The definitions and helpers methods provided by this module are only
|
|
* up to ISO14443-3 layer
|
|
*
|
|
*/
|
|
|
|
/*
|
|
******************************************************************************
|
|
* INCLUDES
|
|
******************************************************************************
|
|
*/
|
|
#include "rfal_nfca.h"
|
|
#include "utils.h"
|
|
|
|
/*
|
|
******************************************************************************
|
|
* ENABLE SWITCH
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef RFAL_FEATURE_NFCA
|
|
#define RFAL_FEATURE_NFCA false /* NFC-A module configuration missing. Disabled by default */
|
|
#endif
|
|
|
|
#if RFAL_FEATURE_NFCA
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL DEFINES
|
|
******************************************************************************
|
|
*/
|
|
|
|
#define RFAL_NFCA_SLP_FWT rfalConvMsTo1fc(1) /*!< Check 1ms for any modulation ISO14443-3 6.4.3 */
|
|
#define RFAL_NFCA_SLP_CMD 0x50U /*!< SLP cmd (byte1) Digital 1.1 6.9.1 & Table 20 */
|
|
#define RFAL_NFCA_SLP_BYTE2 0x00U /*!< SLP byte2 Digital 1.1 6.9.1 & Table 20 */
|
|
#define RFAL_NFCA_SLP_CMD_POS 0U /*!< SLP cmd position Digital 1.1 6.9.1 & Table 20 */
|
|
#define RFAL_NFCA_SLP_BYTE2_POS 1U /*!< SLP byte2 position Digital 1.1 6.9.1 & Table 20 */
|
|
|
|
#define RFAL_NFCA_SDD_CT 0x88U /*!< Cascade Tag value Digital 1.1 6.7.2 */
|
|
#define RFAL_NFCA_SDD_CT_LEN 1U /*!< Cascade Tag length */
|
|
|
|
#define RFAL_NFCA_SLP_REQ_LEN 2U /*!< SLP_REQ length */
|
|
|
|
#define RFAL_NFCA_SEL_CMD_LEN 1U /*!< SEL_CMD length */
|
|
#define RFAL_NFCA_SEL_PAR_LEN 1U /*!< SEL_PAR length */
|
|
#define RFAL_NFCA_SEL_SELPAR rfalNfcaSelPar(7U, 0U)/*!< SEL_PAR on Select is always with 4 data/nfcid */
|
|
#define RFAL_NFCA_BCC_LEN 1U /*!< BCC length */
|
|
|
|
#define RFAL_NFCA_SDD_REQ_LEN (RFAL_NFCA_SEL_CMD_LEN + RFAL_NFCA_SEL_PAR_LEN) /*!< SDD_REQ length */
|
|
#define RFAL_NFCA_SDD_RES_LEN (RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_BCC_LEN) /*!< SDD_RES length */
|
|
|
|
#define RFAL_NFCA_T_RETRANS 5U /*!< t RETRANSMISSION [3, 33]ms EMVCo 2.6 A.5 */
|
|
#define RFAL_NFCA_N_RETRANS 2U /*!< Number of retries EMVCo 2.6 9.6.1.3 */
|
|
|
|
|
|
/*! SDD_REQ (Select) Cascade Levels */
|
|
enum
|
|
{
|
|
RFAL_NFCA_SEL_CASCADE_L1 = 0, /*!< SDD_REQ Cascade Level 1 */
|
|
RFAL_NFCA_SEL_CASCADE_L2 = 1, /*!< SDD_REQ Cascade Level 2 */
|
|
RFAL_NFCA_SEL_CASCADE_L3 = 2 /*!< SDD_REQ Cascade Level 3 */
|
|
};
|
|
|
|
/*! SDD_REQ (Select) request Cascade Level command Digital 1.1 Table 15 */
|
|
enum
|
|
{
|
|
RFAL_NFCA_CMD_SEL_CL1 = 0x93, /*!< SDD_REQ command Cascade Level 1 */
|
|
RFAL_NFCA_CMD_SEL_CL2 = 0x95, /*!< SDD_REQ command Cascade Level 2 */
|
|
RFAL_NFCA_CMD_SEL_CL3 = 0x97, /*!< SDD_REQ command Cascade Level 3 */
|
|
};
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL MACROS
|
|
******************************************************************************
|
|
*/
|
|
#define rfalNfcaSelPar( nBy, nbi ) (uint8_t)((((nBy)<<4U) & 0xF0U) | ((nbi)&0x0FU) ) /*!< Calculates SEL_PAR with the bytes/bits to be sent */
|
|
#define rfalNfcaCLn2SELCMD( cl ) (uint8_t)((uint8_t)(RFAL_NFCA_CMD_SEL_CL1) + (2U*(cl))) /*!< Calculates SEL_CMD with the given cascade level */
|
|
#define rfalNfcaNfcidLen2CL( len ) ((len) / 5U) /*!< Calculates cascade level by the NFCID length */
|
|
#define rfalNfcaRunBlocking( e, fn ) do{ (e)=(fn); rfalWorker(); }while( (e) == ERR_BUSY ) /*!< Macro used for the blocking methods */
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL TYPES
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*! Colission Resolution states */
|
|
typedef enum{
|
|
RFAL_NFCA_CR_IDLE, /*!< IDLE state */
|
|
RFAL_NFCA_CR_CL, /*!< New Cascading Level state */
|
|
RFAL_NFCA_CR_SDD, /*!< Perform anticollsion state */
|
|
RFAL_NFCA_CR_SEL, /*!< Perform CL Selection state */
|
|
RFAL_NFCA_CR_DONE /*!< Collision Resolution done state */
|
|
}colResState;
|
|
|
|
|
|
/*! Colission Resolution context */
|
|
typedef struct{
|
|
uint8_t devLimit; /*!< Device limit to be used */
|
|
rfalComplianceMode compMode; /*!< Compliancy mode to be used */
|
|
rfalNfcaListenDevice* nfcaDevList; /*!< Location of the device list */
|
|
uint8_t* devCnt; /*!< Location of the device counter */
|
|
bool collPending; /*!< Collision pending flag */
|
|
|
|
bool* collPend; /*!< Location of collision pending flag (Single CR) */
|
|
rfalNfcaSelReq selReq; /*!< SelReqused during anticollision (Single CR) */
|
|
rfalNfcaSelRes* selRes; /*!< Location to place of the SEL_RES(SAK) (Single CR) */
|
|
uint8_t* nfcId1; /*!< Location to place the NFCID1 (Single CR) */
|
|
uint8_t* nfcId1Len; /*!< Location to place the NFCID1 length (Single CR) */
|
|
uint8_t cascadeLv; /*!< Current Cascading Level (Single CR) */
|
|
colResState state; /*!< Single Collision Resolution state (Single CR) */
|
|
uint8_t bytesTxRx; /*!< TxRx bytes used during anticollision loop (Single CR) */
|
|
uint8_t bitsTxRx; /*!< TxRx bits used during anticollision loop (Single CR) */
|
|
uint16_t rxLen;
|
|
uint32_t tmrFDT; /*!< FDT timer used between SED_REQs (Single CR) */
|
|
uint8_t retries; /*!< Retries to be performed upon a timeout error (Single CR)*/
|
|
uint8_t backtrackCnt; /*!< Backtrack retries (Single CR) */
|
|
bool doBacktrack; /*!< Backtrack flag (Single CR) */
|
|
}colResParams;
|
|
|
|
|
|
/*! RFAL NFC-A instance */
|
|
typedef struct{
|
|
colResParams CR; /*!< Collision Resolution context */
|
|
} rfalNfca;
|
|
|
|
|
|
/*! SLP_REQ (HLTA) format Digital 1.1 6.9.1 & Table 20 */
|
|
typedef struct
|
|
{
|
|
uint8_t frame[RFAL_NFCA_SLP_REQ_LEN]; /*!< SLP: 0x50 0x00 */
|
|
} rfalNfcaSlpReq;
|
|
|
|
|
|
/*
|
|
******************************************************************************
|
|
* LOCAL VARIABLES
|
|
******************************************************************************
|
|
*/
|
|
static rfalNfca gNfca; /*!< RFAL NFC-A instance */
|
|
|
|
/*
|
|
******************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
******************************************************************************
|
|
*/
|
|
static uint8_t rfalNfcaCalculateBcc( const uint8_t* buf, uint8_t bufLen );
|
|
static ReturnCode rfalNfcaPollerStartSingleCollisionResolution( uint8_t devLimit, bool *collPending, rfalNfcaSelRes *selRes, uint8_t *nfcId1, uint8_t *nfcId1Len );
|
|
static ReturnCode rfalNfcaPollerGetSingleCollisionResolutionStatus( void );
|
|
|
|
/*
|
|
******************************************************************************
|
|
* LOCAL FUNCTIONS
|
|
******************************************************************************
|
|
*/
|
|
|
|
static uint8_t rfalNfcaCalculateBcc( const uint8_t* buf, uint8_t bufLen )
|
|
{
|
|
uint8_t i;
|
|
uint8_t BCC;
|
|
|
|
BCC = 0;
|
|
|
|
/* BCC is XOR over first 4 bytes of the SDD_RES Digital 1.1 6.7.2 */
|
|
for(i = 0; i < bufLen; i++)
|
|
{
|
|
BCC ^= buf[i];
|
|
}
|
|
|
|
return BCC;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
static ReturnCode rfalNfcaPollerStartSingleCollisionResolution( uint8_t devLimit, bool *collPending, rfalNfcaSelRes *selRes, uint8_t *nfcId1, uint8_t *nfcId1Len )
|
|
{
|
|
/* Check parameters */
|
|
if( (collPending == NULL) || (selRes == NULL) || (nfcId1 == NULL) || (nfcId1Len == NULL) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
/* Initialize output parameters */
|
|
*collPending = false; /* Activity 1.1 9.3.4.6 */
|
|
*nfcId1Len = 0;
|
|
ST_MEMSET( nfcId1, 0x00, RFAL_NFCA_CASCADE_3_UID_LEN );
|
|
|
|
|
|
/* Save parameters */
|
|
gNfca.CR.devLimit = devLimit;
|
|
gNfca.CR.collPend = collPending;
|
|
gNfca.CR.selRes = selRes;
|
|
gNfca.CR.nfcId1 = nfcId1;
|
|
gNfca.CR.nfcId1Len = nfcId1Len;
|
|
|
|
platformTimerDestroy( gNfca.CR.tmrFDT );
|
|
gNfca.CR.tmrFDT = 0U;
|
|
gNfca.CR.retries = RFAL_NFCA_N_RETRANS;
|
|
gNfca.CR.cascadeLv = (uint8_t)RFAL_NFCA_SEL_CASCADE_L1;
|
|
gNfca.CR.state = RFAL_NFCA_CR_CL;
|
|
|
|
gNfca.CR.doBacktrack = false;
|
|
gNfca.CR.backtrackCnt = 3U;
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
static ReturnCode rfalNfcaPollerGetSingleCollisionResolutionStatus( void )
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t collBit = 1U; /* standards mandate or recommend collision bit to be set to One. */
|
|
|
|
|
|
/* Check if FDT timer is still running */
|
|
if( !platformTimerIsExpired( gNfca.CR.tmrFDT ) && (gNfca.CR.tmrFDT != 0U) )
|
|
{
|
|
return ERR_BUSY;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Go through all Cascade Levels Activity 1.1 9.3.4 */
|
|
if( gNfca.CR.cascadeLv > (uint8_t)RFAL_NFCA_SEL_CASCADE_L3 )
|
|
{
|
|
return ERR_INTERNAL;
|
|
}
|
|
|
|
switch( gNfca.CR.state )
|
|
{
|
|
/*******************************************************************************/
|
|
case RFAL_NFCA_CR_CL:
|
|
|
|
/* Initialize the SDD_REQ to send for the new cascade level */
|
|
ST_MEMSET( (uint8_t*)&gNfca.CR.selReq, 0x00, sizeof(rfalNfcaSelReq) );
|
|
|
|
gNfca.CR.bytesTxRx = RFAL_NFCA_SDD_REQ_LEN;
|
|
gNfca.CR.bitsTxRx = 0U;
|
|
gNfca.CR.state = RFAL_NFCA_CR_SDD;
|
|
|
|
/* fall through */
|
|
|
|
/*******************************************************************************/
|
|
case RFAL_NFCA_CR_SDD: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */
|
|
|
|
/* Calculate SEL_CMD and SEL_PAR with the bytes/bits to be sent */
|
|
gNfca.CR.selReq.selCmd = rfalNfcaCLn2SELCMD( gNfca.CR.cascadeLv );
|
|
gNfca.CR.selReq.selPar = rfalNfcaSelPar(gNfca.CR.bytesTxRx, gNfca.CR.bitsTxRx);
|
|
|
|
/* Send SDD_REQ (Anticollision frame) */
|
|
ret = rfalISO14443ATransceiveAnticollisionFrame( (uint8_t*)&gNfca.CR.selReq, &gNfca.CR.bytesTxRx, &gNfca.CR.bitsTxRx, &gNfca.CR.rxLen, RFAL_NFCA_FDTMIN );
|
|
|
|
/* Retry upon timeout EMVCo 2.6 9.6.1.3 */
|
|
if( (ret == ERR_TIMEOUT) && (gNfca.CR.devLimit==0U) && (gNfca.CR.retries != 0U) )
|
|
{
|
|
gNfca.CR.retries--;
|
|
platformTimerDestroy( gNfca.CR.tmrFDT );
|
|
gNfca.CR.tmrFDT = platformTimerCreate( RFAL_NFCA_T_RETRANS );
|
|
break;
|
|
}
|
|
|
|
/* Covert rxLen into bytes */
|
|
gNfca.CR.rxLen = rfalConvBitsToBytes( gNfca.CR.rxLen );
|
|
|
|
|
|
if( (ret == ERR_TIMEOUT) && (gNfca.CR.backtrackCnt != 0U) && (!gNfca.CR.doBacktrack)
|
|
&& !((RFAL_NFCA_SDD_REQ_LEN == gNfca.CR.bytesTxRx) && (0U == gNfca.CR.bitsTxRx)) )
|
|
{
|
|
/* In multiple card scenarios it may always happen that some
|
|
* collisions of a weaker tag go unnoticed. If then a later
|
|
* collision is recognized and the strong tag has a 0 at the
|
|
* collision position then no tag will respond. Catch this
|
|
* corner case and then try with the bit being sent as zero. */
|
|
rfalNfcaSensRes sensRes;
|
|
ret = ERR_RF_COLLISION;
|
|
rfalNfcaPollerCheckPresence( RFAL_14443A_SHORTFRAME_CMD_REQA, &sensRes );
|
|
/* Algorithm below does a post-increment, decrement to go back to current position */
|
|
if (0U == gNfca.CR.bitsTxRx)
|
|
{
|
|
gNfca.CR.bitsTxRx = 7;
|
|
gNfca.CR.bytesTxRx--;
|
|
}
|
|
else
|
|
{
|
|
gNfca.CR.bitsTxRx--;
|
|
}
|
|
collBit = (uint8_t)( ((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & (1U << gNfca.CR.bitsTxRx) );
|
|
collBit = (uint8_t)((0U==collBit)?1U:0U); // invert the collision bit
|
|
gNfca.CR.doBacktrack = true;
|
|
gNfca.CR.backtrackCnt--;
|
|
}
|
|
else
|
|
{
|
|
gNfca.CR.doBacktrack = false;
|
|
}
|
|
|
|
if( ret == ERR_RF_COLLISION )
|
|
{
|
|
/* Check received length */
|
|
if( (gNfca.CR.bytesTxRx + ((gNfca.CR.bitsTxRx != 0U) ? 1U : 0U)) > (RFAL_NFCA_SDD_RES_LEN + RFAL_NFCA_SDD_REQ_LEN) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
|
|
if( ((gNfca.CR.bytesTxRx + ((gNfca.CR.bitsTxRx != 0U) ? 1U : 0U)) > (RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_SDD_REQ_LEN)) && (gNfca.CR.backtrackCnt != 0U) )
|
|
{ /* Collision in BCC: Anticollide only UID part */
|
|
gNfca.CR.backtrackCnt--;
|
|
gNfca.CR.bytesTxRx = RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_SDD_REQ_LEN - 1U;
|
|
gNfca.CR.bitsTxRx = 7;
|
|
collBit = (uint8_t)( ((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & (1U << gNfca.CR.bitsTxRx) ); /* Not a real collision, extract the actual bit for the subsequent code */
|
|
}
|
|
|
|
if( (gNfca.CR.devLimit == 0U) && !(*gNfca.CR.collPend) )
|
|
{
|
|
/* Activity 1.0 & 1.1 9.3.4.12: If CON_DEVICES_LIMIT has a value of 0, then
|
|
* NFC Forum Device is configured to perform collision detection only */
|
|
*gNfca.CR.collPend = true;
|
|
return ERR_IGNORE;
|
|
}
|
|
|
|
*gNfca.CR.collPend = true;
|
|
|
|
/* Set and select the collision bit, with the number of bytes/bits successfully TxRx */
|
|
if (collBit != 0U)
|
|
{
|
|
((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] = (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] | (1U << gNfca.CR.bitsTxRx)); /* MISRA 10.3 */
|
|
}
|
|
else
|
|
{
|
|
((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] = (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & ~(1U << gNfca.CR.bitsTxRx)); /* MISRA 10.3 */
|
|
}
|
|
|
|
gNfca.CR.bitsTxRx++;
|
|
|
|
/* Check if number of bits form a byte */
|
|
if( gNfca.CR.bitsTxRx == RFAL_BITS_IN_BYTE )
|
|
{
|
|
gNfca.CR.bitsTxRx = 0;
|
|
gNfca.CR.bytesTxRx++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Check if Collision loop has failed */
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* If collisions are to be reported check whether the response is complete */
|
|
if( (gNfca.CR.devLimit == 0U) && (gNfca.CR.rxLen != sizeof(rfalNfcaSddRes)) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
|
|
/* Check if the received BCC match */
|
|
if( gNfca.CR.selReq.bcc != rfalNfcaCalculateBcc( gNfca.CR.selReq.nfcid1, RFAL_NFCA_CASCADE_1_UID_LEN ) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Anticollision OK, Select this Cascade Level */
|
|
gNfca.CR.selReq.selPar = RFAL_NFCA_SEL_SELPAR;
|
|
|
|
gNfca.CR.retries = RFAL_NFCA_N_RETRANS;
|
|
gNfca.CR.state = RFAL_NFCA_CR_SEL;
|
|
break;
|
|
|
|
/*******************************************************************************/
|
|
case RFAL_NFCA_CR_SEL:
|
|
|
|
/* Send SEL_REQ (Select command) - Retry upon timeout EMVCo 2.6 9.6.1.3 */
|
|
ret = rfalTransceiveBlockingTxRx( (uint8_t*)&gNfca.CR.selReq, sizeof(rfalNfcaSelReq), (uint8_t*)gNfca.CR.selRes, sizeof(rfalNfcaSelRes), &gNfca.CR.rxLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCA_FDTMIN );
|
|
|
|
/* Retry upon timeout EMVCo 2.6 9.6.1.3 */
|
|
if( (ret == ERR_TIMEOUT) && (gNfca.CR.devLimit==0U) && (gNfca.CR.retries != 0U) )
|
|
{
|
|
gNfca.CR.retries--;
|
|
platformTimerDestroy( gNfca.CR.tmrFDT );
|
|
gNfca.CR.tmrFDT = platformTimerCreate( RFAL_NFCA_T_RETRANS );
|
|
break;
|
|
}
|
|
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Ensure proper response length */
|
|
if( gNfca.CR.rxLen != sizeof(rfalNfcaSelRes) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Check cascade byte, if cascade tag then go next cascade level */
|
|
if( *gNfca.CR.selReq.nfcid1 == RFAL_NFCA_SDD_CT )
|
|
{
|
|
/* Cascade Tag present, store nfcid1 bytes (excluding cascade tag) and continue for next CL */
|
|
ST_MEMCPY( &gNfca.CR.nfcId1[*gNfca.CR.nfcId1Len], &((uint8_t*)&gNfca.CR.selReq.nfcid1)[RFAL_NFCA_SDD_CT_LEN], (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN) );
|
|
*gNfca.CR.nfcId1Len += (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN);
|
|
|
|
/* Go to next cascade level */
|
|
gNfca.CR.state = RFAL_NFCA_CR_CL;
|
|
gNfca.CR.cascadeLv++;
|
|
}
|
|
else
|
|
{
|
|
/* UID Selection complete, Stop Cascade Level loop */
|
|
ST_MEMCPY( &gNfca.CR.nfcId1[*gNfca.CR.nfcId1Len], (uint8_t*)&gNfca.CR.selReq.nfcid1, RFAL_NFCA_CASCADE_1_UID_LEN );
|
|
*gNfca.CR.nfcId1Len += RFAL_NFCA_CASCADE_1_UID_LEN;
|
|
|
|
gNfca.CR.state = RFAL_NFCA_CR_DONE;
|
|
break; /* Only flag operation complete on the next execution */
|
|
}
|
|
break;
|
|
|
|
/*******************************************************************************/
|
|
case RFAL_NFCA_CR_DONE:
|
|
return ERR_NONE;
|
|
|
|
/*******************************************************************************/
|
|
default:
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
return ERR_BUSY;
|
|
}
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerInitialize( void )
|
|
{
|
|
ReturnCode ret;
|
|
|
|
EXIT_ON_ERR( ret, rfalSetMode( RFAL_MODE_POLL_NFCA, RFAL_BR_106, RFAL_BR_106 ) );
|
|
rfalSetErrorHandling( RFAL_ERRORHANDLING_NFC );
|
|
|
|
rfalSetGT( RFAL_GT_NFCA );
|
|
rfalSetFDTListen( RFAL_FDT_LISTEN_NFCA_POLLER );
|
|
rfalSetFDTPoll( RFAL_FDT_POLL_NFCA_POLLER );
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerCheckPresence( rfal14443AShortFrameCmd cmd, rfalNfcaSensRes *sensRes )
|
|
{
|
|
ReturnCode ret;
|
|
uint16_t rcvLen;
|
|
|
|
/* Digital 1.1 6.10.1.3 For Commands ALL_REQ, SENS_REQ, SDD_REQ, and SEL_REQ, the NFC Forum Device *
|
|
* MUST treat receipt of a Listen Frame at a time after FDT(Listen, min) as a Timeour Error */
|
|
|
|
ret = rfalISO14443ATransceiveShortFrame( cmd, (uint8_t*)sensRes, (uint8_t)rfalConvBytesToBits(sizeof(rfalNfcaSensRes)), &rcvLen, RFAL_NFCA_FDTMIN );
|
|
if( (ret == ERR_RF_COLLISION) || (ret == ERR_CRC) || (ret == ERR_NOMEM) || (ret == ERR_FRAMING) || (ret == ERR_PAR) )
|
|
{
|
|
ret = ERR_NONE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerTechnologyDetection( rfalComplianceMode compMode, rfalNfcaSensRes *sensRes )
|
|
{
|
|
ReturnCode ret;
|
|
|
|
EXIT_ON_ERR( ret, rfalNfcaPollerCheckPresence( ((compMode == RFAL_COMPLIANCE_MODE_EMV) ? RFAL_14443A_SHORTFRAME_CMD_WUPA : RFAL_14443A_SHORTFRAME_CMD_REQA), sensRes ) );
|
|
|
|
/* Send SLP_REQ as Activity 1.1 9.2.3.6 and EMVCo 2.6 9.2.1.3 */
|
|
if( compMode != RFAL_COMPLIANCE_MODE_ISO)
|
|
{
|
|
rfalNfcaPollerSleep();
|
|
}
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerSingleCollisionResolution( uint8_t devLimit, bool *collPending, rfalNfcaSelRes *selRes, uint8_t *nfcId1, uint8_t *nfcId1Len )
|
|
{
|
|
|
|
ReturnCode ret;
|
|
|
|
EXIT_ON_ERR( ret, rfalNfcaPollerStartSingleCollisionResolution( devLimit, collPending, selRes, nfcId1, nfcId1Len ) );
|
|
rfalNfcaRunBlocking( ret, rfalNfcaPollerGetSingleCollisionResolutionStatus() );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerStartFullCollisionResolution( rfalComplianceMode compMode, uint8_t devLimit, rfalNfcaListenDevice *nfcaDevList, uint8_t *devCnt )
|
|
{
|
|
ReturnCode ret;
|
|
rfalNfcaSensRes sensRes;
|
|
uint16_t rcvLen;
|
|
|
|
if( (nfcaDevList == NULL) || (devCnt == NULL) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
*devCnt = 0;
|
|
ret = ERR_NONE;
|
|
|
|
/*******************************************************************************/
|
|
/* Send ALL_REQ before Anticollision if a Sleep was sent before Activity 1.1 9.3.4.1 and EMVco 2.6 9.3.2.1 */
|
|
if( compMode != RFAL_COMPLIANCE_MODE_ISO )
|
|
{
|
|
ret = rfalISO14443ATransceiveShortFrame( RFAL_14443A_SHORTFRAME_CMD_WUPA, (uint8_t*)&nfcaDevList->sensRes, (uint8_t)rfalConvBytesToBits(sizeof(rfalNfcaSensRes)), &rcvLen, RFAL_NFCA_FDTMIN );
|
|
if(ret != ERR_NONE)
|
|
{
|
|
if( (compMode == RFAL_COMPLIANCE_MODE_EMV) || ((ret != ERR_RF_COLLISION) && (ret != ERR_CRC) && (ret != ERR_FRAMING) && (ret != ERR_PAR)) )
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Check proper SENS_RES/ATQA size */
|
|
if( (ret == ERR_NONE) && (rfalConvBytesToBits(sizeof(rfalNfcaSensRes)) != rcvLen) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Store the SENS_RES from Technology Detection or from WUPA */
|
|
sensRes = nfcaDevList->sensRes;
|
|
|
|
if( devLimit > 0U ) /* MISRA 21.18 */
|
|
{
|
|
ST_MEMSET( nfcaDevList, 0x00, (sizeof(rfalNfcaListenDevice) * devLimit) );
|
|
}
|
|
|
|
/* Restore the prev SENS_RES, assuming that the SENS_RES received is from first device
|
|
* When only one device is detected it's not woken up then we'll have no SENS_RES (ATQA) */
|
|
nfcaDevList->sensRes = sensRes;
|
|
|
|
/* Save parameters */
|
|
gNfca.CR.devCnt = devCnt;
|
|
gNfca.CR.devLimit = devLimit;
|
|
gNfca.CR.nfcaDevList = nfcaDevList;
|
|
gNfca.CR.compMode = compMode;
|
|
|
|
|
|
#if RFAL_FEATURE_T1T
|
|
/*******************************************************************************/
|
|
/* Only check for T1T if previous SENS_RES was received without a transmission *
|
|
* error. When collisions occur bits in the SENS_RES may look like a T1T */
|
|
/* If T1T Anticollision is not supported Activity 1.1 9.3.4.3 */
|
|
if( rfalNfcaIsSensResT1T( &nfcaDevList->sensRes ) && (devLimit != 0U) && (ret == ERR_NONE) && (compMode != RFAL_COMPLIANCE_MODE_EMV) )
|
|
{
|
|
/* RID_REQ shall be performed Activity 1.1 9.3.4.24 */
|
|
rfalT1TPollerInitialize();
|
|
EXIT_ON_ERR( ret, rfalT1TPollerRid( &nfcaDevList->ridRes ) );
|
|
|
|
*devCnt = 1U;
|
|
nfcaDevList->isSleep = false;
|
|
nfcaDevList->type = RFAL_NFCA_T1T;
|
|
nfcaDevList->nfcId1Len = RFAL_NFCA_CASCADE_1_UID_LEN;
|
|
ST_MEMCPY( &nfcaDevList->nfcId1, &nfcaDevList->ridRes.uid, RFAL_NFCA_CASCADE_1_UID_LEN );
|
|
|
|
return ERR_NONE;
|
|
}
|
|
#endif /* RFAL_FEATURE_T1T */
|
|
|
|
return rfalNfcaPollerStartSingleCollisionResolution( devLimit, &gNfca.CR.collPending, &nfcaDevList->selRes, (uint8_t*)&nfcaDevList->nfcId1, &nfcaDevList->nfcId1Len );
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerGetFullCollisionResolutionStatus( void )
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t newDevType;
|
|
|
|
if( (gNfca.CR.nfcaDevList == NULL) || (gNfca.CR.devCnt == NULL) )
|
|
{
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
/* Check whether a T1T has already been detected */
|
|
if( rfalNfcaIsSensResT1T( &gNfca.CR.nfcaDevList->sensRes ) && (gNfca.CR.nfcaDevList->type == RFAL_NFCA_T1T) )
|
|
{
|
|
/* T1T doesn't support Anticollision */
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
EXIT_ON_ERR( ret, rfalNfcaPollerGetSingleCollisionResolutionStatus() );
|
|
|
|
/* Assign Listen Device */
|
|
newDevType = ((uint8_t)gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].selRes.sak) & RFAL_NFCA_SEL_RES_CONF_MASK; /* MISRA 10.8 */
|
|
/* PRQA S 4342 1 # MISRA 10.5 - Guaranteed that no invalid enum values are created: see guard_eq_RFAL_NFCA_T2T, .... */
|
|
gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].type = (rfalNfcaListenDeviceType) newDevType;
|
|
gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].isSleep = false;
|
|
(*gNfca.CR.devCnt)++;
|
|
|
|
|
|
/* If a collision was detected and device counter is lower than limit Activity 1.1 9.3.4.21 */
|
|
if( (*gNfca.CR.devCnt < gNfca.CR.devLimit) && (gNfca.CR.collPending) )
|
|
{
|
|
/* Put this device to Sleep Activity 1.1 9.3.4.22 */
|
|
rfalNfcaPollerSleep();
|
|
gNfca.CR.nfcaDevList[(*gNfca.CR.devCnt - 1U)].isSleep = true;
|
|
|
|
|
|
/* Send a new SENS_REQ to check for other cards Activity 1.1 9.3.4.23 */
|
|
ret = rfalNfcaPollerCheckPresence( RFAL_14443A_SHORTFRAME_CMD_REQA, &gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].sensRes );
|
|
if( ret == ERR_TIMEOUT )
|
|
{
|
|
/* No more devices found, exit */
|
|
gNfca.CR.collPending = false;
|
|
}
|
|
else
|
|
{
|
|
/* Another device found, continue loop */
|
|
gNfca.CR.collPending = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Exit loop */
|
|
gNfca.CR.collPending = false;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
/* Check if collision resolution shall continue */
|
|
if( (*gNfca.CR.devCnt < gNfca.CR.devLimit) && (gNfca.CR.collPending) )
|
|
{
|
|
EXIT_ON_ERR( ret, rfalNfcaPollerStartSingleCollisionResolution( gNfca.CR.devLimit,
|
|
&gNfca.CR.collPending,
|
|
&gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].selRes,
|
|
(uint8_t*)&gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].nfcId1,
|
|
&gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].nfcId1Len ) );
|
|
|
|
return ERR_BUSY;
|
|
}
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerFullCollisionResolution( rfalComplianceMode compMode, uint8_t devLimit, rfalNfcaListenDevice *nfcaDevList, uint8_t *devCnt )
|
|
{
|
|
ReturnCode ret;
|
|
|
|
EXIT_ON_ERR( ret, rfalNfcaPollerStartFullCollisionResolution( compMode, devLimit, nfcaDevList, devCnt ) );
|
|
rfalNfcaRunBlocking( ret, rfalNfcaPollerGetFullCollisionResolutionStatus() );
|
|
|
|
return ret;
|
|
}
|
|
|
|
ReturnCode rfalNfcaPollerSleepFullCollisionResolution( uint8_t devLimit, rfalNfcaListenDevice *nfcaDevList, uint8_t *devCnt )
|
|
{
|
|
bool firstRound;
|
|
uint8_t tmpDevCnt;
|
|
ReturnCode ret;
|
|
|
|
|
|
if( (nfcaDevList == NULL) || (devCnt == NULL) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
/* Only use ALL_REQ (WUPA) on the first round */
|
|
firstRound = true;
|
|
*devCnt = 0;
|
|
|
|
|
|
/* Perform collision resolution until no new device is found */
|
|
do
|
|
{
|
|
tmpDevCnt = 0;
|
|
ret = rfalNfcaPollerFullCollisionResolution( (firstRound ? RFAL_COMPLIANCE_MODE_NFC : RFAL_COMPLIANCE_MODE_ISO), (devLimit - *devCnt), &nfcaDevList[*devCnt], &tmpDevCnt );
|
|
|
|
if( (ret == ERR_NONE) && (tmpDevCnt > 0U) )
|
|
{
|
|
*devCnt += tmpDevCnt;
|
|
|
|
/* Check whether to seacrh for more devices */
|
|
if( *devCnt < devLimit )
|
|
{
|
|
/* Set last found device to sleep (all others are slept already) */
|
|
rfalNfcaPollerSleep();
|
|
nfcaDevList[((*devCnt)-1U)].isSleep = true;
|
|
|
|
/* Check if any other device is present */
|
|
ret = rfalNfcaPollerCheckPresence( RFAL_14443A_SHORTFRAME_CMD_REQA, &nfcaDevList[*devCnt].sensRes );
|
|
if( ret == ERR_NONE )
|
|
{
|
|
firstRound = false;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
while( true );
|
|
|
|
return ((*devCnt > 0U) ? ERR_NONE : ret);
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerSelect( const uint8_t *nfcid1, uint8_t nfcidLen, rfalNfcaSelRes *selRes )
|
|
{
|
|
uint8_t i;
|
|
uint8_t cl;
|
|
uint8_t nfcidOffset;
|
|
uint16_t rxLen;
|
|
ReturnCode ret;
|
|
rfalNfcaSelReq selReq;
|
|
|
|
if( (nfcid1 == NULL) || (nfcidLen > RFAL_NFCA_CASCADE_3_UID_LEN) || (selRes == NULL) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
|
|
/* Calculate Cascate Level */
|
|
cl = rfalNfcaNfcidLen2CL( nfcidLen );
|
|
nfcidOffset = 0;
|
|
|
|
/*******************************************************************************/
|
|
/* Go through all Cascade Levels Activity 1.1 9.4.4 */
|
|
for( i = RFAL_NFCA_SEL_CASCADE_L1; i <= cl; i++ )
|
|
{
|
|
/* Assign SEL_CMD according to the CLn and SEL_PAR*/
|
|
selReq.selCmd = rfalNfcaCLn2SELCMD(i);
|
|
selReq.selPar = RFAL_NFCA_SEL_SELPAR;
|
|
|
|
/* Compute NFCID/Data on the SEL_REQ command Digital 1.1 Table 18 */
|
|
if( cl != i )
|
|
{
|
|
*selReq.nfcid1 = RFAL_NFCA_SDD_CT;
|
|
ST_MEMCPY( &selReq.nfcid1[RFAL_NFCA_SDD_CT_LEN], &nfcid1[nfcidOffset], (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN) );
|
|
nfcidOffset += (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN);
|
|
}
|
|
else
|
|
{
|
|
ST_MEMCPY( selReq.nfcid1, &nfcid1[nfcidOffset], RFAL_NFCA_CASCADE_1_UID_LEN );
|
|
}
|
|
|
|
/* Calculate nfcid's BCC */
|
|
selReq.bcc = rfalNfcaCalculateBcc( (uint8_t*)&selReq.nfcid1, sizeof(selReq.nfcid1) );
|
|
|
|
/*******************************************************************************/
|
|
/* Send SEL_REQ */
|
|
EXIT_ON_ERR( ret, rfalTransceiveBlockingTxRx( (uint8_t*)&selReq, sizeof(rfalNfcaSelReq), (uint8_t*)selRes, sizeof(rfalNfcaSelRes), &rxLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCA_FDTMIN ) );
|
|
|
|
/* Ensure proper response length */
|
|
if( rxLen != sizeof(rfalNfcaSelRes) )
|
|
{
|
|
return ERR_PROTO;
|
|
}
|
|
}
|
|
|
|
/* REMARK: Could check if NFCID1 is complete */
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode rfalNfcaPollerSleep( void )
|
|
{
|
|
rfalNfcaSlpReq slpReq;
|
|
uint8_t rxBuf; /* dummy buffer, just to perform Rx */
|
|
|
|
slpReq.frame[RFAL_NFCA_SLP_CMD_POS] = RFAL_NFCA_SLP_CMD;
|
|
slpReq.frame[RFAL_NFCA_SLP_BYTE2_POS] = RFAL_NFCA_SLP_BYTE2;
|
|
|
|
rfalTransceiveBlockingTxRx( (uint8_t*)&slpReq, sizeof(rfalNfcaSlpReq), &rxBuf, sizeof(rxBuf), NULL, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCA_SLP_FWT );
|
|
|
|
/* ISO14443-3 6.4.3 HLTA - If PICC responds with any modulation during 1 ms this response shall be interpreted as not acknowledge
|
|
Digital 2.0 6.9.2.1 & EMVCo 3.0 5.6.2.1 - consider the HLTA command always acknowledged
|
|
No check to be compliant with NFC and EMVCo, and to improve interoprability (Kovio RFID Tag)
|
|
*/
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
bool rfalNfcaListenerIsSleepReq( const uint8_t *buf, uint16_t bufLen )
|
|
{
|
|
/* Check if length and payload match */
|
|
if( (bufLen != sizeof(rfalNfcaSlpReq)) || (buf[RFAL_NFCA_SLP_CMD_POS] != RFAL_NFCA_SLP_CMD) || (buf[RFAL_NFCA_SLP_BYTE2_POS] != RFAL_NFCA_SLP_BYTE2) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* If the guards here don't compile then the code above cannot work anymore. */
|
|
extern uint8_t guard_eq_RFAL_NFCA_T2T[((RFAL_NFCA_SEL_RES_CONF_MASK&(uint8_t)RFAL_NFCA_T2T) == (uint8_t)RFAL_NFCA_T2T)?1:(-1)];
|
|
extern uint8_t guard_eq_RFAL_NFCA_T4T[((RFAL_NFCA_SEL_RES_CONF_MASK&(uint8_t)RFAL_NFCA_T4T) == (uint8_t)RFAL_NFCA_T4T)?1:(-1)];
|
|
extern uint8_t guard_eq_RFAL_NFCA_NFCDEP[((RFAL_NFCA_SEL_RES_CONF_MASK&(uint8_t)RFAL_NFCA_NFCDEP) == (uint8_t)RFAL_NFCA_NFCDEP)?1:(-1)];
|
|
extern uint8_t guard_eq_RFAL_NFCA_T4T_NFCDEP[((RFAL_NFCA_SEL_RES_CONF_MASK&(uint8_t)RFAL_NFCA_T4T_NFCDEP) == (uint8_t)RFAL_NFCA_T4T_NFCDEP)?1:(-1)];
|
|
#endif /* RFAL_FEATURE_NFCA */
|