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
)
272 basename
= strrchr(p
->p_path
, '/');
273 return(basename
? basename
+ 1 : p
->p_path
);
278 static void swirl(void)
280 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
281 static uint_fast8_t cnt
;
282 static uint32_t tstamp
;
284 if (get_timer(0) > tstamp
) {
285 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
286 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
287 tstamp
= get_timer(0) + 250;
292 * pwd - Print current directory of the current drive.
295 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
300 buf
= (char *) malloc(BUFFER_SIZE
);
302 printf_P(PSTR("pwd: Out of Memory!\n"));
304 return CMD_RET_FAILURE
;
307 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
315 return CMD_RET_FAILURE
;
317 return CMD_RET_SUCCESS
;
322 * cd - Change the current/working directory.
325 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
331 arg
= getenv_str(PSTR(ENV_HOME
));
333 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
334 return CMD_RET_FAILURE
;
344 res
= f_chdrive(drv
);
352 return CMD_RET_FAILURE
;
354 return CMD_RET_SUCCESS
;
359 * Remove trailing slashes,
360 * but keep a leading slash (absolute path)
362 void strip_trailing_slash_relpath(char *p
)
366 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
370 if (n
>= 1 && p
[0] == '/') {
374 while (n
-- != 0 && p
[n
] == '/')
379 int print_dirent(FILINFO
*f
)
381 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
382 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
383 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
384 (f
->fattrib
& AM_HID
) ? 'H' : '-',
385 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
386 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
387 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
388 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
393 * ls path - Directory listing
396 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
399 DIR Dir
; /* Directory object */
408 if (!path_set(&from
, argv
[1])) {
412 char *p
= strrchr(path
, '/');
418 if ((*q
++ & 0x38) == '0' && *q
++ == ':')
422 char *p
= path_basename(&from
);
424 if (strpbrk_P(p
, PSTR("*?")) ||
425 (f_stat(from
.p_path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
429 pattern
= strdup("*");
430 //strip_trailing_slash_relpath(path);
432 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
435 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
436 while (res
== FR_OK
&& Finfo
.fname
[0]) {
437 if (Finfo
.fattrib
& AM_DIR
) {
440 s1
++; p1
+= Finfo
.fsize
;
442 print_dirent(&Finfo
);
445 res
= f_findnext(&Dir
, &Finfo
);
451 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
452 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
453 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
458 return CMD_RET_FAILURE
;
461 return CMD_RET_SUCCESS
;
465 * tst path - for debugging: test access with different functions
468 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
470 DIR Dir
; /* Directory object */
475 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
477 char * buf
= (char *) malloc(BUFFER_SIZE
);
479 printf_P(PSTR("tst: Out of Memory!\n"));
481 return CMD_RET_FAILURE
;
483 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
486 printf_P(PSTR("cwd: '%s'\n"), buf
);
491 return CMD_RET_FAILURE
;
499 printf_P(PSTR("arg: '%s'\n"), path
);
500 printf_P(PSTR("==== f_stat: "));
501 res
= f_stat(path
, &Finfo
);
504 print_dirent(&Finfo
);
507 printf_P(PSTR("==== f_findfirst: "));
508 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
511 print_dirent(&Finfo
);
515 printf_P(PSTR("==== f_opendir: "));
516 res
= f_opendir(&Dir
, path
);
520 return CMD_RET_SUCCESS
;
525 FRESULT
mkpath(TCHAR
*path
)
533 res
= f_stat (path
, &fd
)
535 p
= strchr(path
, ':');
536 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
539 while ((q
= strchr(p
, '/')) != NULL
) {
543 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
552 /******************************************************************************/
554 static uint8_t flags
;
555 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
556 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
557 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
558 #define P_FLAG (1<<4) // preserve attributes and timestamps
559 #define R_FLAG (1<<0) // copy directories recursively
560 #define V_FLAG (1<<5) // explain what is being done
567 fr
= f_utime(to
.p_path
, fs
);
569 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
570 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
572 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
576 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
583 if (blockbuf
== NULL
) {
584 blockbuf_size
= get_freemem() / 512 * 512;
585 if (blockbuf_size
!= 0)
586 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
587 if (blockbuf
== NULL
) {
588 err(PSTR("Not enough memory!\n"));
593 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne
, blockbuf_size
);
594 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
597 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
598 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
603 * If the file exists and we're interactive, verify with the user.
606 if (flags
& N_FLAG
) {
608 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
611 } if (flags
& I_FLAG
) {
612 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
613 if (!confirm_yes()) {
618 if (flags
& F_FLAG
) {
619 /* Remove existing destination file name create a new file. */
620 f_chmod(to
.p_path
,0, AM_RDO
);
622 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
624 /* Overwrite existing destination file name. */
625 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
628 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
630 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
633 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
638 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
640 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
641 if (fr
|| wcount
< rcount
) {
642 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
647 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
650 if ((fr
= f_close(&to_fd
)) != FR_OK
)
651 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
660 static void copy_dir(void)
662 debug_cp("==== copy_dir()");
663 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
665 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
667 command_ret
= CMD_RET_FAILURE
;
670 static void copy_dir(void)
672 DIR Dir
; /* Directory object */
674 char *old_from
, *old_to
;
676 char *pattern
= {"*"};
678 debug_cp("==== copy_dir()");
679 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
682 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
683 res
== FR_OK
&& Finfo
.fname
[0];
684 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
)) {
686 if (!(Finfo
.fattrib
& AM_DIR
) &&
687 (old_from
= path_append(&from
, Finfo
.fname
))) {
688 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
690 path_restore(&to
, old_to
);
692 path_restore(&from
, old_from
);
696 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
697 res
== FR_OK
&& Finfo
.fname
[0];
698 res
= f_findnext(&Dir
, &Finfo
)) {
700 if ((Finfo
.fattrib
& AM_DIR
) &&
701 (old_from
= path_append(&from
, Finfo
.fname
))) {
702 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
704 path_restore(&to
, old_to
);
706 path_restore(&from
, old_from
);
712 static void copy_dir(void)
715 struct dirent
*dp
, **dir_list
;
717 char *old_from
, *old_to
;
719 debug_cp("==== copy_file(): dne: %u\n", dne
);
720 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
722 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
724 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
725 progname
, from
->p_path
);
726 command_ret
= CMD_RET_FAILURE
;
730 * Instead of handling directory entries in the order they appear
731 * on disk, do non-directory files before directory files.
732 * There are two reasons to do directories last. The first is
733 * efficiency. Files tend to be in the same cylinder group as
734 * their parent, whereas directories tend not to be. Copying files
735 * all at once reduces seeking. Second, deeply nested tree's
736 * could use up all the file descriptors if we didn't close one
737 * directory before recursivly starting on the next.
740 for (i
= 0; i
< dir_cnt
; ++i
) {
742 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
743 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
746 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
749 if (statfcn(from
->p_path
, &from_stat
) < 0) {
750 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
751 path_restore(&from
, old_from
);
754 if (S_ISDIR(from_stat
.st_mode
)) {
755 path_restore(&from
, old_from
);
758 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
760 path_restore(&to
, old_to
);
762 path_restore(&from
, old_from
);
763 done
: dir_list
[i
] = NULL
;
767 /* copy directories */
768 for (i
= 0; i
< dir_cnt
; ++i
) {
773 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
778 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
780 path_restore(&from
, old_from
);
785 path_restore(&from
, old_from
);
786 path_restore(&to
, old_to
);
793 * copy file or directory at "from" to "to".
797 FILINFO from_stat
, to_stat
;
801 debug_cp("==== copy()\n");
802 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
804 fr
= f_stat(from
.p_path
, &from_stat
);
806 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
810 /* not an error, but need to remember it happened */
811 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
814 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
815 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
816 to
.p_path
, from
.p_path
);
817 command_ret
= CMD_RET_FAILURE
;
823 if(from_stat
.fattrib
& AM_DIR
) {
824 if (!(flags
& R_FLAG
)) {
825 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
827 command_ret
= CMD_RET_FAILURE
;
832 * If the directory doesn't exist, create the new one.
834 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
835 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
839 else if (!(to_stat
.fattrib
& AM_DIR
)) {
840 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
849 copy_file(&from_stat
, dne
);
852 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
855 FRESULT fr
; /* Return value */
856 //DIR dj; /* Directory search object */
857 FILINFO to_stat
; /* File information */
862 command_ret
= CMD_RET_SUCCESS
;
868 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
871 tflags
&= ~(I_FLAG
| N_FLAG
);
875 tflags
&= ~(F_FLAG
| N_FLAG
);
879 tflags
&= ~(F_FLAG
| I_FLAG
);
892 return CMD_RET_USAGE
;
901 return CMD_RET_USAGE
;
904 from
= (PATH_T
*) malloc(sizeof(PATH_T
));
905 to
= (PATH_T
*) malloc(sizeof(PATH_T
));
906 if (from
== NULL
|| to
== NULL
) {
907 printf_P(PSTR("cp: Out of Memory!\n"));
908 command_ret
= CMD_RET_FAILURE
;
914 /* last argument is destination */
915 if (!path_set(&to
, argv
[--argc
]))
919 * Cp has two distinct cases:
921 * % cp [-rip] source target
922 * % cp [-rip] source1 ... directory
924 * In both cases, source can be either a file or a directory.
926 * In (1), the target becomes a copy of the source. That is, if the
927 * source is a file, the target will be a file, and likewise for
930 * In (2), the real target is not directory, but "directory/source".
933 fr
= f_stat(to
.p_path
, &to_stat
);
934 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr
, to_stat
.fattrib
, flags
);
935 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
937 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
938 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
939 command_ret
= CMD_RET_FAILURE
;
942 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
944 * Case (1). Target is not a directory.
947 err(PSTR("target '%s' is not a directory"), to
.p_path
);
948 //command_ret = CMD_RET_USAGE;
951 if (!path_set(&from
, *argv
)) {
952 command_ret
= CMD_RET_FAILURE
;
959 * Case (2). Target is a directory.
962 if (!path_set(&from
, *argv
))
964 if (!(old_to
= path_append(&to
, path_basename(&from
))))
969 path_restore(&to
, old_to
);
983 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
992 /******************************************************************************/
995 * Work register for stat command
999 WORD AccFiles
, AccDirs
;
1004 FRESULT
scan_files (
1005 char *path
, /* Pointer to the working buffer with start path */
1006 struct stat_dat_s
*statp
1014 res
= f_opendir(&dirs
, path
);
1018 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1019 statp
->Finfo
.fname
[0]) {
1020 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1022 fn
= statp
->Finfo
.fname
;
1023 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1026 strcpy(path
+i
+1, fn
);
1027 res
= scan_files(path
, statp
);
1032 //printf_P(PSTR("%s/%s\n"), path, fn);
1034 statp
->AccSize
+= statp
->Finfo
.fsize
;
1036 if (check_abort()) {
1048 * fatstat path - Show logical drive status
1051 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1058 struct stat_dat_s statp
;
1060 buf
= (char *) malloc(BUFFER_SIZE
);
1062 printf_P(PSTR("fat stat: Out of Memory!\n"));
1063 return CMD_RET_FAILURE
;
1068 res
= f_getfree(path
, &nfreeclst
, &fs
);
1072 "Bytes/Cluster: %lu\n"
1073 "Number of FATs: %u\n"
1074 "Root DIR entries: %u\n"
1075 "Sectors/FAT: %lu\n"
1076 "Number of clusters: %lu\n"
1077 "FAT start (lba): %lu\n"
1078 "DIR start (lba,cluster): %lu\n"
1079 "Data start (lba): %lu\n"),
1080 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1081 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1082 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1086 res
= f_getlabel(path
, buf
, &serial
);
1090 "Volume S/N: %04X-%04X\n"),
1091 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1095 my_puts_P(PSTR("\nCounting... "));
1096 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1099 res
= scan_files(buf
, &statp
);
1102 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1103 "%lu KB total disk space.\n%lu KB available.\n"),
1104 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1105 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1113 return CMD_RET_FAILURE
;
1115 return CMD_RET_SUCCESS
;
1119 * fatread/write - load binary file to/from a dos filesystem
1120 * read <d:/path/filename> <addr> [bytes [pos]]
1121 * write <d:/path/filename> <addr> <bytes>
1123 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1127 unsigned long bytes
;
1129 unsigned long bytes_rw
;
1131 bool dowrite
= (argv
[0][0] == 'w');
1132 FRESULT res
= FR_OK
;
1137 if (argc
< (dowrite
? 4 : 3))
1138 return CMD_RET_USAGE
;
1140 addr
= eval_arg(argv
[2], NULL
);
1141 if (addr
>= MAX_MEMORY
) {
1142 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1143 return CMD_RET_FAILURE
;
1146 bytes
= eval_arg(argv
[3], NULL
);
1150 pos
= eval_arg(argv
[4], NULL
);
1154 if (addr
+ bytes
> MAX_MEMORY
)
1155 bytes
= MAX_MEMORY
- addr
;
1157 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1158 if (buffer
== NULL
) {
1159 printf_P(PSTR("fatstat: Out of Memory!\n"));
1161 return CMD_RET_FAILURE
;
1165 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1169 res
= f_lseek(&File
, pos
);
1172 timer
= get_timer(0);
1174 unsigned int cnt
, br
;
1176 if (bytes
>= BUFFER_SIZE
) {
1178 bytes
-= BUFFER_SIZE
;
1180 cnt
= bytes
; bytes
= 0;
1183 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1187 z80_read_block(buffer
, addr
, cnt
);
1188 z80_bus_cmd(Release
);
1189 res
= f_write(&File
, buffer
, cnt
, &br
);
1193 res
= f_read(&File
, buffer
, cnt
, &br
);
1196 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1200 z80_write_block(buffer
, addr
, br
);
1201 z80_bus_cmd(Release
);
1208 printf_P(PSTR("Disk full?\n"));
1215 FRESULT fr
= f_close(&File
);
1218 timer
= get_timer(timer
);
1219 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1220 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1228 my_puts_P(PSTR("Bus timeout\n"));
1232 return CMD_RET_FAILURE
;
1234 return CMD_RET_SUCCESS
;
1238 * command table for fat subcommands
1241 cmd_tbl_t cmd_tbl_fat
[] = {
1243 stat
, 2, CTBL_RPT
, do_stat
,
1244 "Show logical drive status",
1248 pwd
, 2, CTBL_RPT
, do_pwd
,
1249 "Print name of current/working directory",
1254 "Change the current/working directory.",
1258 ls
, 2, CTBL_RPT
, do_ls
,
1259 "Directory listing",
1263 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1264 "FatFS test function",
1269 "load binary file from a dos filesystem",
1270 "<d:/path/filename> <addr> [bytes [pos]]\n"
1271 " - Load binary file 'path/filename' on logical drive 'd'\n"
1272 " to address 'addr' from dos filesystem.\n"
1273 " 'pos' gives the file position to start loading from.\n"
1274 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1275 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1276 " the load stops on end of file."
1280 "write file into a dos filesystem",
1281 "<d:/path/filename> <addr> <bytes>\n"
1282 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1283 " starting at address 'addr'.\n"
1287 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1289 "[-f | -i | -n] [-prv] source_file target_file\n"
1290 // "[-f | -i | -n] [-prv] source_file target_file\n"
1291 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1292 " -f overwrite existing file ignoring write protection\n"
1293 " this option is ignored when the -n option is also used\n"
1294 " -i prompt before overwrite (overrides a previous -n option)\n"
1295 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1296 " -p preserve attributes and timestamps\n"
1297 " -r copy directories recursively\n"
1298 " -v explain what is being done\n"
1302 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1303 "Print sub command description/usage",
1305 " - print brief description of all sub commands\n"
1306 "fat help command ...\n"
1307 " - print detailed usage of sub cmd 'command'"
1310 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1311 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1312 FSTR("Alias for 'help'"),
1313 #ifdef CONFIG_SYS_LONGHELP
1315 #endif /* CONFIG_SYS_LONGHELP */
1317 #ifdef CONFIG_AUTO_COMPLETE
1321 /* Mark end of table */
1322 CMD_TBL_END(cmd_tbl_fat
)
1326 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1328 puts_P(PSTR("Huch?"));
1330 return CMD_RET_USAGE
;