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_path
[0] = '\0'; from
.p_end
= from
.p_path
;
181 to
.p_path
[0] = '\0'; to
.p_end
= to
.p_path
;
184 static void strip_trailing_slash(PATH_T
*p
)
186 char *beg
= p
->p_path
;
187 char *end
= p
->p_end
;
188 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
190 if (beg
< end
&& *beg
== '/')
192 while (end
> beg
&& end
[-1] == '/')
199 * Move specified string into path. Convert "" to "." to handle BSD
200 * semantics for a null path. Strip trailing slashes.
203 path_set(PATH_T
*p
, char *string
)
205 if (strlen(string
) > MAX_PATHLEN
) {
206 err(PSTR("set: '%s': name too long"), string
);
210 (void)strcpy(p
->p_path
, string
);
211 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
213 if (p
->p_path
== p
->p_end
) {
218 strip_trailing_slash(p
);
223 * Append specified string to path, inserting '/' if necessary. Return a
224 * pointer to the old end of path for restoration.
227 path_append(PATH_T
*p
, char *name
)
229 char *old
= p
->p_end
;
230 int len
= strlen(name
);
232 /* The "+ 1" accounts for the '/' between old path and name. */
233 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
234 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
239 * This code should always be executed, since paths shouldn't
242 if (p
->p_end
[-1] != '/') {
247 (void)strncat(p
->p_end
, name
, len
);
251 strip_trailing_slash(p
);
256 * Restore path to previous value. (As returned by path_append.)
259 path_restore(PATH_T
*p
, char *old
)
266 * Return basename of path.
268 char *path_basename(PATH_T
*p
)
270 char *basename
= strrchr(p
->p_path
, '/');
275 basename
= p
->p_path
;
276 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
283 char *path_basename_pattern(PATH_T
*p
)
285 char *pattern
= path_basename(p
);
286 if (strpbrk_P(pattern
, PSTR("*?"))) {
287 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
290 //p->p_pattern = p->p_end + 1;
291 pattern
= p
->p_end
+ 1;
298 static void swirl(void)
300 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
301 static uint_fast8_t cnt
;
302 static uint32_t tstamp
;
304 if (get_timer(0) > tstamp
) {
305 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
306 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
307 tstamp
= get_timer(0) + 250;
312 * pwd - Print current directory of the current drive.
315 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
320 buf
= (char *) malloc(BUFFER_SIZE
);
322 printf_P(PSTR("pwd: Out of Memory!\n"));
324 return CMD_RET_FAILURE
;
327 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
335 return CMD_RET_FAILURE
;
337 return CMD_RET_SUCCESS
;
342 * cd - Change the current/working directory.
345 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
351 arg
= getenv_str(PSTR(ENV_HOME
));
353 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
354 return CMD_RET_FAILURE
;
364 res
= f_chdrive(drv
);
372 return CMD_RET_FAILURE
;
374 return CMD_RET_SUCCESS
;
379 * Remove trailing slashes,
380 * but keep a leading slash (absolute path)
382 void strip_trailing_slash_relpath(char *p
)
386 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
390 if (n
>= 1 && p
[0] == '/') {
394 while (n
-- != 0 && p
[n
] == '/')
399 int print_dirent(FILINFO
*f
)
401 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
402 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
403 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
404 (f
->fattrib
& AM_HID
) ? 'H' : '-',
405 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
406 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
407 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
408 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
413 * ls path - Directory listing
416 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
419 DIR Dir
; /* Directory object */
427 if (!path_set(&from
, argv
[1])) {
431 char *pattern
= path_basename_pattern(&from
);
433 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
436 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
437 while (res
== FR_OK
&& Finfo
.fname
[0]) {
438 if (Finfo
.fattrib
& AM_DIR
) {
441 s1
++; p1
+= Finfo
.fsize
;
443 print_dirent(&Finfo
);
446 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
;
523 /******************************************************************************/
525 static uint8_t flags
;
526 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
527 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
528 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
529 #define P_FLAG (1<<4) // preserve attributes and timestamps
530 #define R_FLAG (1<<0) // copy directories recursively
531 #define V_FLAG (1<<5) // explain what is being done
538 fr
= f_utime(to
.p_path
, fs
);
540 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
541 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
543 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
547 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
554 if (blockbuf
== NULL
) {
555 blockbuf_size
= get_freemem() / 512 * 512;
556 if (blockbuf_size
!= 0)
557 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
558 if (blockbuf
== NULL
) {
559 err(PSTR("Not enough memory!\n"));
564 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
565 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
568 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
569 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
574 * If the file exists and we're interactive, verify with the user.
577 if (flags
& N_FLAG
) {
579 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
582 } if (flags
& I_FLAG
) {
583 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
584 if (!confirm_yes()) {
589 if (flags
& F_FLAG
) {
590 /* Remove existing destination file name create a new file. */
591 f_chmod(to
.p_path
,0, AM_RDO
);
593 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
595 /* Overwrite existing destination file name. */
596 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
599 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
601 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
604 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
609 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
611 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
612 if (fr
|| wcount
< rcount
) {
613 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
618 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
621 if ((fr
= f_close(&to_fd
)) != FR_OK
)
622 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
631 static void copy_dir(void)
633 debug_cp("==== copy_dir()");
634 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
636 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
638 command_ret
= CMD_RET_FAILURE
;
641 static void copy_dir(void)
643 DIR Dir
; /* Directory object */
645 char *old_from
, *old_to
;
647 char *pattern
= {"*"};
649 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
650 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
653 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
654 res
== FR_OK
&& Finfo
.fname
[0];
655 res
= f_findnext(&Dir
, &Finfo
)) {
657 if (!(Finfo
.fattrib
& AM_DIR
) &&
658 (old_from
= path_append(&from
, Finfo
.fname
))) {
659 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
661 path_restore(&to
, old_to
);
663 path_restore(&from
, old_from
);
667 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
668 res
== FR_OK
&& Finfo
.fname
[0];
669 res
= f_findnext(&Dir
, &Finfo
)) {
671 if ((Finfo
.fattrib
& AM_DIR
) &&
672 (old_from
= path_append(&from
, Finfo
.fname
))) {
673 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
675 path_restore(&to
, old_to
);
677 path_restore(&from
, old_from
);
683 static void copy_dir(void)
686 struct dirent
*dp
, **dir_list
;
688 char *old_from
, *old_to
;
690 debug_cp("==== copy_file(): dne: %u\n", dne
, );
691 debug_cp(" from:'%s' to:'%s'\n", from
->p_path
, to
.p_path
);
693 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
695 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
696 progname
, from
->p_path
);
697 command_ret
= CMD_RET_FAILURE
;
701 * Instead of handling directory entries in the order they appear
702 * on disk, do non-directory files before directory files.
703 * There are two reasons to do directories last. The first is
704 * efficiency. Files tend to be in the same cylinder group as
705 * their parent, whereas directories tend not to be. Copying files
706 * all at once reduces seeking. Second, deeply nested tree's
707 * could use up all the file descriptors if we didn't close one
708 * directory before recursivly starting on the next.
711 for (i
= 0; i
< dir_cnt
; ++i
) {
713 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
714 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
717 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
720 if (statfcn(from
->p_path
, &from_stat
) < 0) {
721 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
722 path_restore(&from
, old_from
);
725 if (S_ISDIR(from_stat
.st_mode
)) {
726 path_restore(&from
, old_from
);
729 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
731 path_restore(&to
, old_to
);
733 path_restore(&from
, old_from
);
734 done
: dir_list
[i
] = NULL
;
738 /* copy directories */
739 for (i
= 0; i
< dir_cnt
; ++i
) {
744 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
749 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
751 path_restore(&from
, old_from
);
756 path_restore(&from
, old_from
);
757 path_restore(&to
, old_to
);
764 * copy file or directory at "from" to "to".
768 FILINFO from_stat
, to_stat
;
772 debug_cp("==== copy(); freemem: %u\n", get_freemem());
773 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
775 fr
= f_stat(from
.p_path
, &from_stat
);
777 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
781 /* not an error, but need to remember it happened */
782 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
785 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
786 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
787 to
.p_path
, from
.p_path
);
788 command_ret
= CMD_RET_FAILURE
;
794 if(from_stat
.fattrib
& AM_DIR
) {
795 if (!(flags
& R_FLAG
)) {
796 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
798 command_ret
= CMD_RET_FAILURE
;
803 * If the directory doesn't exist, create the new one.
805 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
806 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
810 else if (!(to_stat
.fattrib
& AM_DIR
)) {
811 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
820 copy_file(&from_stat
, dne
);
823 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
826 FRESULT fr
; /* Return value */
827 //DIR dj; /* Directory search object */
828 FILINFO to_stat
; /* File information */
833 command_ret
= CMD_RET_SUCCESS
;
839 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
842 tflags
&= ~(I_FLAG
| N_FLAG
);
846 tflags
&= ~(F_FLAG
| N_FLAG
);
850 tflags
&= ~(F_FLAG
| I_FLAG
);
863 return CMD_RET_USAGE
;
872 return CMD_RET_USAGE
;
876 /* last argument is destination */
877 if (!path_set(&to
, argv
[--argc
]))
881 * Cp has two distinct cases:
883 * % cp [-rip] source target
884 * % cp [-rip] source1 ... directory
886 * In both cases, source can be either a file or a directory.
888 * In (1), the target becomes a copy of the source. That is, if the
889 * source is a file, the target will be a file, and likewise for
892 * In (2), the real target is not directory, but "directory/source".
895 fr
= f_stat(to
.p_path
, &to_stat
);
896 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
897 fr
, to_stat
.fattrib
, flags
, get_freemem());
898 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
900 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
901 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
902 command_ret
= CMD_RET_FAILURE
;
905 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
907 * Case (1). Target is not a directory.
910 err(PSTR("target '%s' is not a directory"), to
.p_path
);
911 //command_ret = CMD_RET_USAGE;
914 if (!path_set(&from
, *argv
)) {
915 command_ret
= CMD_RET_FAILURE
;
922 * Case (2). Target is a directory.
925 if (!path_set(&from
, *argv
))
927 if (!(old_to
= path_append(&to
, path_basename(&from
))))
932 path_restore(&to
, old_to
);
946 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
955 /******************************************************************************/
958 * Work register for stat command
962 WORD AccFiles
, AccDirs
;
968 char *path
, /* Pointer to the working buffer with start path */
969 struct stat_dat_s
*statp
977 res
= f_opendir(&dirs
, path
);
981 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
982 statp
->Finfo
.fname
[0]) {
983 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
985 fn
= statp
->Finfo
.fname
;
986 if (statp
->Finfo
.fattrib
& AM_DIR
) {
989 strcpy(path
+i
+1, fn
);
990 res
= scan_files(path
, statp
);
995 //printf_P(PSTR("%s/%s\n"), path, fn);
997 statp
->AccSize
+= statp
->Finfo
.fsize
;
1011 * fatstat path - Show logical drive status
1014 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1021 struct stat_dat_s statp
;
1023 buf
= (char *) malloc(BUFFER_SIZE
);
1025 printf_P(PSTR("fat stat: Out of Memory!\n"));
1026 return CMD_RET_FAILURE
;
1031 res
= f_getfree(path
, &nfreeclst
, &fs
);
1035 "Bytes/Cluster: %lu\n"
1036 "Number of FATs: %u\n"
1037 "Root DIR entries: %u\n"
1038 "Sectors/FAT: %lu\n"
1039 "Number of clusters: %lu\n"
1040 "FAT start (lba): %lu\n"
1041 "DIR start (lba,cluster): %lu\n"
1042 "Data start (lba): %lu\n"),
1043 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1044 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1045 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1049 res
= f_getlabel(path
, buf
, &serial
);
1053 "Volume S/N: %04X-%04X\n"),
1054 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1058 my_puts_P(PSTR("\nCounting... "));
1059 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1062 res
= scan_files(buf
, &statp
);
1065 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1066 "%lu KB total disk space.\n%lu KB available.\n"),
1067 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1068 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1076 return CMD_RET_FAILURE
;
1078 return CMD_RET_SUCCESS
;
1082 * fatread/write - load binary file to/from a dos filesystem
1083 * read <d:/path/filename> <addr> [bytes [pos]]
1084 * write <d:/path/filename> <addr> <bytes>
1086 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1090 unsigned long bytes
;
1092 unsigned long bytes_rw
;
1094 bool dowrite
= (argv
[0][0] == 'w');
1095 FRESULT res
= FR_OK
;
1100 if (argc
< (dowrite
? 4 : 3))
1101 return CMD_RET_USAGE
;
1103 addr
= eval_arg(argv
[2], NULL
);
1104 if (addr
>= MAX_MEMORY
) {
1105 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1106 return CMD_RET_FAILURE
;
1109 bytes
= eval_arg(argv
[3], NULL
);
1113 pos
= eval_arg(argv
[4], NULL
);
1117 if (addr
+ bytes
> MAX_MEMORY
)
1118 bytes
= MAX_MEMORY
- addr
;
1120 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1121 if (buffer
== NULL
) {
1122 printf_P(PSTR("fatstat: Out of Memory!\n"));
1124 return CMD_RET_FAILURE
;
1128 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1132 res
= f_lseek(&File
, pos
);
1135 timer
= get_timer(0);
1137 unsigned int cnt
, br
;
1139 if (bytes
>= BUFFER_SIZE
) {
1141 bytes
-= BUFFER_SIZE
;
1143 cnt
= bytes
; bytes
= 0;
1146 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1150 z80_read_block(buffer
, addr
, cnt
);
1151 z80_bus_cmd(Release
);
1152 res
= f_write(&File
, buffer
, cnt
, &br
);
1156 res
= f_read(&File
, buffer
, cnt
, &br
);
1159 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1163 z80_write_block(buffer
, addr
, br
);
1164 z80_bus_cmd(Release
);
1171 printf_P(PSTR("Disk full?\n"));
1178 FRESULT fr
= f_close(&File
);
1181 timer
= get_timer(timer
);
1182 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1183 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1191 my_puts_P(PSTR("Bus timeout\n"));
1195 return CMD_RET_FAILURE
;
1197 return CMD_RET_SUCCESS
;
1201 * command table for fat subcommands
1204 cmd_tbl_t cmd_tbl_fat
[] = {
1206 stat
, 2, CTBL_RPT
, do_stat
,
1207 "Show logical drive status",
1211 pwd
, 2, CTBL_RPT
, do_pwd
,
1212 "Print name of current/working directory",
1217 "Change the current/working directory.",
1221 ls
, 2, CTBL_RPT
, do_ls
,
1222 "Directory listing",
1226 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1227 "FatFS test function",
1232 "load binary file from a dos filesystem",
1233 "<d:/path/filename> <addr> [bytes [pos]]\n"
1234 " - Load binary file 'path/filename' on logical drive 'd'\n"
1235 " to address 'addr' from dos filesystem.\n"
1236 " 'pos' gives the file position to start loading from.\n"
1237 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1238 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1239 " the load stops on end of file."
1243 "write file into a dos filesystem",
1244 "<d:/path/filename> <addr> <bytes>\n"
1245 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1246 " starting at address 'addr'.\n"
1250 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1252 "[-f | -i | -n] [-prv] source_file target_file\n"
1253 // "[-f | -i | -n] [-prv] source_file target_file\n"
1254 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1255 " -f overwrite existing file ignoring write protection\n"
1256 " this option is ignored when the -n option is also used\n"
1257 " -i prompt before overwrite (overrides a previous -n option)\n"
1258 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1259 " -p preserve attributes and timestamps\n"
1260 " -r copy directories recursively\n"
1261 " -v explain what is being done\n"
1265 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1266 "Print sub command description/usage",
1268 " - print brief description of all sub commands\n"
1269 "fat help command ...\n"
1270 " - print detailed usage of sub cmd 'command'"
1273 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1274 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1275 FSTR("Alias for 'help'"),
1276 #ifdef CONFIG_SYS_LONGHELP
1278 #endif /* CONFIG_SYS_LONGHELP */
1280 #ifdef CONFIG_AUTO_COMPLETE
1284 /* Mark end of table */
1285 CMD_TBL_END(cmd_tbl_fat
)
1289 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1291 puts_P(PSTR("Huch?"));
1293 return CMD_RET_USAGE
;