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