/****************************************************************************** * \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 */