CHG: minor code clean up
[proxmark3-svn] / client / cmdhf14a.c
CommitLineData
a553f267 1//-----------------------------------------------------------------------------
f89c7050 2// 2011, Merlok
534983d7 3// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch
a553f267 4//
5// This code is licensed to you under the terms of the GNU GPL, version 2 or,
6// at your option, any later version. See the LICENSE.txt file for the text of
7// the license.
8//-----------------------------------------------------------------------------
9// High frequency ISO14443A commands
10//-----------------------------------------------------------------------------
11
7fe9b0b7 12#include <stdio.h>
590f8ff9 13#include <stdlib.h>
7fe9b0b7 14#include <string.h>
f397b5cc 15#include <unistd.h>
20f9a2a1 16#include "util.h"
7fe9b0b7 17#include "iso14443crc.h"
18#include "data.h"
902cb3c0 19#include "proxmark3.h"
7fe9b0b7 20#include "ui.h"
21#include "cmdparser.h"
22#include "cmdhf14a.h"
534983d7 23#include "common.h"
20f9a2a1 24#include "cmdmain.h"
902cb3c0 25#include "mifare.h"
7fe9b0b7 26
27static int CmdHelp(const char *Cmd);
5f6d6c90 28static void waitCmd(uint8_t iLen);
7fe9b0b7 29
30int CmdHF14AList(const char *Cmd)
31{
4c3de57a 32 PrintAndLog("Deprecated command, use 'hf list 14a' instead");
f89c7050 33 return 0;
7fe9b0b7 34}
35
534983d7 36void iso14a_set_timeout(uint32_t timeout) {
37 UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}};
38 SendCommand(&c);
39}
40
7fe9b0b7 41int CmdHF14AReader(const char *Cmd)
42{
19d6d91f 43 UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}};
534983d7 44 SendCommand(&c);
902cb3c0 45
46 UsbCommand resp;
7bc95e2e 47 WaitForResponse(CMD_ACK,&resp);
902cb3c0 48
19d6d91f 49 iso14a_card_select_t card;
50 memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t));
534983d7 51
9a573554 52 uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS
19d6d91f 53
54 if(select_status == 0) {
534983d7 55 PrintAndLog("iso14443a card select failed");
52bfb955 56 // disconnect
57 c.arg[0] = 0;
58 c.arg[1] = 0;
59 c.arg[2] = 0;
60 SendCommand(&c);
534983d7 61 return 0;
62 }
63
19d6d91f 64 PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]);
65 PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen));
66 PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]);
713e7ffb 67
19d6d91f 68 switch (card.sak) {
79a73ab2 69 case 0x00: PrintAndLog("TYPE : NXP MIFARE Ultralight | Ultralight C"); break;
3fe4ff4f 70 case 0x01: PrintAndLog("TYPE : NXP TNP3xxx Activision Game Appliance"); break;
79a73ab2 71 case 0x04: PrintAndLog("TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); break;
e691fc45 72 case 0x08: PrintAndLog("TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1"); break;
79a73ab2 73 case 0x09: PrintAndLog("TYPE : NXP MIFARE Mini 0.3k"); break;
e691fc45 74 case 0x10: PrintAndLog("TYPE : NXP MIFARE Plus 2k SL2"); break;
75 case 0x11: PrintAndLog("TYPE : NXP MIFARE Plus 4k SL2"); break;
76 case 0x18: PrintAndLog("TYPE : NXP MIFARE Classic 4k | Plus 4k SL1"); break;
77 case 0x20: PrintAndLog("TYPE : NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41"); break;
79a73ab2 78 case 0x24: PrintAndLog("TYPE : NXP MIFARE DESFire | DESFire EV1"); break;
79 case 0x28: PrintAndLog("TYPE : JCOP31 or JCOP41 v2.3.1"); break;
80 case 0x38: PrintAndLog("TYPE : Nokia 6212 or 6131 MIFARE CLASSIC 4K"); break;
81 case 0x88: PrintAndLog("TYPE : Infineon MIFARE CLASSIC 1K"); break;
82 case 0x98: PrintAndLog("TYPE : Gemplus MPCOS"); break;
9ca155ba
M
83 default: ;
84 }
19d6d91f 85
86
87 // try to request ATS even if tag claims not to support it
88 if (select_status == 2) {
89 uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
90 c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT;
91 c.arg[1] = 2;
92 c.arg[2] = 0;
93 memcpy(c.d.asBytes, rats, 2);
94 SendCommand(&c);
95 WaitForResponse(CMD_ACK,&resp);
96
97 memcpy(&card.ats, resp.d.asBytes, resp.arg[0]);
9a573554 98 card.ats_len = resp.arg[0]; // note: ats_len includes CRC Bytes
19d6d91f 99 }
100
101 // disconnect
102 c.arg[0] = 0;
103 c.arg[1] = 0;
104 c.arg[2] = 0;
105 SendCommand(&c);
106
107
9a573554 108 if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
561f7c11 109 bool ta1 = 0, tb1 = 0, tc1 = 0;
110 int pos;
111
9a573554 112 if (select_status == 2) {
19d6d91f 113 PrintAndLog("SAK incorrectly claims that card doesn't support RATS");
114 }
115 PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len));
9a573554 116 PrintAndLog(" - TL : length is %d bytes", card.ats[0]);
117 if (card.ats[0] != card.ats_len - 2) {
118 PrintAndLog("ATS may be corrupted. Length of ATS (%d bytes incl. 2 Bytes CRC) doesn't match TL", card.ats_len);
561f7c11 119 }
9a573554 120
121 if (card.ats[0] > 1) { // there is a format byte (T0)
19d6d91f 122 ta1 = (card.ats[1] & 0x10) == 0x10;
123 tb1 = (card.ats[1] & 0x20) == 0x20;
124 tc1 = (card.ats[1] & 0x40) == 0x40;
9a573554 125 int16_t fsci = card.ats[1] & 0x0f;
561f7c11 126 PrintAndLog(" - T0 : TA1 is%s present, TB1 is%s present, "
9a573554 127 "TC1 is%s present, FSCI is %d (FSC = %ld)",
561f7c11 128 (ta1 ? "" : " NOT"), (tb1 ? "" : " NOT"), (tc1 ? "" : " NOT"),
9a573554 129 fsci,
130 fsci < 5 ? (fsci - 2) * 8 :
131 fsci < 8 ? (fsci - 3) * 32 :
132 fsci == 8 ? 256 :
133 -1
134 );
561f7c11 135 }
136 pos = 2;
9a573554 137 if (ta1) {
561f7c11 138 char dr[16], ds[16];
139 dr[0] = ds[0] = '\0';
19d6d91f 140 if (card.ats[pos] & 0x10) strcat(ds, "2, ");
141 if (card.ats[pos] & 0x20) strcat(ds, "4, ");
142 if (card.ats[pos] & 0x40) strcat(ds, "8, ");
143 if (card.ats[pos] & 0x01) strcat(dr, "2, ");
144 if (card.ats[pos] & 0x02) strcat(dr, "4, ");
145 if (card.ats[pos] & 0x04) strcat(dr, "8, ");
561f7c11 146 if (strlen(ds) != 0) ds[strlen(ds) - 2] = '\0';
147 if (strlen(dr) != 0) dr[strlen(dr) - 2] = '\0';
148 PrintAndLog(" - TA1 : different divisors are%s supported, "
149 "DR: [%s], DS: [%s]",
19d6d91f 150 (card.ats[pos] & 0x80 ? " NOT" : ""), dr, ds);
561f7c11 151 pos++;
152 }
9a573554 153 if (tb1) {
154 uint32_t sfgi = card.ats[pos] & 0x0F;
155 uint32_t fwi = card.ats[pos] >> 4;
156 PrintAndLog(" - TB1 : SFGI = %d (SFGT = %s%ld/fc), FWI = %d (FWT = %ld/fc)",
157 (sfgi),
158 sfgi ? "" : "(not needed) ",
159 sfgi ? (1 << 12) << sfgi : 0,
160 fwi,
161 (1 << 12) << fwi
162 );
561f7c11 163 pos++;
164 }
9a573554 165 if (tc1) {
561f7c11 166 PrintAndLog(" - TC1 : NAD is%s supported, CID is%s supported",
19d6d91f 167 (card.ats[pos] & 0x01) ? "" : " NOT",
168 (card.ats[pos] & 0x02) ? "" : " NOT");
561f7c11 169 pos++;
170 }
9a573554 171 if (card.ats[0] > pos) {
561f7c11 172 char *tip = "";
9a573554 173 if (card.ats[0] - pos >= 7) {
19d6d91f 174 if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) {
561f7c11 175 tip = "-> MIFARE Plus X 2K or 4K";
19d6d91f 176 } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) {
561f7c11 177 tip = "-> MIFARE Plus S 2K or 4K";
178 }
179 }
9a573554 180 PrintAndLog(" - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip);
19d6d91f 181 if (card.ats[pos] == 0xC1) {
561f7c11 182 PrintAndLog(" c1 -> Mifare or (multiple) virtual cards of various type");
183 PrintAndLog(" %02x -> Length is %d bytes",
19d6d91f 184 card.ats[pos + 1], card.ats[pos + 1]);
185 switch (card.ats[pos + 2] & 0xf0) {
561f7c11 186 case 0x10:
187 PrintAndLog(" 1x -> MIFARE DESFire");
188 break;
189 case 0x20:
190 PrintAndLog(" 2x -> MIFARE Plus");
191 break;
192 }
19d6d91f 193 switch (card.ats[pos + 2] & 0x0f) {
561f7c11 194 case 0x00:
195 PrintAndLog(" x0 -> <1 kByte");
196 break;
197 case 0x01:
198 PrintAndLog(" x0 -> 1 kByte");
199 break;
200 case 0x02:
201 PrintAndLog(" x0 -> 2 kByte");
202 break;
203 case 0x03:
204 PrintAndLog(" x0 -> 4 kByte");
205 break;
206 case 0x04:
207 PrintAndLog(" x0 -> 8 kByte");
208 break;
209 }
19d6d91f 210 switch (card.ats[pos + 3] & 0xf0) {
561f7c11 211 case 0x00:
212 PrintAndLog(" 0x -> Engineering sample");
213 break;
214 case 0x20:
215 PrintAndLog(" 2x -> Released");
216 break;
217 }
19d6d91f 218 switch (card.ats[pos + 3] & 0x0f) {
561f7c11 219 case 0x00:
220 PrintAndLog(" x0 -> Generation 1");
221 break;
222 case 0x01:
223 PrintAndLog(" x1 -> Generation 2");
224 break;
225 case 0x02:
226 PrintAndLog(" x2 -> Generation 3");
227 break;
228 }
19d6d91f 229 switch (card.ats[pos + 4] & 0x0f) {
561f7c11 230 case 0x00:
231 PrintAndLog(" x0 -> Only VCSL supported");
232 break;
233 case 0x01:
234 PrintAndLog(" x1 -> VCS, VCSL, and SVC supported");
235 break;
236 case 0x0E:
237 PrintAndLog(" xE -> no VCS command supported");
238 break;
239 }
240 }
241 }
79a73ab2 242 } else {
19d6d91f 243 PrintAndLog("proprietary non iso14443-4 card found, RATS not supported");
244 }
534983d7 245
19d6d91f 246 return select_status;
7fe9b0b7 247}
248
db22dfe6 249// Collect ISO14443 Type A UIDs
250int CmdHF14ACUIDs(const char *Cmd)
251{
252 // requested number of UIDs
253 int n = atoi(Cmd);
254 // collect at least 1 (e.g. if no parameter was given)
255 n = n > 0 ? n : 1;
256
257 PrintAndLog("Collecting %d UIDs", n);
258 PrintAndLog("Start: %u", time(NULL));
259 // repeat n times
260 for (int i = 0; i < n; i++) {
261 // execute anticollision procedure
262 UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}};
263 SendCommand(&c);
902cb3c0 264
72b1090a 265 UsbCommand resp;
266 WaitForResponse(CMD_ACK,&resp);
902cb3c0 267
72b1090a 268 iso14a_card_select_t *card = (iso14a_card_select_t *) resp.d.asBytes;
db22dfe6 269
270 // check if command failed
902cb3c0 271 if (resp.arg[0] == 0) {
db22dfe6 272 PrintAndLog("Card select failed.");
273 } else {
72b1090a 274 char uid_string[20];
275 for (uint16_t i = 0; i < card->uidlen; i++) {
276 sprintf(&uid_string[2*i], "%02X", card->uid[i]);
db22dfe6 277 }
72b1090a 278 PrintAndLog("%s", uid_string);
db22dfe6 279 }
280 }
281 PrintAndLog("End: %u", time(NULL));
282
283 return 1;
284}
285
7fe9b0b7 286// ## simulate iso14443a tag
287// ## greg - added ability to specify tag UID
288int CmdHF14ASim(const char *Cmd)
81cd0474 289{
290 UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,{0,0,0}};
291
292 // Retrieve the tag type
293 uint8_t tagtype = param_get8ex(Cmd,0,0,10);
294
295 // When no argument was given, just print help message
296 if (tagtype == 0) {
297 PrintAndLog("");
298 PrintAndLog(" Emulating ISO/IEC 14443 type A tag with 4 or 7 byte UID");
299 PrintAndLog("");
300 PrintAndLog(" syntax: hf 14a sim <type> <uid>");
301 PrintAndLog(" types: 1 = MIFARE Classic");
302 PrintAndLog(" 2 = MIFARE Ultralight");
303 PrintAndLog(" 3 = MIFARE DESFIRE");
304 PrintAndLog(" 4 = ISO/IEC 14443-4");
3fe4ff4f 305 PrintAndLog(" 5 = MIFARE TNP3XXX");
81cd0474 306 PrintAndLog("");
307 return 1;
308 }
309
310 // Store the tag type
311 c.arg[0] = tagtype;
312
313 // Retrieve the full 4 or 7 byte long uid
314 uint64_t long_uid = param_get64ex(Cmd,1,0,16);
315
316 // Are we handling the (optional) second part uid?
317 if (long_uid > 0xffffffff) {
125a98a1 318 PrintAndLog("Emulating ISO/IEC 14443 type A tag with 7 byte UID (%014"llx")",long_uid);
81cd0474 319 // Store the second part
320 c.arg[2] = (long_uid & 0xffffffff);
321 long_uid >>= 32;
322 // Store the first part, ignore the first byte, it is replaced by cascade byte (0x88)
323 c.arg[1] = (long_uid & 0xffffff);
324 } else {
325 PrintAndLog("Emulating ISO/IEC 14443 type A tag with 4 byte UID (%08x)",long_uid);
326 // Only store the first part
327 c.arg[1] = long_uid & 0xffffffff;
328 }
329/*
330 // At lease save the mandatory first part of the UID
331 c.arg[0] = long_uid & 0xffffffff;
332
81cd0474 333 if (c.arg[1] == 0) {
334 PrintAndLog("Emulating ISO/IEC 14443 type A tag with UID %01d %08x %08x",c.arg[0],c.arg[1],c.arg[2]);
335 }
336
337 switch (c.arg[0]) {
338 case 1: {
339 PrintAndLog("Emulating ISO/IEC 14443-3 type A tag with 4 byte UID");
340 UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,param_get32ex(Cmd,0,0,10),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16)};
341 } break;
342 case 2: {
343 PrintAndLog("Emulating ISO/IEC 14443-4 type A tag with 7 byte UID");
344 } break;
345 default: {
346 PrintAndLog("Error: unkown tag type (%d)",c.arg[0]);
347 PrintAndLog("syntax: hf 14a sim <uid>",c.arg[0]);
348 PrintAndLog(" type1: 4 ",c.arg[0]);
349
350 return 1;
351 } break;
352 }
353*/
354/*
7fe9b0b7 355 unsigned int hi = 0, lo = 0;
356 int n = 0, i = 0;
357 while (sscanf(&Cmd[i++], "%1x", &n ) == 1) {
358 hi= (hi << 4) | (lo >> 28);
359 lo= (lo << 4) | (n & 0xf);
360 }
81cd0474 361*/
362// UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,param_get32ex(Cmd,0,0,10),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16)};
363// PrintAndLog("Emulating ISO/IEC 14443 type A tag with UID %01d %08x %08x",c.arg[0],c.arg[1],c.arg[2]);
7fe9b0b7 364 SendCommand(&c);
365 return 0;
366}
367
5cd9ec01
M
368int CmdHF14ASnoop(const char *Cmd) {
369 int param = 0;
370
371 if (param_getchar(Cmd, 0) == 'h') {
372 PrintAndLog("It get data from the field and saves it into command buffer.");
4c3de57a 373 PrintAndLog("Buffer accessible from command hf list 14a.");
5cd9ec01
M
374 PrintAndLog("Usage: hf 14a snoop [c][r]");
375 PrintAndLog("c - triggered by first data from card");
376 PrintAndLog("r - triggered by first 7-bit request from reader (REQ,WUP,...)");
377 PrintAndLog("sample: hf 14a snoop c r");
378 return 0;
379 }
380
381 for (int i = 0; i < 2; i++) {
382 char ctmp = param_getchar(Cmd, i);
383 if (ctmp == 'c' || ctmp == 'C') param |= 0x01;
384 if (ctmp == 'r' || ctmp == 'R') param |= 0x02;
385 }
386
387 UsbCommand c = {CMD_SNOOP_ISO_14443a, {param, 0, 0}};
7fe9b0b7 388 SendCommand(&c);
389 return 0;
390}
391
5f6d6c90 392int CmdHF14ACmdRaw(const char *cmd) {
393 UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}};
394 uint8_t reply=1;
395 uint8_t crc=0;
396 uint8_t power=0;
397 uint8_t active=0;
398 uint8_t active_select=0;
399 uint16_t numbits=0;
400 char buf[5]="";
401 int i=0;
402 uint8_t data[100];
403 unsigned int datalen=0, temp;
404
405 if (strlen(cmd)<2) {
406 PrintAndLog("Usage: hf 14a raw [-r] [-c] [-p] [-f] [-b] <number of bits> <0A 0B 0C ... hex>");
407 PrintAndLog(" -r do not read response");
408 PrintAndLog(" -c calculate and append CRC");
409 PrintAndLog(" -p leave the signal field ON after receive");
410 PrintAndLog(" -a active signal field ON without select");
411 PrintAndLog(" -s active signal field ON with select");
412 PrintAndLog(" -b number of bits to send. Useful for send partial byte");
413 return 0;
414 }
415
416 // strip
417 while (*cmd==' ' || *cmd=='\t') cmd++;
418
419 while (cmd[i]!='\0') {
420 if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; }
421 if (cmd[i]=='-') {
422 switch (cmd[i+1]) {
423 case 'r':
424 reply=0;
425 break;
426 case 'c':
427 crc=1;
428 break;
429 case 'p':
430 power=1;
431 break;
432 case 'a':
433 active=1;
434 break;
435 case 's':
436 active_select=1;
437 break;
438 case 'b':
439 sscanf(cmd+i+2,"%d",&temp);
440 numbits = temp & 0xFFFF;
441 i+=3;
442 while(cmd[i]!=' ' && cmd[i]!='\0') { i++; }
443 i-=2;
444 break;
445 default:
446 PrintAndLog("Invalid option");
447 return 0;
448 }
449 i+=2;
450 continue;
451 }
452 if ((cmd[i]>='0' && cmd[i]<='9') ||
453 (cmd[i]>='a' && cmd[i]<='f') ||
454 (cmd[i]>='A' && cmd[i]<='F') ) {
455 buf[strlen(buf)+1]=0;
456 buf[strlen(buf)]=cmd[i];
457 i++;
458
459 if (strlen(buf)>=2) {
460 sscanf(buf,"%x",&temp);
461 data[datalen]=(uint8_t)(temp & 0xff);
462 datalen++;
463 *buf=0;
464 }
465 continue;
466 }
467 PrintAndLog("Invalid char on input");
468 return 0;
469 }
470 if(crc && datalen>0)
471 {
472 uint8_t first, second;
473 ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second);
474 data[datalen++] = first;
475 data[datalen++] = second;
476 }
477
478 if(active || active_select)
479 {
480 c.arg[0] |= ISO14A_CONNECT;
481 if(active)
482 c.arg[0] |= ISO14A_NO_SELECT;
483 }
484 if(power)
485 c.arg[0] |= ISO14A_NO_DISCONNECT;
486 if(datalen>0)
487 c.arg[0] |= ISO14A_RAW;
488
489 c.arg[1] = datalen;
490 c.arg[2] = numbits;
491 memcpy(c.d.asBytes,data,datalen);
492
493 SendCommand(&c);
494
495 if (reply) {
496 if(active_select)
497 waitCmd(1);
498 if(datalen>0)
499 waitCmd(0);
500 } // if reply
501 return 0;
502}
503
504static void waitCmd(uint8_t iSelect)
505{
506 uint8_t *recv;
507 UsbCommand resp;
508 char *hexout;
509
510 if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) {
511 recv = resp.d.asBytes;
512 uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0];
513 PrintAndLog("received %i octets",iLen);
514 if(!iLen)
515 return;
516 hexout = (char *)malloc(iLen * 3 + 1);
517 if (hexout != NULL) {
5f6d6c90 518 for (int i = 0; i < iLen; i++) { // data in hex
f66021cf 519 sprintf(&hexout[i * 3], "%02X ", recv[i]);
5f6d6c90 520 }
521 PrintAndLog("%s", hexout);
522 free(hexout);
523 } else {
524 PrintAndLog("malloc failed your client has low memory?");
525 }
526 } else {
527 PrintAndLog("timeout while waiting for reply.");
528 }
529}
530
7fe9b0b7 531static command_t CommandTable[] =
532{
5acd09bd 533 {"help", CmdHelp, 1, "This help"},
4c3de57a 534 {"list", CmdHF14AList, 0, "[Deprecated] List ISO 14443a history"},
5acd09bd 535 {"reader", CmdHF14AReader, 0, "Act like an ISO14443 Type A reader"},
536 {"cuids", CmdHF14ACUIDs, 0, "<n> Collect n>0 ISO14443 Type A UIDs in one go"},
537 {"sim", CmdHF14ASim, 0, "<UID> -- Fake ISO 14443a tag"},
538 {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"},
5f6d6c90 539 {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"},
7fe9b0b7 540 {NULL, NULL, 0, NULL}
541};
542
902cb3c0 543int CmdHF14A(const char *Cmd) {
f397b5cc 544 // flush
902cb3c0 545 WaitForResponseTimeout(CMD_ACK,NULL,100);
f397b5cc
M
546
547 // parse
7fe9b0b7 548 CmdsParse(CommandTable, Cmd);
549 return 0;
550}
551
552int CmdHelp(const char *Cmd)
553{
554 CmdsHelp(CommandTable);
555 return 0;
556}
Impressum, Datenschutz