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 0 /* set to 1 to debug */
27 #define DEBUG_LS 0 /* 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
;
167 static void swirl(void)
169 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
170 static uint_fast8_t cnt
;
171 static uint32_t tstamp
;
173 if (get_timer(0) > tstamp
) {
174 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
175 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
176 tstamp
= get_timer(0) + 250;
181 * pwd - Print current directory of the current drive.
184 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
189 buf
= (char *) malloc(BUFFER_SIZE
);
191 printf_P(PSTR("pwd: Out of Memory!\n"));
193 return CMD_RET_FAILURE
;
196 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
204 return CMD_RET_FAILURE
;
206 return CMD_RET_SUCCESS
;
211 * cd - Change the current/working directory.
214 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
220 arg
= getenv_str(PSTR(ENV_HOME
));
222 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
223 return CMD_RET_FAILURE
;
233 res
= f_chdrive(drv
);
241 return CMD_RET_FAILURE
;
243 return CMD_RET_SUCCESS
;
248 * Remove trailing slashes,
249 * but keep a leading slash (absolute path)
251 void strip_trailing_slash_relpath(char *p
)
255 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
259 if (n
>= 1 && p
[0] == '/') {
263 while (n
-- != 0 && p
[n
] == '/')
267 int print_dirent(FILINFO
*f
)
269 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
270 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
271 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
272 (f
->fattrib
& AM_HID
) ? 'H' : '-',
273 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
274 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
275 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
276 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
281 * ls path - Directory listing
284 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
287 DIR Dir
; /* Directory object */
296 strip_trailing_slash_relpath(path
);
298 char *p
= strrchr(path
, '/');
304 if ((*q
++ & 0x38) == '0' && *q
++ == ':')
309 if (strpbrk_P(p
, PSTR("*?")) ||
310 (f_stat(path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
314 pattern
= strdup("*");
315 strip_trailing_slash_relpath(path
);
317 debug_ls("==== path: '%s', pattern: '%s'\n", path
? path
: "<NULL>", pattern
? pattern
: "<NULL>");
320 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
321 while (res
== FR_OK
&& Finfo
.fname
[0]) {
322 if (Finfo
.fattrib
& AM_DIR
) {
325 s1
++; p1
+= Finfo
.fsize
;
327 print_dirent(&Finfo
);
330 res
= f_findnext(&Dir
, &Finfo
);
336 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
337 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
338 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
343 return CMD_RET_FAILURE
;
346 return CMD_RET_SUCCESS
;
350 * tst path - for debugging: test access with different functions
353 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
355 DIR Dir
; /* Directory object */
360 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
362 char * buf
= (char *) malloc(BUFFER_SIZE
);
364 printf_P(PSTR("tst: Out of Memory!\n"));
366 return CMD_RET_FAILURE
;
368 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
371 printf_P(PSTR("cwd: '%s'\n"), buf
);
376 return CMD_RET_FAILURE
;
384 printf_P(PSTR("arg: '%s'\n"), path
);
385 printf_P(PSTR("==== f_stat: "));
386 res
= f_stat(path
, &Finfo
);
389 print_dirent(&Finfo
);
392 printf_P(PSTR("==== f_findfirst: "));
393 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
396 print_dirent(&Finfo
);
400 printf_P(PSTR("==== f_opendir: "));
401 res
= f_opendir(&Dir
, path
);
405 return CMD_RET_SUCCESS
;
410 FRESULT
mkpath(TCHAR
*path
)
418 res
= f_stat (path
, &fd
)
420 p
= strchr(path
, ':');
421 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
424 while ((q
= strchr(p
, '/')) != NULL
) {
428 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
437 /******************************************************************************/
440 * These functions manipulate paths in PATH_T structures.
442 * They eliminate multiple slashes in paths when they notice them,
443 * and keep the path non-slash terminated.
445 * Both path_set() and path_append() return 0 if the requested name
450 static void strip_trailing_slash(PATH_T
*p
)
452 char *beg
= p
->p_path
;
453 char *end
= p
->p_end
;
454 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
456 if (beg
< end
&& *beg
== '/')
458 while (end
> beg
&& end
[-1] == '/')
465 * Move specified string into path. Convert "" to "." to handle BSD
466 * semantics for a null path. Strip trailing slashes.
469 path_set(PATH_T
*p
, char *string
)
471 if (strlen(string
) > MAX_PATHLEN
) {
472 err(PSTR("set: '%s': name too long"), string
);
476 (void)strcpy(p
->p_path
, string
);
477 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
479 if (p
->p_path
== p
->p_end
) {
484 strip_trailing_slash(p
);
489 * Append specified string to path, inserting '/' if necessary. Return a
490 * pointer to the old end of path for restoration.
493 path_append(PATH_T
*p
, char *name
, int len
)
501 /* The "+ 1" accounts for the '/' between old path and name. */
502 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
503 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
508 * This code should always be executed, since paths shouldn't
511 if (p
->p_end
[-1] != '/') {
516 (void)strncat(p
->p_end
, name
, len
);
520 strip_trailing_slash(p
);
525 * Restore path to previous value. (As returned by path_append.)
528 path_restore(PATH_T
*p
, char *old
)
535 * Return basename of path.
537 char *path_basename(PATH_T
*p
)
541 basename
= strrchr(p
->p_path
, '/');
542 return(basename
? basename
+ 1 : p
->p_path
);
546 static uint8_t flags
;
547 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
548 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
549 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
550 #define P_FLAG (1<<4) // preserve attributes and timestamps
551 #define R_FLAG (1<<0) // copy directories recursively
552 #define V_FLAG (1<<5) // explain what is being done
559 fr
= f_utime(to
.p_path
, fs
);
561 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
562 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
564 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
568 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
575 if (blockbuf
== NULL
) {
576 blockbuf_size
= get_freemem() / 512 * 512;
577 if (blockbuf_size
!= 0)
578 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
579 if (blockbuf
== NULL
) {
580 err(PSTR("Not enough memory!\n"));
585 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne
, blockbuf_size
);
586 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
589 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
590 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
595 * If the file exists and we're interactive, verify with the user.
598 if (flags
& N_FLAG
) {
600 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
603 } if (flags
& I_FLAG
) {
604 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
605 if (!confirm_yes()) {
610 if (flags
& F_FLAG
) {
611 /* Remove existing destination file name create a new file. */
612 f_chmod(to
.p_path
,0, AM_RDO
);
614 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
616 /* Overwrite existing destination file name. */
617 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
620 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
622 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
625 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
630 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
632 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
633 if (fr
|| wcount
< rcount
) {
634 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
639 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
642 if ((fr
= f_close(&to_fd
)) != FR_OK
)
643 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
652 static void copy_dir(void)
654 debug_cp("==== copy_dir()");
655 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
657 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
659 command_ret
= CMD_RET_FAILURE
;
662 static void copy_dir(void)
664 DIR Dir
; /* Directory object */
666 char *old_from
, *old_to
;
668 char *pattern
= {"*"};
670 debug_cp("==== copy_dir()");
671 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
674 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
675 res
== FR_OK
&& Finfo
.fname
[0];
676 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
)) {
678 if (!(Finfo
.fattrib
& AM_DIR
) &&
679 (old_from
= path_append(&from
, Finfo
.fname
, 0))) {
680 if ((old_to
= path_append(&to
, Finfo
.fname
, 0))) {
682 path_restore(&to
, old_to
);
684 path_restore(&from
, old_from
);
688 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
689 res
== FR_OK
&& Finfo
.fname
[0];
690 res
= f_findnext(&Dir
, &Finfo
)) {
692 if ((Finfo
.fattrib
& AM_DIR
) &&
693 (old_from
= path_append(&from
, Finfo
.fname
, 0))) {
694 if ((old_to
= path_append(&to
, Finfo
.fname
, 0))) {
696 path_restore(&to
, old_to
);
698 path_restore(&from
, old_from
);
704 static void copy_dir(void)
707 struct dirent
*dp
, **dir_list
;
709 char *old_from
, *old_to
;
711 debug_cp("==== copy_file(): dne: %u\n", dne
);
712 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
714 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
716 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
717 progname
, from
->p_path
);
718 command_ret
= CMD_RET_FAILURE
;
722 * Instead of handling directory entries in the order they appear
723 * on disk, do non-directory files before directory files.
724 * There are two reasons to do directories last. The first is
725 * efficiency. Files tend to be in the same cylinder group as
726 * their parent, whereas directories tend not to be. Copying files
727 * all at once reduces seeking. Second, deeply nested tree's
728 * could use up all the file descriptors if we didn't close one
729 * directory before recursivly starting on the next.
732 for (i
= 0; i
< dir_cnt
; ++i
) {
734 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
735 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
738 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
741 if (statfcn(from
->p_path
, &from_stat
) < 0) {
742 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
743 path_restore(&from
, old_from
);
746 if (S_ISDIR(from_stat
.st_mode
)) {
747 path_restore(&from
, old_from
);
750 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
752 path_restore(&to
, old_to
);
754 path_restore(&from
, old_from
);
755 done
: dir_list
[i
] = NULL
;
759 /* copy directories */
760 for (i
= 0; i
< dir_cnt
; ++i
) {
765 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
770 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
772 path_restore(&from
, old_from
);
777 path_restore(&from
, old_from
);
778 path_restore(&to
, old_to
);
785 * copy file or directory at "from" to "to".
789 FILINFO from_stat
, to_stat
;
793 debug_cp("==== copy()\n");
794 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
796 fr
= f_stat(from
.p_path
, &from_stat
);
798 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
802 /* not an error, but need to remember it happened */
803 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
806 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
807 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
808 to
.p_path
, from
.p_path
);
809 command_ret
= CMD_RET_FAILURE
;
815 if(from_stat
.fattrib
& AM_DIR
) {
816 if (!(flags
& R_FLAG
)) {
817 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
819 command_ret
= CMD_RET_FAILURE
;
824 * If the directory doesn't exist, create the new one.
826 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
827 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
831 else if (!(to_stat
.fattrib
& AM_DIR
)) {
832 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
841 copy_file(&from_stat
, dne
);
844 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
847 FRESULT fr
; /* Return value */
848 //DIR dj; /* Directory search object */
849 FILINFO to_stat
; /* File information */
854 command_ret
= CMD_RET_SUCCESS
;
860 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
863 tflags
&= ~(I_FLAG
| N_FLAG
);
867 tflags
&= ~(F_FLAG
| N_FLAG
);
871 tflags
&= ~(F_FLAG
| I_FLAG
);
884 return CMD_RET_USAGE
;
893 return CMD_RET_USAGE
;
896 from
= (PATH_T
*) malloc(sizeof(PATH_T
));
897 to
= (PATH_T
*) malloc(sizeof(PATH_T
));
898 if (from
== NULL
|| to
== NULL
) {
899 printf_P(PSTR("cp: Out of Memory!\n"));
900 command_ret
= CMD_RET_FAILURE
;
904 from
.p_end
= from
.p_path
; from
.p_path
[0] = '\0';
905 to
.p_end
= to
.p_path
; to
.p_path
[0] = '\0';
907 /* last argument is destination */
908 if (!path_set(&to
, argv
[--argc
]))
912 * Cp has two distinct cases:
914 * % cp [-rip] source target
915 * % cp [-rip] source1 ... directory
917 * In both cases, source can be either a file or a directory.
919 * In (1), the target becomes a copy of the source. That is, if the
920 * source is a file, the target will be a file, and likewise for
923 * In (2), the real target is not directory, but "directory/source".
926 fr
= f_stat(to
.p_path
, &to_stat
);
927 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr
, to_stat
.fattrib
, flags
);
928 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
930 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
931 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
932 command_ret
= CMD_RET_FAILURE
;
935 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
937 * Case (1). Target is not a directory.
940 err(PSTR("target '%s' is not a directory"), to
.p_path
);
941 //command_ret = CMD_RET_USAGE;
944 if (!path_set(&from
, *argv
)) {
945 command_ret
= CMD_RET_FAILURE
;
952 * Case (2). Target is a directory.
955 if (!path_set(&from
, *argv
))
957 if (!(old_to
= path_append(&to
, path_basename(&from
), -1)))
962 path_restore(&to
, old_to
);
976 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
985 /******************************************************************************/
988 * Work register for stat command
992 WORD AccFiles
, AccDirs
;
998 char *path
, /* Pointer to the working buffer with start path */
999 struct stat_dat_s
*statp
1007 res
= f_opendir(&dirs
, path
);
1011 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1012 statp
->Finfo
.fname
[0]) {
1013 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1015 fn
= statp
->Finfo
.fname
;
1016 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1019 strcpy(path
+i
+1, fn
);
1020 res
= scan_files(path
, statp
);
1025 //printf_P(PSTR("%s/%s\n"), path, fn);
1027 statp
->AccSize
+= statp
->Finfo
.fsize
;
1029 if (check_abort()) {
1041 * fatstat path - Show logical drive status
1044 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1051 struct stat_dat_s statp
;
1053 buf
= (char *) malloc(BUFFER_SIZE
);
1055 printf_P(PSTR("fat stat: Out of Memory!\n"));
1056 return CMD_RET_FAILURE
;
1061 res
= f_getfree(path
, &nfreeclst
, &fs
);
1065 "Bytes/Cluster: %lu\n"
1066 "Number of FATs: %u\n"
1067 "Root DIR entries: %u\n"
1068 "Sectors/FAT: %lu\n"
1069 "Number of clusters: %lu\n"
1070 "FAT start (lba): %lu\n"
1071 "DIR start (lba,cluster): %lu\n"
1072 "Data start (lba): %lu\n"),
1073 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1074 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1075 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1079 res
= f_getlabel(path
, buf
, &serial
);
1083 "Volume S/N: %04X-%04X\n"),
1084 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1088 my_puts_P(PSTR("\nCounting... "));
1089 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1092 res
= scan_files(buf
, &statp
);
1095 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1096 "%lu KB total disk space.\n%lu KB available.\n"),
1097 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1098 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1106 return CMD_RET_FAILURE
;
1108 return CMD_RET_SUCCESS
;
1112 * fatread/write - load binary file to/from a dos filesystem
1113 * read <d:/path/filename> <addr> [bytes [pos]]
1114 * write <d:/path/filename> <addr> <bytes>
1116 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1120 unsigned long bytes
;
1122 unsigned long bytes_rw
;
1124 bool dowrite
= (argv
[0][0] == 'w');
1125 FRESULT res
= FR_OK
;
1130 if (argc
< (dowrite
? 4 : 3))
1131 return CMD_RET_USAGE
;
1133 addr
= eval_arg(argv
[2], NULL
);
1134 if (addr
>= MAX_MEMORY
) {
1135 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1136 return CMD_RET_FAILURE
;
1139 bytes
= eval_arg(argv
[3], NULL
);
1143 pos
= eval_arg(argv
[4], NULL
);
1147 if (addr
+ bytes
> MAX_MEMORY
)
1148 bytes
= MAX_MEMORY
- addr
;
1150 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1151 if (buffer
== NULL
) {
1152 printf_P(PSTR("fatstat: Out of Memory!\n"));
1154 return CMD_RET_FAILURE
;
1158 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1162 res
= f_lseek(&File
, pos
);
1165 timer
= get_timer(0);
1167 unsigned int cnt
, br
;
1169 if (bytes
>= BUFFER_SIZE
) {
1171 bytes
-= BUFFER_SIZE
;
1173 cnt
= bytes
; bytes
= 0;
1176 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1180 z80_read_block(buffer
, addr
, cnt
);
1181 z80_bus_cmd(Release
);
1182 res
= f_write(&File
, buffer
, cnt
, &br
);
1186 res
= f_read(&File
, buffer
, cnt
, &br
);
1189 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1193 z80_write_block(buffer
, addr
, br
);
1194 z80_bus_cmd(Release
);
1201 printf_P(PSTR("Disk full?\n"));
1208 FRESULT fr
= f_close(&File
);
1211 timer
= get_timer(timer
);
1212 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1213 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1221 my_puts_P(PSTR("Bus timeout\n"));
1225 return CMD_RET_FAILURE
;
1227 return CMD_RET_SUCCESS
;
1231 * command table for fat subcommands
1234 cmd_tbl_t cmd_tbl_fat
[] = {
1236 stat
, 2, CTBL_RPT
, do_stat
,
1237 "Show logical drive status",
1241 pwd
, 2, CTBL_RPT
, do_pwd
,
1242 "Print name of current/working directory",
1247 "Change the current/working directory.",
1251 ls
, 2, CTBL_RPT
, do_ls
,
1252 "Directory listing",
1256 tst
, 2, 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
;