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