mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 13:23:51 -07:00
lf cmdread - now uses cliparser. Had to change the "extra" param structure to fit.\n lf config - now has a reset param
This commit is contained in:
parent
6c31cda33b
commit
2a17cb5545
1 changed files with 112 additions and 120 deletions
|
@ -64,36 +64,6 @@ static bool g_lf_threshold_set = false;
|
||||||
|
|
||||||
static int CmdHelp(const char *Cmd);
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
static int usage_lf_cmdread(void) {
|
|
||||||
PrintAndLogEx(NORMAL, "Usage: lf cmdread d <delay duration> z <zero duration> o <one duration> [e <symbol> <duration>] c <cmd symbols> [q] [s #samples] [@]");
|
|
||||||
PrintAndLogEx(NORMAL, "Options:");
|
|
||||||
PrintAndLogEx(NORMAL, " h This help");
|
|
||||||
PrintAndLogEx(NORMAL, " d <duration> delay OFF period, (0 for bitbang mode)");
|
|
||||||
PrintAndLogEx(NORMAL, " z <duration> ZERO time period");
|
|
||||||
PrintAndLogEx(NORMAL, " o <duration> ONE time period");
|
|
||||||
PrintAndLogEx(NORMAL, " e <symbol> <duration> Extra symbol definition and duration (up to %i)", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
|
||||||
PrintAndLogEx(NORMAL, " b <duration> B period");
|
|
||||||
PrintAndLogEx(NORMAL, " c <cmd> Command symbols (0/1/...)");
|
|
||||||
PrintAndLogEx(NORMAL, " q silent (optional)");
|
|
||||||
PrintAndLogEx(NORMAL, " s #samples number of samples to collect (optional)");
|
|
||||||
PrintAndLogEx(NORMAL, " @ run continuously until a key is pressed (optional)");
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
|
||||||
PrintAndLogEx(NORMAL, " ************* " _YELLOW_("All periods in decimal and in microseconds (us)"));
|
|
||||||
PrintAndLogEx(NORMAL, "Examples:");
|
|
||||||
PrintAndLogEx(NORMAL, _CYAN_(" probing for Hitag 1/Hitag S") ":");
|
|
||||||
PrintAndLogEx(NORMAL, _YELLOW_(" lf cmdread d 50 z 116 o 166 e W 3000 c W00110"));
|
|
||||||
PrintAndLogEx(NORMAL, _CYAN_(" probing for Hitag 2") ":");
|
|
||||||
PrintAndLogEx(NORMAL, _YELLOW_(" lf cmdread d 50 z 116 o 166 e W 3000 c W11000"));
|
|
||||||
PrintAndLogEx(NORMAL, _CYAN_(" probing for Hitag 2, oscilloscope style") ":");
|
|
||||||
PrintAndLogEx(NORMAL, _YELLOW_(" data plot"));
|
|
||||||
PrintAndLogEx(NORMAL, _YELLOW_(" lf cmdread d 50 z 116 o 166 e W 3000 c W11000 q s 2000 @"));
|
|
||||||
PrintAndLogEx(NORMAL, _CYAN_(" probing for Hitag (us)") ":");
|
|
||||||
PrintAndLogEx(NORMAL, _YELLOW_(" lf cmdread d 48 z 112 o 176 e W 3000 e S 240 e E 336 c W0S00000010000E"));
|
|
||||||
PrintAndLogEx(NORMAL, "Extras:");
|
|
||||||
PrintAndLogEx(NORMAL, " use " _YELLOW_("'lf config'")" to set parameters.");
|
|
||||||
return PM3_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Informative user function.
|
// Informative user function.
|
||||||
// loop and wait for either keyboard press or pm3 button to exit
|
// loop and wait for either keyboard press or pm3 button to exit
|
||||||
// if key event, send break loop cmd to Pm3
|
// if key event, send break loop cmd to Pm3
|
||||||
|
@ -239,14 +209,51 @@ static int CmdLFTune(const char *Cmd) {
|
||||||
|
|
||||||
/* send a LF command before reading */
|
/* send a LF command before reading */
|
||||||
int CmdLFCommandRead(const char *Cmd) {
|
int CmdLFCommandRead(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf cmdread",
|
||||||
|
"Modulate LF reader field to send command before read. All periods in microseconds.\n"
|
||||||
|
" - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters"),
|
||||||
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 --> probing for Hitag 1/S\n"
|
||||||
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W11000 --> probing for Hitag 2\n"
|
||||||
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W11000 -q -s 2000 -@ --> probing for Hitag 2, oscilloscope style\n"
|
||||||
|
"lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -c W0S00000010000E --> probing for Hitag (us)\n"
|
||||||
|
);
|
||||||
|
|
||||||
if (!session.pm3_present) return PM3_ENOTTY;
|
char div_str[70] = {0};
|
||||||
|
sprintf(div_str, "Extra symbol definition and duration (up to %i)", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
||||||
|
|
||||||
bool errors = false;
|
void *argtable[] = {
|
||||||
bool verbose = true;
|
arg_param_begin,
|
||||||
bool continuous = false;
|
arg_u64_0("d", "duration", "<us>", "delay OFF period, (0 for bitbang mode)"),
|
||||||
uint32_t samples = 0;
|
arg_str0("c", "cmd", "<0|1|...>", "command symbols"),
|
||||||
uint16_t datalen = 0;
|
arg_strx0("e", "extra", "<us>", div_str),
|
||||||
|
arg_u64_0("o", "one", "<us>", "ONE time period"),
|
||||||
|
arg_u64_0("z", "zero", "<us>", "ZERO time period"),
|
||||||
|
arg_u64_0("s", "samples", "<dec>", "number of samples to collect"),
|
||||||
|
arg_lit0("v", "verbose", "verbose output"),
|
||||||
|
arg_lit0("@", NULL, "continuous mode"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
uint32_t delay = arg_get_u32_def(ctx, 1, 0);
|
||||||
|
|
||||||
|
int cmd_len = 128;
|
||||||
|
char cmd[128] = {0};
|
||||||
|
CLIGetStrWithReturn(ctx, 2, (uint8_t*)cmd, &cmd_len);
|
||||||
|
|
||||||
|
int extra_arg_len = 250;
|
||||||
|
char extra_arg[250] = {0};
|
||||||
|
CLIGetStrWithReturn(ctx, 3, (uint8_t*)extra_arg, &extra_arg_len);
|
||||||
|
|
||||||
|
uint16_t period_1 = arg_get_u32_def(ctx, 4, 0);
|
||||||
|
uint16_t period_0 = arg_get_u32_def(ctx, 5, 0);
|
||||||
|
uint32_t samples = arg_get_u32_def(ctx, 6, 0);
|
||||||
|
bool verbose = arg_get_lit(ctx, 7);
|
||||||
|
bool cm = arg_get_lit(ctx, 8);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if (session.pm3_present == false)
|
||||||
|
return PM3_ENOTTY;
|
||||||
|
|
||||||
const uint8_t payload_header_size = 12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
const uint8_t payload_header_size = 12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
||||||
struct p {
|
struct p {
|
||||||
|
@ -259,76 +266,57 @@ int CmdLFCommandRead(const char *Cmd) {
|
||||||
bool verbose : 1;
|
bool verbose : 1;
|
||||||
uint8_t data[PM3_CMD_DATA_SIZE - payload_header_size];
|
uint8_t data[PM3_CMD_DATA_SIZE - payload_header_size];
|
||||||
} PACKED payload;
|
} PACKED payload;
|
||||||
|
payload.delay = delay;
|
||||||
|
payload.period_1 = period_1;
|
||||||
|
payload.period_0 = period_0;
|
||||||
payload.samples = samples;
|
payload.samples = samples;
|
||||||
payload.verbose = verbose;
|
payload.verbose = verbose;
|
||||||
uint8_t index_extra = 0;
|
memcpy(payload.data, cmd, cmd_len);
|
||||||
|
|
||||||
uint8_t cmdp = 0;
|
// extra symbol definition
|
||||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
uint8_t index_extra = 0;
|
||||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
int i = 0;
|
||||||
case 'h':
|
for (; i < extra_arg_len; ) {
|
||||||
return usage_lf_cmdread();
|
|
||||||
case 'c': // cmd bytes 1010
|
if (index_extra < LF_CMDREAD_MAX_EXTRA_SYMBOLS - 1) {
|
||||||
datalen = param_getstr(Cmd, cmdp + 1, (char *)&payload.data, sizeof(payload.data));
|
payload.symbol_extra[index_extra] = extra_arg[i];
|
||||||
cmdp += 2;
|
int tmp = atoi(extra_arg + (i + 1));
|
||||||
break;
|
payload.period_extra[index_extra] = tmp;
|
||||||
case 'd': // delay
|
index_extra++;
|
||||||
payload.delay = param_get32ex(Cmd, cmdp + 1, 0, 10);
|
i++;
|
||||||
cmdp += 2;
|
while (extra_arg[i] >= 0x30 && extra_arg[i] <= 0x39)
|
||||||
break;
|
i++;
|
||||||
case 'z': // zero
|
|
||||||
payload.period_0 = param_get32ex(Cmd, cmdp + 1, 0, 10) & 0xFFFF;
|
} else {
|
||||||
cmdp += 2;
|
PrintAndLogEx(WARNING, "Too many extra symbols, please define up to %i symbols", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
||||||
break;
|
|
||||||
case 'o': // ones
|
|
||||||
payload.period_1 = param_get32ex(Cmd, cmdp + 1, 0, 10) & 0xFFFF;
|
|
||||||
cmdp += 2;
|
|
||||||
break;
|
|
||||||
case 'e': // extra symbol definition
|
|
||||||
if (index_extra < LF_CMDREAD_MAX_EXTRA_SYMBOLS - 1) {
|
|
||||||
payload.symbol_extra[index_extra] = param_getchar(Cmd, cmdp + 1);
|
|
||||||
payload.period_extra[index_extra] = param_get32ex(Cmd, cmdp + 2, 0, 10) & 0xFFFF;
|
|
||||||
index_extra++;
|
|
||||||
cmdp += 3;
|
|
||||||
} else {
|
|
||||||
PrintAndLogEx(WARNING, "Too many extra symbols, please define up to %i symbols", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
|
||||||
errors = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
samples = param_get32ex(Cmd, cmdp + 1, 0, 10);
|
|
||||||
payload.samples = samples;
|
|
||||||
cmdp += 2;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
verbose = false;
|
|
||||||
payload.verbose = verbose;
|
|
||||||
cmdp++;
|
|
||||||
break;
|
|
||||||
case '@':
|
|
||||||
continuous = true;
|
|
||||||
cmdp++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
|
||||||
errors = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bitbang mode
|
// bitbang mode
|
||||||
if (payload.delay == 0) {
|
if (payload.delay == 0) {
|
||||||
if (payload.period_0 < 7 || payload.period_1 < 7) {
|
if (payload.period_0 < 7 || payload.period_1 < 7) {
|
||||||
PrintAndLogEx(WARNING, "warning periods cannot be less than 7us in bit bang mode");
|
PrintAndLogEx(WARNING, "periods cannot be less than 7us in bit bang mode");
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Validations
|
PrintAndLogEx(DEBUG, "Cmd read - settings");
|
||||||
if (errors || cmdp == 0) return usage_lf_cmdread();
|
PrintAndLogEx(DEBUG, "-------------------");
|
||||||
if (continuous) {
|
PrintAndLogEx(DEBUG, "delay: %u , zero %u , one %u , samples %u", payload.delay, payload.period_0, payload.period_1, payload.samples);
|
||||||
PrintAndLogEx(INFO, "Press " _GREEN_("Enter") " to exit");
|
PrintAndLogEx(DEBUG, "Extra symbols");
|
||||||
|
PrintAndLogEx(DEBUG, "-------------");
|
||||||
|
for (i = 0; i < LF_CMDREAD_MAX_EXTRA_SYMBOLS; i++) {
|
||||||
|
if (payload.symbol_extra[i] == 0x00)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PrintAndLogEx(DEBUG, " %c - %u", payload.symbol_extra[i], payload.period_extra[i]);
|
||||||
}
|
}
|
||||||
|
PrintAndLogEx(DEBUG, "data: %s", payload.data);
|
||||||
|
|
||||||
|
if (cm) {
|
||||||
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
PrintAndLogEx(SUCCESS, "Sending command...");
|
PrintAndLogEx(SUCCESS, "Sending command...");
|
||||||
}
|
}
|
||||||
|
@ -336,11 +324,11 @@ int CmdLFCommandRead(const char *Cmd) {
|
||||||
int ret = PM3_SUCCESS;
|
int ret = PM3_SUCCESS;
|
||||||
do {
|
do {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(CMD_LF_MOD_THEN_ACQ_RAW_ADC, (uint8_t *)&payload, payload_header_size + datalen);
|
SendCommandNG(CMD_LF_MOD_THEN_ACQ_RAW_ADC, (uint8_t *)&payload, payload_header_size + cmd_len);
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
|
|
||||||
uint8_t i = 10;
|
i = 10;
|
||||||
// 20sec wait loop
|
// 20sec wait loop
|
||||||
while (!WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) && i != 0) {
|
while (!WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) && i != 0) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
@ -351,25 +339,23 @@ int CmdLFCommandRead(const char *Cmd) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
}
|
}
|
||||||
if (resp.status == PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
if (i) {
|
|
||||||
if (verbose) {
|
|
||||||
PrintAndLogEx(SUCCESS, "downloading response signal data");
|
|
||||||
}
|
|
||||||
getSamples(samples, false);
|
|
||||||
ret = PM3_SUCCESS;
|
|
||||||
} else {
|
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
|
||||||
return PM3_ETIMEOUT;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintAndLogEx(WARNING, "command failed.");
|
PrintAndLogEx(WARNING, "command failed.");
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
if (kbd_enter_pressed()) {
|
|
||||||
break;
|
if (i) {
|
||||||
|
if (verbose) {
|
||||||
|
PrintAndLogEx(SUCCESS, "downloading response signal data");
|
||||||
|
}
|
||||||
|
getSamples(samples, false);
|
||||||
|
ret = PM3_SUCCESS;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||||
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
} while (continuous);
|
|
||||||
|
} while (cm && kbd_enter_pressed() == false);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +473,7 @@ int CmdLFConfig(const char *Cmd) {
|
||||||
_CYAN_(" - use ") _YELLOW_("`lf sniff`") _CYAN_(" performs a sniff (no active field)"),
|
_CYAN_(" - use ") _YELLOW_("`lf sniff`") _CYAN_(" performs a sniff (no active field)"),
|
||||||
"lf config --> shows current config\n"
|
"lf config --> shows current config\n"
|
||||||
"lf config -b 8 --125 --> samples at 125 kHz, 8 bps\n"
|
"lf config -b 8 --125 --> samples at 125 kHz, 8 bps\n"
|
||||||
"lf config -b 4 --134 --dec 3 --> samples at 134 kHz, averages three samples into one, stored with a resolution of 4 bits per sample"
|
"lf config -b 4 --134 --dec 3 --> samples at 134 kHz, averages three samples into one, stored with a resolution of 4 bits per sample\n"
|
||||||
"lf config --trig 20 -s 10000 --> trigger sampling when above 20, skip 10 000 first samples after triggered\n"
|
"lf config --trig 20 -s 10000 --> trigger sampling when above 20, skip 10 000 first samples after triggered\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -503,6 +489,7 @@ int CmdLFConfig(const char *Cmd) {
|
||||||
arg_int0(NULL, "dec", "<1-8>", "sets decimation. A value of N saves only 1 in N samples (default 1)"),
|
arg_int0(NULL, "dec", "<1-8>", "sets decimation. A value of N saves only 1 in N samples (default 1)"),
|
||||||
arg_int0(NULL, "divisor", "<19-255>", div_str),
|
arg_int0(NULL, "divisor", "<19-255>", div_str),
|
||||||
arg_int0("f", "freq", "<47-600>", "manually set frequency in kHz"),
|
arg_int0("f", "freq", "<47-600>", "manually set frequency in kHz"),
|
||||||
|
arg_lit0("r", "reset", "reset values to defaults"),
|
||||||
arg_int0("s", "skip", "<dec>", "sets a number of samples to skip before capture (default 0)"),
|
arg_int0("s", "skip", "<dec>", "sets a number of samples to skip before capture (default 0)"),
|
||||||
arg_int0("t", "trig", "<0-128>", "sets trigger threshold. 0 means no threshold"),
|
arg_int0("t", "trig", "<0-128>", "sets trigger threshold. 0 means no threshold"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
|
@ -515,8 +502,9 @@ int CmdLFConfig(const char *Cmd) {
|
||||||
int8_t dec = arg_get_int_def(ctx, 5, -1);
|
int8_t dec = arg_get_int_def(ctx, 5, -1);
|
||||||
int16_t div = arg_get_int_def(ctx, 6, -1);
|
int16_t div = arg_get_int_def(ctx, 6, -1);
|
||||||
int16_t freq = arg_get_int_def(ctx, 7, -1);
|
int16_t freq = arg_get_int_def(ctx, 7, -1);
|
||||||
int32_t skip = arg_get_int_def(ctx, 8, -1);
|
bool reset = arg_get_lit(ctx, 8);
|
||||||
int16_t trigg = arg_get_int_def(ctx, 9, -1);
|
int32_t skip = arg_get_int_def(ctx, 9, -1);
|
||||||
|
int16_t trigg = arg_get_int_def(ctx, 10, -1);
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
if (session.pm3_present == false)
|
if (session.pm3_present == false)
|
||||||
|
@ -542,12 +530,16 @@ int CmdLFConfig(const char *Cmd) {
|
||||||
.verbose = true
|
.verbose = true
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
if (reset) {
|
||||||
int8_t bps = arg_get_int_def(ctx, 4, 8);
|
config.decimation = 1;
|
||||||
int8_t dec = arg_get_int_def(ctx, 5, 1);
|
config.bits_per_sample = 8;
|
||||||
int16_t div = arg_get_int_def(ctx, 6, 95);
|
config.averaging = 1,
|
||||||
int16_t freq = arg_get_int_def(ctx, 7, 125);
|
config.divisor = LF_DIVISOR_125;
|
||||||
*/
|
config.samples_to_skip = 0;
|
||||||
|
config.trigger_threshold = 0;
|
||||||
|
g_lf_threshold_set = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (use_125)
|
if (use_125)
|
||||||
config.divisor = LF_DIVISOR_125;
|
config.divisor = LF_DIVISOR_125;
|
||||||
|
|
||||||
|
@ -657,7 +649,7 @@ int CmdLFRead(const char *Cmd) {
|
||||||
int ret = PM3_SUCCESS;
|
int ret = PM3_SUCCESS;
|
||||||
do {
|
do {
|
||||||
ret = lf_read(verbose, samples);
|
ret = lf_read(verbose, samples);
|
||||||
} while (cm && !kbd_enter_pressed());
|
} while (cm && kbd_enter_pressed() == false);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1524,7 +1516,7 @@ static command_t CommandTable[] = {
|
||||||
{"visa2000", CmdLFVisa2k, AlwaysAvailable, "{ Visa2000 RFIDs... }"},
|
{"visa2000", CmdLFVisa2k, AlwaysAvailable, "{ Visa2000 RFIDs... }"},
|
||||||
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
|
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
|
||||||
{"config", CmdLFConfig, IfPm3Lf, "Get/Set config for LF sampling, bit/sample, decimation, frequency"},
|
{"config", CmdLFConfig, IfPm3Lf, "Get/Set config for LF sampling, bit/sample, decimation, frequency"},
|
||||||
{"cmdread", CmdLFCommandRead, IfPm3Lf, "Modulate LF reader field to send command before read (all periods in microseconds)"},
|
{"cmdread", CmdLFCommandRead, IfPm3Lf, "Modulate LF reader field to send command before read"},
|
||||||
{"read", CmdLFRead, IfPm3Lf, "Read LF tag"},
|
{"read", CmdLFRead, IfPm3Lf, "Read LF tag"},
|
||||||
{"search", CmdLFfind, AlwaysAvailable, "Read and Search for valid known tag"},
|
{"search", CmdLFfind, AlwaysAvailable, "Read and Search for valid known tag"},
|
||||||
{"sim", CmdLFSim, IfPm3Lf, "Simulate LF tag from buffer"},
|
{"sim", CmdLFSim, IfPm3Lf, "Simulate LF tag from buffer"},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue