1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Low frequency HID commands (known)
19 // RF interface, programming a T55x7 clone, 26-bit HID H10301 encoding:
20 // http://www.proxmark.org/files/Documents/125%20kHz%20-%20HID/HID_format_example.pdf
22 // "Understanding Card Data Formats"
23 // https://www.hidglobal.com/sites/default/files/hid-understanding_card_data_formats-wp-en.pdf
25 // "What Format Do You Need?"
26 // https://www.hidglobal.com/sites/default/files/resource_files/hid-prox-br-en.pdf
27 //-----------------------------------------------------------------------------
34 #include "cmdparser.h" // command_t
36 #include "commonutil.h" // ARRAYLEN
37 #include "cliparser.h"
40 #include "cmddata.h" // g_debugMode, demodbuff cmds
41 #include "cmdlf.h" // lf_read, lfsim_wait_check
42 #include "util_posix.h"
44 #include "wiegand_formats.h"
45 #include "wiegand_formatutils.h"
46 #include "cmdlfem4x05.h" // EM defines
47 #include "loclass/cipherutils.h" // bitstreamout
53 static int CmdHelp(const char *Cmd
);
55 // sending three times. Didn't seem to break the previous sim?
56 static int sendPing(void) {
57 SendCommandNG(CMD_BREAK_LOOP
, NULL
, 0);
58 SendCommandNG(CMD_PING
, NULL
, 0);
60 PacketResponseNG resp
;
61 if (WaitForResponseTimeout(CMD_PING
, &resp
, 1000) == false) {
66 static int sendTry(uint8_t format_idx
, wiegand_card_t
*card
, uint32_t delay
, bool verbose
) {
68 wiegand_message_t packed
;
69 memset(&packed
, 0, sizeof(wiegand_message_t
));
71 if (HIDPack(format_idx
, card
, &packed
, true) == false) {
72 PrintAndLogEx(WARNING
, "The card data could not be encoded in the selected format.");
77 PrintAndLogEx(INFO
, "Trying FC: " _YELLOW_("%u") " CN: " _YELLOW_("%"PRIu64
) " Issue level: " _YELLOW_("%u") " OEM: " _YELLOW_("%u")
85 lf_hidsim_t payload
= {
91 .longFMT
= (packed
.Mid
> 0xFFF)
96 SendCommandNG(CMD_LF_HID_SIMULATE
, (uint8_t *)&payload
, sizeof(payload
));
98 PacketResponseNG resp;
99 WaitForResponse(CMD_LF_HID_SIMULATE, &resp);
100 if (resp.status == PM3_EOPABORTED)
107 //by marshmellow (based on existing demod + holiman's refactor)
108 //HID Prox demod - FSK RF/50 with preamble of 00011101 (then manchester encoded)
109 //print full HID Prox ID and some bit format details if found
110 int demodHID(bool verbose
) {
111 (void) verbose
; // unused so far
113 // HID simulation etc uses 0/1 as signal data. This must be converted in order to demod it back again
114 if (isGraphBitstream()) {
115 convertGraphFromBitstream();
118 //raw fsk demod no manchester decoding no start bit finding just get binary from wave
119 uint32_t hi2
= 0, hi
= 0, lo
= 0;
121 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
123 PrintAndLogEx(FAILED
, "failed to allocate memory");
126 size_t size
= getFromGraphBuffer(bits
);
128 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID not enough samples"));
132 //get binary from fsk wave
134 int idx
= HIDdemodFSK(bits
, &size
, &hi2
, &hi
, &lo
, &waveIdx
);
138 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID not enough samples"));
140 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID just noise detected"));
142 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID problem during FSK demod"));
144 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID preamble not found"));
146 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID error in Manchester data, size %zu"), size
);
148 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID error demoding fsk %d"), idx
);
154 setDemodBuff(bits
, size
, idx
);
155 setClockGrid(50, waveIdx
+ (idx
* 50));
158 if (hi2
== 0 && hi
== 0 && lo
== 0) {
159 PrintAndLogEx(DEBUG
, "DEBUG: Error - " _RED_("HID no values found"));
163 wiegand_message_t packed
= initialize_message_object(hi2
, hi
, lo
, 0);
164 if (HIDTryUnpack(&packed
) == false) {
165 printDemodBuff(0, false, false, true);
167 PrintAndLogEx(INFO
, "raw: " _GREEN_("%08x%08x%08x"), hi2
, hi
, lo
);
169 PrintAndLogEx(DEBUG
, "DEBUG: HID idx: %d, Len: %zu, Printing DemodBuffer: ", idx
, size
);
171 PrintAndLogEx(DEBUG
, "raw: " _GREEN_("%08x%08x%08x"), hi2
, hi
, lo
);
173 printDemodBuff(0, false, false, false);
179 static int CmdHIDDemod(const char *Cmd
) {
180 CLIParserContext
*ctx
;
181 CLIParserInit(&ctx
, "lf hid demod",
182 "Try to find HID Prox preamble, if found decode / descramble data",
190 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
192 return demodHID(true);
195 // this read is the "normal" read, which download lf signal and tries to demod here.
196 static int CmdHIDReader(const char *Cmd
) {
197 CLIParserContext
*ctx
;
198 CLIParserInit(&ctx
, "lf hid reader",
199 "read a HID Prox tag",
200 "lf hid reader -@ -> continuous reader mode"
205 arg_lit0("@", NULL
, "optional - continuous reader mode"),
208 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
209 bool cm
= arg_get_lit(ctx
, 1);
213 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
217 lf_read(false, 16000);
219 } while (cm
&& !kbd_enter_pressed());
224 // this read loops on device side.
225 // uses the demod in lfops.c
226 static int CmdHIDWatch(const char *Cmd
) {
227 CLIParserContext
*ctx
;
229 CLIParserInit(&ctx
, "lf hid watch",
230 "Enables HID compatible reader mode printing details.\n"
231 "By default, values are printed and logged until the button is pressed or another USB command is issued.\n",
239 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
242 PrintAndLogEx(SUCCESS
, "Watching for HID Prox cards - place tag on antenna");
243 PrintAndLogEx(INFO
, "Press " _GREEN_("pm3 button") " to stop reading cards");
244 clearCommandBuffer();
245 SendCommandNG(CMD_LF_HID_WATCH
, NULL
, 0);
246 return lfsim_wait_check(CMD_LF_HID_WATCH
);
249 static int CmdHIDSim(const char *Cmd
) {
251 CLIParserContext
*ctx
;
252 CLIParserInit(&ctx
, "lf hid sim",
253 "Enables simulation of HID card with card number.\n"
254 "Simulation runs until the button is pressed or another USB command is issued.",
255 "lf hid sim -r 2006ec0c86 -> HID 10301 26 bit\n"
256 "lf hid sim -r 2e0ec00c87 -> HID Corporate 35 bit\n"
257 "lf hid sim -r 01f0760643c3 -> HID P10001 40 bit\n"
258 "lf hid sim -r 01400076000c86 -> HID Corporate 48 bit\n"
259 "lf hid sim -w H10301 --fc 118 --cn 1603 -> HID 10301 26 bit\n"
264 arg_str0("w", "wiegand", "<format>", "see " _YELLOW_("`wiegand list`") " for available formats"),
265 arg_u64_0(NULL
, "fc", "<dec>", "facility code"),
266 arg_u64_0(NULL
, "cn", "<dec>", "card number"),
267 arg_u64_0("i", NULL
, "<dec>", "issue level"),
268 arg_u64_0("o", "oem", "<dec>", "OEM code"),
269 arg_str0("r", "raw", "<hex>", "raw bytes"),
272 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
274 char format
[16] = {0};
276 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)format
, sizeof(format
), &format_len
);
279 memset(&card
, 0, sizeof(wiegand_card_t
));
280 card
.FacilityCode
= arg_get_u32_def(ctx
, 2, 0);
281 card
.CardNumber
= arg_get_u32_def(ctx
, 3, 0);
282 card
.IssueLevel
= arg_get_u32_def(ctx
, 4, 0);
283 card
.OEM
= arg_get_u32_def(ctx
, 5, 0);
287 CLIParamStrToBuf(arg_get_str(ctx
, 6), (uint8_t *)raw
, sizeof(raw
), &raw_len
);
290 wiegand_message_t packed
;
291 memset(&packed
, 0, sizeof(wiegand_message_t
));
294 int format_idx
= HIDFindCardFormat(format
);
295 if (format_idx
== -1 && raw_len
== 0) {
296 PrintAndLogEx(WARNING
, "Unknown format: " _YELLOW_("%s"), format
);
301 uint32_t top
= 0, mid
= 0, bot
= 0;
302 hexstring_to_u96(&top
, &mid
, &bot
, raw
);
307 if (HIDPack(format_idx
, &card
, &packed
, true) == false) {
308 PrintAndLogEx(WARNING
, "The card data could not be encoded in the selected format.");
314 PrintAndLogEx(INFO
, "Simulating HID tag");
315 HIDTryUnpack(&packed
);
317 PrintAndLogEx(INFO
, "Simulating HID tag using raw " _GREEN_("%s"), raw
);
321 payload
.hi2
= packed
.Top
;
322 payload
.hi
= packed
.Mid
;
323 payload
.lo
= packed
.Bot
;
324 payload
.longFMT
= (packed
.Mid
> 0xFFF);
326 clearCommandBuffer();
327 SendCommandNG(CMD_LF_HID_SIMULATE
, (uint8_t *)&payload
, sizeof(payload
));
328 return lfsim_wait_check(CMD_LF_HID_SIMULATE
);
331 static int CmdHIDClone(const char *Cmd
) {
333 CLIParserContext
*ctx
;
334 CLIParserInit(&ctx
, "lf hid clone",
335 "clone a HID Prox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n"
336 "Tag must be on the antenna when issuing this command.",
337 "lf hid clone -r 2006ec0c86 -> write raw value for T55x7 tag (HID 10301 26 bit)\n"
338 "lf hid clone -r 2e0ec00c87 -> write raw value for T55x7 tag (HID Corporate 35 bit)\n"
339 "lf hid clone -r 01f0760643c3 -> write raw value for T55x7 tag (HID P10001 40 bit)\n"
340 "lf hid clone -r 01400076000c86 -> write raw value for T55x7 tag (HID Corporate 48 bit)\n"
341 "lf hid clone -w H10301 --fc 118 --cn 1603 -> HID 10301 26 bit, encode for T55x7 tag\n"
342 "lf hid clone -w H10301 --fc 118 --cn 1603 --q5 -> HID 10301 26 bit, encode for Q5/T5555 tag\n"
343 "lf hid clone -w H10301 --fc 118 --cn 1603 --em -> HID 10301 26 bit, encode for EM4305/4469"
348 arg_str0("w", "wiegand", "<format>", "see " _YELLOW_("`wiegand list`") " for available formats"),
349 arg_u64_0(NULL
, "fc", "<dec>", "facility code"),
350 arg_u64_0(NULL
, "cn", "<dec>", "card number"),
351 arg_int0("i", NULL
, "<dec>", "issue level"),
352 arg_int0("o", "oem", "<dec>", "OEM code"),
353 arg_str0("r", "raw", "<hex>", "raw bytes"),
354 arg_lit0(NULL
, "q5", "optional - specify writing to Q5/T5555 tag"),
355 arg_lit0(NULL
, "em", "optional - specify writing to EM4305/4469 tag"),
356 arg_str0(NULL
, "bin", "<bin>", "Binary string i.e 0001001001"),
359 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
361 char format
[16] = {0};
363 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)format
, sizeof(format
), &format_len
);
366 memset(&card
, 0, sizeof(wiegand_card_t
));
367 card
.FacilityCode
= arg_get_u32_def(ctx
, 2, 0);
368 card
.CardNumber
= arg_get_u32_def(ctx
, 3, 0);
369 card
.IssueLevel
= arg_get_u32_def(ctx
, 4, 0);
370 card
.OEM
= arg_get_u32_def(ctx
, 5, 0);
374 CLIParamStrToBuf(arg_get_str(ctx
, 6), (uint8_t *)raw
, sizeof(raw
), &raw_len
);
376 bool q5
= arg_get_lit(ctx
, 7);
377 bool em
= arg_get_lit(ctx
, 8);
379 // t5577 can do 6 blocks with 32bits == 192 bits, HID is manchester encoded and doubles in length.
380 // With parity, manchester and preamble we have about 3 blocks to play with. Ie: 96 bits
381 uint8_t bin
[97] = {0};
382 int bin_len
= sizeof(bin
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
383 CLIGetStrWithReturn(ctx
, 9, bin
, &bin_len
);
387 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
392 PrintAndLogEx(ERR
, "Binary wiegand string must be less than 96 bits");
396 wiegand_message_t packed
;
397 memset(&packed
, 0, sizeof(wiegand_message_t
));
400 int format_idx
= HIDFindCardFormat(format
);
401 if (format_idx
== -1 && raw_len
== 0) {
402 PrintAndLogEx(WARNING
, "Unknown format: " _YELLOW_("%s"), format
);
406 uint32_t top
= 0, mid
= 0, bot
= 0;
408 hexstring_to_u96(&top
, &mid
, &bot
, raw
);
412 } else if (bin_len
) {
415 memset(hex
, 0, sizeof(hex
));
416 BitstreamOut_t bout
= {hex
, 0, 0 };
418 for (int i
= 0; i
< 96 - bin_len
- 1; i
++) {
421 // add binary sentinel bit.
424 // convert binary string to hex bytes
425 for (int i
= 0; i
< bin_len
; i
++) {
433 packed
.Length
= bin_len
;
434 packed
.Top
= bytes_to_num(hex
, 4);
435 packed
.Mid
= bytes_to_num(hex
+ 4, 4);
436 packed
.Bot
= bytes_to_num(hex
+ 8, 4);
437 add_HID_header(&packed
);
440 if (HIDPack(format_idx
, &card
, &packed
, true) == false) {
441 PrintAndLogEx(WARNING
, "The card data could not be encoded in the selected format.");
446 char cardtype
[16] = {"T55x7"};
449 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
454 PrintAndLogEx(WARNING
, "Beware some EM4305 tags don't support FSK and datarate = RF/50, check your tag copy!");
455 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
459 PrintAndLogEx(INFO
, "Preparing to clone HID tag");
460 HIDUnpack(format_idx
, &packed
);
462 PrintAndLogEx(INFO
, "Preparing to clone HID tag using raw " _YELLOW_("%s"), raw
);
466 payload
.hi2
= packed
.Top
;
467 payload
.hi
= packed
.Mid
;
468 payload
.lo
= packed
.Bot
;
469 payload
.longFMT
= (packed
.Mid
> 0xFFF);
473 clearCommandBuffer();
474 SendCommandNG(CMD_LF_HID_CLONE
, (uint8_t *)&payload
, sizeof(payload
));
475 PacketResponseNG resp
;
476 if (WaitForResponseTimeout(CMD_LF_HID_CLONE
, &resp
, 2000) == false) {
477 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
481 if (resp
.status
== PM3_SUCCESS
) {
482 PrintAndLogEx(SUCCESS
, "Done!");
483 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf hid reader`") " to verify");
485 PrintAndLogEx(FAILED
, "cloning ( " _RED_("fail") " )");
491 PrintAndLogEx(NORMAL, "HID | OEM | FC | CN | Wiegand | HID Formatted");
492 PrintAndLogEx(NORMAL, "----+-----+------+---------+-----------+--------------------");
493 PrintAndLogEx(NORMAL, " %u | %03u | %03u | %" PRIu64 " | %" PRIX64 " | %" PRIX64,
502 PrintAndLogEx(NORMAL, "----+-----+-----+-------+-----------+--------------------");
505 static int CmdHIDBrute(const char *Cmd
) {
506 CLIParserContext
*ctx
;
507 CLIParserInit(&ctx
, "lf hid brute",
508 "Enables bruteforce of HID readers with specified facility code or card number. This is an attack against the reader.\n"
509 "If the field being bruteforced is provided, it starts with it and goes up / down one step while maintaining other supplied values.\n"
510 "If the field being bruteforced is not provided, it will iterate through the full range while maintaining other supplied values.",
511 "lf hid brute -w H10301 --field fc --fc 224 --cn 6278\n"
512 "lf hid brute -w H10301 --field cn --fc 21 -d 2000\n"
513 "lf hid brute -v -w H10301 --field cn --fc 21 --cn 200 -d 2000\n"
514 "lf hid brute -v -w H10301 --field fc --fc 21 --cn 200 -d 2000 --up\n"
519 arg_lit0("v", "verbose", "verbose output"),
520 arg_str1("w", "wiegand", "<format>", "see " _YELLOW_("`wiegand list`") " for available formats"),
521 arg_str1(NULL
, "field", "<fc|cn>", "field to bruteforce"),
522 arg_u64_0(NULL
, "fc", "<dec>", "facility code"),
523 arg_u64_0(NULL
, "cn", "<dec>", "card number"),
524 arg_u64_0("i", "issue", "<dec>", "issue level"),
525 arg_u64_0("o", "oem", "<dec>", "OEM code"),
526 arg_u64_0("d", "delay", "<dec>", "delay betweens attempts in ms. (def is 1000)"),
527 arg_lit0(NULL
, "up", "direction to increment field value. (def is both directions)"),
528 arg_lit0(NULL
, "down", "direction to decrement field value. (def is both directions)"),
531 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
533 bool verbose
= arg_get_lit(ctx
, 1);
535 char format
[16] = {0};
537 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)format
, sizeof(format
), &format_len
);
539 int format_idx
= HIDFindCardFormat(format
);
540 if (format_idx
== -1) {
541 PrintAndLogEx(WARNING
, "Unknown format: " _YELLOW_("%s"), format
);
546 wiegand_card_t card_hi
, card_low
;
547 memset(&card_hi
, 0, sizeof(wiegand_card_t
));
551 CLIParamStrToBuf(arg_get_str(ctx
, 3), (uint8_t *)field
, sizeof(field
), &field_len
);
553 card_hi
.FacilityCode
= arg_get_u32_def(ctx
, 4, 0);
554 card_hi
.CardNumber
= arg_get_u32_def(ctx
, 5, 0);
555 card_hi
.IssueLevel
= arg_get_u32_def(ctx
, 6, 0);
556 card_hi
.OEM
= arg_get_u32_def(ctx
, 7, 0);
558 uint32_t delay
= arg_get_u32_def(ctx
, 8, 1000);
561 if (arg_get_lit(ctx
, 9) && arg_get_lit(ctx
, 10)) {
563 } else if (arg_get_lit(ctx
, 9)) {
565 } else if (arg_get_lit(ctx
, 10)) {
572 PrintAndLogEx(INFO
, "Wiegand format... %i", format_idx
);
573 PrintAndLogEx(INFO
, "OEM.............. %u", card_hi
.OEM
);
574 PrintAndLogEx(INFO
, "ISSUE............ %u", card_hi
.IssueLevel
);
575 PrintAndLogEx(INFO
, "Facility code.... %u", card_hi
.FacilityCode
);
576 PrintAndLogEx(INFO
, "Card number...... %" PRIu64
, card_hi
.CardNumber
);
577 PrintAndLogEx(INFO
, "Delay............ " _YELLOW_("%d"), delay
);
578 if (strcmp(field
, "fc") == 0) {
579 PrintAndLogEx(INFO
, "Field............ " _YELLOW_("fc"));
580 } else if (strcmp(field
, "cn") == 0) {
581 PrintAndLogEx(INFO
, "Field............ " _YELLOW_("cn"));
585 PrintAndLogEx(INFO
, "Direction........ " _YELLOW_("both"));
588 PrintAndLogEx(INFO
, "Direction........ " _YELLOW_("up"));
591 PrintAndLogEx(INFO
, "Direction........ " _YELLOW_("down"));
597 PrintAndLogEx(NORMAL
, "");
598 PrintAndLogEx(INFO
, "Started bruteforcing HID Prox reader");
599 PrintAndLogEx(INFO
, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
600 PrintAndLogEx(NORMAL
, "");
601 // copy values to low.
605 bool exitloop
= false;
606 bool fin_hi
, fin_low
;
607 fin_hi
= fin_low
= false;
610 if (g_session
.pm3_present
== false) {
611 PrintAndLogEx(WARNING
, "Device offline\n");
615 if (kbd_enter_pressed()) {
616 PrintAndLogEx(WARNING
, "aborted via keyboard!");
621 if (direction
!= 2 && fin_hi
!= true) {
622 if (sendTry(format_idx
, &card_hi
, delay
, verbose
) != PM3_SUCCESS
) {
625 if (strcmp(field
, "fc") == 0) {
626 if (card_hi
.FacilityCode
< 0xFF) {
627 card_hi
.FacilityCode
++;
631 } else if (strcmp(field
, "cn") == 0) {
632 if (card_hi
.CardNumber
< 0xFFFF) {
633 card_hi
.CardNumber
++;
641 if (direction
!= 1 && fin_low
!= true) {
642 if (sendTry(format_idx
, &card_low
, delay
, verbose
) != PM3_SUCCESS
) {
645 if (strcmp(field
, "fc") == 0) {
646 if (card_low
.FacilityCode
> 0) {
647 card_low
.FacilityCode
--;
651 } else if (strcmp(field
, "cn") == 0) {
652 if (card_low
.CardNumber
> 0) {
653 card_low
.CardNumber
--;
662 if (fin_hi
&& fin_low
) {
676 } while (exitloop
== false);
678 PrintAndLogEx(NORMAL
, "");
679 PrintAndLogEx(INFO
, "Bruteforcing finished");
683 static command_t CommandTable
[] = {
684 {"help", CmdHelp
, AlwaysAvailable
, "this help"},
685 {"demod", CmdHIDDemod
, AlwaysAvailable
, "demodulate HID Prox tag from the GraphBuffer"},
686 {"reader", CmdHIDReader
, IfPm3Lf
, "attempt to read and extract tag data"},
687 {"clone", CmdHIDClone
, IfPm3Lf
, "clone HID tag to T55x7, Q5/T5555 or EM4305/4469"},
688 {"sim", CmdHIDSim
, IfPm3Lf
, "simulate HID tag"},
689 {"brute", CmdHIDBrute
, IfPm3Lf
, "bruteforce facility code or card number against reader"},
690 {"watch", CmdHIDWatch
, IfPm3Lf
, "continuously watch for cards. Reader mode"},
691 {NULL
, NULL
, NULL
, NULL
}
694 static int CmdHelp(const char *Cmd
) {
695 (void)Cmd
; // Cmd is not used so far
696 CmdsHelp(CommandTable
);
700 int CmdLFHID(const char *Cmd
) {
701 clearCommandBuffer();
702 return CmdsParse(CommandTable
, Cmd
);