diff --git a/examples/UnitUnified/NFCA/Detect/main/Detect.cpp b/examples/UnitUnified/NFCA/Detect/main/Detect.cpp index 8eb04cc..91bcc2f 100644 --- a/examples/UnitUnified/NFCA/Detect/main/Detect.cpp +++ b/examples/UnitUnified/NFCA/Detect/main/Detect.cpp @@ -114,6 +114,8 @@ void loop() M5.Log.printf("%zu PICC\n", piccs.size()); uint32_t idx{}; for (auto&& u : piccs) { + // detect only performs a provisionalclassification based on sak, so further identification is required + nfc_a.identify(u); M5.Log.printf("PICC:%s %s %u/%u\n", u.uidAsString().c_str(), u.typeAsString().c_str(), u.userAreaSize(), u.totalSize()); lcd.printf("[%2u]:PICC:<%s> %s\n", idx, u.uidAsString().c_str(), u.typeAsString().c_str()); diff --git a/examples/UnitUnified/NFCA/Dump/main/Dump.cpp b/examples/UnitUnified/NFCA/Dump/main/Dump.cpp index 998e074..a57db91 100644 --- a/examples/UnitUnified/NFCA/Dump/main/Dump.cpp +++ b/examples/UnitUnified/NFCA/Dump/main/Dump.cpp @@ -116,18 +116,16 @@ void loop() if (M5.BtnA.wasClicked()) { lcd.fillRect(0, lcd.fontHeight(), lcd.width(), lcd.height() - lcd.fontHeight()); - std::vector piccs; - if (nfc_a.detect(piccs)) { - // If multiple occurrences are detected, only the first one detected - auto& picc = piccs.front(); - if (nfc_a.reactivate(picc)) { + PICC picc{}; + if (nfc_a.detect(picc)) { + if (nfc_a.identify(picc) && nfc_a.reactivate(picc)) { M5.Speaker.tone(3000, 20); M5.Log.printf("==== Dump %s %s %u/%u ====\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); nfc_a.dump(keyA); // Need key if MIFARE classic, Ignore key if not MIFARE classic nfc_a.deactivate(); } else { - M5_LOGE("Failed to activate %s", picc.uidAsString().c_str()); + M5_LOGE("Failed to identify/activate %s", picc.uidAsString().c_str()); } } else { M5.Log.printf("PICC NOT exists\n"); diff --git a/examples/UnitUnified/NFCA/NDEF/main/NDEF.cpp b/examples/UnitUnified/NFCA/NDEF/main/NDEF.cpp index 6e3baf4..27a310b 100644 --- a/examples/UnitUnified/NFCA/NDEF/main/NDEF.cpp +++ b/examples/UnitUnified/NFCA/NDEF/main/NDEF.cpp @@ -248,14 +248,14 @@ void loop() if (clicked || held) { PICC picc{}; if (nfc_a.detect(picc)) { - if (nfc_a.reactivate(picc)) { + if (nfc_a.identify(picc) && nfc_a.reactivate(picc)) { M5.Log.printf("PICC:%s %s %u/%u\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); if (picc.supportsNFC()) { if (clicked) { M5.Speaker.tone(2000, 30); lcd.fillScreen(TFT_BLUE); - //nfc_a.dump(); + nfc_a.dump(); read_ndef(); } else if (held) { M5.Speaker.tone(4000, 30); @@ -265,8 +265,11 @@ void loop() M5.Log.printf("Please remove the PICC from the reader\n"); } else { + M5.Speaker.tone(1000, 50); M5.Log.printf("Not support the NDEF\n"); } + } else { + M5_LOGE("Failed to identify/activate %s", picc.uidAsString().c_str()); } nfc_a.deactivate(); lcd.setCursor(0, 0); @@ -277,23 +280,3 @@ void loop() } } } -/* - 6:19:30.599 > Page :00 01 02 03 -16:19:30.605 > -------------------- -16:19:30.605 > [000/00]:04 49 37 F2 -16:19:30.605 > [001/01]:D2 A6 1C 90 -16:19:30.610 > [002/02]:F8 48 00 00 -16:19:30.610 > [003/03]:E1 10 12 00 -16:19:30.610 > [004/04]:03 0F D1 01 -16:19:30.616 > [005/05]:0B 54 02 65 -16:19:30.616 > [006/06]:6E 33 37 42 -16:19:30.616 > [007/07]:37 34 43 37 -16:19:30.622 > [008/08]:41 FE 00 00 -16:19:30.622 > [009/09]:00 00 00 00 -16:19:30.622 > [010/0A]:00 00 00 00 -16:19:30.627 > [011/0B]:00 00 00 00 -16:19:30.627 > [012/0C]:00 00 00 00 -16:19:30.627 > [013/0D]:00 00 00 00 -16:19:30.632 > [014/0E]:00 00 00 00 -16:19:30.632 > [015/0F]:00 00 00 00 -*/ diff --git a/examples/UnitUnified/NFCA/ReadWrite/main/ReadWrite.cpp b/examples/UnitUnified/NFCA/ReadWrite/main/ReadWrite.cpp index 609c534..5daedb5 100644 --- a/examples/UnitUnified/NFCA/ReadWrite/main/ReadWrite.cpp +++ b/examples/UnitUnified/NFCA/ReadWrite/main/ReadWrite.cpp @@ -267,7 +267,7 @@ void loop() PICC picc; if (nfc_a.detect(picc)) { lcd.fillScreen(TFT_DARKGREEN); - if (nfc_a.reactivate(picc)) { + if (nfc_a.identify(picc) && nfc_a.reactivate(picc)) { M5.Log.printf("PICC:%s %s %u/%u\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); @@ -288,7 +288,10 @@ void loop() } } nfc_a.deactivate(); + } else { + M5_LOGE("Failed to identify/activate %s", picc.uidAsString().c_str()); } + } else { M5.Log.printf("PICC NOT exists\n"); } diff --git a/examples/UnitUnified/NFCA/ValueBlock/main/ValueBlock.cpp b/examples/UnitUnified/NFCA/ValueBlock/main/ValueBlock.cpp index 15dd7a5..2f888d8 100644 --- a/examples/UnitUnified/NFCA/ValueBlock/main/ValueBlock.cpp +++ b/examples/UnitUnified/NFCA/ValueBlock/main/ValueBlock.cpp @@ -366,7 +366,7 @@ void loop() if (clicked || held) { PICC picc{}; if (nfc_a.detect(picc)) { - if (nfc_a.reactivate(picc)) { + if (nfc_a.identify(picc) && nfc_a.reactivate(picc)) { M5.Log.printf("PICC:%s %s %u/%u\n", picc.uidAsString().c_str(), picc.typeAsString().c_str(), picc.userAreaSize(), picc.totalSize()); if (picc.isMifareClassic()) { @@ -386,6 +386,8 @@ void loop() M5.Log.printf("Not support the value block\n"); } nfc_a.deactivate(); + } else { + M5_LOGE("Failed to identify/activate %s", picc.uidAsString().c_str()); } } else { M5.Log.printf("PICC NOT exists\n"); diff --git a/src/nfc/a/nfca.cpp b/src/nfc/a/nfca.cpp index 07beeb5..51ae4e6 100644 --- a/src/nfc/a/nfca.cpp +++ b/src/nfc/a/nfca.cpp @@ -57,13 +57,14 @@ constexpr const char* name_table[] = { name_unknown, // name_classic_mini, name_classic_1K, name_classic_2K, name_classic_4K, // Classic name_ul, name_ul_ev1_11, name_ul_ev1_21, name_ul_nano, name_ul_c, // Light - name_plus_2K, name_plus_4K, name_plus_se, // Plus - name_desfire_2K, name_desfire_4K, name_desfire_8K, // DESFire name_ntag203, name_ntag210u, name_ntag210, // NTAG name_ntag212, name_ntag213, name_ntag215, // NTAG name_ntag216, // NTAG name_st25ta_2k, name_st25ta_16k, name_st25ta_64k, // ST25A - name_iso14443_4, name_iso18092, // Others + name_iso14443_4, // 14443-4 + name_plus_2K, name_plus_4K, name_plus_se, // Plus + name_desfire_2K, name_desfire_4K, name_desfire_8K, // DESFire + name_iso18092, // }; // @@ -85,11 +86,12 @@ constexpr const char* name_sub_desfire_table[] = {name_sub_desfire_ev1, name_sub constexpr uint16_t max_block_table[] = {0, // Unknown 20, 64, 128, 256, // Classic 16, 20, 40, 14, 48, // Light - 128, 256, 64, // Plus - 0, 0, 0, // DESFire (Not has blocks, File base system) 42, 20, 16, 40, 45, 135, 231, // NTAG 0, 0, 0, // ST25TA - 0, 0}; + 0, // 14443-4 + 128, 256, 64, // Plus + 0, 0, 0, // DESFire (Not has blocks, File base system) + 0}; // [first/last] constexpr uint8_t user_block_table[][2] = { @@ -105,14 +107,6 @@ constexpr uint8_t user_block_table[][2] = { {4, 35}, // Exclusive 0-3 and last 5 pages {4, 13}, // Exclusive 0-3 {4, 39}, // Exclusive 0-3 and last 8 pages - // Plus - {1, 126}, - {1, 254}, - {1, 62}, - // DESFire - {0, 0}, - {0, 0}, - {0, 0}, // NTAG {4, 39}, // 203 Exclusive 0-3 and last 2 pages {4, 15}, // 210u Exclusive 0-3 @@ -127,6 +121,15 @@ constexpr uint8_t user_block_table[][2] = { {0, 0}, // {0, 0}, + // Plus + {1, 126}, + {1, 254}, + {1, 62}, + // DESFire + {0, 0}, + {0, 0}, + {0, 0}, + // {0, 0}, }; @@ -134,11 +137,12 @@ constexpr uint8_t max_sector_table[] = { 0, // Unknown 5, 16, 32, 40, // Classic 0, 0, 0, 0, 0, // Light - 32, 40, 16, // Plus - 0, 0, 0, // Desfire 0, 0, 0, 0, 0, 0, 0, // NTAG 0, 0, 0, // ST25TA - 0, 0, + 0, // + 32, 40, 16, // Plus + 0, 0, 0, // Desfire + 0, }; constexpr uint16_t user_area_size_table[] = { @@ -146,22 +150,24 @@ constexpr uint16_t user_area_size_table[] = { 0, // Unknown 240, 752, 1504, 3440, // Classic 48, 48, 128, 40, 144, // Light + 144, 48, 144, 208, 144, 504, 888, // NTAG + 0, // 1504, 3440, 752, // Plus 0, 0, 0, // Desfire - 144, 48, 144, 208, 144, 504, 888, // NTAG - 0, 0, + 0, }; constexpr NFCForumTag nfc_forum_tag_table[] = { NFCForumTag::None, // NFCForumTag::None, NFCForumTag::None, NFCForumTag::None, NFCForumTag::None, // Classic NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, // Light - NFCForumTag::None, NFCForumTag::None, NFCForumTag::None, // Plus - NFCForumTag::Type4, NFCForumTag::Type4, NFCForumTag::Type4, // DESFire NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, // NTAG NFCForumTag::Type2, NFCForumTag::Type2, NFCForumTag::Type2, // NTAG NFCForumTag::Type4, NFCForumTag::Type4, NFCForumTag::Type4, // ST25TA - NFCForumTag::None, NFCForumTag::None, // + NFCForumTag::None, // + NFCForumTag::None, NFCForumTag::None, NFCForumTag::None, // Plus + NFCForumTag::Type4, NFCForumTag::Type4, NFCForumTag::Type4, // DESFire + NFCForumTag::None, // }; struct Historical { diff --git a/src/nfc/a/nfca.hpp b/src/nfc/a/nfca.hpp index bef4160..162f246 100644 --- a/src/nfc/a/nfca.hpp +++ b/src/nfc/a/nfca.hpp @@ -42,15 +42,6 @@ enum class Type : uint8_t { MIFARE_Ultralight_Nano, //!< MIFARE Ultralight Nano MIFARE_UltralightC, //!< MIFARE UltralightC - MIFARE_Plus_2K, //!< MIFARE Plus 2K - MIFARE_Plus_4K, //!< MIFARE Plus 4K - MIFARE_Plus_SE, //!< MIFARE Plus SE 1K - - MIFARE_DESFire_2K, //!< MIFARE DESFire 2K - MIFARE_DESFire_4K, //!< MIFARE DESFire 4K - MIFARE_DESFire_8K, //!< MIFARE DESFire 8K - // LIGHT - NTAG_203, //!< NATG 203 NTAG_210u, //!< NTAG 210μ NTAG_210, //!< NTAG 210 @@ -64,7 +55,17 @@ enum class Type : uint8_t { ST25TA_64K, //!< ST25TA64K ISO_14443_4, //!< PICC compliant with ISO/IEC 14443-4 - ISO_18092, //!< PICC compliant with ISO/IEC 18092 + + MIFARE_Plus_2K, //!< MIFARE Plus 2K + MIFARE_Plus_4K, //!< MIFARE Plus 4K + MIFARE_Plus_SE, //!< MIFARE Plus SE 1K + + MIFARE_DESFire_2K, //!< MIFARE DESFire 2K + MIFARE_DESFire_4K, //!< MIFARE DESFire 4K + MIFARE_DESFire_8K, //!< MIFARE DESFire 8K + // LIGHT + + ISO_18092, //!< PICC compliant with ISO/IEC 18092 NotCompleted = 0xFF, //!< SAK indicates UID is not complete }; @@ -92,12 +93,6 @@ inline bool is_mifare_classic(const Type t) return t >= Type::MIFARE_Classic_Mini && t <= Type::MIFARE_Classic_4K; } -//! @brief Is type MIFARE? -inline bool is_mifare(const Type t) -{ - return t >= Type::MIFARE_Classic_Mini && t <= Type::MIFARE_DESFire_8K; -} - //! @brief Is type MIFARE Ultralight series? inline bool is_mifare_ultralight(const Type t) { @@ -116,6 +111,30 @@ inline bool is_mifare_plus(const Type t) return t >= Type::MIFARE_Plus_2K && t <= Type::MIFARE_Plus_SE; } +//! @brief Is type MIFARE DESFire? +inline bool is_mifare_desfire(const Type t) +{ + return t >= Type::MIFARE_DESFire_2K && t <= Type::MIFARE_DESFire_8K; +} + +//! @brief Is type MIFARE? +inline bool is_mifare(const Type t) +{ + return is_mifare_classic(t) || is_mifare_ultralight(t) || is_mifare_plus(t) || is_mifare_desfire(t); +} + +//! @brief Is ISO 14443-4? +inline bool is_iso14443_4(const Type t) +{ + return is_mifare_plus(t) || is_mifare_desfire(t) || (t == Type::ISO_14443_4); +} + +//! @brief Is ISO 14443-3? +inline bool is_iso14443_3(const Type t) +{ + return !is_iso14443_4(t); +} + //! @brief Does the specified type function as NFC? inline bool supports_NFC(const Type t) { @@ -262,6 +281,10 @@ struct PICC { { return valid() && is_ntag(type); } + inline bool isISO14443_4() const + { + return is_iso14443_4(type); // Don't check valid(), as there may exist where blocks == 0 + } //! @brief Supports NFC? inline bool supportsNFC() const @@ -354,7 +377,8 @@ enum class Command : uint8_t { SELCT_CL3_OPT = 0x96, //!< Select CL3 and swich bit rate to fc/64 after receive SAK READ = 0x30, //!< Read // ISO/IEC 14443-4 - RATS = 0xE0, //!< Request for Answer to Select + RATS = 0xE0, //!< Request for Answer to Select + DESELECT = 0xC2, //!< DESELECT // MIFARE AUTH_WITH_KEY_A = 0x60, //!< MIFARE Classic. Authentication with Key A AUTH_WITH_KEY_B = 0x61, //!< MIFARE Classic. Authentication with Key B @@ -397,6 +421,7 @@ constexpr uint32_t TIMEOUT_WRITE1{5}; constexpr uint32_t TIMEOUT_WRITE2{10}; constexpr uint32_t TIMEOUT_VALUE_BLOCK{5}; // Value block operation constexpr uint32_t TIMEOUT_RATS{5}; +constexpr uint32_t TIMEOUT_DESELECT{5}; ///@} diff --git a/src/nfc/layer/ndef_layer.cpp b/src/nfc/layer/ndef_layer.cpp index 2171751..560c24e 100644 --- a/src/nfc/layer/ndef_layer.cpp +++ b/src/nfc/layer/ndef_layer.cpp @@ -74,7 +74,7 @@ bool NDEFLayer::readCapabilityContainer(m5::nfc::ndef::type2::CapabilityContaine } memcpy(cc.block, rx + ((block_size == 4) ? 0 : 12), sizeof(cc.block)); - M5_LIB_LOGE("CC2:%02X %u.%u %u %02X/%02X", cc.block[0], cc.major_version(), cc.minor_version(), cc.ndef_size(), + M5_LIB_LOGV("CC2:%02X %u.%u %u %02X/%02X", cc.block[0], cc.major_version(), cc.minor_version(), cc.ndef_size(), cc.read_access(), cc.write_access()); return true; @@ -258,7 +258,6 @@ bool NDEFLayer::read_type3(m5::nfc::ndef::TLV& tlv) uint16_t block = _interface.first_user_block(); uint16_t last_block = _interface.last_user_block(); if (block == 0xFFFF || last_block == 0xFFFF) { - M5_LIB_LOGE("ERROR"); return false; } diff --git a/src/nfc/layer/nfc_layer_a.cpp b/src/nfc/layer/nfc_layer_a.cpp index 9aa7f31..61cb3ab 100644 --- a/src/nfc/layer/nfc_layer_a.cpp +++ b/src/nfc/layer/nfc_layer_a.cpp @@ -111,13 +111,13 @@ bool NFCLayerA::detect(std::vector& piccs, const uint32_t timeout_ms) m5::utility::delay(1); continue; } - M5_LIB_LOGE(">>>>ATQA:%04X", picc.atqa); + // M5_LIB_LOGE("ATQA:%04X", picc.atqa); // Select if (!select(picc)) { return false; } - M5_LIB_LOGE("Detect:%04X %s %s", picc.atqa, picc.uidAsString().c_str(), picc.typeAsString().c_str()); + // M5_LIB_LOGE("Detect:%04X %s %s", picc.atqa, picc.uidAsString().c_str(), picc.typeAsString().c_str()); // Hlt if (!deactivate()) { @@ -158,8 +158,8 @@ bool NFCLayerA::reactivate(const PICC& picc) // If arg referrence is the same as _activePICC, it will cause an error, so it must be a separate instance (*1) PICC tmp = picc; uint16_t discard{}; - if (tmp.valid() && deactivate()) { - m5::utility::delay(2); + if (deactivate()) { + // m5::utility::delay(2); // guard time (ULC) return wakeup(discard) && activate(tmp); } return false; @@ -167,8 +167,78 @@ bool NFCLayerA::reactivate(const PICC& picc) bool NFCLayerA::deactivate() { + if (_activePICC.isISO14443_4()) { + if (_impl->deactivate(true)) { + return true; + } + } + _activePICC.clear(); - return _impl->deactivate(); + return _impl->deactivate(false); +} + +bool NFCLayerA::identify(m5::nfc::a::PICC& picc) +{ + bool ret = identify_picc(picc); + deactivate(); + return ret; +} + +bool NFCLayerA::identify_picc(m5::nfc::a::PICC& picc) +{ + Type type{}; + + if (!reactivate(picc)) { + return false; + } + + // ISO_14443_4 series + if (picc.type == Type::ISO_14443_4) { + // RATS + ATS ats{}; + if (!_impl->nfca_request_ats(ats)) { + return false; + } + + // GetVersion(L4) + uint8_t ver[8]{}; + if (_impl->mifare_get_version_L4(ver)) { + type = version4_to_type(picc.sub_type, ver); + } else { + // Check historical bytes + type = historical_bytes_to_type_sak20(picc.sub_type, ats.historical.data(), ats.historical_len, picc.atqa); + } + // If it's still unclassify at this stage, read more in SystemFile + if (type != Type::Unknown) { + picc.type = type; + picc.blocks = get_number_of_blocks(picc.type); + return true; + } + M5_LIB_LOGW("NEED MORE!!"); + return true; + } + + if (picc.type == Type::MIFARE_Ultralight) { + uint8_t ver[8]{}; + // GetVersion(L3) + if (_impl->mifare_get_version_L3(ver)) { + // UL EV, UL Nano, NTAG2xx + picc.type = version3_to_type(ver); + picc.blocks = get_number_of_blocks(picc.type); + return true; + } + // The PICC goes idle when sending an external command, so select again + if (!reactivate(picc)) { + return false; + } + // Try ULC Auth + uint8_t discard_ek[8]{}; + picc.type = + _impl->mifare_ultralightc_authenticate1(discard_ek) ? Type::MIFARE_UltralightC : Type::MIFARE_Ultralight; + picc.blocks = get_number_of_blocks(picc.type); + return true; + } + return true; } bool NFCLayerA::read4(uint8_t rx[4], const uint8_t addr) @@ -456,6 +526,7 @@ bool NFCLayerA::dump(const Key& mkey) } M5_LIB_LOGW("Not supported %s", _activePICC.typeAsString().c_str()); } + M5_LIB_LOGW("Invalid PICC %s", _activePICC.typeAsString().c_str()); return false; } diff --git a/src/nfc/layer/nfc_layer_a.hpp b/src/nfc/layer/nfc_layer_a.hpp index ca24bec..8a9096c 100644 --- a/src/nfc/layer/nfc_layer_a.hpp +++ b/src/nfc/layer/nfc_layer_a.hpp @@ -86,6 +86,8 @@ public: @param timeout_ms Polling time budget in milliseconds @return True if detected @note The detected PICC is typically put into HALT during enumeration to allow discovering others + @note To identify the type, call NFCLayerA::identify + @warning The type of activated PICC is determined solely by SAK and is provisional */ bool detect(m5::nfc::a::PICC& picc, const uint32_t timeout_ms = 100U); /*! @@ -94,12 +96,16 @@ public: @param timeout_ms Polling time budget in milliseconds @return True if detected @note The detected PICC is typically put into HALT during enumeration to allow discovering others + @note To identify the type, call NFCLayerA::identify + @warning The type of activated PICC is determined solely by SAK and is provisional */ bool detect(std::vector& piccs, const uint32_t timeout_ms = 1000U); /*! @brief Select a PICC (anti-collision + SELECT cascade to ACTIVE) @param[out] picc The fully activated PICC (single- or multi-cascade) @return True if successful + @warning The type of activated PICC is determined solely by SAK and is provisional + @note To identify the type, call NFCLayerA::identify @pre A PICC is in the READY state (after REQA/WUPA) @post PICC transitions: READY -> ACTIVE on successful response */ @@ -146,6 +152,15 @@ public: */ bool deactivate(); + /*! + @brief Identify the specified PICC type + @details Classification Based on AN10883 + @param[in/out] picc PICC + @return True if successful + @warning Before calling, the previously active PICC is deactivated + */ + bool identify(m5::nfc::a::PICC& picc); + /*! @brief Read the 1 page @param rx Buffer (at least 4 bytes) @@ -450,6 +465,8 @@ protected: return (_activePICC.supportsNFC()) ? 4 : 16; } + bool identify_picc(m5::nfc::a::PICC& picc); + bool read_using_fast(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr); bool read_using_read16(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr, const m5::nfc::a::mifare::classic::Key& key); @@ -486,8 +503,9 @@ struct NFCLayerA::Adapter { virtual bool select(m5::nfc::a::PICC& picc) = 0; virtual bool activate(const m5::nfc::a::PICC& picc) = 0; - virtual bool deactivate() = 0; + virtual bool deactivate(const bool iso14443_4) = 0; + virtual bool nfca_request_ats(m5::nfc::a::ATS& ats) = 0; virtual bool nfca_read_block(uint8_t rx[16], const uint8_t addr) = 0; // READ virtual bool nfca_write_block(const uint8_t addr, const uint8_t tx[16]) = 0; // WRITE_BLOCK virtual bool nfca_write_page(const uint8_t addr, const uint8_t tx[4]) = 0; // WRITE_PAGE @@ -498,6 +516,9 @@ struct NFCLayerA::Adapter { const uint32_t arg = 0) = 0; virtual bool mifare_ultralightC_authenticate1(uint8_t ek[8]) = 0; virtual bool mifare_ultralightC_authenticate2(uint8_t rx_ek[8], const uint8_t tx_ek[16]) = 0; + virtual bool mifare_get_version_L3(uint8_t ver[8]) = 0; + virtual bool mifare_get_version_L4(uint8_t ver[8]) = 0; + virtual bool mifare_ultralightc_authenticate1(uint8_t ek[8]) = 0; virtual bool ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) = 0; // FAST_READ diff --git a/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp b/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp index 02972db..a51e811 100644 --- a/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp +++ b/src/nfc/layer/unit/nfc_layer_a_ST25R3916.cpp @@ -37,8 +37,9 @@ struct AdapterST25R3916ForA final : NFCLayerA::Adapter { virtual bool select(m5::nfc::a::PICC& picc) override; virtual bool activate(const m5::nfc::a::PICC& picc) override; - virtual bool deactivate() override; + virtual bool deactivate(const bool iso14443_4) override; + virtual bool nfca_request_ats(m5::nfc::a::ATS& ats) override; virtual bool nfca_read_block(uint8_t rx[16], const uint8_t addr) override; // READ virtual bool nfca_write_block(const uint8_t addr, const uint8_t tx[16]) override; // WRITE_BLOCK virtual bool nfca_write_page(const uint8_t addr, const uint8_t tx[4]) override; // WRITE_PAGE @@ -49,6 +50,9 @@ struct AdapterST25R3916ForA final : NFCLayerA::Adapter { const uint32_t arg = 0) override; virtual bool mifare_ultralightC_authenticate1(uint8_t ek[8]) override; virtual bool mifare_ultralightC_authenticate2(uint8_t rx_ek[8], const uint8_t tx_ek[16]) override; + virtual bool mifare_get_version_L3(uint8_t ver[8]) override; + virtual bool mifare_get_version_L4(uint8_t ver[8]) override; + virtual bool mifare_ultralightc_authenticate1(uint8_t ek[8]) override; virtual bool ntag_read_page(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) override; // FAST_READ @@ -84,9 +88,14 @@ bool AdapterST25R3916ForA::activate(const PICC& picc) return _u.nfcaSelect(picc); } -bool AdapterST25R3916ForA::deactivate() +bool AdapterST25R3916ForA::deactivate(const bool iso14443_4) { - return _u.nfcaHlt(); + return iso14443_4 ? _u.nfcaDeselect() : _u.nfcaHlt(); +} + +bool AdapterST25R3916ForA::nfca_request_ats(m5::nfc::a::ATS& ats) +{ + return _u.nfcaRequestATS(ats); } bool AdapterST25R3916ForA::nfca_read_block(uint8_t rx[16], const uint8_t addr) @@ -99,11 +108,6 @@ bool AdapterST25R3916ForA::nfca_write_block(const uint8_t addr, const uint8_t tx return _u.nfcaWriteBlock(addr, tx); } -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 AdapterST25R3916ForA::nfca_write_page(const uint8_t addr, const uint8_t tx[4]) { return _u.nfcaWritePage(addr, tx); @@ -131,6 +135,26 @@ bool AdapterST25R3916ForA::mifare_ultralightC_authenticate2(uint8_t rx_ek[8], co return _u.mifareUltralightCAuthenticate2(rx_ek, tx_ek); } +bool AdapterST25R3916ForA::mifare_get_version_L3(uint8_t ver[8]) +{ + return _u.mifareGetVersion3(ver); +} + +bool AdapterST25R3916ForA::mifare_get_version_L4(uint8_t ver[8]) +{ + return _u.mifareGetVersion4(ver); +} + +bool AdapterST25R3916ForA::mifare_ultralightc_authenticate1(uint8_t ek[8]) +{ + return _u.mifareUltralightCAuthenticate1(ek); +} + +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); +} + // namespace { std::unique_ptr make_st25r3916_adapter(UnitST25R3916& u) diff --git a/src/unit/unit_ST25R3916.hpp b/src/unit/unit_ST25R3916.hpp index ad40072..77dc2e3 100644 --- a/src/unit/unit_ST25R3916.hpp +++ b/src/unit/unit_ST25R3916.hpp @@ -1741,6 +1741,7 @@ public: @param[out] picc Selected PICC @param lv Cascade level (1-3) @return True if successful + @warning The type of activated PICC is determined solely by SAK and is provisional */ bool nfcaSelectWithAnticollision(bool& completed, m5::nfc::a::PICC& picc, const uint8_t lv); /*! @@ -1777,8 +1778,24 @@ public: @return True if successful */ bool nfcaHlt(); + + /*! + @brief Request for answer to select (RATS) + @param[out] ats Answer to select (ATS) + @param fsdi Frame Size for PCD Integer + @param cid Card Identifier + @return True if successful + */ + bool nfcaRequestATS(m5::nfc::a::ATS& ats, const uint8_t fsdi = 5, const uint8_t cid = 0); + /*! + @brief Deselect ISO/IEC 14443-4 PICC + @return True if successful + @note Call before nfcaHlt if ISO/IEC 14443-4 PICC + */ + bool nfcaDeselect(); ///@} + // ---------------------------------------------------------------------------------------------- ///@name MIFARE ///@{ /*! @@ -1829,6 +1846,18 @@ public: @return True if successful */ bool mifareUltralightCAuthenticate2(uint8_t rx_ek[8], const uint8_t tx_ek[16]); + /*! + @brief GetVersion (L3) + @param[out] ver Version information + @return True if successful + */ + bool mifareGetVersion3(uint8_t ver[8]); + /*! + @brief GetVersion (L4) + @param[out] ver Version information + @return True if successful + */ + bool mifareGetVersion4(uint8_t ver[8]); ///@} ///@name NTAG @@ -1845,25 +1874,6 @@ public: bool ntagReadPage(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage); ///@} - ///@name ISO/IEC 14443-4 - ///@{ - /*! - @brief Request for answer to select (RATS) - @param[out] ats Answer to select (ATS) - @param fsdi Frame Size for PCD Integer - @param cid Card Identifier - @return True if successful - */ - bool iso144434RequestATS(m5::nfc::a::ATS& ats, const uint8_t fsdi = 5, const uint8_t cid = 0); - /*! - @brief Deselect ISO/IEC 14443-4 PICC - @return True if successful - @note Call before nfcaHlt if ISO/IEC 14443-4 PICC - */ - bool iso14434Deselect(); - - ///@} - // ---------------------------------------------------------------------------------------------- ///@name NFC-B ///@{ @@ -2119,9 +2129,6 @@ protected: bool mifare_classic_authenticate(const m5::nfc::a::Command cmd, const m5::nfc::a::PICC& picc, const uint8_t block, const m5::nfc::a::mifare::classic::Key& key); - bool mifare_get_version3(uint8_t info[8]); - bool mifare_get_version4(uint8_t info[8]); - // NFC-V bool nfcv_transmit(const uint8_t* tx, const uint16_t tx_len, const m5::nfc::v::ModulationMode mode, const uint32_t timeout_ms); diff --git a/src/unit/unit_ST25R3916_nfca.cpp b/src/unit/unit_ST25R3916_nfca.cpp index b67b696..47d8ac3 100644 --- a/src/unit/unit_ST25R3916_nfca.cpp +++ b/src/unit/unit_ST25R3916_nfca.cpp @@ -146,12 +146,7 @@ uint32_t UnitST25R3916::nfcaTransceive(uint8_t* rx, uint16_t& rx_len, const uint } if (!wait_for_FIFO(timeout_ms, rx_len_org)) { - M5_LIB_LOGE("Timeout"); - /* - if (tx) { - m5::utility::log::dump(tx, tx_len, false); - } - */ + M5_LIB_LOGD("Timeout"); return false; } @@ -304,11 +299,19 @@ bool UnitST25R3916::nfcaSelectWithAnticollision(bool& completed, PICC& picc, con } uint8_t sak = rbuf[0]; - M5_LIB_LOGE(">>>> SAK:%02X (%u, %u) %u ", // - sak, is_sak_completed(sak), is_sak_completed_14443_4(sak), sak_to_type(sak)); + // M5_LIB_LOGE(">>>> SAK:%02X (%u, %u) %u ", // + // sak, is_sak_completed(sak), is_sak_completed_14443_4(sak), sak_to_type(sak)); - // Need RATS? if (is_sak_completed_14443_4(sak)) { + picc.size = 1 + lv * 3; + picc.sak = sak; + picc.type = + Type::ISO_14443_4; // WARNING: This is a preliminary diagnosis; a more accurate diagnosis is required + picc.blocks = get_number_of_blocks(picc.type); + completed = true; + return true; + } +#if 0 ATS ats{}; // RATS if (!iso144434RequestATS(ats)) { @@ -324,8 +327,8 @@ bool UnitST25R3916::nfcaSelectWithAnticollision(bool& completed, PICC& picc, con picc.blocks = get_number_of_blocks(picc.type); } else { // Check historical bytes - m5::utility::log::dump(ats.header, sizeof(ats.header), false); - m5::utility::log::dump(ats.historical.data(), ats.historical_len, false); + //m5::utility::log::dump(ats.header, sizeof(ats.header), false); + //m5::utility::log::dump(ats.historical.data(), ats.historical_len, false); picc.type = historical_bytes_to_type_sak20(picc.sub_type, ats.historical.data(), ats.historical_len, picc.atqa); picc.blocks = get_number_of_blocks(picc.type); @@ -338,18 +341,19 @@ bool UnitST25R3916::nfcaSelectWithAnticollision(bool& completed, PICC& picc, con } return true; } +#endif // Completed? if (is_sak_completed(sak)) { - completed = true; picc.size = 1 + lv * 3; picc.sak = sak; picc.type = sak_to_type(sak); // WARNING: This is a preliminary diagnosis; a more accurate diagnosis is required picc.blocks = get_number_of_blocks(picc.type); - + completed = true; // M5_LIB_LOGE(">>>> tmp type %s", picc.typeAsString().c_str()); +#if 0 // More detailed type identification if (picc.type == Type::MIFARE_Ultralight) { picc.type = Type::Unknown; @@ -379,6 +383,7 @@ bool UnitST25R3916::nfcaSelectWithAnticollision(bool& completed, PICC& picc, con completed = nfcaWakeup(discard) && nfcaSelect(picc); } } +#endif } // M5_LIB_LOGE(">>>> Select %02X %u %u", sak, completed, has_sak_dependent_bit(sak)); return completed || has_sak_dependent_bit(sak); // completed or continue @@ -422,7 +427,7 @@ bool UnitST25R3916::nfcaSelect(const PICC& picc) M5_LIB_LOGE("Failed to select"); return false; } - completed = is_sak_completed(rbuf[0]); + completed = is_sak_completed(rbuf[0]) || is_sak_completed_14443_4(rbuf[0]); ++lv; } while (!completed && lv < 4); @@ -542,6 +547,39 @@ bool UnitST25R3916::nfcaWritePage(const uint8_t page, const uint8_t tx[4]) return false; } +bool UnitST25R3916::nfcaRequestATS(m5::nfc::a::ATS& ats, const uint8_t fsdi, const uint8_t cid) +{ + uint8_t rx[128]{}; + uint16_t rx_len = sizeof(rx); + uint8_t cmd[2] = {m5::stl::to_underlying(Command::RATS)}; + cmd[1] = ((fsdi & 0x0F) << 4) | (cid & 0x0F); + + if (!nfcaTransceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_RATS) || rx_len < 7) { + return false; + } + // M5_LIB_LOGE(">>>>ATS %u bytes", rx_len); + // m5::utility::log::dump(rx, rx_len, false); + + memcpy(ats.header, rx, 5); + if (7 < rx_len) { + memcpy(ats.historical.data(), rx + 5, rx_len - 7); + } + ats.historical_len = rx_len - 7; + return true; +} + +bool UnitST25R3916::nfcaDeselect() +{ + uint8_t rx[1]{}; + uint16_t rx_len = sizeof(rx); + uint8_t cmd[1] = {m5::stl::to_underlying(Command::DESELECT)}; + if (!nfcaTransceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_DESELECT) || !rx_len) { + return false; + } + // m5::utility::log::dump(rx, rx_len, false); + return true; +} + // -------------------------------- For MIFARE classic bool UnitST25R3916::mifare_transceive(uint8_t* rx, uint16_t& rx_len, const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms) @@ -824,7 +862,7 @@ bool UnitST25R3916::mifareUltralightCAuthenticate2(uint8_t rx_ek[8], const uint8 return false; } -bool UnitST25R3916::mifare_get_version3(uint8_t info[8]) +bool UnitST25R3916::mifareGetVersion3(uint8_t info[8]) { // GetVerison (L3) uint8_t cmd[1] = {m5::stl::to_underlying(Command::GET_VERSION)}; @@ -832,7 +870,7 @@ bool UnitST25R3916::mifare_get_version3(uint8_t info[8]) return info && mifare_transceive(info, rx_len, cmd, sizeof(cmd), TIMEOUT_GET_VERSION); } -bool UnitST25R3916::mifare_get_version4(uint8_t info[8]) +bool UnitST25R3916::mifareGetVersion4(uint8_t info[8]) { // GetVerison (L4) uint8_t cmd[2] = {0x02, m5::stl::to_underlying(Command::GET_VERSION)}; @@ -841,7 +879,6 @@ bool UnitST25R3916::mifare_get_version4(uint8_t info[8]) if (info && mifare_transceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_GET_VERSION) && rx_len >= 10) { // M5_LIB_LOGE(">>>> VER L4"); // m5::utility::log::dump(rx,rx_len, false); - const uint8_t* p = rx; uint8_t* q = info; while (p < rx + rx_len && q < info + 8) { @@ -856,33 +893,6 @@ bool UnitST25R3916::mifare_get_version4(uint8_t info[8]) return false; } -// -------------------------------- For ISO/IEC 14443-4 -bool UnitST25R3916::iso144434RequestATS(m5::nfc::a::ATS& ats, const uint8_t fsdi, const uint8_t cid) -{ - uint8_t rx[128]{}; - uint16_t rx_len = sizeof(rx); - uint8_t cmd[2] = {m5::stl::to_underlying(Command::RATS)}; - cmd[1] = ((fsdi & 0x0F) << 4) | (cid & 0x0F); - - if (!nfcaTransceive(rx, rx_len, cmd, sizeof(cmd), TIMEOUT_RATS) || rx_len < 7) { - return false; - } - // M5_LIB_LOGE(">>>>ATS %u bytes", rx_len); - // m5::utility::log::dump(rx, rx_len, false); - - memcpy(ats.header, rx, 5); - if (7 < rx_len) { - memcpy(ats.historical.data(), rx + 5, rx_len - 7); - } - ats.historical_len = rx_len - 7; - return true; -} - -bool UnitST25R3916::iso14434Deselect() -{ - return false; -} - // -------------------------------- For NTAG bool UnitST25R3916::ntagReadPage(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage) {