Fixes emulation for NFC-A

This commit is contained in:
GOB
2025-12-22 15:01:43 +09:00
parent 29b9dc798d
commit b30c4fbd7b
7 changed files with 106 additions and 53 deletions
@@ -43,18 +43,21 @@ m5::unit::HackerCapNFC unit{}; // HackerCap (SPI)
#endif
m5::nfc::EmulationLayerA emu_a{unit};
// KeyA that can authenticate all blocks
// If it's a different key value, change it
constexpr Key keyA = DEFAULT_KEY; // Default as 0xFFFFFFFFFFFF
// constexpr Key keyA = DEFAULT_KEY; // Default as 0xFFFFFFFFFFFF
// constexpr Key keyB = DEFAULT_KEY; // Default as 0xFFFFFFFFFFFF
PICC picc{};
//#define EMU_MIFARE_ULTRALIGHT
#define EMU_NTAG213
#define EMU_MIFARE_ULTRALIGHT
// HackerCap : iPhone, NFC tools, NFC Taginfo. NFC Tap
// Basic+I2C : iPhone, NFC Tap
// NG -> NFC tools, NFC Taginfo
#if defined(EMU_MIFARE_CLASSIC_1K)
// #define EMU_NTAG213
// HackerCap : iPhone, NFC tools, NFC Taginfo. NFC Tap
// Basic+I2C : iPhone, NFC tools, NFC Taginfo. NFC Tap
#elif defined(EMU_MIFARE_ULTRALIGHT)
#if defined(EMU_MIFARE_ULTRALIGHT)
constexpr Type type{Type::MIFARE_Ultralight};
constexpr uint8_t uid[] = {0x04, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE};
uint8_t picc_memory[] = {
@@ -186,7 +189,7 @@ void setup()
M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
Wire.end();
Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U);
// Wire.begin(pin_num_sda, pin_num_scl, 1000 * 1000u);
if (!Units.add(unit, Wire) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.clear(TFT_RED);
@@ -221,38 +224,25 @@ void setup()
lcd.setFont(&fonts::Font2);
//
picc.emulate(type, uid, sizeof(uid)); // PICC information to emulate
if (picc.isMifareUltralight() || picc.isNTAG()) {
lcd.startWrite();
lcd.fillScreen(TFT_RED);
if (picc.emulate(type, uid, sizeof(uid))) {
embed_uid(picc_memory, uid);
}
if (emu_a.begin(picc, picc_memory, sizeof(picc_memory))) {
lcd.fillScreen(TFT_DARKGREEN);
lcd.setCursor(0, 16);
const auto& e_picc = emu_a.emulatePICC();
M5.Log.printf("Emulation:%s %s ATQA:%04X SAK:%u\n", e_picc.typeAsString().c_str(), e_picc.uidAsString().c_str(),
e_picc.atqa, e_picc.sak);
lcd.printf("%s\n%s\nATQA:%04X SAK:%u", e_picc.typeAsString().c_str(), e_picc.uidAsString().c_str(), e_picc.atqa,
e_picc.sak);
} else {
lcd.fillScreen(TFT_RED);
if (emu_a.begin(picc, picc_memory, sizeof(picc_memory))) {
lcd.fillScreen(TFT_DARKGREEN);
lcd.setCursor(0, 16);
const auto& e_picc = emu_a.emulatePICC();
M5.Log.printf("Emulation:%s %s ATQA:%04X SAK:%u\n", e_picc.typeAsString().c_str(),
e_picc.uidAsString().c_str(), e_picc.atqa, e_picc.sak);
lcd.printf("%s\n%s\nATQA:%04X SAK:%u", e_picc.typeAsString().c_str(), e_picc.uidAsString().c_str(),
e_picc.atqa, e_picc.sak);
}
}
lcd.fillRect(0, 0, 32, 16, color_table[0]);
lcd.drawString(state_table[0], 0, 0);
lcd.endWrite();
}
/*
17:31:54.797 > PICC:04C90EC2DA1C94 NTAG 213 144/180
17:31:54.828 > Page :00 01 02 03
17:31:54.832 > --------------------
17:31:54.834 > [000/00]:04 C9 0E 4B
17:31:54.840 > [001/01]:C2 DA 1C 94
17:31:54.840 > [002/02]:90 48 00 00
17:34:34.767 > [000/00]:04 C9 0E 4B
17:34:34.767 > [001/01]:C2 DA 1C 94
17:34:34.767 > [002/02]:18 A3 00 00
*/
void loop()
{
M5.update();
@@ -241,6 +241,13 @@ void setup()
M5.Log.printf("Please put the PICC and click/hold BtnA\n");
}
///////////////////////////////////////
// NRT 計算が正しいか確認 RFAL 比較
// S3:PICC Hacker:NDEF で通すことを目標?
// PCD からくる? FRT とかはのうタイミングは?
// ISR での IRQ取得のトランザクションは?
// no_crc 除去? RFAL は?
void loop()
{
M5.update();
+32 -1
View File
@@ -3,7 +3,8 @@
; For UnitTest and examples (Using M5UnitUnified)
;-----------------------------------------------------------------------
[platformio]
;default_envs = UnitNFC_NFCA_Detect_Core_Arduino_latest, UnitNFC_NFCA_Dump_Core_Arduino_latest, UnitNFC_NFCA_ReadWrite_Core_Arduino_latest, UnitNFC_NFCA_ValueBlock_Core_Arduino_latest, UnitNFC_NFCA_NDEF_Core_Arduino_latest, UnitNFC_NFCF_Detect_Core_Arduino_latest, UnitNFC_NFCF_Dump_Core_Arduino_latest, UnitNFC_NFCF_NDEF_Core_Arduino_latest, UnitNFC_NFCF_Subtract_Core_Arduino_latest, UnitNFC_NFCF_ReadWriteMAC_Core_Arduino_latest, UnitNFC_NFCF_JapanTransportationICCard_Core_Arduino_latest, UnitNFC_NFCV_Detect_CardputerADV_Arduino_latest, UnitNFC_NFCV_Dump_CardputerADV_Arduino_latest, UnitNFC_NFCV_NDEF_Core_Arduino_latest
;default_envs = UnitNFC_NFCA_Detect_Core_Arduino_latest, UnitNFC_NFCA_Dump_Core_Arduino_latest, UnitNFC_NFCA_ReadWrite_Core_Arduino_latest, UnitNFC_NFCA_ValueBlock_Core_Arduino_latest, UnitNFC_NFCA_NDEF_Core_Arduino_latest, UnitNFC_NFCF_Detect_Core_Arduino_latest, UnitNFC_NFCF_Dump_Core_Arduino_latest, UnitNFC_NFCF_NDEF_Core_Arduino_latest, UnitNFC_NFCF_Subtract_Core_Arduino_latest, UnitNFC_NFCF_ReadWriteMAC_Core_Arduino_latest, UnitNFC_NFCF_JapanTransportationICCard_Core_Arduino_latest, UnitNFC_NFCV_Detect_CardputerADV_Arduino_latest, UnitNFC_NFCV_Dump_CardputerADV_Arduino_latest, UnitNFC_NFCV_NDEF_Core_Arduino_latest, UnitNFC_NFCB_Detect_Core_Arduino_latest, UnitNFC_NFCB_Dump_Core_Arduino_latest, CapHacker_NFCB_Detect_CardputerADV_Arduino_latest, CapHacker_NFCB_Dump_CardputerADV_Arduino_latest
[env]
@@ -234,6 +235,28 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCA/NDEF>
build_flags = ${option_release.build_flags}
-D USING_UNIT_NFC
[env:UnitNFC_NFCA_Emulation_Core_Arduino_latest]
extends=Core, option_release, arduino_latest
build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCA/Emulation>
build_flags = ${option_release.build_flags}
-D USING_UNIT_NFC
; NFC-B
[env:UnitNFC_NFCB_Detect_Core_Arduino_latest]
extends=Core, option_release, arduino_latest
build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCB/Detect>
build_flags = ${option_release.build_flags}
-D USING_UNIT_NFC
[env:UnitNFC_NFCB_Dump_Core_Arduino_latest]
extends=Core, option_release, arduino_latest
build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCB/Dump>
build_flags = ${option_release.build_flags}
-D USING_UNIT_NFC
; NFC-F
[env:UnitNFC_NFCF_Detect_Core_Arduino_latest]
extends=Core, option_release, arduino_latest
@@ -329,6 +352,14 @@ build_flags = ${option_release.build_flags}
${CardputerADV.build_flags}
-D USING_HACKER_CAP
[env:CapHacker_NFCA_Emulation_CardputerADV_Arduino_latest]
extends=CardputerADV, option_release, arduino_latest
build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/NFCA/Emulation>
build_flags = ${option_release.build_flags}
${CardputerADV.build_flags}
-D USING_HACKER_CAP
; NFC-B
[env:CapHacker_NFCB_Detect_CardputerADV_Arduino_latest]
extends=CardputerADV, option_release, arduino_latest
+19 -6
View File
@@ -31,6 +31,11 @@ bool EmulationLayerA::begin(const m5::nfc::a::PICC& picc, uint8_t* ptr, const ui
return false;
}
if (!(picc.isNTAG() || picc.type == Type::MIFARE_Ultralight)) {
M5_LIB_LOGE("Not support %u %s", picc.type, picc.typeAsString().c_str());
return false;
}
_picc = picc;
_memory = ptr;
_memory_size = size;
@@ -43,6 +48,8 @@ bool EmulationLayerA::begin(const m5::nfc::a::PICC& picc, uint8_t* ptr, const ui
_state = _impl->start_emulation(_picc) ? State::Off : State::None;
_prev = State::None;
_expired_at = m5::utility::millis() + _expired_ms;
return (_state != State::None);
}
@@ -58,28 +65,31 @@ bool EmulationLayerA::end()
void EmulationLayerA::update()
{
auto save = _state;
// if (_state != _prev) {
// _expired_at = m5::utility::millis() + _expired_ms; // IRQ byGT ???
// }
switch (_state) {
case State::None:
break;
case State::Off:
if (_state != _prev) M5_LIB_LOGE("==OFF");
// if (_state != _prev) M5_LIB_LOGE("==OFF");
update_off();
break;
case State::Idle:
if (_state != _prev) M5_LIB_LOGE("==IDLE");
// if (_state != _prev) M5_LIB_LOGE("==IDLE");
update_idle();
break;
case State::Ready:
if (_state != _prev) M5_LIB_LOGE("==READY");
// if (_state != _prev) M5_LIB_LOGE("==READY");
update_ready();
break;
case State::Active:
if (_state != _prev) M5_LIB_LOGE("==ACTIVE");
// if (_state != _prev) M5_LIB_LOGE("==ACTIVE");
update_active();
break;
case State::Halt:
if (_state != _prev) M5_LIB_LOGE("==HALT");
// if (_state != _prev) M5_LIB_LOGE("==HALT");
update_halt();
break;
default:
@@ -119,7 +129,6 @@ EmulationLayerA::State EmulationLayerA::receive_callback(const uint8_t* rx, cons
return State::Idle;
}
// m5::utility::log::dump(rx, rx_len, false);
M5_LIB_LOGE("cmd:%02X %u", rx[0], rx_len);
State ret{State::Idle};
switch (static_cast<Command>(rx[0])) {
@@ -127,6 +136,8 @@ EmulationLayerA::State EmulationLayerA::receive_callback(const uint8_t* rx, cons
ret = (rx_len == 2 && rx[1] == 0x00) ? State::Halt : State::Idle;
break;
case Command::READ: // 16 bytes read
// M5_LIB_LOGE(" R:%u", rx[1]);
ret = (rx_len == 2) && _impl->transmit(_memory + _picc.unitSize() * rx[1], 16, 4) ? State::Active
: State::Idle;
break;
@@ -156,6 +167,7 @@ EmulationLayerA::State EmulationLayerA::receive_callback(const uint8_t* rx, cons
const uint32_t offset = 4 * rx[1];
if (offset + 4 <= _memory_size) {
memcpy(_memory + offset, rx + 2, 4);
// ret = _impl->send_ack() ? State::Active : State::Idle; // Return ACK
ret = _impl->transmit(&ACK_NIBBLE, 1, 1) ? State::Active : State::Idle; // Return ACK
}
}
@@ -170,6 +182,7 @@ EmulationLayerA::State EmulationLayerA::receive_callback(const uint8_t* rx, cons
break;
default:
M5_LIB_LOGE("cmd:%02X %u", rx[0], rx_len);
break;
}
// M5_LIB_LOGE(" --> %u", ret);
+12 -6
View File
@@ -51,16 +51,25 @@ public:
{
return _picc;
}
inline uint32_t expiredTime() const
{
return _expired_ms;
}
void setExpiredTime(const uint32_t ms)
{
_expired_ms = ms;
}
bool begin(const m5::nfc::a::PICC& picc, uint8_t* ptr, const uint32_t size);
bool end();
void update();
State receive_callback(const uint8_t* rx, const uint32_t rx_len);
virtual State receive_callback(const uint8_t* rx, const uint32_t rx_len);
protected:
bool load_config();
void update_expired();
private:
void update_off();
void update_idle();
void update_ready();
@@ -73,7 +82,7 @@ protected:
private:
State _state{}, _prev{};
uint32_t _expired_ms{10*1000};
uint32_t _expired_ms{60 * 1000u};
unsigned int _expired_at{};
std::unique_ptr<Adapter> _impl;
m5::nfc::a::PICC _picc{};
@@ -84,9 +93,6 @@ private:
struct EmulationLayerA::Adapter {
virtual ~Adapter() = default;
// virtual uint16_t max_fifo_depth() = 0;
virtual bool load_config(const m5::nfc::a::PICC& picc) = 0;
virtual bool start_emulation(const m5::nfc::a::PICC& picc) = 0;
virtual bool stop_emulation() = 0;
virtual bool transmit(const uint8_t* tx, const uint16_t tx_len, const uint32_t timeout_ms) = 0;
+4 -4
View File
@@ -277,6 +277,7 @@ bool NFCLayerA::identify_picc(m5::nfc::a::PICC& picc)
}
// The PICC goes idle when sending an external command, so select again
if (!reactivate(picc)) {
M5_LIB_LOGE(">>>> ERR3");
return false;
}
// Try ULC Auth
@@ -981,12 +982,11 @@ bool NFCLayerA::dump_page_structure(const uint16_t maxPage)
"Page :00 01 02 03\n"
"--------------------");
bool ret{true};
for (uint_fast8_t page = 0; page < maxPage; page += 4) {
if (!dump_page(page, maxPage)) {
return false;
}
ret &= dump_page(page, maxPage);
}
return true;
return ret;
}
bool NFCLayerA::dump_page(const uint8_t page, uint16_t maxPage)
@@ -245,6 +245,8 @@ EmulationLayerA::State ListenerST25R3916ForA::goto_off()
_u.enable_interrupts(I_nfct32 | I_rxs32 | I_crc32 | I_err132 | I_osc32 | I_err232 | I_par32 | I_eon32 | I_eof32 |
mode_irq);
_u.set_bit_register8(REG_AUXILIARY_DEFINITION, no_crc_rx);
if (is_extra_field()) {
return goto_idle();
} else {
@@ -270,8 +272,9 @@ EmulationLayerA::State ListenerST25R3916ForA::goto_idle()
(void)get_irq(I_osc32);
}
_u.set_bit_register8(REG_AUXILIARY_DEFINITION, no_crc_rx);
if (_layer.state() == EmulationLayerA::State::Active && !_wakeup) {
M5_LIB_LOGE(" >> Active to idle");
_u.clear_bit_register8(REG_NFCIP_1_PASSIVE_TARGET_DEFINITION, d_106_ac_a); // Enable auto response for NFC-A
_u.writeDirectCommand(CMD_GO_TO_SENSE);
}
@@ -291,6 +294,9 @@ EmulationLayerA::State ListenerST25R3916ForA::goto_ready()
return goto_off();
}
_u.clear_bit_register8(REG_AUXILIARY_DEFINITION, no_crc_rx);
_u.clear_bit_register8(REG_OPERATION_CONTROL, wu); // Disable wakeup mode
_u.writeModeDefinition(mode_listen_nfc_a); // Disable birrate detection and collision
_u.writeBitrate(_bitrate, _bitrate);
@@ -364,7 +370,7 @@ EmulationLayerA::State ListenerST25R3916ForA::update_idle()
}
if (is_eof(irq32) && !_data_flag) {
M5_LIB_LOGE("OFF");
//M5_LIB_LOGE("OFF");
return goto_off();
}
if ((irq32 & I_rxe32) && _bitrate != Bitrate::Invalid) {