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 */
28 #define DEBUG_RM 1 /* set to 1 to debug */
30 #define debug_cp(fmt, args...) \
31 debug_cond(DEBUG_CP, fmt, ##args)
32 #define debug_ls(fmt, args...) \
33 debug_cond(DEBUG_LS, fmt, ##args)
34 #define debug_rm(fmt, args...) \
35 debug_cond(DEBUG_RM, fmt, ##args)
39 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
40 /* TODO: detect_ramsize() should be moved to z80-if.c */
41 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
42 #define BUFFER_SIZE 512
43 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
47 char *p_end
; /* pointer to NULL at end of path */
48 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
52 * Multible (fat) partitions per physical drive are not supported,
53 * but we have up to 2 sdcard slots.
62 command_ret_t command_ret
;
66 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
67 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
68 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
69 #define P_FLAG (1<<4) // preserve attributes and timestamps
70 #define R_FLAG (1<<0) // copy directories recursively
71 #define V_FLAG (1<<5) // explain what is being done
75 void setup_fatfs(void)
77 f_mount(&FatFs0
, "0:", 0);
78 f_mount(&FatFs1
, "1:", 0);
81 DWORD
get_fattime (void)
87 gmtime_r(&timer
, &tm_timer
);
89 return fatfs_time(&tm_timer
);
93 static bool check_abort(void)
98 printf_P(PSTR("Abort\n"));
104 static const FLASH
char * const FLASH rc_strings
[] = {
107 FSTR("internal error"),
111 FSTR("invalid name"),
114 FSTR("invalid object"),
115 FSTR("write protected"),
116 FSTR("invalid drive"),
118 FSTR("no file system"),
119 FSTR("mkfs aborted"),
122 FSTR("not enough core"),
123 FSTR("too many open files"),
124 FSTR("invalid parameter")
127 static const FLASH
char * const FLASH rc_names
[] = {
134 FSTR("INVALID_NAME"),
137 FSTR("INVALID_OBJECT"),
138 FSTR("WRITE_PROTECTED"),
139 FSTR("INVALID_DRIVE"),
141 FSTR("NO_FILE_SYSTEM"),
142 FSTR("MKFS_ABORTED"),
145 FSTR("NOT_ENOUGH_CORE"),
146 FSTR("TOO_MANY_OPEN_FILES"),
147 FSTR("INVALID_PARAMETER")
151 void put_rc (FRESULT rc
)
154 printf_P(PSTR("rc=%u FR_"), rc
);
155 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
156 my_puts_P(PSTR("\n"));
158 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
159 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
163 const FLASH
char * rctostr(FRESULT rc
)
165 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
168 void err(const char *fmt
, ...)
172 printf_P(PSTR("fat %s: "), cmdname
);
173 vfprintf_P(stdout
, fmt
, ap
);
175 printf_P(PSTR("\n"));
177 command_ret
= CMD_RET_FAILURE
;
180 /******************************************************************************/
183 * These functions manipulate paths in PATH_T structures.
185 * They eliminate multiple slashes in paths when they notice them,
186 * and keep the path non-slash terminated.
188 * Both path_set() and path_append() return 0 if the requested name
193 static void path_init(void)
195 from
.p_path
[0] = '\0'; from
.p_end
= from
.p_path
;
196 to
.p_path
[0] = '\0'; to
.p_end
= to
.p_path
;
199 static void strip_trailing_slash(PATH_T
*p
)
201 char *beg
= p
->p_path
;
202 char *end
= p
->p_end
;
203 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
205 if (beg
< end
&& *beg
== '/')
207 while (end
> beg
&& end
[-1] == '/')
214 * Move specified string into path. Convert "" to "." to handle BSD
215 * semantics for a null path. Strip trailing slashes.
218 path_set(PATH_T
*p
, char *string
)
220 if (strlen(string
) > MAX_PATHLEN
) {
221 err(PSTR("set: '%s': name too long"), string
);
225 (void)strcpy(p
->p_path
, string
);
226 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
228 if (p
->p_path
== p
->p_end
) {
233 strip_trailing_slash(p
);
238 * Append specified string to path, inserting '/' if necessary. Return a
239 * pointer to the old end of path for restoration.
242 path_append(PATH_T
*p
, char *name
)
244 char *old
= p
->p_end
;
245 int len
= strlen(name
);
247 /* The "+ 1" accounts for the '/' between old path and name. */
248 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
249 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
254 * This code should always be executed, since paths shouldn't
257 if (p
->p_end
[-1] != '/') {
262 (void)strncat(p
->p_end
, name
, len
);
266 strip_trailing_slash(p
);
271 * Restore path to previous value. (As returned by path_append.)
274 path_restore(PATH_T
*p
, char *old
)
281 * Return basename of path.
283 char *path_basename(PATH_T
*p
)
285 char *basename
= strrchr(p
->p_path
, '/');
290 basename
= p
->p_path
;
291 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
298 char *path_basename_pattern(PATH_T
*p
)
300 char *pattern
= path_basename(p
);
301 if (strpbrk_P(pattern
, PSTR("*?"))) {
302 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
305 //p->p_pattern = p->p_end + 1;
306 pattern
= p
->p_end
+ 1;
313 static void swirl(void)
315 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
316 static uint_fast8_t cnt
;
317 static uint32_t tstamp
;
319 if (get_timer(0) > tstamp
) {
320 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
321 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
322 tstamp
= get_timer(0) + 250;
327 * pwd - Print current directory of the current drive.
330 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
337 buf
= (char *) malloc(BUFFER_SIZE
);
339 printf_P(PSTR("pwd: Out of Memory!\n"));
341 return CMD_RET_FAILURE
;
344 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
352 return CMD_RET_FAILURE
;
354 return CMD_RET_SUCCESS
;
359 * cd - Change the current/working directory.
362 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
370 arg
= getenv_str(PSTR(ENV_HOME
));
372 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
373 return CMD_RET_FAILURE
;
383 res
= f_chdrive(drv
);
391 return CMD_RET_FAILURE
;
393 return CMD_RET_SUCCESS
;
398 * Remove trailing slashes,
399 * but keep a leading slash (absolute path)
401 void strip_trailing_slash_relpath(char *p
)
405 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
409 if (n
>= 1 && p
[0] == '/') {
413 while (n
-- != 0 && p
[n
] == '/')
418 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
420 DIR Dir
; /* Directory object */
431 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
440 return CMD_RET_USAGE
;
447 command_ret
= CMD_RET_SUCCESS
;
449 err(PSTR("missing operand"));
451 for (int i
= 0; i
< argc
; i
++) {
452 if (!path_set(&from
, argv
[i
])) {
455 char *pattern
= path_basename_pattern(&from
);
457 debug_rm("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
459 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
460 debug_rm("==== findfirst %d\n", res
);
461 while (res
== FR_OK
&& Finfo
.fname
[0]) {
462 if (Finfo
.fattrib
& AM_DIR
) {
463 err(PSTR("cannot remove '%s': Is a directory"), Finfo
.fname
);
465 if (!(flags
& N_FLAG
)) {
466 if ((res
= f_unlink(Finfo
.fname
)) == FR_OK
) {
468 printf_P(PSTR("removed '%s'\n"), Finfo
.fname
);
470 err(PSTR("cannot remove '%s': Read only?"), Finfo
.fname
);
474 printf_P(PSTR("not removed '%s'\n"), Finfo
.fname
);
477 res
= f_findnext(&Dir
, &Finfo
);
478 debug_rm("==== findnext %d\n", res
);
486 return CMD_RET_FAILURE
;
492 command_ret_t
do_rmdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
496 return CMD_RET_SUCCESS
;
499 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
503 return CMD_RET_SUCCESS
;
507 int print_dirent(FILINFO
*f
)
509 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
510 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
511 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
512 (f
->fattrib
& AM_HID
) ? 'H' : '-',
513 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
514 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
515 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
516 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
521 * ls path - Directory listing
524 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
527 DIR Dir
; /* Directory object */
538 if (!path_set(&from
, argv
[1])) {
542 char *pattern
= path_basename_pattern(&from
);
544 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
547 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
548 while (res
== FR_OK
&& Finfo
.fname
[0]) {
549 if (Finfo
.fattrib
& AM_DIR
) {
552 s1
++; p1
+= Finfo
.fsize
;
554 print_dirent(&Finfo
);
557 res
= f_findnext(&Dir
, &Finfo
);
562 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
563 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
564 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
569 return CMD_RET_FAILURE
;
572 return CMD_RET_SUCCESS
;
576 * tst path - for debugging: test access with different functions
579 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
581 DIR Dir
; /* Directory object */
587 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
589 char * buf
= (char *) malloc(BUFFER_SIZE
);
591 printf_P(PSTR("tst: Out of Memory!\n"));
592 return CMD_RET_FAILURE
;
594 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
597 printf_P(PSTR("cwd: '%s'\n"), buf
);
602 return CMD_RET_FAILURE
;
610 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
611 printf_P(PSTR("==== f_stat: "));
612 res
= f_stat(path
, &Finfo
);
615 print_dirent(&Finfo
);
618 printf_P(PSTR("==== f_findfirst: "));
619 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
622 print_dirent(&Finfo
);
626 printf_P(PSTR("==== f_opendir: "));
627 res
= f_opendir(&Dir
, path
);
631 return CMD_RET_SUCCESS
;
634 /******************************************************************************/
641 fr
= f_utime(to
.p_path
, fs
);
643 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
644 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
646 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
650 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
657 if (blockbuf
== NULL
) {
658 blockbuf_size
= get_freemem() / 512 * 512;
659 if (blockbuf_size
!= 0)
660 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
661 if (blockbuf
== NULL
) {
662 err(PSTR("Not enough memory!\n"));
667 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
668 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
671 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
672 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
677 * If the file exists and we're interactive, verify with the user.
680 if (flags
& N_FLAG
) {
682 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
685 } if (flags
& I_FLAG
) {
686 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
687 if (!confirm_yes()) {
692 if (flags
& F_FLAG
) {
693 /* Remove existing destination file name create a new file. */
694 f_chmod(to
.p_path
,0, AM_RDO
);
696 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
698 /* Overwrite existing destination file name. */
699 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
702 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
704 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
707 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
712 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
714 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
715 if (fr
|| wcount
< rcount
) {
716 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
721 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
724 if ((fr
= f_close(&to_fd
)) != FR_OK
)
725 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
733 static void copy_dir(void)
735 DIR Dir
; /* Directory object */
737 char *old_from
, *old_to
;
739 char *pattern
= {"*"};
741 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
742 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
746 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
748 command_ret
= CMD_RET_FAILURE
;
752 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
753 res
== FR_OK
&& Finfo
.fname
[0];
754 res
= f_findnext(&Dir
, &Finfo
)) {
756 if (!(Finfo
.fattrib
& AM_DIR
) &&
757 (old_from
= path_append(&from
, Finfo
.fname
))) {
758 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
760 path_restore(&to
, old_to
);
762 path_restore(&from
, old_from
);
766 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
767 res
== FR_OK
&& Finfo
.fname
[0];
768 res
= f_findnext(&Dir
, &Finfo
)) {
770 if ((Finfo
.fattrib
& AM_DIR
) &&
771 (old_from
= path_append(&from
, Finfo
.fname
))) {
772 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
774 path_restore(&to
, old_to
);
776 path_restore(&from
, old_from
);
783 * copy file or directory at "from" to "to".
787 FILINFO from_stat
, to_stat
;
791 debug_cp("==== copy(); freemem: %u\n", get_freemem());
792 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
794 fr
= f_stat(from
.p_path
, &from_stat
);
796 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
800 /* not an error, but need to remember it happened */
801 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
804 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
805 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
806 to
.p_path
, from
.p_path
);
807 command_ret
= CMD_RET_FAILURE
;
813 if(from_stat
.fattrib
& AM_DIR
) {
814 if (!(flags
& R_FLAG
)) {
815 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
817 command_ret
= CMD_RET_FAILURE
;
822 * If the directory doesn't exist, create the new one.
824 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
825 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
828 } else if (!(to_stat
.fattrib
& AM_DIR
)) {
829 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
838 copy_file(&from_stat
, dne
);
841 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
844 FRESULT fr
; /* Return value */
845 //DIR dj; /* Directory search object */
846 FILINFO to_stat
; /* File information */
852 command_ret
= CMD_RET_SUCCESS
;
858 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
861 tflags
&= ~(I_FLAG
| N_FLAG
);
865 tflags
&= ~(F_FLAG
| N_FLAG
);
869 tflags
&= ~(F_FLAG
| I_FLAG
);
882 return CMD_RET_USAGE
;
891 return CMD_RET_USAGE
;
895 /* last argument is destination */
896 if (!path_set(&to
, argv
[--argc
]))
900 * Cp has two distinct cases:
902 * % cp [-rip] source target
903 * % cp [-rip] source1 ... directory
905 * In both cases, source can be either a file or a directory.
907 * In (1), the target becomes a copy of the source. That is, if the
908 * source is a file, the target will be a file, and likewise for
911 * In (2), the real target is not directory, but "directory/source".
914 fr
= f_stat(to
.p_path
, &to_stat
);
915 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
916 fr
, to_stat
.fattrib
, flags
, get_freemem());
917 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
919 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
920 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
921 command_ret
= CMD_RET_FAILURE
;
924 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
926 * Case (1). Target is not a directory.
929 err(PSTR("target '%s' is not a directory"), to
.p_path
);
930 //command_ret = CMD_RET_USAGE;
933 if (!path_set(&from
, *argv
)) {
934 command_ret
= CMD_RET_FAILURE
;
941 * Case (2). Target is a directory.
944 if (!path_set(&from
, *argv
))
946 if (!(old_to
= path_append(&to
, path_basename(&from
))))
951 path_restore(&to
, old_to
);
965 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
970 /******************************************************************************/
973 * Work register for stat command
977 WORD AccFiles
, AccDirs
;
983 char *path
, /* Pointer to the working buffer with start path */
984 struct stat_dat_s
*statp
992 res
= f_opendir(&dirs
, path
);
996 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
997 statp
->Finfo
.fname
[0]) {
998 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1000 fn
= statp
->Finfo
.fname
;
1001 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1004 strcpy(path
+i
+1, fn
);
1005 res
= scan_files(path
, statp
);
1010 //printf_P(PSTR("%s/%s\n"), path, fn);
1012 statp
->AccSize
+= statp
->Finfo
.fsize
;
1014 if (check_abort()) {
1026 * fatstat path - Show logical drive status
1029 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1036 struct stat_dat_s statp
;
1038 buf
= (char *) malloc(BUFFER_SIZE
);
1040 printf_P(PSTR("fat stat: Out of Memory!\n"));
1041 return CMD_RET_FAILURE
;
1046 res
= f_getfree(path
, &nfreeclst
, &fs
);
1050 "Bytes/Cluster: %lu\n"
1051 "Number of FATs: %u\n"
1052 "Root DIR entries: %u\n"
1053 "Sectors/FAT: %lu\n"
1054 "Number of clusters: %lu\n"
1055 "FAT start (lba): %lu\n"
1056 "DIR start (lba,cluster): %lu\n"
1057 "Data start (lba): %lu\n"),
1058 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1059 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1060 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1064 res
= f_getlabel(path
, buf
, &serial
);
1068 "Volume S/N: %04X-%04X\n"),
1069 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1073 my_puts_P(PSTR("\nCounting... "));
1074 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1077 res
= scan_files(buf
, &statp
);
1080 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1081 "%lu KB total disk space.\n%lu KB available.\n"),
1082 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1083 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1091 return CMD_RET_FAILURE
;
1093 return CMD_RET_SUCCESS
;
1097 * fatread/write - load binary file to/from a dos filesystem
1098 * read <d:/path/filename> <addr> [bytes [pos]]
1099 * write <d:/path/filename> <addr> <bytes>
1101 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1105 unsigned long bytes
;
1107 unsigned long bytes_rw
;
1109 bool dowrite
= (argv
[0][0] == 'w');
1110 FRESULT res
= FR_OK
;
1115 if (argc
< (dowrite
? 4 : 3))
1116 return CMD_RET_USAGE
;
1118 addr
= eval_arg(argv
[2], NULL
);
1119 if (addr
>= MAX_MEMORY
) {
1120 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1121 return CMD_RET_FAILURE
;
1124 bytes
= eval_arg(argv
[3], NULL
);
1128 pos
= eval_arg(argv
[4], NULL
);
1132 if (addr
+ bytes
> MAX_MEMORY
)
1133 bytes
= MAX_MEMORY
- addr
;
1135 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1136 if (buffer
== NULL
) {
1137 printf_P(PSTR("fatstat: Out of Memory!\n"));
1139 return CMD_RET_FAILURE
;
1143 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1147 res
= f_lseek(&File
, pos
);
1150 timer
= get_timer(0);
1152 unsigned int cnt
, br
;
1154 if (bytes
>= BUFFER_SIZE
) {
1156 bytes
-= BUFFER_SIZE
;
1158 cnt
= bytes
; bytes
= 0;
1161 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1165 z80_read_block(buffer
, addr
, cnt
);
1166 z80_bus_cmd(Release
);
1167 res
= f_write(&File
, buffer
, cnt
, &br
);
1171 res
= f_read(&File
, buffer
, cnt
, &br
);
1174 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1178 z80_write_block(buffer
, addr
, br
);
1179 z80_bus_cmd(Release
);
1186 printf_P(PSTR("Disk full?\n"));
1193 FRESULT fr
= f_close(&File
);
1196 timer
= get_timer(timer
);
1197 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1198 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1206 my_puts_P(PSTR("Bus timeout\n"));
1210 return CMD_RET_FAILURE
;
1212 return CMD_RET_SUCCESS
;
1216 * command table for fat subcommands
1219 cmd_tbl_t cmd_tbl_fat
[] = {
1221 stat
, 2, CTBL_RPT
, do_stat
,
1222 "Show logical drive status",
1226 pwd
, 2, CTBL_RPT
, do_pwd
,
1227 "Print name of current/working directory",
1232 "Change the current/working directory.",
1236 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
1238 "[OPTION]... [FILE]..."
1241 rmdir
, CONFIG_SYS_MAXARGS
, 0, do_rmdir
,
1242 "Remove the DIRECTORY(ies), if they are empty",
1243 "[OPTION]... DIRECTORY..."
1246 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
1247 "Create the DIRECTORY(ies), if they do not already exist.",
1248 "[OPTION]... DIRECTORY..."
1251 ls
, 2, CTBL_RPT
, do_ls
,
1252 "Directory listing",
1256 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
1257 "FatFS test function",
1262 "load binary file from a dos filesystem",
1263 "<d:/path/filename> <addr> [bytes [pos]]\n"
1264 " - Load binary file 'path/filename' on logical drive 'd'\n"
1265 " to address 'addr' from dos filesystem.\n"
1266 " 'pos' gives the file position to start loading from.\n"
1267 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1268 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1269 " the load stops on end of file."
1273 "write file into a dos filesystem",
1274 "<d:/path/filename> <addr> <bytes>\n"
1275 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1276 " starting at address 'addr'.\n"
1280 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1282 "[-f | -i | -n] [-prv] source_file target_file\n"
1283 // "[-f | -i | -n] [-prv] source_file target_file\n"
1284 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1285 " -f overwrite existing file ignoring write protection\n"
1286 " this option is ignored when the -n option is also used\n"
1287 " -i prompt before overwrite (overrides a previous -n option)\n"
1288 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1289 " -p preserve attributes and timestamps\n"
1290 " -r copy directories recursively\n"
1291 " -v explain what is being done\n"
1295 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1296 "Print sub command description/usage",
1298 " - print brief description of all sub commands\n"
1299 "fat help command ...\n"
1300 " - print detailed usage of sub cmd 'command'"
1303 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1304 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1305 FSTR("Alias for 'help'"),
1306 #ifdef CONFIG_SYS_LONGHELP
1308 #endif /* CONFIG_SYS_LONGHELP */
1310 #ifdef CONFIG_AUTO_COMPLETE
1314 /* Mark end of table */
1315 CMD_TBL_END(cmd_tbl_fat
)
1319 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1321 puts_P(PSTR("Huch?"));
1323 return CMD_RET_USAGE
;