NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
+439
-397
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,431 +0,0 @@
|
||||
/**
|
||||
* @file furi_hal_nfc.h
|
||||
* NFC HAL API
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <st_errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <rfal_nfc.h>
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
|
||||
#define FURI_HAL_NFC_UID_MAX_LEN 10
|
||||
#define FURI_HAL_NFC_DATA_BUFF_SIZE (512)
|
||||
#define FURI_HAL_NFC_PARITY_BUFF_SIZE (FURI_HAL_NFC_DATA_BUFF_SIZE / 8)
|
||||
|
||||
#define FURI_HAL_NFC_TXRX_DEFAULT \
|
||||
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \
|
||||
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
|
||||
|
||||
#define FURI_HAL_NFC_TX_DEFAULT_RX_NO_CRC \
|
||||
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
|
||||
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
|
||||
|
||||
#define FURI_HAL_NFC_TXRX_WITH_PAR \
|
||||
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
|
||||
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
|
||||
|
||||
#define FURI_HAL_NFC_TXRX_RAW \
|
||||
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
|
||||
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE)
|
||||
|
||||
#define FURI_HAL_NFC_TX_RAW_RX_DEFAULT \
|
||||
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \
|
||||
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE)
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcTxRxTypeDefault,
|
||||
FuriHalNfcTxRxTypeRxNoCrc,
|
||||
FuriHalNfcTxRxTypeRxKeepPar,
|
||||
FuriHalNfcTxRxTypeRaw,
|
||||
FuriHalNfcTxRxTypeRxRaw,
|
||||
FuriHalNfcTxRxTransparent,
|
||||
} FuriHalNfcTxRxType;
|
||||
|
||||
typedef bool (*FuriHalNfcEmulateCallback)(
|
||||
uint8_t* buff_rx,
|
||||
uint16_t buff_rx_len,
|
||||
uint8_t* buff_tx,
|
||||
uint16_t* buff_tx_len,
|
||||
uint32_t* flags,
|
||||
void* context);
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcTypeA,
|
||||
FuriHalNfcTypeB,
|
||||
FuriHalNfcTypeF,
|
||||
FuriHalNfcTypeV,
|
||||
} FuriHalNfcType;
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcInterfaceRf,
|
||||
FuriHalNfcInterfaceIsoDep,
|
||||
FuriHalNfcInterfaceNfcDep,
|
||||
} FuriHalNfcInterface;
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcType type;
|
||||
FuriHalNfcInterface interface;
|
||||
uint8_t uid_len;
|
||||
uint8_t uid[10];
|
||||
uint32_t cuid;
|
||||
uint8_t atqa[2];
|
||||
uint8_t sak;
|
||||
} FuriHalNfcDevData;
|
||||
|
||||
typedef void (
|
||||
*FuriHalNfcTxRxSniffCallback)(uint8_t* data, uint16_t bits, bool crc_dropped, void* context);
|
||||
|
||||
typedef struct {
|
||||
uint8_t tx_data[FURI_HAL_NFC_DATA_BUFF_SIZE];
|
||||
uint8_t tx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
|
||||
uint16_t tx_bits;
|
||||
uint8_t rx_data[FURI_HAL_NFC_DATA_BUFF_SIZE];
|
||||
uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
|
||||
uint16_t rx_bits;
|
||||
FuriHalNfcTxRxType tx_rx_type;
|
||||
NfcaSignal* nfca_signal;
|
||||
|
||||
FuriHalNfcTxRxSniffCallback sniff_tx;
|
||||
FuriHalNfcTxRxSniffCallback sniff_rx;
|
||||
void* sniff_context;
|
||||
} FuriHalNfcTxRxContext;
|
||||
|
||||
/** Init nfc
|
||||
*/
|
||||
void furi_hal_nfc_init();
|
||||
|
||||
/** Deinit nfc
|
||||
*/
|
||||
void furi_hal_nfc_deinit();
|
||||
|
||||
/** Check if nfc worker is busy
|
||||
*
|
||||
* @return true if busy
|
||||
*/
|
||||
bool furi_hal_nfc_is_busy();
|
||||
|
||||
/** Check if nfc is initialized
|
||||
*
|
||||
* @return true if initialized
|
||||
*/
|
||||
bool furi_hal_nfc_is_init();
|
||||
|
||||
/** NFC field on
|
||||
*/
|
||||
void furi_hal_nfc_field_on();
|
||||
|
||||
/** NFC field off
|
||||
*/
|
||||
void furi_hal_nfc_field_off();
|
||||
|
||||
/** NFC start sleep
|
||||
*/
|
||||
void furi_hal_nfc_start_sleep();
|
||||
|
||||
void furi_hal_nfc_stop_cmd();
|
||||
|
||||
/** NFC stop sleep
|
||||
*/
|
||||
void furi_hal_nfc_exit_sleep();
|
||||
|
||||
/** NFC poll
|
||||
*
|
||||
* @param dev_list pointer to rfalNfcDevice buffer
|
||||
* @param dev_cnt pointer device count
|
||||
* @param timeout timeout in ms
|
||||
* @param deactivate deactivate flag
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout);
|
||||
|
||||
/** Activate NFC-A tag
|
||||
*
|
||||
* @param timeout timeout in ms
|
||||
* @param cuid pointer to 32bit uid
|
||||
*
|
||||
* @return true on succeess
|
||||
*/
|
||||
bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid);
|
||||
|
||||
/** NFC listen
|
||||
*
|
||||
* @param uid pointer to uid buffer
|
||||
* @param uid_len uid length
|
||||
* @param atqa pointer to atqa
|
||||
* @param sak sak
|
||||
* @param activate_after_sak activate after sak flag
|
||||
* @param timeout timeout in ms
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_listen(
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak,
|
||||
bool activate_after_sak,
|
||||
uint32_t timeout);
|
||||
|
||||
/** Start Target Listen mode
|
||||
* @note RFAL free implementation
|
||||
*
|
||||
* @param nfc_data FuriHalNfcDevData instance
|
||||
*/
|
||||
void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data);
|
||||
|
||||
/** Read data in Target Listen mode
|
||||
* @note Must be called only after furi_hal_nfc_listen_start()
|
||||
*
|
||||
* @param tx_rx FuriHalNfcTxRxContext instance
|
||||
* @param timeout_ms timeout im ms
|
||||
*
|
||||
* @return true on not empty receive
|
||||
*/
|
||||
bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms);
|
||||
|
||||
/** Set Target in Sleep state */
|
||||
void furi_hal_nfc_listen_sleep();
|
||||
|
||||
/** Emulate NFC-A Target
|
||||
* @note RFAL based implementation
|
||||
*
|
||||
* @param uid NFC-A UID
|
||||
* @param uid_len NFC-A UID length
|
||||
* @param atqa NFC-A ATQA
|
||||
* @param sak NFC-A SAK
|
||||
* @param callback FuriHalNfcEmulateCallback instance
|
||||
* @param context pointer to context for callback
|
||||
* @param timeout timeout in ms
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_emulate_nfca(
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak,
|
||||
FuriHalNfcEmulateCallback callback,
|
||||
void* context,
|
||||
uint32_t timeout);
|
||||
|
||||
/** NFC data exchange
|
||||
*
|
||||
* @param tx_rx_ctx FuriHalNfcTxRxContext instance
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms);
|
||||
|
||||
/** NFC data full exhange
|
||||
*
|
||||
* @param tx_rx_ctx FuriHalNfcTxRxContext instance
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool furi_hal_nfc_tx_rx_full(FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
/** NFC deactivate and start sleep
|
||||
*/
|
||||
void furi_hal_nfc_sleep();
|
||||
|
||||
void furi_hal_nfc_stop();
|
||||
|
||||
/* Low level transport API, use it to implement your own transport layers */
|
||||
|
||||
#define furi_hal_nfc_ll_ms2fc rfalConvMsTo1fc
|
||||
|
||||
#define FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL RFAL_TXRX_FLAGS_CRC_TX_MANUAL
|
||||
#define FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON RFAL_TXRX_FLAGS_AGC_ON
|
||||
#define FURI_HAL_NFC_LL_TXRX_FLAGS_PAR_RX_REMV RFAL_TXRX_FLAGS_PAR_RX_REMV
|
||||
#define FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP RFAL_TXRX_FLAGS_CRC_RX_KEEP
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcReturnOk = 0, /*!< no error occurred */
|
||||
FuriHalNfcReturnNomem = 1, /*!< not enough memory to perform the requested operation */
|
||||
FuriHalNfcReturnBusy = 2, /*!< device or resource busy */
|
||||
FuriHalNfcReturnIo = 3, /*!< generic IO error */
|
||||
FuriHalNfcReturnTimeout = 4, /*!< error due to timeout */
|
||||
FuriHalNfcReturnRequest =
|
||||
5, /*!< invalid request or requested function can't be executed at the moment */
|
||||
FuriHalNfcReturnNomsg = 6, /*!< No message of desired type */
|
||||
FuriHalNfcReturnParam = 7, /*!< Parameter error */
|
||||
FuriHalNfcReturnSystem = 8, /*!< System error */
|
||||
FuriHalNfcReturnFraming = 9, /*!< Framing error */
|
||||
FuriHalNfcReturnOverrun = 10, /*!< lost one or more received bytes */
|
||||
FuriHalNfcReturnProto = 11, /*!< protocol error */
|
||||
FuriHalNfcReturnInternal = 12, /*!< Internal Error */
|
||||
FuriHalNfcReturnAgain = 13, /*!< Call again */
|
||||
FuriHalNfcReturnMemCorrupt = 14, /*!< memory corruption */
|
||||
FuriHalNfcReturnNotImplemented = 15, /*!< not implemented */
|
||||
FuriHalNfcReturnPcCorrupt =
|
||||
16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */
|
||||
FuriHalNfcReturnSend = 17, /*!< error sending*/
|
||||
FuriHalNfcReturnIgnore = 18, /*!< indicates error detected but to be ignored */
|
||||
FuriHalNfcReturnSemantic = 19, /*!< indicates error in state machine (unexpected cmd) */
|
||||
FuriHalNfcReturnSyntax = 20, /*!< indicates error in state machine (unknown cmd) */
|
||||
FuriHalNfcReturnCrc = 21, /*!< crc error */
|
||||
FuriHalNfcReturnNotfound = 22, /*!< transponder not found */
|
||||
FuriHalNfcReturnNotunique =
|
||||
23, /*!< transponder not unique - more than one transponder in field */
|
||||
FuriHalNfcReturnNotsupp = 24, /*!< requested operation not supported */
|
||||
FuriHalNfcReturnWrite = 25, /*!< write error */
|
||||
FuriHalNfcReturnFifo = 26, /*!< fifo over or underflow error */
|
||||
FuriHalNfcReturnPar = 27, /*!< parity error */
|
||||
FuriHalNfcReturnDone = 28, /*!< transfer has already finished */
|
||||
FuriHalNfcReturnRfCollision =
|
||||
29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */
|
||||
FuriHalNfcReturnHwOverrun = 30, /*!< lost one or more received bytes */
|
||||
FuriHalNfcReturnReleaseReq = 31, /*!< device requested release */
|
||||
FuriHalNfcReturnSleepReq = 32, /*!< device requested sleep */
|
||||
FuriHalNfcReturnWrongState = 33, /*!< incorrent state for requested operation */
|
||||
FuriHalNfcReturnMaxReruns = 34, /*!< blocking procedure reached maximum runs */
|
||||
FuriHalNfcReturnDisabled = 35, /*!< operation aborted due to disabled configuration */
|
||||
FuriHalNfcReturnHwMismatch = 36, /*!< expected hw do not match */
|
||||
FuriHalNfcReturnLinkLoss =
|
||||
37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */
|
||||
FuriHalNfcReturnInvalidHandle = 38, /*!< invalid or not initalized device handle */
|
||||
FuriHalNfcReturnIncompleteByte = 40, /*!< Incomplete byte rcvd */
|
||||
FuriHalNfcReturnIncompleteByte01 = 41, /*!< Incomplete byte rcvd - 1 bit */
|
||||
FuriHalNfcReturnIncompleteByte02 = 42, /*!< Incomplete byte rcvd - 2 bit */
|
||||
FuriHalNfcReturnIncompleteByte03 = 43, /*!< Incomplete byte rcvd - 3 bit */
|
||||
FuriHalNfcReturnIncompleteByte04 = 44, /*!< Incomplete byte rcvd - 4 bit */
|
||||
FuriHalNfcReturnIncompleteByte05 = 45, /*!< Incomplete byte rcvd - 5 bit */
|
||||
FuriHalNfcReturnIncompleteByte06 = 46, /*!< Incomplete byte rcvd - 6 bit */
|
||||
FuriHalNfcReturnIncompleteByte07 = 47, /*!< Incomplete byte rcvd - 7 bit */
|
||||
} FuriHalNfcReturn;
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcModeNone = 0, /*!< No mode selected/defined */
|
||||
FuriHalNfcModePollNfca = 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */
|
||||
FuriHalNfcModePollNfcaT1t = 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */
|
||||
FuriHalNfcModePollNfcb = 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */
|
||||
FuriHalNfcModePollBPrime = 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */
|
||||
FuriHalNfcModePollBCts = 5, /*!< Mode to perform as CTS Poller (PCD) */
|
||||
FuriHalNfcModePollNfcf = 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */
|
||||
FuriHalNfcModePollNfcv = 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */
|
||||
FuriHalNfcModePollPicopass = 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */
|
||||
FuriHalNfcModePollActiveP2p = 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */
|
||||
FuriHalNfcModeListenNfca = 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */
|
||||
FuriHalNfcModeListenNfcb = 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */
|
||||
FuriHalNfcModeListenNfcf = 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */
|
||||
FuriHalNfcModeListenActiveP2p = 13 /*!< Mode to perform as Active P2P (ISO18092) Target */
|
||||
} FuriHalNfcMode;
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcBitrate106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */
|
||||
FuriHalNfcBitrate212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */
|
||||
FuriHalNfcBitrate424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */
|
||||
FuriHalNfcBitrate848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */
|
||||
FuriHalNfcBitrate1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */
|
||||
FuriHalNfcBitrate3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */
|
||||
FuriHalNfcBitrate6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */
|
||||
FuriHalNfcBitrate13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */
|
||||
FuriHalNfcBitrate52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */
|
||||
FuriHalNfcBitrate26p48 =
|
||||
0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */
|
||||
FuriHalNfcBitrate1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */
|
||||
FuriHalNfcBitrateKeep = 0xFF /*!< Value indicating to keep the same previous bit rate */
|
||||
} FuriHalNfcBitrate;
|
||||
|
||||
FuriHalNfcReturn
|
||||
furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate txBR, FuriHalNfcBitrate rxBR);
|
||||
|
||||
#define FURI_HAL_NFC_LL_GT_NFCA furi_hal_nfc_ll_ms2fc(5U) /*!< GTA Digital 2.0 6.10.4.1 & B.2 */
|
||||
#define FURI_HAL_NFC_LL_GT_NFCB furi_hal_nfc_ll_ms2fc(5U) /*!< GTB Digital 2.0 7.9.4.1 & B.3 */
|
||||
#define FURI_HAL_NFC_LL_GT_NFCF furi_hal_nfc_ll_ms2fc(20U) /*!< GTF Digital 2.0 8.7.4.1 & B.4 */
|
||||
#define FURI_HAL_NFC_LL_GT_NFCV furi_hal_nfc_ll_ms2fc(5U) /*!< GTV Digital 2.0 9.7.5.1 & B.5 */
|
||||
#define FURI_HAL_NFC_LL_GT_PICOPASS furi_hal_nfc_ll_ms2fc(1U) /*!< GT Picopass */
|
||||
#define FURI_HAL_NFC_LL_GT_AP2P furi_hal_nfc_ll_ms2fc(5U) /*!< TIRFG Ecma 340 11.1.1 */
|
||||
#define FURI_HAL_NFC_LL_GT_AP2P_ADJUSTED \
|
||||
furi_hal_nfc_ll_ms2fc( \
|
||||
5U + \
|
||||
25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */
|
||||
|
||||
void furi_hal_nfc_ll_set_guard_time(uint32_t cycles);
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcErrorHandlingNone = 0, /*!< No special error handling will be performed */
|
||||
FuriHalNfcErrorHandlingNfc = 1, /*!< Error handling set to perform as NFC compliant device */
|
||||
FuriHalNfcErrorHandlingEmvco =
|
||||
2 /*!< Error handling set to perform as EMVCo compliant device */
|
||||
} FuriHalNfcErrorHandling;
|
||||
|
||||
void furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandling eHandling);
|
||||
|
||||
/* RFAL Frame Delay Time (FDT) Listen default values */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER \
|
||||
1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_POLLER \
|
||||
1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_POLLER \
|
||||
2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER \
|
||||
4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER \
|
||||
3400U /*!< ISO15693 t1 min - observed adjustment */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_POLLER \
|
||||
64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_LISTENER 1172U /*!< FDTA,LISTEN,MIN Digital 1.1 6.10 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_LISTENER \
|
||||
1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_LISTENER \
|
||||
2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */
|
||||
#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_LISTENER \
|
||||
64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */
|
||||
|
||||
void furi_hal_nfc_ll_set_fdt_listen(uint32_t cycles);
|
||||
|
||||
/* RFAL Frame Delay Time (FDT) Poll default values */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER \
|
||||
6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_T1T_POLLER \
|
||||
384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_NFCB_POLLER \
|
||||
6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_NFCF_POLLER 6800U /*!< FDTF,POLL,MIN Digital 2.1 8.7.3 & B.4 */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER 4192U /*!< FDTV,POLL Digital 2.1 9.7.3.1 & B.5 */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER 1790U /*!< FDT Max */
|
||||
#define FURI_HAL_NFC_LL_FDT_POLL_AP2P_POLLER \
|
||||
0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */
|
||||
|
||||
void furi_hal_nfc_ll_set_fdt_poll(uint32_t FDTPoll);
|
||||
|
||||
void furi_hal_nfc_ll_txrx_on();
|
||||
|
||||
void furi_hal_nfc_ll_txrx_off();
|
||||
|
||||
FuriHalNfcReturn furi_hal_nfc_ll_txrx(
|
||||
uint8_t* txBuf,
|
||||
uint16_t txBufLen,
|
||||
uint8_t* rxBuf,
|
||||
uint16_t rxBufLen,
|
||||
uint16_t* actLen,
|
||||
uint32_t flags,
|
||||
uint32_t fwt);
|
||||
|
||||
FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits(
|
||||
uint8_t* txBuf,
|
||||
uint16_t txBufLen,
|
||||
uint8_t* rxBuf,
|
||||
uint16_t rxBufLen,
|
||||
uint16_t* actLen,
|
||||
uint32_t flags,
|
||||
uint32_t fwt);
|
||||
|
||||
void furi_hal_nfc_ll_poll();
|
||||
|
||||
void furi_hal_nfc_field_detect_start();
|
||||
|
||||
bool furi_hal_nfc_field_is_present();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <furi_hal_nfc_i.h>
|
||||
|
||||
FuriHalNfcEventInternal* furi_hal_nfc_event = NULL;
|
||||
|
||||
void furi_hal_nfc_event_init() {
|
||||
furi_hal_nfc_event = malloc(sizeof(FuriHalNfcEventInternal));
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_event_start() {
|
||||
furi_assert(furi_hal_nfc_event);
|
||||
|
||||
furi_hal_nfc_event->thread = furi_thread_get_current_id();
|
||||
furi_thread_flags_clear(FURI_HAL_NFC_EVENT_INTERNAL_ALL);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_event_stop() {
|
||||
furi_assert(furi_hal_nfc_event);
|
||||
|
||||
furi_hal_nfc_event->thread = NULL;
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
void furi_hal_nfc_event_set(FuriHalNfcEventInternalType event) {
|
||||
furi_assert(furi_hal_nfc_event);
|
||||
furi_assert(furi_hal_nfc_event->thread);
|
||||
|
||||
furi_thread_flags_set(furi_hal_nfc_event->thread, event);
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_abort() {
|
||||
furi_hal_nfc_event_set(FuriHalNfcEventInternalTypeAbort);
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms) {
|
||||
furi_assert(furi_hal_nfc_event);
|
||||
furi_assert(furi_hal_nfc_event->thread);
|
||||
|
||||
FuriHalNfcEvent event = 0;
|
||||
uint32_t event_timeout = timeout_ms == FURI_HAL_NFC_EVENT_WAIT_FOREVER ? FuriWaitForever :
|
||||
timeout_ms;
|
||||
uint32_t event_flag =
|
||||
furi_thread_flags_wait(FURI_HAL_NFC_EVENT_INTERNAL_ALL, FuriFlagWaitAny, event_timeout);
|
||||
if(event_flag != (unsigned)FuriFlagErrorTimeout) {
|
||||
if(event_flag & FuriHalNfcEventInternalTypeIrq) {
|
||||
furi_thread_flags_clear(FuriHalNfcEventInternalTypeIrq);
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
uint32_t irq = furi_hal_nfc_get_irq(handle);
|
||||
if(irq & ST25R3916_IRQ_MASK_OSC) {
|
||||
event |= FuriHalNfcEventOscOn;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_TXE) {
|
||||
event |= FuriHalNfcEventTxEnd;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_RXS) {
|
||||
event |= FuriHalNfcEventRxStart;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_RXE) {
|
||||
event |= FuriHalNfcEventRxEnd;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_COL) {
|
||||
event |= FuriHalNfcEventCollision;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_EON) {
|
||||
event |= FuriHalNfcEventFieldOn;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_EOF) {
|
||||
event |= FuriHalNfcEventFieldOff;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_WU_A) {
|
||||
event |= FuriHalNfcEventListenerActive;
|
||||
}
|
||||
if(irq & ST25R3916_IRQ_MASK_WU_A_X) {
|
||||
event |= FuriHalNfcEventListenerActive;
|
||||
}
|
||||
}
|
||||
if(event_flag & FuriHalNfcEventInternalTypeTimerFwtExpired) {
|
||||
event |= FuriHalNfcEventTimerFwtExpired;
|
||||
furi_thread_flags_clear(FuriHalNfcEventInternalTypeTimerFwtExpired);
|
||||
}
|
||||
if(event_flag & FuriHalNfcEventInternalTypeTimerBlockTxExpired) {
|
||||
event |= FuriHalNfcEventTimerBlockTxExpired;
|
||||
furi_thread_flags_clear(FuriHalNfcEventInternalTypeTimerBlockTxExpired);
|
||||
}
|
||||
if(event_flag & FuriHalNfcEventInternalTypeAbort) {
|
||||
event |= FuriHalNfcEventAbortRequest;
|
||||
furi_thread_flags_clear(FuriHalNfcEventInternalTypeAbort);
|
||||
}
|
||||
} else {
|
||||
event = FuriHalNfcEventTimeout;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_event_wait_for_specific_irq(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint32_t mask,
|
||||
uint32_t timeout_ms) {
|
||||
furi_assert(furi_hal_nfc_event);
|
||||
furi_assert(furi_hal_nfc_event->thread);
|
||||
|
||||
bool irq_received = false;
|
||||
uint32_t event_flag =
|
||||
furi_thread_flags_wait(FuriHalNfcEventInternalTypeIrq, FuriFlagWaitAny, timeout_ms);
|
||||
if(event_flag == FuriHalNfcEventInternalTypeIrq) {
|
||||
uint32_t irq = furi_hal_nfc_get_irq(handle);
|
||||
irq_received = ((irq & mask) == mask);
|
||||
furi_thread_flags_clear(FuriHalNfcEventInternalTypeIrq);
|
||||
}
|
||||
|
||||
return irq_received;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
#include "furi_hal_nfc_tech_i.h"
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) {
|
||||
// Enable Felica mode, AM modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_MODE,
|
||||
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
||||
ST25R3916_REG_MODE_om_felica | ST25R3916_REG_MODE_tr_am_am);
|
||||
|
||||
// 10% ASK modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_TX_DRIVER,
|
||||
ST25R3916_REG_TX_DRIVER_am_mod_mask,
|
||||
ST25R3916_REG_TX_DRIVER_am_mod_10percent);
|
||||
|
||||
// Use regulator AM, resistive AM disabled
|
||||
st25r3916_clear_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_AUX_MOD,
|
||||
ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am);
|
||||
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_BIT_RATE,
|
||||
ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask,
|
||||
ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212);
|
||||
|
||||
// Receive configuration
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_RX_CONF1,
|
||||
ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz);
|
||||
|
||||
// Correlator setup
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_CORR_CONF1,
|
||||
ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 |
|
||||
ST25R3916_REG_CORR_CONF1_corr_s3);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* handle) {
|
||||
UNUSED(handle);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
const FuriHalNfcTechBase furi_hal_nfc_felica = {
|
||||
.poller =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC,
|
||||
.fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_felica_poller_init,
|
||||
.deinit = furi_hal_nfc_felica_poller_deinit,
|
||||
.wait_event = furi_hal_nfc_wait_event_common,
|
||||
.tx = furi_hal_nfc_poller_tx_common,
|
||||
.rx = furi_hal_nfc_common_fifo_rx,
|
||||
},
|
||||
|
||||
.listener = {},
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file furi_hal_nfc_i.h
|
||||
* @brief NFC HAL library (private definitions).
|
||||
*
|
||||
* This file is an implementation detail. It must not be included in
|
||||
* any public API-related headers.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_spi.h>
|
||||
|
||||
#include <drivers/st25r3916.h>
|
||||
#include <drivers/st25r3916_reg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Common frame delay time compensation for pollers. */
|
||||
#define FURI_HAL_NFC_POLLER_FDT_COMP_FC (-500)
|
||||
/** @brief Common frame wait time compensation for pollers. */
|
||||
#define FURI_HAL_NFC_POLLER_FWT_COMP_FC (FURI_HAL_NFC_POLLER_FDT_COMP_FC)
|
||||
|
||||
/**
|
||||
* @brief Enumeration containing bitmask values for NFC HAL internal events.
|
||||
*/
|
||||
typedef enum {
|
||||
FuriHalNfcEventInternalTypeAbort = (1U << 0), /**< Abort waiting for hardware events. */
|
||||
FuriHalNfcEventInternalTypeIrq = (1U << 1), /**< NFC hardware interrupt has occurred. */
|
||||
FuriHalNfcEventInternalTypeTimerFwtExpired =
|
||||
(1U << 2), /**< Frame wait time timeout has expired. */
|
||||
FuriHalNfcEventInternalTypeTimerBlockTxExpired =
|
||||
(1U << 3), /**< Transmission block timeout has expired. */
|
||||
FuriHalNfcEventInternalTypeTransparentDataReceived =
|
||||
(1U << 4), /**< Data was received in transparent mode. */
|
||||
} FuriHalNfcEventInternalType;
|
||||
|
||||
/** @brief Special bitmask value of all internal events. */
|
||||
#define FURI_HAL_NFC_EVENT_INTERNAL_ALL \
|
||||
((FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeIrq | \
|
||||
FuriHalNfcEventInternalTypeTimerFwtExpired | \
|
||||
FuriHalNfcEventInternalTypeTimerBlockTxExpired | \
|
||||
FuriHalNfcEventInternalTypeTransparentDataReceived))
|
||||
|
||||
/**
|
||||
* @brief NFC HAL internal event structure.
|
||||
*/
|
||||
typedef struct {
|
||||
FuriThreadId thread; /**< Identifier of the thread that will be receiving events. */
|
||||
void* context; /**< Pointer to the user-provided context (will be passed to the event callback). */
|
||||
} FuriHalNfcEventInternal;
|
||||
|
||||
/**
|
||||
* @brief NFC HAL global state structure.
|
||||
*/
|
||||
typedef struct {
|
||||
FuriMutex* mutex; /**< Pointer to the mutex serving as global NFC HAL lock. */
|
||||
FuriHalNfcMode mode; /**< Currently selected operating mode. */
|
||||
FuriHalNfcTech tech; /**< Currently selected NFC technology. */
|
||||
} FuriHalNfc;
|
||||
|
||||
/**
|
||||
* @brief NFC HAL global state object declaration.
|
||||
*/
|
||||
extern FuriHalNfc furi_hal_nfc;
|
||||
|
||||
/**
|
||||
* @brief Initialise NFC HAL event system.
|
||||
*/
|
||||
void furi_hal_nfc_event_init();
|
||||
|
||||
/**
|
||||
* @brief Forcibly emit (a) particular internal event(s).
|
||||
*
|
||||
* @param[in] event bitmask of one or more events to be emitted.
|
||||
*/
|
||||
void furi_hal_nfc_event_set(FuriHalNfcEventInternalType event);
|
||||
|
||||
/**
|
||||
* @brief Initialise GPIO to generate an interrupt from the NFC hardware.
|
||||
*/
|
||||
void furi_hal_nfc_init_gpio_isr();
|
||||
|
||||
/**
|
||||
* @brief Disable interrupts from the NFC hardware.
|
||||
*/
|
||||
void furi_hal_nfc_deinit_gpio_isr();
|
||||
|
||||
/**
|
||||
* @brief Initialise all NFC timers.
|
||||
*/
|
||||
void furi_hal_nfc_timers_init();
|
||||
|
||||
/**
|
||||
* @brief Disable all NFC timers.
|
||||
*/
|
||||
void furi_hal_nfc_timers_deinit();
|
||||
|
||||
/**
|
||||
* @brief Get the interrupt bitmask from the NFC hardware.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @returns bitmask of zero or more occurred interrupts.
|
||||
*/
|
||||
uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Wait until a specified type of interrupt occurs.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @param[in] mask bitmask of one or more interrupts to wait for.
|
||||
* @param[in] timeout_ms maximum time to wait for an interrupt, in milliseconds.
|
||||
* @returns true if specified interrupt(s) have occured within timeout, false otherwise.
|
||||
*/
|
||||
bool furi_hal_nfc_event_wait_for_specific_irq(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint32_t mask,
|
||||
uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Wait for any event to occur.
|
||||
*
|
||||
* This function is common to all technologies.
|
||||
*
|
||||
* @param[in] timeout_ms maximum time to wait for an event, in milliseconds.
|
||||
* @returns bitmask of zero or more occurred events.
|
||||
*/
|
||||
FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Start reception in listener mode.
|
||||
*
|
||||
* This function is common to all technologies.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
FuriHalNfcError furi_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Transmit data using on-chip FIFO.
|
||||
*
|
||||
* This function is common to all technologies.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @param[in] tx_data pointer to a byte array containing the data to be transmitted.
|
||||
* @param[in] tx_bits transmit data size, in bits.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
FuriHalNfcError furi_hal_nfc_common_fifo_tx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits);
|
||||
|
||||
/**
|
||||
* @brief Receive data using on-chip FIFO.
|
||||
*
|
||||
* This function is common to all technologies.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @param[out] rx_data pointer to a byte array to be filled with received data.
|
||||
* @param[in] rx_data_size maximum received data size, in bytes.
|
||||
* @param[out] rx_bits pointer to the variable to contain the received data size, in bits.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
FuriHalNfcError furi_hal_nfc_common_fifo_rx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* rx_data,
|
||||
size_t rx_data_size,
|
||||
size_t* rx_bits);
|
||||
|
||||
/**
|
||||
* @brief Transmit data in poller mode.
|
||||
*
|
||||
* This function is common to all technologies.
|
||||
*
|
||||
* @param[in,out] handle pointer to the SPI handle associated with the NFC chip.
|
||||
* @param[in] tx_data pointer to a byte array containing the data to be transmitted.
|
||||
* @param[in] tx_bits transmit data size, in bits.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
FuriHalNfcError furi_hal_nfc_poller_tx_common(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
|
||||
#include <lib/drivers/st25r3916.h>
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
static void furi_hal_nfc_int_callback() {
|
||||
furi_hal_nfc_event_set(FuriHalNfcEventInternalTypeIrq);
|
||||
}
|
||||
|
||||
uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle) {
|
||||
uint32_t irq = 0;
|
||||
while(furi_hal_gpio_read_port_pin(gpio_nfc_irq_rfid_pull.port, gpio_nfc_irq_rfid_pull.pin)) {
|
||||
irq |= st25r3916_get_irq(handle);
|
||||
}
|
||||
return irq;
|
||||
}
|
||||
|
||||
void furi_hal_nfc_init_gpio_isr() {
|
||||
furi_hal_gpio_init(
|
||||
&gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, furi_hal_nfc_int_callback, NULL);
|
||||
furi_hal_gpio_enable_int_callback(&gpio_nfc_irq_rfid_pull);
|
||||
}
|
||||
|
||||
void furi_hal_nfc_deinit_gpio_isr() {
|
||||
furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_remove_int_callback(&gpio_nfc_irq_rfid_pull);
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
#include "furi_hal_nfc_tech_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
#include <digital_signal/presets/nfc/iso14443_3a_signal.h>
|
||||
|
||||
#define TAG "FuriHalIso14443a"
|
||||
|
||||
// Prevent FDT timer from starting
|
||||
#define FURI_HAL_NFC_ISO14443A_LISTENER_FDT_COMP_FC (INT32_MAX)
|
||||
|
||||
static Iso14443_3aSignal* iso14443_3a_signal = NULL;
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* handle) {
|
||||
// Common NFC-A settings, 106 kbps
|
||||
|
||||
// 1st stage zero = 600kHz, 3rd stage zero = 200 kHz
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_z600k);
|
||||
// AGC enabled, ratio 3:1, squelch after TX
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_RX_CONF2,
|
||||
ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m |
|
||||
ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn);
|
||||
// HF operation, full gain on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00);
|
||||
// No gain reduction on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00);
|
||||
// Correlator config
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_CORR_CONF1,
|
||||
ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s4 |
|
||||
ST25R3916_REG_CORR_CONF1_corr_s6);
|
||||
// Sleep mode disable, 424kHz mode off
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* handle) {
|
||||
// Enable ISO14443A mode, OOK modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_MODE,
|
||||
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
||||
ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook);
|
||||
|
||||
// Overshoot protection - is this necessary here?
|
||||
st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0xff, 0x40);
|
||||
st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0xff, 0x03);
|
||||
st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0xff, 0x40);
|
||||
st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0xff, 0x03);
|
||||
|
||||
return furi_hal_nfc_iso14443a_common_init(handle);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* handle) {
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_ISO14443A_NFC,
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par),
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off));
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* handle) {
|
||||
furi_check(iso14443_3a_signal == NULL);
|
||||
iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi);
|
||||
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_OP_CONTROL,
|
||||
ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en |
|
||||
ST25R3916_REG_OP_CONTROL_en_fd_auto_efd);
|
||||
st25r3916_write_reg(
|
||||
handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om0);
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_PASSIVE_TARGET,
|
||||
ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 |
|
||||
ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r);
|
||||
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02);
|
||||
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP);
|
||||
uint32_t interrupts =
|
||||
(ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS |
|
||||
ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC |
|
||||
ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE |
|
||||
ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A_X |
|
||||
ST25R3916_IRQ_MASK_WU_A);
|
||||
// Clear interrupts
|
||||
st25r3916_get_irq(handle);
|
||||
// Enable interrupts
|
||||
st25r3916_mask_irq(handle, ~interrupts);
|
||||
// Enable auto collision resolution
|
||||
st25r3916_clear_reg_bits(
|
||||
handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE);
|
||||
|
||||
return furi_hal_nfc_iso14443a_common_init(handle);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandle* handle) {
|
||||
UNUSED(handle);
|
||||
|
||||
if(iso14443_3a_signal) {
|
||||
iso14443_3a_signal_free(iso14443_3a_signal);
|
||||
iso14443_3a_signal = NULL;
|
||||
}
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcEvent furi_hal_nfc_iso14443_3a_listener_wait_event(uint32_t timeout_ms) {
|
||||
FuriHalNfcEvent event = furi_hal_nfc_wait_event_common(timeout_ms);
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
|
||||
if(event & FuriHalNfcEventListenerActive) {
|
||||
st25r3916_set_reg_bits(
|
||||
handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443a_poller_trx_short_frame(FuriHalNfcaShortFrame frame) {
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
|
||||
// Disable crc check
|
||||
st25r3916_set_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx);
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_ISO14443A_NFC,
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par),
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off));
|
||||
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, 0);
|
||||
uint32_t interrupts =
|
||||
(ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS |
|
||||
ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC |
|
||||
ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE);
|
||||
// Clear interrupts
|
||||
st25r3916_get_irq(handle);
|
||||
// Enable interrupts
|
||||
st25r3916_mask_irq(handle, ~interrupts);
|
||||
if(frame == FuriHalNfcaShortFrameAllReq) {
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_REQA);
|
||||
} else {
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WUPA);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits) {
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
// No anticollision is supported in this version of library
|
||||
error = furi_hal_nfc_poller_tx(tx_data, tx_bits);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError
|
||||
furi_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) {
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
UNUSED(rx_data);
|
||||
UNUSED(rx_bits);
|
||||
UNUSED(rx_data_size);
|
||||
|
||||
error = furi_hal_nfc_poller_rx(rx_data, rx_data_size, rx_bits);
|
||||
// No anticollision is supported in this version of library
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError
|
||||
furi_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits) {
|
||||
furi_assert(tx_data);
|
||||
|
||||
FuriHalNfcError err = FuriHalNfcErrorNone;
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
|
||||
// Prepare tx
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO);
|
||||
st25r3916_clear_reg_bits(
|
||||
handle, ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv);
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_ISO14443A_NFC,
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par),
|
||||
(ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par));
|
||||
uint32_t interrupts =
|
||||
(ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS |
|
||||
ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC |
|
||||
ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE);
|
||||
// Clear interrupts
|
||||
st25r3916_get_irq(handle);
|
||||
// Enable interrupts
|
||||
st25r3916_mask_irq(handle, ~interrupts);
|
||||
|
||||
st25r3916_write_fifo(handle, tx_data, tx_bits);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC);
|
||||
return err;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data(
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
furi_assert(uid);
|
||||
furi_assert(atqa);
|
||||
UNUSED(uid_len);
|
||||
UNUSED(sak);
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
|
||||
// Set 4 or 7 bytes UID
|
||||
if(uid_len == 4) {
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_AUX,
|
||||
ST25R3916_REG_AUX_nfc_id_mask,
|
||||
ST25R3916_REG_AUX_nfc_id_4bytes);
|
||||
} else {
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_AUX,
|
||||
ST25R3916_REG_AUX_nfc_id_mask,
|
||||
ST25R3916_REG_AUX_nfc_id_7bytes);
|
||||
}
|
||||
// Write PT Memory
|
||||
uint8_t pt_memory[15] = {};
|
||||
memcpy(pt_memory, uid, uid_len);
|
||||
pt_memory[10] = atqa[0];
|
||||
pt_memory[11] = atqa[1];
|
||||
if(uid_len == 4) {
|
||||
pt_memory[12] = sak & ~0x04;
|
||||
} else {
|
||||
pt_memory[12] = 0x04;
|
||||
}
|
||||
pt_memory[13] = sak & ~0x04;
|
||||
pt_memory[14] = sak & ~0x04;
|
||||
|
||||
st25r3916_write_pta_mem(handle, pt_memory, sizeof(pt_memory));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso4443a_listener_tx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits) {
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
|
||||
do {
|
||||
error = furi_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits);
|
||||
if(error != FuriHalNfcErrorNone) break;
|
||||
|
||||
bool tx_end = furi_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_TXE, 10);
|
||||
if(!tx_end) {
|
||||
error = FuriHalNfcErrorCommunicationTimeout;
|
||||
break;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity(
|
||||
const uint8_t* tx_data,
|
||||
const uint8_t* tx_parity,
|
||||
size_t tx_bits) {
|
||||
furi_assert(tx_data);
|
||||
furi_assert(tx_parity);
|
||||
|
||||
furi_assert(iso14443_3a_signal);
|
||||
|
||||
FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc;
|
||||
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE);
|
||||
// Reconfigure gpio for Transparent mode
|
||||
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
|
||||
|
||||
// Send signal
|
||||
iso14443_3a_signal_tx(iso14443_3a_signal, tx_data, tx_parity, tx_bits);
|
||||
|
||||
// Exit transparent mode
|
||||
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||
|
||||
// Configure gpio back to SPI and exit transparent
|
||||
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_sleep(FuriHalSpiBusHandle* handle) {
|
||||
// Enable auto collision resolution
|
||||
st25r3916_clear_reg_bits(
|
||||
handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SLEEP);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_idle(FuriHalSpiBusHandle* handle) {
|
||||
// Enable auto collision resolution
|
||||
st25r3916_clear_reg_bits(
|
||||
handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP);
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
const FuriHalNfcTechBase furi_hal_nfc_iso14443a = {
|
||||
.poller =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC,
|
||||
.fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_iso14443a_poller_init,
|
||||
.deinit = furi_hal_nfc_iso14443a_poller_deinit,
|
||||
.wait_event = furi_hal_nfc_wait_event_common,
|
||||
.tx = furi_hal_nfc_poller_tx_common,
|
||||
.rx = furi_hal_nfc_common_fifo_rx,
|
||||
},
|
||||
|
||||
.listener =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_ISO14443A_LISTENER_FDT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_iso14443a_listener_init,
|
||||
.deinit = furi_hal_nfc_iso14443a_listener_deinit,
|
||||
.wait_event = furi_hal_nfc_iso14443_3a_listener_wait_event,
|
||||
.tx = furi_hal_nfc_iso4443a_listener_tx,
|
||||
.rx = furi_hal_nfc_common_fifo_rx,
|
||||
.sleep = furi_hal_nfc_iso14443_3a_listener_sleep,
|
||||
.idle = furi_hal_nfc_iso14443_3a_listener_idle,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
#include "furi_hal_nfc_tech_i.h"
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) {
|
||||
// Common NFC-B settings, 106kbps
|
||||
|
||||
// 1st stage zero = 60kHz, 3rd stage zero = 200 kHz
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_h200);
|
||||
|
||||
// Enable AGC
|
||||
// AGC Ratio 6
|
||||
// AGC algorithm with RESET (recommended for ISO14443-B)
|
||||
// AGC operation during complete receive period
|
||||
// Squelch ratio 6/3 (recommended for ISO14443-B)
|
||||
// Squelch automatic activation on TX end
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_RX_CONF2,
|
||||
ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_alg |
|
||||
ST25R3916_REG_RX_CONF2_agc_m | ST25R3916_REG_RX_CONF2_agc_en |
|
||||
ST25R3916_REG_RX_CONF2_pulz_61 | ST25R3916_REG_RX_CONF2_sqm_dyn);
|
||||
|
||||
// HF operation, full gain on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00);
|
||||
// No gain reduction on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00);
|
||||
|
||||
// Subcarrier end detector enabled
|
||||
// Subcarrier end detection level = 66%
|
||||
// BPSK start 33 pilot pulses
|
||||
// AM & PM summation before digitizing on
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_CORR_CONF1,
|
||||
ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 |
|
||||
ST25R3916_REG_CORR_CONF1_corr_s3 | ST25R3916_REG_CORR_CONF1_corr_s4);
|
||||
// Sleep mode disable, 424kHz mode off
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* handle) {
|
||||
// Enable ISO14443B mode, AM modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_MODE,
|
||||
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
||||
ST25R3916_REG_MODE_om_iso14443b | ST25R3916_REG_MODE_tr_am_am);
|
||||
|
||||
// 10% ASK modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_TX_DRIVER,
|
||||
ST25R3916_REG_TX_DRIVER_am_mod_mask,
|
||||
ST25R3916_REG_TX_DRIVER_am_mod_10percent);
|
||||
|
||||
// Use regulator AM, resistive AM disabled
|
||||
st25r3916_clear_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_AUX_MOD,
|
||||
ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am);
|
||||
|
||||
// EGT = 0 etu
|
||||
// SOF = 10 etu LOW + 2 etu HIGH
|
||||
// EOF = 10 etu
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_ISO14443B_1,
|
||||
ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask |
|
||||
ST25R3916_REG_ISO14443B_1_eof,
|
||||
(0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu |
|
||||
ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu);
|
||||
|
||||
// TR1 = 80 / fs
|
||||
// B' mode off (no_sof & no_eof = 0)
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_ISO14443B_2,
|
||||
ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof |
|
||||
ST25R3916_REG_ISO14443B_2_no_eof,
|
||||
ST25R3916_REG_ISO14443B_2_tr1_80fs80fs);
|
||||
|
||||
return furi_hal_nfc_iso14443b_common_init(handle);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso14443b_poller_deinit(FuriHalSpiBusHandle* handle) {
|
||||
UNUSED(handle);
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
const FuriHalNfcTechBase furi_hal_nfc_iso14443b = {
|
||||
.poller =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC,
|
||||
.fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_iso14443b_poller_init,
|
||||
.deinit = furi_hal_nfc_iso14443b_poller_deinit,
|
||||
.wait_event = furi_hal_nfc_wait_event_common,
|
||||
.tx = furi_hal_nfc_poller_tx_common,
|
||||
.rx = furi_hal_nfc_common_fifo_rx,
|
||||
},
|
||||
|
||||
.listener = {},
|
||||
};
|
||||
@@ -0,0 +1,463 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
#include "furi_hal_nfc_tech_i.h"
|
||||
|
||||
#include <digital_signal/presets/nfc/iso15693_signal.h>
|
||||
#include <signal_reader/parsers/iso15693/iso15693_parser.h>
|
||||
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
#define FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE (1024U)
|
||||
#define FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE (64)
|
||||
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE (5)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_EOF_SIZE (5)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_SOF_MASK (0x1FU)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN (0x17U)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN (0x1DU)
|
||||
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK (0x03U)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_0 (0x01U)
|
||||
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_1 (0x02U)
|
||||
|
||||
// Derived experimentally
|
||||
#define FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC (-1300)
|
||||
#define FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC (2850)
|
||||
|
||||
#define BITS_IN_BYTE (8U)
|
||||
|
||||
#define TAG "FuriHalIso15693"
|
||||
|
||||
typedef struct {
|
||||
Iso15693Signal* signal;
|
||||
Iso15693Parser* parser;
|
||||
} FuriHalNfcIso15693Listener;
|
||||
|
||||
typedef struct {
|
||||
// 4 bits per data bit on transmit
|
||||
uint8_t fifo_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 4];
|
||||
size_t fifo_buf_bits;
|
||||
uint8_t frame_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 2];
|
||||
size_t frame_buf_bits;
|
||||
} FuriHalNfcIso15693Poller;
|
||||
|
||||
static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener = NULL;
|
||||
static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller = NULL;
|
||||
|
||||
static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener_alloc() {
|
||||
FuriHalNfcIso15693Listener* instance = malloc(sizeof(FuriHalNfcIso15693Listener));
|
||||
|
||||
instance->signal = iso15693_signal_alloc(&gpio_spi_r_mosi);
|
||||
instance->parser =
|
||||
iso15693_parser_alloc(&gpio_spi_r_miso, FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_iso15693_listener_free(FuriHalNfcIso15693Listener* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
iso15693_signal_free(instance->signal);
|
||||
iso15693_parser_free(instance->parser);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller_alloc() {
|
||||
FuriHalNfcIso15693Poller* instance = malloc(sizeof(FuriHalNfcIso15693Poller));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_iso15693_poller_free(FuriHalNfcIso15693Poller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) {
|
||||
// Common NFC-V settings, 26.48 kbps
|
||||
|
||||
// 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_RX_CONF1,
|
||||
ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 |
|
||||
ST25R3916_REG_RX_CONF1_lp_600khz);
|
||||
|
||||
// Enable AGC
|
||||
// AGC Ratio 6
|
||||
// AGC algorithm with RESET (recommended for ISO15693)
|
||||
// AGC operation during complete receive period
|
||||
// Squelch automatic activation on TX end
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_RX_CONF2,
|
||||
ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m |
|
||||
ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn);
|
||||
|
||||
// HF operation, full gain on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00);
|
||||
// No gain reduction on AM and PM channels
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00);
|
||||
|
||||
// Collision detection level 53%
|
||||
// AM & PM summation before digitizing on
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_CORR_CONF1,
|
||||
ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 |
|
||||
ST25R3916_REG_CORR_CONF1_corr_s4);
|
||||
// 424 kHz subcarrier stream mode on
|
||||
st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8);
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) {
|
||||
furi_assert(furi_hal_nfc_iso15693_poller == NULL);
|
||||
|
||||
furi_hal_nfc_iso15693_poller = furi_hal_nfc_iso15693_poller_alloc();
|
||||
|
||||
// Enable Subcarrier Stream mode, OOK modulation
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_MODE,
|
||||
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
||||
ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook);
|
||||
|
||||
// Subcarrier 424 kHz mode
|
||||
// 8 sub-carrier pulses in report period
|
||||
st25r3916_write_reg(
|
||||
handle,
|
||||
ST25R3916_REG_STREAM_MODE,
|
||||
ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 |
|
||||
ST25R3916_REG_STREAM_MODE_scp_8pulses);
|
||||
|
||||
// Use regulator AM, resistive AM disabled
|
||||
st25r3916_clear_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_AUX_MOD,
|
||||
ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am);
|
||||
|
||||
return furi_hal_nfc_iso15693_common_init(handle);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) {
|
||||
UNUSED(handle);
|
||||
furi_assert(furi_hal_nfc_iso15693_poller);
|
||||
|
||||
furi_hal_nfc_iso15693_poller_free(furi_hal_nfc_iso15693_poller);
|
||||
furi_hal_nfc_iso15693_poller = NULL;
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static void iso15693_3_poller_encode_frame(
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits,
|
||||
uint8_t* frame_buf,
|
||||
size_t frame_buf_size,
|
||||
size_t* frame_buf_bits) {
|
||||
static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80};
|
||||
size_t frame_buf_size_calc = (tx_bits / 2) + 2;
|
||||
furi_assert(frame_buf_size >= frame_buf_size_calc);
|
||||
|
||||
// Add SOF 1 out of 4
|
||||
frame_buf[0] = 0x21;
|
||||
|
||||
size_t byte_pos = 1;
|
||||
for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; ++i) {
|
||||
for(size_t j = 0; j < BITS_IN_BYTE; j += (BITS_IN_BYTE) / 4) {
|
||||
const uint8_t bit_pair = (tx_data[i] >> j) & 0x03;
|
||||
frame_buf[byte_pos++] = bit_patterns_1_out_of_4[bit_pair];
|
||||
}
|
||||
}
|
||||
// Add EOF
|
||||
frame_buf[byte_pos++] = 0x04;
|
||||
*frame_buf_bits = byte_pos * BITS_IN_BYTE;
|
||||
}
|
||||
|
||||
static FuriHalNfcError iso15693_3_poller_decode_frame(
|
||||
const uint8_t* buf,
|
||||
size_t buf_bits,
|
||||
uint8_t* buf_decoded,
|
||||
size_t buf_decoded_size,
|
||||
size_t* buf_decoded_bits) {
|
||||
FuriHalNfcError ret = FuriHalNfcErrorDataFormat;
|
||||
size_t bit_pos = 0;
|
||||
memset(buf_decoded, 0, buf_decoded_size);
|
||||
|
||||
do {
|
||||
if(buf_bits == 0) break;
|
||||
// Check SOF
|
||||
if((buf[0] & FURI_HAL_NFC_ISO15693_RESP_SOF_MASK) !=
|
||||
FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN)
|
||||
break;
|
||||
|
||||
if(buf_bits == BITS_IN_BYTE) {
|
||||
ret = FuriHalNfcErrorIncompleteFrame;
|
||||
break;
|
||||
}
|
||||
|
||||
// 2 response bits = 1 data bit
|
||||
for(uint32_t i = FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE;
|
||||
i < buf_bits - FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE;
|
||||
i += BITS_IN_BYTE / 4) {
|
||||
const size_t byte_index = i / BITS_IN_BYTE;
|
||||
const size_t bit_offset = i % BITS_IN_BYTE;
|
||||
const uint8_t resp_byte = (buf[byte_index] >> bit_offset) |
|
||||
(buf[byte_index + 1] << (BITS_IN_BYTE - bit_offset));
|
||||
|
||||
// Check EOF
|
||||
if(resp_byte == FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN) {
|
||||
ret = FuriHalNfcErrorNone;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t bit_pattern = resp_byte & FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK;
|
||||
|
||||
if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_0) {
|
||||
bit_pos++;
|
||||
} else if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_1) {
|
||||
buf_decoded[bit_pos / BITS_IN_BYTE] |= 1 << (bit_pos % BITS_IN_BYTE);
|
||||
bit_pos++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if(bit_pos / BITS_IN_BYTE > buf_decoded_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
if(ret == FuriHalNfcErrorNone) {
|
||||
*buf_decoded_bits = bit_pos;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_poller_tx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits) {
|
||||
FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller;
|
||||
iso15693_3_poller_encode_frame(
|
||||
tx_data,
|
||||
tx_bits,
|
||||
instance->frame_buf,
|
||||
sizeof(instance->frame_buf),
|
||||
&instance->frame_buf_bits);
|
||||
return furi_hal_nfc_poller_tx_common(handle, instance->frame_buf, instance->frame_buf_bits);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_poller_rx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* rx_data,
|
||||
size_t rx_data_size,
|
||||
size_t* rx_bits) {
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller;
|
||||
|
||||
do {
|
||||
error = furi_hal_nfc_common_fifo_rx(
|
||||
handle, instance->fifo_buf, sizeof(instance->fifo_buf), &instance->fifo_buf_bits);
|
||||
if(error != FuriHalNfcErrorNone) break;
|
||||
|
||||
error = iso15693_3_poller_decode_frame(
|
||||
instance->fifo_buf,
|
||||
instance->fifo_buf_bits,
|
||||
instance->frame_buf,
|
||||
sizeof(instance->frame_buf),
|
||||
&instance->frame_buf_bits);
|
||||
if(error != FuriHalNfcErrorNone) break;
|
||||
|
||||
if(rx_data_size < instance->frame_buf_bits / BITS_IN_BYTE) {
|
||||
error = FuriHalNfcErrorBufferOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(rx_data, instance->frame_buf, instance->frame_buf_bits / BITS_IN_BYTE);
|
||||
*rx_bits = instance->frame_buf_bits;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) {
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE);
|
||||
|
||||
furi_hal_spi_bus_handle_deinit(handle);
|
||||
furi_hal_nfc_deinit_gpio_isr();
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) {
|
||||
// Configure gpio back to SPI and exit transparent mode
|
||||
furi_hal_nfc_init_gpio_isr();
|
||||
furi_hal_spi_bus_handle_init(handle);
|
||||
|
||||
st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA);
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) {
|
||||
furi_assert(furi_hal_nfc_iso15693_listener == NULL);
|
||||
|
||||
furi_hal_nfc_iso15693_listener = furi_hal_nfc_iso15693_listener_alloc();
|
||||
|
||||
// Set default operation mode
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_MODE,
|
||||
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
||||
ST25R3916_REG_MODE_om_targ_nfca | ST25R3916_REG_MODE_tr_am_ook);
|
||||
|
||||
st25r3916_change_reg_bits(
|
||||
handle,
|
||||
ST25R3916_REG_OP_CONTROL,
|
||||
ST25R3916_REG_OP_CONTROL_rx_en,
|
||||
ST25R3916_REG_OP_CONTROL_rx_en);
|
||||
|
||||
// Enable passive target mode
|
||||
st25r3916_change_reg_bits(
|
||||
handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ, ST25R3916_REG_MODE_targ_targ);
|
||||
|
||||
FuriHalNfcError error = furi_hal_nfc_iso15693_common_init(handle);
|
||||
|
||||
furi_hal_nfc_iso15693_listener_transparent_mode_enter(handle);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) {
|
||||
furi_assert(furi_hal_nfc_iso15693_listener);
|
||||
|
||||
furi_hal_nfc_iso15693_listener_transparent_mode_exit(handle);
|
||||
|
||||
furi_hal_nfc_iso15693_listener_free(furi_hal_nfc_iso15693_listener);
|
||||
furi_hal_nfc_iso15693_listener = NULL;
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError
|
||||
furi_hal_nfc_iso15693_listener_tx_transparent(const uint8_t* data, size_t data_size) {
|
||||
iso15693_signal_tx(
|
||||
furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi, data, data_size);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
if(event == Iso15693ParserEventDataReceived) {
|
||||
FuriThreadId thread_id = context;
|
||||
furi_thread_flags_set(thread_id, FuriHalNfcEventInternalTypeTransparentDataReceived);
|
||||
}
|
||||
}
|
||||
|
||||
static FuriHalNfcEvent furi_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) {
|
||||
FuriHalNfcEvent event = 0;
|
||||
|
||||
FuriThreadId thread_id = furi_thread_get_current_id();
|
||||
iso15693_parser_start(
|
||||
furi_hal_nfc_iso15693_listener->parser, furi_hal_nfc_iso15693_parser_callback, thread_id);
|
||||
|
||||
while(true) {
|
||||
uint32_t flag = furi_thread_flags_wait(
|
||||
FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeTransparentDataReceived,
|
||||
FuriFlagWaitAny,
|
||||
timeout_ms);
|
||||
furi_thread_flags_clear(flag);
|
||||
|
||||
if(flag & FuriHalNfcEventInternalTypeAbort) {
|
||||
event = FuriHalNfcEventAbortRequest;
|
||||
break;
|
||||
}
|
||||
if(flag & FuriHalNfcEventInternalTypeTransparentDataReceived) {
|
||||
if(iso15693_parser_run(furi_hal_nfc_iso15693_listener->parser)) {
|
||||
event = FuriHalNfcEventRxEnd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
iso15693_parser_stop(furi_hal_nfc_iso15693_listener->parser);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_listener_tx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* tx_data,
|
||||
size_t tx_bits) {
|
||||
UNUSED(handle);
|
||||
furi_assert(furi_hal_nfc_iso15693_listener);
|
||||
|
||||
FuriHalNfcError error = FuriHalNfcErrorNone;
|
||||
|
||||
error = furi_hal_nfc_iso15693_listener_tx_transparent(tx_data, tx_bits / BITS_IN_BYTE);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof() {
|
||||
iso15693_signal_tx_sof(furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* rx_data,
|
||||
size_t rx_data_size,
|
||||
size_t* rx_bits) {
|
||||
furi_assert(furi_hal_nfc_iso15693_listener);
|
||||
UNUSED(handle);
|
||||
|
||||
if(rx_data_size <
|
||||
iso15693_parser_get_data_size_bytes(furi_hal_nfc_iso15693_listener->parser)) {
|
||||
return FuriHalNfcErrorBufferOverflow;
|
||||
}
|
||||
|
||||
iso15693_parser_get_data(
|
||||
furi_hal_nfc_iso15693_listener->parser, rx_data, rx_data_size, rx_bits);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
FuriHalNfcError furi_hal_nfc_iso15693_listener_sleep(FuriHalSpiBusHandle* handle) {
|
||||
UNUSED(handle);
|
||||
|
||||
return FuriHalNfcErrorNone;
|
||||
}
|
||||
|
||||
const FuriHalNfcTechBase furi_hal_nfc_iso15693 = {
|
||||
.poller =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC,
|
||||
.fwt = FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_iso15693_poller_init,
|
||||
.deinit = furi_hal_nfc_iso15693_poller_deinit,
|
||||
.wait_event = furi_hal_nfc_wait_event_common,
|
||||
.tx = furi_hal_nfc_iso15693_poller_tx,
|
||||
.rx = furi_hal_nfc_iso15693_poller_rx,
|
||||
},
|
||||
|
||||
.listener =
|
||||
{
|
||||
.compensation =
|
||||
{
|
||||
.fdt = FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC,
|
||||
},
|
||||
.init = furi_hal_nfc_iso15693_listener_init,
|
||||
.deinit = furi_hal_nfc_iso15693_listener_deinit,
|
||||
.wait_event = furi_hal_nfc_iso15693_wait_event,
|
||||
.tx = furi_hal_nfc_iso15693_listener_tx,
|
||||
.rx = furi_hal_nfc_iso15693_listener_rx,
|
||||
.sleep = furi_hal_nfc_iso15693_listener_sleep,
|
||||
.idle = furi_hal_nfc_iso15693_listener_sleep,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @file furi_hal_nfc_tech_i.h
|
||||
* @brief NFC HAL technology-related private definitions.
|
||||
*
|
||||
* This file is an implementation detail. It must not be included in
|
||||
* any public API-related headers.
|
||||
*
|
||||
* This file is to be changed in an unlikely event of adding support
|
||||
* for a new NFC technology.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_spi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure the NFC chip for use with this technology.
|
||||
*
|
||||
* Used for init() and deinit() functions.
|
||||
*
|
||||
* @param[in,out] handle pointer to the NFC chip SPI handle.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Transmit data using technology-specific framing and timings.
|
||||
*
|
||||
* @param[in,out] handle pointer to the NFC chip SPI handle.
|
||||
* @param[in] tx_data pointer to a byte array containing the data to be transmitted.
|
||||
* @param[in] tx_bits transmit data size, in bits.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
typedef FuriHalNfcError (
|
||||
*FuriHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits);
|
||||
|
||||
/**
|
||||
* @brief Receive data using technology-specific framing and timings.
|
||||
*
|
||||
* @param[in,out] handle pointer to the NFC chip SPI handle.
|
||||
* @param[out] rx_data pointer to a byte array to be filled with received data.
|
||||
* @param[in] rx_data_size maximum received data length, in bytes.
|
||||
* @param[out] rx_bits pointer to a variable to contain received data length, in bits.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
typedef FuriHalNfcError (*FuriHalNfcRx)(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* rx_data,
|
||||
size_t rx_data_size,
|
||||
size_t* rx_bits);
|
||||
|
||||
/**
|
||||
* @brief Wait for an event using technology-specific method.
|
||||
*
|
||||
* @param[in] timeout_ms maximum time to wait, in milliseconds.
|
||||
* @return bitmask of occurred events.
|
||||
*/
|
||||
typedef FuriHalNfcEvent (*FuriHalNfcWaitEvent)(uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Go to sleep in listener mode.
|
||||
*
|
||||
* Puts the passive target logic into Sleep (Halt) state.
|
||||
*
|
||||
* @param[in,out] handle pointer to the NFC chip SPI handle.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Go to idle in listener mode.
|
||||
*
|
||||
* Puts the passive target logic into Sense (Idle) state.
|
||||
*
|
||||
* @param[in,out] handle pointer to the NFC chip SPI handle.
|
||||
* @returns FuriHalNfcErrorNone on success, any other error code on failure.
|
||||
*/
|
||||
typedef FuriHalNfcError (*FuriHalNfcIdle)(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Technology-specific compenstaion values for pollers.
|
||||
*
|
||||
* Timing compensations are needed due to execution delays not accounted for
|
||||
* in standards, they are usually found out experimentally.
|
||||
*
|
||||
* The compensation value will be subtracted from the respective timer running
|
||||
* time, so positive values shorten timeouts, and negative ones make them longer.
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */
|
||||
int32_t fwt; /**< Frame wait time compensaton, in carrier cycles. */
|
||||
} FuriHalNfcPollerCompensation;
|
||||
|
||||
/**
|
||||
* @brief Abstract technology-specific poller structure.
|
||||
*/
|
||||
typedef struct {
|
||||
FuriHalNfcPollerCompensation compensation; /**< Compensation values in poller mode. */
|
||||
FuriHalNfcChipConfig init; /**< Pointer to the init() function. */
|
||||
FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */
|
||||
FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */
|
||||
FuriHalNfcTx tx; /**< Pointer to the tx() function. */
|
||||
FuriHalNfcRx rx; /**< Pointer to the rx() function. */
|
||||
} FuriHalNfcTechPollerBase;
|
||||
|
||||
/**
|
||||
* @brief Technology-specific compenstaion values for listeners.
|
||||
*
|
||||
* Same considerations apply as with FuriHalNfcPollerCompensation.
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */
|
||||
} FuriHalNfcListenerCompensation;
|
||||
|
||||
/**
|
||||
* @brief Abstract technology-specific listener structure.
|
||||
*
|
||||
* If the listener operating mode is not supported for a particular
|
||||
* technology, fill this structure with zeroes.
|
||||
*/
|
||||
typedef struct {
|
||||
FuriHalNfcListenerCompensation compensation; /**< Compensation values in listener mode. */
|
||||
FuriHalNfcChipConfig init; /**< Pointer to the init() function. */
|
||||
FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */
|
||||
FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */
|
||||
FuriHalNfcTx tx; /**< Pointer to the tx() function. */
|
||||
FuriHalNfcRx rx; /**< Pointer to the rx() function. */
|
||||
FuriHalNfcSleep sleep; /**< Pointer to the sleep() function. */
|
||||
FuriHalNfcIdle idle; /**< Pointer to the idle() function. */
|
||||
} FuriHalNfcTechListenerBase;
|
||||
|
||||
/**
|
||||
* @brief Abstract NFC technology definition structure.
|
||||
*
|
||||
* Each concrete technology implementation must fill this structure
|
||||
* with its proper functions and constants.
|
||||
*/
|
||||
typedef struct {
|
||||
FuriHalNfcTechPollerBase poller; /**< Structure containing the poller definition. */
|
||||
FuriHalNfcTechListenerBase listener; /**< Structure containing the listener definition. */
|
||||
} FuriHalNfcTechBase;
|
||||
|
||||
/** @brief Technology declaration for ISO14443 (Type A). */
|
||||
extern const FuriHalNfcTechBase furi_hal_nfc_iso14443a;
|
||||
/** @brief Technology declaration for ISO14443 (Type B). */
|
||||
extern const FuriHalNfcTechBase furi_hal_nfc_iso14443b;
|
||||
/** @brief Technology declaration for ISO15693. */
|
||||
extern const FuriHalNfcTechBase furi_hal_nfc_iso15693;
|
||||
/** @brief Technology declaration for FeliCa. */
|
||||
extern const FuriHalNfcTechBase furi_hal_nfc_felica;
|
||||
/* Declare new tehcnologies here. */
|
||||
|
||||
/**
|
||||
* @brief Array of pointers to every supported technology.
|
||||
*
|
||||
* This variable is defined in furi_hal_nfc.c. It will need to be modified
|
||||
* in case when a new technology is to be added.
|
||||
*/
|
||||
extern const FuriHalNfcTechBase* furi_hal_nfc_tech[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,229 @@
|
||||
#include "furi_hal_nfc_i.h"
|
||||
#include "furi_hal_nfc_tech_i.h"
|
||||
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
#include <furi_hal_interrupt.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <furi_hal_bus.h>
|
||||
|
||||
#define TAG "FuriHalNfcTimer"
|
||||
|
||||
#define FURI_HAL_NFC_TIMER_US_IN_S (1000000UL)
|
||||
|
||||
/**
|
||||
* To enable timer debug output on GPIO, define the FURI_HAL_NFC_TIMER_DEBUG macro
|
||||
* Example: ./fbt --extra-define=FURI_HAL_NFC_TIMER_DEBUG
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
FuriHalNfcTimerFwt,
|
||||
FuriHalNfcTimerBlockTx,
|
||||
FuriHalNfcTimerCount,
|
||||
} FuriHalNfcTimer;
|
||||
|
||||
typedef struct {
|
||||
TIM_TypeDef* timer;
|
||||
FuriHalBus bus;
|
||||
uint32_t prescaler;
|
||||
uint32_t freq_khz;
|
||||
FuriHalNfcEventInternalType event;
|
||||
FuriHalInterruptId irq_id;
|
||||
IRQn_Type irq_type;
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
const GpioPin* pin;
|
||||
#endif
|
||||
} FuriHalNfcTimerConfig;
|
||||
|
||||
static const FuriHalNfcTimerConfig furi_hal_nfc_timers[FuriHalNfcTimerCount] = {
|
||||
[FuriHalNfcTimerFwt] =
|
||||
{
|
||||
.timer = TIM1,
|
||||
.bus = FuriHalBusTIM1,
|
||||
.event = FuriHalNfcEventInternalTypeTimerFwtExpired,
|
||||
.irq_id = FuriHalInterruptIdTim1UpTim16,
|
||||
.irq_type = TIM1_UP_TIM16_IRQn,
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
.pin = &gpio_ext_pa7,
|
||||
#endif
|
||||
},
|
||||
[FuriHalNfcTimerBlockTx] =
|
||||
{
|
||||
.timer = TIM17,
|
||||
.bus = FuriHalBusTIM17,
|
||||
.event = FuriHalNfcEventInternalTypeTimerBlockTxExpired,
|
||||
.irq_id = FuriHalInterruptIdTim1TrgComTim17,
|
||||
.irq_type = TIM1_TRG_COM_TIM17_IRQn,
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
.pin = &gpio_ext_pa6,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static void furi_hal_nfc_timer_irq_callback(void* context) {
|
||||
// Returning removed const-ness
|
||||
const FuriHalNfcTimerConfig* config = context;
|
||||
if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) {
|
||||
LL_TIM_ClearFlag_UPDATE(config->timer);
|
||||
furi_hal_nfc_event_set(config->event);
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
furi_hal_gpio_write(timer_config->pin, false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_init(FuriHalNfcTimer timer) {
|
||||
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];
|
||||
|
||||
furi_hal_bus_enable(config->bus);
|
||||
|
||||
LL_TIM_SetOnePulseMode(config->timer, LL_TIM_ONEPULSEMODE_SINGLE);
|
||||
LL_TIM_EnableUpdateEvent(config->timer);
|
||||
LL_TIM_SetCounterMode(config->timer, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetClockSource(config->timer, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
|
||||
furi_hal_interrupt_set_isr(
|
||||
config->irq_id,
|
||||
furi_hal_nfc_timer_irq_callback,
|
||||
// Warning: casting const-ness away
|
||||
(FuriHalNfcTimerConfig*)config);
|
||||
NVIC_SetPriority(config->irq_type, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(config->irq_type);
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
furi_hal_gpio_init(config->pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(config->pin, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_deinit(FuriHalNfcTimer timer) {
|
||||
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];
|
||||
|
||||
LL_TIM_ClearFlag_UPDATE(config->timer);
|
||||
furi_hal_interrupt_set_isr(config->irq_id, NULL, NULL);
|
||||
NVIC_DisableIRQ(config->irq_type);
|
||||
|
||||
if(furi_hal_bus_is_enabled(config->bus)) {
|
||||
furi_hal_bus_disable(config->bus);
|
||||
}
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
furi_hal_gpio_init_simple(config->pin, GpioModeAnalog);
|
||||
furi_hal_gpio_write(config->pin, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int32_t furi_hal_nfc_timer_get_compensation(FuriHalNfcTimer timer) {
|
||||
const FuriHalNfcTechBase* current_tech = furi_hal_nfc_tech[furi_hal_nfc.tech];
|
||||
|
||||
if(furi_hal_nfc.mode == FuriHalNfcModePoller) {
|
||||
const FuriHalNfcPollerCompensation* comp = ¤t_tech->poller.compensation;
|
||||
if(timer == FuriHalNfcTimerFwt)
|
||||
return comp->fwt;
|
||||
else if(timer == FuriHalNfcTimerBlockTx)
|
||||
return comp->fdt;
|
||||
|
||||
} else if(furi_hal_nfc.mode == FuriHalNfcModeListener) {
|
||||
const FuriHalNfcListenerCompensation* comp = ¤t_tech->listener.compensation;
|
||||
if(timer == FuriHalNfcTimerBlockTx) return comp->fdt;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool furi_hal_nfc_timer_is_running(FuriHalNfcTimer timer) {
|
||||
return LL_TIM_IsEnabledCounter(furi_hal_nfc_timers[timer].timer) != 0;
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_start_core_ticks(FuriHalNfcTimer timer, uint64_t core_ticks) {
|
||||
furi_check(!furi_hal_nfc_timer_is_running(timer));
|
||||
|
||||
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];
|
||||
furi_check(furi_hal_bus_is_enabled(config->bus));
|
||||
|
||||
const uint32_t prescaler = (core_ticks - 1) / UINT16_MAX;
|
||||
furi_check(prescaler <= UINT16_MAX);
|
||||
|
||||
const uint32_t arr_reg = core_ticks / (prescaler + 1);
|
||||
furi_check(arr_reg <= UINT16_MAX);
|
||||
|
||||
LL_TIM_DisableIT_UPDATE(config->timer);
|
||||
|
||||
LL_TIM_SetPrescaler(config->timer, prescaler);
|
||||
LL_TIM_SetAutoReload(config->timer, arr_reg);
|
||||
|
||||
LL_TIM_GenerateEvent_UPDATE(config->timer);
|
||||
while(!LL_TIM_IsActiveFlag_UPDATE(config->timer))
|
||||
;
|
||||
LL_TIM_ClearFlag_UPDATE(config->timer);
|
||||
|
||||
LL_TIM_EnableIT_UPDATE(config->timer);
|
||||
LL_TIM_EnableCounter(config->timer);
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
furi_hal_gpio_write(config->pin, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_start_us(FuriHalNfcTimer timer, uint32_t time_us) {
|
||||
furi_hal_nfc_timer_start_core_ticks(
|
||||
timer, SystemCoreClock / FURI_HAL_NFC_TIMER_US_IN_S * time_us);
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_start_fc(FuriHalNfcTimer timer, uint32_t time_fc) {
|
||||
const int32_t comp_fc = furi_hal_nfc_timer_get_compensation(timer);
|
||||
// Not starting the timer if the compensation value is greater than the requested delay
|
||||
if(comp_fc >= (int32_t)time_fc) return;
|
||||
|
||||
furi_hal_nfc_timer_start_core_ticks(
|
||||
timer, ((uint64_t)SystemCoreClock * (time_fc - comp_fc)) / FURI_HAL_NFC_CARRIER_HZ);
|
||||
}
|
||||
|
||||
static void furi_hal_nfc_timer_stop(FuriHalNfcTimer timer) {
|
||||
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];
|
||||
|
||||
LL_TIM_DisableIT_UPDATE(config->timer);
|
||||
LL_TIM_DisableCounter(config->timer);
|
||||
LL_TIM_SetCounter(config->timer, 0);
|
||||
LL_TIM_SetAutoReload(config->timer, 0);
|
||||
|
||||
if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) {
|
||||
LL_TIM_ClearFlag_UPDATE(config->timer);
|
||||
}
|
||||
#ifdef FURI_HAL_NFC_TIMER_DEBUG
|
||||
furi_hal_gpio_write(config->pin, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timers_init() {
|
||||
for(size_t i = 0; i < FuriHalNfcTimerCount; i++) {
|
||||
furi_hal_nfc_timer_init(i);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timers_deinit() {
|
||||
for(size_t i = 0; i < FuriHalNfcTimerCount; i++) {
|
||||
furi_hal_nfc_timer_deinit(i);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timer_fwt_start(uint32_t time_fc) {
|
||||
furi_hal_nfc_timer_start_fc(FuriHalNfcTimerFwt, time_fc);
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timer_fwt_stop() {
|
||||
furi_hal_nfc_timer_stop(FuriHalNfcTimerFwt);
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timer_block_tx_start(uint32_t time_fc) {
|
||||
furi_hal_nfc_timer_start_fc(FuriHalNfcTimerBlockTx, time_fc);
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timer_block_tx_start_us(uint32_t time_us) {
|
||||
furi_hal_nfc_timer_start_us(FuriHalNfcTimerBlockTx, time_us);
|
||||
}
|
||||
|
||||
void furi_hal_nfc_timer_block_tx_stop() {
|
||||
furi_hal_nfc_timer_stop(FuriHalNfcTimerBlockTx);
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_timer_block_tx_is_running() {
|
||||
return furi_hal_nfc_timer_is_running(FuriHalNfcTimerBlockTx);
|
||||
}
|
||||
@@ -30,9 +30,9 @@
|
||||
"nfc",
|
||||
"digital_signal",
|
||||
"pulse_reader",
|
||||
"signal_reader",
|
||||
"microtar",
|
||||
"usb_stm32",
|
||||
"st25rfal002",
|
||||
"infrared",
|
||||
"appframe",
|
||||
"assets",
|
||||
@@ -44,6 +44,7 @@
|
||||
"lfrfid",
|
||||
"flipper_application",
|
||||
"flipperformat",
|
||||
"toolbox"
|
||||
"toolbox",
|
||||
"flipper7"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user