/****************************************************************************** * \attention * *

© COPYRIGHT 2020 STMicroelectronics

* * 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_nfcb.c * * \author Gustavo Patricio * * \brief Implementation of NFC-B (ISO14443B) helpers * */ /* ****************************************************************************** * INCLUDES ****************************************************************************** */ #include "rfal_nfcb.h" #include "utils.h" /* ****************************************************************************** * ENABLE SWITCH ****************************************************************************** */ #ifndef RFAL_FEATURE_NFCB #define RFAL_FEATURE_NFCB false /* NFC-B module configuration missing. Disabled by default */ #endif #if RFAL_FEATURE_NFCB /* ****************************************************************************** * GLOBAL DEFINES ****************************************************************************** */ #define RFAL_NFCB_SENSB_REQ_EXT_SENSB_RES_SUPPORTED 0x10U /*!< Bit mask for Extended SensB Response support in SENSB_REQ */ #define RFAL_NFCB_SENSB_RES_PROT_TYPE_RFU 0x08U /*!< Bit mask for Protocol Type RFU in SENSB_RES */ #define RFAL_NFCB_SLOT_MARKER_SC_SHIFT 4U /*!< Slot Code position on SLOT_MARKER APn */ #define RFAL_NFCB_SLOTMARKER_SLOTCODE_MIN 1U /*!< SLOT_MARKER Slot Code minimum Digital 1.1 Table 37 */ #define RFAL_NFCB_SLOTMARKER_SLOTCODE_MAX 16U /*!< SLOT_MARKER Slot Code maximum Digital 1.1 Table 37 */ #define RFAL_NFCB_ACTIVATION_FWT (RFAL_NFCB_FWTSENSB + RFAL_NFCB_DTPOLL_20) /*!< FWT(SENSB) + dTbPoll Digital 2.0 7.9.1.3 */ /*! Advanced and Extended bit mask in Parameter of SENSB_REQ */ #define RFAL_NFCB_SENSB_REQ_PARAM (RFAL_NFCB_SENSB_REQ_ADV_FEATURE | RFAL_NFCB_SENSB_REQ_EXT_SENSB_RES_SUPPORTED) /*! NFC-B commands definition */ enum { RFAL_NFCB_CMD_SENSB_REQ = 0x05, /*!< SENSB_REQ (REQB) & SLOT_MARKER Digital 1.1 Table 24 */ RFAL_NFCB_CMD_SENSB_RES = 0x50, /*!< SENSB_RES (ATQB) & SLOT_MARKER Digital 1.1 Table 27 */ RFAL_NFCB_CMD_SLPB_REQ = 0x50, /*!< SLPB_REQ (HLTB command) Digital 1.1 Table 38 */ RFAL_NFCB_CMD_SLPB_RES = 0x00 /*!< SLPB_RES (HLTB Answer) Digital 1.1 Table 39 */ }; /* ****************************************************************************** * GLOBAL MACROS ****************************************************************************** */ #define rfalNfcbNI2NumberOfSlots( ni ) (uint8_t)(1U << (ni)) /*!< Converts the Number of slots Identifier to slot number */ /* ****************************************************************************** * GLOBAL TYPES ****************************************************************************** */ /*! ALLB_REQ (WUPB) and SENSB_REQ (REQB) Command Format Digital 1.1 7.6.1 */ typedef struct { uint8_t cmd; /*!< xxxxB_REQ: 05h */ uint8_t AFI; /*!< NFC Identifier */ uint8_t PARAM; /*!< Application Data */ } rfalNfcbSensbReq; /*! SLOT_MARKER Command format Digital 1.1 7.7.1 */ typedef struct { uint8_t APn; /*!< Slot number 2..16 | 0101b */ } rfalNfcbSlotMarker; /*! SLPB_REQ (HLTB) Command Format Digital 1.1 7.8.1 */ typedef struct { uint8_t cmd; /*!< SLPB_REQ: 50h */ uint8_t nfcid0[RFAL_NFCB_NFCID0_LEN]; /*!< NFC Identifier (PUPI)*/ } rfalNfcbSlpbReq; /*! SLPB_RES (HLTB) Response Format Digital 1.1 7.8.2 */ typedef struct { uint8_t cmd; /*!< SLPB_RES: 00h */ } rfalNfcbSlpbRes; /*! RFAL NFC-B instance */ typedef struct { uint8_t AFI; /*!< AFI to be used */ uint8_t PARAM; /*!< PARAM to be used */ } rfalNfcb; /* ****************************************************************************** * LOCAL FUNCTION PROTOTYPES ****************************************************************************** */ static ReturnCode rfalNfcbCheckSensbRes( const rfalNfcbSensbRes *sensbRes, uint8_t sensbResLen ); /* ****************************************************************************** * LOCAL VARIABLES ****************************************************************************** */ static rfalNfcb gRfalNfcb; /*!< RFAL NFC-B Instance */ /* ****************************************************************************** * LOCAL FUNCTIONS ****************************************************************************** */ /*******************************************************************************/ static ReturnCode rfalNfcbCheckSensbRes( const rfalNfcbSensbRes *sensbRes, uint8_t sensbResLen ) { /* Check response length */ if( ( (sensbResLen != RFAL_NFCB_SENSB_RES_LEN) && (sensbResLen != RFAL_NFCB_SENSB_RES_EXT_LEN) ) ) { return ERR_PROTO; } /* Check SENSB_RES and Protocol Type Digital 1.1 7.6.2.19 */ if( ((sensbRes->protInfo.FsciProType & RFAL_NFCB_SENSB_RES_PROT_TYPE_RFU) != 0U) || (sensbRes->cmd != (uint8_t)RFAL_NFCB_CMD_SENSB_RES) ) { return ERR_PROTO; } return ERR_NONE; } /* ****************************************************************************** * GLOBAL FUNCTIONS ****************************************************************************** */ /*******************************************************************************/ ReturnCode rfalNfcbPollerInitialize( void ) { ReturnCode ret; EXIT_ON_ERR( ret, rfalSetMode( RFAL_MODE_POLL_NFCB, RFAL_BR_106, RFAL_BR_106 ) ); rfalSetErrorHandling( RFAL_ERRORHANDLING_NFC ); rfalSetGT( RFAL_GT_NFCB ); rfalSetFDTListen( RFAL_FDT_LISTEN_NFCB_POLLER ); rfalSetFDTPoll( RFAL_FDT_POLL_NFCB_POLLER ); gRfalNfcb.AFI = RFAL_NFCB_AFI; gRfalNfcb.PARAM = RFAL_NFCB_PARAM; return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcbPollerInitializeWithParams( uint8_t AFI, uint8_t PARAM ) { ReturnCode ret; EXIT_ON_ERR( ret, rfalNfcbPollerInitialize() ); gRfalNfcb.AFI = AFI; gRfalNfcb.PARAM = (PARAM & RFAL_NFCB_SENSB_REQ_PARAM); return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcbPollerCheckPresence( rfalNfcbSensCmd cmd, rfalNfcbSlots slots, rfalNfcbSensbRes *sensbRes, uint8_t *sensbResLen ) { uint16_t rxLen; ReturnCode ret; rfalNfcbSensbReq sensbReq; /* Check if the command requested and given the slot number are valid */ if( ((RFAL_NFCB_SENS_CMD_SENSB_REQ != cmd) && (RFAL_NFCB_SENS_CMD_ALLB_REQ != cmd)) || (slots > RFAL_NFCB_SLOT_NUM_16) || (sensbRes == NULL) || (sensbResLen == NULL) ) { return ERR_PARAM; } *sensbResLen = 0; ST_MEMSET(sensbRes, 0x00, sizeof(rfalNfcbSensbRes) ); /* Compute SENSB_REQ */ sensbReq.cmd = RFAL_NFCB_CMD_SENSB_REQ; sensbReq.AFI = gRfalNfcb.AFI; sensbReq.PARAM = (((uint8_t)gRfalNfcb.PARAM & RFAL_NFCB_SENSB_REQ_PARAM) | (uint8_t)cmd | (uint8_t)slots); /* Send SENSB_REQ and disable AGC to detect collisions */ ret = rfalTransceiveBlockingTxRx( (uint8_t*)&sensbReq, sizeof(rfalNfcbSensbReq), (uint8_t*)sensbRes, sizeof(rfalNfcbSensbRes), &rxLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCB_FWTSENSB ); *sensbResLen = (uint8_t)rxLen; /* Check if a transmission error was detected */ if( (ret == ERR_CRC) || (ret == ERR_FRAMING) ) { /* Invalidate received frame as an error was detected (CollisionResolution checks if valid) */ *sensbResLen = 0; return ERR_NONE; } if( ret == ERR_NONE ) { return rfalNfcbCheckSensbRes( sensbRes, *sensbResLen ); } return ret; } /*******************************************************************************/ ReturnCode rfalNfcbPollerSleep( const uint8_t* nfcid0 ) { uint16_t rxLen; ReturnCode ret; rfalNfcbSlpbReq slpbReq; rfalNfcbSlpbRes slpbRes; if( nfcid0 == NULL ) { return ERR_PARAM; } /* Compute SLPB_REQ */ slpbReq.cmd = RFAL_NFCB_CMD_SLPB_REQ; ST_MEMCPY( slpbReq.nfcid0, nfcid0, RFAL_NFCB_NFCID0_LEN ); EXIT_ON_ERR( ret, rfalTransceiveBlockingTxRx( (uint8_t*)&slpbReq, sizeof(rfalNfcbSlpbReq), (uint8_t*)&slpbRes, sizeof(rfalNfcbSlpbRes), &rxLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCB_ACTIVATION_FWT )); /* Check SLPB_RES */ if( (rxLen != sizeof(rfalNfcbSlpbRes)) || (slpbRes.cmd != (uint8_t)RFAL_NFCB_CMD_SLPB_RES) ) { return ERR_PROTO; } return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcbPollerSlotMarker( uint8_t slotCode, rfalNfcbSensbRes *sensbRes, uint8_t *sensbResLen ) { ReturnCode ret; rfalNfcbSlotMarker slotMarker; uint16_t rxLen; /* Check parameters */ if( (sensbRes == NULL) || (sensbResLen == NULL) || (slotCode < RFAL_NFCB_SLOTMARKER_SLOTCODE_MIN) || (slotCode > RFAL_NFCB_SLOTMARKER_SLOTCODE_MAX) ) { return ERR_PARAM; } /* Compose and send SLOT_MARKER with disabled AGC to detect collisions */ slotMarker.APn = ((slotCode << RFAL_NFCB_SLOT_MARKER_SC_SHIFT) | (uint8_t)RFAL_NFCB_CMD_SENSB_REQ); ret = rfalTransceiveBlockingTxRx( (uint8_t*)&slotMarker, sizeof(rfalNfcbSlotMarker), (uint8_t*)sensbRes, sizeof(rfalNfcbSensbRes), &rxLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCB_ACTIVATION_FWT ); *sensbResLen = (uint8_t)rxLen; /* Check if a transmission error was detected */ if( (ret == ERR_CRC) || (ret == ERR_FRAMING) ) { return ERR_RF_COLLISION; } if( ret == ERR_NONE ) { return rfalNfcbCheckSensbRes( sensbRes, *sensbResLen ); } return ret; } ReturnCode rfalNfcbPollerTechnologyDetection( rfalComplianceMode compMode, rfalNfcbSensbRes *sensbRes, uint8_t *sensbResLen ) { NO_WARNING(compMode); return rfalNfcbPollerCheckPresence( RFAL_NFCB_SENS_CMD_SENSB_REQ, RFAL_NFCB_SLOT_NUM_1, sensbRes, sensbResLen ); } /*******************************************************************************/ ReturnCode rfalNfcbPollerCollisionResolution( rfalComplianceMode compMode, uint8_t devLimit, rfalNfcbListenDevice *nfcbDevList, uint8_t *devCnt ) { bool colPending; /* dummy */ return rfalNfcbPollerSlottedCollisionResolution( compMode, devLimit, RFAL_NFCB_SLOT_NUM_1, RFAL_NFCB_SLOT_NUM_16, nfcbDevList, devCnt, &colPending ); } /*******************************************************************************/ ReturnCode rfalNfcbPollerSlottedCollisionResolution( rfalComplianceMode compMode, uint8_t devLimit, rfalNfcbSlots initSlots, rfalNfcbSlots endSlots, rfalNfcbListenDevice *nfcbDevList, uint8_t *devCnt, bool *colPending ) { ReturnCode ret; uint8_t slotsNum; uint8_t slotCode; uint8_t curDevCnt; /* Check parameters. In ISO | Activity 1.0 mode the initial slots must be 1 as continuation of Technology Detection */ if( (nfcbDevList == NULL) || (devCnt == NULL) || (colPending == NULL) || (initSlots > RFAL_NFCB_SLOT_NUM_16) || (endSlots > RFAL_NFCB_SLOT_NUM_16) || ((compMode == RFAL_COMPLIANCE_MODE_ISO) && (initSlots != RFAL_NFCB_SLOT_NUM_1)) ) { return ERR_PARAM; } /* Initialise as no error in case Activity 1.0 where the previous SENSB_RES from technology detection should be used */ ret = ERR_NONE; *devCnt = 0; curDevCnt = 0; *colPending = false; /* Send ALLB_REQ Activity 1.1 9.3.5.2 and 9.3.5.3 (Symbol 1 and 2) */ if( compMode != RFAL_COMPLIANCE_MODE_ISO ) { ret = rfalNfcbPollerCheckPresence( RFAL_NFCB_SENS_CMD_ALLB_REQ, initSlots, &nfcbDevList->sensbRes, &nfcbDevList->sensbResLen ); if( (ret != ERR_NONE) && (initSlots == RFAL_NFCB_SLOT_NUM_1) ) { return ret; } } /* Check if there was a transmission error on WUPB EMVCo 2.6 9.3.3.1 */ if( (compMode == RFAL_COMPLIANCE_MODE_EMV) && (nfcbDevList->sensbResLen == 0U) ) { return ERR_FRAMING; } for( slotsNum = (uint8_t)initSlots; slotsNum <= (uint8_t)endSlots; slotsNum++ ) { do { /* Activity 1.1 9.3.5.23 - Symbol 22 */ if( (compMode == RFAL_COMPLIANCE_MODE_NFC) && (curDevCnt != 0U) ) { rfalNfcbPollerSleep( nfcbDevList[((*devCnt) - (uint8_t)1U)].sensbRes.nfcid0 ); nfcbDevList[((*devCnt) - (uint8_t)1U)].isSleep = true; } /* Send SENSB_REQ with number of slots if not the first Activity 1.1 9.3.5.24 - Symbol 23 */ if( (slotsNum != (uint8_t)initSlots) || *colPending ) { /* PRQA S 4342 1 # MISRA 10.5 - Layout of rfalNfcbSlots and above loop guarantee that no invalid enum values are created. */ ret = rfalNfcbPollerCheckPresence( RFAL_NFCB_SENS_CMD_SENSB_REQ, (rfalNfcbSlots)slotsNum, &nfcbDevList[*devCnt].sensbRes, &nfcbDevList[*devCnt].sensbResLen ); } /* Activity 1.1 9.3.5.6 - Symbol 5 */ slotCode = 0; curDevCnt = 0; *colPending = false; do{ /* Activity 1.1 9.3.5.26 - Symbol 25 */ if( slotCode != 0U ) { ret = rfalNfcbPollerSlotMarker( slotCode, &nfcbDevList[*devCnt].sensbRes, &nfcbDevList[*devCnt].sensbResLen ); } /* Activity 1.1 9.3.5.7 and 9.3.5.8 - Symbol 6 */ if( ret != ERR_TIMEOUT ) { /* Activity 1.1 9.3.5.8 - Symbol 7 */ if( (rfalNfcbCheckSensbRes( &nfcbDevList[*devCnt].sensbRes, nfcbDevList[*devCnt].sensbResLen) == ERR_NONE) && (ret == ERR_NONE) ) { nfcbDevList[*devCnt].isSleep = false; if( compMode == RFAL_COMPLIANCE_MODE_EMV ) { (*devCnt)++; return ret; } else if( compMode == RFAL_COMPLIANCE_MODE_ISO ) { /* Activity 1.0 9.3.5.8 - Symbol 7 */ (*devCnt)++; curDevCnt++; /* Activity 1.0 9.3.5.10 - Symbol 9 */ if( (*devCnt >= devLimit) || (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1) ) { return ret; } /* Activity 1.0 9.3.5.11 - Symbol 10 */ rfalNfcbPollerSleep( nfcbDevList[*devCnt-1U].sensbRes.nfcid0 ); nfcbDevList[*devCnt-1U].isSleep = true; } else if( compMode == RFAL_COMPLIANCE_MODE_NFC ) { /* Activity 1.1 9.3.5.10 and 9.3.5.11 - Symbol 9 and Symbol 11*/ if(curDevCnt != 0U) { rfalNfcbPollerSleep( nfcbDevList[(*devCnt) - (uint8_t)1U].sensbRes.nfcid0 ); nfcbDevList[(*devCnt) - (uint8_t)1U].isSleep = true; } /* Activity 1.1 9.3.5.12 - Symbol 11 */ (*devCnt)++; curDevCnt++; /* Activity 1.1 9.3.5.6 - Symbol 13 */ if( (*devCnt >= devLimit) || (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1) ) { return ret; } } else { /* MISRA 15.7 - Empty else */ } } else { /* If deviceLimit is set to 0 the NFC Forum Device is configured to perform collision detection only Activity 1.0 and 1.1 9.3.5.5 - Symbol 4 */ if( (devLimit == 0U) && (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1) ) { return ERR_RF_COLLISION; } /* Activity 1.1 9.3.5.9 - Symbol 8 */ *colPending = true; } } /* Activity 1.1 9.3.5.15 - Symbol 14 */ slotCode++; } while( slotCode < rfalNfcbNI2NumberOfSlots(slotsNum) ); /* Activity 1.1 9.3.5.17 - Symbol 16 */ if( !(*colPending) ) { return ERR_NONE; } /* Activity 1.1 9.3.5.18 - Symbol 17 */ } while (curDevCnt != 0U); /* If a collision is detected and card(s) were found on this loop keep the same number of available slots */ } return ERR_NONE; } /*******************************************************************************/ uint32_t rfalNfcbTR2ToFDT( uint8_t tr2Code ) { /*******************************************************************************/ /* MISRA 8.9 An object should be defined at block scope if its identifier only appears in a single function */ /*! TR2 Table according to Digital 1.1 Table 33 */ const uint16_t rfalNfcbTr2Table[] = { 1792, 3328, 5376, 9472 }; /*******************************************************************************/ return rfalNfcbTr2Table[ (tr2Code & RFAL_NFCB_SENSB_RES_PROTO_TR2_MASK) ]; } #endif /* RFAL_FEATURE_NFCB */