mirror of
https://gitlab.com/fabinfra/fabhardware/FabReader2.git
synced 2025-03-13 07:01:53 +01:00
934 lines
32 KiB
C
934 lines
32 KiB
C
/******************************************************************************
|
|
* \attention
|
|
*
|
|
* <h2><center>© COPYRIGHT 2019 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: NDEF firmware
|
|
* Revision:
|
|
* LANGUAGE: ISO C99
|
|
*/
|
|
|
|
/*! \file
|
|
*
|
|
* \author
|
|
*
|
|
* \brief Provides NDEF methods and definitions to access NFC Forum T4T
|
|
*
|
|
* This module provides an interface to perform as a NFC Reader/Writer
|
|
* to handle a Type 4 Tag T4T
|
|
*
|
|
*/
|
|
|
|
/*
|
|
******************************************************************************
|
|
* INCLUDES
|
|
******************************************************************************
|
|
*/
|
|
#include "ndef_poller.h"
|
|
#include "ndef_t4t.h"
|
|
#include "utils.h"
|
|
|
|
/*
|
|
******************************************************************************
|
|
* ENABLE SWITCH
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef RFAL_FEATURE_T4T
|
|
#error " RFAL: Module configuration missing. Please enable/disable T4T module by setting: RFAL_FEATURE_T4T "
|
|
#endif
|
|
|
|
#if RFAL_FEATURE_T4T
|
|
|
|
#if RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN < MAX(NDEF_T4T_MAX_CAPDU_LEN, NDEF_T4T_MAX_RAPDU_LEN)
|
|
#error " RFAL: ISO-DEP APDU Max length must be greater than ISO7816 Command/Response-APDU length. Please increase RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN"
|
|
#endif
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL DEFINES
|
|
******************************************************************************
|
|
*/
|
|
|
|
#define NDEF_T4T_FID_SIZE 2U /*!< File Id size */
|
|
#define NDEF_T4T_WRITE_ODO_PREFIX_SIZE 7U /*!< Size of ODO for Write Binary: 54 03 xxyyzz 53 Ld */
|
|
|
|
#define NDEF_T4T_DEFAULT_MLC 0x000DU /*!< Defauit Max Lc value before reading CCFILE values */
|
|
#define NDEF_T4T_DEFAULT_MLE 0x000FU /*!< Defauit Max Le value before reading CCFILE values */
|
|
|
|
#define NDEF_T4T_OFFSET_MAX 0x7FFFU /*!< Maximum offset value for ReadBinary */
|
|
#define NDEF_T4T_ODO_OFFSET_MAX 0xFFFFFEU /*!< Maximum offset value for ReadBinary with ODO */
|
|
|
|
#define NDEF_T4T_CCFILEV2_LEN 15U /*!< CCFILE Len mapping version V2 */
|
|
#define NDEF_T4T_CCFILEV3_LEN 17U /*!< CCFILE Len mapping version V3 */
|
|
|
|
#define NDEF_T4T_NDEF_CTLV_T 0x04U /*!< NDEF-File_Ctrl_TLV T field value */
|
|
#define NDEF_T4T_ENDEF_CTLV_T 0x06U /*!< ENDEF-File_Ctrl_TLV T field value */
|
|
|
|
#define NDEF_T4T_NDEF_CTLV_L 0x06U /*!< NDEF-File_Ctrl_TLV T field value */
|
|
#define NDEF_T4T_ENDEF_CTLV_L 0x08U /*!< ENDEF-File_Ctrl_TLV T field value */
|
|
|
|
#define NDEF_T4T_MIN_VALID_MLE 0x000FU /*!< Min valid MLe. MLe valid range 000Fh-FFFFh */
|
|
#define NDEF_T4T_MIN_VALID_MLC 0x000DU /*!< Min valid MLc. MLc valid range 000Dh-FFFFh */
|
|
|
|
#define NDEF_T4T_NLEN_LEN 2U /*!< NLEN LEN (mapping version v2.0): 2 bytes */
|
|
#define NDEF_T4T_ENLEN_LEN 4U /*!< ENLEN LEN (mapping version v3.0): 4 bytes */
|
|
|
|
#define NDEF_T4T_MIN_NLEN 3U /*!< Minimun non null NLEN value. TS T4T v1.0 B */
|
|
|
|
#define NDEF_T4T_MV2_MAX_OFSSET 0x7FFFU /*!< ReadBinary maximum Offset (offset range 0000-7FFFh)*/
|
|
|
|
#define NDEF_T4T_MAX_MLE 255U /*!< Maximum MLe value supported in this implementation (short field coding). Le=0 (MLe=256) not supported by some tag. */
|
|
#define NDEF_T4T_MAX_MLC 255U /*!< Maximum MLc value supported in this implementation (short field coding). */
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL TYPES
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL MACROS
|
|
******************************************************************************
|
|
*/
|
|
|
|
#define ndefT4TisT4TDevice(device) ((((device)->type == RFAL_NFC_LISTEN_TYPE_NFCA) && ((device)->dev.nfca.type == RFAL_NFCA_T4T)) || ((device)->type == RFAL_NFC_LISTEN_TYPE_NFCB))
|
|
|
|
/*
|
|
******************************************************************************
|
|
* LOCAL VARIABLES
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
******************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
******************************************************************************
|
|
*/
|
|
static void ndefT4TInitializeIsoDepTxRxParam(ndefContext *ctx, rfalIsoDepApduTxRxParam *isoDepAPDU);
|
|
static ReturnCode ndefT4TTransceiveTxRx(ndefContext *ctx, rfalIsoDepApduTxRxParam *isoDepAPDU);
|
|
static ReturnCode ndefT4TReadAndParseCCFile(ndefContext *ctx);
|
|
|
|
/*
|
|
******************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*******************************************************************************/
|
|
static void ndefT4TInitializeIsoDepTxRxParam(ndefContext *ctx, rfalIsoDepApduTxRxParam *isoDepAPDU)
|
|
{
|
|
/* Initialize the ISO-DEP protocol transceive context */
|
|
isoDepAPDU->txBuf = &ctx->subCtx.t4t.cApduBuf;
|
|
isoDepAPDU->DID = ctx->device.proto.isoDep.info.DID;
|
|
isoDepAPDU->FWT = ctx->device.proto.isoDep.info.FWT;
|
|
isoDepAPDU->dFWT = ctx->device.proto.isoDep.info.dFWT;
|
|
isoDepAPDU->FSx = ctx->device.proto.isoDep.info.FSx;
|
|
isoDepAPDU->ourFSx = RFAL_ISODEP_FSX_KEEP;
|
|
isoDepAPDU->rxBuf = &ctx->subCtx.t4t.rApduBuf;
|
|
isoDepAPDU->tmpBuf = &ctx->subCtx.t4t.tmpBuf;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
static ReturnCode ndefT4TTransceiveTxRx(ndefContext *ctx, rfalIsoDepApduTxRxParam *isoDepAPDU)
|
|
{
|
|
ReturnCode ret;
|
|
|
|
/* Initialize respAPDU */
|
|
ctx->subCtx.t4t.respAPDU.rApduBuf = &ctx->subCtx.t4t.rApduBuf;
|
|
isoDepAPDU->rxLen = &ctx->subCtx.t4t.respAPDU.rcvdLen;
|
|
|
|
ret = rfalIsoDepStartApduTransceive(*isoDepAPDU);
|
|
if( ret == ERR_NONE )
|
|
{
|
|
do {
|
|
/* Blocking implementation, T4T may define rather long timeouts */
|
|
rfalWorker();
|
|
ret = rfalIsoDepGetApduTransceiveStatus();
|
|
} while (ret == ERR_BUSY);
|
|
}
|
|
|
|
if (ret != ERR_NONE)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = rfalT4TPollerParseRAPDU(&ctx->subCtx.t4t.respAPDU);
|
|
ctx->subCtx.t4t.rApduBodyLen = ctx->subCtx.t4t.respAPDU.rApduBodyLen;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
static ReturnCode ndefT4TReadAndParseCCFile(ndefContext *ctx)
|
|
{
|
|
static const uint8_t RFAL_T4T_FID_CC[] = {0xE1, 0x03}; /*!< FID_CC-File T4T 1.0 4.2 */
|
|
|
|
ReturnCode ret;
|
|
uint8_t dataIt;
|
|
|
|
/* Select CCFILE TS T4T v1.0 7.2.1.3 */
|
|
ret = ndefT4TPollerSelectFile(ctx, RFAL_T4T_FID_CC);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.4 */
|
|
return ret;
|
|
}
|
|
|
|
/* Read CCFILE TS T4T v1.0 7.2.1.5 */
|
|
/* read CCFILE assuming at least 15 bytes are available. If V3 found will read the extra bytes in a second step */
|
|
ret = ndefT4TPollerReadBinary(ctx, 0U, NDEF_T4T_CCFILEV2_LEN);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.6 */
|
|
return ret;
|
|
}
|
|
(void)ST_MEMCPY(ctx->ccBuf, ctx->subCtx.t4t.rApduBuf.apdu, NDEF_T4T_CCFILEV2_LEN);
|
|
dataIt = 0;
|
|
ctx->cc.t4t.ccLen = GETU16(&ctx->ccBuf[dataIt]);
|
|
dataIt += (uint8_t)sizeof(uint16_t);
|
|
ctx->cc.t4t.vNo = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.mLe = GETU16(&ctx->ccBuf[dataIt]);
|
|
dataIt += (uint8_t)sizeof(uint16_t);
|
|
ctx->cc.t4t.mLc = GETU16(&ctx->ccBuf[dataIt]);
|
|
dataIt += (uint8_t)sizeof(uint16_t);
|
|
|
|
/* TS T4T v1.0 7.2.1.7 verify MLe and MLc are within the valid range */
|
|
if( (ctx->cc.t4t.mLe < NDEF_T4T_MIN_VALID_MLE) || (ctx->cc.t4t.mLc < NDEF_T4T_MIN_VALID_MLC) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.8 */
|
|
return ERR_REQUEST;
|
|
}
|
|
|
|
ctx->subCtx.t4t.curMLe = (uint8_t)MIN(ctx->cc.t4t.mLe, NDEF_T4T_MAX_MLE); /* Only short field codind supported */
|
|
ctx->subCtx.t4t.curMLc = (uint8_t)MIN(ctx->cc.t4t.mLc, NDEF_T4T_MAX_MLC); /* Only short field codind supported */
|
|
|
|
/* TS T4T v1.0 7.2.1.7 and 4.3.2.4 verify support of mapping version */
|
|
if( ndefMajorVersion(ctx->cc.t4t.vNo) > ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.8 */
|
|
return ERR_REQUEST;
|
|
}
|
|
if( ndefMajorVersion(ctx->cc.t4t.vNo) == ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) )
|
|
{
|
|
/* V3 found: read remainng bytes */
|
|
ret = ndefT4TPollerReadBinary(ctx, NDEF_T4T_CCFILEV2_LEN, NDEF_T4T_CCFILEV3_LEN - NDEF_T4T_CCFILEV2_LEN);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.6 */
|
|
return ret;
|
|
}
|
|
(void)ST_MEMCPY(&ctx->ccBuf[NDEF_T4T_CCFILEV2_LEN], ctx->subCtx.t4t.rApduBuf.apdu, NDEF_T4T_CCFILEV3_LEN - NDEF_T4T_CCFILEV2_LEN);
|
|
|
|
/* TS T4T v1.0 7.2.1.7 verify coding as in table 5 */
|
|
if( ctx->ccBuf[dataIt] != NDEF_T4T_ENDEF_CTLV_T )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.8 */
|
|
return ERR_REQUEST;
|
|
}
|
|
dataIt++;
|
|
if( ctx->ccBuf[dataIt] < NDEF_T4T_ENDEF_CTLV_L )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.8 */
|
|
return ERR_REQUEST;
|
|
}
|
|
dataIt++;
|
|
ctx->cc.t4t.fileId[0U] = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.fileId[1U] = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.fileSize = GETU32(&ctx->ccBuf[dataIt]);
|
|
dataIt += (uint8_t)sizeof(uint32_t);
|
|
ctx->cc.t4t.readAccess = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.writeAccess = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
}
|
|
else
|
|
{
|
|
if( ctx->ccBuf[dataIt] != NDEF_T4T_NDEF_CTLV_T )
|
|
{
|
|
return ERR_REQUEST;
|
|
}
|
|
dataIt++;
|
|
if( ctx->ccBuf[dataIt] < NDEF_T4T_NDEF_CTLV_L )
|
|
{
|
|
return ERR_REQUEST;
|
|
}
|
|
dataIt++;
|
|
ctx->cc.t4t.fileId[0U] = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.fileId[1U] = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.fileSize = ndefBytes2Uint16(ctx->ccBuf[dataIt], ctx->ccBuf[dataIt + 1U]);
|
|
dataIt += (uint8_t)sizeof(uint16_t);
|
|
ctx->cc.t4t.readAccess = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
ctx->cc.t4t.writeAccess = ctx->ccBuf[dataIt];
|
|
dataIt++;
|
|
}
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerSelectNdefTagApplication(ndefContext *ctx)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
static const uint8_t NDEF_T4T_AID_NDEF[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; /*!< AID_NDEF v2.0 or higher T4T 1.0 4.3.3 */
|
|
static const uint8_t NDEF_T4T_AID_NDEF_V1[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00}; /*!< AID_NDEF v1.0 T4T 1.0 4.3.3 */
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeSelectAppl(isoDepAPDU.txBuf, NDEF_T4T_AID_NDEF, (uint8_t)sizeof(NDEF_T4T_AID_NDEF), &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
if( ret == ERR_NONE )
|
|
{
|
|
/* application v2 or higher found */
|
|
ctx->subCtx.t4t.mv1Flag = false;
|
|
return ret;
|
|
}
|
|
|
|
if( ret != ERR_REQUEST )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* if v2 application not found, try v1 */
|
|
(void)rfalT4TPollerComposeSelectAppl(isoDepAPDU.txBuf, NDEF_T4T_AID_NDEF_V1, (uint8_t)sizeof(NDEF_T4T_AID_NDEF_V1), &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
if( ret == ERR_NONE )
|
|
{
|
|
/* application v1 found */
|
|
ctx->subCtx.t4t.mv1Flag = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerSelectFile(ndefContext *ctx, const uint8_t *fileId)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
|
|
if (ctx->subCtx.t4t.mv1Flag)
|
|
{
|
|
(void)rfalT4TPollerComposeSelectFileV1Mapping(isoDepAPDU.txBuf, fileId, (uint8_t)sizeof(fileId), &isoDepAPDU.txBufLen);
|
|
}
|
|
else
|
|
{
|
|
(void)rfalT4TPollerComposeSelectFile(isoDepAPDU.txBuf, fileId, NDEF_T4T_FID_SIZE, &isoDepAPDU.txBufLen);
|
|
}
|
|
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerReadBinary(ndefContext *ctx, uint16_t offset, uint8_t len)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (len > ctx->subCtx.t4t.curMLe) || (offset > NDEF_T4T_OFFSET_MAX) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeReadData(isoDepAPDU.txBuf, offset, len, &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerReadBinaryODO(ndefContext *ctx, uint32_t offset, uint8_t len)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (len > ctx->subCtx.t4t.curMLe) || (offset > NDEF_T4T_ODO_OFFSET_MAX) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeReadDataODO(isoDepAPDU.txBuf, offset, len, &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerReadBytes(ndefContext *ctx, uint32_t offset, uint32_t len, uint8_t *buf, uint32_t *rcvdLen)
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t le;
|
|
uint32_t lvOffset = offset;
|
|
uint32_t lvLen = len;
|
|
uint8_t * lvBuf = buf;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (lvLen == 0U) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
if( rcvdLen != NULL )
|
|
{
|
|
*rcvdLen = 0U;
|
|
}
|
|
|
|
do {
|
|
le = ( lvLen > ctx->subCtx.t4t.curMLe ) ? ctx->subCtx.t4t.curMLe : (uint8_t)lvLen;
|
|
if( lvOffset > NDEF_T4T_MV2_MAX_OFSSET )
|
|
{
|
|
ret = ndefT4TPollerReadBinaryODO(ctx, lvOffset, le);
|
|
}
|
|
else
|
|
{
|
|
ret = ndefT4TPollerReadBinary(ctx, (uint16_t)lvOffset, le);
|
|
}
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
if( ctx->subCtx.t4t.rApduBodyLen == 0U )
|
|
{
|
|
break; /* no more to read */
|
|
}
|
|
if( ctx->subCtx.t4t.rApduBodyLen > lvLen )
|
|
{
|
|
return ERR_SYSTEM;
|
|
}
|
|
(void)ST_MEMCPY(lvBuf, ctx->subCtx.t4t.rApduBuf.apdu, ctx->subCtx.t4t.rApduBodyLen);
|
|
lvBuf = &lvBuf[ctx->subCtx.t4t.rApduBodyLen];
|
|
lvOffset += ctx->subCtx.t4t.rApduBodyLen;
|
|
lvLen -= ctx->subCtx.t4t.rApduBodyLen;
|
|
if( rcvdLen != NULL )
|
|
{
|
|
*rcvdLen += ctx->subCtx.t4t.rApduBodyLen;
|
|
}
|
|
} while( lvLen != 0U );
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerContextInitialization(ndefContext *ctx, const rfalNfcDevice *dev)
|
|
{
|
|
if( (ctx == NULL) || (dev == NULL) || !ndefT4TisT4TDevice(dev) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
(void)ST_MEMCPY(&ctx->device, dev, sizeof(ctx->device));
|
|
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
ctx->subCtx.t4t.curMLc = NDEF_T4T_DEFAULT_MLC;
|
|
ctx->subCtx.t4t.curMLe = NDEF_T4T_DEFAULT_MLE;
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerNdefDetect(ndefContext *ctx, ndefInfo *info)
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t* nLen;
|
|
uint8_t nlenLen;
|
|
|
|
if( info != NULL )
|
|
{
|
|
info->state = NDEF_STATE_INVALID;
|
|
info->majorVersion = 0U;
|
|
info->minorVersion = 0U;
|
|
info->areaLen = 0U;
|
|
info->areaAvalableSpaceLen = 0U;
|
|
info->messageLen = 0U;
|
|
}
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
|
|
/* Select NDEF Tag application TS T4T v1.0 7.2.1.1 */
|
|
ret = ndefT4TPollerSelectNdefTagApplication(ctx);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.2 */
|
|
return ret;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.1.3 and following */
|
|
ret = ndefT4TReadAndParseCCFile(ctx);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
nlenLen = ( ndefMajorVersion(ctx->cc.t4t.vNo) == ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) ) ? NDEF_T4T_ENLEN_LEN : NDEF_T4T_NLEN_LEN;
|
|
|
|
/* TS T4T v1.0 7.2.1.7 verify file READ access */
|
|
if( !(ndefT4TIsReadAccessGranted(ctx->cc.t4t.readAccess)) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.8 */
|
|
return ERR_REQUEST;
|
|
}
|
|
/* File size need at least be enough to store NLEN or ENLEN */
|
|
if( ctx->cc.t4t.fileSize < nlenLen)
|
|
{
|
|
return ERR_REQUEST;
|
|
}
|
|
|
|
/* Select NDEF File TS T4T v1.0 7.2.1.9 */
|
|
ret = ndefT4TPollerSelectFile(ctx, ctx->cc.t4t.fileId);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.10 */
|
|
return ret;
|
|
}
|
|
/* Read NLEN/ENLEN TS T4T v1.0 7.2.1.11 */
|
|
ret = ndefT4TPollerReadBinary(ctx, 0U, nlenLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.11 */
|
|
return ret;
|
|
}
|
|
nLen = ctx->subCtx.t4t.rApduBuf.apdu;
|
|
ctx->messageLen = (nlenLen == NDEF_T4T_ENLEN_LEN) ? GETU32(&nLen[0]) : (uint32_t)ndefBytes2Uint16(nLen[0], nLen[1]);
|
|
ctx->messageOffset = nlenLen;
|
|
ctx->areaLen = ctx->cc.t4t.fileSize;
|
|
|
|
if ( (ctx->messageLen > (ctx->cc.t4t.fileSize - nlenLen)) || ((ctx->messageLen > 0U) && (ctx->messageLen < NDEF_T4T_MIN_NLEN)) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.11 */
|
|
return ERR_REQUEST;
|
|
}
|
|
|
|
if( ctx->messageLen == 0U )
|
|
{
|
|
if( !(ndefT4TIsWriteAccessGranted(ctx->cc.t4t.writeAccess)) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.1.11 */
|
|
return ERR_REQUEST;
|
|
}
|
|
ctx->state = NDEF_STATE_INITIALIZED;
|
|
}
|
|
else
|
|
{
|
|
ctx->state = (ndefT4TIsWriteAccessGranted(ctx->cc.t4t.writeAccess)) ? NDEF_STATE_READWRITE : NDEF_STATE_READONLY;
|
|
}
|
|
if( info != NULL )
|
|
{
|
|
info->state = ctx->state;
|
|
info->majorVersion = ndefMajorVersion(ctx->cc.t4t.vNo);
|
|
info->minorVersion = ndefMinorVersion(ctx->cc.t4t.vNo);
|
|
info->areaLen = ctx->areaLen;
|
|
info->areaAvalableSpaceLen = ctx->areaLen - ctx->messageOffset;
|
|
info->messageLen = ctx->messageLen;
|
|
}
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerReadRawMessage(ndefContext *ctx, uint8_t *buf, uint32_t bufLen, uint32_t *rcvdLen)
|
|
{
|
|
ReturnCode ret;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (buf == NULL) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
/* TS T4T v1.0 7.2.2.1: T4T NDEF Detect should have been called before NDEF read procedure */
|
|
/* Warning: current selected file must not be changed between NDEF Detect procedure and NDEF read procedure*/
|
|
|
|
/* TS T4T v1.0 7.3.3.2: check presence of NDEF message */
|
|
if ( ctx->state <= NDEF_STATE_INITIALIZED )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.2.2 */
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
if( ctx->messageLen > bufLen )
|
|
{
|
|
return ERR_NOMEM;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.3.3.3: read the NDEF message */
|
|
ret = ndefT4TPollerReadBytes(ctx, ctx->messageOffset, ctx->messageLen, buf, rcvdLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if NDEF_FEATURE_ALL
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerWriteBinary(ndefContext *ctx, uint16_t offset, const uint8_t *data, uint8_t len)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (len > ctx->subCtx.t4t.curMLc) || (offset > NDEF_T4T_OFFSET_MAX) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeWriteData(isoDepAPDU.txBuf, offset, data, len, &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerWriteBinaryODO(ndefContext *ctx, uint32_t offset, const uint8_t *data, uint8_t len)
|
|
{
|
|
ReturnCode ret;
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (len > ctx->subCtx.t4t.curMLc) || (offset > NDEF_T4T_ODO_OFFSET_MAX) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeWriteDataODO(isoDepAPDU.txBuf, offset, data, len, &isoDepAPDU.txBufLen);
|
|
ret = ndefT4TTransceiveTxRx(ctx, &isoDepAPDU);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerWriteBytes(ndefContext *ctx, uint32_t offset, const uint8_t *buf, uint32_t len)
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t lc;
|
|
uint32_t lvOffset = offset;
|
|
uint32_t lvLen = len;
|
|
const uint8_t * lvBuf = buf;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || (lvLen == 0U) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
do {
|
|
|
|
if( lvOffset > NDEF_T4T_MV2_MAX_OFSSET )
|
|
{
|
|
lc = ( lvLen > ((uint32_t)ctx->subCtx.t4t.curMLc - NDEF_T4T_WRITE_ODO_PREFIX_SIZE) ) ? (uint8_t)(ctx->subCtx.t4t.curMLc - NDEF_T4T_WRITE_ODO_PREFIX_SIZE) : (uint8_t)lvLen;
|
|
ret = ndefT4TPollerWriteBinaryODO(ctx, lvOffset, lvBuf, lc);
|
|
}
|
|
else
|
|
{
|
|
lc = ( lvLen > ctx->subCtx.t4t.curMLc ) ? ctx->subCtx.t4t.curMLc : (uint8_t)lvLen;
|
|
ret = ndefT4TPollerWriteBinary(ctx, (uint16_t)lvOffset, lvBuf, lc);
|
|
}
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
lvBuf = &lvBuf[lc];
|
|
lvOffset += lc;
|
|
lvLen -= lc;
|
|
} while( lvLen != 0U );
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerWriteRawMessageLen(ndefContext *ctx, uint32_t rawMessageLen)
|
|
{
|
|
ReturnCode ret;
|
|
uint8_t buf[NDEF_T4T_ENLEN_LEN];
|
|
uint8_t dataIt;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
if ( (ctx->state != NDEF_STATE_INITIALIZED) && (ctx->state != NDEF_STATE_READWRITE) )
|
|
{
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
dataIt = 0U;
|
|
if( ndefMajorVersion(ctx->cc.t4t.vNo) == ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) )
|
|
{
|
|
buf[dataIt] = (uint8_t)(rawMessageLen >> 24U);
|
|
dataIt++;
|
|
buf[dataIt] = (uint8_t)(rawMessageLen >> 16U);
|
|
dataIt++;
|
|
buf[dataIt] = (uint8_t)(rawMessageLen >> 8U);
|
|
dataIt++;
|
|
buf[dataIt] = (uint8_t)(rawMessageLen);
|
|
dataIt++;
|
|
}
|
|
else
|
|
{
|
|
buf[dataIt] = (uint8_t)(rawMessageLen >> 8U);
|
|
dataIt++;
|
|
buf[dataIt] = (uint8_t)(rawMessageLen);
|
|
dataIt++;
|
|
}
|
|
|
|
ret = ndefT4TPollerWriteBytes(ctx, 0U, buf, dataIt);
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerWriteRawMessage(ndefContext *ctx, const uint8_t *buf, uint32_t bufLen)
|
|
{
|
|
ReturnCode ret;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) || ((buf == NULL) && (bufLen != 0U)) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.1: T4T NDEF Detect should have been called before NDEF write procedure */
|
|
/* Warning: current selected file must not be changed between NDEF Detect procedure and NDEF Write procedure*/
|
|
|
|
/* TS T4T v1.0 7.3.3.2: check write access condition */
|
|
if ( (ctx->state != NDEF_STATE_INITIALIZED) && (ctx->state != NDEF_STATE_READWRITE) )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.3.2 */
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.3: check Mapping Version */
|
|
/* Done automatically inside underlying fucntions */
|
|
|
|
/* TS T4T v1.0 7.2.3.4/8 verify length of the NDEF message */
|
|
ret = ndefT4TPollerCheckAvailableSpace(ctx, bufLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.3.4/8 */
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.5/9 Write value 0000h in NLEN field (resp. 00000000h in ENLEN field) */
|
|
ret = ndefT4TPollerBeginWriteMessage(ctx, bufLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
/* Conclude procedure TS T4T v1.0 7.2.3.5/9 */
|
|
return ret;
|
|
}
|
|
|
|
if( bufLen != 0U )
|
|
{
|
|
/* TS T4T v1.0 7.2.3.6/10 Write NDEF message) */
|
|
ret = ndefT4TPollerWriteBytes(ctx, ctx->messageOffset, buf, bufLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.3.6/10 */
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
return ret;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.7/11 Write value length in NLEN field (resp. in ENLEN field) */
|
|
ret = ndefT4TPollerEndWriteMessage(ctx, bufLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure TS T4T v1.0 7.2.3.7/11 */
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerTagFormat(ndefContext *ctx, const ndefCapabilityContainer *cc, uint32_t options)
|
|
{
|
|
ReturnCode ret;
|
|
|
|
uint8_t buf[NDEF_T4T_ENLEN_LEN];
|
|
|
|
NO_WARNING(cc);
|
|
NO_WARNING(options);
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ret = ndefT4TPollerSelectNdefTagApplication(ctx);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = ndefT4TReadAndParseCCFile(ctx);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = ndefT4TPollerSelectFile(ctx, ctx->cc.t4t.fileId);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
return ret;
|
|
}
|
|
(void)ST_MEMSET(buf, 0x00, sizeof(buf));
|
|
ret = ndefT4TPollerWriteBytes(ctx, 0U, buf, ( ndefMajorVersion(ctx->cc.t4t.vNo) == ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) ) ? NDEF_T4T_ENLEN_LEN : NDEF_T4T_NLEN_LEN);
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerCheckPresence(ndefContext *ctx)
|
|
{
|
|
rfalIsoDepApduTxRxParam isoDepAPDU;
|
|
ReturnCode ret;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
ndefT4TInitializeIsoDepTxRxParam(ctx, &isoDepAPDU);
|
|
(void)rfalT4TPollerComposeReadData(isoDepAPDU.txBuf, 0, 1, &isoDepAPDU.txBufLen);
|
|
|
|
/* Initialize respAPDU */
|
|
ctx->subCtx.t4t.respAPDU.rApduBuf = &ctx->subCtx.t4t.rApduBuf;
|
|
isoDepAPDU.rxLen = &ctx->subCtx.t4t.respAPDU.rcvdLen;
|
|
|
|
(void)rfalIsoDepStartApduTransceive(isoDepAPDU);
|
|
do {
|
|
/* Blocking implementation, T4T may define rather long timeouts */
|
|
rfalWorker();
|
|
ret = rfalIsoDepGetApduTransceiveStatus();
|
|
} while (ret == ERR_BUSY);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerCheckAvailableSpace(const ndefContext *ctx, uint32_t messageLen)
|
|
{
|
|
uint8_t nlenLen;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
if ( ctx->state == NDEF_STATE_INVALID )
|
|
{
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
nlenLen = ( ndefMajorVersion(ctx->cc.t4t.vNo) == ndefMajorVersion(NDEF_T4T_MAPPING_VERSION_3_0) ) ? NDEF_T4T_ENLEN_LEN : NDEF_T4T_NLEN_LEN;
|
|
if( (messageLen + (uint32_t)nlenLen) > ctx->cc.t4t.fileSize )
|
|
{
|
|
return ERR_NOMEM;
|
|
}
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerBeginWriteMessage(ndefContext *ctx, uint32_t messageLen)
|
|
{
|
|
ReturnCode ret;
|
|
NO_WARNING(messageLen);
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
if( (ctx->state != NDEF_STATE_INITIALIZED) && (ctx->state != NDEF_STATE_READWRITE) )
|
|
{
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.5/9 Write value 0000h in NLEN field (resp. 00000000h in ENLEN field) */
|
|
ret = ndefT4TPollerWriteRawMessageLen(ctx, 0U);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure */
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
return ret;
|
|
}
|
|
|
|
ctx->state = NDEF_STATE_INITIALIZED;
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
/*******************************************************************************/
|
|
ReturnCode ndefT4TPollerEndWriteMessage(ndefContext *ctx, uint32_t messageLen)
|
|
{
|
|
ReturnCode ret;
|
|
|
|
if( (ctx == NULL) || !ndefT4TisT4TDevice(&ctx->device) )
|
|
{
|
|
return ERR_PARAM;
|
|
}
|
|
|
|
if( ctx->state != NDEF_STATE_INITIALIZED )
|
|
{
|
|
return ERR_WRONG_STATE;
|
|
}
|
|
|
|
/* TS T4T v1.0 7.2.3.7/11 Write value length in NLEN field (resp. in ENLEN field) */
|
|
ret = ndefT4TPollerWriteRawMessageLen(ctx, messageLen);
|
|
if( ret != ERR_NONE )
|
|
{
|
|
/* Conclude procedure */
|
|
ctx->state = NDEF_STATE_INVALID;
|
|
return ret;
|
|
}
|
|
ctx->messageLen = messageLen;
|
|
ctx->state = (ctx->messageLen == 0U) ? NDEF_STATE_INITIALIZED : NDEF_STATE_READWRITE;
|
|
return ERR_NONE;
|
|
}
|
|
|
|
#endif /* NDEF_FEATURE_ALL */
|
|
|
|
#endif /* RFAL_FEATURE_T4T */
|