2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 * FAT filesystem commands
12 #include <util/delay.h>
17 #include "con-utils.h"
18 #include "print-utils.h"
23 #include "getopt-min.h"
26 #define DEBUG_CP 1 /* set to 1 to debug */
27 #define DEBUG_LS 1 /* set to 1 to debug */
29 #define debug_cp(fmt, args...) \
30 debug_cond(DEBUG_CP, fmt, ##args)
31 #define debug_ls(fmt, args...) \
32 debug_cond(DEBUG_LS, fmt, ##args)
35 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
36 /* TODO: detect_ramsize() should be moved to z80-if.c */
37 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
38 #define BUFFER_SIZE 512
39 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
43 char *p_end
; /* pointer to NULL at end of path */
44 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
48 * Multible (fat) partitions per physical drive are not supported,
49 * but we have up to 2 sdcard slots.
58 command_ret_t command_ret
;
60 void setup_fatfs(void)
62 f_mount(&FatFs0
, "0:", 0);
63 f_mount(&FatFs1
, "1:", 0);
66 DWORD
get_fattime (void)
72 gmtime_r(&timer
, &tm_timer
);
74 return fatfs_time(&tm_timer
);
78 static bool check_abort(void)
83 printf_P(PSTR("Abort\n"));
89 static const FLASH
char * const FLASH rc_strings
[] = {
92 FSTR("internal error"),
99 FSTR("invalid object"),
100 FSTR("write protected"),
101 FSTR("invalid drive"),
103 FSTR("no file system"),
104 FSTR("mkfs aborted"),
107 FSTR("not enough core"),
108 FSTR("too many open files"),
109 FSTR("invalid parameter")
112 static const FLASH
char * const FLASH rc_names
[] = {
119 FSTR("INVALID_NAME"),
122 FSTR("INVALID_OBJECT"),
123 FSTR("WRITE_PROTECTED"),
124 FSTR("INVALID_DRIVE"),
126 FSTR("NO_FILE_SYSTEM"),
127 FSTR("MKFS_ABORTED"),
130 FSTR("NOT_ENOUGH_CORE"),
131 FSTR("TOO_MANY_OPEN_FILES"),
132 FSTR("INVALID_PARAMETER")
136 void put_rc (FRESULT rc
)
139 printf_P(PSTR("rc=%u FR_"), rc
);
140 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
141 my_puts_P(PSTR("\n"));
143 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
144 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
148 const FLASH
char * rctostr(FRESULT rc
)
150 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
153 void err(const char *fmt
, ...)
157 // (void)fprintf(stderr, "%s: ", progname);
158 (void)vfprintf_P(stdout
, fmt
, ap
);
160 (void)printf_P(PSTR("\n"));
162 command_ret
= CMD_RET_FAILURE
;
165 /******************************************************************************/
168 * These functions manipulate paths in PATH_T structures.
170 * They eliminate multiple slashes in paths when they notice them,
171 * and keep the path non-slash terminated.
173 * Both path_set() and path_append() return 0 if the requested name
178 static void path_init(void)
180 from
.p_end
= from
.p_path
; from
.p_path
[0] = '\0';
181 to
.p_end
= to
.p_path
; to
.p_path
[0] = '\0';
184 static void strip_trailing_slash(PATH_T
*p
)
186 char *beg
= p
->p_path
;
187 char *end
= p
->p_end
;
188 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
190 if (beg
< end
&& *beg
== '/')
192 while (end
> beg
&& end
[-1] == '/')
199 * Move specified string into path. Convert "" to "." to handle BSD
200 * semantics for a null path. Strip trailing slashes.
203 path_set(PATH_T
*p
, char *string
)
205 if (strlen(string
) > MAX_PATHLEN
) {
206 err(PSTR("set: '%s': name too long"), string
);
210 (void)strcpy(p
->p_path
, string
);
211 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
213 if (p
->p_path
== p
->p_end
) {
218 strip_trailing_slash(p
);
223 * Append specified string to path, inserting '/' if necessary. Return a
224 * pointer to the old end of path for restoration.
227 path_append(PATH_T
*p
, char *name
)
229 char *old
= p
->p_end
;
230 int len
= strlen(name
);
232 /* The "+ 1" accounts for the '/' between old path and name. */
233 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
234 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
239 * This code should always be executed, since paths shouldn't
242 if (p
->p_end
[-1] != '/') {
247 (void)strncat(p
->p_end
, name
, len
);
251 strip_trailing_slash(p
);
256 * Restore path to previous value. (As returned by path_append.)
259 path_restore(PATH_T
*p
, char *old
)
266 * Return basename of path.
268 char *path_basename(PATH_T
*p
)
270 char *basename
= strrchr(p
->p_path
, '/');
275 basename
= p
->p_path
;
276 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
285 static void swirl(void)
287 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
288 static uint_fast8_t cnt
;
289 static uint32_t tstamp
;
291 if (get_timer(0) > tstamp
) {
292 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
293 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
294 tstamp
= get_timer(0) + 250;
299 * pwd - Print current directory of the current drive.
302 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
307 buf
= (char *) malloc(BUFFER_SIZE
);
309 printf_P(PSTR("pwd: Out of Memory!\n"));
311 return CMD_RET_FAILURE
;
314 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
322 return CMD_RET_FAILURE
;
324 return CMD_RET_SUCCESS
;
329 * cd - Change the current/working directory.
332 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
338 arg
= getenv_str(PSTR(ENV_HOME
));
340 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
341 return CMD_RET_FAILURE
;
351 res
= f_chdrive(drv
);
359 return CMD_RET_FAILURE
;
361 return CMD_RET_SUCCESS
;
366 * Remove trailing slashes,
367 * but keep a leading slash (absolute path)
369 void strip_trailing_slash_relpath(char *p
)
373 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
377 if (n
>= 1 && p
[0] == '/') {
381 while (n
-- != 0 && p
[n
] == '/')
386 int print_dirent(FILINFO
*f
)
388 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
389 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
390 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
391 (f
->fattrib
& AM_HID
) ? 'H' : '-',
392 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
393 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
394 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
395 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
400 * ls path - Directory listing
403 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
406 DIR Dir
; /* Directory object */
415 if (!path_set(&from
, argv
[1])) {
419 char *p
= path_basename(&from
);
420 if (strpbrk_P(p
, PSTR("*?")) ||
421 (f_stat(from
.p_path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
429 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", to
.p_path
? to
.p_path
: "<NULL>");
432 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, to
.p_path
); /* Start to search for files */
433 while (res
== FR_OK
&& Finfo
.fname
[0]) {
434 if (Finfo
.fattrib
& AM_DIR
) {
437 s1
++; p1
+= Finfo
.fsize
;
439 print_dirent(&Finfo
);
442 res
= f_findnext(&Dir
, &Finfo
);
447 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
448 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
449 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
454 return CMD_RET_FAILURE
;
457 return CMD_RET_SUCCESS
;
461 * tst path - for debugging: test access with different functions
464 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
466 DIR Dir
; /* Directory object */
471 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
473 char * buf
= (char *) malloc(BUFFER_SIZE
);
475 printf_P(PSTR("tst: Out of Memory!\n"));
477 return CMD_RET_FAILURE
;
479 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
482 printf_P(PSTR("cwd: '%s'\n"), buf
);
487 return CMD_RET_FAILURE
;
495 printf_P(PSTR("arg: '%s'\n"), path
);
496 printf_P(PSTR("==== f_stat: "));
497 res
= f_stat(path
, &Finfo
);
500 print_dirent(&Finfo
);
503 printf_P(PSTR("==== f_findfirst: "));
504 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
507 print_dirent(&Finfo
);
511 printf_P(PSTR("==== f_opendir: "));
512 res
= f_opendir(&Dir
, path
);
516 return CMD_RET_SUCCESS
;
519 /******************************************************************************/
521 static uint8_t flags
;
522 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
523 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
524 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
525 #define P_FLAG (1<<4) // preserve attributes and timestamps
526 #define R_FLAG (1<<0) // copy directories recursively
527 #define V_FLAG (1<<5) // explain what is being done
534 fr
= f_utime(to
.p_path
, fs
);
536 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
537 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
539 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
543 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
550 if (blockbuf
== NULL
) {
551 blockbuf_size
= get_freemem() / 512 * 512;
552 if (blockbuf_size
!= 0)
553 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
554 if (blockbuf
== NULL
) {
555 err(PSTR("Not enough memory!\n"));
560 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne
, blockbuf_size
);
561 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
564 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
565 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
570 * If the file exists and we're interactive, verify with the user.
573 if (flags
& N_FLAG
) {
575 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
578 } if (flags
& I_FLAG
) {
579 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
580 if (!confirm_yes()) {
585 if (flags
& F_FLAG
) {
586 /* Remove existing destination file name create a new file. */
587 f_chmod(to
.p_path
,0, AM_RDO
);
589 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
591 /* Overwrite existing destination file name. */
592 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
595 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
597 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
600 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
605 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
607 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
608 if (fr
|| wcount
< rcount
) {
609 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
614 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
617 if ((fr
= f_close(&to_fd
)) != FR_OK
)
618 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
627 static void copy_dir(void)
629 debug_cp("==== copy_dir()");
630 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
632 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
634 command_ret
= CMD_RET_FAILURE
;
637 static void copy_dir(void)
639 DIR Dir
; /* Directory object */
641 char *old_from
, *old_to
;
643 char *pattern
= {"*"};
645 debug_cp("==== copy_dir()");
646 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
649 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
650 res
== FR_OK
&& Finfo
.fname
[0];
651 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
)) {
653 if (!(Finfo
.fattrib
& AM_DIR
) &&
654 (old_from
= path_append(&from
, Finfo
.fname
))) {
655 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
657 path_restore(&to
, old_to
);
659 path_restore(&from
, old_from
);
663 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
664 res
== FR_OK
&& Finfo
.fname
[0];
665 res
= f_findnext(&Dir
, &Finfo
)) {
667 if ((Finfo
.fattrib
& AM_DIR
) &&
668 (old_from
= path_append(&from
, Finfo
.fname
))) {
669 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
671 path_restore(&to
, old_to
);
673 path_restore(&from
, old_from
);
679 static void copy_dir(void)
682 struct dirent
*dp
, **dir_list
;
684 char *old_from
, *old_to
;
686 debug_cp("==== copy_file(): dne: %u\n", dne
);
687 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
689 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
691 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
692 progname
, from
->p_path
);
693 command_ret
= CMD_RET_FAILURE
;
697 * Instead of handling directory entries in the order they appear
698 * on disk, do non-directory files before directory files.
699 * There are two reasons to do directories last. The first is
700 * efficiency. Files tend to be in the same cylinder group as
701 * their parent, whereas directories tend not to be. Copying files
702 * all at once reduces seeking. Second, deeply nested tree's
703 * could use up all the file descriptors if we didn't close one
704 * directory before recursivly starting on the next.
707 for (i
= 0; i
< dir_cnt
; ++i
) {
709 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
710 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
713 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
716 if (statfcn(from
->p_path
, &from_stat
) < 0) {
717 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
718 path_restore(&from
, old_from
);
721 if (S_ISDIR(from_stat
.st_mode
)) {
722 path_restore(&from
, old_from
);
725 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
727 path_restore(&to
, old_to
);
729 path_restore(&from
, old_from
);
730 done
: dir_list
[i
] = NULL
;
734 /* copy directories */
735 for (i
= 0; i
< dir_cnt
; ++i
) {
740 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
745 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
747 path_restore(&from
, old_from
);
752 path_restore(&from
, old_from
);
753 path_restore(&to
, old_to
);
760 * copy file or directory at "from" to "to".
764 FILINFO from_stat
, to_stat
;
768 debug_cp("==== copy()\n");
769 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
771 fr
= f_stat(from
.p_path
, &from_stat
);
773 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
777 /* not an error, but need to remember it happened */
778 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
781 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
782 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
783 to
.p_path
, from
.p_path
);
784 command_ret
= CMD_RET_FAILURE
;
790 if(from_stat
.fattrib
& AM_DIR
) {
791 if (!(flags
& R_FLAG
)) {
792 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
794 command_ret
= CMD_RET_FAILURE
;
799 * If the directory doesn't exist, create the new one.
801 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
802 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
806 else if (!(to_stat
.fattrib
& AM_DIR
)) {
807 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
816 copy_file(&from_stat
, dne
);
819 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
822 FRESULT fr
; /* Return value */
823 //DIR dj; /* Directory search object */
824 FILINFO to_stat
; /* File information */
829 command_ret
= CMD_RET_SUCCESS
;
835 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
838 tflags
&= ~(I_FLAG
| N_FLAG
);
842 tflags
&= ~(F_FLAG
| N_FLAG
);
846 tflags
&= ~(F_FLAG
| I_FLAG
);
859 return CMD_RET_USAGE
;
868 return CMD_RET_USAGE
;
872 /* last argument is destination */
873 if (!path_set(&to
, argv
[--argc
]))
877 * Cp has two distinct cases:
879 * % cp [-rip] source target
880 * % cp [-rip] source1 ... directory
882 * In both cases, source can be either a file or a directory.
884 * In (1), the target becomes a copy of the source. That is, if the
885 * source is a file, the target will be a file, and likewise for
888 * In (2), the real target is not directory, but "directory/source".
891 fr
= f_stat(to
.p_path
, &to_stat
);
892 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr
, to_stat
.fattrib
, flags
);
893 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
895 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
896 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
897 command_ret
= CMD_RET_FAILURE
;
900 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
902 * Case (1). Target is not a directory.
905 err(PSTR("target '%s' is not a directory"), to
.p_path
);
906 //command_ret = CMD_RET_USAGE;
909 if (!path_set(&from
, *argv
)) {
910 command_ret
= CMD_RET_FAILURE
;
917 * Case (2). Target is a directory.
920 if (!path_set(&from
, *argv
))
922 if (!(old_to
= path_append(&to
, path_basename(&from
))))
927 path_restore(&to
, old_to
);
941 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
950 /******************************************************************************/
953 * Work register for stat command
957 WORD AccFiles
, AccDirs
;
963 char *path
, /* Pointer to the working buffer with start path */
964 struct stat_dat_s
*statp
972 res
= f_opendir(&dirs
, path
);
976 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
977 statp
->Finfo
.fname
[0]) {
978 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
980 fn
= statp
->Finfo
.fname
;
981 if (statp
->Finfo
.fattrib
& AM_DIR
) {
984 strcpy(path
+i
+1, fn
);
985 res
= scan_files(path
, statp
);
990 //printf_P(PSTR("%s/%s\n"), path, fn);
992 statp
->AccSize
+= statp
->Finfo
.fsize
;
1006 * fatstat path - Show logical drive status
1009 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1016 struct stat_dat_s statp
;
1018 buf
= (char *) malloc(BUFFER_SIZE
);
1020 printf_P(PSTR("fat stat: Out of Memory!\n"));
1021 return CMD_RET_FAILURE
;
1026 res
= f_getfree(path
, &nfreeclst
, &fs
);
1030 "Bytes/Cluster: %lu\n"
1031 "Number of FATs: %u\n"
1032 "Root DIR entries: %u\n"
1033 "Sectors/FAT: %lu\n"
1034 "Number of clusters: %lu\n"
1035 "FAT start (lba): %lu\n"
1036 "DIR start (lba,cluster): %lu\n"
1037 "Data start (lba): %lu\n"),
1038 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1039 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1040 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1044 res
= f_getlabel(path
, buf
, &serial
);
1048 "Volume S/N: %04X-%04X\n"),
1049 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1053 my_puts_P(PSTR("\nCounting... "));
1054 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1057 res
= scan_files(buf
, &statp
);
1060 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1061 "%lu KB total disk space.\n%lu KB available.\n"),
1062 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1063 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1071 return CMD_RET_FAILURE
;
1073 return CMD_RET_SUCCESS
;
1077 * fatread/write - load binary file to/from a dos filesystem
1078 * read <d:/path/filename> <addr> [bytes [pos]]
1079 * write <d:/path/filename> <addr> <bytes>
1081 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1085 unsigned long bytes
;
1087 unsigned long bytes_rw
;
1089 bool dowrite
= (argv
[0][0] == 'w');
1090 FRESULT res
= FR_OK
;
1095 if (argc
< (dowrite
? 4 : 3))
1096 return CMD_RET_USAGE
;
1098 addr
= eval_arg(argv
[2], NULL
);
1099 if (addr
>= MAX_MEMORY
) {
1100 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1101 return CMD_RET_FAILURE
;
1104 bytes
= eval_arg(argv
[3], NULL
);
1108 pos
= eval_arg(argv
[4], NULL
);
1112 if (addr
+ bytes
> MAX_MEMORY
)
1113 bytes
= MAX_MEMORY
- addr
;
1115 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1116 if (buffer
== NULL
) {
1117 printf_P(PSTR("fatstat: Out of Memory!\n"));
1119 return CMD_RET_FAILURE
;
1123 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1127 res
= f_lseek(&File
, pos
);
1130 timer
= get_timer(0);
1132 unsigned int cnt
, br
;
1134 if (bytes
>= BUFFER_SIZE
) {
1136 bytes
-= BUFFER_SIZE
;
1138 cnt
= bytes
; bytes
= 0;
1141 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1145 z80_read_block(buffer
, addr
, cnt
);
1146 z80_bus_cmd(Release
);
1147 res
= f_write(&File
, buffer
, cnt
, &br
);
1151 res
= f_read(&File
, buffer
, cnt
, &br
);
1154 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1158 z80_write_block(buffer
, addr
, br
);
1159 z80_bus_cmd(Release
);
1166 printf_P(PSTR("Disk full?\n"));
1173 FRESULT fr
= f_close(&File
);
1176 timer
= get_timer(timer
);
1177 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1178 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1186 my_puts_P(PSTR("Bus timeout\n"));
1190 return CMD_RET_FAILURE
;
1192 return CMD_RET_SUCCESS
;
1196 * command table for fat subcommands
1199 cmd_tbl_t cmd_tbl_fat
[] = {
1201 stat
, 2, CTBL_RPT
, do_stat
,
1202 "Show logical drive status",
1206 pwd
, 2, CTBL_RPT
, do_pwd
,
1207 "Print name of current/working directory",
1212 "Change the current/working directory.",
1216 ls
, 2, CTBL_RPT
, do_ls
,
1217 "Directory listing",
1221 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1222 "FatFS test function",
1227 "load binary file from a dos filesystem",
1228 "<d:/path/filename> <addr> [bytes [pos]]\n"
1229 " - Load binary file 'path/filename' on logical drive 'd'\n"
1230 " to address 'addr' from dos filesystem.\n"
1231 " 'pos' gives the file position to start loading from.\n"
1232 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1233 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1234 " the load stops on end of file."
1238 "write file into a dos filesystem",
1239 "<d:/path/filename> <addr> <bytes>\n"
1240 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1241 " starting at address 'addr'.\n"
1245 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1247 "[-f | -i | -n] [-prv] source_file target_file\n"
1248 // "[-f | -i | -n] [-prv] source_file target_file\n"
1249 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1250 " -f overwrite existing file ignoring write protection\n"
1251 " this option is ignored when the -n option is also used\n"
1252 " -i prompt before overwrite (overrides a previous -n option)\n"
1253 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1254 " -p preserve attributes and timestamps\n"
1255 " -r copy directories recursively\n"
1256 " -v explain what is being done\n"
1260 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1261 "Print sub command description/usage",
1263 " - print brief description of all sub commands\n"
1264 "fat help command ...\n"
1265 " - print detailed usage of sub cmd 'command'"
1268 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1269 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1270 FSTR("Alias for 'help'"),
1271 #ifdef CONFIG_SYS_LONGHELP
1273 #endif /* CONFIG_SYS_LONGHELP */
1275 #ifdef CONFIG_AUTO_COMPLETE
1279 /* Mark end of table */
1280 CMD_TBL_END(cmd_tbl_fat
)
1284 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1286 puts_P(PSTR("Huch?"));
1288 return CMD_RET_USAGE
;