/****************************************************************************** * \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_nfcv.c * * \author Gustavo Patricio * * \brief Implementation of NFC-V Poller (ISO15693) device * * The definitions and helpers methods provided by this module are * aligned with NFC-V (ISO15693) * * The definitions and helpers methods provided by this module * are aligned with NFC-V Digital 2.1 * */ /* ****************************************************************************** * INCLUDES ****************************************************************************** */ #include "rfal_nfcv.h" #include "utils.h" /* ****************************************************************************** * ENABLE SWITCH ****************************************************************************** */ #ifndef RFAL_FEATURE_NFCV #define RFAL_FEATURE_NFCV false /* NFC-V module configuration missing. Disabled by default */ #endif #if RFAL_FEATURE_NFCV /* ****************************************************************************** * GLOBAL DEFINES ****************************************************************************** */ #define RFAL_NFCV_INV_REQ_FLAG 0x06U /*!< INVENTORY_REQ INV_FLAG Digital 2.1 9.6.1 */ #define RFAL_NFCV_MASKVAL_MAX_LEN 8U /*!< Mask value max length: 64 bits (UID length) */ #define RFAL_NFCV_MASKVAL_MAX_1SLOT_LEN 64U /*!< Mask value max length in 1 Slot mode in bits Digital 2.1 9.6.1.6 */ #define RFAL_NFCV_MASKVAL_MAX_16SLOT_LEN 60U /*!< Mask value max length in 16 Slot mode in bits Digital 2.1 9.6.1.6 */ #define RFAL_NFCV_MAX_SLOTS 16U /*!< NFC-V max number of Slots */ #define RFAL_NFCV_INV_REQ_HEADER_LEN 3U /*!< INVENTORY_REQ header length (INV_FLAG, CMD, MASK_LEN) */ #define RFAL_NFCV_INV_RES_LEN 10U /*!< INVENTORY_RES length */ #define RFAL_NFCV_WR_MUL_REQ_HEADER_LEN 4U /*!< Write Multiple header length (INV_FLAG, CMD, [UID], BNo, Bno) */ #define RFAL_NFCV_CMD_LEN 1U /*!< Commandbyte length */ #define RFAL_NFCV_FLAG_POS 0U /*!< Flag byte position */ #define RFAL_NFCV_FLAG_LEN 1U /*!< Flag byte length */ #define RFAL_NFCV_DATASTART_POS 1U /*!< Position of start of data */ #define RFAL_NFCV_DSFI_LEN 1U /*!< DSFID length */ #define RFAL_NFCV_SLPREQ_REQ_FLAG 0x22U /*!< SLPV_REQ request flags Digital 2.0 (Candidate) 9.7.1.1 */ #define RFAL_NFCV_RES_FLAG_NOERROR 0x00U /*!< RES_FLAG indicating no error (checked during activation) */ #define RFAL_NFCV_MAX_COLL_SUPPORTED 16U /*!< Maximum number of collisions supported by the Anticollision loop */ #define RFAL_NFCV_FDT_MAX rfalConvMsTo1fc(20) /*!< Maximum Wait time FDTV,EOF and MAX2 Digital 2.1 B.5*/ #define RFAL_NFCV_FDT_MAX1 4394U /*!< Read alike command FWT FDTV,LISTEN,MAX1 Digital 2.0 B.5 */ /*! Time from special frame to EOF * ISO15693 2009 10.4.2 : 20ms * NFC Forum defines Digital 2.0 9.7.4 : FDTV,EOF = [10 ; 20]ms */ #define RFAL_NFCV_FDT_EOF 20U /*! Time between slots - ISO 15693 defines t3min depending on modulation depth and data rate. * With only high-bitrate supported, AM modulation and a length of 12 bytes (96bits) for INV_RES we get: * - ISO t3min = 96/26 ms + 300us = 4 ms * - NFC Forum defines FDTV,INVENT_NORES = (4394 + 2048)/fc. Digital 2.0 B.5*/ #define RFAL_NFCV_FDT_V_INVENT_NORES 4U /* ****************************************************************************** * GLOBAL MACROS ****************************************************************************** */ /*! Checks if a valid INVENTORY_RES is valid Digital 2.2 9.6.2.1 & 9.6.2.3 */ #define rfalNfcvCheckInvRes( f, l ) (((l)==rfalConvBytesToBits(RFAL_NFCV_INV_RES_LEN + RFAL_NFCV_CRC_LEN)) && ((f)==RFAL_NFCV_RES_FLAG_NOERROR)) /* ****************************************************************************** * GLOBAL TYPES ****************************************************************************** */ /*! NFC-V INVENTORY_REQ format Digital 2.0 9.6.1 */ typedef struct { uint8_t INV_FLAG; /*!< Inventory Flags */ uint8_t CMD; /*!< Command code: 01h */ uint8_t MASK_LEN; /*!< Mask Value Length */ uint8_t MASK_VALUE[RFAL_NFCV_MASKVAL_MAX_LEN]; /*!< Mask Value */ } rfalNfcvInventoryReq; /*! NFC-V SLP_REQ format Digital 2.0 (Candidate) 9.7.1 */ typedef struct { uint8_t REQ_FLAG; /*!< Request Flags */ uint8_t CMD; /*!< Command code: 02h */ uint8_t UID[RFAL_NFCV_UID_LEN]; /*!< Mask Value */ } rfalNfcvSlpvReq; /*! Container for a collision found during Anticollision loop */ typedef struct { uint8_t maskLen; uint8_t maskVal[RFAL_NFCV_MASKVAL_MAX_LEN]; }rfalNfcvCollision; /* ****************************************************************************** * LOCAL FUNCTION PROTOTYPES ****************************************************************************** */ static ReturnCode rfalNfcvParseError( uint8_t err ); /* ****************************************************************************** * LOCAL VARIABLES ****************************************************************************** */ /* ****************************************************************************** * LOCAL FUNCTIONS ****************************************************************************** */ /*******************************************************************************/ static ReturnCode rfalNfcvParseError( uint8_t err ) { switch(err) { case RFAL_NFCV_ERROR_CMD_NOT_SUPPORTED: case RFAL_NFCV_ERROR_OPTION_NOT_SUPPORTED: return ERR_NOTSUPP; case RFAL_NFCV_ERROR_CMD_NOT_RECOGNIZED: return ERR_PROTO; case RFAL_NFCV_ERROR_WRITE_FAILED: return ERR_WRITE; default: return ERR_REQUEST; } } /* ****************************************************************************** * GLOBAL FUNCTIONS ****************************************************************************** */ /*******************************************************************************/ ReturnCode rfalNfcvPollerInitialize( void ) { ReturnCode ret; EXIT_ON_ERR( ret, rfalSetMode( RFAL_MODE_POLL_NFCV, RFAL_BR_26p48, RFAL_BR_26p48 ) ); rfalSetErrorHandling( RFAL_ERRORHANDLING_NFC ); rfalSetGT( RFAL_GT_NFCV ); rfalSetFDTListen( RFAL_FDT_LISTEN_NFCV_POLLER ); rfalSetFDTPoll( RFAL_FDT_POLL_NFCV_POLLER ); return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcvPollerCheckPresence( rfalNfcvInventoryRes *invRes ) { ReturnCode ret; /* INVENTORY_REQ with 1 slot and no Mask Activity 2.0 (Candidate) 9.2.3.32 */ ret = rfalNfcvPollerInventory( RFAL_NFCV_NUM_SLOTS_1, 0, NULL, invRes, NULL ); if( (ret == ERR_RF_COLLISION) || (ret == ERR_CRC) || (ret == ERR_FRAMING) || (ret == ERR_PROTO) ) { ret = ERR_NONE; } return ret; } /*******************************************************************************/ ReturnCode rfalNfcvPollerInventory( rfalNfcvNumSlots nSlots, uint8_t maskLen, const uint8_t *maskVal, rfalNfcvInventoryRes *invRes, uint16_t* rcvdLen ) { ReturnCode ret; rfalNfcvInventoryReq invReq; uint16_t rxLen; if( ((maskVal == NULL) && (maskLen != 0U)) || (invRes == NULL) ) { return ERR_PARAM; } invReq.INV_FLAG = (RFAL_NFCV_INV_REQ_FLAG | (uint8_t)nSlots); invReq.CMD = RFAL_NFCV_CMD_INVENTORY; invReq.MASK_LEN = (uint8_t)MIN( maskLen, ((nSlots == RFAL_NFCV_NUM_SLOTS_1) ? RFAL_NFCV_MASKVAL_MAX_1SLOT_LEN : RFAL_NFCV_MASKVAL_MAX_16SLOT_LEN) ); /* Digital 2.0 9.6.1.6 */ if( (rfalConvBitsToBytes(invReq.MASK_LEN) > 0U) && (maskVal != NULL) ) /* MISRA 21.18 & 1.3 */ { ST_MEMCPY( invReq.MASK_VALUE, maskVal, rfalConvBitsToBytes(invReq.MASK_LEN) ); } ret = rfalISO15693TransceiveAnticollisionFrame( (uint8_t*)&invReq, (uint8_t)(RFAL_NFCV_INV_REQ_HEADER_LEN + rfalConvBitsToBytes(invReq.MASK_LEN)), (uint8_t*)invRes, sizeof(rfalNfcvInventoryRes), &rxLen ); /* Check for optional output parameter */ if( rcvdLen != NULL ) { *rcvdLen = rxLen; } if( ret == ERR_NONE ) { /* Check for valid INVENTORY_RES Digital 2.2 9.6.2.1 & 9.6.2.3 */ if( !rfalNfcvCheckInvRes( invRes->RES_FLAG, rxLen ) ) { return ERR_PROTO; } } return ret; } /*******************************************************************************/ ReturnCode rfalNfcvPollerCollisionResolution( rfalComplianceMode compMode, uint8_t devLimit, rfalNfcvListenDevice *nfcvDevList, uint8_t *devCnt ) { ReturnCode ret; uint8_t slotNum; uint16_t rcvdLen; uint8_t colIt; uint8_t colCnt; uint8_t colPos; bool colPending; rfalNfcvCollision colFound[RFAL_NFCV_MAX_COLL_SUPPORTED]; if( (nfcvDevList == NULL) || (devCnt == NULL) ) { return ERR_PARAM; } /* Initialize parameters */ *devCnt = 0; colIt = 0; colCnt = 0; colPending = false; ST_MEMSET(colFound, 0x00, (sizeof(rfalNfcvCollision)*RFAL_NFCV_MAX_COLL_SUPPORTED) ); if( devLimit > 0U ) /* MISRA 21.18 */ { ST_MEMSET(nfcvDevList, 0x00, (sizeof(rfalNfcvListenDevice)*devLimit) ); } NO_WARNING(colPending); /* colPending is not exposed externally, in future it might become exposed/ouput parameter */ if( compMode == RFAL_COMPLIANCE_MODE_NFC ) { /* Send INVENTORY_REQ with one slot Activity 2.1 9.3.7.1 (Symbol 0) */ ret = rfalNfcvPollerInventory( RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &nfcvDevList->InvRes, NULL ); if( ret == ERR_TIMEOUT ) /* Exit if no device found Activity 2.1 9.3.7.2 (Symbol 1) */ { return ERR_NONE; } if( ret == ERR_NONE ) /* Device found without transmission error/collision Activity 2.1 9.3.7.3 (Symbol 2) */ { (*devCnt)++; return ERR_NONE; } /* A Collision has been identified Activity 2.1 9.3.7.4 (Symbol 3) */ colPending = true; colCnt = 1; /* Check if the Collision Resolution is set to perform only Collision detection Activity 2.1 9.3.7.5 (Symbol 4)*/ if( devLimit == 0U ) { return ERR_RF_COLLISION; } platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); /*******************************************************************************/ /* Collisions pending, Anticollision loop must be executed */ /*******************************************************************************/ } else { /* Advance to 16 slots below without mask. Will give a good chance to identify multiple cards */ colPending = true; colCnt = 1; } /* Execute until all collisions are resolved Activity 2.1 9.3.7.18 (Symbol 17) */ do { /* Activity 2.1 9.3.7.7 (Symbol 6 / 7) */ colPending = false; slotNum = 0; do { if( slotNum == 0U ) { /* Send INVENTORY_REQ with 16 slots Activity 2.1 9.3.7.9 (Symbol 8) */ ret = rfalNfcvPollerInventory( RFAL_NFCV_NUM_SLOTS_16, colFound[colIt].maskLen, colFound[colIt].maskVal, &nfcvDevList[(*devCnt)].InvRes, &rcvdLen ); } else { ret = rfalISO15693TransceiveEOFAnticollision( (uint8_t*)&nfcvDevList[(*devCnt)].InvRes, sizeof(rfalNfcvInventoryRes), &rcvdLen ); } slotNum++; /*******************************************************************************/ if( ret != ERR_TIMEOUT ) { if( rcvdLen < rfalConvBytesToBits(RFAL_NFCV_INV_RES_LEN + RFAL_NFCV_CRC_LEN) ) { /* If only a partial frame was received make sure the FDT_V_INVENT_NORES is fulfilled */ platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); } /* Check if response is a correct frame (no TxRx error) Activity 2.1 9.3.7.11 (Symbol 10)*/ if( (ret == ERR_NONE) || (ret == ERR_PROTO) ) { /* Check if the device found is already on the list and its response is a valid INVENTORY_RES */ if( rfalNfcvCheckInvRes( nfcvDevList[(*devCnt)].InvRes.RES_FLAG, rcvdLen ) ) { /* Activity 2.1 9.3.7.12 (Symbol 11) */ (*devCnt)++; } } else /* Treat everything else as collision */ { /* Activity 2.1 9.3.7.17 (Symbol 16) */ colPending = true; /*******************************************************************************/ /* Ensure that this collision still fits on the container */ if( colCnt < RFAL_NFCV_MAX_COLL_SUPPORTED ) { /* Store this collision on the container to be resolved later */ /* Activity 2.1 9.3.7.17 (Symbol 16): add the collision information * (MASK_VAL + SN) to the list containing the collision information */ ST_MEMCPY(colFound[colCnt].maskVal, colFound[colIt].maskVal, RFAL_NFCV_UID_LEN); colPos = colFound[colIt].maskLen; colFound[colCnt].maskVal[(colPos/RFAL_BITS_IN_BYTE)] &= (uint8_t)((1U << (colPos % RFAL_BITS_IN_BYTE)) - 1U); colFound[colCnt].maskVal[(colPos/RFAL_BITS_IN_BYTE)] |= (uint8_t)((slotNum-1U) << (colPos % RFAL_BITS_IN_BYTE)); colFound[colCnt].maskVal[((colPos/RFAL_BITS_IN_BYTE)+1U)] = (uint8_t)((slotNum-1U) >> (RFAL_BITS_IN_BYTE - (colPos % RFAL_BITS_IN_BYTE))); colFound[colCnt].maskLen = (colFound[colIt].maskLen + 4U); colCnt++; } } } else { /* Timeout */ platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); } /* Check if devices found have reached device limit Activity 2.1 9.3.7.13 (Symbol 12) */ if( *devCnt >= devLimit ) { return ERR_NONE; } } while( slotNum < RFAL_NFCV_MAX_SLOTS ); /* Slot loop */ colIt++; } while( colIt < colCnt ); /* Collisions found loop */ return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcvPollerSleepCollisionResolution( uint8_t devLimit, rfalNfcvListenDevice *nfcvDevList, uint8_t *devCnt ) { uint8_t tmpDevCnt; ReturnCode ret; uint8_t i; if( (nfcvDevList == NULL) || (devCnt == NULL) ) { return ERR_PARAM; } *devCnt = 0; do { tmpDevCnt = 0; ret = rfalNfcvPollerCollisionResolution( RFAL_COMPLIANCE_MODE_ISO, (devLimit - *devCnt), &nfcvDevList[*devCnt], &tmpDevCnt ); for( i = *devCnt; i < (*devCnt + tmpDevCnt); i++ ) { rfalNfcvPollerSleep( 0x00, nfcvDevList[i].InvRes.UID ); nfcvDevList[i].isSleep = true; } *devCnt += tmpDevCnt; } while( (ret == ERR_NONE) && (tmpDevCnt > 0U) && (*devCnt < devLimit) ); return ret; } /*******************************************************************************/ ReturnCode rfalNfcvPollerSleep( uint8_t flags, const uint8_t* uid ) { ReturnCode ret; rfalNfcvSlpvReq slpReq; uint8_t rxBuf; /* dummy buffer, just to perform Rx */ if( uid == NULL ) { return ERR_PARAM; } /* Compute SLPV_REQ */ slpReq.REQ_FLAG = (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS); /* Should be with UID according Digital 2.0 (Candidate) 9.7.1.1 */ slpReq.CMD = RFAL_NFCV_CMD_SLPV; ST_MEMCPY( slpReq.UID, uid, RFAL_NFCV_UID_LEN ); /* NFC Forum device SHALL wait at least FDTVpp to consider the SLPV acknowledged (FDTVpp = FDTVpoll) Digital 2.0 (Candidate) 9.7 9.8.2 */ ret = rfalTransceiveBlockingTxRx( (uint8_t*)&slpReq, sizeof(rfalNfcvSlpvReq), &rxBuf, sizeof(rxBuf), NULL, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCV_FDT_MAX1 ); if( ret != ERR_TIMEOUT ) { return ret; } return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcvPollerSelect( uint8_t flags, const uint8_t* uid ) { uint16_t rcvLen; rfalNfcvGenericRes res; if( uid == NULL ) { return ERR_PARAM; } return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_SELECT, flags, RFAL_NFCV_PARAM_SKIP, uid, NULL, 0U, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerReadSingleBlock( uint8_t flags, const uint8_t* uid, uint8_t blockNum, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { uint8_t bn; bn = blockNum; return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_READ_SINGLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, &bn, sizeof(uint8_t), rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerWriteSingleBlock( uint8_t flags, const uint8_t* uid, uint8_t blockNum, const uint8_t* wrData, uint8_t blockLen ) { uint8_t data[(RFAL_NFCV_BLOCKNUM_LEN + RFAL_NFCV_MAX_BLOCK_LEN)]; uint8_t dataLen; uint16_t rcvLen; rfalNfcvGenericRes res; /* Check for valid parameters */ if( (blockLen == 0U) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || (wrData == NULL) ) { return ERR_PARAM; } dataLen = 0U; /* Compute Request Data */ data[dataLen++] = blockNum; /* Set Block Number (8 bits) */ ST_MEMCPY( &data[dataLen], wrData, blockLen ); /* Append Block data to write */ dataLen += blockLen; return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_WRITE_SINGLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerLockBlock( uint8_t flags, const uint8_t* uid, uint8_t blockNum ) { uint16_t rcvLen; rfalNfcvGenericRes res; uint8_t bn; bn = blockNum; return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_LOCK_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, &bn, sizeof(uint8_t), (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerReadMultipleBlocks( uint8_t flags, const uint8_t* uid, uint8_t firstBlockNum, uint8_t numOfBlocks, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { uint8_t data[(RFAL_NFCV_BLOCKNUM_LEN + RFAL_NFCV_BLOCKNUM_LEN)]; uint8_t dataLen; dataLen = 0U; /* Compute Request Data */ data[dataLen++] = firstBlockNum; /* Set first Block Number */ data[dataLen++] = numOfBlocks; /* Set number of blocks to read */ return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_READ_MULTIPLE_BLOCKS, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerWriteMultipleBlocks( uint8_t flags, const uint8_t* uid, uint8_t firstBlockNum, uint8_t numOfBlocks, uint8_t *txBuf, uint16_t txBufLen, uint8_t blockLen, const uint8_t* wrData, uint16_t wrDataLen ) { ReturnCode ret; uint16_t rcvLen; uint16_t reqLen; rfalNfcvGenericRes res; uint16_t msgIt; /* Calculate required buffer length */ reqLen = (uint16_t)((uid != NULL) ? (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + RFAL_NFCV_UID_LEN + wrDataLen) : (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + wrDataLen)); if( (reqLen > txBufLen) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || ((((uint16_t)numOfBlocks) * (uint16_t)blockLen) != wrDataLen) || (numOfBlocks == 0U) || (wrData == NULL) ) { return ERR_PARAM; } msgIt = 0; /* Compute Request Command */ txBuf[msgIt++] = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); txBuf[msgIt++] = RFAL_NFCV_CMD_WRITE_MULTIPLE_BLOCKS; /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ if( uid != NULL ) { txBuf[RFAL_NFCV_FLAG_POS] |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; ST_MEMCPY( &txBuf[msgIt], uid, RFAL_NFCV_UID_LEN ); msgIt += (uint8_t)RFAL_NFCV_UID_LEN; } txBuf[msgIt++] = firstBlockNum; txBuf[msgIt++] = (numOfBlocks - 1U); if( wrDataLen > 0U ) /* MISRA 21.18 */ { ST_MEMCPY( &txBuf[msgIt], wrData, wrDataLen ); msgIt += wrDataLen; } /* Transceive Command */ ret = rfalTransceiveBlockingTxRx( txBuf, msgIt, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCV_FDT_MAX ); if( ret != ERR_NONE ) { return ret; } /* Check if the response minimum length has been received */ if( rcvLen < (uint8_t)RFAL_NFCV_FLAG_LEN ) { return ERR_PROTO; } /* Check if an error has been signalled */ if( (res.RES_FLAG & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U ) { return rfalNfcvParseError( *res.data ); } return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedReadSingleBlock( uint8_t flags, const uint8_t* uid, uint16_t blockNum, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { uint8_t data[RFAL_NFCV_BLOCKNUM_EXTENDED_LEN]; uint8_t dataLen; dataLen = 0U; /* Compute Request Data */ data[dataLen++] = (uint8_t)blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_EXTENDED_READ_SINGLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedWriteSingleBlock( uint8_t flags, const uint8_t* uid, uint16_t blockNum, const uint8_t* wrData, uint8_t blockLen ) { uint8_t data[(RFAL_NFCV_BLOCKNUM_EXTENDED_LEN + RFAL_NFCV_MAX_BLOCK_LEN)]; uint8_t dataLen; uint16_t rcvLen; rfalNfcvGenericRes res; /* Check for valid parameters */ if( (blockLen == 0U) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) ) { return ERR_PARAM; } dataLen = 0U; /* Compute Request Data */ data[dataLen++] = (uint8_t)blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); ST_MEMCPY( &data[dataLen], wrData, blockLen ); /* Append Block data to write */ dataLen += blockLen; return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_EXTENDED_WRITE_SINGLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedLockSingleBlock( uint8_t flags, const uint8_t* uid, uint16_t blockNum ) { uint8_t data[RFAL_NFCV_BLOCKNUM_EXTENDED_LEN]; uint8_t dataLen; uint16_t rcvLen; rfalNfcvGenericRes res; dataLen = 0U; /* Compute Request Data */ data[dataLen++] = (uint8_t)blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_EXTENDED_LOCK_SINGLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedReadMultipleBlocks( uint8_t flags, const uint8_t* uid, uint16_t firstBlockNum, uint16_t numOfBlocks, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { uint8_t data[(RFAL_NFCV_BLOCKNUM_EXTENDED_LEN + RFAL_NFCV_BLOCKNUM_EXTENDED_LEN)]; uint8_t dataLen; dataLen = 0U; /* Compute Request Data */ data[dataLen++] = (uint8_t)((firstBlockNum >> 0U) & 0xFFU); data[dataLen++] = (uint8_t)((firstBlockNum >> 8U) & 0xFFU); data[dataLen++] = (uint8_t)((numOfBlocks >> 0U) & 0xFFU); data[dataLen++] = (uint8_t)((numOfBlocks >> 8U) & 0xFFU); return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_EXTENDED_READ_MULTIPLE_BLOCK, flags, RFAL_NFCV_PARAM_SKIP, uid, data, dataLen, rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedWriteMultipleBlocks( uint8_t flags, const uint8_t* uid, uint16_t firstBlockNum, uint16_t numOfBlocks, uint8_t *txBuf, uint16_t txBufLen, uint8_t blockLen, const uint8_t* wrData, uint16_t wrDataLen ) { ReturnCode ret; uint16_t rcvLen; uint16_t reqLen; rfalNfcvGenericRes res; uint16_t msgIt; uint16_t nBlocks; /* Calculate required buffer length */ reqLen = ((uid != NULL) ? (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + RFAL_NFCV_UID_LEN + wrDataLen) : (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + wrDataLen) ); if( (reqLen > txBufLen) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || (( (uint16_t)numOfBlocks * (uint16_t)blockLen) != wrDataLen) || (numOfBlocks == 0U) ) { return ERR_PARAM; } msgIt = 0; nBlocks = (numOfBlocks - 1U); /* Compute Request Command */ txBuf[msgIt++] = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); txBuf[msgIt++] = RFAL_NFCV_CMD_EXTENDED_WRITE_MULTIPLE_BLOCK; /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ if( uid != NULL ) { txBuf[RFAL_NFCV_FLAG_POS] |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; ST_MEMCPY( &txBuf[msgIt], uid, RFAL_NFCV_UID_LEN ); msgIt += (uint8_t)RFAL_NFCV_UID_LEN; } txBuf[msgIt++] = (uint8_t)((firstBlockNum >> 0) & 0xFFU); txBuf[msgIt++] = (uint8_t)((firstBlockNum >> 8) & 0xFFU); txBuf[msgIt++] = (uint8_t)((nBlocks >> 0) & 0xFFU); txBuf[msgIt++] = (uint8_t)((nBlocks >> 8) & 0xFFU); if( wrDataLen > 0U ) /* MISRA 21.18 */ { ST_MEMCPY( &txBuf[msgIt], wrData, wrDataLen ); msgIt += wrDataLen; } /* Transceive Command */ ret = rfalTransceiveBlockingTxRx( txBuf, msgIt, (uint8_t*)&res, sizeof(rfalNfcvGenericRes), &rcvLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCV_FDT_MAX ); if( ret != ERR_NONE ) { return ret; } /* Check if the response minimum length has been received */ if( rcvLen < (uint8_t)RFAL_NFCV_FLAG_LEN ) { return ERR_PROTO; } /* Check if an error has been signalled */ if( (res.RES_FLAG & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U ) { return rfalNfcvParseError( *res.data ); } return ERR_NONE; } /*******************************************************************************/ ReturnCode rfalNfcvPollerGetSystemInformation( uint8_t flags, const uint8_t* uid, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_GET_SYS_INFO, flags, RFAL_NFCV_PARAM_SKIP, uid, NULL, 0U, rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerExtendedGetSystemInformation( uint8_t flags, const uint8_t* uid, uint8_t requestField, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { return rfalNfcvPollerTransceiveReq( RFAL_NFCV_CMD_EXTENDED_GET_SYS_INFO, flags, requestField, uid, NULL, 0U, rxBuf, rxBufLen, rcvLen ); } /*******************************************************************************/ ReturnCode rfalNfcvPollerTransceiveReq( uint8_t cmd, uint8_t flags, uint8_t param, const uint8_t* uid, const uint8_t *data, uint16_t dataLen, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t *rcvLen ) { ReturnCode ret; rfalNfcvGenericReq req; uint8_t msgIt; rfalBitRate rxBR; bool fastMode; msgIt = 0; fastMode = false; /* Check for valid parameters */ if( (rxBuf == NULL) || (rcvLen == NULL) || ((dataLen > 0U) && (data == NULL)) || (dataLen > ((uid != NULL) ? RFAL_NFCV_MAX_GEN_DATA_LEN : (RFAL_NFCV_MAX_GEN_DATA_LEN - RFAL_NFCV_UID_LEN))) ) { return ERR_PARAM; } /* Check if the command is an ST's Fast command */ if( (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_SINGLE_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_EXTENDED_READ_SINGLE_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MULTIPLE_BLOCKS) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_EXTENDED_READ_MULTIPLE_BLOCKS) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_WRITE_MESSAGE) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MESSAGE_LENGTH) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MESSAGE) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_DYN_CONFIGURATION) || (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_WRITE_DYN_CONFIGURATION) ) { /* Store current Rx bit rate and move to fast mode */ rfalGetBitRate( NULL, &rxBR ); rfalSetBitRate( RFAL_BR_KEEP, RFAL_BR_52p97 ); fastMode = true; } /* Compute Request Command */ req.REQ_FLAG = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); req.CMD = cmd; /* Prepend parameter on ceratin proprietary requests: IC Manuf, Parameters */ if( param != RFAL_NFCV_PARAM_SKIP ) { req.payload.data[msgIt++] = param; } /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ if( uid != NULL ) { req.REQ_FLAG |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; ST_MEMCPY( &req.payload.data[msgIt], uid, RFAL_NFCV_UID_LEN ); msgIt += RFAL_NFCV_UID_LEN; } if( dataLen > 0U ) { ST_MEMCPY( &req.payload.data[msgIt], data, dataLen); msgIt += (uint8_t)dataLen; } /* Transceive Command */ ret = rfalTransceiveBlockingTxRx( (uint8_t*)&req, (RFAL_NFCV_CMD_LEN + RFAL_NFCV_FLAG_LEN +(uint16_t)msgIt), rxBuf, rxBufLen, rcvLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_NFCV_FDT_MAX ); /* If the Option Flag is set in certain commands an EOF needs to be sent after 20ms to retrieve the VICC response ISO15693-3 2009 10.4.2 & 10.4.3 & 10.4.5 */ if( ((flags & (uint8_t)RFAL_NFCV_REQ_FLAG_OPTION) != 0U) && ((cmd == (uint8_t)RFAL_NFCV_CMD_WRITE_SINGLE_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_WRITE_MULTIPLE_BLOCKS) || (cmd == (uint8_t)RFAL_NFCV_CMD_LOCK_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_WRITE_SINGLE_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_LOCK_SINGLE_BLOCK) || (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_WRITE_MULTIPLE_BLOCK)) ) { ret = rfalISO15693TransceiveEOF( rxBuf, (uint8_t)rxBufLen, rcvLen ); } /* Restore Rx BitRate */ if( fastMode ) { rfalSetBitRate( RFAL_BR_KEEP, rxBR ); } if( ret != ERR_NONE ) { return ret; } /* Check if the response minimum length has been received */ if( (*rcvLen) < (uint8_t)RFAL_NFCV_FLAG_LEN ) { return ERR_PROTO; } /* Check if an error has been signalled */ if( (rxBuf[RFAL_NFCV_FLAG_POS] & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U ) { return rfalNfcvParseError( rxBuf[RFAL_NFCV_DATASTART_POS] ); } return ERR_NONE; } #endif /* RFAL_FEATURE_NFCV */