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