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
42 * Multible (fat) partitions per physical drive are not supported,
43 * but we have up to 2 sdcard slots.
48 command_ret_t command_ret
;
50 void setup_fatfs(void)
52 f_mount(&FatFs0
, "0:", 0);
53 f_mount(&FatFs1
, "1:", 0);
56 DWORD
get_fattime (void)
62 gmtime_r(&timer
, &tm_timer
);
64 return fatfs_time(&tm_timer
);
68 static bool check_abort(void)
73 printf_P(PSTR("Abort\n"));
79 static const FLASH
char * const FLASH rc_strings
[] = {
82 FSTR("internal error"),
89 FSTR("invalid object"),
90 FSTR("write protected"),
91 FSTR("invalid drive"),
93 FSTR("no file system"),
97 FSTR("not enough core"),
98 FSTR("too many open files"),
99 FSTR("invalid parameter")
102 static const FLASH
char * const FLASH rc_names
[] = {
109 FSTR("INVALID_NAME"),
112 FSTR("INVALID_OBJECT"),
113 FSTR("WRITE_PROTECTED"),
114 FSTR("INVALID_DRIVE"),
116 FSTR("NO_FILE_SYSTEM"),
117 FSTR("MKFS_ABORTED"),
120 FSTR("NOT_ENOUGH_CORE"),
121 FSTR("TOO_MANY_OPEN_FILES"),
122 FSTR("INVALID_PARAMETER")
126 void put_rc (FRESULT rc
)
129 printf_P(PSTR("rc=%u FR_"), rc
);
130 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
131 my_puts_P(PSTR("\n"));
133 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
134 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
138 const FLASH
char * rctostr(FRESULT rc
)
140 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
143 void err(const char *fmt
, ...)
147 // (void)fprintf(stderr, "%s: ", progname);
148 (void)vfprintf_P(stdout
, fmt
, ap
);
150 (void)printf_P(PSTR("\n"));
152 command_ret
= CMD_RET_FAILURE
;
157 static void swirl(void)
159 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
160 static uint_fast8_t cnt
;
161 static uint32_t tstamp
;
163 if (get_timer(0) > tstamp
) {
164 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
165 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
166 tstamp
= get_timer(0) + 250;
171 * pwd - Print current directory of the current drive.
174 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
179 buf
= (char *) malloc(BUFFER_SIZE
);
181 printf_P(PSTR("pwd: Out of Memory!\n"));
183 return CMD_RET_FAILURE
;
186 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
194 return CMD_RET_FAILURE
;
196 return CMD_RET_SUCCESS
;
201 * cd - Change the current/working directory.
204 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
210 arg
= getenv_str(PSTR(ENV_HOME
));
212 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
213 return CMD_RET_FAILURE
;
223 res
= f_chdrive(drv
);
231 return CMD_RET_FAILURE
;
233 return CMD_RET_SUCCESS
;
236 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
239 * Remove trailing slashes,
240 * but keep a leading slash (absolute path)
242 void strip_trailing_slash_relpath(char *p
)
246 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
250 if (n
>= 1 && p
[0] == '/') {
254 while (n
-- != 0 && p
[n
] == '/')
258 int print_dirent(FILINFO
*f
)
260 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
261 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
262 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
263 (f
->fattrib
& AM_HID
) ? 'H' : '-',
264 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
265 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
266 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
267 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
272 * ls path - Directory listing
275 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
278 DIR Dir
; /* Directory object */
287 strip_trailing_slash_relpath(path
);
289 char *p
= strrchr(path
, '/');
295 if ((*q
++ & 0x38) == '0' && *q
++ == ':')
300 if (strpbrk_P(p
, PSTR("*?")) ||
301 (f_stat(path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
305 pattern
= strdup("*");
306 strip_trailing_slash_relpath(path
);
308 debug_ls("==== path: '%s', pattern: '%s'\n", path
? path
: "<NULL>", pattern
? pattern
: "<NULL>");
311 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
312 while (res
== FR_OK
&& Finfo
.fname
[0]) {
313 if (Finfo
.fattrib
& AM_DIR
) {
316 s1
++; p1
+= Finfo
.fsize
;
318 print_dirent(&Finfo
);
321 res
= f_findnext(&Dir
, &Finfo
);
327 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
328 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
329 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
334 return CMD_RET_FAILURE
;
337 return CMD_RET_SUCCESS
;
341 * tst path - for debugging: test access with different functions
344 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
346 DIR Dir
; /* Directory object */
351 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
353 char * buf
= (char *) malloc(BUFFER_SIZE
);
355 printf_P(PSTR("tst: Out of Memory!\n"));
357 return CMD_RET_FAILURE
;
359 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
362 printf_P(PSTR("cwd: '%s'\n"), buf
);
367 return CMD_RET_FAILURE
;
375 printf_P(PSTR("arg: '%s'\n"), path
);
376 printf_P(PSTR("==== f_stat: "));
377 res
= f_stat(path
, &Finfo
);
380 print_dirent(&Finfo
);
383 printf_P(PSTR("==== f_findfirst: "));
384 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
387 print_dirent(&Finfo
);
391 printf_P(PSTR("==== f_opendir: "));
392 res
= f_opendir(&Dir
, path
);
396 return CMD_RET_SUCCESS
;
401 FRESULT
mkpath(TCHAR
*path
)
409 res
= f_stat (path
, &fd
)
411 p
= strchr(path
, ':');
412 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
415 while ((q
= strchr(p
, '/')) != NULL
) {
419 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
428 /******************************************************************************/
431 * These functions manipulate paths in PATH_T structures.
433 * They eliminate multiple slashes in paths when they notice them,
434 * and keep the path non-slash terminated.
436 * Both path_set() and path_append() return 0 if the requested name
442 char *p_end
; /* pointer to NULL at end of path */
443 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
446 static void strip_trailing_slash(PATH_T
*p
)
448 char *beg
= p
->p_path
;
449 char *end
= p
->p_end
;
450 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
452 if (beg
< end
&& *beg
== '/')
454 while (end
> beg
&& end
[-1] == '/')
461 * Move specified string into path. Convert "" to "." to handle BSD
462 * semantics for a null path. Strip trailing slashes.
465 path_set(PATH_T
*p
, char *string
)
467 if (strlen(string
) > MAX_PATHLEN
) {
468 err(PSTR("set: '%s': name too long"), string
);
472 (void)strcpy(p
->p_path
, string
);
473 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
475 if (p
->p_path
== p
->p_end
) {
480 strip_trailing_slash(p
);
485 * Append specified string to path, inserting '/' if necessary. Return a
486 * pointer to the old end of path for restoration.
489 path_append(PATH_T
*p
, char *name
, int len
)
497 /* The "+ 1" accounts for the '/' between old path and name. */
498 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
499 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
504 * This code should always be executed, since paths shouldn't
507 if (p
->p_end
[-1] != '/') {
512 (void)strncat(p
->p_end
, name
, len
);
516 strip_trailing_slash(p
);
521 * Restore path to previous value. (As returned by path_append.)
524 path_restore(PATH_T
*p
, char *old
)
531 * Return basename of path.
533 char *path_basename(PATH_T
*p
)
537 basename
= strrchr(p
->p_path
, '/');
538 return(basename
? basename
+ 1 : p
->p_path
);
542 static uint8_t flags
;
545 //PATH_T *from = &from_static;
546 //PATH_T *to = &to_static;
547 static uint8_t *blockbuf
;
548 static int blockbuf_size
;
550 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
551 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
552 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
553 #define P_FLAG (1<<4) // preserve attributes and timestamps
554 #define R_FLAG (1<<0) // copy directories recursively
555 #define V_FLAG (1<<5) // explain what is being done
562 fr
= f_utime(to
.p_path
, fs
);
564 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
565 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
567 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
571 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
578 if (blockbuf
== NULL
) {
579 blockbuf_size
= get_freemem() / 512 * 512;
580 if (blockbuf_size
!= 0)
581 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
582 if (blockbuf
== NULL
) {
583 err(PSTR("Not enough memory!\n"));
588 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne
, blockbuf_size
);
589 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
592 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
593 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
598 * If the file exists and we're interactive, verify with the user.
601 if (flags
& N_FLAG
) {
603 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
606 } if (flags
& I_FLAG
) {
607 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
608 if (!confirm_yes()) {
613 if (flags
& F_FLAG
) {
614 /* Remove existing destination file name create a new file. */
615 f_chmod(to
.p_path
,0, AM_RDO
);
617 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
619 /* Overwrite existing destination file name. */
620 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
623 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
625 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
628 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
633 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
635 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
636 if (fr
|| wcount
< rcount
) {
637 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
642 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
645 if ((fr
= f_close(&to_fd
)) != FR_OK
)
646 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
655 static void copy_dir(void)
657 debug_cp("==== copy_dir()");
658 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
660 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
662 command_ret
= CMD_RET_FAILURE
;
665 static void copy_dir(void)
667 DIR Dir
; /* Directory object */
669 char *old_from
, *old_to
;
671 char *pattern
= {"*"};
673 debug_cp("==== copy_dir()");
674 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
677 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
678 res
== FR_OK
&& Finfo
.fname
[0];
679 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
)) {
681 if (!(Finfo
.fattrib
& AM_DIR
) &&
682 (old_from
= path_append(&from
, Finfo
.fname
, 0))) {
683 if ((old_to
= path_append(&to
, Finfo
.fname
, 0))) {
685 path_restore(&to
, old_to
);
687 path_restore(&from
, old_from
);
691 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
692 res
== FR_OK
&& Finfo
.fname
[0];
693 res
= f_findnext(&Dir
, &Finfo
)) {
695 if ((Finfo
.fattrib
& AM_DIR
) &&
696 (old_from
= path_append(&from
, Finfo
.fname
, 0))) {
697 if ((old_to
= path_append(&to
, Finfo
.fname
, 0))) {
699 path_restore(&to
, old_to
);
701 path_restore(&from
, old_from
);
707 static void copy_dir(void)
710 struct dirent
*dp
, **dir_list
;
712 char *old_from
, *old_to
;
714 debug_cp("==== copy_file(): dne: %u\n", dne
);
715 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
717 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
719 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
720 progname
, from
->p_path
);
721 command_ret
= CMD_RET_FAILURE
;
725 * Instead of handling directory entries in the order they appear
726 * on disk, do non-directory files before directory files.
727 * There are two reasons to do directories last. The first is
728 * efficiency. Files tend to be in the same cylinder group as
729 * their parent, whereas directories tend not to be. Copying files
730 * all at once reduces seeking. Second, deeply nested tree's
731 * could use up all the file descriptors if we didn't close one
732 * directory before recursivly starting on the next.
735 for (i
= 0; i
< dir_cnt
; ++i
) {
737 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
738 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
741 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
744 if (statfcn(from
->p_path
, &from_stat
) < 0) {
745 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
746 path_restore(&from
, old_from
);
749 if (S_ISDIR(from_stat
.st_mode
)) {
750 path_restore(&from
, old_from
);
753 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
755 path_restore(&to
, old_to
);
757 path_restore(&from
, old_from
);
758 done
: dir_list
[i
] = NULL
;
762 /* copy directories */
763 for (i
= 0; i
< dir_cnt
; ++i
) {
768 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
773 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
775 path_restore(&from
, old_from
);
780 path_restore(&from
, old_from
);
781 path_restore(&to
, old_to
);
788 * copy file or directory at "from" to "to".
792 FILINFO from_stat
, to_stat
;
796 debug_cp("==== copy()\n");
797 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
799 fr
= f_stat(from
.p_path
, &from_stat
);
801 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
805 /* not an error, but need to remember it happened */
806 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
809 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
810 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
811 to
.p_path
, from
.p_path
);
812 command_ret
= CMD_RET_FAILURE
;
818 if(from_stat
.fattrib
& AM_DIR
) {
819 if (!(flags
& R_FLAG
)) {
820 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
822 command_ret
= CMD_RET_FAILURE
;
827 * If the directory doesn't exist, create the new one.
829 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
830 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
834 else if (!(to_stat
.fattrib
& AM_DIR
)) {
835 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
844 copy_file(&from_stat
, dne
);
847 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
850 FRESULT fr
; /* Return value */
851 //DIR dj; /* Directory search object */
852 FILINFO to_stat
; /* File information */
857 command_ret
= CMD_RET_SUCCESS
;
863 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
866 tflags
&= ~(I_FLAG
| N_FLAG
);
870 tflags
&= ~(F_FLAG
| N_FLAG
);
874 tflags
&= ~(F_FLAG
| I_FLAG
);
887 return CMD_RET_USAGE
;
896 return CMD_RET_USAGE
;
899 from
= (PATH_T
*) malloc(sizeof(PATH_T
));
900 to
= (PATH_T
*) malloc(sizeof(PATH_T
));
901 if (from
== NULL
|| to
== NULL
) {
902 printf_P(PSTR("cp: Out of Memory!\n"));
903 command_ret
= CMD_RET_FAILURE
;
907 from
.p_end
= from
.p_path
; from
.p_path
[0] = '\0';
908 to
.p_end
= to
.p_path
; to
.p_path
[0] = '\0';
910 /* last argument is destination */
911 if (!path_set(&to
, argv
[--argc
]))
915 * Cp has two distinct cases:
917 * % cp [-rip] source target
918 * % cp [-rip] source1 ... directory
920 * In both cases, source can be either a file or a directory.
922 * In (1), the target becomes a copy of the source. That is, if the
923 * source is a file, the target will be a file, and likewise for
926 * In (2), the real target is not directory, but "directory/source".
929 fr
= f_stat(to
.p_path
, &to_stat
);
930 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr
, to_stat
.fattrib
, flags
);
931 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
933 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
934 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
935 command_ret
= CMD_RET_FAILURE
;
938 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
940 * Case (1). Target is not a directory.
943 err(PSTR("target '%s' is not a directory"), to
.p_path
);
944 //command_ret = CMD_RET_USAGE;
947 if (!path_set(&from
, *argv
)) {
948 command_ret
= CMD_RET_FAILURE
;
955 * Case (2). Target is a directory.
958 if (!path_set(&from
, *argv
))
960 if (!(old_to
= path_append(&to
, path_basename(&from
), -1)))
965 path_restore(&to
, old_to
);
979 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
988 /******************************************************************************/
991 * Work register for stat command
995 WORD AccFiles
, AccDirs
;
1000 FRESULT
scan_files (
1001 char *path
, /* Pointer to the working buffer with start path */
1002 struct stat_dat_s
*statp
1010 res
= f_opendir(&dirs
, path
);
1014 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1015 statp
->Finfo
.fname
[0]) {
1016 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1018 fn
= statp
->Finfo
.fname
;
1019 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1022 strcpy(path
+i
+1, fn
);
1023 res
= scan_files(path
, statp
);
1028 //printf_P(PSTR("%s/%s\n"), path, fn);
1030 statp
->AccSize
+= statp
->Finfo
.fsize
;
1032 if (check_abort()) {
1044 * fatstat path - Show logical drive status
1047 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1054 struct stat_dat_s statp
;
1056 buf
= (char *) malloc(BUFFER_SIZE
);
1058 printf_P(PSTR("fat stat: Out of Memory!\n"));
1059 return CMD_RET_FAILURE
;
1064 res
= f_getfree(path
, &nfreeclst
, &fs
);
1068 "Bytes/Cluster: %lu\n"
1069 "Number of FATs: %u\n"
1070 "Root DIR entries: %u\n"
1071 "Sectors/FAT: %lu\n"
1072 "Number of clusters: %lu\n"
1073 "FAT start (lba): %lu\n"
1074 "DIR start (lba,cluster): %lu\n"
1075 "Data start (lba): %lu\n"),
1076 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1077 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1078 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1082 res
= f_getlabel(path
, buf
, &serial
);
1086 "Volume S/N: %04X-%04X\n"),
1087 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1091 my_puts_P(PSTR("\nCounting... "));
1092 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1095 res
= scan_files(buf
, &statp
);
1098 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1099 "%lu KB total disk space.\n%lu KB available.\n"),
1100 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1101 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1109 return CMD_RET_FAILURE
;
1111 return CMD_RET_SUCCESS
;
1115 * fatread/write - load binary file to/from a dos filesystem
1116 * read <d:/path/filename> <addr> [bytes [pos]]
1117 * write <d:/path/filename> <addr> <bytes>
1119 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1123 unsigned long bytes
;
1125 unsigned long bytes_rw
;
1127 bool dowrite
= (argv
[0][0] == 'w');
1128 FRESULT res
= FR_OK
;
1133 if (argc
< (dowrite
? 4 : 3))
1134 return CMD_RET_USAGE
;
1136 addr
= eval_arg(argv
[2], NULL
);
1137 if (addr
>= MAX_MEMORY
) {
1138 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1139 return CMD_RET_FAILURE
;
1142 bytes
= eval_arg(argv
[3], NULL
);
1146 pos
= eval_arg(argv
[4], NULL
);
1150 if (addr
+ bytes
> MAX_MEMORY
)
1151 bytes
= MAX_MEMORY
- addr
;
1153 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1154 if (buffer
== NULL
) {
1155 printf_P(PSTR("fatstat: Out of Memory!\n"));
1157 return CMD_RET_FAILURE
;
1161 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1165 res
= f_lseek(&File
, pos
);
1168 timer
= get_timer(0);
1170 unsigned int cnt
, br
;
1172 if (bytes
>= BUFFER_SIZE
) {
1174 bytes
-= BUFFER_SIZE
;
1176 cnt
= bytes
; bytes
= 0;
1179 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1183 z80_read_block(buffer
, addr
, cnt
);
1184 z80_bus_cmd(Release
);
1185 res
= f_write(&File
, buffer
, cnt
, &br
);
1189 res
= f_read(&File
, buffer
, cnt
, &br
);
1192 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1196 z80_write_block(buffer
, addr
, br
);
1197 z80_bus_cmd(Release
);
1204 printf_P(PSTR("Disk full?\n"));
1211 FRESULT fr
= f_close(&File
);
1214 timer
= get_timer(timer
);
1215 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1216 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1224 my_puts_P(PSTR("Bus timeout\n"));
1228 return CMD_RET_FAILURE
;
1230 return CMD_RET_SUCCESS
;
1234 * command table for fat subcommands
1237 cmd_tbl_t cmd_tbl_fat
[] = {
1239 stat
, 2, CTBL_RPT
, do_stat
,
1240 "Show logical drive status",
1244 pwd
, 2, CTBL_RPT
, do_pwd
,
1245 "Print name of current/working directory",
1250 "Change the current/working directory.",
1254 ls
, 2, CTBL_RPT
, do_ls
,
1255 "Directory listing",
1259 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1260 "FatFS test function",
1265 "load binary file from a dos filesystem",
1266 "<d:/path/filename> <addr> [bytes [pos]]\n"
1267 " - Load binary file 'path/filename' on logical drive 'd'\n"
1268 " to address 'addr' from dos filesystem.\n"
1269 " 'pos' gives the file position to start loading from.\n"
1270 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1271 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1272 " the load stops on end of file."
1276 "write file into a dos filesystem",
1277 "<d:/path/filename> <addr> <bytes>\n"
1278 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1279 " starting at address 'addr'.\n"
1283 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1285 "[-f | -i | -n] [-prv] source_file target_file\n"
1286 // "[-f | -i | -n] [-prv] source_file target_file\n"
1287 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1288 " -f overwrite existing file ignoring write protection\n"
1289 " this option is ignored when the -n option is also used\n"
1290 " -i prompt before overwrite (overrides a previous -n option)\n"
1291 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1292 " -p preserve attributes and timestamps\n"
1293 " -r copy directories recursively\n"
1294 " -v explain what is being done\n"
1298 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1299 "Print sub command description/usage",
1301 " - print brief description of all sub commands\n"
1302 "fat help command ...\n"
1303 " - print detailed usage of sub cmd 'command'"
1306 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1307 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1308 FSTR("Alias for 'help'"),
1309 #ifdef CONFIG_SYS_LONGHELP
1311 #endif /* CONFIG_SYS_LONGHELP */
1313 #ifdef CONFIG_AUTO_COMPLETE
1317 /* Mark end of table */
1318 CMD_TBL_END(cmd_tbl_fat
)
1322 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1324 puts_P(PSTR("Huch?"));
1326 return CMD_RET_USAGE
;