diff --git a/examples/UnitUnified/NFCF/Detect/Detect.ino b/examples/UnitUnified/NFCF/Detect/Detect.ino new file mode 100644 index 0000000..8d44340 --- /dev/null +++ b/examples/UnitUnified/NFCF/Detect/Detect.ino @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for M5Cardputer-ADV with HackerCap + Detect NFC-A PICC +*/ +#include "main/Detect.cpp" diff --git a/examples/UnitUnified/NFCF/Detect/main/Detect.cpp b/examples/UnitUnified/NFCF/Detect/main/Detect.cpp new file mode 100644 index 0000000..2d2142a --- /dev/null +++ b/examples/UnitUnified/NFCF/Detect/main/Detect.cpp @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for M5Cardputer-ADV with HackerCap + Detect NFC-F PICC +*/ +#include +#include +#include +#include +#include + +using namespace m5::nfc; +using namespace m5::nfc::f; + +namespace { +auto& lcd = M5.Display; +m5::unit::UnitUnified Units; +m5::unit::CapST25R3916 cap; // ST25R3916 in the HackerCap +m5::unit::nfc::NFCLayerF nfc_f{cap}; + +} // namespace + +void setup() +{ + M5.begin(); + + auto cfg = cap.config(); + cfg.mode = NFC::F; + cap.config(cfg); + +#if 0 + //// M5GFX 0.2.15 NG! with HackerCap + auto board = M5.getBoard(); + if (board != lgfx::board_t::board_M5CardputerADV) { + M5_LOGE("This is NOT M5Cardputer-ADV %U/%XH", board, board); + lcd.fillScreen(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } +#endif + + if (!SPI.bus()) { + auto spi_sclk = M5.getPin(m5::pin_name_t::sd_spi_sclk); + auto spi_mosi = M5.getPin(m5::pin_name_t::sd_spi_mosi); + auto spi_miso = M5.getPin(m5::pin_name_t::sd_spi_miso); + M5_LOGI("getPin: %d,%d,%d", spi_sclk, spi_mosi, spi_miso); + SPI.begin(spi_sclk, spi_miso, spi_mosi /* SS is shared SD, CC1101, ST25R3916 */); + } + + delay(1000); + + SPISettings settings = {10000000, MSBFIRST, SPI_MODE1}; + if (!Units.add(cap, SPI, settings) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.fillScreen(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } + + M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("%s", Units.debugInfo().c_str()); + + if (lcd.width() < lcd.height()) { + lcd.setRotation(1); + } + lcd.setFont(&fonts::Font0); + lcd.fillScreen(0); + + // + // cap.dumpRegister(); + // +} + +void loop() +{ + M5.update(); + Units.update(); + + std::vector piccs; + + // if (nfc_f.detect(piccs)) { + if (nfc_f.detect(piccs, 0x0003)) { + M5.Speaker.tone(3000, 10); + M5.Log.printf("%zu PICC\n", piccs.size()); + for (auto&& picc : piccs) { + M5.Log.printf(" %s:%s %02X\n", picc.idmAsString().c_str(), picc.pmmAsString().c_str(), picc.format); + } + } +} diff --git a/platformio.ini b/platformio.ini index e9a2ffe..cd54312 100644 --- a/platformio.ini +++ b/platformio.ini @@ -141,3 +141,13 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCA/NDEF> build_flags = ${option_release.build_flags} ${CardputerADV.build_flags} + +; NFC-F +[env:CapHacker_NFCF_Detect_CardputerADV_Arduino_latest] +extends=CardputerADV, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCF/Detect> +build_flags = ${option_release.build_flags} + ${CardputerADV.build_flags} + + + diff --git a/src/M5UnitUnifiedNFC.hpp b/src/M5UnitUnifiedNFC.hpp index f8da636..0cd8602 100644 --- a/src/M5UnitUnifiedNFC.hpp +++ b/src/M5UnitUnifiedNFC.hpp @@ -22,6 +22,7 @@ #include "unit/unit_ST25R3916.hpp" #include "nfc/layer/nfc_layer_a.hpp" +#include "nfc/layer/nfc_layer_f.hpp" /*! @namespace m5 diff --git a/src/nfc/f/nfcf.cpp b/src/nfc/f/nfcf.cpp new file mode 100644 index 0000000..6f79f9d --- /dev/null +++ b/src/nfc/f/nfcf.cpp @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file nfcf.cpp + @brief NFC-F definitions +*/ + +#include "nfcf.hpp" +#include + +namespace { +std::string to_string(const uint8_t* p, const uint8_t size) +{ + char buf[2 * size + 1]{}; + if (p && size) { + uint8_t left{}; + for (uint_fast8_t i = 0; i < size; ++i) { + left += snprintf(buf + left, 3, "%02X", p[i]); + } + } + return std::string(buf); +} +} // namespace + +namespace m5 { +namespace nfc { +namespace f { + +std::string PICC::idmAsString() const +{ + return to_string(this->idm.data(), this->idm.size()); +} + +std::string PICC::pmmAsString() const +{ + return to_string(this->pmm.data(), this->pmm.size()); +} + +} // namespace f +} // namespace nfc +} // namespace m5 diff --git a/src/nfc/f/nfcf.hpp b/src/nfc/f/nfcf.hpp index c6e44e1..e0fcb7c 100644 --- a/src/nfc/f/nfcf.hpp +++ b/src/nfc/f/nfcf.hpp @@ -11,6 +11,7 @@ #define M5_UNIT_UNIFIED_NFC_NFC_F_NFCF_HPP #include +#include namespace m5 { namespace nfc { @@ -26,17 +27,114 @@ namespace f { */ enum class Type : uint8_t { Unknown, //!< Unknown type + FeliCaStandard, + FeliCaLiteS, + FelicaPlugin, +}; + +///@name Format bits +///@{ +using Format = uint8_t; +constexpr Format format_nfcip1{0x0001}; +constexpr Format format_dfc{0x0002}; +constexpr Format format_private{0x0004}; +constexpr Format format_ndef{0x0008}; +constexpr Format format_shared{0x0010}; +constexpr Format format_secure{0x0020}; +///@} + +using IDm = std::array; //!< Manufacture ID +using PMm = std::array; //!< Manufacture Parameter + +/*! + @enum CommandCode + @brief NFC-F Command code + */ +enum class CommandCode : uint8_t { + Polling, + RequestService = 0x02, + RequestResponse = 0x04, }; /*! - @struct IDm - @brief The Idm(Identifier for Manufacturer) of the PICC - */ -struct IDm { - //! @brief uid data - uint8_t uid[8]{}; + @enum ResponseCode + @brief NFC-F Response code + +*/ +enum class ResponseCode : uint8_t { + Polling = 0x01, + RequestService = 0x03, }; +///@name SystemCode +///@{ +constexpr uint16_t system_code_wildcard{0xFFFF}; //!< Wildcard +constexpr uint16_t system_code_ndef{0x12FC}; //!< NDEF +constexpr uint16_t system_code_secure{0x957A}; //!< FeliCa secure ID +constexpr uint16_t system_code_shared{0xFE00}; //!< Shared area +constexpr uint16_t system_code_dfc{0x8884}; //!< DFC +///@} + +/*! + @enum RequestCode + @brief Request code for Polling + */ +enum class RequestCode : uint8_t { + None, //!< No request + SystemCode, //!< Request system code + CommunicationPerformance, //!< Request communication performance +}; + +/*! + @enum TimeSlot + @brief Timeslot value for Polling + */ +enum class TimeSlot : uint8_t { Slot1, Slot2, Slot4 = 0x03, Slot8 = 0x07, Slot16 = 0x0F }; + +//! @brief TimeSlot to the number of the slot +inline constexpr uint8_t timeslot_to_slot(const TimeSlot ts) +{ + return (ts == TimeSlot::Slot16) ? 16 + : (ts == TimeSlot::Slot8) ? 8 + : (ts == TimeSlot::Slot4) ? 4 + : (ts == TimeSlot::Slot2) ? 2 + : (ts == TimeSlot::Slot1) ? 1 + : 0; // Illegal +} + +/*! + @strutc PICC + @brief PICC informationg for NFC-F + */ +struct PICC { + IDm idm{}; //!< Manufacture ID + PMm pmm{}; //!< //!< Manufacture Parameter + uint16_t request_data{}; //!< Any request data + RequestCode request_code{}; //!< Tyepe of the request_data + Format format{}; //!< Format type group bits + + //! @breif Gets the IDm string for debug + std::string idmAsString() const; + //! @breif Gets the PMm string for debug + std::string pmmAsString() const; +}; + +//! @brief Equal? +inline bool operator==(const PICC& a, const PICC& b) +{ + return a.idm == b.idm && a.pmm == b.pmm; +} +//! @brief Not equal? +inline bool operator!=(const PICC& a, const PICC& b) +{ + return !(a == b); +} + +///@name Timeout +///@{ +constexpr uint32_t TIMEOUT_POLLING{3}; +constexpr uint32_t TIMEOUT_POLLING_PICC{2}; // 2 ms per PICC + } // namespace f } // namespace nfc } // namespace m5 diff --git a/src/nfc/layer/nfc_layer_a.cpp b/src/nfc/layer/nfc_layer_a.cpp index f6c6e6f..d67e8fb 100644 --- a/src/nfc/layer/nfc_layer_a.cpp +++ b/src/nfc/layer/nfc_layer_a.cpp @@ -631,6 +631,8 @@ bool NFCLayerA::mifareUltralightChangeFormatToNTAG() M5_LIB_LOGE("Failed to write4"); return false; } + // Verify + return ntag_check_format(); } } return true; @@ -662,7 +664,7 @@ bool NFCLayerA::ndefRead(std::vector& msgs, const m5::nf bool NFCLayerA::ndefWrite(const m5::nfc::ndef::Message& msg) { std::vector msgs = {msg}; - return _activeUID.supportsNFC() && _ndef.write(msgs); + return msg.isNDEFMessage() && _activeUID.supportsNFC() && _ndef.write(msgs); } bool NFCLayerA::ndefWrite(const std::vector& msgs) diff --git a/src/nfc/layer/nfc_layer_a.hpp b/src/nfc/layer/nfc_layer_a.hpp index cf27b2e..e6aff17 100644 --- a/src/nfc/layer/nfc_layer_a.hpp +++ b/src/nfc/layer/nfc_layer_a.hpp @@ -19,8 +19,8 @@ #include "nfc/a/nfca.hpp" #include "ndef_layer.hpp" -#include #include +#include namespace m5 { @@ -83,7 +83,7 @@ public: @brief Detect idle PICCs @param[out] uids Detected PICC UIDs (one per activated PICC candidate) @param timeout_ms Polling time budget in milliseconds - @return True if successful + @return True if detected @note The selected PICC is typically put into HALT during enumeration to allow discovering others */ bool detect(std::vector& uids, const uint32_t timeout_ms = 10 * 1000U); @@ -339,11 +339,12 @@ public: bool mifareClassicRestoreValueBlock(const uint8_t block); /*! - @brief Write change to NFC Type-2 (NDEF) format + @brief Write change to NFC Type-2 (NDEF) format for MIFARE Ultralight/C @return True if successful @note Returns true if the data is already in NDEF format or if the PICC is an NTAG @warning Only MIFARE Ultralight,UltralightC @warning Changes are irreversible and cannot be undone + @warning If the relevant area has already been overwritten, changes may not be possible */ bool mifareUltralightChangeFormatToNTAG(); ///@} @@ -365,7 +366,7 @@ public: */ bool ndefRead(m5::nfc::ndef::Message& msg); /*! - @brief Read any NDEF Message + @brief Read any NDEF TLV @param[out] msgs Messgae vector @param tagBits Bit indicating the group of NDEF tags to be read @return True if successful @@ -374,8 +375,8 @@ public: bool ndefRead(std::vector& msgs, const m5::nfc::ndef::TagBits tagBits = m5::nfc::ndef::tagBitsAll); /*! - @brief Write NDEF message TLV - @param msg Messgae + @brief Write NDEF message + @param msg Messgae (NDEF Message) @return True if successful @note Other existing tags will be preserved @warning Existing NDEF message TLVs will be overwritten @@ -383,7 +384,7 @@ public: */ bool ndefWrite(const m5::nfc::ndef::Message& msg); /*! - @brief Write any NDEF Messages + @brief Write any NDEF Messages TLV @param msgs Messgae vector @return True if successful @note Write starting from the beginning of the user area @@ -442,6 +443,7 @@ private: m5::nfc::ndef::NDEFLayer _ndef; }; +///@cond // Impl for units struct NFCLayerA::Adapter { virtual ~Adapter() = default; @@ -466,12 +468,8 @@ struct NFCLayerA::Adapter { virtual bool ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) = 0; // FAST_READ - - // virtual bool mifare_classic_read_block(uint8_t* rx, uint16_t& rx_len, const uint16_t addr) = 0; - // virtual bool mifare_classic_write_block(const uint16_t addr, const uint8_t* tx, const uint16_t tx_len) = 0; - -protected: }; +///@endcond } // namespace nfc } // namespace unit diff --git a/src/nfc/layer/nfc_layer_f.cpp b/src/nfc/layer/nfc_layer_f.cpp new file mode 100644 index 0000000..83ed5c1 --- /dev/null +++ b/src/nfc/layer/nfc_layer_f.cpp @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file nfc_layer_f.cpp + @brief Common layer for NFC-F related units +*/ +#include "nfc_layer_f.hpp" +#include "nfc/ndef/ndef.hpp" +#include "nfc/ndef/ndef_message.hpp" +#include +#include +#include + +using namespace m5::nfc::f; +using namespace m5::nfc::ndef; + +namespace { + +constexpr uint16_t known_system_code_table[] = { + system_code_wildcard, system_code_ndef, system_code_secure, system_code_shared, system_code_dfc, +}; + +inline bool exists_system_code(const uint16_t code) +{ + return std::find(std::begin(known_system_code_table), std::end(known_system_code_table), code) != + std::end(known_system_code_table); +} + +inline bool exists_picc(const std::vector& v, const PICC& picc) +{ + return std::find_if(v.begin(), v.end(), [&picc](const PICC& p) { return p == picc; }) != v.end(); +} + +} // namespace + +namespace m5 { +namespace unit { +namespace nfc { + +bool NFCLayerF::polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, + const m5::nfc::f::TimeSlot time_slot) +{ + return _impl->polling(picc, system_code, request_code, time_slot); +} + +bool NFCLayerF::detect(m5::nfc::f::PICC& picc, const uint16_t system_code, const uint32_t timeout_ms) +{ + std::vector v{}; + uint16_t codes[1] = {system_code}; + if (detect_picc(v, codes, 1, TimeSlot::Slot1, timeout_ms)) { + if (!exists_system_code(system_code) && picc.format & format_private) { + picc = v.front(); + return true; + } + } + return false; +} + +bool NFCLayerF::detect(std::vector& piccs, const uint16_t system_code, m5::nfc::f::TimeSlot time_slot, + const uint32_t timeout_ms) +{ + uint16_t codes[1] = {system_code}; + if (!detect_picc(piccs, codes, 1, time_slot, timeout_ms)) { + return false; + } + + // Remove items that do not meet the conditions + if (!piccs.empty() && !exists_system_code(system_code)) { + auto it = + std::remove_if(piccs.begin(), piccs.end(), [](PICC& picc) { return (picc.format & format_private) == 0; }); + piccs.erase(it, piccs.end()); + } + return !piccs.empty(); +} + +bool NFCLayerF::detect_picc(std::vector& piccs, const uint16_t* private_code, const uint8_t pc_size, + m5::nfc::f::TimeSlot time_slot, const uint32_t timeout_ms) +{ + uint8_t slots = timeslot_to_slot(time_slot); + + piccs.clear(); + piccs.reserve(slots); + + auto timeout_at = m5::utility::millis() + timeout_ms; + uint8_t detected{}; + do { + PICC picc1{}, picc2{}; + uint8_t format{}; + + // 1. Polling wildcard + if (!polling(picc1, system_code_wildcard, RequestCode::None, time_slot)) { + break; + } + // Already exists? + if (exists_picc(piccs, picc1)) { + continue; + } + + // 2. Check IDm for NFCIP-1 Transport Protocol / DFC + if (picc1.idm[0] == 0x01 && picc1.idm[1] == 0xFE) { + format |= format_nfcip1; + } else if (picc1.idm[0] == 0x03 && picc1.idm[1] == 0xFE) { + format |= format_dfc; + } + + // 3. Check private area + for (uint_fast8_t i = 0; i < pc_size; ++i) { + auto code = private_code[i]; + if (exists_system_code(code)) { + M5_LIB_LOGW("Skip %04X (known system code)", code); + continue; + } + if (polling(picc2, code, RequestCode::None, time_slot)) { + if (picc1 != picc2) { + continue; + } + format |= format_private; + } + } + + // 4. Check NDEF + if (polling(picc2, system_code_ndef, RequestCode::None, time_slot)) { + if (picc1 != picc2) { + continue; + } + format |= format_ndef; + } + + // 5. Check shared area + if (polling(picc2, system_code_shared, RequestCode::None, time_slot)) { + if (picc1 != picc2) { + continue; + } + format |= format_shared; + } + + // 6. Check DEF for FeliCa Lite-S + if (polling(picc2, system_code_dfc, RequestCode::None, time_slot)) { + if (picc1 != picc2) { + continue; + } + // TODO: read lite-S + format |= format_shared; + } + + // 7. Check secure + if (polling(picc2, system_code_secure, RequestCode::None, time_slot)) { + if (picc1 != picc2) { + continue; + } + format |= format_secure; + } + + picc1.format = format; + piccs.push_back(picc1); + ++detected; + } while (detected < slots && m5::utility::millis() <= timeout_at); + + return !piccs.empty(); +} + +// +bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr) +{ + return false; +} +bool NFCLayerF::write(const uint8_t saddr, const uint8_t* tx, const uint16_t tx_len) +{ + return false; +} +uint16_t NFCLayerF::firstUserBlock() const +{ + return 0; +} +uint16_t NFCLayerF::lastUserBlock() const +{ + return 0; +} + +} // namespace nfc +} // namespace unit +} // namespace m5 diff --git a/src/nfc/layer/nfc_layer_f.hpp b/src/nfc/layer/nfc_layer_f.hpp new file mode 100644 index 0000000..e1b945e --- /dev/null +++ b/src/nfc/layer/nfc_layer_f.hpp @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file nfc_layer_f.hpp + @brief Common layer for NFC-F related units + + @note Glossary + - PCD: Proximity Coupling Device (reader) + - PICC: Proximity Integrated Circuit Card (card/tag, target device) + - IDLE/READY/ACTIVE/HALT: ISO14443-3 state names + + @note In NFC Forum (NDEF) context, a PICC is often called a "Tag" +*/ +#ifndef M5_UNIT_NFC_NFC_LAYER_NFC_LAYER_F_HPP +#define M5_UNIT_NFC_NFC_LAYER_NFC_LAYER_F_HPP + +#include "nfc/f/nfcf.hpp" +#include "ndef_layer.hpp" +#include +#include + +namespace m5 { + +namespace unit { +class UnitST25R3916; +class CapST25R3916; + +namespace nfc { + +/*! + @class NFCLayerF + @brief Common interface layer for each chip of the NFC-F reader + */ +class NFCLayerF : public m5::nfc::NFCLayerInterface { +public: + struct Adapter; + explicit NFCLayerF(UnitST25R3916& u); + explicit NFCLayerF(CapST25R3916& u); + + ///@name Detection + ///@{ + /*! + @brief Polling + @param[out] PICC detected PICC + @param system_code System code + @param request_code Request code + @param time_slot Maximum number of slots that can be responded + @return True if successful + @note SENSF_REQ + */ + bool polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, + const m5::nfc::f::TimeSlot time_slot); + + /*! + @brief Detect single PICC + @param[out] picc Detected PICC + @param timeout_ms Polling time budget in milliseconds + @return True if detected + */ + inline bool detect(m5::nfc::f::PICC& picc, const uint32_t timeout_ms) + { + return detect(picc, 0xFFFF /*skip*/, timeout_ms); + } + /*! + @brief Detect single PICC matching the specified system code + @param[out] picc Detected PICC + @param system_code System code + @param timeout_ms Polling time budget in milliseconds + @return True if detected + */ + bool detect(m5::nfc::f::PICC& picc, const uint16_t system_code, const uint32_t timeout_ms = 100U); + /*! + @brief Detect PICCs + @param[out] piccs Detected PICCs + @param time_slot Maximum number of slots that can be responded + @param timeout_ms Polling time budget in milliseconds + @return True if detected + */ + inline bool detect(std::vector& piccs, + m5::nfc::f::TimeSlot time_slot = m5::nfc::f::TimeSlot::Slot16, const uint32_t timeout_ms = 500U) + { + return detect_picc(piccs, nullptr, 0, time_slot, timeout_ms); + } + /*! + @brief Detect PICCs matching the specified system code + @param[out] piccs Detected PICCs + @param system_code System code + @param time_slot Maximum number of slots that can be responded + @param timeout_ms Polling time budget in milliseconds + @return True if detected + */ + bool detect(std::vector& piccs, const uint16_t system_code, + m5::nfc::f::TimeSlot time_slot = m5::nfc::f::TimeSlot::Slot16, const uint32_t timeout_ms = 500U); + +protected: + virtual bool read(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr) override; + virtual bool write(const uint8_t saddr, const uint8_t* tx, const uint16_t tx_len) override; + virtual uint16_t firstUserBlock() const override; + virtual uint16_t lastUserBlock() const override; + + bool detect_picc(std::vector& piccs, const uint16_t* private_system_code, const uint8_t pc_size, + m5::nfc::f::TimeSlot time_slot, const uint32_t timeout_ms); + +private: + std::unique_ptr _impl; + m5::nfc::ndef::NDEFLayer _ndef; +}; + +///@cond +// Impl for units +struct NFCLayerF::Adapter { + virtual ~Adapter() = default; + + virtual bool polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, + const m5::nfc::f::TimeSlot time_slot) = 0; +}; +///@endcond + +} // namespace nfc +} // namespace unit +} // namespace m5 +#endif diff --git a/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp b/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp index a3da0bb..c42b956 100644 --- a/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp +++ b/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp @@ -5,7 +5,7 @@ */ /*! @file nfc_layer_a_ST25R3916.cpp - @brief ST25R3916 adapter for common layer + @brief ST25R3916 NFC-A adapter for common layer */ #include "nfc/layer/nfc_layer_a.hpp" #include "nfc/layer/ndef_layer.hpp" @@ -21,10 +21,9 @@ using namespace m5::nfc::a::mifare::classic; namespace m5 { namespace unit { namespace nfc { - // -struct AdapterST25R3916 final : NFCLayerA::Adapter { - explicit AdapterST25R3916(UnitST25R3916& ref) : _u{ref} +struct AdapterST25R3916ForA final : NFCLayerA::Adapter { + explicit AdapterST25R3916ForA(UnitST25R3916& ref) : _u{ref} { } @@ -55,17 +54,17 @@ struct AdapterST25R3916 final : NFCLayerA::Adapter { UnitST25R3916& _u; }; -bool AdapterST25R3916::request(uint16_t& atqa) +bool AdapterST25R3916ForA::request(uint16_t& atqa) { return _u.nfcaRequest(atqa); } -bool AdapterST25R3916::wakeup(uint16_t& atqa) +bool AdapterST25R3916ForA::wakeup(uint16_t& atqa) { return _u.nfcaWakeup(atqa); } -bool AdapterST25R3916::select(m5::nfc::a::UID& uid) +bool AdapterST25R3916ForA::select(m5::nfc::a::UID& uid) { uint8_t lv{1}; // Cascade level 1-3 bool completed{}; @@ -78,44 +77,44 @@ bool AdapterST25R3916::select(m5::nfc::a::UID& uid) return completed; } -bool AdapterST25R3916::activate(const UID& uid) +bool AdapterST25R3916ForA::activate(const UID& uid) { return _u.nfcaSelect(uid); } -bool AdapterST25R3916::deactivate() +bool AdapterST25R3916ForA::deactivate() { return _u.nfcaHlt(); } -bool AdapterST25R3916::nfca_read_block(uint8_t rx[16], const uint8_t addr) +bool AdapterST25R3916ForA::nfca_read_block(uint8_t rx[16], const uint8_t addr) { return _u.nfcaReadBlock(rx, addr); } -bool AdapterST25R3916::nfca_write_block(const uint8_t addr, const uint8_t tx[16]) +bool AdapterST25R3916ForA::nfca_write_block(const uint8_t addr, const uint8_t tx[16]) { return _u.nfcaWriteBlock(addr, tx); } -bool AdapterST25R3916::ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) +bool AdapterST25R3916ForA::ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) { return _u.ntagReadPage(rx, rx_len, spage, epage); } -bool AdapterST25R3916::nfca_write_page(const uint8_t addr, const uint8_t tx[4]) +bool AdapterST25R3916ForA::nfca_write_page(const uint8_t addr, const uint8_t tx[4]) { return _u.nfcaWritePage(addr, tx); } -bool AdapterST25R3916::mifare_classic_authenticate(const bool auth_a, const UID& uid, const uint8_t block, - const Key& key) +bool AdapterST25R3916ForA::mifare_classic_authenticate(const bool auth_a, const UID& uid, const uint8_t block, + const Key& key) { return auth_a ? _u.mifareClassicAuthenticateA(uid, block, key) : _u.mifareClassicAuthenticateB(uid, block, key); } -bool AdapterST25R3916::mifare_classic_value_block(const m5::nfc::a::Command cmd, const uint8_t block, - const uint32_t arg) +bool AdapterST25R3916ForA::mifare_classic_value_block(const m5::nfc::a::Command cmd, const uint8_t block, + const uint32_t arg) { return _u.mifareClassicValueBlock(cmd, block, arg); } @@ -124,7 +123,7 @@ bool AdapterST25R3916::mifare_classic_value_block(const m5::nfc::a::Command cmd, namespace { std::unique_ptr make_st25r3916_adapter(UnitST25R3916& u) { - return std::unique_ptr(new AdapterST25R3916(u)); + return std::unique_ptr(new AdapterST25R3916ForA(u)); } } // namespace diff --git a/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp b/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp new file mode 100644 index 0000000..0a250f3 --- /dev/null +++ b/src/nfc/layer/unit/nfc_layer_f_ST25R3916.cpp @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file nfc_layer_f_ST25R3916.cpp + @brief ST25R3916 NFC-F adapter for common layer +*/ +#include "nfc/layer/nfc_layer_f.hpp" +#include "nfc/layer/ndef_layer.hpp" +#include "unit/unit_ST25R3916.hpp" +#include + +using namespace m5::unit; +using namespace m5::unit::st25r3916; +using namespace m5::nfc::f; + +namespace m5 { +namespace unit { +namespace nfc { + +// +struct AdapterST25R3916ForF final : NFCLayerF::Adapter { + explicit AdapterST25R3916ForF(UnitST25R3916& ref) : _u{ref} + { + } + + virtual bool polling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, + const m5::nfc::f::TimeSlot time_slot) override; + + UnitST25R3916& _u; +}; + +bool AdapterST25R3916ForF::polling(m5::nfc::f::PICC& picc, const uint16_t system_code, + const m5::nfc::f::RequestCode request_code, const m5::nfc::f::TimeSlot time_slot) +{ + return _u.nfcfPolling(picc, system_code, request_code, time_slot); +} + +// +namespace { +std::unique_ptr make_st25r3916_adapter(UnitST25R3916& u) +{ + return std::unique_ptr(new AdapterST25R3916ForF(u)); +} +} // namespace + +NFCLayerF::NFCLayerF(UnitST25R3916& u) : _impl(make_st25r3916_adapter(u)), _ndef{*this} +{ +} + +NFCLayerF::NFCLayerF(CapST25R3916& u) : _impl(make_st25r3916_adapter(static_cast(u))), _ndef{*this} +{ +} + +} // namespace nfc +} // namespace unit +} // namespace m5 diff --git a/src/nfc/nfc.hpp b/src/nfc/nfc.hpp index 7d25e5f..f6273ea 100644 --- a/src/nfc/nfc.hpp +++ b/src/nfc/nfc.hpp @@ -22,6 +22,18 @@ namespace m5 { */ namespace nfc { +/*! + @enum NFC + @brief Operation Mode + */ +enum class NFC : uint8_t { + None, //!< No mode + A, //!< NFC-A + B, //!< NFC-B + F, //!< NFC-F + V //!< NFC-V +}; + } // namespace nfc } // namespace m5 diff --git a/src/unit/ST25R3916_definition.hpp b/src/unit/ST25R3916_definition.hpp index 630d4ba..7ce70be 100644 --- a/src/unit/ST25R3916_definition.hpp +++ b/src/unit/ST25R3916_definition.hpp @@ -30,7 +30,7 @@ enum class InitiatorOperationMode : uint8_t { NFCIP1 = 0x00 << 3, //!< NFCIP-1 active communication ISO14443A = 0x01 << 3, //!< ISO14443A ISO14443B = 0x02 << 3, //!< ISO14443B - Felica = 0x03 << 3, //!< Felica + FeliCa = 0x03 << 3, //!< FeliCa NFCForumType1 = 0x04 << 3, //!< NFC Forum Type 1 tag (Topaz) SubCarrierStream = 0x0E << 3, //!< Sub-carrier stream mode BPSKStream = 0x0F << 3, //!< BPSK stream mode @@ -232,40 +232,79 @@ constexpr uint8_t OP_DIRECT_COMMAND{0xC0}; // 11xxxxxxb; */ namespace regval { ///@cond -// IO configuration register 2 +// 0x01 IO configuration register 2 constexpr uint8_t sup3v{0x80}; constexpr uint8_t io_drv_lvl{0x04}; constexpr uint8_t miso_pd1{0x08}; constexpr uint8_t miso_pd2{0x10}; -// Operation control register -constexpr uint8_t en{0x80}; -constexpr uint8_t rx_en{0x40}; -constexpr uint8_t tx_en{0x08}; -// ISO14443A and NFC 106kb/s settings register + +// 0x02 Operation control register +constexpr uint8_t en{0x80}; // 1: Enables oscillator and regulator(Ready mode) +constexpr uint8_t rx_en{0x40}; // 1: Enables Rx operation +constexpr uint8_t tx_en{0x08}; // 1: Enables Tx operation +constexpr uint8_t wu{0x04}; // 1: Enables Wake-up mode +constexpr uint8_t en_fd_c1{0x02}; +constexpr uint8_t en_fd_c0{0x01}; + +constexpr uint8_t en_fd_mask{0x03}; +constexpr uint8_t en_fd_off{0x00}; +constexpr uint8_t en_fd_manual_ca{0x01}; +constexpr uint8_t en_fd_manual_pdt{0x02}; +constexpr uint8_t en_fd_manual_auto{0x03}; + +// 0x03 Mode definition register +constexpr uint8_t targ{0x80}; // 0: Initiator 1: Target +constexpr uint8_t tr_am{0x04}; +constexpr uint8_t nfc_ar0{0x01}; +constexpr uint8_t nfc_ar1{0x02}; + +constexpr uint8_t nfc_ar8_off{0x00}; +constexpr uint8_t nfc_ar8_auto{0x01}; +constexpr uint8_t nfc_ar8_always{0x02}; +constexpr uint8_t nfc_ar8_RFI{0x03}; + +// 0x05 ISO14443A and NFC 106kb/s settings register constexpr uint8_t no_tx_par{0x80}; constexpr uint8_t no_rx_par{0x40}; constexpr uint8_t antcl{0x01}; -// Auxiliary definition register + +// 0x0A Auxiliary definition register constexpr uint8_t no_crc_rx{0x80}; -// Main interrupt register +constexpr uint8_t nfc_n1{0x02}; +constexpr uint8_t nfc_n0{0x01}; + +constexpr uint8_t nfc_n_mask{0x03}; + +// 0x12 Timer and EMV control register +constexpr uint8_t mrt_step{0x08}; // Mask receive timer step size +constexpr uint8_t nrt_step{0x01}; // No-response timer step size + +// 0x1A Main interrupt register constexpr uint8_t I_wl{0x40}; // IRQ due to FIFO water level constexpr uint8_t I_rxs{0x20}; // IRQ due to start of receive constexpr uint8_t I_rxe{0x10}; // IRQ due to end of receive constexpr uint8_t I_txe{0x08}; // IRQ due to end of transmission constexpr uint8_t I_col{0x04}; // IRQ due to bit collision -// Timer and NFC interrupt register + +// 0x1B Timer and NFC interrupt register constexpr uint8_t I_nre{0x40}; // IRQ due to No-response timer expire constexpr uint8_t I_cac{0x04}; // IRQ due to detection of collision during RF collision avoidance -// Timer and EMV control register -constexpr uint8_t mrt_step{0x08}; // Mask receive timer step size -constexpr uint8_t nrt_step{0x01}; // No-response timer step size -// +constexpr uint8_t I_cat{0x02}; // IRQ after minimum guard time expire + +// 0x1D Passive target interrupt register +constexpr uint8_t I_apon{0x20}; // IRQ due to active P2P field on event + constexpr uint32_t I_wl32 = ((uint32_t)I_wl << 24); constexpr uint32_t I_rxs32 = ((uint32_t)I_rxs << 24); constexpr uint32_t I_rxe32 = ((uint32_t)I_rxe << 24); constexpr uint32_t I_txe32 = ((uint32_t)I_txe << 24); -constexpr uint32_t I_col32 = ((uint32_t)I_col << 24) | ((uint32_t)I_cac << 16); +constexpr uint32_t I_col32 = ((uint32_t)I_col << 24); + constexpr uint32_t I_nre32 = ((uint32_t)I_nre << 16); +constexpr uint32_t I_cac32 = ((uint32_t)I_cac << 16); +constexpr uint32_t I_cat32 = ((uint32_t)I_cat << 16); + +constexpr uint32_t I_apon32 = I_apon; ///@endcond } // namespace regval diff --git a/src/unit/unit_ST25R3916.cpp b/src/unit/unit_ST25R3916.cpp index 9b84325..86bf22e 100644 --- a/src/unit/unit_ST25R3916.cpp +++ b/src/unit/unit_ST25R3916.cpp @@ -12,10 +12,13 @@ #include using namespace m5::utility::mmh3; + using namespace m5::unit::types; using namespace m5::unit::st25r3916; using namespace m5::unit::st25r3916::regval; using namespace m5::unit::st25r3916::command; + +using namespace m5::nfc; using namespace m5::nfc::a; using namespace m5::nfc::a::mifare; using namespace m5::nfc::a::mifare::classic; @@ -81,7 +84,7 @@ uint16_t calculate_nrt(const uint32_t ms, const bool nrt_step) if (nrt > max) { nrt = max; } - // M5_LIB_LOGE(">>>> %ums fc4096:%u => %04X", ms, nrt_step, nrt); + // M5_LIB_LOGE(">>>> %ums fc4096:%u => %04X", ms, nrt_step, nrt); return nrt; } @@ -185,23 +188,7 @@ bool UnitST25R3916::begin() readTXDriver(txd); M5_LIB_LOGD("TXD:%02X", txd); - writeReceiverConfiguration1(0x08); // z600k - writeReceiverConfiguration2(0x2D); // sqm_dyn , agc_en, agc_m, agc6_3, - writeReceiverConfiguration3(0x00); - writeReceiverConfiguration4(0x00); - // - // ISO14443A - // M5_LIB_LOGE(">>>>>> try ISO14443A REQA"); - writeInitiatorOperationMode(InitiatorOperationMode::ISO14443A, 0x01 /* nfc_ar01 */); - writeBitrate(Bitrate::FC128_106Kbits, Bitrate::FC128_106Kbits); - writeSettingsISO14443A(0x0); - - // - writeOperationControl(en | rx_en | tx_en); - writeMaskInterrupts(0); - writeDirectCommand(CMD_NFC_INITIAL_FIELD_ON); - m5::utility::delay(5); #if 0 // MRT/SQT @@ -215,70 +202,7 @@ bool UnitST25R3916::begin() M5_LIB_LOGE("====== MRT:%02X SQT:%02X", mrt, sqt); #endif -// -#if 0 - uint8_t a_table[] = { - /* - 0x07, 0x3C, 0x83, 0x08, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x08, 0x2D, 0xD8, 0x00, 0x0C, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFB, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x82, 0x82, 0x70, 0x5F, 0x13, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - */ - 0x07, 0x3C, 0xCB, 0x08, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x03, 0x08, 0x2D, 0x00, 0x00, 0x0E, - 0x00, 0x23, 0x20, 0x02, 0xC8, 0x80, 0x87, 0xA6, 0x0F, 0x7B, 0x00, 0x20, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x38, 0x00, 0xDF, 0x82, 0x82, 0x70, 0x5F, 0x13, 0x02, 0x00, 0x33, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - struct B { - uint16_t reg; - uint8_t val; - } b_table[] = { - {0x05, 0x40}, {0x06, 0x00}, {0x0B, 0x0C}, {0x0C, 0x51}, {0x0D, 0x00}, {0x0F, 0x00}, {0x15, 0x00}, {0x28, 0x10}, - {0x29, 0x7C}, {0x2A, 0x80}, {0x2B, 0x04}, {0x2C, 0xD0}, {0X30, 0x40}, {0x31, 0x03}, {0x32, 0x40}, {0x33, 0x03}, - }; - - uint8_t r{}; - for (auto&& v : a_table) { - write_register8(r++, v); - } - for (auto&& b : b_table) { - write_register8(b.reg, b.val); - } - -#endif - -#if 0 - write_register8((uint8_t)0x08, 0x5D); - write_register8((uint8_t)0x09, 0x03); - write_register8((uint8_t)0x12, 0x20); - write_register8((uint8_t)0x13, 0x02); - - write_register8((uint8_t)0x25, 0xDF); - write_register8((uint8_t)0x26, 0x82); - write_register8((uint8_t)0x27, 0x82); - - write_register8((uint8_t)0x28, 0x70); - write_register8((uint8_t)0x29, 0x5F); - write_register8((uint8_t)0x2A, 0x13); - write_register8((uint8_t)0x2B, 0x02); - - write_register8((uint8_t)0x00, 0x07); - write_register8((uint8_t)0x01, 0x3C); - write_register8((uint8_t)0x02, 0xCB); - - - write_register8((uint16_t)0x05, 0x40); - write_register8((uint16_t)0x0C, 0x51); - write_register8((uint16_t)0x15, 0x00); - write_register8((uint16_t)0x2A, 0x80); - write_register8((uint16_t)0x2C, 0xD0); - write_register8((uint16_t)0x30, 0x40); - write_register8((uint16_t)0x31, 0x03); - write_register8((uint16_t)0x32, 0x40); - write_register8((uint16_t)0x33, 0x03); -#endif - - return true; + return configureNFCMode(_cfg.mode); } void UnitST25R3916::update(const bool /*force*/) @@ -294,6 +218,188 @@ void UnitST25R3916::update(const bool /*force*/) */ } +bool UnitST25R3916::configureNFCMode(const m5::nfc::NFC mode) +{ + if (mode == NFC::None) { + return false; + } + + if (NFCMode() == mode) { + return true; + } + + if (!writeDirectCommand(CMD_STOP_ALL_ACTIVITIES) || // + !modify_bit_register8(REG_OPERATION_CONTROL, 0x00, wu)) { // Disable wakeup mode + return false; + } + + bool ok{}; + switch (mode) { + case m5::nfc::NFC::A: + ok = configure_nfc_a(); + break; + case m5::nfc::NFC::B: + ok = configure_nfc_b(); + break; + case m5::nfc::NFC::F: + ok = configure_nfc_f(); + break; + case m5::nfc::NFC::V: + ok = configure_nfc_v(); + break; + default: + return false; + } + + /* + write_noresponse_timeout(default_nrt_for(mode)); + write_mask_receiver_timer(default_mrt_for(mode)); + write_squelch_timer(default_sqt_for(mode)); + */ + if (ok) { + M5_LIB_LOGE(">>>> Change to %u", mode); + _nfcMode = mode; + } + return ok; +} + +bool UnitST25R3916::nfc_initial_field_on() +{ + uint8_t v{}; + if (!readOperationControl(v)) { + return false; + } + if (v & tx_en) { + M5_LIB_LOGE("Already tx_en"); + return false; + } + + writeDirectCommand(CMD_NFC_INITIAL_FIELD_ON); + m5::utility::delay(5); + return modify_bit_register8(REG_OPERATION_CONTROL, tx_en | rx_en, 0x00); + +#if 0 + + if (!writeNFCFieldOnGuardTimer(0x00)) { + return false; + } + if (!modify_bit_register8(REG_OPERATION_CONTROL, en_fd_manual_ca, en_fd_mask)) { + return false; + } + + // + writeExternalFieldDetectorActivationThreshold(0x00); + modify_bit_register8(REG_AUXILIARY_DEFINITION, 0x00, nfc_n_mask); + + clearInterrupts(); + + uint32_t mask{}; + readMaskInterrupts(mask); + mask |= (I_cac32 | I_cat32 | I_apon32); + writeMaskInterrupts(mask); + + writeDirectCommand(CMD_NFC_INITIAL_FIELD_ON); + + bool ret{}; + auto irq = wait_for_interrupt(I_cac32 | I_cat32 | I_apon32, 10); + M5_LIB_LOGE("IRQ:%08X", irq); + if (irq & I_cac32) { + M5_LIB_LOGE("RF Collison"); + } else if (irq & I_apon32) { + irq = wait_for_interrupt(I_cat32, 10); + M5_LIB_LOGE(" IRQ:%08X", irq); + ret = (irq & I_cat32) != 0; + } else { + M5_LIB_LOGE("ERROR"); + } + + clearInterrupts(); + mask &= ~(I_cac32 | I_cat32 | I_apon32); + writeMaskInterrupts(mask); + + return ret && modify_bit_register8(REG_OPERATION_CONTROL, tx_en | rx_en, 0x00); +#endif + +#if 0 +/*******************************************************************************/ +ReturnCode RfalRfST25R3916Class::st25r3916PerformCollisionAvoidance(uint8_t FieldONCmd, uint8_t pdThreshold, uint8_t caThreshold, uint8_t nTRFW) +{ + uint8_t treMask; + uint32_t irqs; + ReturnCode err; + + if ((FieldONCmd != ST25R3916_CMD_INITIAL_RF_COLLISION) && (FieldONCmd != ST25R3916_CMD_RESPONSE_RF_COLLISION_N)) { + return ERR_PARAM; + } + + err = ERR_INTERNAL; + + + /* Check if new thresholds are to be applied */ + if ((pdThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) || (caThreshold != ST25R3916_THRESHOLD_DO_NOT_SET)) { + treMask = 0; + + if (pdThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) { + treMask |= ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask; + } + + if (caThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) { + treMask |= ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask; + } + + /* Set Detection Threshold and|or Collision Avoidance Threshold */ + st25r3916ChangeRegisterBits(ST25R3916_REG_FIELD_THRESHOLD_ACTV, treMask, (pdThreshold & ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask) | (caThreshold & ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask)); + } + + /* Set n x TRFW */ + st25r3916ChangeRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_n_mask, nTRFW); + + /*******************************************************************************/ + /* Enable and clear CA specific interrupts and execute command */ + st25r3916GetInterrupt((ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); + st25r3916EnableInterrupts((ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); + + st25r3916ExecuteCommand(FieldONCmd); + + /*******************************************************************************/ + /* Wait for initial APON interrupt, indicating anticollision avoidance done and ST25R3916's + * field is now on, or a CAC indicating a collision */ + irqs = st25r3916WaitForInterruptsTimed((ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_APON), ST25R3916_TOUT_CA); + + if ((ST25R3916_IRQ_MASK_CAC & irqs) != 0U) { /* Collision occurred */ + err = ERR_RF_COLLISION; + } else if ((ST25R3916_IRQ_MASK_APON & irqs) != 0U) { + /* After APON wait for CAT interrupt, indication field was switched on minimum guard time has been fulfilled */ + irqs = st25r3916WaitForInterruptsTimed((ST25R3916_IRQ_MASK_CAT), ST25R3916_TOUT_CA); + + if ((ST25R3916_IRQ_MASK_CAT & irqs) != 0U) { /* No Collision detected, Field On */ + err = ERR_NONE; + } + } else { + /* MISRA 15.7 - Empty else */ + } + + /* Clear any previous External Field events and disable CA specific interrupts */ + st25r3916GetInterrupt((ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_EON)); + st25r3916DisableInterrupts((ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); + + return err; +} +#endif +} + +bool UnitST25R3916::configure_nfc_b() +{ + M5_LIB_LOGE("Not yet supported"); + return false; +} + +bool UnitST25R3916::configure_nfc_v() +{ + M5_LIB_LOGE("Not yet supported"); + return false; +} + bool UnitST25R3916::writeDirectCommand(const uint8_t cmd, const uint8_t* data, uint32_t dlen) { TRANSACTION_GUARD(); @@ -428,62 +534,6 @@ bool UnitST25R3916::write_register8(const uint16_t reg, const uint8_t v) return writeRegister8(to_write_reg(reg), v, false); } -bool UnitST25R3916::set_bit_register8(const uint8_t reg, const uint8_t bits) -{ - TRANSACTION_GUARD(); - - uint8_t v{}; - if (read_register8(reg, v)) { - if ((v & bits) == bits) { - return true; // Already set spesific bits - } - return write_register8(reg, v | bits); - } - return false; -} - -bool UnitST25R3916::set_bit_register8(const uint16_t reg, const uint8_t bits) -{ - TRANSACTION_GUARD(); - - uint8_t v{}; - if (read_register8(reg, v)) { - if ((v & bits) == bits) { - return true; // Already set spesific bits - } - return write_register8(reg, v | bits); - } - return false; -} - -bool UnitST25R3916::clear_bit_register8(const uint8_t reg, const uint8_t bits) -{ - TRANSACTION_GUARD(); - - uint8_t v{}; - if (read_register8(reg, v)) { - if ((v & ~bits) == ~bits) { - return true; // Already cleared spesific bits - } - return write_register8(reg, v & ~bits); - } - return false; -} - -bool UnitST25R3916::clear_bit_register8(const uint16_t reg, const uint8_t bits) -{ - TRANSACTION_GUARD(); - - uint8_t v{}; - if (read_register8(reg, v)) { - if ((v & ~bits) == ~bits) { - return true; // Already cleared spesific bits - } - return write_register8(reg, v & ~bits); - } - return false; -} - bool UnitST25R3916::read_register16(const uint8_t reg, uint16_t& v) { TRANSACTION_GUARD(); @@ -536,6 +586,34 @@ bool UnitST25R3916::write_register32(const uint16_t reg, const uint32_t v) return writeRegister32BE(to_write_reg(reg), v, false); } +bool UnitST25R3916::modify_bit_register8(const uint8_t reg, const uint8_t set_mask, const uint8_t clear_mask) +{ + uint8_t v{}; + if (read_register8(reg, v)) { + const uint8_t w = (v & ~clear_mask) | set_mask; + // M5_LIB_LOGE("[%2u]:%02X %02X/%02X => %02X %08o", reg, v, set_mask, clear_mask, w, OCB(w)); + if (w == v) { + return true; + } + return write_register8(reg, w); + } + return false; +} + +bool UnitST25R3916::modify_bit_register8(const uint16_t reg, const uint8_t set_mask, const uint8_t clear_mask) +{ + uint8_t v{}; + if (read_register8(reg, v)) { + const uint8_t w = (v & ~clear_mask) | set_mask; + // M5_LIB_LOGE("[%2u]:%02X %02X/%02X => %02X %08o", reg, v, set_mask, clear_mask, w, OCB(w)); + if (w == v) { + return true; + } + return write_register8(reg, w); + } + return false; +} + uint32_t UnitST25R3916::wait_for_interrupt(const uint32_t irq, const uint32_t timeout_ms, bool include_error) { auto timeout_at = m5::utility::millis() + timeout_ms; @@ -561,13 +639,18 @@ bool UnitST25R3916::wait_for_FIFO(const uint32_t timeout_ms, const uint16_t requ auto irq = wait_for_interrupt(I_rxe32, timeout_ms); const uint16_t reqSize = required_size ? required_size : 1; + if (has_irq32_error(irq)) { + M5_LIB_LOGE("Error: %08X", irq); + return false; + } + if (is_irq32_rxe(irq)) { return true; } - // M5_LIB_LOGE("IRQ:%08X %u", irq, timeout_ms); + // M5_LIB_LOGE("IRQ:%08X %u", irq, timeout_ms); + // Check the FIFO size in case I_rxe doesn't arrive if (is_irq32_rxs(irq)) { - // Check the FIFO size in case I_rxe doesn't arrive auto timeout_at = m5::utility::millis() + timeout_ms; uint16_t bytes{}; uint8_t bits{}; @@ -578,11 +661,8 @@ bool UnitST25R3916::wait_for_FIFO(const uint32_t timeout_ms, const uint16_t requ } m5::utility::delay(1); } while (m5::utility::millis() <= timeout_at); - readFIFOSize(bytes, bits); - - // M5_LIB_LOGE(" FIFO:%u,%u/%u", bytes, bits, required_size); - - return bytes >= reqSize; + // M5_LIB_LOGE(" FIFO:%u,%u/%u", bytes, bits, required_size); + return readFIFOSize(bytes, bits) && bytes >= reqSize; } return false; } diff --git a/src/unit/unit_ST25R3916.hpp b/src/unit/unit_ST25R3916.hpp index c996350..387b57e 100644 --- a/src/unit/unit_ST25R3916.hpp +++ b/src/unit/unit_ST25R3916.hpp @@ -42,8 +42,9 @@ public: @brief Settings for begin */ struct config_t { - bool vdd_voltage_5V{false}; //!< VDD voltage true:5V false:3.3V - uint8_t tx_am_modulation{13}; // 0-15 See also 4.5.48 TX driver register + m5::nfc::NFC mode{m5::nfc::NFC::A}; //!< Initial Operation Mode + bool vdd_voltage_5V{false}; //!< VDD voltage true:5V false:3.3V + uint8_t tx_am_modulation{13}; // 0-15 See also 4.5.48 TX driver register }; ///@name Settings for begin @@ -60,6 +61,28 @@ public: } ///@} + //! @brief Gets the current operating mode + inline m5::nfc::NFC NFCMode() const + { + return _nfcMode; + } + /*! + @brief Configure NFC mode + @param mode NFC mode + @return True if successful + */ + bool configureNFCMode(const m5::nfc::NFC mode); + + /*! + @breif Is the current operating mode the one specified? + @param mode Mode + @return True if the current operation is in the specified mode + */ + inline bool isNFCMode(const m5::nfc::NFC mode) + { + return NFCMode() == mode; + } + /*! @brief Write the direct command with data @param cmd Direct command @@ -351,8 +374,7 @@ public: */ inline bool writeAuxiliaryDefinition(const uint8_t value) { - // return write_register8(st25r3916::command::REG_AUXILIARY_DEFINITION, value | 0x03); - return write_register8(st25r3916::command::REG_AUXILIARY_DEFINITION, value | 0x03); + return write_register8(st25r3916::command::REG_AUXILIARY_DEFINITION, value); } /*! @@ -500,7 +522,7 @@ public: @param value Value @return True if successful */ - inline bool writeP2PReceiverConfiguration(uint8_t& value) + inline bool writeP2PReceiverConfiguration(const uint8_t value) { return write_register8(st25r3916::command::REG_P2P_RECEIVER_CONFIGURATION, value); } @@ -518,7 +540,7 @@ public: @param value Value @return True if successful */ - inline bool writeCorrelatorConfiguration1(uint8_t& value) + inline bool writeCorrelatorConfiguration1(const uint8_t value) { return write_register8(st25r3916::command::REG_CORRELATOR_CONFIGURATION_1, value); } @@ -536,7 +558,7 @@ public: @param value Value @return True if successful */ - inline bool writeCorrelatorConfiguration2(uint8_t& value) + inline bool writeCorrelatorConfiguration2(const uint8_t value) { return write_register8(st25r3916::command::REG_CORRELATOR_CONFIGURATION_2, value); } @@ -554,7 +576,7 @@ public: @param value Value (MSB cfg1,cfg2 LSB) @return True if successful */ - inline bool writeCorrelatorConfiguration(uint16_t& value) + inline bool writeCorrelatorConfiguration(const uint16_t value) { return write_register16(st25r3916::command::REG_CORRELATOR_CONFIGURATION_1, value); } @@ -739,7 +761,7 @@ public: @param value Value @return True if successful */ - inline bool writeSquelchTimer(uint8_t value) + inline bool writeSquelchTimer(const uint8_t value) { return write_register8(st25r3916::command::REG_SQUELCH_TIMER, value); } @@ -757,7 +779,7 @@ public: @param value Value @return True if successful */ - inline bool writeNFCFieldOnGuardTimer(uint8_t& value) + inline bool writeNFCFieldOnGuardTimer(const uint8_t value) { return write_register8(st25r3916::command::REG_NFC_FIELD_ON_GUARD_TIMER, value); } @@ -779,7 +801,7 @@ public: @param value Value @return True if successful */ - inline bool writeMaskMainInterrupt(const uint8_t& value) + inline bool writeMaskMainInterrupt(const uint8_t value) { return write_register8(st25r3916::command::REG_MASK_MAIN_INTERRUPT, value); } @@ -797,7 +819,7 @@ public: @param value Value @return True if successful */ - inline bool writeMaskTimerAndNFCInterrupt(const uint8_t& value) + inline bool writeMaskTimerAndNFCInterrupt(const uint8_t value) { return write_register8(st25r3916::command::REG_MASK_TIMER_AND_NFC_INTERRUPT, value); } @@ -815,7 +837,7 @@ public: @param value Value @return True if successful */ - inline bool writeMaskErrorAndWakeupInterrupt(const uint8_t& value) + inline bool writeMaskErrorAndWakeupInterrupt(const uint8_t value) { return write_register8(st25r3916::command::REG_MASK_ERROR_AND_WAKEUP_INTERRUPT, value); } @@ -833,7 +855,7 @@ public: @param value Value @return True if successful */ - inline bool writeMaskPassiveTargetInterrupt(const uint8_t& value) + inline bool writeMaskPassiveTargetInterrupt(const uint8_t value) { return write_register8(st25r3916::command::REG_MASK_PASSIVE_TARGET_INTERRUPT, value); } @@ -1652,7 +1674,7 @@ public: bool readICIdentity(uint8_t& type, uint8_t& rev); ///@} - // ---------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- ///@name NFC-A ///@{ /*! @@ -1779,6 +1801,38 @@ public: bool ntagReadPage(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage); ///@} + // ---------------------------------------------------------------------------------------------- + ///@name NFC-F + ///@{ + /*! + @brief Transceive + @param rx Receive buffer + @param[in/out] rx_len in:Size of receive buffer out:actual read size + @param tx Send buffer + @param tx_len Size of send buffer + @param timeout_ms Timeout(ms) + @return True if successful + */ + bool nfcfTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, + const uint32_t timeout_ms); + /*! + @brief Polling + @param[out] PICC detected PICC + @param system_code System code + @param request_code Request code + @param time_slot Maximum number of slots that can be responded + @return True if successful + @note SENSF_REQ + */ + bool nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_code, const m5::nfc::f::RequestCode request_code, + const m5::nfc::f::TimeSlot time_slot); + + + bool nfcfRequestService(const m5::nfc::f::PICC& picc); + + + ///@} + // For debug void dumpRegister(); @@ -1789,10 +1843,9 @@ protected: bool read_register8(const uint16_t reg, uint8_t& v); bool write_register8(const uint8_t reg, const uint8_t v); bool write_register8(const uint16_t reg, const uint8_t v); - bool set_bit_register8(const uint8_t reg, const uint8_t bitss); - bool set_bit_register8(const uint16_t reg, const uint8_t bitss); - bool clear_bit_register8(const uint8_t reg, const uint8_t clear_bitss); - bool clear_bit_register8(const uint16_t reg, const uint8_t clear_bitss); + bool modify_bit_register8(const uint8_t reg, const uint8_t set_mask, const uint8_t clear_mask); + bool modify_bit_register8(const uint16_t reg, const uint8_t set_mask, const uint8_t clear_mask); + bool read_register16(const uint8_t reg, uint16_t& v); bool read_register16(const uint16_t reg, uint16_t& v); bool write_register16(const uint8_t reg, const uint16_t v); @@ -1811,19 +1864,25 @@ protected: bool wait_for_FIFO(const uint32_t timeout_ms, const uint16_t required_size = 0); bool read_FIFO(std::vector& out); + bool configure_nfc_a(); + bool configure_nfc_b(); + bool configure_nfc_f(); + bool configure_nfc_v(); + bool nfc_initial_field_on(); + + // NFC-A bool nfca_request_wakeup(uint16_t& atqa, const bool req); bool nfca_anti_collision(uint8_t rbuf[5], const uint8_t lv); - bool mifare_classic_send_encrypt(const uint8_t* tx, const uint16_t tx_len); bool mifare_classic_transceive_encrypt(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms, const bool include_crc, const bool decrypt); bool mifare_classic_authenticate(const m5::nfc::a::Command cmd, const m5::nfc::a::UID& uid, const uint8_t block, const m5::nfc::a::mifare::classic::Key& key); - bool ntag_get_version(uint8_t info[10]); private: config_t _cfg{}; + m5::nfc::NFC _nfcMode{}; m5::nfc::a::mifare::classic::Crypto1 _crypto1{}; bool _encrypted{}; volatile bool _interrupt_occurred{}; diff --git a/src/unit/unit_ST25R3916_nfca.cpp b/src/unit/unit_ST25R3916_nfca.cpp index e9a4ae8..ec1a7d9 100644 --- a/src/unit/unit_ST25R3916_nfca.cpp +++ b/src/unit/unit_ST25R3916_nfca.cpp @@ -11,20 +11,24 @@ #include using namespace m5::utility::mmh3; + using namespace m5::unit::types; using namespace m5::unit::st25r3916; using namespace m5::unit::st25r3916::regval; using namespace m5::unit::st25r3916::command; + +using namespace m5::nfc; using namespace m5::nfc::a; using namespace m5::nfc::a::mifare; using namespace m5::nfc::a::mifare::classic; -using namespace m5::unit::st25r3916; -using namespace m5::unit::st25r3916::regval; -using namespace m5::unit::st25r3916::command; -using namespace m5::nfc::a; -using namespace m5::nfc::a::mifare; -using namespace m5::nfc::a::mifare::classic; +#define CHECK_MODE() \ + do { \ + if (!isNFCMode(NFC::A)) { \ + M5_LIB_LOGE("Illegal mode %u", NFCMode()); \ + return false; \ + } \ + } while (0) namespace { void suc_23(const uint32_t Nt, uint32_t& suc2, uint32_t& suc3) @@ -95,11 +99,37 @@ void append_parity(uint8_t* out, const uint32_t out_len, const uint8_t* in, cons namespace m5 { namespace unit { - // -------------------------------- For NFC-A +bool UnitST25R3916::configure_nfc_a() +{ +#if 0 + // + // ISO14443A + // M5_LIB_LOGE(">>>>>> try ISO14443A REQA"); + writeInitiatorOperationMode(InitiatorOperationMode::ISO14443A, 0x01 /* nfc_ar01 */); + writeBitrate(Bitrate::FC128_106Kbits, Bitrate::FC128_106Kbits); + writeSettingsISO14443A(0x0); +#endif + + if (!writeInitiatorOperationMode(InitiatorOperationMode::ISO14443A, nfc_ar8_auto) || // + !writeBitrate(Bitrate::FC128_106Kbits, Bitrate::FC128_106Kbits) || // + !writeSettingsISO14443A(0x00)) { + return false; + } + + return writeReceiverConfiguration1(0x08) && // z600k + writeReceiverConfiguration2(0x2D) && // sqm_dyn , agc_en, agc_m, agc6_3, + writeReceiverConfiguration3(0x00) && // + writeReceiverConfiguration4(0x00) && // + writeMaskInterrupts(0) && // + nfc_initial_field_on(); +} + bool UnitST25R3916::nfcaTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms) { + CHECK_MODE(); + const auto rx_len_org = rx_len; rx_len = 0; if (!rx || !rx_len_org || !tx || !tx_len) { @@ -135,6 +165,8 @@ bool UnitST25R3916::nfcaTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* bool UnitST25R3916::nfca_request_wakeup(uint16_t& atqa, const bool request) { + CHECK_MODE(); + _encrypted = false; atqa = 0; @@ -243,6 +275,8 @@ bool UnitST25R3916::nfcaSelectWithAnticollision(bool& completed, UID& uid, const completed = false; _encrypted = false; + CHECK_MODE(); + if (lv < 1 || lv > 3) { return false; } @@ -297,6 +331,8 @@ bool UnitST25R3916::nfcaSelect(const UID& uid) { _encrypted = false; + CHECK_MODE(); + if (!uid.valid()) { return false; } @@ -340,18 +376,10 @@ bool UnitST25R3916::nfcaSelect(const UID& uid) bool UnitST25R3916::nfcaHlt() { + CHECK_MODE(); + const uint8_t hlt_frame[2] = {m5::stl::to_underlying(Command::HLTA), 0x00}; -#if 0 - - if (!write_noresponse_timeout(TIMEOUT_HALT) || !writeSettingsISO14443A(0x00 /*standard*/) || - !writeAuxiliaryDefinition(0) || - - !clearInterrupts() || !writeDirectCommand(CMD_CLEAR_FIFO) || !writeFIFO(hlta, sizeof(hlta)) || - !writeNumberOfTransmittedBytes(sizeof(hlta), 0) || !writeDirectCommand(CMD_TRANSMIT_WITH_CRC)) { - return false; - } -#else if (_encrypted) { if (!write_noresponse_timeout(TIMEOUT_HALT) || !mifare_classic_send_encrypt(hlt_frame, sizeof(hlt_frame))) { return false; @@ -365,7 +393,6 @@ bool UnitST25R3916::nfcaHlt() return false; } } -#endif _encrypted = false; // No response is coming back, so need to confirm if it was sent auto irq = wait_for_interrupt(I_txe32, TIMEOUT_HALT); @@ -374,6 +401,8 @@ bool UnitST25R3916::nfcaHlt() bool UnitST25R3916::nfcaReadBlock(uint8_t rx[16], const uint8_t addr) { + CHECK_MODE(); + if (!rx) { return false; } @@ -387,6 +416,8 @@ bool UnitST25R3916::nfcaReadBlock(uint8_t rx[16], const uint8_t addr) bool UnitST25R3916::nfcaWriteBlock(const uint8_t addr, const uint8_t tx[16]) { + CHECK_MODE(); + if (!tx) { return false; } @@ -431,6 +462,8 @@ bool UnitST25R3916::nfcaWriteBlock(const uint8_t addr, const uint8_t tx[16]) bool UnitST25R3916::nfcaWritePage(const uint8_t page, const uint8_t tx[4]) { + CHECK_MODE(); + if (!tx) { return false; } @@ -566,6 +599,8 @@ bool UnitST25R3916::mifare_classic_transceive_encrypt(uint8_t* rx, uint16_t& rx_ bool UnitST25R3916::mifare_classic_authenticate(const Command cmd, const UID& uid, const uint8_t block, const Key& mkey) { + CHECK_MODE(); + if ((cmd != Command::AUTH_WITH_KEY_A && cmd != Command::AUTH_WITH_KEY_B) || !uid.isMifareClassic()) { return false; } @@ -655,6 +690,8 @@ bool UnitST25R3916::mifare_classic_authenticate(const Command cmd, const UID& ui bool UnitST25R3916::mifareClassicValueBlock(const m5::nfc::a::Command cmd, const uint8_t block, const uint32_t arg) { + CHECK_MODE(); + if (cmd != Command::DECREMENT && cmd != Command::INCREMENT && cmd != Command::RESTORE && cmd != Command::TRANSFER) { return false; } @@ -692,6 +729,8 @@ bool UnitST25R3916::mifareClassicValueBlock(const m5::nfc::a::Command cmd, const // -------------------------------- For NTAG bool UnitST25R3916::ntagReadPage(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) { + CHECK_MODE(); + if (spage > epage) { return false; } diff --git a/src/unit/unit_ST25R3916_nfcf.cpp b/src/unit/unit_ST25R3916_nfcf.cpp new file mode 100644 index 0000000..4f412aa --- /dev/null +++ b/src/unit/unit_ST25R3916_nfcf.cpp @@ -0,0 +1,411 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file unit_ST25R3916_nfcf.cpp + @brief class UnitST25R3916 implementation for NFC-F +*/ +#include "unit_ST25R3916.hpp" +#include + +using namespace m5::utility::mmh3; + +using namespace m5::unit::types; +using namespace m5::unit::st25r3916; +using namespace m5::unit::st25r3916::regval; +using namespace m5::unit::st25r3916::command; + +using namespace m5::nfc; +using namespace m5::nfc::f; + +#define CHECK_MODE() \ + do { \ + if (!isNFCMode(NFC::F)) { \ + M5_LIB_LOGE("Illegal mode %u", NFCMode()); \ + return false; \ + } \ + } while (0) + +namespace { +/* +uint8_t val_table[] = { + + 0x07, 0x3C, 0xCB, 0x1C, 0x11, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x13, 0x3D, 0x00, 0x00, 0x28, + 0x06, 0x11, 0x22, 0x02, 0xCA, 0x80, 0x85, 0xA6, 0x0F, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x00, 0xDF, 0x82, 0x82, 0x70, 0x5F, 0x13, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +}; +*/ + +} + +namespace m5 { +namespace unit { + +// -------------------------------- For NFC-F +bool UnitST25R3916::configure_nfc_f() +{ + if (!writeInitiatorOperationMode(InitiatorOperationMode::FeliCa, tr_am) || // + !writeBitrate(Bitrate::FC64_212Kbits, Bitrate::FC64_212Kbits)) { + return false; + } + + return modify_bit_register8(REG_OPERATION_CONTROL, en_fd_c1 | en_fd_c0, 0x00) && // + writeIOConfiguration1(0x07) && // + // writeAuxiliaryDefinition(nfc_n0) && // + writeAuxiliaryDefinition(0x00) && // + writeReceiverConfiguration1(0x13) && // + writeReceiverConfiguration2(0x3D) && // + writeReceiverConfiguration3(0x00) && // + writeReceiverConfiguration4(0x00) && // + writeCorrelatorConfiguration1(0x54) && // + writeCorrelatorConfiguration2(0x00) && // + writeMaskInterrupts(0) && // + nfc_initial_field_on(); + +#if 0 + if (ret) { + uint8_t reg = 0x00; + for (auto&& v : val_table) { + // if (reg < 0x3F) { + // if(reg <= 0x20){ // OK + // if(reg <= 0x10){ // NG + // if(reg <= 0x18){ // OK + // if(reg <= 0x14){ // NG + // if(reg <= 0x16){ // OK + // if(reg <= 0x15){ // NG + if (reg >= 0x16 && reg <= 0x19) { + write_register8(reg, v); + } + ++reg; + } + } + return true; +#endif +} + +bool UnitST25R3916::nfcfTransceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, + const uint32_t timeout_ms) +{ + CHECK_MODE(); + + const auto rx_len_org = rx_len; + rx_len = 0; + + if (!tx || !tx_len) { + return false; + } + + if (timeout_ms && !write_noresponse_timeout(timeout_ms)) { + return false; + } + + if (!writeAuxiliaryDefinition(0) || !clearInterrupts() || !writeDirectCommand(CMD_CLEAR_FIFO) || + !writeFIFO(tx, tx_len) || !writeNumberOfTransmittedBytes(tx_len, 0) || + !writeDirectCommand(CMD_TRANSMIT_WITH_CRC)) { + return false; + } + + // Send only + if (!rx && !rx_len) { + return (wait_for_interrupt(I_txe32, timeout_ms) & I_txe32) != 0; + } + + if (rx && rx_len_org) { + if (!wait_for_FIFO(timeout_ms, rx_len_org)) { + M5_LIB_LOGV("NFC-F Timeout"); + return false; + } + uint16_t actual{}; + if (readFIFO(actual, rx, rx_len_org)) { + rx_len = actual; + return true; + } + M5_LIB_LOGE("Failed to readFIFO %u/%u", actual, rx_len_org); + } + return false; +} + +bool UnitST25R3916::nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_code, + const m5::nfc::f::RequestCode request_code, const m5::nfc::f::TimeSlot time_slot) +{ + CHECK_MODE(); + + picc = {}; + + uint8_t packet[] = {m5::stl::to_underlying(CommandCode::Polling), (uint8_t)(system_code >> 8), + (uint8_t)(system_code & 0xFF), m5::stl::to_underlying(request_code), + m5::stl::to_underlying(time_slot)}; + + // m5::utility::log::dump(packet, sizeof(packet), false); + + uint8_t timeout_ms = TIMEOUT_POLLING * TIMEOUT_POLLING_PICC * timeslot_to_slot(time_slot); + + uint8_t rbuf[128]{}; + uint16_t rx_len{128}; + if (!nfcfTransceive(rbuf, rx_len, packet, sizeof(packet), timeout_ms) // + || rx_len < 20 || rbuf[1] != m5::stl::to_underlying(ResponseCode::Polling)) { + if (rx_len) { + M5_LIB_LOGE("Failed to Polling %u %u %u", rx_len, rbuf[0], rbuf[1]); + } + return false; + } + + // m5::utility::log::dump(rbuf, rx_len, false); + + memcpy(picc.idm.data(), rbuf + 2, picc.idm.size()); + memcpy(picc.pmm.data(), rbuf + 10, picc.pmm.size()); + picc.request_code = request_code; + if (rbuf[0] >= 20) { + picc.request_data = ((uint16_t)rbuf[18]) << 0; + picc.request_data |= (uint16_t)rbuf[19]; + } + return true; +} + +bool UnitST25R3916::nfcfRequestService(const m5::nfc::f::PICC& picc) +{ + CHECK_MODE(); + + std::vector packet{}; + uint8_t node_size = 1; + packet.resize(1 + 8 + 1 + (2 * node_size)); + + uint8_t timeout_ms = 10; + + packet[0] = m5::stl::to_underlying(CommandCode::RequestService); + memcpy(packet.data() + 1, picc.idm.data(), picc.idm.size()); + packet[9] = node_size; + packet[10] = 0xAA; // todo + packet[11] = 0xBB; // toddo + + uint8_t rbuf[packet.size()]{}; + uint16_t rx_len{packet.size()}; + + if (!nfcfTransceive(rbuf, rx_len, packet.data(), packet.size(), timeout_ms) // + || rx_len < packet.size() || rbuf[1] != m5::stl::to_underlying(ResponseCode::RequestService)) { + if (rx_len) { + M5_LIB_LOGE("Failed to RequestService %u %u", rx_len, rbuf[0]); + } + return false; + } + + m5::utility::log::dump(rbuf, rx_len, false); + + return true; +} + +} // namespace unit +} // namespace m5 + +#if 0 +/* +RFAL +14:34:49.261 > DUMP:0x3fcebc8b 5 bytes +14:34:49.267 > 0x3fcebc8b| 00 FF FF 00 03 |..... +14:34:49.268 > SpaceA +14:34:49.270 > Reg[0x00]:0x07:00000111 +14:34:49.272 > Reg[0x01]:0x3C:00111100 +14:34:49.274 > Reg[0x02]:0xCB:11001011 +14:34:49.276 > Reg[0x03]:0x1C:00011100 +14:34:49.278 > Reg[0x04]:0x11:00010001 +14:34:49.280 > Reg[0x05]:0x00:00000000 +14:34:49.283 > Reg[0x06]:0x00:00000000 +14:34:49.285 > Reg[0x07]:0x00:00000000 +14:34:49.287 > Reg[0x08]:0x5D:01011101 +14:34:49.289 > Reg[0x09]:0x00:00000000 +14:34:49.291 > Reg[0x0A]:0x00:00000000 +14:34:49.293 > Reg[0x0B]:0x13:00010011 +14:34:49.296 > Reg[0x0C]:0x3D:00111101 +14:34:49.298 > Reg[0x0D]:0x00:00000000 +14:34:49.300 > Reg[0x0E]:0x00:00000000 +14:34:49.302 > Reg[0x0F]:0x28:00101000 +14:34:49.304 > Reg[0x10]:0x06:00000110 +14:34:49.306 > Reg[0x11]:0x11:00010001 +14:34:49.309 > Reg[0x12]:0x22:00100010 +14:34:49.311 > Reg[0x13]:0x02:00000010 +14:34:49.313 > Reg[0x14]:0xCA:11001010 +14:34:49.315 > Reg[0x15]:0x80:10000000 +14:34:49.317 > Reg[0x16]:0x85:10000101 Mask M_osc, RFU, M_col +14:34:49.319 > Reg[0x17]:0xA6:10100110 M_dct, M_gpe, M_cac, M_cat +14:34:49.322 > Reg[0x18]:0x0F:00001111 +14:34:49.324 > Reg[0x19]:0x7B:01111011 M_sl_wl, M_apon, M_rxe_pta, M_wu_f, M_wu_a_, M_wu_a +14:34:49.326 > Reg[0x1A]:0x00:00000000 +14:34:49.328 > Reg[0x1B]:0x00:00000000 +14:34:49.330 > Reg[0x1C]:0x00:00000000 +14:34:49.332 > Reg[0x1D]:0x00:00000000 +14:34:49.335 > Reg[0x1E]:0x00:00000000 +14:34:49.337 > Reg[0x1F]:0x00:00000000 +14:34:49.339 > Reg[0x20]:0x00:00000000 +14:34:49.341 > Reg[0x21]:0x00:00000000 +14:34:49.343 > Reg[0x22]:0x00:00000000 +14:34:49.345 > Reg[0x23]:0x28:00101000 +14:34:49.348 > Reg[0x24]:0x00:00000000 +14:34:49.350 > Reg[0x25]:0xDF:11011111 +14:34:49.352 > Reg[0x26]:0x82:10000010 +14:34:49.354 > Reg[0x27]:0x82:10000010 +14:34:49.356 > Reg[0x28]:0x70:01110000 +14:34:49.358 > Reg[0x29]:0x5F:01011111 +14:34:49.361 > Reg[0x2A]:0x13:00010011 +14:34:49.363 > Reg[0x2B]:0x02:00000010 +14:34:49.365 > Reg[0x2C]:0x00:00000000 +14:34:49.367 > Reg[0x2D]:0x00:00000000 +14:34:49.369 > Reg[0x2E]:0x00:00000000 +14:34:49.372 > Reg[0x2F]:0x00:00000000 +14:34:49.374 > Reg[0x30]:0x00:00000000 +14:34:49.376 > Reg[0x31]:0x10:00010000 +14:34:49.378 > Reg[0x32]:0x00:00000000 +14:34:49.380 > Reg[0x33]:0x00:00000000 +14:34:49.382 > Reg[0x34]:0x00:00000000 +14:34:49.385 > Reg[0x35]:0x00:00000000 +14:34:49.387 > Reg[0x36]:0x00:00000000 +14:34:49.389 > Reg[0x37]:0x00:00000000 +14:34:49.391 > Reg[0x38]:0x00:00000000 +14:34:49.393 > Reg[0x39]:0x00:00000000 +14:34:49.395 > Reg[0x3A]:0x00:00000000 +14:34:49.398 > Reg[0x3B]:0x00:00000000 +14:34:49.400 > Reg[0x3C]:0x00:00000000 +14:34:49.402 > Reg[0x3D]:0x00:00000000 +14:34:49.404 > Reg[0x3E]:0x00:00000000 +14:34:49.406 > Reg[0x3F]:0x2A:00101010 +14:34:49.407 > SpaceB +14:34:49.409 > Reg[0x00]:0x40:01000000 +14:34:49.411 > Reg[0x01]:0x14:00010100 +14:34:49.414 > Reg[0x02]:0x0C:00001100 +14:34:49.416 > Reg[0x03]:0x54:01010100 +14:34:49.418 > Reg[0x04]:0x00:00000000 +14:34:49.420 > Reg[0x05]:0x00:00000000 +14:34:49.422 > Reg[0x06]:0x00:00000000 +14:34:49.424 > Reg[0x07]:0x10:00010000 +14:34:49.426 > Reg[0x08]:0x7C:01111100 +14:34:49.429 > Reg[0x09]:0x80:10000000 +14:34:49.431 > Reg[0x0A]:0x04:00000100 +14:34:49.433 > Reg[0x0B]:0xD0:11010000 +14:34:49.435 > Reg[0x0C]:0x00:00000000 +14:34:49.437 > Reg[0x0D]:0x00:00000000 +14:34:49.439 > Reg[0x0E]:0x00:00000000 +14:34:49.442 > Reg[0x0F]:0x00:00000000 +14:34:49.445 > ST25R3916_CMD_TRANSMIT_WITH_CRC + +---- +MINE +14:36:51.267 > DUMP:0x3fcebce7 5 bytes +14:36:51.267 > 0x3fcebce7| 00 FF FF 00 03 |..... +14:36:51.269 > [ 4100][I][unit_ST25R3916.cpp:627] dumpRegister(): SpaceA +14:36:51.275 > [ 4102][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X00]:0X00:00000000 +14:36:51.282 > [ 4109][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X01]:0X98:10011000 +14:36:51.289 > [ 4115][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X02]:0X88:10001000 +14:36:51.295 > [ 4122][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X03]:0X1C:00011100 +14:36:51.302 > [ 4129][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X04]:0X11:00010001 +14:36:51.309 > [ 4135][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X05]:0X00:00000000 +14:36:51.315 > [ 4142][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X06]:0X00:00000000 +14:36:51.322 > [ 4149][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X07]:0X00:00000000 +14:36:51.329 > [ 4155][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X08]:0X00:00000000 +14:36:51.336 > [ 4162][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X09]:0X00:00000000 +14:36:51.342 > [ 4169][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0A]:0X01:00000001 +14:36:51.349 > [ 4175][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0B]:0X13:00010011 +14:36:51.356 > [ 4182][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0C]:0X3D:00111101 +14:36:51.362 > [ 4189][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0D]:0X00:00000000 +14:36:51.369 > [ 4195][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0E]:0X00:00000000 +14:36:51.376 > [ 4202][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X0F]:0X0C:00001100 +14:36:51.382 > [ 4209][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X10]:0X4F:01001111 +14:36:51.389 > [ 4215][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X11]:0X74:01110100 +14:36:51.396 > [ 4222][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X12]:0X00:00000000 +14:36:51.402 > [ 4229][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X13]:0X00:00000000 +14:36:51.409 > [ 4236][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X14]:0X00:00000000 +14:36:51.416 > [ 4242][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X15]:0X80:10000000 + +14:36:51.422 > [ 4249][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X16]:0XFF:11111111 +14:36:51.429 > [ 4256][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X17]:0XFF:11111111 +14:36:51.436 > [ 4262][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X18]:0X00:00000000 +14:36:51.443 > [ 4269][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X19]:0XFB:11111011 + +14:34:49.317 > Reg[0x16]:0x85:10000101 Mask M_osc, RFU, M_col +14:34:49.319 > Reg[0x17]:0xA6:10100110 M_dct, M_gpe, M_cac, M_cat +14:34:49.322 > Reg[0x18]:0x0F:00001111 Error mask +14:34:49.324 > Reg[0x19]:0x7B:01111011 M_sl_wl, M_apon, M_rxe_pta, M_wu_f, M_wu_a_, M_wu_a + + + +14:36:51.449 > [ 4276][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1A]:0X00:00000000 +14:36:51.456 > [ 4282][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1B]:0X00:00000000 +14:36:51.463 > [ 4289][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1C]:0X00:00000000 +14:36:51.469 > [ 4296][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1D]:0X00:00000000 +14:36:51.476 > [ 4302][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1E]:0X00:00000000 +14:36:51.483 > [ 4309][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X1F]:0X00:00000000 +14:36:51.489 > [ 4316][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X20]:0X00:00000000 +14:36:51.496 > [ 4322][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X21]:0X00:00000000 +14:36:51.503 > [ 4329][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X22]:0X00:00000000 +14:36:51.509 > [ 4336][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X23]:0X28:00101000 +14:36:51.516 > [ 4342][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X24]:0X00:00000000 +14:36:51.523 > [ 4349][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X25]:0X00:00000000 +14:36:51.529 > [ 4356][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X26]:0X80:10000000 +14:36:51.536 > [ 4363][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X27]:0X80:10000000 +14:36:51.543 > [ 4369][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X28]:0XD0:11010000 +14:36:51.549 > [ 4376][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X29]:0X60:01100000 +14:36:51.556 > [ 4383][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2A]:0X33:00110011 +14:36:51.563 > [ 4389][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2B]:0X22:00100010 +14:36:51.569 > [ 4396][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2C]:0X00:00000000 +14:36:51.576 > [ 4403][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2D]:0X00:00000000 +14:36:51.583 > [ 4409][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2E]:0X00:00000000 +14:36:51.589 > [ 4416][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X2F]:0X00:00000000 +14:36:51.596 > [ 4423][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X30]:0X00:00000000 +14:36:51.603 > [ 4429][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X31]:0X10:00010000 +14:36:51.610 > [ 4436][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X32]:0X00:00000000 +14:36:51.616 > [ 4443][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X33]:0X00:00000000 +14:36:51.623 > [ 4449][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X34]:0X00:00000000 +14:36:51.630 > [ 4456][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X35]:0X00:00000000 +14:36:51.636 > [ 4463][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X36]:0X00:00000000 +14:36:51.643 > [ 4469][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X37]:0X00:00000000 +14:36:51.650 > [ 4476][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X38]:0X00:00000000 +14:36:51.656 > [ 4483][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X39]:0X00:00000000 +14:36:51.663 > [ 4490][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3A]:0X00:00000000 +14:36:51.670 > [ 4496][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3B]:0X00:00000000 +14:36:51.676 > [ 4503][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3C]:0X00:00000000 +14:36:51.683 > [ 4510][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3D]:0X00:00000000 +14:36:51.690 > [ 4516][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3E]:0X00:00000000 +14:36:51.696 > [ 4523][I][unit_ST25R3916.cpp:631] dumpRegister(): Reg[0X3F]:0X2A:00101010 +14:36:51.702 > [ 4530][I][unit_ST25R3916.cpp:652] dumpRegister(): SpaceB +14:36:51.708 > [ 4535][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X05]:0X00:00000000 +14:36:51.715 > [ 4542][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X06]:0X00:00000000 +14:36:51.722 > [ 4548][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X0B]:0X0C:00001100 +14:36:51.728 > [ 4555][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X0C]:0X54:01010100 +14:36:51.735 > [ 4562][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X0D]:0X00:00000000 +14:36:51.742 > [ 4568][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X0F]:0X00:00000000 +14:36:51.748 > [ 4575][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X15]:0X33:00110011 +14:36:51.755 > [ 4582][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X28]:0X10:00010000 +14:36:51.762 > [ 4588][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X29]:0X7C:01111100 +14:36:51.768 > [ 4595][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X2A]:0X00:00000000 +14:36:51.775 > [ 4602][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X2B]:0X04:00000100 +14:36:51.782 > [ 4608][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X2C]:0XF0:11110000 +14:36:51.789 > [ 4615][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X30]:0X00:00000000 +14:36:51.795 > [ 4622][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X31]:0X00:00000000 +14:36:51.802 > [ 4628][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X32]:0X00:00000000 +14:36:51.809 > [ 4635][I][unit_ST25R3916.cpp:656] dumpRegister(): Reg[0X33]:0X00:00000000 + +14:34:49.407 > SpaceB +14:34:49.409 > Reg[0x05]:0x40:01000000 +14:34:49.411 > Reg[0x06]:0x14:00010100 +14:34:49.414 > Reg[0x0B]:0x0C:00001100 +14:34:49.416 > Reg[0x0C]:0x54:01010100 +14:34:49.418 > Reg[0x0D]:0x00:00000000 +14:34:49.420 > Reg[0x0F]:0x00:00000000 +14:34:49.422 > Reg[0x15]:0x00:00000000 +14:34:49.424 > Reg[0x28]:0x10:00010000 +14:34:49.426 > Reg[0x29]:0x7C:01111100 +14:34:49.429 > Reg[0x2A]:0x80:10000000 +14:34:49.431 > Reg[0x2B]:0x04:00000100 +14:34:49.433 > Reg[0x2C]:0xD0:11010000 +14:34:49.435 > Reg[0x30]:0x00:00000000 +14:34:49.437 > Reg[0x31]:0x00:00000000 +14:34:49.439 > Reg[0x32]:0x00:00000000 +14:34:49.442 > Reg[0x33]:0x00:00000000 + + + +*/ + +#endif