Fix for circular buffer error during hf iclass sim

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.
This commit is contained in:
Antiklesys
2026-04-17 03:00:14 +08:00
parent ffba70d30b
commit 0ffe244a6b
5 changed files with 19 additions and 9 deletions
+11 -3
View File
@@ -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;
+5 -4
View File
@@ -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;
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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) {
+1
View File
@@ -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