2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 * FAT filesystem commands
13 uint32_t fat_time(const struct tm
* timeptr
);
18 #include "con-utils.h"
19 #include "print-utils.h"
24 #define DEBUG_FA 0 /* set to 1 to debug */
26 #define debug_fa(fmt, args...) \
27 debug_cond(DEBUG_FA, fmt, ##args)
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
33 #define BUFFER_SIZE FF_MAX_SS
34 #define PATH_MAX CONFIG_SYS_MAX_PATHLEN
38 * Multible (fat) partitions per physical drive are not supported,
39 * but we have up to 2 sdcard slots.
44 command_ret_t command_ret
;
48 void setup_fatfs(void)
50 f_mount(&FatFs0
, "0:", 0);
51 f_mount(&FatFs1
, "1:", 0);
54 DWORD
get_fattime (void)
60 gmtime_r(&timer
, &tm_timer
);
62 return fat_time(&tm_timer
);
66 static bool check_abort(void)
71 printf_P(PSTR("Abort\n"));
77 static const FLASH
char * const FLASH rc_strings
[] = {
80 FSTR("Internal error"),
87 FSTR("Invalid object"),
88 FSTR("Write protected"),
89 FSTR("Invalid drive"),
91 FSTR("No file system"),
95 FSTR("Not enough core"),
96 FSTR("Too many open files"),
97 FSTR("Invalid parameter")
102 const FLASH
char * fat_rctostr(FRESULT rc
)
104 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
107 static void swirl(void)
109 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
110 static uint_fast8_t cnt
;
111 static uint32_t tstamp
;
113 if (get_timer(0) > tstamp
) {
114 tstamp
= get_timer(0) + 250;
117 putchar(swirlchar
[cnt
]);
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 */
127 static char *path_skip_heading(char *p
)
129 if (isdigit(p
[0]) && p
[1] == ':') {
136 if (*q
== '\0' || *q
== '/')
147 static void strip_trailing_slash(PATH_T
*p
)
149 char *beg
= path_skip_heading(p
->p_path
);
150 char *end
= p
->p_end
;
152 while (end
> beg
&& end
[-1] == '/')
159 * Move specified string into path. Convert "" to "." to handle BSD
160 * semantics for a null path. Strip trailing slashes.
162 static PATH_T
*path_setup(char *string
)
164 if (strlen(string
) > PATH_MAX
) {
165 cmd_error(0, 0, PSTR("'%s': Name too long"), string
);
169 PATH_T
*p
= (PATH_T
*) malloc(sizeof *p
);
171 cmd_error(0, ENOMEM
, NULL
);
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);
183 cmd_error(0, ENOMEM
, NULL
);
188 p
->p_end
= p
->p_path
+ len
;
189 if (p
->p_path
== p
->p_end
) {
194 strip_trailing_slash(p
);
199 * pwd - Print current directory of the current drive.
202 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
207 buf
= (char *) malloc(PATH_MAX
);
209 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
211 res
= f_getcwd(buf
, PATH_MAX
); /* Get current directory path */
213 debug_fa("### f_getcwd(): buf: '%s', res: %d\n", buf
, res
);
220 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
222 return CMD_RET_SUCCESS
;
227 * cd - Change the current/working directory.
230 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
236 arg
= getenv_str(PSTR(ENV_HOME
));
238 cmd_error(CMD_RET_FAILURE
, 0, PSTR("\"%S\" is not set"), PSTR(ENV_HOME
));
243 PATH_T
*path
= path_setup(arg
);
245 return CMD_RET_FAILURE
;
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
);
252 res
= f_chdir(path
->p_path
);
253 debug_fa("### f_chdir(): p_path: '%s', res: %d\n", path
->p_path
, res
);
257 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
259 return CMD_RET_SUCCESS
;
264 * ls path - Directory listing
267 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
270 DIR Dir
; /* Directory object */
278 buf
= (char *) malloc(PATH_MAX
);
280 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
283 res
= f_getcwd(buf
, PATH_MAX
); /* Get current directory path */
285 strncpy(buf
, argv
[1], PATH_MAX
);
288 res
= f_opendir(&Dir
, buf
);
291 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
296 res
= f_readdir(&Dir
, &Finfo
);
297 if ((res
!= FR_OK
) || !Finfo
.fname
[0])
299 if (Finfo
.fattrib
& AM_DIR
) {
302 s1
++; p1
+= Finfo
.fsize
;
304 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
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,
311 (Finfo
.ftime
>> 11), (Finfo
.ftime
>> 5) & 63,
312 Finfo
.fsize
, Finfo
.fname
);
318 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
319 if (f_getfree(buf
, (DWORD
*)&p1
, &fs
) == FR_OK
)
320 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
325 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
327 return CMD_RET_SUCCESS
;
332 FRESULT
mkpath(TCHAR
*path
)
340 res
= f_stat (path
, &fd
)
342 p
= strchr(path
, ':');
343 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
346 while ((q
= strchr(p
, '/')) != NULL
) {
350 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
359 /* Work register for fs command */
362 WORD AccFiles
, AccDirs
;
368 char *path
, /* Pointer to the working buffer with start path */
369 struct stat_dat_s
*statp
377 res
= f_opendir(&dirs
, 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] == '.')
385 fn
= statp
->Finfo
.fname
;
386 if (statp
->Finfo
.fattrib
& AM_DIR
) {
389 strcpy(path
+i
+1, fn
);
390 res
= scan_files(path
, statp
);
395 //printf_P(PSTR("%s/%s\n"), path, fn);
397 statp
->AccSize
+= statp
->Finfo
.fsize
;
411 * fatstat path - Show logical drive status
414 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
421 struct stat_dat_s statp
;
423 buf
= (char *) malloc(PATH_MAX
);
425 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
429 res
= f_getfree(path
, &nfreeclst
, &fs
);
433 "Bytes/Cluster: %lu\n"
434 "Number of FATs: %u\n"
435 "Root DIR entries: %u\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
);
447 res
= f_getlabel(path
, buf
, &serial
);
451 "Volume S/N: %04X-%04X\n"),
452 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
456 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
459 my_puts_P(PSTR("\nCounting... "));
460 res
= scan_files(buf
, &statp
);
464 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
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)
474 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
476 return CMD_RET_SUCCESS
;
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>
484 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
490 unsigned long bytes_rw
;
492 bool dowrite
= (argv
[0][0] == 'w');
498 if (argc
< (dowrite
? 4 : 3))
499 return CMD_RET_USAGE
;
501 addr
= eval_arg(argv
[2], NULL
);
502 if (addr
>= MAX_MEMORY
)
503 cmd_error(CMD_RET_FAILURE
, 0, PSTR("Address too high: %#lx"), addr
);
506 bytes
= eval_arg(argv
[3], NULL
);
510 pos
= eval_arg(argv
[4], NULL
);
514 if (addr
+ bytes
> MAX_MEMORY
)
515 bytes
= MAX_MEMORY
- addr
;
517 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
519 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
521 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
525 res
= f_lseek(&File
, pos
);
528 timer
= get_timer(0);
530 unsigned int cnt
, br
;
532 if (bytes
>= BUFFER_SIZE
) {
534 bytes
-= BUFFER_SIZE
;
536 cnt
= bytes
; bytes
= 0;
539 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
543 z80_read_block(buffer
, addr
, cnt
);
544 z80_bus_cmd(Release
);
545 res
= f_write(&File
, buffer
, cnt
, &br
);
549 res
= f_read(&File
, buffer
, cnt
, &br
);
552 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
556 z80_write_block(buffer
, addr
, br
);
557 z80_bus_cmd(Release
);
564 printf_P(PSTR("Disk full?\n"));
571 FRESULT fr
= f_close(&File
);
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);
583 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), argv
[1]);
585 cmd_error(CMD_RET_FAILURE
, EBUSTO
, NULL
);
587 return CMD_RET_SUCCESS
;
591 * command table for fat subcommands
594 cmd_tbl_t cmd_tbl_fat
[] = {
596 status
, 2, CTBL_RPT
, do_stat
,
597 "Show logical drive status",
601 pwd
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_pwd
,
602 "Print name of current/working directory",
606 cd
, 2, 0|CTBL_SUBCMDAUTO
, do_cd
,
607 "Change the current/working directory.",
611 ls
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_ls
,
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."
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"
635 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
636 "Print sub command description/usage",
638 " - print brief description of all sub commands\n"
639 "fat help command ...\n"
640 " - print detailed usage of sub cmd 'command'"
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
,
646 #ifdef CONFIG_SYS_LONGHELP
648 #endif /* CONFIG_SYS_LONGHELP */
650 #ifdef CONFIG_AUTO_COMPLETE
654 /* Mark end of table */
655 CMD_TBL_END(cmd_tbl_fat
)
659 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
661 puts_P(PSTR("Huch?"));
663 return CMD_RET_USAGE
;