Add read/write without encryption

This commit is contained in:
GOB
2025-12-01 17:03:08 +09:00
parent b4e2f0cb7d
commit fce9487624
13 changed files with 1050 additions and 123 deletions
+1 -1
View File
@@ -5,6 +5,6 @@
*/
/*
Example using M5UnitUnified for M5Cardputer-ADV with HackerCap
Detect NFC-A PICC
Detect NFC-F PICC
*/
#include "main/Detect.cpp"
@@ -71,10 +71,6 @@ void setup()
}
lcd.setFont(&fonts::Font0);
lcd.fillScreen(0);
//
// cap.dumpRegister();
//
}
void loop()
@@ -83,13 +79,12 @@ void loop()
Units.update();
std::vector<PICC> piccs;
// if (nfc_f.detect(piccs)) {
if (nfc_f.detect(piccs, 0x0003)) {
if (nfc_f.detect(piccs)) {
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);
M5.Log.printf(" %s:%s %s F:%02X DF:%04X\n", picc.idmAsString().c_str(), picc.pmmAsString().c_str(),
picc.typeAsString().c_str(), picc.format, picc.dfc_format);
}
}
}
+10
View File
@@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for M5Cardputer-ADV with HackerCap
Dump NFC-F PICC
*/
#include "main/Dump.cpp"
@@ -0,0 +1,92 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Example using M5UnitUnified for M5Cardputer-ADV with HackerCap
Dump NFC-F PICC
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedNFC.h>
#include <M5Utility.h>
#include <vector>
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 */);
}
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);
lcd.setCursor(0, 0);
lcd.printf("Please put the PICC and click G0");
M5.Log.printf("Please put the PICC and click G0\n");
}
void loop()
{
M5.update();
Units.update();
if (M5.BtnA.wasClicked()) {
lcd.fillRect(0, lcd.fontHeight(), lcd.width(), lcd.height() - lcd.fontHeight());
PICC picc{};
if (nfc_f.detect(picc)) {
M5.Log.printf("==== Dump %s:%s %s ====\n", picc.idmAsString().c_str(), picc.pmmAsString().c_str(),
picc.typeAsString().c_str());
nfc_f.dump(picc);
} else {
M5.Log.printf("PICC NOT exists\n");
}
}
}
+2 -2
View File
@@ -211,9 +211,9 @@ struct UID {
return valid() ? is_user_block(type, block) : false;
}
//! @breif Gets the uid string for debug
//! @brief Gets the uid string
std::string uidAsString() const;
//! @breif Gets the type string for debug
//! @brief Gets the type string
std::string typeAsString() const;
//! @brief clear
void clear()
+54
View File
@@ -12,6 +12,21 @@
#include <M5Utility.hpp>
namespace {
constexpr char name_unknown[] = "Unknown";
constexpr char name_standard[] = "FeliCa Standard";
constexpr char name_lite[] = "FeliCa Lite";
constexpr char name_lite_s[] = "FeliCa Lite-S";
constexpr char name_plug[] = "FeliCa Plug";
constexpr const char* name_table[] = {name_unknown, name_standard, name_lite, name_lite_s, name_plug};
// Maximum block number (Note that there are gaps in the blocks)
constexpr uint16_t max_block_table[] = {0, 0, 0x88, 0xA0, 0};
// Maximum number of blocks that can be read simultaneously
constexpr uint16_t max_read_block_table[] = {1, 8, 4, 4, 4};
// [first/last]
constexpr uint8_t user_block_table[][2] = {{0XFF, 0XFF}, {0XFF, 0XFF}, {0x00, 0x0D}, {0x00, 0x0D}, {0XFF, 0XFF}};
std::string to_string(const uint8_t* p, const uint8_t size)
{
char buf[2 * size + 1]{};
@@ -29,6 +44,39 @@ namespace m5 {
namespace nfc {
namespace f {
uint16_t get_maximum_block(const Type t)
{
uint8_t idx = m5::stl::to_underlying(t);
return max_block_table[idx < m5::stl::size(max_block_table) ? idx : 0];
}
uint16_t get_number_of_user_blocks(const Type t)
{
uint8_t idx = m5::stl::to_underlying(t);
auto p = user_block_table[idx < m5::stl::size(user_block_table) ? idx : 0];
uint8_t sz = p[1] - p[0];
return sz ? (sz + 1) : 0;
}
uint16_t get_first_user_block(const Type t)
{
uint8_t idx = m5::stl::to_underlying(t);
return user_block_table[idx < m5::stl::size(user_block_table) ? idx : 0][0];
}
uint16_t get_last_user_block(const Type t)
{
uint8_t idx = m5::stl::to_underlying(t);
return user_block_table[idx < m5::stl::size(user_block_table) ? idx : 0][1];
}
uint8_t get_maxumum_read_blocks(const Type t)
{
uint8_t idx = m5::stl::to_underlying(t);
return max_read_block_table[idx < m5::stl::size(max_block_table) ? idx : 0];
}
//
std::string PICC::idmAsString() const
{
return to_string(this->idm.data(), this->idm.size());
@@ -39,6 +87,12 @@ std::string PICC::pmmAsString() const
return to_string(this->pmm.data(), this->pmm.size());
}
std::string PICC::typeAsString() const
{
const auto idx = m5::stl::to_underlying(this->type);
return std::string((idx <= m5::stl::size(name_table)) ? name_table[idx] : name_unknown);
}
} // namespace f
} // namespace nfc
} // namespace m5
+323 -30
View File
@@ -21,58 +21,78 @@ namespace nfc {
*/
namespace f {
using IDm = std::array<uint8_t, 8>; //!< Manufacture ID
using PMm = std::array<uint8_t, 8>; //!< Manufacture Parameter
/*!
@enum Type
@brief Type of the PICC
*/
enum class Type : uint8_t {
Unknown, //!< Unknown type
FeliCaStandard,
FeliCaLiteS,
FelicaPlugin,
Unknown, //!< Unknown type
FeliCaStandard, //!< Standard
FeliCaLite, //!< Lite
FeliCaLiteS, //!< Lite-S
FeliCaPlug, //!< Plug
// FeliCaLink, //!< Link
};
/*!
@enum Mode
@brief NFC-F Mode status
*/
enum class Mode : uint8_t {
Mode0, //!< Power was supplied to the PICC
Mode1, //!< Certification for PICC has been completed (Auth1)
Mode2, //!< After mutual authentication is complete (Auth2)
Mode3, //!< After registering area services or executing system partitioning
};
///@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};
constexpr Format format_nfcip1{0x0001}; //!< Support ISO/IEC18092
constexpr Format format_dfc{0x0002}; //!< Has DFC
constexpr Format format_private{0x0004}; //!< Has private area
constexpr Format format_ndef{0x0008}; //!< Support NDEF
constexpr Format format_shared{0x0010}; //!< Has shared area
constexpr Format format_secure{0x0020}; //!< FeliCa Secure ID
constexpr Format format_felica_plug{0x0040}; //< FeliCa Plug
///@}
using IDm = std::array<uint8_t, 8>; //!< Manufacture ID
using PMm = std::array<uint8_t, 8>; //!< Manufacture Parameter
/*!
@enum CommandCode
@brief NFC-F Command code
*/
enum class CommandCode : uint8_t {
Polling,
RequestService = 0x02,
RequestResponse = 0x04,
RequestService = 0x02,
RequestResponse = 0x04,
ReadWithoutEncryption = 0x06,
WriteWithoutEncryption = 0x08,
};
/*!
@enum ResponseCode
@brief NFC-F Response code
*/
enum class ResponseCode : uint8_t {
Polling = 0x01,
RequestService = 0x03,
Polling = 0x01,
RequestService = 0x03,
RequestResponse = 0x05,
ReadWithoutEncryption = 0x07,
WriteWithoutEncryption = 0x09,
};
///@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
constexpr uint16_t system_code_wildcard{0xFFFF}; //!< Wildcard
constexpr uint16_t system_code_ndef{0x12FC}; //!< NDEF
constexpr uint16_t system_code_felica_secure_id{0x957A}; //!< FeliCa secure ID
constexpr uint16_t system_code_shared{0xFE00}; //!< Shared area
constexpr uint16_t system_code_dfc{0x88B4}; //!< Lite, Lite-S
constexpr uint16_t system_code_felica_plug{0xFEE1}; //!< FeliCa Plug
///@}
/*!
@@ -102,24 +122,214 @@ inline constexpr uint8_t timeslot_to_slot(const TimeSlot ts)
: 0; // Illegal
}
///@name Service attribute
///@{
// Random service
constexpr uint16_t service_random_read_write_auth{0x0008}; //!< Random,Read/write,Authentication required (S)
constexpr uint16_t service_random_read_write{0x0009}; //!< Random,Read/write,No authentication required (S, L, LS)
constexpr uint16_t service_random_read_auth{0x000A}; //!< Random,Read only,No authentication required (S)
constexpr uint16_t service_random_read{0x000B}; //!< Random,Read only,No authentication required (S,LS)
// Cyclic service
constexpr uint16_t service_cyclic_read_write_auth{0x000C}; //!< Cyclic,Read/write,Authentication required(S)
constexpr uint16_t service_cyclic_read_write{0x000D}; //!< Cyclic,Read/write,No athentication required(S)
constexpr uint16_t service_cyclic_read_auth{0x000E}; //!< Cyclic,Read only,Authentication required(S)
constexpr uint16_t service_cyclic_read{0x000F}; //!< Cyclic,Read only,No authentication required(S)
// Parse service
constexpr uint16_t service_parse_direct_auth{0x0100};
constexpr uint16_t service_parse_direct{0x0101};
constexpr uint16_t service_parse_cacheback_auth{0x0102};
constexpr uint16_t service_parse_cacheback{0x0103};
constexpr uint16_t service_parse_decrement_auth{0x0104};
constexpr uint16_t service_parse_decrement{0x0105};
constexpr uint16_t service_parse_increment_auth{0x0106};
constexpr uint16_t service_parse_increment{0x0107};
///@}
/*!
@strutc PICC
@brief PICC informationg for NFC-F
@struct block_t
@brief Block list element
*/
struct block_t {
uint8_t pad{};
uint8_t header{}; //!< size:1 access:3 order:4
uint16_t number{}; //!< block number (using low byte if 2 byte mode)
inline block_t() : block_t(0)
{
}
inline constexpr block_t(const uint16_t num, const uint8_t access = 0, const uint8_t order = 0)
: header{(uint8_t)(((num > 0xFF) ? 0x00 : 0x80) | ((access & 0x03) << 4) | (order & 0x0F))}, number{num}
{
}
inline constexpr bool is_2byte() const
{
return (header & 0x80) != 0;
}
inline constexpr bool is_3byte() const
{
return (header & 0x80) == 0;
}
inline constexpr uint8_t access_mode() const
{
return (header >> 4) & 0x07;
}
inline constexpr uint8_t ordere() const
{
return (header & 0x0F);
}
inline constexpr uint16_t block() const
{
return number;
}
inline operator uint16_t() const
{
return block();
}
};
/*!
@namespacce lite
@brief For FeliCa Lite
*/
namespace lite {
///@name
constexpr block_t S_PAD0{0x00};
constexpr block_t S_PAD1{0x01};
constexpr block_t S_PAD2{0x02};
constexpr block_t S_PAD3{0x03};
constexpr block_t S_PAD4{0x04};
constexpr block_t S_PAD5{0x05};
constexpr block_t S_PAD6{0x06};
constexpr block_t S_PAD7{0x07};
constexpr block_t S_PAD8{0x08};
constexpr block_t S_PAD9{0x09};
constexpr block_t S_PAD10{0x0A};
constexpr block_t S_PAD11{0x0B};
constexpr block_t S_PAD12{0X0C};
constexpr block_t S_PAD13{0x0D};
constexpr block_t REG{0x0E};
constexpr block_t RC{0x80};
constexpr block_t MAC{0x81};
constexpr block_t ID{0x082};
constexpr block_t D_ID{0x83};
constexpr block_t SER_C{0x84};
constexpr block_t SYS_C{0x85};
constexpr block_t CKV{0x86};
constexpr block_t CK{0x87};
constexpr block_t MC{0x88};
} // namespace lite
/*!
@namespacce lite_s
@brief For FeliCa Lite-S
*/
namespace lite_s {
constexpr block_t S_PAD0{0x00};
constexpr block_t S_PAD1{0x01};
constexpr block_t S_PAD2{0x02};
constexpr block_t S_PAD3{0x03};
constexpr block_t S_PAD4{0x04};
constexpr block_t S_PAD5{0x05};
constexpr block_t S_PAD6{0x06};
constexpr block_t S_PAD7{0x07};
constexpr block_t S_PAD8{0x08};
constexpr block_t S_PAD9{0x09};
constexpr block_t S_PAD10{0x0A};
constexpr block_t S_PAD11{0x0B};
constexpr block_t S_PAD12{0X0C};
constexpr block_t S_PAD13{0x0D};
constexpr block_t REG{0x0E};
constexpr block_t RC{0x80};
constexpr block_t MAC{0x81};
constexpr block_t ID{0x082};
constexpr block_t D_ID{0x83};
constexpr block_t SER_C{0x84};
constexpr block_t SYS_C{0x85};
constexpr block_t CKV{0x86};
constexpr block_t CK{0x87};
constexpr block_t MC{0x88};
constexpr block_t WCNT{0x90};
constexpr block_t MAC_A{0x91};
constexpr block_t STATE{0x92};
constexpr block_t CRC_CHECK{0xA0};
} // namespace lite_s
//! @brief Gets the maximum block
uint16_t get_maximum_block(const Type t);
//! @brief Gets the number of user blocks
uint16_t get_number_of_user_blocks(const Type t);
//!@brief Gets the user area bytes
inline uint16_t get_user_area_size(const Type t)
{
return 16 * get_number_of_user_blocks(t);
}
//! @brief Gets the first user area block number
uint16_t get_first_user_block(const Type t);
//! @brief Gets the last user area block number
uint16_t get_last_user_block(const Type t);
//! @brief Is block user area?
inline bool is_user_block(const Type t, const uint16_t block)
{
return (block >= get_first_user_block(t)) && (block <= get_last_user_block(t));
}
//! @brief Maximum number of blocks that can be read simultaneously
uint8_t get_maxumum_read_blocks(const Type t);
///@name RequestService
///@{
constexpr uint16_t NODE_SYSTEM_KEY{0xFFFF}; //!< Retrieving the System Key Version
constexpr uint16_t KEY_VERIOSN_NONE{0xFFFF}; //!< No key version exists
///@}
/*!
@struct PICC
@brief PICC information for NFC-F
*/
struct PICC {
IDm idm{}; //!< Manufacture ID
PMm pmm{}; //!< //!< Manufacture Parameter
uint16_t request_data{}; //!< Any request data
PMm pmm{}; //!< Manufacture Parameter
uint16_t request_data{}; //!< Any request data if exists
RequestCode request_code{}; //!< Tyepe of the request_data
Type type{}; //!< PICC Type
Format format{}; //!< Format type group bits
uint16_t dfc_format{}; //!< DFC format (ID[8],ID[9] LE) if format include DFC
//! @breif Gets the IDm string for debug
//! @brief Valid?
inline bool valid() const
{
return type != Type::Unknown;
}
//! @brief Total user area size
inline uint16_t userAreaSize() const
{
return valid() ? get_user_area_size(type) : 0;
}
//! @brief Gets the first user block/page address
inline uint16_t firstUserBlock() const
{
return valid() ? get_first_user_block(type) : 0xFFFF;
}
inline uint16_t lastUserBlock() const
{
return valid() ? get_last_user_block(type) : 0xFFFF;
}
inline bool isUserBlock(const block_t block) const
{
return is_user_block(type, block);
}
//! @brief Gets the IDm string
std::string idmAsString() const;
//! @breif Gets the PMm string for debug
//! @brief Gets the PMm string
std::string pmmAsString() const;
//! @brief Gets the type string
std::string typeAsString() const;
};
//! @brief Equal?
//! @brief Equal? (Only IDm,PMm)
inline bool operator==(const PICC& a, const PICC& b)
{
return a.idm == b.idm && a.pmm == b.pmm;
@@ -135,6 +345,89 @@ inline bool operator!=(const PICC& a, const PICC& b)
constexpr uint32_t TIMEOUT_POLLING{3};
constexpr uint32_t TIMEOUT_POLLING_PICC{2}; // 2 ms per PICC
/*!
@struct REG
@brief Subtract Register Block Data
*/
union REG {
uint8_t reg[16]{};
struct {
uint8_t reg_a[4]; // RegA
uint8_t reg_b[4]; // RegB
uint8_t reg_c[8]; // RegC
} __attribute__((packed));
REG() {};
//!@brief Gets the RegA
inline uint32_t regA() const
{
return ((uint32_t)reg_a[3] << 24) | ((uint32_t)reg_a[2] << 16) | ((uint32_t)reg_a[1] << 8) |
((uint32_t)reg_a[0]);
}
//!@brief Gets the RegB
inline uint32_t regB() const
{
return ((uint32_t)reg_b[3] << 24) | ((uint32_t)reg_b[2] << 16) | ((uint32_t)reg_b[1] << 8) |
((uint32_t)reg_b[0]);
}
//!@brief Gets the RegC
inline uint64_t regC() const
{
return ((uint64_t)reg_c[0] << 56) | ((uint64_t)reg_c[1] << 48) | ((uint64_t)reg_c[2] << 40) |
((uint64_t)reg_c[3] << 32) | ((uint64_t)reg_c[4] << 24) | ((uint64_t)reg_c[5] << 16) |
((uint64_t)reg_c[6] << 8) | ((uint64_t)reg_c[7]);
}
//!@brief Set the RegA
void regA(const uint32_t v)
{
reg_a[0] = v & 0xFF;
reg_a[1] = v >> 8;
reg_a[2] = v >> 16;
reg_a[3] = v >> 24;
}
//!@brief Set the RegB
void regB(const uint32_t v)
{
reg_b[0] = v & 0xFF;
reg_b[1] = v >> 8;
reg_b[2] = v >> 16;
reg_b[3] = v >> 24;
}
//!@brief Set the RegC
void regC(const uint64_t v)
{
reg_c[0] = v & 0xFF;
reg_c[1] = v >> 8;
reg_c[2] = v >> 16;
reg_c[3] = v >> 24;
reg_c[4] = v >> 32;
reg_c[5] = v >> 40;
reg_c[6] = v >> 48;
reg_c[7] = v >> 56;
}
inline operator const uint8_t*() const
{
return reg;
}
inline operator uint8_t*()
{
return reg;
}
} __attribute__((packed));
/*!
brief Is the new value writable?
@param o Old REG value
@param New REG value
@return true if writable
*/
inline bool can_write_reg(const REG& o, const REG& n)
{
return (o.regA() >= n.regA()) && (o.regB() >= n.regB());
}
} // namespace f
} // namespace nfc
} // namespace m5
+3 -1
View File
@@ -396,14 +396,16 @@ public:
///@}
/*!
@brief Dump all blocks for debug
@brief Dump all blocks
@param key MIFARE classic key
@return True if successful
@pre All blocks must be authenticatable using the specified key if MIFARE classic
*/
bool dump(const m5::nfc::a::mifare::classic::Key& mkey = m5::nfc::a::mifare::classic::DEFAULT_KEY);
/*!
@brief Dump 1 block
@param addr Block address
@return True if successful
@note The sector to which the block belongs is dumped
@pre The block must be authenticated if MIFARE classic
*/
+248 -45
View File
@@ -19,11 +19,10 @@ 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,
};
constexpr uint16_t known_system_code_table[] = {system_code_wildcard, system_code_ndef, system_code_felica_secure_id,
system_code_shared, system_code_dfc, system_code_felica_plug};
inline bool exists_system_code(const uint16_t code)
inline bool exists_known_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);
@@ -34,6 +33,19 @@ inline bool exists_picc(const std::vector<PICC>& v, const PICC& picc)
return std::find_if(v.begin(), v.end(), [&picc](const PICC& p) { return p == picc; }) != v.end();
}
void print_block(const uint8_t buf[16], const int16_t block)
{
char tmp[64 + 1] = " ";
uint32_t left{};
// Block
left += snprintf(tmp + left, 12, "[%04X]:", block);
// Data
for (uint8_t i = 0; i < 16; ++i) {
left += snprintf(tmp + left, 4, "%02X ", buf[i]);
}
::puts(tmp);
}
} // namespace
namespace m5 {
@@ -46,38 +58,19 @@ bool NFCLayerF::polling(m5::nfc::f::PICC& picc, const uint16_t system_code, cons
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)
bool NFCLayerF::detect(m5::nfc::f::PICC& picc, const uint32_t timeout_ms)
{
std::vector<PICC> 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;
}
std::vector<PICC> piccs;
picc = PICC{};
if (detect(piccs, TimeSlot::Slot1, timeout_ms)) {
picc = piccs.front();
return true;
}
return false;
}
bool NFCLayerF::detect(std::vector<m5::nfc::f::PICC>& 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<m5::nfc::f::PICC>& piccs, const uint16_t* private_code, const uint8_t pc_size,
m5::nfc::f::TimeSlot time_slot, const uint32_t timeout_ms)
bool NFCLayerF::detect(std::vector<m5::nfc::f::PICC>& 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);
@@ -88,8 +81,10 @@ bool NFCLayerF::detect_picc(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t
uint8_t detected{};
do {
PICC picc1{}, picc2{};
Type type{};
uint8_t format{};
// Format identification
// 1. Polling wildcard
if (!polling(picc1, system_code_wildcard, RequestCode::None, time_slot)) {
break;
@@ -106,18 +101,28 @@ bool NFCLayerF::detect_picc(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t
format |= format_dfc;
}
if ((picc1.idm[0] & 0x0F) == 0x04 && picc1.idm[1] == 0xFE) {
type = Type::FeliCaStandard;
}
// 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) {
if (private_code && pc_size) {
for (uint_fast8_t i = 0; i < pc_size; ++i) {
auto code = private_code[i];
if (exists_known_system_code(code)) {
M5_LIB_LOGW("Skip %04X (known system code)", code);
continue;
}
format |= format_private;
if (polling(picc2, code, RequestCode::None, time_slot)) {
if (picc1 != picc2) {
continue;
}
format |= format_private;
}
}
// When private code is specified, PICC that do not meet the conditions are invalid
if ((format & format_private) == 0) {
continue;
}
}
@@ -137,29 +142,227 @@ bool NFCLayerF::detect_picc(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t
format |= format_shared;
}
// 6. Check DEF for FeliCa Lite-S
// 6. Check DFC
if (polling(picc2, system_code_dfc, RequestCode::None, time_slot)) {
if (picc1 != picc2) {
continue;
}
// TODO: read lite-S
format |= format_shared;
format |= format_dfc;
}
// 7. Check secure
if (polling(picc2, system_code_secure, RequestCode::None, time_slot)) {
if (polling(picc2, system_code_felica_secure_id, RequestCode::None, time_slot)) {
if (picc1 != picc2) {
continue;
}
format |= format_secure;
}
// Type identification
if (polling(picc2, system_code_felica_plug, RequestCode::None, time_slot)) {
if (picc1 != picc2) {
continue;
}
type = Type::FeliCaPlug;
} else {
// Lite or LiteS?
if (format & format_dfc) {
uint8_t rbuf[16]{};
type = read16(rbuf, picc1, 0xA0 /* CRC_CHECK */) ? Type::FeliCaLiteS : Type::FeliCaLite;
if (!read16(rbuf, picc1, 0x82)) { // Read ID(DFC format)
continue;
}
picc1.dfc_format = rbuf[8] | ((uint16_t)rbuf[9] << 8);
}
}
//
if (type == Type::Unknown && format) {
type = Type::FeliCaStandard;
}
//
picc1.format = format;
picc1.type = type;
piccs.push_back(picc1);
++detected;
} while (detected < slots && m5::utility::millis() <= timeout_at);
return !piccs.empty();
return detected > 0;
}
bool NFCLayerF::requestService(uint16_t& key_version, const m5::nfc::f::PICC& picc, const uint16_t node_code)
{
uint16_t n[1] = {node_code};
uint16_t k[1]{};
key_version = KEY_VERIOSN_NONE;
if (requestService(k, picc, n, 1)) {
key_version = k[0];
return true;
}
return false;
}
bool NFCLayerF::requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_size)
{
return _impl->requestService(key_version, picc, node_code, node_size);
}
bool NFCLayerF::read16(uint8_t rx[16], const m5::nfc::f::PICC& picc, const block_t block)
{
uint16_t rx_len{16};
uint16_t sc{service_random_read};
return rx && _impl->readWithoutEncryption(rx, rx_len, picc, &sc, 1, &block, 1);
}
bool NFCLayerF::read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, const block_t sblock)
{
auto rx_len_org = rx_len;
rx_len = 0;
uint16_t sc{service_random_read};
uint16_t start = sblock.block();
uint16_t blocks = rx_len_org >> 4;
uint16_t last = std::min<uint16_t>(picc.lastUserBlock(), start + blocks - 1);
if (!rx || !rx_len_org || !picc.isUserBlock(sblock) || !picc.isUserBlock(last)) {
return false;
}
const uint16_t batch_size = get_maxumum_read_blocks(picc.type);
uint8_t rbuf[16 * batch_size]{};
block_t block{start};
auto out = rx;
uint16_t read_count{};
while (block.block() <= last) {
uint16_t num = std::min<uint16_t>(last - block.block() + 1, batch_size);
uint16_t actual = 16 * num;
block_t block_list[blocks]{};
for (uint_fast16_t i = 0; i < num; ++i) {
block_list[i] = block_t(block.block() + i);
// M5_LIB_LOGE(" BL[%u]:%02X", i, block_list[i].block());
}
// M5_LIB_LOGE("rx_len:%u block_list_num:%u", actual, num);
if (!_impl->readWithoutEncryption(rbuf, actual, picc, &sc, 1, block_list, num) || actual != 16 * num) {
return false;
}
memcpy(out, rbuf, actual);
out += actual;
read_count += actual;
block.number += num;
}
rx_len = read_count;
return true;
}
bool NFCLayerF::write16(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t block, const uint8_t tx[16],
const uint16_t tx_len)
{
if (tx && tx_len) {
uint16_t sc{service_random_read_write};
uint8_t buf[16]{};
memcpy(buf, tx, std::min<uint16_t>(16u, tx_len));
return _impl->writeWithoutEncryption(picc, &sc, 1, &block, 1, buf, 16);
}
return false;
}
bool NFCLayerF::write(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t sblock, const uint8_t* tx,
const uint16_t tx_len)
{
if (!tx || !tx_len) {
return false;
}
uint16_t start = sblock.block();
uint16_t blocks = (tx_len + 15) >> 4;
uint16_t last = std::min<uint16_t>(picc.lastUserBlock(), start + blocks - 1);
if (!tx || !tx_len || !picc.isUserBlock(sblock) || !picc.isUserBlock(last)) {
return false;
}
uint16_t written{};
for (uint16_t block = start; block <= last; ++block) {
const uint16_t wsize = std::min<uint16_t>(tx_len - written, 16);
// M5_LIB_LOGE("write:%02X %u", block, wsize);
if (!write16(picc, block, tx + written, wsize)) {
return false;
}
written += wsize;
}
return true;
}
bool NFCLayerF::dump(const PICC& picc)
{
switch (picc.type) {
case Type::FeliCaLite:
return dump_felica_lite(picc);
case Type::FeliCaLiteS:
return dump_felica_lite_s(picc);
case Type::FeliCaStandard:
M5_LIB_LOGE("Not yet");
break;
default:
M5_LIB_LOGE("Not yet");
break;
}
return false;
}
bool NFCLayerF::dump(const PICC& picc, const block_t block)
{
// TODO
return dump_block(picc, block);
}
//
bool NFCLayerF::dump_felica_lite(const m5::nfc::f::PICC& picc)
{
puts(
"Block: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"
"------------------------------------------------------");
bool ret{true};
for (uint8_t block = 0; block < 0x0F; ++block) {
ret &= dump_block(picc, block);
}
for (uint8_t block = 0x80; block < 0x89; ++block) {
ret &= dump_block(picc, block);
}
return ret;
}
bool NFCLayerF::dump_felica_lite_s(const m5::nfc::f::PICC& picc)
{
bool ret{true};
ret &= dump_felica_lite(picc);
for (uint8_t block = 0x90; block < 0x93; ++block) {
// MAC_A(0x91) cannot be read unless written to RC.
if (block == 0x91) {
printf("[%04X]:MAC_A needs wrtite to RC\n", block);
continue;
}
ret &= dump_block(picc, block);
}
ret &= dump_block(picc, 0xA0);
return ret;
}
bool NFCLayerF::dump_block(const m5::nfc::f::PICC& picc, m5::nfc::f::block_t block)
{
uint8_t buf[16]{};
if (read16(buf, picc, block)) {
print_block(buf, block.block());
return true;
}
printf("[%04X]:ERROR\n", block.block());
return false;
}
//
+104 -19
View File
@@ -23,7 +23,6 @@
#include <memory>
namespace m5 {
namespace unit {
class UnitST25R3916;
class CapST25R3916;
@@ -59,41 +58,118 @@ public:
@param[out] picc Detected PICC
@param timeout_ms Polling time budget in milliseconds
@return True if detected
@warning Misclassification may occur depending on the distance between the reader and the PICC
*/
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);
bool detect(m5::nfc::f::PICC& picc, const uint32_t timeout_ms = 25);
/*!
@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
@warning Misclassification may occur depending on the distance between the reader and the PICC
*/
inline bool detect(std::vector<m5::nfc::f::PICC>& 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);
return detect(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 private_code Private 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
@note If a private code is specified, PICC that does not meet the conditions will not be detected
@warning Misclassification may occur depending on the distance between the reader and the PICC
*/
bool detect(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t system_code,
inline bool detect(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t private_code,
m5::nfc::f::TimeSlot time_slot = m5::nfc::f::TimeSlot::Slot16, const uint32_t timeout_ms = 500U)
{
return detect(piccs, &private_code, 1, time_slot, timeout_ms);
}
/*!
@brief Detect PICCs matching the specified system code
@param[out] piccs Detected PICCs
@param private_code Private system code array
@param pc_size private_code size
@param time_slot Maximum number of slots that can be responded
@param timeout_ms Polling time budget in milliseconds
@return True if detected
@note If a private code is specified, PICC that does not meet the conditions will not be detected
@note private codes are OR matching
@warning Misclassification may occur depending on the distance between the reader and the PICC
*/
bool detect(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t* private_code, const uint8_t pc_size,
m5::nfc::f::TimeSlot time_slot = m5::nfc::f::TimeSlot::Slot16, const uint32_t timeout_ms = 500U);
///@}
///@name Request
///@{
bool requestService(uint16_t& key_version, const m5::nfc::f::PICC& picc, const uint16_t node_code);
bool requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_num);
///@}
///@name Random Read/Write
///@{
/*!
@brief Read the 1 block
@param[out] rx Output buffer
@param picc Target PICC
@param block Target block
@return True if detected
*/
bool read16(uint8_t rx[16], const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t block);
/*!
@brief Read any bytes from user area
@details Continue reading only the user area from the first block of the user area until rx_len is satisfied
@param[out] rx Buffer
@param[in/out] rx_len in:buffer size, out:actual read size
@param sblock Block to start reading
@return True if successful
@warning rx in 16-byte units
*/
bool read(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t sblock);
/*!
@brief Write the 1 block
@param picc Target PICC
@param block Target block
@param tx Buffer
@param tx_len Buffer size
@return True if detected
@warning If the tx_len is less than 16 bytes, the remaining space is filled with 0x00
@warning If the tx_len is larger than 16 bytes, only the first 16 bytes will be written
*/
bool write16(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t block, const uint8_t tx[16],
const uint16_t tx_len);
/*!
@brief Write any bytes to user area
@details Continue writing only the user area from the first block of the user area until tx_len is satisfied
@param sblock Block to start writing
@param tx Buffer
@param tx_len Buffer size
@return True if successful
*/
bool write(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t sblock, const uint8_t* tx,
const uint16_t tx_len);
///@}
/*!
@brief Dump all blocks
@return True if successful
@note Only the sections that can be read without authentication
*/
bool dump(const m5::nfc::f::PICC& picc);
/*!
@brief Dump 1 block
@param block block list element
@return True if successful
@note Only the sections that can be read without authentication
*/
bool dump(const m5::nfc::f::PICC& picc, const m5::nfc::f::block_t block);
protected:
virtual bool read(uint8_t* rx, uint16_t& rx_len, const uint8_t saddr) override;
@@ -101,8 +177,9 @@ protected:
virtual uint16_t firstUserBlock() const override;
virtual uint16_t lastUserBlock() const override;
bool detect_picc(std::vector<m5::nfc::f::PICC>& piccs, const uint16_t* private_system_code, const uint8_t pc_size,
m5::nfc::f::TimeSlot time_slot, const uint32_t timeout_ms);
bool dump_felica_lite(const m5::nfc::f::PICC& picc);
bool dump_felica_lite_s(const m5::nfc::f::PICC& picc);
bool dump_block(const m5::nfc::f::PICC& picc, m5::nfc::f::block_t block);
private:
std::unique_ptr<Adapter> _impl;
@@ -115,7 +192,15 @@ 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;
const m5::nfc::f::TimeSlot time_slot) = 0;
virtual bool requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_num) = 0;
virtual bool readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc,
const uint16_t* service_code, const uint8_t service_num,
const m5::nfc::f::block_t* block_list, const uint8_t block_num) = 0;
virtual bool writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code,
const uint8_t service_num, const m5::nfc::f::block_t* block_list,
const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len) = 0;
};
///@endcond
@@ -28,6 +28,14 @@ struct AdapterST25R3916ForF final : NFCLayerF::Adapter {
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;
virtual bool requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_num) override;
virtual bool readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc,
const uint16_t* service_code, const uint8_t service_num,
const block_t* block_list, const uint8_t block_num) override;
virtual bool writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code,
const uint8_t service_num, const m5::nfc::f::block_t* block_list,
const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len) override;
UnitST25R3916& _u;
};
@@ -38,6 +46,26 @@ bool AdapterST25R3916ForF::polling(m5::nfc::f::PICC& picc, const uint16_t system
return _u.nfcfPolling(picc, system_code, request_code, time_slot);
}
bool AdapterST25R3916ForF::requestService(uint16_t key_version[], const m5::nfc::f::PICC& picc,
const uint16_t* node_code, const uint8_t node_num)
{
return _u.nfcfRequestService(key_version, picc, node_code, node_num);
}
bool AdapterST25R3916ForF::readWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc,
const uint16_t* service_code, const uint8_t service_num,
const block_t* block_list, const uint8_t block_size)
{
return _u.nfcfReadWithoutEncryption(rx, rx_len, picc, service_code, service_num, block_list, block_size);
}
bool AdapterST25R3916ForF::writeWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code,
const uint8_t service_num, const m5::nfc::f::block_t* block_list,
const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len)
{
return _u.nfcfWriteWithoutEncryption(picc, service_code, service_num, block_list, block_num, tx, tx_len);
}
//
namespace {
std::unique_ptr<NFCLayerF::Adapter> make_st25r3916_adapter(UnitST25R3916& u)
+46 -6
View File
@@ -74,7 +74,7 @@ public:
bool configureNFCMode(const m5::nfc::NFC mode);
/*!
@breif Is the current operating mode the one specified?
@brief Is the current operating mode the one specified?
@param mode Mode
@return True if the current operation is in the specified mode
*/
@@ -1796,7 +1796,7 @@ public:
@param spage Start reading page
@param epage End reading page
@return True if successful
@warning Only PICC supporting the FAST_READ command
@warning Only PICC with the FAST_READ command
*/
bool ntagReadPage(uint8_t* rx, uint16_t& rx_len, const uint8_t spage, const uint8_t epage);
///@}
@@ -1806,7 +1806,7 @@ public:
///@{
/*!
@brief Transceive
@param rx Receive buffer
@param[out] 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
@@ -1827,10 +1827,50 @@ public:
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);
/*!
@brief Requset service
@param[out] ky_version Node key version array (at leaset node_size)
@param picc PICC
@param node_code Node code array
@param node_num Number of elements in the node_code array
@return True if successful
@warning FeliCa Standard only
*/
bool nfcfRequestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_num);
bool nfcfRequestService(const m5::nfc::f::PICC& picc);
/*!
@brief Read the area that does not require authentication
@param[out] rx Buffer(at least 16 bytes * number of blocks)
@param[in/out] rx_len in:Bufffer size out:Actual size
@param picc PICC
@param service_code Service code array
@param service_num Number of elements in the service_code array (1-16)
@param block_list Block list
@param block_num Number of elements in the block_list array (1-8)
@return True if successful
@note The read unit is 16 bytes
@warning The maximum number of blocks read varies by type
*/
bool nfcfReadWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc,
const uint16_t* service_code, const uint8_t service_num,
const m5::nfc::f::block_t* block_list, const uint8_t block_num);
/*!
@brief Write the area that does not require authentication
@param picc PICC
@param service_code Service code array
@param service_num Number of elements in the service_code array (1-16)
@param block_list Block list
@param block_num Number of elements in the block_list array
@param tx Buffer
@param tx_len tx size
@return True if successful
@note The write unit is 16 bytes
@warning The maximum number of blocks write varies by type
*/
bool nfcfWriteWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code,
const uint8_t service_num, const m5::nfc::f::block_t* block_list,
const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len);
///@}
// For debug
+136 -11
View File
@@ -40,7 +40,17 @@ uint8_t val_table[] = {
};
*/
uint32_t get_block_list_size(const block_t* block_list, const uint8_t block_num)
{
uint32_t sz{};
if (block_list && block_num) {
for (uint_fast16_t i = 0; i < block_num; ++i) {
sz += 2 + block_list[i].is_3byte();
}
}
return sz;
}
} // namespace
namespace m5 {
namespace unit {
@@ -142,7 +152,7 @@ bool UnitST25R3916::nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_co
// m5::utility::log::dump(packet, sizeof(packet), false);
uint8_t timeout_ms = TIMEOUT_POLLING * TIMEOUT_POLLING_PICC * timeslot_to_slot(time_slot);
uint32_t timeout_ms = TIMEOUT_POLLING * TIMEOUT_POLLING_PICC * timeslot_to_slot(time_slot);
uint8_t rbuf[128]{};
uint16_t rx_len{128};
@@ -166,24 +176,33 @@ bool UnitST25R3916::nfcfPolling(m5::nfc::f::PICC& picc, const uint16_t system_co
return true;
}
bool UnitST25R3916::nfcfRequestService(const m5::nfc::f::PICC& picc)
bool UnitST25R3916::nfcfRequestService(uint16_t key_version[], const m5::nfc::f::PICC& picc, const uint16_t* node_code,
const uint8_t node_num)
{
CHECK_MODE();
std::vector<uint8_t> packet{};
uint8_t node_size = 1;
packet.resize(1 + 8 + 1 + (2 * node_size));
if (!key_version || !node_code || !node_num || picc.type != Type::FeliCaStandard) {
return false;
}
uint8_t timeout_ms = 10;
std::vector<uint8_t> packet{};
uint32_t timeout_ms = 10; // TODO
packet.resize(1 + 8 + 1 + (2 * node_num));
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
packet[9] = node_num;
uint8_t* p = packet.data() + 10;
for (uint_fast8_t i = 0; i < node_num; ++i) {
*p++ = node_code[i] & 0xFF;
*p++ = node_code[i] >> 8;
}
uint8_t rbuf[packet.size()]{};
uint16_t rx_len{packet.size()};
uint16_t rx_len{(uint8_t)packet.size()};
// m5::utility::log::dump(packet.data(), packet.size(), false);
if (!nfcfTransceive(rbuf, rx_len, packet.data(), packet.size(), timeout_ms) //
|| rx_len < packet.size() || rbuf[1] != m5::stl::to_underlying(ResponseCode::RequestService)) {
@@ -193,8 +212,114 @@ bool UnitST25R3916::nfcfRequestService(const m5::nfc::f::PICC& picc)
return false;
}
m5::utility::log::dump(rbuf, rx_len, false);
// m5::utility::log::dump(rbuf, rx_len, false);
// TODO
return false;
}
bool UnitST25R3916::nfcfReadWithoutEncryption(uint8_t* rx, uint16_t& rx_len, const m5::nfc::f::PICC& picc,
const uint16_t* service_code, const uint8_t service_num,
const block_t* block_list, const uint8_t block_num)
{
CHECK_MODE();
auto rx_org_len = rx_len;
rx_len = 0;
if (!rx || !rx_org_len || !service_code || service_num > 16 || !block_num) {
return false;
}
std::vector<uint8_t> packet{};
const uint32_t block_size = get_block_list_size(block_list, block_num);
uint32_t timeout_ms = 10; // TODO
packet.resize(1 + 8 + 1 + (2 * service_num) + 1 + block_size);
packet[0] = m5::stl::to_underlying(CommandCode::ReadWithoutEncryption);
memcpy(packet.data() + 1, picc.idm.data(), picc.idm.size());
packet[9] = service_num;
uint8_t* p = packet.data() + 10;
for (uint_fast8_t i = 0; i < service_num; ++i) {
*p++ = service_code[i] & 0xFF;
*p++ = service_code[i] >> 8;
}
*p++ = block_num;
for (uint_fast8_t i = 0; i < block_num; ++i) {
block_t ble = block_list[i];
*p++ = ble.header;
if (ble.is_3byte()) {
*p++ = ble.block() >> 8;
}
*p++ = ble.block() & 0xFF;
}
// m5::utility::log::dump(packet.data(), packet.size(), false);
uint8_t rbuf[1 + 1 + 8 + 1 + 1 + 1 + 16 * block_num]{};
uint16_t actual = sizeof(rbuf);
if (!nfcfTransceive(rbuf, actual, packet.data(), packet.size(), timeout_ms) || actual < 12 || (rbuf[0] < 11) ||
rbuf[1] != m5::stl::to_underlying(ResponseCode::ReadWithoutEncryption) || //
(rbuf[10] /*status 1*/ != 0x00) || (rbuf[11] /*status 2*/ != 0x00)) {
M5_LIB_LOGE("Failed to readWithoutEncryption %u %u %02X %02X", actual, rbuf[0], rbuf[10], rbuf[11]);
return false;
}
// m5::utility::log::dump(rbuf, actual, false);
// const uint8_t blocks = rbuf[11];
rx_len = std::min<uint16_t>(actual - 13, rx_org_len);
memcpy(rx, rbuf + 13, rx_len);
return true;
}
bool UnitST25R3916::nfcfWriteWithoutEncryption(const m5::nfc::f::PICC& picc, const uint16_t* service_code,
const uint8_t service_num, const m5::nfc::f::block_t* block_list,
const uint8_t block_num, const uint8_t* tx, const uint16_t tx_len)
{
CHECK_MODE();
if (!tx || !tx_len || !service_code || service_num > 16 || !block_num) {
return false;
}
std::vector<uint8_t> packet{};
const uint32_t block_size = get_block_list_size(block_list, block_num);
uint32_t timeout_ms = 10; // TODO
packet.resize(1 + 8 + 1 + (2 * service_num) + 1 + block_size + tx_len);
packet[0] = m5::stl::to_underlying(CommandCode::WriteWithoutEncryption);
memcpy(packet.data() + 1, picc.idm.data(), picc.idm.size());
packet[9] = service_num;
uint8_t* p = packet.data() + 10;
for (uint_fast8_t i = 0; i < service_num; ++i) {
*p++ = service_code[i] & 0xFF;
*p++ = service_code[i] >> 8;
}
*p++ = block_num;
for (uint_fast8_t i = 0; i < block_num; ++i) {
block_t ble = block_list[i];
*p++ = ble.header;
if (ble.is_3byte()) {
*p++ = ble.block() >> 8;
}
*p++ = ble.block() & 0xFF;
}
memcpy(p, tx, tx_len);
// m5::utility::log::dump(packet.data(), packet.size(), false);
uint8_t rbuf[1 + 1 + 8 + 1 + 1]{};
uint16_t actual = sizeof(rbuf);
if (!nfcfTransceive(rbuf, actual, packet.data(), packet.size(), timeout_ms) || actual < 12 || (rbuf[0] < 11) ||
rbuf[1] != m5::stl::to_underlying(ResponseCode::WriteWithoutEncryption) || //
(rbuf[10] /*status 1*/ != 0x00) || (rbuf[11] /*status 2*/ != 0x00)) {
// m5::utility::log::dump(rbuf, actual, false);
M5_LIB_LOGE("Failed to writeWithoutEncryption %u %u %02X %02X", actual, rbuf[0], rbuf[10], rbuf[11]);
return false;
}
return true;
}