From 0ffe244a6b7d60d1dfeccc5958c0b9a62fe37cce Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 17 Apr 2026 03:00:14 +0800 Subject: [PATCH] Fix for circular buffer error during hf iclass sim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue origin: Commit 6b7665ed5 "Added live fc/cn update to hf iclass tagsim" added a data_available() poll inside the per-byte DMA loop of GetIso15693CommandFromReader so the ARM could drop out of RF-listen and process live emulator updates. Before that commit, that tight loop had no USB poll at all — only gotFrame / BUTTON_PRESS / WDT_HIT. Verified via git show 6b7665ed5^:armsrc/iso15693.c. Why it shows up on sim -t 3/6/7: those are the FULL sim modes that share do_iclass_simulation. Between reader commands the decoder sits in STATE_READER_UNSYNCD, so the gated poll at iso15693.c:1570-1575 fires every byte (reading UDP peripheral registers). With DMA filling at ~1 byte / ~19 µs, the added USB register reads plus jitter occasionally push the CPU past the 90% lag threshold → behindBy 461 with DMA_BUFFER_SIZE=512. Commit fb8f94fa2 narrowed the gate to UNSYNCD to stop mid-frame exits, but the per-byte poll itself is still what's new on that path. Fix: New mode constant in include/iclass_cmd.h: #define ICLASS_SIM_MODE_FULL_LIVE 8 // FULL + allow USB interrupt for live emul updates Treat it identically to ICLASS_SIM_MODE_FULL everywhere except for the poll gate. Add a flag param to GetIso15693CommandFromReader — e.g. bool allow_usb_interrupt in iso15693.c:1495 and iso15693.h:42. Wrap the poll: if (allow_usb_interrupt && (dr->state == STATE_READER_UNSYNCD || dr->state == STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF) && data_available()) { ... } Pass true only for live mode in do_iclass_simulation iclass.c:502: bool live = (simulationMode == ICLASS_SIM_MODE_FULL_LIVE); len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time, live); The len == -2 drain block stays but becomes dead code for non-live modes (never returns -2). Client side: cmdhficlass.c:1687 (CmdHFiClassTagSim) sends ICLASS_SIM_MODE_FULL_LIVE. CmdHFiClassSim -t 3/6/7 keeps sending ICLASS_SIM_MODE_FULL / _GLITCH / _GLITCH_KEY. Other callers (iso15693.c:2270, iclass.c:1121 = reader-attack sim) pass false. Result: hf iclass sim -t 3/6/7 → byte-inner loop is back to its pre-tagsim shape → no blow-buffer abort. hf iclass tagsim → keeps live update ability; still has the overhead, but that's the trade-off the feature needs. --- armsrc/iclass.c | 14 +++++++++++--- armsrc/iso15693.c | 9 +++++---- armsrc/iso15693.h | 2 +- client/src/cmdhficlass.c | 2 +- include/iclass_cmd.h | 1 + 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 7a47564b5..d86b6dc72 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -196,7 +196,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (send_reply) reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); - } else if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { + } else if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY || sim_type == ICLASS_SIM_MODE_FULL_LIVE) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command @@ -292,6 +292,14 @@ out: */ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { + // FULL_LIVE = FULL + per-byte USB poll so the client can push live emul + // updates (see hf iclass tagsim). Unfold the flag and continue as FULL + // so all existing FULL-mode checks below work unchanged. + const bool allow_usb_interrupt = (simulationMode == ICLASS_SIM_MODE_FULL_LIVE); + if (simulationMode == ICLASS_SIM_MODE_FULL_LIVE) { + simulationMode = ICLASS_SIM_MODE_FULL; + } + // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); @@ -499,7 +507,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data_size = 0; uint32_t reader_eof_time = 0; - len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time, allow_usb_interrupt); if (len == -2) { // USB data arrived while waiting for RF — drain all pending EML_MEMSET // commands inline (without FpgaDownloadAndGo) so live tag updates work. @@ -1118,7 +1126,7 @@ int do_iclass_simulation_nonsec(void) { WDT_HIT(); uint32_t reader_eof_time = 0; - len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time, false); if (len < 0) { button_pressed = true; exit_loop = true; diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 748e5f4a0..2ed033b7d 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1492,7 +1492,7 @@ static int RAMFUNC Handle15693SampleFromReader(bool bit, DecodeReader_t *reader) // correctly. //----------------------------------------------------------------------------- -int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time, bool allow_usb_interrupt) { int samples = 0; bool gotFrame = false; @@ -1567,8 +1567,9 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo break; } - if ((dr->state == STATE_READER_UNSYNCD || - dr->state == STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF) && + if (allow_usb_interrupt && + (dr->state == STATE_READER_UNSYNCD || + dr->state == STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF) && data_available()) { dr->byteCount = -2; break; @@ -2267,7 +2268,7 @@ void SimTagIso15693(const uint8_t *uid, uint8_t block_size) { // Listen to reader uint32_t reader_eof_time = 0; - int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &reader_eof_time); + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &reader_eof_time, false); if (cmd_len < 0) { button_pressed = true; break; diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index a4f633252..a3197bbd8 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -39,7 +39,7 @@ void CodeIso15693AsReader(const uint8_t *cmd, int n); void CodeIso15693AsTag(const uint8_t *cmd, size_t len); void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); -int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time, bool allow_usb_interrupt); void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool shallow_mod); int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 9b24f1034..8906136f6 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1508,7 +1508,7 @@ static int CmdHFiClassTagSim(const char *Cmd) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, ICLASS_SIM_MODE_FULL, 0, 1, csn, 8); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, ICLASS_SIM_MODE_FULL_LIVE, 0, 1, csn, 8); // --- live FC/CN navigation (wiegand mode only; binary mode has no FC/CN to adjust) if (bin_len == 0) { diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index 0ec809b8a..ebc868cc0 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -52,6 +52,7 @@ #define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only #define ICLASS_SIM_MODE_FULL_GLITCH 6 #define ICLASS_SIM_MODE_FULL_GLITCH_KEY 7 +#define ICLASS_SIM_MODE_FULL_LIVE 8 // FULL + USB poll for live emul updates (hf iclass tagsim) // iCLASS auth request data structure