Merge pull request #3107 from kormax/hf-field-timeout

Add 'hf.field.timeout' to prefs
This commit is contained in:
Iceman
2026-04-01 10:06:28 +07:00
committed by GitHub
13 changed files with 215 additions and 4 deletions
+31 -1
View File
@@ -91,10 +91,12 @@
int g_dbglevel = DBG_ERROR;
uint8_t g_trigger = 0;
bool g_hf_field_active = false;
bool g_hf_field_timeout_active = false;
extern uint32_t _stack_start[], _stack_end[];
common_area_t g_common_area __attribute__((section(".commonarea")));
static int button_status = BUTTON_NO_CLICK;
static bool allow_send_wtx = false;
static uint32_t g_hf_field_activity_timeout_ms = 0;
uint16_t g_tearoff_delay_us = 0;
bool g_tearoff_enabled = false;
uint8_t g_tearoff_skip = 0;
@@ -125,6 +127,7 @@ void hf_field_off(void) {
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
g_hf_field_active = false;
g_hf_field_timeout_active = false;
}
void send_wtx(uint16_t wtx) {
@@ -1002,6 +1005,17 @@ static void PacketReceived(PacketCommandNG *packet) {
reply_ng(CMD_SET_TEAROFF, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_SET_HF_FIELD_TIMEOUT: {
if (packet->length != sizeof(uint32_t)) {
reply_ng(CMD_SET_HF_FIELD_TIMEOUT, PM3_EINVARG, NULL, 0);
break;
}
uint32_t timeout_ms = 0;
memcpy(&timeout_ms, packet->data.asBytes, sizeof(timeout_ms));
g_hf_field_activity_timeout_ms = timeout_ms;
reply_ng(CMD_SET_HF_FIELD_TIMEOUT, PM3_SUCCESS, NULL, 0);
break;
}
// always available
case CMD_HF_DROPFIELD: {
hf_field_off();
@@ -2699,6 +2713,8 @@ static void PacketReceived(PacketCommandNG *packet) {
}
case CMD_FPGA_MAJOR_MODE_OFF: { // ## FPGA Control
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
g_hf_field_active = false;
g_hf_field_timeout_active = false;
SpinDelay(200);
LED_D_OFF(); // LED D indicates field ON or OFF
break;
@@ -3315,6 +3331,8 @@ void __attribute__((noreturn)) AppMain(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
StartTickCount();
uint32_t last_activity_tick = GetTickCount();
uint32_t last_activity_label = GetTickCountLabel();
#ifdef WITH_LCD
LCDInit();
@@ -3378,12 +3396,24 @@ void __attribute__((noreturn)) AppMain(void) {
int ret = receive_ng(&rx);
if (ret == PM3_SUCCESS) {
PacketReceived(&rx);
last_activity_label = GetTickCountLabel();
last_activity_tick = GetTickCount();
} else if (ret != PM3_ENODATA) {
Dbprintf("Error in frame reception: %d %s", ret, (ret == PM3_EIO) ? "PM3_EIO" : "");
// TODO if error, shall we resync ?
}
if (g_hf_field_activity_timeout_ms > 0 && g_hf_field_timeout_active) {
uint32_t tickcount_label = GetTickCountLabel();
if (tickcount_label != last_activity_label) {
last_activity_label = tickcount_label;
last_activity_tick = GetTickCount();
} else if (GetTickCountDelta(last_activity_tick) >= g_hf_field_activity_timeout_ms) {
hf_field_off();
Dbprintf("HF field auto-off: inactivity timeout (%u ms). To disable, use 'prefs set hf.field.timeout_sec --sec 0'", g_hf_field_activity_timeout_ms);
}
}
// Press button for one second to enter a possible standalone mode
button_status = BUTTON_HELD(1000);
if (button_status == BUTTON_HOLD) {
+1
View File
@@ -24,6 +24,7 @@
extern uint8_t g_trigger;
extern bool g_hf_field_active;
extern bool g_hf_field_timeout_active;
void hf_field_off(void);
int tearoff_hook(void);
+26
View File
@@ -562,12 +562,38 @@ void FpgaSendCommand(uint16_t cmd, uint16_t v) {
AT91C_BASE_SPI->SPI_TDR = AT91C_SPI_LASTXFER | cmd | v; // send the data
while (!(AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF)) {}; // wait till transfer is complete
}
//-----------------------------------------------------------------------------
// Write the FPGA setup word (that determines what mode the logic is in, read
// vs. clone vs. etc.). This is now a special case of FpgaSendCommand() to
// avoid changing this function's occurrence everywhere in the source code.
//-----------------------------------------------------------------------------
void FpgaWriteConfWord(uint16_t v) {
const int current = FpgaGetCurrent();
// Keep track of whether or not we should be monitoring the HF field timeout
if (current == FPGA_BITSTREAM_HF || current == FPGA_BITSTREAM_HF_15 || current == FPGA_BITSTREAM_HF_FELICA) {
const uint16_t major = v & FPGA_MAJOR_MODE_MASK;
const uint16_t minor = v & FPGA_MINOR_MODE_MASK;
switch (major) {
case FPGA_MAJOR_MODE_HF_READER:
g_hf_field_timeout_active = true;
break;
case FPGA_MAJOR_MODE_HF_ISO14443A:
g_hf_field_timeout_active = (minor == FPGA_HF_ISO14443A_READER_LISTEN || minor == FPGA_HF_ISO14443A_READER_MOD);
break;
case FPGA_MAJOR_MODE_HF_ISO18092:
g_hf_field_timeout_active = (minor & FPGA_HF_ISO18092_FLAG_READER) != 0;
break;
default:
g_hf_field_timeout_active = false;
break;
}
} else {
g_hf_field_timeout_active = false;
}
FpgaSendCommand(FPGA_CMD_SET_CONFREG, v);
}
+14 -2
View File
@@ -212,14 +212,26 @@ void CmdsHelp(const command_t Commands[]) {
PrintAndLogEx(NORMAL, "");
int i = 0;
size_t max_name_len = 16; // minimum width for command name column
while (Commands[i].Name) {
if (Commands[i].IsAvailable()) {
size_t name_len = strlen(Commands[i].Name);
if (name_len > max_name_len) {
max_name_len = name_len;
}
}
++i;
}
i = 0;
while (Commands[i].Name) {
if (Commands[i].IsAvailable()) {
uint8_t old_printAndLog = g_printAndLog;
g_printAndLog &= PRINTANDLOG_PRINT;
if (Commands[i].Name[0] == '-' || Commands[i].Name[0] == ' ') {
PrintAndLogEx(NORMAL, "%-16s %s", Commands[i].Name, Commands[i].Help);
PrintAndLogEx(NORMAL, "%-*s %s", (int)max_name_len, Commands[i].Name, Commands[i].Help);
} else {
PrintAndLogEx(NORMAL, _GREEN_("%-16s")" %s", Commands[i].Name, Commands[i].Help);
PrintAndLogEx(NORMAL, _GREEN_("%-*s") " %s", (int)max_name_len, Commands[i].Name, Commands[i].Help);
}
g_printAndLog = old_printAndLog;
}
+31
View File
@@ -846,6 +846,30 @@ bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int
}
}
int SetHfFieldTimeout(uint32_t timeout_sec, bool quiet) {
if (g_session.pm3_present == false) {
return PM3_ENOTTY;
}
uint32_t timeout_ms = timeout_sec * 1000U;
clearCommandBuffer();
SendCommandNG(CMD_SET_HF_FIELD_TIMEOUT, (uint8_t *)&timeout_ms, sizeof(timeout_ms));
PacketResponseNG resp;
if (WaitForResponseTimeoutW(CMD_SET_HF_FIELD_TIMEOUT, &resp, 1000, false) == false) {
if (!quiet) {
PrintAndLogEx(WARNING, "timeout while setting HF field timeout");
}
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS && !quiet) {
PrintAndLogEx(WARNING, "HF field timeout command failed (%d)", resp.status);
}
return resp.status;
}
// check if we can communicate with Pm3
int TestProxmark(pm3_device_t *dev) {
@@ -921,6 +945,13 @@ int TestProxmark(pm3_device_t *dev) {
return res;
}
}
if (g_session.hf_field_timeout_sec > 0) {
int timeout_res = SetHfFieldTimeout(g_session.hf_field_timeout_sec, true);
if (timeout_res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Failed to apply HF field timeout (" _YELLOW_("%u") " s)", g_session.hf_field_timeout_sec);
}
}
return PM3_SUCCESS;
}
+2
View File
@@ -120,6 +120,8 @@ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms
bool WaitForResponseTimeout(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout);
bool WaitForResponse(uint32_t cmd, PacketResponseNG *response);
int SetHfFieldTimeout(uint32_t timeout_sec, bool quiet);
//bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning);
bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, uint8_t *data, uint32_t datalen, PacketResponseNG *response, size_t ms_timeout, bool show_warning);
+2
View File
@@ -45,6 +45,7 @@ const static vocabulary_t vocabulary[] = {
{ 1, "prefs get client.debug" },
{ 1, "prefs get client.delay" },
{ 1, "prefs get client.timeout" },
{ 1, "prefs get hf.field.timeout_sec" },
{ 1, "prefs get color" },
{ 1, "prefs get savepaths" },
{ 1, "prefs get emoji" },
@@ -57,6 +58,7 @@ const static vocabulary_t vocabulary[] = {
{ 1, "prefs set client.debug" },
{ 1, "prefs set client.delay" },
{ 1, "prefs set client.timeout" },
{ 1, "prefs set hf.field.timeout_sec" },
{ 1, "prefs set color" },
{ 1, "prefs set emoji" },
{ 1, "prefs set hints" },
+77
View File
@@ -102,6 +102,7 @@ int preferences_load(void) {
g_session.client_debug_level = cdbOFF;
// g_session.device_debug_level = ddbOFF;
g_session.timeout = uart_get_timeouts();
g_session.hf_field_timeout_sec = 0;
g_session.window_changed = false;
g_session.plot.x = 10;
@@ -329,6 +330,7 @@ void preferences_save_callback(json_t *root) {
*/
JsonSaveInt(root, "client.exe.delay", g_session.client_exe_delay);
JsonSaveInt(root, "client.timeout", g_session.timeout);
JsonSaveInt(root, "hf.field.timeout_sec", g_session.hf_field_timeout_sec);
// MQTT
JsonSaveStr(root, "mqtt.server", g_session.mqtt_server);
@@ -440,6 +442,10 @@ void preferences_load_callback(json_t *root) {
if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.timeout", &i1) == 0)
g_session.timeout = i1;
// persistent HF field timeout (seconds)
if (json_unpack_ex(root, &up_error, 0, "{s:i}", "hf.field.timeout_sec", &i1) == 0)
g_session.hf_field_timeout_sec = (i1 > 0) ? (uint32_t)i1 : 0;
// MQTT server
if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.server", &s1) == 0)
setDefaultMqttServer(s1);
@@ -671,6 +677,14 @@ static void showClientTimeoutState(void) {
PrintAndLogEx(INFO, " communication timeout... " _GREEN_("%u") " ms", g_session.timeout);
}
static void showFieldTimeoutState(void) {
if (g_session.hf_field_timeout_sec == 0) {
PrintAndLogEx(INFO, " HF field timeout........ " _WHITE_("off"));
} else {
PrintAndLogEx(INFO, " HF field timeout........ " _GREEN_("%u") " s", g_session.hf_field_timeout_sec);
}
}
static void showMqttServer(prefShowOpt_t opt) {
if ((g_session.mqtt_server == NULL) || (strcmp(g_session.mqtt_server, "") == 0)) {
PrintAndLogEx(INFO, " MQTT server.............%s "_WHITE_("not set"), pref_show_status_msg(opt));
@@ -1067,6 +1081,50 @@ static int setCmdClientTimeout(const char *Cmd) {
return PM3_SUCCESS;
}
static int setCmdHfFieldTimeout(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs set hf.field.timeout_sec",
"Set persistent preference of PM3 HF field inactivity timeout",
"prefs set hf.field.timeout_sec --sec 0 --> disable HF field auto timeout\n"
"prefs set hf.field.timeout_sec --sec 5 --> turn HF field off after 5 seconds of inactivity\n"
"prefs set hf.field.timeout_sec --sec 300 --> turn HF field off after 5 minutes of inactivity\n"
"prefs set hf.field.timeout_sec --sec 900 --> turn HF field off after 15 minutes of inactivity\n");
void *argtable[] = {
arg_param_begin,
arg_int0("s", "sec", "<sec>", "HF field inactivity timeout in seconds"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int32_t arg = arg_get_int_def(ctx, 1, -1);
CLIParserFree(ctx);
if (arg < 0) {
showFieldTimeoutState();
return PM3_SUCCESS;
}
uint32_t new_value = (uint32_t)arg;
if (g_session.hf_field_timeout_sec != new_value) {
showFieldTimeoutState();
g_session.hf_field_timeout_sec = new_value;
showFieldTimeoutState();
preferences_save();
} else {
showFieldTimeoutState();
}
if (g_session.pm3_present) {
int res = SetHfFieldTimeout(g_session.hf_field_timeout_sec, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Failed to apply HF field timeout to connected PM3");
}
}
return PM3_SUCCESS;
}
static int setCmdHint(const char *Cmd) {
CLIParserContext *ctx;
@@ -1517,6 +1575,22 @@ static int getCmdClientTimeout(const char *Cmd) {
return PM3_SUCCESS;
}
static int getCmdHfFieldTimeout(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs get hf.field.timeout_sec",
"Get preference of PM3 HF field inactivity timeout",
"prefs get hf.field.timeout_sec"
);
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
showFieldTimeoutState();
return PM3_SUCCESS;
}
static int getCmdMqtt(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs get mqtt",
@@ -1540,6 +1614,7 @@ static command_t CommandTableGet[] = {
{"client.debug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"},
{"client.delay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"},
{"client.timeout", getCmdClientTimeout, AlwaysAvailable, "Get client execution delay preference"},
{"hf.field.timeout_sec", getCmdHfFieldTimeout, AlwaysAvailable, "Get PM3 HF field inactivity timeout preference"},
{"color", getCmdColor, AlwaysAvailable, "Get color support preference"},
{"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "},
// {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"},
@@ -1557,6 +1632,7 @@ static command_t CommandTableSet[] = {
{"client.debug", setCmdDebug, AlwaysAvailable, "Set client debug level"},
{"client.delay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"},
{"client.timeout", setCmdClientTimeout, AlwaysAvailable, "Set client communication timeout"},
{"hf.field.timeout_sec", setCmdHfFieldTimeout, AlwaysAvailable, "Set PM3 HF field inactivity timeout"},
{"color", setCmdColor, AlwaysAvailable, "Set color support"},
{"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"},
@@ -1630,6 +1706,7 @@ static int CmdPrefShow(const char *Cmd) {
showClientExeDelayState();
showOutputState(prefShowNone);
showClientTimeoutState();
showFieldTimeoutState();
showMqttServer(prefShowNone);
showMqttPort(prefShowNone);
showMqttTopic(prefShowNone);
+1
View File
@@ -63,6 +63,7 @@ typedef struct {
char *history_path;
pm3_device_t *current_device;
uint32_t timeout;
uint32_t hf_field_timeout_sec;
char *mqtt_server;
char *mqtt_port;
char *mqtt_topic;
+14
View File
@@ -97,7 +97,13 @@ void SpinDelay(int ms) {
// SpinDelay(1000);
// ti = GetTickCount() - ti;
// Dbprintf("timer(1s): %d t=%d", ti, GetTickCount());
// Increments whenever StartTickCount() reconfigures/resets RTTC.
// Callers can use this to detect that previously saved tick deltas are no longer valid.
static uint32_t g_tickcount_label = 0;
void StartTickCount(void) {
g_tickcount_label++;
// This timer is based on the slow clock. The slow clock frequency is between 22kHz and 40kHz.
// We can determine the actual slow clock frequency by looking at the Main Clock Frequency Register.
while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available...
@@ -122,6 +128,14 @@ uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) {
return (UINT32_MAX - start_ticks) + stop_ticks;
}
/*
* Get current RTTC counter label.
* If counter config changes between calls, the value is incremented.
*/
uint32_t GetTickCountLabel(void) {
return g_tickcount_label;
}
// -------------------------------------------------------------------------
// Timer for iso14443 commands. Uses ssp_clk from FPGA
// -------------------------------------------------------------------------
+1
View File
@@ -47,6 +47,7 @@ void SpinDelayUsPrecision(int us); // precision 0.6us , running for 43ms before
void StartTickCount(void);
uint32_t RAMFUNC GetTickCount(void);
uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks);
uint32_t GetTickCountLabel(void);
void ResetUSClock(void);
void SpinDelayCountUs(uint32_t us);
+14 -1
View File
@@ -118,7 +118,8 @@ Characteristics:
* 1 kHz, 32b (49 days), if used with 16b: 65s
* Configured at boot (or TIA) with `StartTickCount()`
* Time events with `GetTickCount()`/`GetTickCountDeltaDelta()`, see example
* Time events with `GetTickCount()`/`GetTickCountDelta()`, see example
* Each change in configuration of the clock increments the label value, retrievable through `GetTickCountLabel()`
* Coarse, based on the ~32kHz RC slow clock with some adjustment factor computed by TIA
* Maybe 2.5% error, can increase if temperature conditions change and no TIA is recomputed
* If TimingIntervalAcquisition() is called later, StartTickCount() is called again and RTC is reset
@@ -131,12 +132,24 @@ uint32_t ti = GetTickCount();
uint32_t delta = GetTickCountDelta(ti);
```
If `StartTickCount()` may run between two reads (e.g. via TIA), pair tick with a label:
```
uint32_t label = GetTickCountLabel();
uint32_t ti = GetTickCount();
...do stuff...
if (label == GetTickCountLabel()) {
uint32_t delta = GetTickCountDelta(ti);
}
```
Current usages:
* cheap random for nonces, e.g. `prng_successor(GetTickCount(), 32)`
* rough timing of some operations, only for informative purposes
* timeouts
* USB connection speed measure
* Optional HF field inactivity timeout
## Occasional PWM timer
^[Top](#top)
+1
View File
@@ -531,6 +531,7 @@ typedef struct {
#define CMD_TIA 0x0117
#define CMD_BREAK_LOOP 0x0118
#define CMD_SET_TEAROFF 0x0119
#define CMD_SET_HF_FIELD_TIMEOUT 0x011A
#define CMD_GET_DBGMODE 0x0120
// RDV40, Flash memory operations