]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
cmd_fat.c, cmd_sd: use cmd_error()
[z180-stamp.git] / avr / cmd_fat.c
CommitLineData
35edb766 1/*
016251b6 2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
35edb766 3 *
2d914b45 4 * SPDX-License-Identifier: GPL-2.0
35edb766
L
5 */
6
7/*
8 * FAT filesystem commands
9 */
10
8a2b0da0 11#include "cmd_fat.h"
7439a2b5 12#include <ctype.h>
2f53dd65 13
2f53dd65
L
14#include "ff.h"
15#include "z80-if.h"
2d914b45 16#include "eval_arg.h"
4565be9a 17#include "con-utils.h"
2f53dd65 18#include "print-utils.h"
19b9a7d8 19#include "time.h"
2f53dd65 20#include "timer.h"
4f881b02 21#include "debug.h"
8a2b0da0 22#include "env.h"
86ee5f8b 23#include "errnum.h"
66f6a44e
L
24
25#define DEBUG_FA 0 /* set to 1 to debug */
26
27#define debug_fa(fmt, args...) \
28 debug_cond(DEBUG_FA, fmt, ##args)
29
30
016251b6
L
31/* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
32/* TODO: detect_ramsize() should be moved to z80-if.c */
33#define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
7439a2b5
L
34#define BUFFER_SIZE FF_MAX_SS
35#define PATH_MAX CONFIG_SYS_MAX_PATHLEN
36
19b9a7d8 37
016251b6
L
38/*
39 * Multible (fat) partitions per physical drive are not supported,
40 * but we have up to 2 sdcard slots.
41 */
8a2b0da0
L
42FATFS FatFs0;
43FATFS FatFs1;
44
7439a2b5
L
45command_ret_t command_ret;
46char *cmdname;
47
48
016251b6
L
49void setup_fatfs(void)
50{
51 f_mount(&FatFs0, "0:", 0);
52 f_mount(&FatFs1, "1:", 0);
53}
8a2b0da0 54
2f53dd65
L
55DWORD get_fattime (void)
56{
19b9a7d8
L
57 time_t timer;
58 struct tm tm_timer;
59
60 time(&timer);
61 gmtime_r(&timer, &tm_timer);
62
63 return fatfs_time(&tm_timer);
2f53dd65
L
64}
65
66
b15d22a4
L
67static bool check_abort(void)
68{
69 bool ret = ctrlc();
70
71 if (ret)
72 printf_P(PSTR("Abort\n"));
73
74 return ret;
75}
76
77
022330eb 78static const FLASH char * const FLASH rc_strings[] = {
414caa77
L
79 FSTR("Success"),
80 FSTR("Disk error"),
81 FSTR("Internal error"),
82 FSTR("Not ready"),
83 FSTR("No file"),
84 FSTR("No path"),
85 FSTR("Invalid name"),
86 FSTR("Denied"),
87 FSTR("Exist"),
88 FSTR("Invalid object"),
89 FSTR("Write protected"),
90 FSTR("Invalid drive"),
91 FSTR("Not enabled"),
92 FSTR("No file system"),
93 FSTR("Mkfs aborted"),
94 FSTR("Timeout"),
95 FSTR("Locked"),
96 FSTR("Not enough core"),
97 FSTR("Too many open files"),
98 FSTR("Invalid parameter")
022330eb
L
99 };
100
2f53dd65 101
b15d22a4 102
414caa77 103const FLASH char * fat_rctostr(FRESULT rc)
7439a2b5 104{
022330eb 105 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
7439a2b5
L
106}
107
b15d22a4
L
108static void swirl(void)
109{
6204987c 110 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
111 static uint_fast8_t cnt;
112 static uint32_t tstamp;
113
114 if (get_timer(0) > tstamp) {
6204987c 115 tstamp = get_timer(0) + 250;
81a43faf
L
116 putchar('\b');
117 cnt = (cnt+1) & 3;
118 putchar(swirlchar[cnt]);
b15d22a4
L
119 }
120}
121
7439a2b5
L
122typedef struct {
123 char *p_end; /* pointer to NULL at end of path */
124 char p_path[PATH_MAX + 1]; /* pointer to the start of a path */
125} PATH_T;
126
127
128static char *path_skip_heading(char *p)
129{
130 if (isdigit(p[0]) && p[1] == ':') {
131 p += 2;
132 } else {
133 char *q = p;
134 if (*q++ == '.') {
135 if (*q == '.')
136 ++q;
137 if (*q == '\0' || *q == '/')
138 p = q;
139 }
140 return p;
141 }
142 if (*p == '/')
143 ++p;
144
145 return p;
146}
147
148static void strip_trailing_slash(PATH_T *p)
149{
150 char *beg = path_skip_heading(p->p_path);
151 char *end = p->p_end;
152
153 while (end > beg && end[-1] == '/')
154 *--end = '\0';
155
156 p->p_end =end;
157}
158
159/*
160 * Move specified string into path. Convert "" to "." to handle BSD
161 * semantics for a null path. Strip trailing slashes.
162 */
163static PATH_T *path_setup(char *string)
164{
165 if (strlen(string) > PATH_MAX) {
414caa77 166 cmd_error(0, 0, PSTR("'%s': Name too long"), string);
7439a2b5
L
167 return NULL;
168 }
169
170 PATH_T *p = (PATH_T *) malloc(sizeof *p);
171 if (p == NULL) {
86ee5f8b 172 cmd_error(0, ENOMEM, NULL);
7439a2b5
L
173 return NULL;
174 }
175
176 strcpy(p->p_path, string);
177 size_t len = strlen(string);
178 if (len > 1 && p->p_path[1] == ':' && p->p_path[2] != '/') {
179 if (len < PATH_MAX) {
180 memmove(p->p_path+3, p->p_path+2, len-1);
181 p->p_path[2] = '/';
182 len += 1;
183 } else {
86ee5f8b 184 cmd_error(0, ENOMEM, NULL);
7439a2b5
L
185 return NULL;
186 }
187 }
188
189 p->p_end = p->p_path + len;
190 if (p->p_path == p->p_end) {
191 *p->p_end++ = '.';
192 *p->p_end = '\0';
193 }
194
195 strip_trailing_slash(p);
196 return p;
197}
198
8a2b0da0
L
199/*
200 * pwd - Print current directory of the current drive.
201 *
202 */
016251b6 203command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0
L
204{
205 FRESULT res;
206 char *buf;
207
7439a2b5 208 buf = (char *) malloc(PATH_MAX);
86ee5f8b
L
209 if (buf == NULL)
210 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
7439a2b5
L
211 *buf = '\0';
212 res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
8a2b0da0 213
6bc0153d 214 debug_fa("### f_getcwd(): buf: '%s', res: %d\n", buf, res);
8a2b0da0 215
6bc0153d 216 if (res == FR_OK) {
8a2b0da0
L
217 puts(buf);
218 }
219 free(buf);
86ee5f8b 220 if (res != FR_OK)
414caa77 221 cmd_error(CMD_RET_FAILURE, res, NULL);
86ee5f8b 222
8a2b0da0
L
223 return CMD_RET_SUCCESS;
224}
225
226
227/*
228 * cd - Change the current/working directory.
229 *
230 */
016251b6 231command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
8a2b0da0
L
232{
233 char *arg;
7439a2b5
L
234 FRESULT res = FR_OK;
235
8a2b0da0 236 if (argc < 2) {
b5251896 237 arg = getenv_str(PSTR(ENV_HOME));
8a2b0da0 238 if (arg == NULL) {
86ee5f8b 239 cmd_error(CMD_RET_FAILURE, 0, PSTR("\"%S\" is not set"), PSTR(ENV_HOME));
8a2b0da0
L
240 }
241 } else
242 arg = argv[1];
243
7439a2b5
L
244 PATH_T *path = path_setup(arg);
245 if (path == NULL)
246 return CMD_RET_FAILURE;
247
248 if (strlen(path->p_path) > 1 && path->p_path[1] == ':') {
249 res = f_chdrive(path->p_path);
250 debug_fa("### f_chdrive(): p_path: '%s', res: %d\n", path->p_path, res);
6bc0153d
L
251 }
252 if (res == FR_OK) {
7439a2b5
L
253 res = f_chdir(path->p_path);
254 debug_fa("### f_chdir(): p_path: '%s', res: %d\n", path->p_path, res);
8a2b0da0 255 }
7439a2b5 256 free(path);
86ee5f8b
L
257 if (res != FR_OK)
258 cmd_error(CMD_RET_FAILURE, res, NULL);
259
8a2b0da0
L
260 return CMD_RET_SUCCESS;
261}
262
2f53dd65
L
263
264/*
016251b6 265 * ls path - Directory listing
2f53dd65
L
266 *
267 */
016251b6 268command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 269{
bbd45c46 270 FATFS *fs;
2f53dd65
L
271 DIR Dir; /* Directory object */
272 FILINFO Finfo;
273 unsigned long p1;
274 unsigned int s1, s2;
8a2b0da0
L
275 FRESULT res = FR_OK;
276 char *buf;
2f53dd65 277
2f53dd65 278
7439a2b5 279 buf = (char *) malloc(PATH_MAX);
86ee5f8b
L
280 if (buf == NULL)
281 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
8a2b0da0
L
282
283 if (argc < 2)
7439a2b5 284 res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
8a2b0da0 285 else
7439a2b5 286 strncpy(buf, argv[1], PATH_MAX);
8a2b0da0
L
287
288 if (res == FR_OK)
289 res = f_opendir(&Dir, buf);
290 if (res != FR_OK) {
291 free(buf);
86ee5f8b 292 cmd_error(CMD_RET_FAILURE, res, NULL);
2f53dd65
L
293 }
294
295 p1 = s1 = s2 = 0;
296 for(;;) {
297 res = f_readdir(&Dir, &Finfo);
298 if ((res != FR_OK) || !Finfo.fname[0])
299 break;
300 if (Finfo.fattrib & AM_DIR) {
301 s2++;
302 } else {
303 s1++; p1 += Finfo.fsize;
304 }
7af9364e 305 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
2f53dd65
L
306 (Finfo.fattrib & AM_DIR) ? 'D' : '-',
307 (Finfo.fattrib & AM_RDO) ? 'R' : '-',
308 (Finfo.fattrib & AM_HID) ? 'H' : '-',
309 (Finfo.fattrib & AM_SYS) ? 'S' : '-',
310 (Finfo.fattrib & AM_ARC) ? 'A' : '-',
311 (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
7af9364e
L
312 (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
313 Finfo.fsize, Finfo.fname);
b15d22a4
L
314 if (check_abort())
315 break;
2f53dd65
L
316 }
317
318 if (res == FR_OK) {
319 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
8a2b0da0 320 if (f_getfree(buf, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
321 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
322 }
323
8a2b0da0 324 free(buf);
86ee5f8b
L
325 if (res)
326 cmd_error(CMD_RET_FAILURE, res, NULL);
2f53dd65
L
327
328 return CMD_RET_SUCCESS;
329}
330
016251b6 331#if 0
35edb766
L
332static
333FRESULT mkpath(TCHAR *path)
334{
335 /* TODO: */
336 (void) path;
35edb766
L
337 FILINFO fd
338 TCHAR *p, *q;
339 FRESULT ret;
340
35edb766
L
341 res = f_stat (path, &fd)
342
343 p = strchr(path, ':');
344 if (p == NULL || *++p == '\0' || *p++ != '/')
345 return FR_OK;
346
347 while ((q = strchr(p, '/')) != NULL) {
348 *q = '\0';
349 ret = f_mkdir(path);
350 *q = '/';
351 if (ret != FR_OK && ret != FR_EXIST)
352 return ret;
353 p = q + 1;
354 }
35edb766
L
355
356 return FR_OK;
357}
016251b6
L
358#endif
359
360/* Work register for fs command */
361struct stat_dat_s {
362 DWORD AccSize;
363 WORD AccFiles, AccDirs;
364 FILINFO Finfo;
365};
366
367static
368FRESULT scan_files (
369 char *path, /* Pointer to the working buffer with start path */
370 struct stat_dat_s *statp
371)
372{
373 DIR dirs;
374 FRESULT res;
375 int i;
376 char *fn;
377
378 res = f_opendir(&dirs, path);
379 swirl();
380 if (res == FR_OK) {
381 i = strlen(path);
382 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
383 statp->Finfo.fname[0]) {
384 if (FF_FS_RPATH && statp->Finfo.fname[0] == '.')
385 continue;
386 fn = statp->Finfo.fname;
387 if (statp->Finfo.fattrib & AM_DIR) {
388 statp->AccDirs++;
389 path[i] = '/';
390 strcpy(path+i+1, fn);
391 res = scan_files(path, statp);
392 path[i] = '\0';
393 if (res != FR_OK)
394 break;
395 } else {
396 //printf_P(PSTR("%s/%s\n"), path, fn);
397 statp->AccFiles++;
398 statp->AccSize += statp->Finfo.fsize;
399 }
400 if (check_abort()) {
401 res = 255;
402 break;
403 }
404 }
405 }
406
407 return res;
408}
409
410
411/*
412 * fatstat path - Show logical drive status
413 *
414 */
415command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
416{
417 FATFS *fs;
418 DWORD nfreeclst;
419 FRESULT res;
420 char *buf;
421 char *path = "";
422 struct stat_dat_s statp;
423
7439a2b5 424 buf = (char *) malloc(PATH_MAX);
86ee5f8b
L
425 if (buf == NULL)
426 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
016251b6
L
427
428 if (argc > 1)
429 path = argv[1];
430 res = f_getfree(path, &nfreeclst, &fs);
431 if (!res) {
432 printf_P(PSTR(
433 "FAT type: %u\n"
434 "Bytes/Cluster: %lu\n"
435 "Number of FATs: %u\n"
436 "Root DIR entries: %u\n"
437 "Sectors/FAT: %lu\n"
438 "Number of clusters: %lu\n"
439 "FAT start (lba): %lu\n"
440 "DIR start (lba,cluster): %lu\n"
441 "Data start (lba): %lu\n"),
442 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
443 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
444 fs->fatbase, fs->dirbase, fs->database);
445
177aa6a6 446#if FF_USE_LABEL
016251b6
L
447 DWORD serial;
448 res = f_getlabel(path, buf, &serial);
449 if (!res) {
450 printf_P(PSTR(
451 "Volume name: %s\n"
452 "Volume S/N: %04X-%04X\n"),
453 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
454 }
455#endif
456 if (!res) {
016251b6
L
457 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
458 strcpy(buf, path);
459
81a43faf 460 my_puts_P(PSTR("\nCounting... "));
016251b6 461 res = scan_files(buf, &statp);
81a43faf 462 putchar('\r');
016251b6
L
463 }
464 if (!res) {
81a43faf 465 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
016251b6
L
466 "%lu KB total disk space.\n%lu KB available.\n"),
467 statp.AccFiles, statp.AccSize, statp.AccDirs,
468 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
469 );
470 }
471 }
472
473 free(buf);
86ee5f8b
L
474 if (res)
475 cmd_error(CMD_RET_FAILURE, res, NULL);
476
016251b6
L
477 return CMD_RET_SUCCESS;
478}
2f53dd65 479
4565be9a
L
480/*
481 * fatread/write - load binary file to/from a dos filesystem
482 * read <d:/path/filename> <addr> [bytes [pos]]
483 * write <d:/path/filename> <addr> <bytes>
484 */
016251b6 485command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 486{
4565be9a
L
487 FIL File;
488 uint32_t addr;
489 unsigned long bytes;
490 unsigned long pos;
491 unsigned long bytes_rw;
492
fcf1d5b3 493 bool dowrite = (argv[0][0] == 'w');
3b841cea 494 FRESULT res = FR_OK;
4565be9a
L
495 bool buserr = 0;
496 uint32_t timer;
19b9a7d8 497 uint8_t *buffer;
4565be9a 498
4565be9a
L
499 if (argc < (dowrite ? 4 : 3))
500 return CMD_RET_USAGE;
501
2d914b45 502 addr = eval_arg(argv[2], NULL);
86ee5f8b
L
503 if (addr >= MAX_MEMORY)
504 cmd_error(CMD_RET_FAILURE, 0, PSTR("Address too high: %#lx"), addr);
505
4565be9a 506 if (argc > 3)
2d914b45 507 bytes = eval_arg(argv[3], NULL);
4565be9a
L
508 else
509 bytes = MAX_MEMORY;
510 if (argc > 4)
2d914b45 511 pos = eval_arg(argv[4], NULL);
4565be9a
L
512 else
513 pos = 0;
514
515 if (addr + bytes > MAX_MEMORY)
516 bytes = MAX_MEMORY - addr;
517
19b9a7d8 518 buffer = (uint8_t *) malloc(BUFFER_SIZE);
86ee5f8b
L
519 if (buffer == NULL)
520 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
19b9a7d8 521
86ee5f8b
L
522 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
523 : FA_READ );
4565be9a 524
86ee5f8b
L
525 if (!res) {
526 res = f_lseek(&File, pos);
4565be9a 527 if (!res) {
86ee5f8b
L
528 bytes_rw = 0;
529 timer = get_timer(0);
530 while (bytes) {
531 unsigned int cnt, br;
532
533 if (bytes >= BUFFER_SIZE) {
534 cnt = BUFFER_SIZE;
535 bytes -= BUFFER_SIZE;
536 } else {
537 cnt = bytes; bytes = 0;
538 }
539 if (dowrite) {
540 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
541 buserr = 1;
4565be9a
L
542 break;
543 }
86ee5f8b
L
544 z80_read_block(buffer, addr, cnt);
545 z80_bus_cmd(Release);
546 res = f_write(&File, buffer, cnt, &br);
547 if (res != FR_OK)
548 break;
549 } else {
550 res = f_read(&File, buffer, cnt, &br);
551 if (res != FR_OK)
552 break;
553 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
554 buserr = 1;
4565be9a 555 break;
86ee5f8b
L
556 }
557 z80_write_block(buffer, addr, br);
558 z80_bus_cmd(Release);
4565be9a 559 }
86ee5f8b 560 addr += br;
4565be9a 561
86ee5f8b
L
562 bytes_rw += br;
563 if (cnt != br) {
564 if (dowrite)
565 printf_P(PSTR("Disk full?\n"));
566 break;
567 }
568 if (check_abort())
569 break;
4565be9a 570 }
86ee5f8b
L
571
572 FRESULT fr = f_close(&File);
573 if (!res)
574 res = fr;
575 timer = get_timer(timer);
576 printf_P(PSTR("%lu (%#lx) bytes read/written with %lu bytes/sec.\n"),
577 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
4565be9a 578 }
4565be9a
L
579 }
580
19b9a7d8 581 free(buffer);
19b9a7d8 582
4565be9a 583 if (res)
86ee5f8b 584 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
4565be9a 585 if (buserr || res)
86ee5f8b 586 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
4565be9a
L
587
588 return CMD_RET_SUCCESS;
589}
8a2b0da0 590
016251b6
L
591/*
592 * command table for fat subcommands
593 */
8a2b0da0 594
8da60ec5 595cmd_tbl_t cmd_tbl_fat[] = {
8a2b0da0 596CMD_TBL_ITEM(
d530fed0 597 status, 2, CTBL_RPT, do_stat,
8a2b0da0
L
598 "Show logical drive status",
599 "dev"
600),
601CMD_TBL_ITEM(
04f84937 602 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
8a2b0da0
L
603 "Print name of current/working directory",
604 ""
605),
606CMD_TBL_ITEM(
04f84937 607 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
8a2b0da0
L
608 "Change the current/working directory.",
609 "path"
610),
611CMD_TBL_ITEM(
04f84937 612 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
8a2b0da0
L
613 "Directory listing",
614 "path"
615),
616CMD_TBL_ITEM(
617 load, 5, 0, do_rw,
618 "load binary file from a dos filesystem",
619 "<d:/path/filename> <addr> [bytes [pos]]\n"
620 " - Load binary file 'path/filename' on logical drive 'd'\n"
621 " to address 'addr' from dos filesystem.\n"
622 " 'pos' gives the file position to start loading from.\n"
623 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
624 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
625 " the load stops on end of file."
626),
627CMD_TBL_ITEM(
7a1ed620 628 write, 4, 0, do_rw,
8a2b0da0
L
629 "write file into a dos filesystem",
630 "<d:/path/filename> <addr> <bytes>\n"
631 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
632 " starting at address 'addr'.\n"
633),
634
b5251896 635CMD_TBL_ITEM(
7a1ed620 636 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
b5251896
L
637 "Print sub command description/usage",
638 "\n"
639 " - print brief description of all sub commands\n"
640 "fat help command ...\n"
641 " - print detailed usage of sub cmd 'command'"
642),
643
8a2b0da0
L
644/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
645 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
04f84937 646 NULL,
8a2b0da0
L
647#ifdef CONFIG_SYS_LONGHELP
648 FSTR(""),
649#endif /* CONFIG_SYS_LONGHELP */
8da60ec5 650 NULL,
8a2b0da0 651#ifdef CONFIG_AUTO_COMPLETE
8da60ec5 652 NULL,
8a2b0da0
L
653#endif
654},
8da60ec5 655/* Mark end of table */
5caa8c2b 656CMD_TBL_END(cmd_tbl_fat)
8a2b0da0
L
657};
658
8a2b0da0 659
016251b6 660command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0 661{
5caa8c2b 662 puts_P(PSTR("Huch?"));
8a2b0da0
L
663
664 return CMD_RET_USAGE;
665}