diff --git a/.editorconfig b/.editorconfig
index cf375163a6..aaa22ea26c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,3 +16,9 @@ charset = utf-8
end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset
+
+# Ignore rfal nfc library
+[/core/embed/io/nfc/rfal/**]
+end_of_line = unset
+insert_final_newline = unset
+trim_trailing_whitespace = unset
diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index b08d2d6201..93d0a3d5ce 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -106,6 +106,7 @@ SOURCE_PRODTEST = [
'embed/projects/prodtest/cmd/prodtest_get_cpuid.c',
'embed/projects/prodtest/cmd/prodtest_haptic.c',
'embed/projects/prodtest/cmd/prodtest_help.c',
+ 'embed/projects/prodtest/cmd/prodtest_nfc.c',
'embed/projects/prodtest/cmd/prodtest_optiga.c',
'embed/projects/prodtest/cmd/prodtest_otp_batch.c',
'embed/projects/prodtest/cmd/prodtest_otp_variant.c',
diff --git a/core/embed/io/nfc/inc/io/nfc.h b/core/embed/io/nfc/inc/io/nfc.h
index 51953a963d..736e07f7d7 100644
--- a/core/embed/io/nfc/inc/io/nfc.h
+++ b/core/embed/io/nfc/inc/io/nfc.h
@@ -17,23 +17,37 @@
* along with this program. If not, see .
*/
-#ifndef TREZORHAL_NFC_H
-#define TREZORHAL_NFC_H
+#pragma once
-#include
-#include
+#include
+
+#define NFC_MAX_UID_LEN 10
+#define NFC_MAX_UID_BUF_SIZE ((NFC_MAX_UID_LEN + 1) * 2)
typedef enum {
- NFC_CARD_EMU_TECH_A,
- NFC_CARD_EMU_TECH_V,
-} nfc_card_emul_tech_t;
+ NFC_POLLER_TECH_A = 0x1,
+ NFC_POLLER_TECH_B = 0x1 << 1,
+ NFC_POLLER_TECH_F = 0x1 << 2,
+ NFC_POLLER_TECH_V = 0x1 << 3,
+ NFC_CARD_EMU_TECH_A = 0x1 << 4,
+ NFC_CARD_EMU_TECH_F = 0x1 << 5,
+} nfc_tech_t;
typedef enum {
- NFC_POLLER_TECH_A,
- NFC_POLLER_TECH_B,
- NFC_POLLER_TECH_F,
- NFC_POLLER_TECH_V,
-} nfc_poller_tech_t;
+ NFC_DEV_TYPE_A,
+ NFC_DEV_TYPE_B,
+ NFC_DEV_TYPE_F,
+ NFC_DEV_TYPE_V,
+ NFC_DEV_TYPE_ST25TB,
+ NFC_DEV_TYPE_AP2P,
+ NFC_DEV_TYPE_UNKNOWN,
+} nfc_dev_type_t;
+
+typedef enum {
+ NFC_NO_EVENT,
+ NFC_EVENT_DEACTIVATED,
+ NFC_EVENT_ACTIVATED,
+} nfc_event_t;
typedef enum {
NFC_OK,
@@ -43,14 +57,43 @@ typedef enum {
NFC_INITIALIZATION_FAILED,
} nfc_status_t;
-nfc_status_t nfc_init();
+typedef struct {
+ uint8_t type;
+ char uid[NFC_MAX_UID_BUF_SIZE]; // Plus one for string termination
+ uint8_t uid_len;
+} nfc_dev_info_t;
-nfc_status_t nfc_deinit();
+// Initialize NFC driver including supportive RFAL middleware
+nfc_status_t nfc_init(void);
-nfc_status_t nfc_register_card_emu(nfc_card_emul_tech_t tech);
+// Deinitialize NFC driver
+void nfc_deinit(void);
-nfc_status_t nfc_register_poller(nfc_poller_tech_t tech);
+// Register NFC technology (or several) to be explored by NFC state machine
+// use this function before activating the state machine with nfc_activate_stm()
+nfc_status_t nfc_register_tech(const nfc_tech_t tech);
-nfc_status_t nfc_run_worker();
+// Activates the NFC RFAL state machine to explore the previously registered
+// technologies. The RFAL handles low-level NFC protocols and provides
+// information about the activated device. This function only starts the
+// exploration; you must regularly call nfc_get_event() to continue processing
+// NFC operations.
+nfc_status_t nfc_activate_stm(void);
-#endif // TREZORHAL_NFC_H
+// Deactivate the NFC RFAL state machine (put in IDLE state).
+nfc_status_t nfc_deactivate_stm(void);
+
+// Calls NFC RFAL worker to service the NFC state machine and expolore
+// registered technologies. This function has to be actively called in loop
+// (main NFC poll function), returns nfc event.
+nfc_status_t nfc_get_event(nfc_event_t *event);
+
+// Deactivate the currently activated NFC device and put RFAL state machine back
+// to discovary state.
+nfc_status_t nfc_dev_deactivate(void);
+
+// Read the general device information of the activated NFC device.
+nfc_status_t nfc_dev_read_info(nfc_dev_info_t *dev_info);
+
+// Write the NDEF message with the trezor.io URI to the activated NFC device.
+nfc_status_t nfc_dev_write_ndef_uri(void);
diff --git a/core/embed/io/nfc/st25r3916b/card_emulation.c b/core/embed/io/nfc/st25r3916b/card_emulation.c
new file mode 100644
index 0000000000..11aa173b3a
--- /dev/null
+++ b/core/embed/io/nfc/st25r3916b/card_emulation.c
@@ -0,0 +1,653 @@
+/**
+ ******************************************************************************
+ * @file card_emulation.c
+ * @author MMY Application Team
+ * @brief Body function to manage card emul mode
+ ******************************************************************************
+ ** This notice applies to any and all portions of this file
+ * that are not between comment pairs USER CODE BEGIN and
+ * USER CODE END. Other portions of this file, whether
+ * inserted by the user or by software development tools
+ * are owned by their respective copyright owners.
+ *
+ * COPYRIGHT(c) 2018 STMicroelectronics
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "card_emulation.h"
+#include "rfal_nfca.h"
+#include "rfal_nfcf.h"
+#include "rfal_rf.h"
+
+/** @addtogroup X-CUBE-NFC6_Applications
+ * @{
+ */
+
+/** @addtogroup CardEmulation
+ * @{
+ */
+
+/** @addtogroup CE_CardEmul
+ * @{
+ */
+
+/* Private typedef -----------------------------------------------------------*/
+/** @defgroup CE_CardEmul_Private_Typedef
+ * @{
+ */
+enum States {
+ STATE_IDLE = 0, /*!< Emulated Tag state idle */
+ STATE_APP_SELECTED = 1, /*!< Emulated Tag state application selected */
+ STATE_CC_SELECTED = 2, /*!< Emulated Tag state CCFile selected */
+ STATE_FID_SELECTED = 3, /*!< Emulated Tag state FileID selected */
+};
+/**
+ * @}
+ */
+
+/* Private define ------------------------------------------------------------*/
+/** @defgroup CE_CardEmul_Private_Define
+ * @{
+ */
+
+#define NDEF_SIZE 2048 /*!< Max NDEF size emulated. Range: 0005h - 7FFFh */
+#define T4T_CLA_00 0x00 /*!< CLA value for type 4 command */
+#define T4T_INS_SELECT \
+ 0xA4 /*!< INS value for select command */
+#define T4T_INS_READ \
+ 0xB0 /*!< INS value for reabbinary command */
+#define T4T_INS_UPDATE \
+ 0xD6 /*!< INS value for update command */
+#define FID_CC 0xE103 /*!< File ID number for CCFile */
+#define FID_NDEF 0x0001 /*!< File ID number for NDEF file */
+#define T3T_BLOCK_SIZE \
+ 0x10 /*!< Block size in Type 3 Tag */
+/**
+ * @}
+ */
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/** @defgroup CE_CardEmul_Private_Variables
+ * @{
+ */
+static uint8_t gNfcfNfcid[RFAL_NFCF_NFCID2_LEN];
+static uint8_t ndefFile[NDEF_SIZE]; /*!< Buffer to store NDEF File */
+static int8_t nState = STATE_IDLE; /*!< Type 4 tag emulation status */
+static int32_t nSelectedIdx = -1; /*!< current file selected */
+static int32_t nFiles = 2; /*!< Number of file emulated */
+
+/**
+ * CCLEN : Indicates the size of this CC File
+ * T4T_VNo : Indicates the Mapping Version
+ * MLe high : Max R-APDU size
+ * MLc high : Max C-APDU size
+ * NDEF FCI T: Indicates the NDEF-File_Ctrl_TLV
+ * NDEF FCI L: The length of the V-field
+ * NDEF FCI V1: NDEF File Identifier
+ * NDEF FCI V2: NDEF File size
+ * NDEF FCI V3: NDEF Read AC
+ * NDEF FCI V4: NDEF Write AC
+ */
+static uint8_t ccfile[] = {
+ 0x00,
+ 0x0F, /* CCLEN */
+ 0x20, /* T4T_VNo */
+ 0x00,
+ 0x7F, /* MLe */
+ 0x00,
+ 0x7F, /* MLc */
+ 0x04, /* T */
+ 0x06, /* L */
+ (FID_NDEF & 0xFF00) >> 8,
+ (FID_NDEF & 0x00FF), /* V1 */
+ (NDEF_SIZE & 0xFF00) >> 8,
+ (NDEF_SIZE & 0x00FF), /* V2 */
+ 0x00, /* V3 */
+ 0x00 /* V4 */
+};
+
+static uint32_t pdwFileSize[] = {sizeof(ccfile),
+ NDEF_SIZE}; /*!< Current emulated files size */
+
+/**
+ * NDEF length
+ * NDEF Header: MB,ME,SR,Well known Type
+ * NDEF type length
+ * NDEF payload length
+ * NDEF Type : URI
+ * NDEF URI abreviation field : http://www.
+ * NDEF URI string : st.com/st25-demo
+ */
+static const uint8_t ndef_uri[] = {
+ 0x00, 0x15, /* NDEF length */
+ 0xD1, /* NDEF Header */
+ 0x01, /* NDEF type length */
+ 0x11, /* NDEF payload length */
+ 0x55, /* NDEF Type */
+ 0x01, /* NDEF URI abreviation field */
+ 0x74, 0x72, 0x65, 0x7A, 0x6F, 0x72, 0x2E, 0x69, 0x6F}; /* NDEF URI string */
+
+__WEAK const uint8_t *NdefFile = ndef_uri;
+__WEAK uint32_t NdefFileLen = sizeof(ndef_uri);
+
+/**
+ * Ver : Indicates the NDEF mapping version
+ * Nbr : Indicates the number of blocks that can be read
+ * Nbw : Indicates the number of blocks that can be written
+ * NmaxB : Indicates the maximum number of blocks available for NDEF data
+ * WriteFlag : Indicates whether a previous NDEF write procedure has finished or
+ * not
RWFlag : Indicates data can be updated or not
Ln : Is the size
+ * of the actual stored NDEF data in bytes
Checksum : allows the
+ * Reader/Writer to check whether the Attribute Data are correct
+ */
+static uint8_t InformationBlock[] = {
+ 0x10, /* Ver */
+ 0x08, /* Nbr */
+ 0x08, /* Nbw */
+ 0x00, 0x0F, /* NmaxB */
+ 0x00, 0x00, 0x00, 0x00, /* RFU */
+ 0x00, /* WriteFlag */
+ 0x01, /* RWFlag */
+ 0x00, 0x00, 0x15, /* Ln */
+ 0x00, 0x45 /* Checksum */
+};
+/**
+ * @}
+ */
+
+/* Private function prototypes -----------------------------------------------*/
+
+/* Private functions ---------------------------------------------------------*/
+/** @defgroup CE_CardEmul_Private_Functions
+ * @{
+ */
+
+/**
+ *****************************************************************************
+ * @brief Compare 2 commands supplied in parameters
+ *
+ * @param[in] cmd : pointer to the received command.
+ * @param[in] find : pointer to the avalaible command.
+ * @param[in] len : length of the available command.
+ *
+ * @retval True : Same command.
+ * @retval False : Different command.
+ *****************************************************************************
+ */
+static bool cmd_compare(uint8_t *cmd, uint8_t *find, uint16_t len) {
+ for (int i = 0; i < 20; i++) {
+ if (!memcmp(&cmd[i], find, len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ *****************************************************************************
+ * @brief Manage the T4T Select answer to the reader
+ *
+ * @param[in] cmdData : pointer to the received command.
+ * @param[out] rspData : pointer to the answer to send.
+ *
+ * @return Answer size.
+ *****************************************************************************
+ */
+static uint16_t card_emulation_t4t_select(uint8_t *cmdData, uint8_t *rspData) {
+ bool success = false;
+ /*
+ * Cmd: CLA(1) | INS(1) | P1(1) | P2(1) | Lc(1) | Data(n) | [Le(1)]
+ * Rsp: [FCI(n)] | SW12
+ *
+ * Select App by Name NDEF: 00 A4 04 00 07 D2 76 00 00 85 01 01 00
+ * Select App by Name NDEF 4 ST: 00 A4 04 00 07 A0 00 00 00 03 00 00 00
+ * Select CC FID: 00 A4 00 0C 02 xx xx
+ * Select NDEF FID: 00 A4 00 0C 02 xx xx
+ */
+
+ uint8_t aid[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};
+ uint8_t fidCC[] = {FID_CC >> 8, FID_CC & 0xFF};
+ uint8_t fidNDEF[] = {FID_NDEF >> 8, FID_NDEF & 0xFF};
+ uint8_t selectFileId[] = {0xA4, 0x00, 0x0C, 0x02, 0x00, 0x01};
+
+ if (cmd_compare(cmdData, aid, sizeof(aid))) { /* Select Appli */
+ nState = STATE_APP_SELECTED;
+ success = true;
+ } else if ((nState >= STATE_APP_SELECTED) &&
+ cmd_compare(cmdData, fidCC, sizeof(fidCC))) { /* Select CC */
+ nState = STATE_CC_SELECTED;
+ nSelectedIdx = 0;
+ success = true;
+ } else if ((nState >= STATE_APP_SELECTED) &&
+ (cmd_compare(cmdData, fidNDEF, sizeof(fidNDEF)) ||
+ cmd_compare(cmdData, selectFileId,
+ sizeof(selectFileId)))) { /* Select NDEF */
+ nState = STATE_FID_SELECTED;
+ nSelectedIdx = 1;
+ success = true;
+ } else {
+ nState = STATE_IDLE;
+ }
+
+ rspData[0] = (success ? (char)0x90 : 0x6A);
+ rspData[1] = (success ? (char)0x00 : 0x82);
+
+ return 2;
+}
+
+/**
+ *****************************************************************************
+ * @brief Manage the T4T Read answer to the reader
+ *
+ * @param[in] cmdData : pointer to the received command.
+ * @param[out] rspData : pointer to the answer to send.
+ * @param[in] rspDataLen : size of the answer buffer.
+ *
+ * @return Answer size.
+ *****************************************************************************
+ */
+static uint16_t card_emulation_t4t_read(uint8_t *cmdData, uint8_t *rspData,
+ uint16_t rspDataLen) {
+ /*
+ * Cmd: CLA(1) | INS(1) | P1(1).. offset inside file high | P2(1).. offset
+ * inside file high | Le(1).. nBytes to read Rsp: BytesRead | SW12
+ */
+ unsigned short offset = (cmdData[2] << 8) | cmdData[3];
+ unsigned short toRead = cmdData[4];
+ uint8_t *ppbMemory;
+
+ if (rspDataLen < 2) {
+ // platformErrorHandle(); /* Must ensure appropriate buffer */
+ }
+
+ /* Any file selected */
+ if (nSelectedIdx < 0 || nSelectedIdx >= nFiles) {
+ rspData[0] = ((char)0x6A);
+ rspData[1] = ((char)0x82);
+ return 2;
+ }
+
+ /* offset + length exceed file size */
+ if ((unsigned long)(offset + toRead) > pdwFileSize[nSelectedIdx]) {
+ toRead = pdwFileSize[nSelectedIdx] - offset;
+ }
+
+ if (rspDataLen < (toRead + 2)) {
+ rspData[0] = ((char)0x6F);
+ rspData[1] = ((char)0x00);
+ return 2;
+ }
+
+ ppbMemory = (nSelectedIdx == 0 ? ccfile : ndefFile);
+ /* read data */
+ memcpy(rspData, &ppbMemory[offset], toRead);
+
+ rspData[toRead] = ((char)0x90);
+ rspData[toRead + 1] = ((char)0x00);
+ return toRead + 2;
+}
+
+/**
+ *****************************************************************************
+ * @brief Manage the T4T Update answer to the reader
+ *
+ * @param[in] cmdData : pointer to the received command.
+ * @param[in] rspData : pointer to the answer to send.
+ *
+ * @return Answer size.
+ *****************************************************************************
+ */
+static uint16_t card_emulation_t4t_update(uint8_t *cmdData, uint8_t *rspData) {
+ uint32_t offset = (cmdData[2] << 8) | cmdData[3];
+ uint32_t length = cmdData[4];
+
+ if (nSelectedIdx != 1) {
+ rspData[0] = ((char)0x6A);
+ rspData[1] = ((char)0x82);
+ return 2;
+ }
+
+ if ((unsigned long)(offset + length) > pdwFileSize[nSelectedIdx]) {
+ rspData[0] = ((char)0x62);
+ rspData[1] = ((char)0x82);
+ return 2;
+ }
+
+ memcpy((ndefFile + offset), &cmdData[5], length);
+
+ rspData[0] = ((char)0x90);
+ rspData[1] = ((char)0x00);
+ return 2;
+}
+
+/**
+ *****************************************************************************
+ * @brief Manage the T4T Read answer to the reader
+ *
+ * @param[in] cmdData : pointer to the received command.
+ * @param[out] rspData : pointer to the answer to send.
+ * @param[in] rspDataLen : size of the answer buffer.
+ *
+ * @return Answer size.
+ *****************************************************************************
+ */
+static uint16_t card_emulation_t3t_check(uint8_t *cmdData, uint8_t *rspData,
+ uint16_t rspDataLen) {
+ /*
+ * Cmd: cmd | NFCID2 | NoS | Service code list | NoB | Block list
+ * Rsp: rsp | NFCID2 | Status Flag 1 | Status Flag 2 | NoB | Block Data
+ */
+ uint8_t *block;
+ uint16_t blocknb[256];
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+ uint32_t nbmax = 0;
+
+ /* Command response */
+ rspData[idx++] = RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION + 1;
+
+ /* NFCID 2 bytes */
+ if (memcmp(gNfcfNfcid, &cmdData[RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CMD_LEN],
+ RFAL_NFCF_NFCID2_LEN) == 0) {
+ memcpy(&rspData[idx], &gNfcfNfcid, RFAL_NFCF_NFCID2_LEN);
+ idx += RFAL_NFCF_NFCID2_LEN;
+ } else {
+ /* If NFCID2 in command is different, no answer */
+ return 0;
+ }
+
+ /* Check for command errors */
+ if ((cmdData[10] != 1) || ((cmdData[11] != 0x09) && (cmdData[11] != 0x0B)) ||
+ (cmdData[13] == 0) || (cmdData[13] > InformationBlock[1])) {
+ rspData[idx++] = 0xFF;
+ rspData[idx++] = 0xFF;
+ return idx;
+ } else {
+ rspData[idx++] = 0x00;
+ rspData[idx++] = 0x00;
+ }
+
+ /* Verify CHECK response length */
+ if (rspDataLen < (11 + (cmdData[13] * T3T_BLOCK_SIZE))) {
+ // platformErrorHandle(); /* Must ensure appropriate buffer */
+ }
+
+ /* Nob */
+ rspData[idx++] = cmdData[13];
+
+ /* Retrieving block to read */
+ block = &cmdData[14];
+ for (cnt = 0; cnt < cmdData[13]; cnt++) {
+ /* TS T3T 5.6.1.5 Service Code List Order value SHALL be between 0 and NoS-1
+ */
+ if (((*block) & 0x0F) >= cmdData[10]) {
+ rspData[idx - 3] = 0xFF;
+ rspData[idx - 2] = 0x80; /* TS T3T table 13 - proprietary value to
+ indicate specific error conditions.*/
+ return (idx - 1);
+ }
+ /* Check block list element size */
+ if (*block & 0x80) {
+ /* 2-byte Block List element */
+ blocknb[cnt] = *(block + 1);
+ block += 2;
+ } else {
+ /* 3-byte Block List element */
+ blocknb[cnt] = *(block + 2); /* Little Endian Format */
+ blocknb[cnt] <<= 8;
+ blocknb[cnt] |= *(block + 1);
+ block += 3;
+ }
+
+ /* Return error if Blocknb > NmaxB */
+ nbmax = InformationBlock[3];
+ nbmax = (nbmax << 8) | InformationBlock[4];
+ if (blocknb[cnt] > nbmax) {
+ rspData[idx - 3] = 0xFF;
+ rspData[idx - 2] = 0x70;
+ return (idx - 1);
+ }
+ }
+
+ for (cnt = 0; cnt < cmdData[13]; cnt++) {
+ if (blocknb[cnt] == 0x0000) {
+ /* Read information block */
+ memcpy(&rspData[idx], InformationBlock, sizeof(InformationBlock));
+ idx += sizeof(InformationBlock);
+ } else {
+ /* Read ndef block */
+ memcpy(&rspData[idx],
+ &ndefFile[2 + ((blocknb[cnt] - 1) * T3T_BLOCK_SIZE)],
+ T3T_BLOCK_SIZE);
+ idx += T3T_BLOCK_SIZE;
+ }
+ }
+
+ return idx;
+}
+
+/**
+ *****************************************************************************
+ * @brief Manage the T3T Update answer to the reader
+ *
+ * @param[in] cmdData : pointer to the received command.
+ * @param[in] rspData : pointer to the answer to send.
+ *
+ * @return Answer size.
+ *****************************************************************************
+ */
+static uint16_t card_emulation_t3t_update(uint8_t *cmdData, uint8_t *rspData) {
+ /*
+ * Cmd: cmd | NFCID2 | NoS | Service code list | NoB | Block list | Block Data
+ * Rsp: rsp | NFCID2 | Status Flag 1 | Status Flag 2
+ */
+ uint8_t *block;
+ uint16_t blocknb[256];
+ uint32_t idx = 0;
+ uint32_t cnt = 0;
+ uint32_t nbmax = 0;
+
+ /* Command response */
+ rspData[idx++] = RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION + 1;
+
+ /* NFCID 2 bytes */
+ if (memcmp(gNfcfNfcid, &cmdData[RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CMD_LEN],
+ RFAL_NFCF_NFCID2_LEN) == 0) {
+ memcpy(&rspData[idx], gNfcfNfcid, RFAL_NFCF_NFCID2_LEN);
+ idx += RFAL_NFCF_NFCID2_LEN;
+ } else {
+ /* If NFCID2 in command is different, no answer */
+ return 0;
+ }
+
+ /* Check for command errors */
+ if ((cmdData[10] != 1) || (cmdData[11] != 0x09) || (cmdData[13] == 0) ||
+ (cmdData[13] > InformationBlock[2])) {
+ rspData[idx++] = 0xFF;
+ rspData[idx++] = 0xFF;
+ return idx;
+ } else {
+ rspData[idx++] = 0x00;
+ rspData[idx++] = 0x00;
+ }
+
+ /* Retrieving block to read */
+ block = &cmdData[14];
+ for (cnt = 0; cnt < cmdData[13]; cnt++) {
+ /* Check block list element size */
+ if (*block & 0x80) {
+ /* 2-byte Block List element */
+ blocknb[cnt] = *(block + 1);
+ block += 2;
+ } else {
+ /* 3-byte Block List element */
+ blocknb[cnt] = *(block + 2); /* Little Endian Format */
+ blocknb[cnt] <<= 8;
+ blocknb[cnt] |= *(block + 1);
+ block += 3;
+ }
+ /* Return error if Blocknb > NmaxB */
+ nbmax = InformationBlock[3];
+ nbmax = (nbmax << 8) | InformationBlock[4];
+ if (blocknb[cnt] > nbmax) {
+ rspData[idx - 2] = 0xFF;
+ rspData[idx - 1] = 0x70;
+ return idx;
+ }
+ }
+
+ for (cnt = 0; cnt < cmdData[13]; cnt++) {
+ if (blocknb[cnt] == 0x0000) {
+ /* Write information block */
+ memcpy(InformationBlock, block, T3T_BLOCK_SIZE);
+ block += T3T_BLOCK_SIZE;
+ } else {
+ /* Read ndef block */
+ memcpy(&ndefFile[2 + ((blocknb[cnt] - 1) * T3T_BLOCK_SIZE)], block,
+ T3T_BLOCK_SIZE);
+ block += T3T_BLOCK_SIZE;
+ }
+ }
+
+ /* Status flag answer */
+ rspData[idx++] = 0x00;
+ rspData[idx++] = 0x00;
+
+ return idx;
+}
+
+/**
+ *****************************************************************************
+ * @brief card emulation initialize
+ *
+ * Initializes the CE mode
+ *
+ * @param[in] nfcfNfcid : The NFCID to be used in T3T CE.
+ *
+ * @return None
+ *****************************************************************************
+ */
+void card_emulation_init(const uint8_t *nfcfNfcid) {
+ if (nfcfNfcid != NULL) {
+ memcpy(gNfcfNfcid, nfcfNfcid, RFAL_NFCF_NFCID2_LEN);
+ }
+
+ memcpy(ndefFile, (uint8_t *)NdefFile, NdefFileLen);
+
+ /* Update AIB Ln with actual NDEF length */
+ InformationBlock[12] = NdefFile[0];
+ InformationBlock[13] = NdefFile[1];
+ uint16_t checksum = 0;
+ for (int i = 0; i < 14; i++) {
+ checksum += InformationBlock[i];
+ }
+ InformationBlock[14] = checksum >> 8;
+ InformationBlock[15] = checksum & 0xFF;
+}
+
+/**
+ *****************************************************************************
+ * @brief Demo CE T4T
+ *
+ * Parses the received command and computes the response to be sent back to
+ * the Reader
+ *
+ * @param[in] rxData : pointer to the received command.
+ * @param[in] rxDataLen : length of the received command.
+ * @param[in] txBuf : pointer to where the response will be placed.
+ * @param[in] txBufLen : size of txBuf.
+ *
+ * @return Response size.
+ *****************************************************************************
+ */
+uint16_t card_emulation_t4t(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf,
+ uint16_t txBufLen) {
+ if ((txBuf == NULL) || (txBufLen < 2)) {
+ // platformErrorHandle(); /* Must ensure appropriate buffer */
+ return 0;
+ }
+
+ if ((rxData != NULL) && (rxDataLen >= 4)) {
+ if (rxData[0] == T4T_CLA_00) {
+ switch (rxData[1]) {
+ case T4T_INS_SELECT:
+ return card_emulation_t4t_select(rxData, txBuf);
+
+ case T4T_INS_READ:
+ return card_emulation_t4t_read(rxData, txBuf, txBufLen);
+
+ case T4T_INS_UPDATE:
+ return card_emulation_t4t_update(rxData, txBuf);
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Function not supported ... */
+ txBuf[0] = ((char)0x68);
+ txBuf[1] = ((char)0x00);
+ return 2;
+}
+
+/**
+ *****************************************************************************
+ * @brief Demo CE T3T
+ *
+ * Parses the received command and computes the response to be sent back to
+ * the Reader
+ *
+ * @param[in] rxData : pointer to the received command.
+ * @param[in] rxDataLen : length of the received command.
+ * @param[in] txBuf : pointer to where the response will be placed.
+ * @param[in] txBufLen : size of txBuf.
+ *
+ * @return Response size.
+ *****************************************************************************
+ */
+uint16_t card_emulation_t3t(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf,
+ uint16_t txBufLen) {
+ if ((txBuf == NULL) || (txBufLen < 11)) {
+ // platformErrorHandle(); /* Must ensure appropriate buffer */
+ return 0;
+ }
+
+ if ((rxData != NULL) && (rxDataLen >= 4)) {
+ switch (rxData[1]) {
+ case RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION:
+ return card_emulation_t3t_check(rxData, txBuf, txBufLen);
+
+ case RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION:
+ return card_emulation_t3t_update(rxData, txBuf);
+
+ default:
+ break;
+ }
+ }
+
+ /* Function not supported ... */
+ txBuf[0] = ((char)0xFF);
+ txBuf[1] = ((char)0xFF);
+ return 2;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/core/embed/io/nfc/st25r3916b/card_emulation.h b/core/embed/io/nfc/st25r3916b/card_emulation.h
new file mode 100644
index 0000000000..eebcb393c5
--- /dev/null
+++ b/core/embed/io/nfc/st25r3916b/card_emulation.h
@@ -0,0 +1,101 @@
+/**
+ ******************************************************************************
+ * @file card_emulation.h
+ * @author MMY Application Team
+ * @brief Implementation of Common CardEmulation parts
+ ******************************************************************************
+ ** This notice applies to any and all portions of this file
+ * that are not between comment pairs USER CODE BEGIN and
+ * USER CODE END. Other portions of this file, whether
+ * inserted by the user or by software development tools
+ * are owned by their respective copyright owners.
+ *
+ * COPYRIGHT(c) 2018 STMicroelectronics
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+
+/** @addtogroup X-CUBE-NFC6_Applications
+ * @brief Sample applications for X-NUCLEO-NFC06A1 STM32 expansion boards.
+ * @{
+ */
+
+/** @addtogroup CardEmulation
+ * @{
+ */
+
+/** @defgroup CE_CardEmul
+ * @brief Card Emulation management functions
+ * @{
+ */
+
+/* Exported constants --------------------------------------------------------*/
+/* T3T Information Block Bytes offset */
+#define T3T_INFBLK_VER_OFFSET 0
+#define T3T_INFBLK_NBR_OFFSET 1
+#define T3T_INFBLK_NBW_OFFSET 2
+#define T3T_INFBLK_NMAXB_OFFSET 3
+#define T3T_INFBLK_WRITEFLAG_OFFSET 9
+#define T3T_INFBLK_RWFLAG_OFFSET 10
+#define T3T_INFBLK_LN_OFFSET 11
+#define T3T_INFBCK_CHECKSUM_OFFSET 14
+
+/* T3T Information Block WriteFlag values */
+#define T3T_WRITEFLAG_OFF 0x00
+#define T3T_WRITEFLAG_ON 0x0F
+
+/* T3T COMMAND OFFSET */
+#define T3T_CHECK_RESP_CMD_OFFSET 0
+#define T3T_CHECK_RESP_NFCID2_OFFSET 1
+#define T3T_CHECK_RESP_SF1_OFFSET 9
+#define T3T_CHECK_RESP_SF2_OFFSET 10
+#define T3T_CHECK_RESP_NOB_OFFSET 11
+#define T3T_CHECK_RESP_DATA_OFFSET 12
+#define T3T_UPDATE_RESP_CMD_OFFSET 0
+#define T3T_UPDATE_RESP_NFCID2_OFFSET 1
+#define T3T_UPDATE_RESP_SF1_OFFSET 9
+#define T3T_UPDATE_RESP_SF2_OFFSET 10
+
+/* External variables --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+/** @defgroup CE_CardEmul_Exported_functions
+ * @{
+ */
+void card_emulation_init(const uint8_t *nfcfNfcid);
+uint16_t card_emulation_t3t(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf,
+ uint16_t txBufLen);
+uint16_t card_emulation_t4t(uint8_t *rxData, uint16_t rxDataLen, uint8_t *txBuf,
+ uint16_t txBufLen);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/******************* (C) COPYRIGHT 2018 STMicroelectronics *****END OF FILE****/
diff --git a/core/embed/io/nfc/st25r3916b/ndef.c b/core/embed/io/nfc/st25r3916b/ndef.c
index ee10eb45b4..83004ab7df 100644
--- a/core/embed/io/nfc/st25r3916b/ndef.c
+++ b/core/embed/io/nfc/st25r3916b/ndef.c
@@ -1,92 +1,202 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
-
-#include
-#include
#include "ndef.h"
+#include
-// ndef_status_t parse_ndef_message(uint8_t *buffer, uint16_t buffer_len ,ndef_message_t *message){
+#define NDEF_MESSAGE_URI_OVERHEAD 7
-// memset(message, 0, sizeof(ndef_message_t));
+ndef_status_t ndef_parse_message(const uint8_t *buffer, size_t buffer_size,
+ ndef_message_t *message) {
+ memset(message, 0, sizeof(ndef_message_t));
-// uint16_t offset = 0;
-// uint16_t remainin_len = buffer_len
+ size_t remaining_len = buffer_size;
-// while(1){
+ if (remaining_len <= 0) {
+ return NDEF_ERROR;
+ }
-// ndef_record_t record;
-// parse_ndef_record(buffer + offset, remainin_len , &record);
-// offset += record.payload_length;
+ // Indicate TLV header
+ if (*buffer == 0x3) {
+ buffer++;
+ remaining_len--;
+ } else {
+ return NDEF_ERROR; // Not a valid TLV structure
+ }
-// message->records_cnt++;
-// if(message->records_cnt >= NDEF_MAX_RECORDS){
-// break; // Max records reached
-// }
-// }
+ if (remaining_len <= 0) {
+ return NDEF_ERROR;
+ }
-// return NDEF_OK;
-// }
+ // TLV length
+ if (*buffer == 0xFF) {
+ // TLV 3 byte length format
+ buffer++;
+ remaining_len--;
-ndef_status_t parse_ndef_record(uint8_t *buffer, uint16_t len, ndef_record_t *rec){
+ if (remaining_len < 2) {
+ return NDEF_ERROR;
+ }
+ message->message_total_len = (int16_t)(buffer[0] << 8 | buffer[1]);
+ buffer = buffer + 2;
+ remaining_len = remaining_len - 2;
+ } else {
+ message->message_total_len = *buffer;
+ buffer++;
+ remaining_len--;
+ }
+
+ if (message->message_total_len > remaining_len) {
+ return NDEF_ERROR; // Not enough room to cover while message
+ }
+ remaining_len = message->message_total_len;
+
+ ndef_status_t status;
+ while (1) {
+ status = ndef_parse_record(buffer, remaining_len,
+ &message->records[message->records_cnt]);
+
+ if (status != NDEF_OK) {
+ return status;
+ }
+
+ buffer += message->records[message->records_cnt].record_total_len;
+ remaining_len -= message->records[message->records_cnt].record_total_len;
+ message->records_cnt++;
+
+ if (message->records_cnt >= NDEF_MAX_RECORDS) {
+ break; // Max records reached
+ }
+
+ if (remaining_len == 0) {
+ break;
+ }
+ }
+
+ // Check TLV termination character
+ if (*buffer != 0xFE) {
+ return NDEF_ERROR; // Not a valid TLV structure
+ }
+
+ return NDEF_OK;
+}
+
+ndef_status_t ndef_parse_record(const uint8_t *buffer, size_t buffer_size,
+ ndef_record_t *rec) {
uint8_t bp = 0;
- // Check if there is enough items to cover first part of the header revelaing record length
- if(len < 3){
- return NDEF_ERROR; // Not enough data to parse
+ // Check if there is enough items to cover first part of the header revelaing
+ // record length
+ if (buffer_size < 3) {
+ return NDEF_ERROR; // Not enough data to parse
}
// Look at first byte, parse header
- memcpy(&(rec->header), buffer, 1);
- bp++;
+ rec->header.byte = buffer[bp++];
- if(rec->header.tnf == 0x00 || rec->header.tnf > 0x06){
- return NDEF_ERROR; // Empty or non-existing record
+ if (rec->header.tnf == 0x00 || rec->header.tnf > 0x06) {
+ return NDEF_ERROR; // Empty or non-existing record
}
rec->type_length = buffer[bp++];
- if(rec->header.sr){
+ if (rec->header.sr) {
rec->payload_length = buffer[bp++];
- }else{
- memcpy(&(rec->payload_length), buffer + bp, 4);
+ } else {
+ rec->payload_length = (uint32_t)buffer[bp];
bp += 4;
}
- if(rec->header.il){
+ if (rec->header.il) {
rec->id_length = buffer[bp++];
- }else{
+ } else {
// ID length ommited
rec->id_length = 0;
}
- if(rec->type_length > 0){
- memcpy(&(rec->type), buffer + bp, rec->type_length);
+ if (rec->type_length > 0) {
+ rec->type = buffer[bp];
bp += rec->type_length;
- }else{
+ } else {
// Type length ommited
rec->type = 0;
}
- if(rec->id_length > 0){
- memcpy(&(rec->id), buffer + bp, rec->id_length);
- bp += rec->id_length;
- }else{
- // ID length ommited
+ if (rec->id_length == 0) {
+ // ID ommited
rec->id = 0;
+ } else {
+ rec->id = buffer[bp++];
}
- if(rec->payload_length > 0){
+ if (rec->payload_length > 0) {
+ if (rec->payload_length > NDEF_MAX_RECORD_PAYLOAD_BYTES) {
+ return NDEF_ERROR; // Payload too long
+ }
+
memcpy(rec->payload, buffer + bp, rec->payload_length);
bp += rec->payload_length;
- }else{
+ } else {
// Payload length ommited;
}
rec->record_total_len = bp;
return NDEF_OK;
-
}
+size_t ndef_create_uri(const char *uri, uint8_t *buffer, size_t buffer_size) {
+ size_t uri_len = strlen(uri);
+ if (buffer_size < (uri_len + NDEF_MESSAGE_URI_OVERHEAD)) {
+ return 0; // Not enough room to create URI
+ }
+ *buffer = 0x3; // TLV header
+ buffer++;
+
+ // NDEF message length
+ *buffer = uri_len + 5; // uri + record header;
+ buffer++;
+
+ *buffer = 0xD1; // NDEF record Header
+ buffer++;
+
+ *buffer = 0x1; // Type length
+ buffer++;
+
+ *buffer = uri_len; // Payload length
+ buffer++;
+
+ *buffer = 0x55; // URI type
+ buffer++;
+
+ *buffer = 0x1; // URI abreviation
+ buffer++;
+
+ for (uint8_t i = 0; i < uri_len; i++) {
+ *buffer = uri[i];
+ buffer++;
+ }
+
+ *buffer = 0xFE; // TLV termination
+
+ return uri_len + NDEF_MESSAGE_URI_OVERHEAD; // return buffer len
+}
diff --git a/core/embed/io/nfc/st25r3916b/ndef.h b/core/embed/io/nfc/st25r3916b/ndef.h
index c5e78438a5..91be63ee2f 100644
--- a/core/embed/io/nfc/st25r3916b/ndef.h
+++ b/core/embed/io/nfc/st25r3916b/ndef.h
@@ -1,23 +1,49 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include
#define NDEF_MAX_RECORDS 3
#define NDEF_MAX_RECORD_PAYLOAD_BYTES 50
-typedef enum{
- NDEF_OK = 0,
- NDEF_ERROR = 1,
+typedef enum {
+ NDEF_OK = 0,
+ NDEF_ERROR = 1,
} ndef_status_t;
-typedef struct{
- uint8_t tnf : 3;
- uint8_t il : 1;
- uint8_t sr : 1;
- uint8_t cf : 1;
- uint8_t me : 1;
- uint8_t mb : 1;
-}ndef_record_header_t;
+typedef struct {
+ union {
+ struct {
+ uint8_t mb : 1;
+ uint8_t me : 1;
+ uint8_t cf : 1;
+ uint8_t sr : 1;
+ uint8_t il : 1;
+ uint8_t tnf : 3;
+ };
+ uint8_t byte;
+ };
+} ndef_record_header_t;
-typedef struct{
+typedef struct {
ndef_record_header_t header;
uint8_t type_length;
uint32_t payload_length;
@@ -26,13 +52,24 @@ typedef struct{
uint8_t id;
uint8_t payload[NDEF_MAX_RECORD_PAYLOAD_BYTES];
uint16_t record_total_len;
-}ndef_record_t;
+} ndef_record_t;
-typedef struct{
- uint8_t records_cnt;
- ndef_record_t records[NDEF_MAX_RECORDS];
+typedef struct {
+ uint32_t message_total_len;
+ uint8_t records_cnt;
+ ndef_record_t records[NDEF_MAX_RECORDS];
} ndef_message_t;
-// ndef_status_t parse_ndef_message(uint8_t *buffer, uint16_t buffer_len ,ndef_message_t *message);
-ndef_status_t parse_ndef_record(uint8_t *buffer, uint16_t len, ndef_record_t *rec);
+// Parse the NDEF message from the byte buffer
+// Returns NDEF_OK if the message is parsed successfully
+ndef_status_t ndef_parse_message(const uint8_t *buffer, size_t buffer_size,
+ ndef_message_t *message);
+// Parse the NDEF record from the byte buffer
+// Returns NDEF_OK if the record is parsed successfully
+ndef_status_t ndef_parse_record(const uint8_t *buffer, size_t buffer_size,
+ ndef_record_t *rec);
+
+// Write the NDEF message with single URI record into the byte buffer
+// Returns the size of the byte buffer.
+size_t ndef_create_uri(const char *uri, uint8_t *buffer, size_t buffer_size);
diff --git a/core/embed/io/nfc/st25r3916b/nfc.c b/core/embed/io/nfc/st25r3916b/nfc.c
index 5223503e71..44352779a9 100644
--- a/core/embed/io/nfc/st25r3916b/nfc.c
+++ b/core/embed/io/nfc/st25r3916b/nfc.c
@@ -1,32 +1,60 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
-#include
-#include
#include
#include
-#include "../inc/io/nfc.h"
-#include "nfc_internal.h"
-#include "rfal_platform.h"
+#include
+#include
+#include
+#include
+
+#include "card_emulation.h"
#include "ndef.h"
+#include "nfc_internal.h"
+#include "rfal_isoDep.h"
+#include "rfal_nfc.h"
+#include "rfal_nfca.h"
+#include "rfal_platform.h"
+#include "rfal_rf.h"
+#include "rfal_t2t.h"
+#include "rfal_utils.h"
-#include "../rfal/include/rfal_isoDep.h"
-#include "../rfal/include/rfal_nfc.h"
-#include "../rfal/include/rfal_nfca.h"
-#include "../rfal/include/rfal_rf.h"
-#include "../rfal/include/rfal_t2t.h"
-#include "../rfal/include/rfal_utils.h"
-#include "prodtest_common.h"
+// NFC-A SEL_RES configured for Type 4A Tag Platform
+#define LM_SEL_RES 0x20U
-#include "stm32u5xx_hal.h"
+// NFC-F SENSF_RES configured for Type 3 Tag Platform
+#define LM_NFCID2_BYTE1 0x02U
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-variable"
+// NFC-F System Code byte 1
+#define LM_SC_BYTE1 0x12U
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
+// NFC-F System Code byte 2
+#define LM_SC_BYTE2 0xFCU
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+// NFC-F PAD0
+#define LM_PAD0 0x00U
+
+typedef enum {
+ NFC_STATE_ACTIVE,
+ NFC_STATE_NOT_ACTIVE,
+} nfc_state_t;
typedef struct {
bool initialized;
@@ -35,218 +63,68 @@ typedef struct {
// NFC IRQ pin callback
void (*nfc_irq_callback)(void);
EXTI_HandleTypeDef hEXTI;
-
rfalNfcDiscoverParam disc_params;
+ bool rfal_initialized;
+ nfc_state_t last_nfc_state;
} st25r3916b_driver_t;
static st25r3916b_driver_t g_st25r3916b_driver = {
.initialized = false,
+ .rfal_initialized = false,
};
-static void parse_tag_header(uint8_t *data, uint16_t dataLen);
-
-/* Definition of possible states the demo state machine could have */
-#define DEMO_ST_CE_NOTINIT 0 /*!< Demo State: Not initialized */
-#define DEMO_ST_CE_START_DISCOVERY 1 /*!< Demo State: Start Discovery */
-#define DEMO_ST_CE_DISCOVERY 2 /*!< Demo State: Discovery */
-#define DEMO_ST_CE_TAG_OPERATION 3 /*!< Demo State: Discovery */
-
-/* Definition of possible states the demo state machine could have */
-#define DEMO_ST_NOTINIT 0 /*!< Demo State: Not initialized */
-#define DEMO_ST_START_DISCOVERY 1 /*!< Demo State: Start Discovery */
-#define DEMO_ST_DISCOVERY 2 /*!< Demo State: Discovery */
-
-#define DEMO_NFCV_BLOCK_LEN 4 /*!< NFCV Block len */
-#define DEMO_NFCV_USE_SELECT_MODE \
- false /*!< NFCV demonstrate select mode */
-
-/*
-******************************************************************************
-* GLOBAL DEFINES
-******************************************************************************
-*/
-
-
-
-/* Definition of possible states the demo state machine could have */
-#define DEMO_ST_NOTINIT 0 /*!< Demo State: Not initialized | Stopped */
-#define DEMO_ST_START_DISCOVERY 1 /*!< Demo State: Start Discovery */
-#define DEMO_ST_DISCOVERY 2 /*!< Demo State: Discovery */
-
-#define DEMO_NFCV_BLOCK_LEN 4 /*!< NFCV Block len */
-
-#define DEMO_NFCV_USE_SELECT_MODE \
- false /*!< NFCV demonstrate select mode */
-#define DEMO_NFCV_WRITE_TAG false /*!< NFCV demonstrate Write Single Block */
-
-/* Definition of various Listen Mode constants */
-#if defined(DEMO_LISTEN_MODE_TARGET)
-#define DEMO_LM_SEL_RES \
- 0x40U /*!> 4) & 0xF];
- *pout++ = hex[(*pin++) & 0xF];
- }
- *pout++ = hex[(*pin >> 4) & 0xF];
- *pout++ = hex[(*pin) & 0xF];
- *pout = 0;
- }
-
- hexStrIdx++;
- hexStrIdx %= MAX_HEX_STR;
-
- return hexStr[idx];
- }
-}
-
-typedef struct{
+typedef struct {
uint8_t UID[7];
uint8_t BCC[1];
uint8_t SYSTEM_AREA[2];
- union{
+ union {
uint8_t CC[4];
- struct{
+ struct {
uint8_t CC_MAGIC_NUMBER;
uint8_t CC_VERSION;
uint8_t CC_SIZE;
uint8_t CC_ACCESS_CONDITION;
};
};
-} type2_header_t;
+} nfc_device_header_t2t_t;
+
+// P2P communication data
+static const uint8_t nfcid3[] = {0x01, 0xFE, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0A};
+static const uint8_t gb[] = {0x46, 0x66, 0x6d, 0x01, 0x01, 0x11, 0x02,
+ 0x02, 0x07, 0x80, 0x03, 0x02, 0x00, 0x03,
+ 0x04, 0x01, 0x32, 0x07, 0x01, 0x03};
+
+// NFC-A CE config
+// 4-byte UIDs with first byte 0x08 would need random number for the subsequent
+// 3 bytes. 4-byte UIDs with first byte 0x*F are Fixed number, not unique, use
+// for this demo 7-byte UIDs need a manufacturer ID and need to assure
+// uniqueness of the rest.
+static const uint8_t ce_nfca_nfcid[] = {
+ 0x1, 0x2, 0x3, 0x4}; // =_STM, 5F 53 54 4D NFCID1 / UID (4 bytes)
+static const uint8_t ce_nfca_sens_res[] = {
+ 0x02, 0x00}; // SENS_RES / ATQA for 4-byte UID
+static const uint8_t ce_nfca_sel_res = LM_SEL_RES; // SEL_RES / SAK
+
+static const uint8_t ce_nfcf_nfcid2[] = {
+ LM_NFCID2_BYTE1, 0xFE, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+// NFC-F CE config
+static const uint8_t ce_nfcf_sc[] = {LM_SC_BYTE1, LM_SC_BYTE2};
+static uint8_t ce_nfcf_sensf_res[] = {
+ 0x01, // SENSF_RES
+ 0x02, 0xFE, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, // NFCID2
+ LM_PAD0, LM_PAD0, 0x00, 0x00,
+ 0x00, 0x7F, 0x7F, 0x00, // PAD0, PAD1, MRTIcheck, MRTIupdate, PAD2
+ 0x00, 0x00}; // RD
+
+static nfc_status_t nfc_transcieve_blocking(uint8_t *tx_buf,
+ uint16_t tx_buf_size,
+ uint8_t **rx_buf,
+ uint16_t **rcv_len, uint32_t fwt);
+
+static void nfc_card_emulator_loop(rfalNfcDevice *nfc_dev);
nfc_status_t nfc_init() {
st25r3916b_driver_t *drv = &g_st25r3916b_driver;
@@ -255,37 +133,41 @@ nfc_status_t nfc_init() {
return NFC_OK;
}
+ memset(drv, 0, sizeof(st25r3916b_driver_t));
+
// Enable clock of relevant peripherals
// SPI + GPIO ports
- SPI_INSTANCE_3_CLK_EN();
- SPI_INSTANCE_3_MISO_CLK_EN();
- SPI_INSTANCE_3_MOSI_CLK_EN();
- SPI_INSTANCE_3_SCK_CLK_EN();
- SPI_INSTANCE_3_NSS_CLK_EN();
+ NFC_SPI_FORCE_RESET();
+ NFC_SPI_RELEASE_RESET();
+ NFC_SPI_CLK_EN();
+ NFC_SPI_MISO_CLK_EN();
+ NFC_SPI_MOSI_CLK_EN();
+ NFC_SPI_SCK_CLK_EN();
+ NFC_SPI_NSS_CLK_EN();
// SPI peripheral pin config
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStruct.Alternate = SPI_INSTANCE_3_PIN_AF;
+ GPIO_InitStruct.Alternate = NFC_SPI_PIN_AF;
- GPIO_InitStruct.Pin = SPI_INSTANCE_3_MISO_PIN;
- HAL_GPIO_Init(SPI_INSTANCE_3_MISO_PORT, &GPIO_InitStruct);
+ GPIO_InitStruct.Pin = NFC_SPI_MISO_PIN;
+ HAL_GPIO_Init(NFC_SPI_MISO_PORT, &GPIO_InitStruct);
- GPIO_InitStruct.Pin = SPI_INSTANCE_3_MOSI_PIN;
- HAL_GPIO_Init(SPI_INSTANCE_3_MOSI_PORT, &GPIO_InitStruct);
+ GPIO_InitStruct.Pin = NFC_SPI_MOSI_PIN;
+ HAL_GPIO_Init(NFC_SPI_MOSI_PORT, &GPIO_InitStruct);
- GPIO_InitStruct.Pin = SPI_INSTANCE_3_SCK_PIN;
- HAL_GPIO_Init(SPI_INSTANCE_3_SCK_PORT, &GPIO_InitStruct);
+ GPIO_InitStruct.Pin = NFC_SPI_SCK_PIN;
+ HAL_GPIO_Init(NFC_SPI_SCK_PORT, &GPIO_InitStruct);
- // NSS pin controled by software, set as classical GPIO
+ // NSS pin controlled by software, set as classical GPIO
GPIO_InitTypeDef GPIO_InitStruct_nss = {0};
GPIO_InitStruct_nss.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct_nss.Pull = GPIO_NOPULL;
GPIO_InitStruct_nss.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStruct_nss.Pin = SPI_INSTANCE_3_NSS_PIN;
- HAL_GPIO_Init(SPI_INSTANCE_3_NSS_PORT, &GPIO_InitStruct_nss);
+ GPIO_InitStruct_nss.Pin = NFC_SPI_NSS_PIN;
+ HAL_GPIO_Init(NFC_SPI_NSS_PORT, &GPIO_InitStruct_nss);
// NFC IRQ pin
GPIO_InitTypeDef GPIO_InitStructure_int = {0};
@@ -295,9 +177,9 @@ nfc_status_t nfc_init() {
GPIO_InitStructure_int.Pin = NFC_INT_PIN;
HAL_GPIO_Init(NFC_INT_PORT, &GPIO_InitStructure_int);
- memset(&(drv->hspi), 0, sizeof(drv->hspi));
+ memset(&drv->hspi, 0, sizeof(drv->hspi));
- drv->hspi.Instance = SPI_INSTANCE_3;
+ drv->hspi.Instance = NFC_SPI_INSTANCE;
drv->hspi.Init.Mode = SPI_MODE_MASTER;
drv->hspi.Init.BaudRatePrescaler =
SPI_BAUDRATEPRESCALER_32; // TODO: Calculate frequency precisly.
@@ -309,22 +191,11 @@ nfc_status_t nfc_init() {
drv->hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
drv->hspi.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
- EXTI_ConfigTypeDef EXTI_Config = {0};
- EXTI_Config.GPIOSel = NFC_EXTI_INTERRUPT_GPIOSEL;
- EXTI_Config.Line = NFC_EXTI_INTERRUPT_LINE;
- EXTI_Config.Mode = EXTI_MODE_INTERRUPT;
- EXTI_Config.Trigger = EXTI_TRIGGER_RISING;
- HAL_EXTI_SetConfigLine(&drv->hEXTI, &EXTI_Config);
- NVIC_SetPriority(NFC_EXTI_INTERRUPT_NUM, IRQ_PRI_NORMAL);
- __HAL_GPIO_EXTI_CLEAR_FLAG(NFC_INT_PIN);
- NVIC_EnableIRQ(NFC_EXTI_INTERRUPT_NUM);
-
HAL_StatusTypeDef status;
-
- status = HAL_SPI_Init(&(drv->hspi));
+ status = HAL_SPI_Init(&drv->hspi);
if (status != HAL_OK) {
- return false;
+ goto cleanup;
}
ReturnCode ret;
@@ -334,124 +205,146 @@ nfc_status_t nfc_init() {
rfalNfcDefaultDiscParams(&drv->disc_params);
if (ret != RFAL_ERR_NONE) {
- return NFC_ERROR;
+ goto cleanup;
}
+ drv->rfal_initialized = true;
+
+ // Initialize EXTI for NFC IRQ pin
+ EXTI_ConfigTypeDef EXTI_Config = {0};
+ EXTI_Config.GPIOSel = NFC_EXTI_INTERRUPT_GPIOSEL;
+ EXTI_Config.Line = NFC_EXTI_INTERRUPT_LINE;
+ EXTI_Config.Mode = EXTI_MODE_INTERRUPT;
+ EXTI_Config.Trigger = EXTI_TRIGGER_RISING;
+ status = HAL_EXTI_SetConfigLine(&drv->hEXTI, &EXTI_Config);
+
+ if (status != HAL_OK) {
+ goto cleanup;
+ }
+
+ NVIC_SetPriority(NFC_EXTI_INTERRUPT_NUM, IRQ_PRI_NORMAL);
+ __HAL_GPIO_EXTI_CLEAR_FLAG(NFC_INT_PIN);
+ NVIC_ClearPendingIRQ(NFC_EXTI_INTERRUPT_NUM);
+ NVIC_EnableIRQ(NFC_EXTI_INTERRUPT_NUM);
+
drv->initialized = true;
+ drv->last_nfc_state = NFC_STATE_NOT_ACTIVE;
return NFC_OK;
+
+cleanup:
+ nfc_deinit();
+ return NFC_ERROR;
}
-nfc_status_t nfc_deinit() {
+void nfc_deinit(void) {
st25r3916b_driver_t *drv = &g_st25r3916b_driver;
- if (!drv->initialized) {
- return NFC_OK;
- }
-
- // Deactivate rfal STM.
- rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
- while (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
- rfalNfcWorker();
+ if (drv->rfal_initialized) {
+ // Deactivate rfal STM (Disconnects active devices)
+ rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
+ while (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
+ rfalNfcWorker();
+ }
}
HAL_EXTI_ClearConfigLine(&drv->hEXTI);
NVIC_DisableIRQ(NFC_EXTI_INTERRUPT_NUM);
- ReturnCode ret_code = rfalDeinitialize();
+ if (drv->rfal_initialized) {
+ rfalDeinitialize();
+ drv->rfal_initialized = false;
+ }
- if (ret_code != RFAL_ERR_NONE) {
+ HAL_SPI_DeInit(&drv->hspi);
+
+ HAL_GPIO_DeInit(NFC_SPI_MISO_PORT, NFC_SPI_MISO_PIN);
+ HAL_GPIO_DeInit(NFC_SPI_MOSI_PORT, NFC_SPI_MOSI_PIN);
+ HAL_GPIO_DeInit(NFC_SPI_SCK_PORT, NFC_SPI_SCK_PIN);
+ HAL_GPIO_DeInit(NFC_SPI_NSS_PORT, NFC_SPI_NSS_PIN);
+ HAL_GPIO_DeInit(NFC_INT_PORT, NFC_INT_PIN);
+
+ memset(&drv, 0, sizeof(st25r3916b_driver_t));
+}
+
+nfc_status_t nfc_register_tech(const nfc_tech_t tech) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
+
+ if (drv->initialized == false) {
+ return NFC_NOT_INITIALIZED;
+ }
+
+ drv->disc_params.devLimit = 1;
+ memcpy(&drv->disc_params.nfcid3, nfcid3, sizeof(nfcid3));
+ memcpy(&drv->disc_params.GB, gb, sizeof(gb));
+ drv->disc_params.GBLen = sizeof(gb);
+ drv->disc_params.p2pNfcaPrio = true;
+ drv->disc_params.totalDuration = 1000U;
+
+ if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
return NFC_ERROR;
}
- HAL_SPI_DeInit(&(drv->hspi));
-
- HAL_GPIO_DeInit(SPI_INSTANCE_3_MISO_PORT, SPI_INSTANCE_3_MISO_PIN);
- HAL_GPIO_DeInit(SPI_INSTANCE_3_MOSI_PORT, SPI_INSTANCE_3_MOSI_PIN);
- HAL_GPIO_DeInit(SPI_INSTANCE_3_SCK_PORT, SPI_INSTANCE_3_SCK_PIN);
- HAL_GPIO_DeInit(SPI_INSTANCE_3_NSS_PORT, SPI_INSTANCE_3_NSS_PIN);
- HAL_GPIO_DeInit(NFC_INT_PORT, NFC_INT_PIN);
-
- drv->initialized = false;
-
- return NFC_OK;
-}
-
-nfc_status_t nfc_register_card_emu(nfc_card_emul_tech_t tech) {
- st25r3916b_driver_t *drv = &g_st25r3916b_driver;
-
- // In case the NFC state machine is active, deactivate to idle before
- // registering a new card emulation technology.
- if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
- rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
- do {
- rfalNfcWorker();
- } while (rfalNfcGetState() != RFAL_NFC_STATE_IDLE);
+ // Set general discovery parameters.
+ if (tech & NFC_POLLER_TECH_A) {
+ drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_A;
}
- drv->disc_params.devLimit = 1;
- memcpy(&drv->disc_params.nfcid3, NFCID3, sizeof(NFCID3));
- memcpy(&drv->disc_params.GB, GB, sizeof(GB));
- drv->disc_params.GBLen = sizeof(GB);
- drv->disc_params.p2pNfcaPrio = true;
- drv->disc_params.totalDuration = 1000U;
+ if (tech & NFC_POLLER_TECH_B) {
+ drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_B;
+ }
- switch (tech) {
- case NFC_CARD_EMU_TECH_A:
- memcpy(drv->disc_params.lmConfigPA.SENS_RES, ceNFCA_SENS_RES,
- RFAL_LM_SENS_RES_LEN); /* Set SENS_RES / ATQA */
- memcpy(drv->disc_params.lmConfigPA.nfcid, ceNFCA_NFCID,
- RFAL_LM_NFCID_LEN_04); /* Set NFCID / UID */
- drv->disc_params.lmConfigPA.nfcidLen =
- RFAL_LM_NFCID_LEN_04; /* Set NFCID length to 4 bytes */
- drv->disc_params.lmConfigPA.SEL_RES =
- ceNFCA_SEL_RES; /* Set SEL_RES / SAK */
- drv->disc_params.techs2Find |= RFAL_NFC_LISTEN_TECH_A;
- break;
- case NFC_CARD_EMU_TECH_V:
- drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_V;
- break;
- default:
- return NFC_ERROR;
+ if (tech & NFC_POLLER_TECH_F) {
+ drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_F;
+ }
+
+ if (tech & NFC_POLLER_TECH_V) {
+ drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_V;
+ }
+
+ if (tech & NFC_CARD_EMU_TECH_A) {
+ card_emulation_init(ce_nfcf_nfcid2);
+
+ // Set SENS_RES / ATQA
+ memcpy(drv->disc_params.lmConfigPA.SENS_RES, ce_nfca_sens_res,
+ RFAL_LM_SENS_RES_LEN);
+
+ // Set NFCID / UID
+ memcpy(drv->disc_params.lmConfigPA.nfcid, ce_nfca_nfcid,
+ RFAL_LM_NFCID_LEN_04);
+
+ // Set NFCID length to 4 bytes
+ drv->disc_params.lmConfigPA.nfcidLen = RFAL_LM_NFCID_LEN_04;
+
+ // Set SEL_RES / SAK
+ drv->disc_params.lmConfigPA.SEL_RES = ce_nfca_sel_res;
+ drv->disc_params.techs2Find |= RFAL_NFC_LISTEN_TECH_A;
+ }
+
+ if (tech & NFC_CARD_EMU_TECH_F) {
+ // Set configuration for NFC-F CE
+ memcpy(drv->disc_params.lmConfigPF.SC, ce_nfcf_sc,
+ RFAL_LM_SENSF_SC_LEN); // Set System Code
+
+ // Load NFCID2 on SENSF_RES
+ memcpy(&ce_nfcf_sensf_res[RFAL_NFCF_CMD_LEN], ce_nfcf_nfcid2,
+ RFAL_NFCID2_LEN);
+
+ // Set SENSF_RES / Poll Response
+ memcpy(drv->disc_params.lmConfigPF.SENSF_RES, ce_nfcf_sensf_res,
+ RFAL_LM_SENSF_RES_LEN);
+
+ drv->disc_params.techs2Find |= RFAL_NFC_LISTEN_TECH_F;
}
return NFC_OK;
}
-nfc_status_t nfc_register_poller(nfc_poller_tech_t tech) {
+nfc_status_t nfc_activate_stm(void) {
st25r3916b_driver_t *drv = &g_st25r3916b_driver;
- // In case the NFC state machine is active, deactivate to idle before
- // registering a new card emulation technology.
- if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
- rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
- do {
- rfalNfcWorker();
- } while (rfalNfcGetState() != RFAL_NFC_STATE_IDLE);
- }
-
- drv->disc_params.devLimit = 1;
- memcpy(&drv->disc_params.nfcid3, NFCID3, sizeof(NFCID3));
- memcpy(&drv->disc_params.GB, GB, sizeof(GB));
- drv->disc_params.GBLen = sizeof(GB);
- drv->disc_params.p2pNfcaPrio = true;
- drv->disc_params.totalDuration = 1000U;
-
- switch (tech) {
- case NFC_POLLER_TECH_A:
- drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_A;
- break;
- case NFC_POLLER_TECH_B:
- drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_B;
- break;
- case NFC_POLLER_TECH_F:
- drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_F;
- break;
- case NFC_POLLER_TECH_V:
- drv->disc_params.techs2Find |= RFAL_NFC_POLL_TECH_V;
- break;
- default:
- return NFC_ERROR;
+ if (!drv->initialized) {
+ return NFC_NOT_INITIALIZED;
}
ReturnCode err;
@@ -463,219 +356,248 @@ nfc_status_t nfc_register_poller(nfc_poller_tech_t tech) {
return NFC_OK;
}
-nfc_status_t nfc_run_worker() {
- static rfalNfcDevice *nfcDevice;
+nfc_status_t nfc_deactivate_stm(void) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
- rfalNfcWorker(); /* Run RFAL worker periodically */
-
- if (rfalNfcIsDevActivated(rfalNfcGetState())) {
- rfalNfcGetActiveDevice(&nfcDevice);
-
- switch (nfcDevice->type) {
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_NFCA:
-
- switch (nfcDevice->dev.nfca.type) {
- case RFAL_NFCA_T1T:
- vcp_println("ISO14443A/Topaz (NFC-A T1T) TAG found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- break;
-
- case RFAL_NFCA_T4T:
- vcp_println("NFCA Passive ISO-DEP device found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- break;
-
- case RFAL_NFCA_T4T_NFCDEP:
- case RFAL_NFCA_NFCDEP:
- break;
- default:
- vcp_println("ISO14443A/NFC-A card found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- ReturnCode err;
- uint16_t rcvLen;
- uint8_t blockNum = 0;
- uint8_t rxBuf[16];
-
- vcp_println("Tag ID len: %d", nfcDevice->nfcidLen);
-
- // Read first 16 bytes (1st block)
- err = rfalT2TPollerRead(0, rxBuf, sizeof(rxBuf), &rcvLen);
- parse_tag_header(rxBuf, sizeof(rxBuf));
-
- uint8_t memory_area_data[160] = {0};
- for (uint8_t i = 0; i < 10; i++) {
- err = rfalT2TPollerRead(4+i*4, memory_area_data+i*16, 16 , &rcvLen);
- }
-
- ndef_record_t record;
- parse_ndef_record(memory_area_data+2, 160, &record);
- vcp_println("Record payload: %s", record.payload);
-
-
-
-
-
-
- break;
- }
-
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_NFCB:
-
- vcp_println("NFC TYPE B card found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- break;
-
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_NFCF:
-
- vcp_println("NFC TYPE F card found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- break;
-
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_NFCV:
-
- vcp_println("NFC TYPE V card found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
-
- break;
-
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_ST25TB:
-
- vcp_println("ST25TB card found. UID: %s\r\n",
- hex2Str(nfcDevice->nfcid, nfcDevice->nfcidLen));
- break;
-
- /*******************************************************************************/
- case RFAL_NFC_LISTEN_TYPE_AP2P:
- case RFAL_NFC_POLL_TYPE_AP2P:
- break;
-
- /*******************************************************************************/
- case RFAL_NFC_POLL_TYPE_NFCA:
- break;
- case RFAL_NFC_POLL_TYPE_NFCF:
- break;
- /*******************************************************************************/
- default:
- break;
- }
-
- rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_DISCOVERY);
+ if (!drv->initialized) {
+ return NFC_OK;
+ }
+ // In case the NFC state machine is active, deactivate to idle before
+ // registering a new card emulation technology.
+ if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
+ rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
+ do {
+ rfalNfcWorker();
+ } while (rfalNfcGetState() != RFAL_NFC_STATE_IDLE);
}
return NFC_OK;
}
+nfc_status_t nfc_get_event(nfc_event_t *event) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
+ *event = NFC_NO_EVENT;
-static void parse_tag_header(uint8_t *data, uint16_t dataLen){
-
- type2_header_t hdr;
-
- memcpy(hdr.UID, data, 3);
- hdr.BCC[0] = data[3];
- memcpy(hdr.UID + 3, data+4, 4);
- memcpy(hdr.SYSTEM_AREA, data+8, 2);
- memcpy(hdr.CC, data+12, 4);
-
- vcp_println("UID: %s", hex2Str(hdr.UID, sizeof(hdr.UID)));
- vcp_println("BCC: %s (%s)", hex2Str(hdr.BCC, sizeof(hdr.BCC)), (0x88 == (hdr.UID[0] ^ hdr.UID[1] ^ hdr.UID[2] ^ hdr.BCC[0]))? " CHECKSUM PASSED" : "CHECKSUM FAILED");
- vcp_println("SYSTEM_AREA: %s", hex2Str(hdr.SYSTEM_AREA, sizeof(hdr.SYSTEM_AREA)));
- vcp_println("CC: %s", hex2Str(hdr.CC, sizeof(hdr.CC)));
- vcp_println(" -> CC_MAGIC_NUMBER: %02X", hdr.CC_MAGIC_NUMBER);
- vcp_println(" -> CC_VERSION: %02X", hdr.CC_VERSION);
- vcp_println(" -> CC_SIZE: %02X (%d bytes)", hdr.CC_SIZE, hdr.CC_SIZE*8);
- vcp_println(" -> CC_ACCESS_CONDITION: %02X", hdr.CC_ACCESS_CONDITION);
-
-}
-
-
-/*!
- *****************************************************************************
- * \brief Demo Blocking Transceive
- *
- * Helper function to send data in a blocking manner via the rfalNfc module
- *
- * \warning A protocol transceive handles long timeouts (several seconds),
- * transmission errors and retransmissions which may lead to a long period of
- * time where the MCU/CPU is blocked in this method.
- * This is a demo implementation, for a non-blocking usage example please
- * refer to the Examples available with RFAL
- *
- * \param[in] txBuf : data to be transmitted
- * \param[in] txBufSize : size of the data to be transmited
- * \param[out] rxData : location where the received data has been placed
- * \param[out] rcvLen : number of data bytes received
- * \param[in] fwt : FWT to be used (only for RF frame interface,
- * otherwise use RFAL_FWT_NONE)
- *
- * \return RFAL_ERR_PARAM : Invalid parameters
- * \return RFAL_ERR_TIMEOUT : Timeout error
- * \return RFAL_ERR_FRAMING : Framing error detected
- * \return RFAL_ERR_PROTO : Protocol error detected
- * \return RFAL_ERR_NONE : No error, activation successful
- *
- *****************************************************************************
- */
-ReturnCode demoTransceiveBlocking(uint8_t *txBuf, uint16_t txBufSize,
- uint8_t **rxData, uint16_t **rcvLen,
- uint32_t fwt) {
- ReturnCode err;
-
- err = rfalNfcDataExchangeStart(txBuf, txBufSize, rxData, rcvLen, fwt);
- if (err == RFAL_ERR_NONE) {
- do {
- rfalNfcWorker();
- err = rfalNfcDataExchangeGetStatus();
- } while (err == RFAL_ERR_BUSY);
+ if (!drv->initialized) {
+ return NFC_NOT_INITIALIZED;
}
- return err;
+
+ rfalNfcDevice *nfc_device;
+
+ // Run RFAL worker periodically
+ rfalNfcWorker();
+
+ rfalNfcState rfal_state = rfalNfcGetState();
+
+ nfc_state_t cur_nfc_state = NFC_STATE_NOT_ACTIVE;
+
+ if (rfalNfcIsDevActivated(rfal_state)) {
+ cur_nfc_state = NFC_STATE_ACTIVE;
+ }
+
+ if (cur_nfc_state != drv->last_nfc_state) {
+ switch (cur_nfc_state) {
+ case NFC_STATE_ACTIVE:
+ *event = NFC_EVENT_ACTIVATED;
+ break;
+
+ case NFC_STATE_NOT_ACTIVE:
+ *event = NFC_EVENT_DEACTIVATED;
+ break;
+
+ default:
+ *event = NFC_NO_EVENT;
+ }
+
+ drv->last_nfc_state = cur_nfc_state;
+ }
+
+ if (cur_nfc_state == NFC_STATE_ACTIVE) {
+ rfalNfcGetActiveDevice(&nfc_device);
+
+ // Perform immediate mandatory actions for certain technology (Placeholder)
+ switch (nfc_device->type) {
+ case RFAL_NFC_LISTEN_TYPE_NFCA:
+
+ switch (nfc_device->dev.nfca.type) {
+ case RFAL_NFCA_T1T:
+ break;
+ case RFAL_NFCA_T4T:
+ break;
+ case RFAL_NFCA_T4T_NFCDEP:
+ break;
+ case RFAL_NFCA_NFCDEP:
+ break;
+ default:
+ break;
+ }
+
+ case RFAL_NFC_LISTEN_TYPE_NFCB:
+ break;
+
+ case RFAL_NFC_LISTEN_TYPE_NFCF:
+ break;
+
+ case RFAL_NFC_LISTEN_TYPE_NFCV:
+ break;
+
+ case RFAL_NFC_LISTEN_TYPE_ST25TB:
+ break;
+
+ case RFAL_NFC_LISTEN_TYPE_AP2P:
+ case RFAL_NFC_POLL_TYPE_AP2P:
+ break;
+
+ // Card emulators must respond to reader commands promptly. Once
+ // activated, the RFAL worker is called multiple times until back-to-back
+ // communication with the reader finishes. This can prolong the
+ // nfc_get_event() service time compared to standard reader mode.
+ case RFAL_NFC_POLL_TYPE_NFCA:
+ case RFAL_NFC_POLL_TYPE_NFCF:
+
+ if (nfc_device->rfInterface == RFAL_NFC_INTERFACE_NFCDEP) {
+ // not supported yet
+ } else {
+ nfc_card_emulator_loop(nfc_device);
+ rfalNfcDeactivate(
+ RFAL_NFC_DEACTIVATE_DISCOVERY); // Automatically deactivate
+ }
+
+ // No event in CE mode, activation/deactivation handled automatically
+ *event = NFC_NO_EVENT;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return NFC_OK;
}
-HAL_StatusTypeDef nfc_spi_transmit_receive(const uint8_t *txData,
- uint8_t *rxData, uint16_t length) {
+nfc_status_t nfc_dev_deactivate(void) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
+
+ if (!drv->initialized) {
+ return NFC_NOT_INITIALIZED;
+ }
+
+ rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_DISCOVERY);
+
+ return NFC_OK;
+}
+
+nfc_status_t nfc_transceive(const uint8_t *tx_data, uint16_t tx_data_len,
+ uint8_t *rx_data, uint16_t *rx_data_len) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
+
+ if (drv->initialized == false) {
+ return NFC_NOT_INITIALIZED;
+ }
+
+ if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE) {
+ return NFC_ERROR;
+ }
+
+ ReturnCode err;
+ err = nfc_transcieve_blocking((uint8_t *)tx_data, tx_data_len, &rx_data,
+ &rx_data_len, RFAL_FWT_NONE);
+
+ if (err != RFAL_ERR_NONE) {
+ return NFC_ERROR;
+ }
+
+ return NFC_OK;
+}
+
+nfc_status_t nfc_dev_write_ndef_uri(void) {
+ st25r3916b_driver_t *drv = &g_st25r3916b_driver;
+
+ if (!drv->initialized) {
+ return NFC_NOT_INITIALIZED;
+ }
+
+ // NDEF message
+ uint8_t ndef_message[128] = {0};
+
+ uint16_t buffer_len =
+ ndef_create_uri("trezor.io/", ndef_message, sizeof(ndef_message));
+
+ for (uint8_t i = 0; i < buffer_len / 4; i++) {
+ rfalT2TPollerWrite(4 + i, ndef_message + i * 4);
+ }
+
+ return NFC_OK;
+}
+
+nfc_status_t nfc_dev_read_info(nfc_dev_info_t *dev_info) {
+ if (rfalNfcIsDevActivated(rfalNfcGetState())) {
+ rfalNfcDevice *nfc_device;
+ rfalNfcGetActiveDevice(&nfc_device);
+
+ // Resolve device type
+ switch (nfc_device->type) {
+ case RFAL_NFC_LISTEN_TYPE_NFCA:
+ dev_info->type = NFC_DEV_TYPE_A;
+ break;
+ case RFAL_NFC_LISTEN_TYPE_NFCB:
+ dev_info->type = NFC_DEV_TYPE_B;
+ break;
+ case RFAL_NFC_LISTEN_TYPE_NFCF:
+ dev_info->type = NFC_DEV_TYPE_F;
+ break;
+ case RFAL_NFC_LISTEN_TYPE_NFCV:
+ dev_info->type = NFC_DEV_TYPE_V;
+ break;
+ case RFAL_NFC_LISTEN_TYPE_ST25TB:
+ dev_info->type = NFC_DEV_TYPE_ST25TB;
+ break;
+ case RFAL_NFC_LISTEN_TYPE_AP2P:
+ dev_info->type = NFC_DEV_TYPE_AP2P;
+ break;
+ default:
+ dev_info->type = NFC_DEV_TYPE_UNKNOWN;
+ break;
+ }
+
+ dev_info->uid_len = nfc_device->nfcidLen;
+
+ if (nfc_device->nfcidLen > NFC_MAX_UID_LEN) {
+ return NFC_ERROR;
+ }
+
+ // Copy the hex UID in printable string
+ cstr_encode_hex(dev_info->uid, NFC_MAX_UID_BUF_SIZE, nfc_device->nfcid,
+ nfc_device->nfcidLen);
+
+ } else {
+ // No device activated
+ return NFC_ERROR;
+ }
+
+ return NFC_OK;
+}
+
+HAL_StatusTypeDef nfc_spi_transmit_receive(const uint8_t *tx_data,
+ uint8_t *rx_data, uint16_t length) {
st25r3916b_driver_t *drv = &g_st25r3916b_driver;
HAL_StatusTypeDef status;
- if ((txData != NULL) && (rxData == NULL)) {
- status = HAL_SPI_Transmit(&(drv->hspi), (uint8_t *)txData, length, 1000);
- } else if ((txData == NULL) && (rxData != NULL)) {
- status = HAL_SPI_Receive(&(drv->hspi), rxData, length, 1000);
+ if ((tx_data != NULL) && (rx_data == NULL)) {
+ status = HAL_SPI_Transmit(&drv->hspi, (uint8_t *)tx_data, length, 1000);
+ } else if ((tx_data == NULL) && (rx_data != NULL)) {
+ status = HAL_SPI_Receive(&drv->hspi, rx_data, length, 1000);
} else {
- status = HAL_SPI_TransmitReceive(&(drv->hspi), (uint8_t *)txData, rxData,
+ status = HAL_SPI_TransmitReceive(&drv->hspi, (uint8_t *)tx_data, rx_data,
length, 1000);
}
return status;
}
-uint32_t nfc_create_timer(uint16_t time) { return (systick_ms() + time); }
-
-bool nfc_timer_is_expired(uint32_t timer) {
- uint32_t u_diff;
- int32_t s_diff;
-
- u_diff = (timer - systick_ms()); // Calculate the diff between the timers
- s_diff = u_diff; // Convert the diff to a signed var
-
- // Check if the given timer has expired already
- if (s_diff < 0) {
- return true;
- }
-
- return false;
-}
-
void nfc_ext_irq_set_callback(void (*cb)(void)) {
st25r3916b_driver_t *drv = &g_st25r3916b_driver;
drv->nfc_irq_callback = cb;
@@ -690,3 +612,62 @@ void NFC_EXTI_INTERRUPT_HANDLER(void) {
drv->nfc_irq_callback();
}
}
+
+static void nfc_card_emulator_loop(rfalNfcDevice *nfc_dev) {
+ ReturnCode err = RFAL_ERR_INTERNAL;
+ uint8_t *rx_buf;
+ uint16_t *rcv_len;
+ uint8_t tx_buf[150];
+ uint16_t tx_len;
+
+ do {
+ rfalNfcWorker();
+
+ switch (rfalNfcGetState()) {
+ case RFAL_NFC_STATE_ACTIVATED:
+ err = nfc_transcieve_blocking(NULL, 0, &rx_buf, &rcv_len, 0);
+ break;
+
+ case RFAL_NFC_STATE_DATAEXCHANGE:
+ case RFAL_NFC_STATE_DATAEXCHANGE_DONE:
+
+ tx_len =
+ ((nfc_dev->type == RFAL_NFC_POLL_TYPE_NFCA)
+ ? card_emulation_t4t(rx_buf, *rcv_len, tx_buf, sizeof(tx_buf))
+ : rfalConvBytesToBits(
+ card_emulation_t3t(rx_buf, rfalConvBitsToBytes(*rcv_len),
+ tx_buf, sizeof(rx_buf))));
+
+ err = nfc_transcieve_blocking(tx_buf, tx_len, &rx_buf, &rcv_len,
+ RFAL_FWT_NONE);
+ break;
+
+ case RFAL_NFC_STATE_START_DISCOVERY:
+ return;
+
+ case RFAL_NFC_STATE_LISTEN_SLEEP:
+ default:
+ break;
+ }
+ } while ((err == RFAL_ERR_NONE) || (err == RFAL_ERR_SLEEP_REQ));
+}
+
+static nfc_status_t nfc_transcieve_blocking(uint8_t *tx_buf,
+ uint16_t tx_buf_size,
+ uint8_t **rx_buf,
+ uint16_t **rcv_len, uint32_t fwt) {
+ ReturnCode err;
+ err = rfalNfcDataExchangeStart(tx_buf, tx_buf_size, rx_buf, rcv_len, fwt);
+ if (err == RFAL_ERR_NONE) {
+ do {
+ rfalNfcWorker();
+ err = rfalNfcDataExchangeGetStatus();
+ } while (err == RFAL_ERR_BUSY);
+ }
+
+ if (err != RFAL_ERR_NONE) {
+ return NFC_ERROR;
+ } else {
+ return NFC_OK;
+ }
+}
diff --git a/core/embed/io/nfc/st25r3916b/nfc_internal.h b/core/embed/io/nfc/st25r3916b/nfc_internal.h
index 579a786efa..ce1ed90413 100644
--- a/core/embed/io/nfc/st25r3916b/nfc_internal.h
+++ b/core/embed/io/nfc/st25r3916b/nfc_internal.h
@@ -1,16 +1,27 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
-#include
+#pragma once
-#ifndef TREZORHAL_NFC_INTERNAL_H
-#define TREZORHAL_NFC_INTERNAL_H
+#include
-HAL_StatusTypeDef nfc_spi_transmit_receive(const uint8_t *txData,
- uint8_t *rxData, uint16_t length);
-
-uint32_t nfc_create_timer(uint16_t time);
-
-bool nfc_timer_is_expired(uint32_t timer);
+HAL_StatusTypeDef nfc_spi_transmit_receive(const uint8_t *tx_data,
+ uint8_t *rx_data, uint16_t length);
void nfc_ext_irq_set_callback(void (*cb)(void));
-
-#endif
diff --git a/core/embed/io/nfc/st25r3916b/rfal_platform.h b/core/embed/io/nfc/st25r3916b/rfal_platform.h
index d410e93001..a848e773f7 100644
--- a/core/embed/io/nfc/st25r3916b/rfal_platform.h
+++ b/core/embed/io/nfc/st25r3916b/rfal_platform.h
@@ -1,296 +1,256 @@
-/******************************************************************************
- * @attention
- *
- * COPYRIGHT 2018 STMicroelectronics, all rights reserved
- *
- * 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.
- *
- ******************************************************************************/
-/*! \file
- *
- * \author
- *
- * \brief Platform header file. Defining platform independent functionality.
- *
- */
-
-#ifndef RFAL_PLATFORM_H
-#define RFAL_PLATFORM_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*ReturnCode
-******************************************************************************
-* INCLUDES
-******************************************************************************
-*/
-
-#include
-#include "stm32u5xx_hal.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "io/nfc.h"
-#include "nfc_internal.h"
-
-/*
-******************************************************************************
-* GLOBAL DEFINES
-******************************************************************************
-*/
-
-// Device type definition
-#define ST25R3916B
-
-// GPIO pin used for ST25R SPI SS
-#define ST25R_SS_PIN SPI_INSTANCE_3_NSS_PIN
-
-// GPIO port used for ST25R SPI SS port
-#define ST25R_SS_PORT SPI_INSTANCE_3_NSS_PORT
-
-// GPIO pin used for ST25R External Interrupt
-#define ST25R_INT_PIN NFC_INT_PIN
-
-// GPIO port used for ST25R External Interrupt
-#define ST25R_INT_PORT NFC_INT_PORT
-
-/*
-******************************************************************************
-* GLOBAL MACROS
-******************************************************************************
-*/
-#define platformProtectST25RComm() \
- NVIC_DisableIRQ(EXTI10_IRQn) // TODO: PRobably should be irq_lock instead //
- // Protect the unique access to communication
- // channel (disable IRQ on single thread)
-
-#define platformUnprotectST25RComm() \
- NVIC_EnableIRQ(EXTI10_IRQn) // TODO: Use macro here /
-
-#define platformProtectST25RIrqStatus() \
- platformProtectST25RComm() /*!< Protect unique access to IRQ status var - \
- IRQ disable on single thread environment (MCU) \
- ; Mutex lock on a multi thread environment */
-#define platformUnprotectST25RIrqStatus() \
- platformUnprotectST25RComm() /*!< Unprotect the IRQ status var - IRQ enable \
- on a single thread environment (MCU) ; Mutex \
- unlock on a multi thread environment */
-
-// Turns the given GPIO High
-#define platformGpioSet(port, pin) HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET)
-
-// Turns the given GPIO Low
-#define platformGpioClear(port, pin) \
- HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET)
-
-// Toggles the given GPIO
-#define platformGpioToggle(port, pin) HAL_GPIO_TogglePin(port, pin)
-
-// Checks if the given LED is High
-#define platformGpioIsHigh(port, pin) \
- (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)
-
-// Checks if the given LED is Low
-#define platformGpioIsLow(port, pin) (!platformGpioIsHigh(port, pin))
-
-// Create a timer with the given time (ms)
-#define platformTimerCreate(t) nfc_create_timer(t)
-
-// Checks if the given timer is expired
-#define platformTimerIsExpired(timer) nfc_timer_is_expired(timer)
-
-// Performs a delay for the given time (ms)
-#define platformDelay(t) HAL_Delay(t)
-
-// Get System Tick ( 1 tick = 1 ms)
-#define platformGetSysTick() HAL_GetTick()
-
-// Asserts whether the given expression is true
-#define platformAssert(exp) assert_param(exp)
-
-#define platformErrorHandle() //_Error_Handler(__FILE__, __LINE__) /*!< Global
- // error handle\trap */
-
-#define platformIrqST25RSetCallback(cb) nfc_ext_irq_set_callback(cb)
-
-// SPI SS\CS: Chip|Slave Select
-#define platformSpiSelect() \
- HAL_GPIO_WritePin(ST25R_SS_PORT, ST25R_SS_PIN, GPIO_PIN_RESET)
-
-// SPI SS\CS: Chip|Slave Deselect
-#define platformSpiDeselect() \
- HAL_GPIO_WritePin(ST25R_SS_PORT, ST25R_SS_PIN, GPIO_PIN_SET)
-
-// SPI transceive
-#define platformSpiTxRx(txBuf, rxBuf, len) \
- nfc_spi_transmit_receive(txBuf, rxBuf, len)
-
-// Log method
-#define platformLog(...) // logUsart(__VA_ARGS__)
-
-/*
-******************************************************************************
-* GLOBAL VARIABLES
-******************************************************************************
-*/
-extern uint8_t globalCommProtectCnt; /* Global Protection Counter provided per
- platform - instantiated in main.c */
-
-/*
-******************************************************************************
-* RFAL FEATURES CONFIGURATION
-******************************************************************************
-*/
-
-#define RFAL_FEATURE_LISTEN_MODE \
- true /*!< Enable/Disable RFAL support for Listen Mode */
-#define RFAL_FEATURE_WAKEUP_MODE \
- true /*!< Enable/Disable RFAL support for the Wake-Up mode */
-#define RFAL_FEATURE_LOWPOWER_MODE \
- false /*!< Enable/Disable RFAL support for the Low Power mode */
-#define RFAL_FEATURE_NFCA \
- true /*!< Enable/Disable RFAL support for NFC-A (ISO14443A) */
-#define RFAL_FEATURE_NFCB \
- true /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */
-#define RFAL_FEATURE_NFCF \
- true /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */
-#define RFAL_FEATURE_NFCV \
- true /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */
-#define RFAL_FEATURE_T1T \
- true /*!< Enable/Disable RFAL support for T1T (Topaz) */
-#define RFAL_FEATURE_T2T true /*!< Enable/Disable RFAL support for T2T */
-#define RFAL_FEATURE_T4T true /*!< Enable/Disable RFAL support for T4T */
-#define RFAL_FEATURE_ST25TB \
- true /*!< Enable/Disable RFAL support for ST25TB \
- */
-#define RFAL_FEATURE_ST25xV \
- true /*!< Enable/Disable RFAL support for ST25TV/ST25DV */
-#define RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG \
- false /*!< Enable/Disable Analog Configs to be dynamically updated (RAM) */
-#define RFAL_FEATURE_DPO \
- true /*!< Enable/Disable RFAL Dynamic Power Output support */
-#define RFAL_FEATURE_ISO_DEP \
- true /*!< Enable/Disable RFAL support for ISO-DEP (ISO14443-4) */
-#define RFAL_FEATURE_ISO_DEP_POLL \
- true /*!< Enable/Disable RFAL support for Poller mode (PCD) ISO-DEP \
- (ISO14443-4) */
-#define RFAL_FEATURE_ISO_DEP_LISTEN \
- true /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP \
- (ISO14443-4) */
-#define RFAL_FEATURE_NFC_DEP \
- true /*!< Enable/Disable RFAL support for NFC-DEP (NFCIP1/P2P) */
-
-#define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN \
- 256U /*!< ISO-DEP I-Block max length. Please use values as defined by \
- rfalIsoDepFSx */
-#define RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN \
- 254U /*!< NFC-DEP Block/Payload length. Allowed values: 64, 128, 192, 254 */
-#define RFAL_FEATURE_NFC_RF_BUF_LEN \
- 258U /*!< RF buffer length used by RFAL NFC layer */
-
-#define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN \
- 512U /*!< ISO-DEP APDU max length. Please use multiples of I-Block max \
- length */
-#define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN 512U /*!< NFC-DEP PDU max length. */
-
-/*
-******************************************************************************
-* RFAL CUSTOM SETTINGS
-******************************************************************************
- Custom analog configs are used to cope with Automatic Antenna Tuning (AAT)
- that are optimized differently for each board.
-*/
-// #define RFAL_ANALOG_CONFIG_CUSTOM /*!< Use Custom
-// Analog Configs when defined */
-
-#ifndef platformProtectST25RIrqStatus
-#define platformProtectST25RIrqStatus() /*!< Protect unique access to IRQ \
- status var - IRQ disable on single \
- thread environment (MCU) ; Mutex \
- lock on a multi thread environment \
- */
-#endif /* platformProtectST25RIrqStatus */
-
-#ifndef platformUnprotectST25RIrqStatus
-#define platformUnprotectST25RIrqStatus() /*!< Unprotect the IRQ status var - \
- IRQ enable on a single thread \
- environment (MCU) ; Mutex unlock \
- on a multi thread environment */
-#endif /* platformUnprotectST25RIrqStatus */
-
-#ifndef platformProtectWorker
-#define platformProtectWorker() /* Protect RFAL Worker/Task/Process from \
- concurrent execution on multi thread \
- platforms */
-#endif /* platformProtectWorker */
-
-#ifndef platformUnprotectWorker
-#define platformUnprotectWorker() /* Unprotect RFAL Worker/Task/Process from \
- concurrent execution on multi thread \
- platforms */
-#endif /* platformUnprotectWorker */
-
-#ifndef platformIrqST25RPinInitialize
-#define platformIrqST25RPinInitialize() /*!< Initializes ST25R IRQ pin */
-#endif /* platformIrqST25RPinInitialize */
-
-#ifndef platformIrqST25RSetCallback
-#define platformIrqST25RSetCallback(cb) /*!< Sets ST25R ISR callback */
-#endif /* platformIrqST25RSetCallback */
-
-#ifndef platformLedsInitialize
-#define platformLedsInitialize() /*!< Initializes the pins used as LEDs to \
- outputs */
-#endif /* platformLedsInitialize */
-
-#ifndef platformLedOff
-#define platformLedOff(port, pin) /*!< Turns the given LED Off */
-#endif /* platformLedOff */
-
-#ifndef platformLedOn
-#define platformLedOn(port, pin) /*!< Turns the given LED On */
-#endif /* platformLedOn */
-
-#ifndef platformLedToggle
-#define platformLedToggle(port, pin) /*!< Toggles the given LED */
-#endif /* platformLedToggle */
-
-#ifndef platformGetSysTick
-#define platformGetSysTick() /*!< Get System Tick ( 1 tick = 1 ms) */
-#endif /* platformGetSysTick */
-
-#ifndef platformTimerDestroy
-#define platformTimerDestroy(timer) /*!< Stops and released the given timer */
-#endif /* platformTimerDestroy */
-
-#ifndef platformLog
-#define platformLog(...) /*!< Log method */
-#endif /* platformLog */
-
-#ifndef platformAssert
-#define platformAssert(exp) /*!< Asserts whether the given expression is true \
- */
-#endif /* platformAssert */
-
-#ifndef platformErrorHandle
-#define platformErrorHandle() /*!< Global error handler or trap */
-#endif /* platformErrorHandle */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* RFAL_PLATFORM_H */
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "io/nfc.h"
+
+#include "nfc_internal.h"
+
+// Device type definition
+#define ST25R3916B
+
+// GPIO pin used for ST25R SPI SS
+#define ST25R_SS_PIN NFC_SPI_NSS_PIN
+
+// GPIO port used for ST25R SPI SS port
+#define ST25R_SS_PORT NFC_SPI_NSS_PORT
+
+// GPIO pin used for ST25R External Interrupt
+#define ST25R_INT_PIN NFC_INT_PIN
+
+// GPIO port used for ST25R External Interrupt
+#define ST25R_INT_PORT NFC_INT_PORT
+
+#define platformProtectST25RComm() NVIC_DisableIRQ(NFC_EXTI_INTERRUPT_NUM)
+
+#define platformUnprotectST25RComm() NVIC_EnableIRQ(NFC_EXTI_INTERRUPT_NUM)
+
+#define platformProtectST25RIrqStatus() \
+ platformProtectST25RComm() /*!< Protect unique access to IRQ status var - \
+ IRQ disable on single thread environment (MCU) \
+ ; Mutex lock on a multi thread environment */
+#define platformUnprotectST25RIrqStatus() \
+ platformUnprotectST25RComm() /*!< Unprotect the IRQ status var - IRQ enable \
+ on a single thread environment (MCU) ; Mutex \
+ unlock on a multi thread environment */
+
+// Turns the given GPIO High
+#define platformGpioSet(port, pin) HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET)
+
+// Turns the given GPIO Low
+#define platformGpioClear(port, pin) \
+ HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET)
+
+// Toggles the given GPIO
+#define platformGpioToggle(port, pin) HAL_GPIO_TogglePin(port, pin)
+
+// Checks if the given LED is High
+#define platformGpioIsHigh(port, pin) \
+ (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)
+
+// Checks if the given LED is Low
+#define platformGpioIsLow(port, pin) (!platformGpioIsHigh(port, pin))
+
+// Create a timer with the given time (ms)
+#define platformTimerCreate(t) ticks_timeout(t)
+
+// Checks if the given timer is expired
+#define platformTimerIsExpired(timer) ticks_expired(timer)
+
+// Performs a delay for the given time (ms)
+#define platformDelay(t) HAL_Delay(t)
+
+// Get System Tick ( 1 tick = 1 ms)
+#define platformGetSysTick() HAL_GetTick()
+
+// Asserts whether the given expression is true
+#define platformAssert(exp) assert_param(exp)
+
+#define platformErrorHandle() //_Error_Handler(__FILE__, __LINE__) /*!< Global
+ // error handle\trap */
+
+#define platformIrqST25RSetCallback(cb) nfc_ext_irq_set_callback(cb)
+
+// SPI SS\CS: Chip|Slave Select
+#define platformSpiSelect() \
+ HAL_GPIO_WritePin(ST25R_SS_PORT, ST25R_SS_PIN, GPIO_PIN_RESET)
+
+// SPI SS\CS: Chip|Slave Deselect
+#define platformSpiDeselect() \
+ HAL_GPIO_WritePin(ST25R_SS_PORT, ST25R_SS_PIN, GPIO_PIN_SET)
+
+// SPI transceive
+#define platformSpiTxRx(txBuf, rxBuf, len) \
+ nfc_spi_transmit_receive(txBuf, rxBuf, len)
+
+// Log method
+#define platformLog(...) // logUsart(__VA_ARGS__)
+
+extern uint8_t globalCommProtectCnt; /* Global Protection Counter provided per
+ platform - instantiated in main.c */
+
+#define RFAL_FEATURE_LISTEN_MODE \
+ true /*!< Enable/Disable RFAL support for Listen Mode */
+#define RFAL_FEATURE_WAKEUP_MODE \
+ true /*!< Enable/Disable RFAL support for the Wake-Up mode */
+#define RFAL_FEATURE_LOWPOWER_MODE \
+ false /*!< Enable/Disable RFAL support for the Low Power mode */
+#define RFAL_FEATURE_NFCA \
+ true /*!< Enable/Disable RFAL support for NFC-A (ISO14443A) */
+#define RFAL_FEATURE_NFCB \
+ true /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */
+#define RFAL_FEATURE_NFCF \
+ true /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */
+#define RFAL_FEATURE_NFCV \
+ true /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */
+#define RFAL_FEATURE_T1T \
+ true /*!< Enable/Disable RFAL support for T1T (Topaz) */
+#define RFAL_FEATURE_T2T true /*!< Enable/Disable RFAL support for T2T */
+#define RFAL_FEATURE_T4T true /*!< Enable/Disable RFAL support for T4T */
+#define RFAL_FEATURE_ST25TB \
+ true /*!< Enable/Disable RFAL support for ST25TB \
+ */
+#define RFAL_FEATURE_ST25xV \
+ true /*!< Enable/Disable RFAL support for ST25TV/ST25DV */
+#define RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG \
+ false /*!< Enable/Disable Analog Configs to be dynamically updated (RAM) */
+#define RFAL_FEATURE_DPO \
+ true /*!< Enable/Disable RFAL Dynamic Power Output support */
+#define RFAL_FEATURE_ISO_DEP \
+ true /*!< Enable/Disable RFAL support for ISO-DEP (ISO14443-4) */
+#define RFAL_FEATURE_ISO_DEP_POLL \
+ true /*!< Enable/Disable RFAL support for Poller mode (PCD) ISO-DEP \
+ (ISO14443-4) */
+#define RFAL_FEATURE_ISO_DEP_LISTEN \
+ true /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP \
+ (ISO14443-4) */
+#define RFAL_FEATURE_NFC_DEP \
+ true /*!< Enable/Disable RFAL support for NFC-DEP (NFCIP1/P2P) */
+
+#define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN \
+ 256U /*!< ISO-DEP I-Block max length. Please use values as defined by \
+ rfalIsoDepFSx */
+#define RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN \
+ 254U /*!< NFC-DEP Block/Payload length. Allowed values: 64, 128, 192, 254 */
+#define RFAL_FEATURE_NFC_RF_BUF_LEN \
+ 258U /*!< RF buffer length used by RFAL NFC layer */
+
+#define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN \
+ 512U /*!< ISO-DEP APDU max length. Please use multiples of I-Block max \
+ length */
+#define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN 512U /*!< NFC-DEP PDU max length. */
+
+/*
+******************************************************************************
+* RFAL CUSTOM SETTINGS
+******************************************************************************
+ Custom analog configs are used to cope with Automatic Antenna Tuning (AAT)
+ that are optimized differently for each board.
+*/
+// #define RFAL_ANALOG_CONFIG_CUSTOM /*!< Use Custom
+// Analog Configs when defined */
+
+#ifndef platformProtectST25RIrqStatus
+#define platformProtectST25RIrqStatus() /*!< Protect unique access to IRQ \
+ status var - IRQ disable on single \
+ thread environment (MCU) ; Mutex \
+ lock on a multi thread environment \
+ */
+#endif /* platformProtectST25RIrqStatus */
+
+#ifndef platformUnprotectST25RIrqStatus
+#define platformUnprotectST25RIrqStatus() /*!< Unprotect the IRQ status var - \
+ IRQ enable on a single thread \
+ environment (MCU) ; Mutex unlock \
+ on a multi thread environment */
+#endif /* platformUnprotectST25RIrqStatus */
+
+#ifndef platformProtectWorker
+#define platformProtectWorker() /* Protect RFAL Worker/Task/Process from \
+ concurrent execution on multi thread \
+ platforms */
+#endif /* platformProtectWorker */
+
+#ifndef platformUnprotectWorker
+#define platformUnprotectWorker() /* Unprotect RFAL Worker/Task/Process from \
+ concurrent execution on multi thread \
+ platforms */
+#endif /* platformUnprotectWorker */
+
+#ifndef platformIrqST25RPinInitialize
+#define platformIrqST25RPinInitialize() /*!< Initializes ST25R IRQ pin */
+#endif /* platformIrqST25RPinInitialize */
+
+#ifndef platformIrqST25RSetCallback
+#define platformIrqST25RSetCallback(cb) /*!< Sets ST25R ISR callback */
+#endif /* platformIrqST25RSetCallback */
+
+#ifndef platformLedsInitialize
+#define platformLedsInitialize() /*!< Initializes the pins used as LEDs to \
+ outputs */
+#endif /* platformLedsInitialize */
+
+#ifndef platformLedOff
+#define platformLedOff(port, pin) /*!< Turns the given LED Off */
+#endif /* platformLedOff */
+
+#ifndef platformLedOn
+#define platformLedOn(port, pin) /*!< Turns the given LED On */
+#endif /* platformLedOn */
+
+#ifndef platformLedToggle
+#define platformLedToggle(port, pin) /*!< Toggles the given LED */
+#endif /* platformLedToggle */
+
+#ifndef platformGetSysTick
+#define platformGetSysTick() /*!< Get System Tick ( 1 tick = 1 ms) */
+#endif /* platformGetSysTick */
+
+#ifndef platformTimerDestroy
+#define platformTimerDestroy(timer) /*!< Stops and released the given timer */
+#endif /* platformTimerDestroy */
+
+#ifndef platformLog
+#define platformLog(...) /*!< Log method */
+#endif /* platformLog */
+
+#ifndef platformAssert
+#define platformAssert(exp) /*!< Asserts whether the given expression is true \
+ */
+#endif /* platformAssert */
+
+#ifndef platformErrorHandle
+#define platformErrorHandle() /*!< Global error handler or trap */
+#endif /* platformErrorHandle */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
index f925495484..64330ac3a2 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
@@ -160,22 +160,24 @@
#define NRF_OUT_FW_RUNNING_PORT GPIOE
#define NRF_OUT_FW_RUNNING_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
-#define SPI_INSTANCE_3 SPI3
-#define SPI_INSTANCE_3_PIN_AF GPIO_AF6_SPI3
-#define SPI_INSTANCE_3_CLK_EN __HAL_RCC_SPI3_CLK_ENABLE
-#define SPI_INSTANCE_3_CLK_DIS __HAL_RCC_SPI3_CLK_DISABLE
-#define SPI_INSTANCE_3_MISO_PORT GPIOB
-#define SPI_INSTANCE_3_MISO_PIN GPIO_PIN_4
-#define SPI_INSTANCE_3_MISO_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
-#define SPI_INSTANCE_3_MOSI_PORT GPIOB
-#define SPI_INSTANCE_3_MOSI_PIN GPIO_PIN_5
-#define SPI_INSTANCE_3_MOSI_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
-#define SPI_INSTANCE_3_SCK_PORT GPIOG
-#define SPI_INSTANCE_3_SCK_PIN GPIO_PIN_9
-#define SPI_INSTANCE_3_SCK_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
-#define SPI_INSTANCE_3_NSS_PORT GPIOG
-#define SPI_INSTANCE_3_NSS_PIN GPIO_PIN_12
-#define SPI_INSTANCE_3_NSS_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+#define NFC_SPI_INSTANCE SPI3
+#define NFC_SPI_PIN_AF GPIO_AF6_SPI3
+#define NFC_SPI_CLK_EN __HAL_RCC_SPI3_CLK_ENABLE
+#define NFC_SPI_CLK_DIS __HAL_RCC_SPI3_CLK_DISABLE
+#define NFC_SPI_FORCE_RESET __HAL_RCC_SPI3_FORCE_RESET
+#define NFC_SPI_RELEASE_RESET __HAL_RCC_SPI3_RELEASE_RESET
+#define NFC_SPI_MISO_PORT GPIOB
+#define NFC_SPI_MISO_PIN GPIO_PIN_4
+#define NFC_SPI_MISO_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define NFC_SPI_MOSI_PORT GPIOB
+#define NFC_SPI_MOSI_PIN GPIO_PIN_5
+#define NFC_SPI_MOSI_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define NFC_SPI_SCK_PORT GPIOG
+#define NFC_SPI_SCK_PIN GPIO_PIN_9
+#define NFC_SPI_SCK_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+#define NFC_SPI_NSS_PORT GPIOG
+#define NFC_SPI_NSS_PIN GPIO_PIN_12
+#define NFC_SPI_NSS_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
#define NFC_INT_PIN GPIO_PIN_10
#define NFC_INT_PORT GPIOG
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
index 1317a58954..7d08569ba3 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
@@ -160,4 +160,31 @@
#define NRF_OUT_FW_RUNNING_PORT GPIOE
#define NRF_OUT_FW_RUNNING_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
+#define NFC_SPI_INSTANCE SPI3
+#define NFC_SPI_PIN_AF GPIO_AF6_SPI3
+#define NFC_SPI_CLK_EN __HAL_RCC_SPI3_CLK_ENABLE
+#define NFC_SPI_CLK_DIS __HAL_RCC_SPI3_CLK_DISABLE
+#define NFC_SPI_FORCE_RESET __HAL_RCC_SPI3_FORCE_RESET
+#define NFC_SPI_RELEASE_RESET __HAL_RCC_SPI3_RELEASE_RESET
+#define NFC_SPI_MISO_PORT GPIOB
+#define NFC_SPI_MISO_PIN GPIO_PIN_4
+#define NFC_SPI_MISO_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define NFC_SPI_MOSI_PORT GPIOB
+#define NFC_SPI_MOSI_PIN GPIO_PIN_5
+#define NFC_SPI_MOSI_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define NFC_SPI_SCK_PORT GPIOG
+#define NFC_SPI_SCK_PIN GPIO_PIN_9
+#define NFC_SPI_SCK_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+#define NFC_SPI_NSS_PORT GPIOG
+#define NFC_SPI_NSS_PIN GPIO_PIN_12
+#define NFC_SPI_NSS_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+
+#define NFC_INT_PIN GPIO_PIN_10
+#define NFC_INT_PORT GPIOG
+#define NFC_INT_PIN_CLK_ENA __HAL_RCC_GPIOG_CLK_ENABLE
+#define NFC_EXTI_INTERRUPT_GPIOSEL EXTI_GPIOG
+#define NFC_EXTI_INTERRUPT_LINE EXTI_LINE_10
+#define NFC_EXTI_INTERRUPT_NUM EXTI10_IRQn
+#define NFC_EXTI_INTERRUPT_HANDLER EXTI10_IRQHandler
+
#endif // TREZOR_T3W1_REVA_H_
diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md
index 23d589f891..be5c2fa267 100644
--- a/core/embed/projects/prodtest/README.md
+++ b/core/embed/projects/prodtest/README.md
@@ -771,3 +771,38 @@ wpc-update
# Updating STWLC38...
# WPC update completed 800 ms
```
+
+### nfc-read-card
+Activate the NFC in reader mode for a given time. Read general information from firstly discovered NFC tag or exits on timeout.
+
+Example:
+```
+nfc-read-card
+# NFC activated in reader mode for seconds.
+# NFC card detected.
+# NFC Type A: UID: %s
+OK
+```
+
+
+### nfc-emulate-card
+Activate NFC in Card Emulator mode for given time.
+
+Example:
+```
+nfc-emulate-card
+# Emulation started for
+# Emulation over
+OK
+```
+
+### nfc-write-card
+Activates the NFC reader for given time. Writes the NDEF URI message into the first discovered NFC tag type A or exits on timeout.
+
+Example:
+```
+nfc-write_card
+# NFC reader on, put the card on the reader (timeout s)
+# Writting URI to NFC tag 7AF403
+OK
+```
diff --git a/core/embed/projects/prodtest/cmd/prodtest_nfc.c b/core/embed/projects/prodtest/cmd/prodtest_nfc.c
new file mode 100644
index 0000000000..023a9185c3
--- /dev/null
+++ b/core/embed/projects/prodtest/cmd/prodtest_nfc.c
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifdef USE_NFC
+
+#include
+
+#include
+#include
+#include
+
+static nfc_dev_info_t dev_info = {0};
+
+static void prodtest_nfc_read_card(cli_t* cli) {
+ uint32_t timeout = 0;
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_status_t ret = nfc_init();
+
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ goto cleanup;
+ } else {
+ cli_trace(cli, "NFC activated in reader mode for %d ms.", timeout);
+ }
+
+ nfc_register_tech(NFC_POLLER_TECH_A | NFC_POLLER_TECH_B | NFC_POLLER_TECH_F |
+ NFC_POLLER_TECH_V);
+ nfc_activate_stm();
+
+ nfc_event_t nfc_event;
+ uint32_t expire_time = ticks_timeout(timeout);
+
+ while (true) {
+ if (ticks_expired(expire_time)) {
+ cli_error(cli, CLI_ERROR_TIMEOUT, "NFC timeout");
+ goto cleanup;
+ }
+
+ nfc_status_t nfc_status = nfc_get_event(&nfc_event);
+
+ if (nfc_status != NFC_OK) {
+ cli_error(cli, CLI_ERROR, "NFC error");
+ goto cleanup;
+ }
+
+ if (nfc_event == NFC_EVENT_ACTIVATED) {
+ nfc_dev_read_info(&dev_info);
+ nfc_dev_deactivate();
+ break;
+ }
+
+ if (cli_aborted(cli)) {
+ goto cleanup;
+ }
+ }
+
+ cli_trace(cli, "NFC card detected.");
+
+ switch (dev_info.type) {
+ case NFC_DEV_TYPE_A:
+ cli_trace(cli, "NFC Type A: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_B:
+ cli_trace(cli, "NFC Type B: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_F:
+ cli_trace(cli, "NFC Type F: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_V:
+ cli_trace(cli, "NFC Type V: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_ST25TB:
+ cli_trace(cli, "NFC Type ST25TB: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_AP2P:
+ cli_trace(cli, "NFC Type AP2P: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_UNKNOWN:
+ cli_error(cli, CLI_ERROR_ABORT, "NFC Type UNKNOWN");
+ goto cleanup;
+ return;
+
+ default:
+ cli_error(cli, CLI_ERROR_ABORT, "NFC Type UNKNOWN");
+ goto cleanup;
+ }
+
+ cli_ok(cli, "");
+
+cleanup:
+ nfc_deinit();
+}
+
+static void prodtest_nfc_emulate_card(cli_t* cli) {
+ uint32_t timeout = 0;
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_status_t ret = nfc_init();
+
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ goto cleanup;
+ } else {
+ cli_trace(cli, "Emulation started for %d ms", timeout);
+ }
+
+ nfc_register_tech(NFC_CARD_EMU_TECH_A);
+ nfc_activate_stm();
+
+ uint32_t expire_time = ticks_timeout(timeout);
+ nfc_event_t nfc_event;
+
+ while (!ticks_expired(expire_time)) {
+ nfc_status_t nfc_status = nfc_get_event(&nfc_event);
+
+ if (nfc_status != NFC_OK) {
+ cli_error(cli, CLI_ERROR, "NFC error");
+ goto cleanup;
+ }
+
+ if (cli_aborted(cli)) {
+ goto cleanup;
+ }
+ }
+
+ cli_trace(cli, "Emulation over");
+
+ cli_ok(cli, "");
+
+cleanup:
+ nfc_deinit();
+}
+
+static void prodtest_nfc_write_card(cli_t* cli) {
+ uint32_t timeout = 0;
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_status_t ret = nfc_init();
+
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ goto cleanup;
+ } else {
+ cli_trace(cli, "NFC reader on, put the card on the reader (timeout %d ms)",
+ timeout);
+ }
+
+ nfc_register_tech(NFC_POLLER_TECH_A | NFC_POLLER_TECH_B | NFC_POLLER_TECH_F |
+ NFC_POLLER_TECH_V);
+ nfc_activate_stm();
+
+ nfc_event_t nfc_event;
+ uint32_t expire_time = ticks_timeout(timeout);
+
+ while (true) {
+ if (ticks_expired(expire_time)) {
+ cli_error(cli, CLI_ERROR_TIMEOUT, "NFC timeout");
+ goto cleanup;
+ }
+
+ nfc_status_t nfc_status = nfc_get_event(&nfc_event);
+
+ if (nfc_status != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC error");
+ goto cleanup;
+ }
+
+ if (nfc_event == NFC_EVENT_ACTIVATED) {
+ nfc_dev_read_info(&dev_info);
+
+ if (dev_info.type != NFC_DEV_TYPE_A) {
+ cli_error(cli, CLI_ERROR_ABORT, "Only NFC type A cards supported");
+ goto cleanup;
+ }
+
+ cli_trace(cli, "Writting URI to NFC tag %s", dev_info.uid);
+ nfc_dev_write_ndef_uri();
+
+ nfc_dev_deactivate();
+ break;
+ }
+
+ if (cli_aborted(cli)) {
+ goto cleanup;
+ }
+ }
+
+ cli_ok(cli, "");
+
+cleanup:
+ nfc_deinit();
+}
+
+// clang-format off
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-read-card",
+ .func = prodtest_nfc_read_card,
+ .info = "Activate NFC in reader mode",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-emulate-card",
+ .func = prodtest_nfc_emulate_card,
+ .info = "Activate NFC in card emulation (CE) mode",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-write-card",
+ .func = prodtest_nfc_write_card,
+ .info = "Activate NFC in reader mode and write a URI to the attached card",
+ .args = ""
+);
+
+#endif // USE_NFC
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 39200aaa6f..4a9a8f17d7 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -94,6 +94,7 @@ def configure(
if "nfc" in features_wanted:
sources += ["embed/io/nfc/st25r3916b/nfc.c"]
sources += ["embed/io/nfc/st25r3916b/ndef.c"]
+ sources += ["embed/io/nfc/st25r3916b/card_emulation.c"]
sources += ["embed/io/nfc/rfal/source/st25r3916/rfal_rfst25r3916.c"]
sources += ["embed/io/nfc/rfal/source/rfal_analogConfig.c"]
sources += ["embed/io/nfc/rfal/source/rfal_nfc.c"]
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index 46f4033575..b0c92b24f9 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -91,6 +91,35 @@ def configure(
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
]
+ if "nfc" in features_wanted:
+ sources += ["embed/io/nfc/st25r3916b/nfc.c"]
+ sources += ["embed/io/nfc/st25r3916b/ndef.c"]
+ sources += ["embed/io/nfc/st25r3916b/card_emulation.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/rfal_rfst25r3916.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_analogConfig.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfc.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfca.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcb.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcf.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcv.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_isoDep.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcDep.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_st25tb.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_t1t.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_t2t.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_iso15693_2.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_crc.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_com.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_led.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_irq.c"]
+ paths += ["embed/io/nfc/inc/"]
+ paths += ["embed/io/nfc/st25r3916b/"]
+ paths += ["embed/io/nfc/rfal/source"]
+ paths += ["embed/io/nfc/rfal/source/st25r3916"]
+ paths += ["embed/io/nfc/rfal/include/"]
+ defines += [("USE_NFC", "1")]
+
if "optiga" in features_wanted:
sources += ["embed/sec/optiga/stm32/optiga_hal.c"]
sources += ["embed/sec/optiga/optiga.c"]
diff --git a/tools/style.c.exclude b/tools/style.c.exclude
index 6487231786..8d9ca1e4ca 100644
--- a/tools/style.c.exclude
+++ b/tools/style.c.exclude
@@ -10,3 +10,4 @@
^\./crypto/sha3
^\./legacy/vendor
^\./core/embed/sys/systemview/stm32
+^\./core/embed/io/nfc/rfal