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"
25 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
26 /* TODO: detect_ramsize() should be moved to z80-if.c */
27 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
28 #define BUFFER_SIZE 512
29 #define MAXBSIZE 512 /* TODO */
33 * Multible (fat) partitions per physical drive are not supported,
34 * but we have up to 2 sdcard slots.
39 command_ret_t command_ret
;
41 void setup_fatfs(void)
43 f_mount(&FatFs0
, "0:", 0);
44 f_mount(&FatFs1
, "1:", 0);
47 DWORD
get_fattime (void)
53 gmtime_r(&timer
, &tm_timer
);
55 return fatfs_time(&tm_timer
);
59 static bool check_abort(void)
64 printf_P(PSTR("Abort\n"));
70 static const FLASH
char * const FLASH rc_strings
[] = {
73 FSTR("internal error"),
80 FSTR("invalid object"),
81 FSTR("write protected"),
82 FSTR("invalid drive"),
84 FSTR("no file system"),
88 FSTR("not enough core"),
89 FSTR("too many open files"),
90 FSTR("invalid parameter")
93 static const FLASH
char * const FLASH rc_names
[] = {
100 FSTR("INVALID_NAME"),
103 FSTR("INVALID_OBJECT"),
104 FSTR("WRITE_PROTECTED"),
105 FSTR("INVALID_DRIVE"),
107 FSTR("NO_FILE_SYSTEM"),
108 FSTR("MKFS_ABORTED"),
111 FSTR("NOT_ENOUGH_CORE"),
112 FSTR("TOO_MANY_OPEN_FILES"),
113 FSTR("INVALID_PARAMETER")
117 void put_rc (FRESULT rc
)
120 printf_P(PSTR("rc=%u FR_"), rc
);
121 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
122 my_puts_P(PSTR("\n"));
124 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
125 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
129 const FLASH
char * rctostr(FRESULT rc
)
131 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
134 void err(const char *fmt
, ...)
138 // (void)fprintf(stderr, "%s: ", progname);
139 (void)vfprintf_P(stdout
, fmt
, ap
);
141 (void)printf_P(PSTR("\n"));
143 command_ret
= CMD_RET_FAILURE
;
148 static void swirl(void)
150 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
151 static uint_fast8_t cnt
;
152 static uint32_t tstamp
;
154 if (get_timer(0) > tstamp
) {
155 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
156 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
157 tstamp
= get_timer(0) + 250;
162 * pwd - Print current directory of the current drive.
165 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
170 buf
= (char *) malloc(BUFFER_SIZE
);
172 printf_P(PSTR("pwd: Out of Memory!\n"));
174 return CMD_RET_FAILURE
;
177 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
185 return CMD_RET_FAILURE
;
187 return CMD_RET_SUCCESS
;
192 * cd - Change the current/working directory.
195 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
201 arg
= getenv_str(PSTR(ENV_HOME
));
203 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
204 return CMD_RET_FAILURE
;
214 res
= f_chdrive(drv
);
222 return CMD_RET_FAILURE
;
224 return CMD_RET_SUCCESS
;
227 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
230 * Remove trailing slashes,
231 * but keep a leading slash (absolute path)
233 void strip_trailing_slash_relpath(char *p
)
237 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
241 if (n
>= 1 && p
[0] == '/') {
245 while (n
-- != 0 && p
[n
] == '/')
249 int print_dirent(FILINFO
*f
)
251 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
252 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
253 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
254 (f
->fattrib
& AM_HID
) ? 'H' : '-',
255 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
256 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
257 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
258 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
263 * ls path - Directory listing
266 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
269 DIR Dir
; /* Directory object */
278 strip_trailing_slash_relpath(path
);
280 char *p
= strrchr(path
, '/');
286 if ((*q
++ & 0x38) == '0' && *q
++ == ':')
291 if (strpbrk_P(p
, PSTR("*?")) ||
292 (f_stat(path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
296 pattern
= strdup("*");
297 strip_trailing_slash_relpath(path
);
299 //printf_P(PSTR("*: |%s| |%s|\n"), path ? path : "<NULL>", pattern ? pattern : "<NULL>");
302 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
303 while (res
== FR_OK
&& Finfo
.fname
[0]) {
304 if (Finfo
.fattrib
& AM_DIR
) {
307 s1
++; p1
+= Finfo
.fsize
;
309 print_dirent(&Finfo
);
312 res
= f_findnext(&Dir
, &Finfo
);
318 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
319 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
320 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
325 return CMD_RET_FAILURE
;
328 return CMD_RET_SUCCESS
;
332 * tst path - for debugging: test access with different functions
335 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
337 DIR Dir
; /* Directory object */
342 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
344 char * buf
= (char *) malloc(BUFFER_SIZE
);
346 printf_P(PSTR("pwd: Out of Memory!\n"));
348 return CMD_RET_FAILURE
;
350 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
353 printf_P(PSTR("cwd: |%s|\n"), buf
);
358 return CMD_RET_FAILURE
;
366 printf_P(PSTR("arg: |%s|\n"), path
);
367 printf_P(PSTR("==== f_stat: "));
368 res
= f_stat(path
, &Finfo
);
371 print_dirent(&Finfo
);
374 printf_P(PSTR("==== f_findfirst: "));
375 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
378 print_dirent(&Finfo
);
382 printf_P(PSTR("==== f_opendir: "));
383 res
= f_opendir(&Dir
, path
);
387 return CMD_RET_SUCCESS
;
392 FRESULT
mkpath(TCHAR
*path
)
400 res
= f_stat (path
, &fd
)
402 p
= strchr(path
, ':');
403 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
406 while ((q
= strchr(p
, '/')) != NULL
) {
410 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
419 /******************************************************************************/
422 * These functions manipulate paths in PATH_T structures.
424 * They eliminate multiple slashes in paths when they notice them,
425 * and keep the path non-slash terminated.
427 * Both path_set() and path_append() return 0 if the requested name
433 char *p_end
; /* pointer to NULL at end of path */
434 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
437 static void strip_trailing_slash(PATH_T
*p
)
439 while (p
->p_end
> p
->p_path
&& p
->p_end
[-1] == '/')
444 * Move specified string into path. Convert "" to "." to handle BSD
445 * semantics for a null path. Strip trailing slashes.
448 path_set(PATH_T
*p
, char *string
)
450 if (strlen(string
) > MAX_PATHLEN
) {
451 err(PSTR("set: '%s': name too long"), string
);
455 (void)strcpy(p
->p_path
, string
);
456 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
458 if (p
->p_path
== p
->p_end
) {
463 strip_trailing_slash(p
);
468 * Append specified string to path, inserting '/' if necessary. Return a
469 * pointer to the old end of path for restoration.
472 path_append(PATH_T
*p
, char *name
, int len
)
480 /* The "+ 1" accounts for the '/' between old path and name. */
481 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
482 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
487 * This code should always be executed, since paths shouldn't
490 if (p
->p_end
[-1] != '/') {
495 (void)strncat(p
->p_end
, name
, len
);
499 strip_trailing_slash(p
);
504 * Restore path to previous value. (As returned by path_append.)
507 path_restore(PATH_T
*p
, char *old
)
514 * Return basename of path.
516 char *path_basename(PATH_T
*p
)
520 basename
= strrchr(p
->p_path
, '/');
521 return(basename
? basename
+ 1 : p
->p_path
);
525 static uint8_t flags
;
528 PATH_T
*from
= &from_static
;
529 PATH_T
*to
= &to_static
;
530 static uint8_t *blockbuf
;
531 static int blockbuf_size
;
533 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
534 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
535 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
536 #define P_FLAG (1<<4) // preserve attributes and timestamps
537 #define R_FLAG (1<<0) // copy directories recursively
538 #define V_FLAG (1<<5) // explain what is being done
541 setfile(FILINFO
*fs
, FIL
*fd
)
545 static struct timeval tv
[2];
547 fs
->st_mode
&= S_ISUID
|S_ISGID
|S_IRWXU
|S_IRWXG
|S_IRWXO
;
549 tv
[0].tv_sec
= fs
->st_atime
;
550 tv
[1].tv_sec
= fs
->st_mtime
;
551 if (utimes(to
->p_path
, tv
))
552 err(PSTR("utimes: %s: %s"), to
->p_path
, strerror(errno
));
556 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
564 if (blockbuf
== NULL
) {
565 blockbuf_size
= get_freemem() / 512 * 512;
566 if (blockbuf_size
!= 0)
567 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
568 if (blockbuf
== NULL
) {
569 err(PSTR("Not enough memory!\n"));
574 debug("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne
, blockbuf_size
);
575 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
578 if ((fr
= f_open(&from_fd
, from
->p_path
, FA_READ
)) != FR_OK
) {
579 err(PSTR("%s: %S"), from
->p_path
, rctostr(fr
));
584 * If the file exists and we're interactive, verify with the user.
585 * If the file DNE, set the mode to be the from file, minus setuid
586 * bits, modified by the umask; arguably wrong, but it makes copying
587 * executables work right and it's been that way forever. (The
588 * other choice is 666 or'ed with the execute bits on the from file
589 * modified by the umask.)
592 if (flags
& I_FLAG
) {
593 printf_P(PSTR("overwrite '%s'? "), to
->p_path
);
594 if (!confirm_yes()) {
599 fr
= f_open(&to_fd
, to
->p_path
, FA_WRITE
|FA_CREATE_ALWAYS
);
601 fr
= f_open(&to_fd
, to
->p_path
, FA_WRITE
|FA_CREATE_NEW
);
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
));
624 if ((fr
= f_close(&to_fd
)) != FR_OK
)
625 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
630 static void copy_dir(void)
632 debug("==== copy_dir()");
633 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
635 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
637 command_ret
= CMD_RET_FAILURE
;
640 static void copy_dir(void)
643 struct dirent
*dp
, **dir_list
;
645 char *old_from
, *old_to
;
647 debug("==== copy_file(): dne: %u\n", dne
);
648 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
650 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
652 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
653 progname
, from
->p_path
);
654 command_ret
= CMD_RET_FAILURE
;
658 * Instead of handling directory entries in the order they appear
659 * on disk, do non-directory files before directory files.
660 * There are two reasons to do directories last. The first is
661 * efficiency. Files tend to be in the same cylinder group as
662 * their parent, whereas directories tend not to be. Copying files
663 * all at once reduces seeking. Second, deeply nested tree's
664 * could use up all the file descriptors if we didn't close one
665 * directory before recursivly starting on the next.
668 for (i
= 0; i
< dir_cnt
; ++i
) {
670 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
671 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
674 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
677 if (statfcn(from
->p_path
, &from_stat
) < 0) {
678 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
679 path_restore(&from
, old_from
);
682 if (S_ISDIR(from_stat
.st_mode
)) {
683 path_restore(&from
, old_from
);
686 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
688 path_restore(&to
, old_to
);
690 path_restore(&from
, old_from
);
691 done
: dir_list
[i
] = NULL
;
695 /* copy directories */
696 for (i
= 0; i
< dir_cnt
; ++i
) {
701 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
706 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
708 path_restore(&from
, old_from
);
713 path_restore(&from
, old_from
);
714 path_restore(&to
, old_to
);
721 * copy file or directory at "from" to "to".
725 FILINFO from_stat
, to_stat
;
729 debug("==== copy()\n");
730 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
732 fr
= f_stat(from
->p_path
, &from_stat
);
734 err(PSTR("%s: %S"), from
->p_path
, rctostr(fr
));
738 /* not an error, but need to remember it happened */
739 if (f_stat(to
->p_path
, &to_stat
) != FR_OK
)
742 if (strcmp(to
->p_path
, from
->p_path
) == 0) {
743 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
744 to
->p_path
, from
->p_path
);
745 command_ret
= CMD_RET_FAILURE
;
751 if(from_stat
.fattrib
& AM_DIR
) {
752 if (!(flags
& R_FLAG
)) {
753 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
755 command_ret
= CMD_RET_FAILURE
;
760 * If the directory doesn't exist, create the new one.
762 if ((fr
= f_mkdir(to
->p_path
)) != FR_OK
) {
763 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
767 else if (!(to_stat
.fattrib
& AM_DIR
)) {
768 (void)printf_P(PSTR("%s: not a directory.\n"), to
->p_path
);
773 setfile(&from_stat
, 0);
777 copy_file(&from_stat
, dne
);
780 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
783 FRESULT fr
; /* Return value */
784 //DIR dj; /* Directory search object */
785 FILINFO to_stat
; /* File information */
790 command_ret
= CMD_RET_SUCCESS
;
796 // while ((opt = getopt(argc, argv, PSTR("fiprv"))) != -1) {
797 while ((opt
= getopt(argc
, argv
, PSTR("fiprv"))) != -1) {
800 tflags
&= ~(I_FLAG
| N_FLAG
);
804 tflags
&= ~(F_FLAG
| N_FLAG
);
808 tflags
&= ~(F_FLAG
| I_FLAG
);
821 return CMD_RET_USAGE
;
830 return CMD_RET_USAGE
;
833 from
= (PATH_T
*) malloc(sizeof(PATH_T
));
834 to
= (PATH_T
*) malloc(sizeof(PATH_T
));
835 if (from
== NULL
|| to
== NULL
) {
836 printf_P(PSTR("cp: Out of Memory!\n"));
837 command_ret
= CMD_RET_FAILURE
;
841 from
->p_end
= from
->p_path
; *from
->p_path
= '\0';
842 to
->p_end
= to
->p_path
; *to
->p_path
= '\0';
844 /* consume last argument first. */
845 if (!path_set(to
, argv
[--argc
])) {
846 command_ret
= CMD_RET_FAILURE
;
851 * Cp has two distinct cases:
853 * % cp [-rip] source target
854 * % cp [-rip] source1 ... directory
856 * In both cases, source can be either a file or a directory.
858 * In (1), the target becomes a copy of the source. That is, if the
859 * source is a file, the target will be a file, and likewise for
862 * In (2), the real target is not directory, but "directory/source".
865 fr
= f_stat(to
->p_path
, &to_stat
);
866 debug("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr
, to_stat
.fattrib
, flags
);
867 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
869 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
870 err(PSTR("Test1: %s: %S"), to
->p_path
, rctostr(fr
));
871 command_ret
= CMD_RET_FAILURE
;
874 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
876 * Case (1). Target is not a directory.
879 err(PSTR("target '%s' is not a directory"), to
->p_path
);
880 //command_ret = CMD_RET_USAGE;
883 if (!path_set(from
, *argv
)) {
884 command_ret
= CMD_RET_FAILURE
;
891 * Case (2). Target is a directory.
894 if (!path_set(from
, *argv
))
896 if (!(old_to
= path_append(to
, path_basename(from
), -1)))
901 path_restore(to
, old_to
);
915 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
->p_path
)));
924 /******************************************************************************/
927 * Work register for stat command
931 WORD AccFiles
, AccDirs
;
937 char *path
, /* Pointer to the working buffer with start path */
938 struct stat_dat_s
*statp
946 res
= f_opendir(&dirs
, path
);
950 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
951 statp
->Finfo
.fname
[0]) {
952 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
954 fn
= statp
->Finfo
.fname
;
955 if (statp
->Finfo
.fattrib
& AM_DIR
) {
958 strcpy(path
+i
+1, fn
);
959 res
= scan_files(path
, statp
);
964 //printf_P(PSTR("%s/%s\n"), path, fn);
966 statp
->AccSize
+= statp
->Finfo
.fsize
;
980 * fatstat path - Show logical drive status
983 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
990 struct stat_dat_s statp
;
992 buf
= (char *) malloc(BUFFER_SIZE
);
994 printf_P(PSTR("fat stat: Out of Memory!\n"));
995 return CMD_RET_FAILURE
;
1000 res
= f_getfree(path
, &nfreeclst
, &fs
);
1004 "Bytes/Cluster: %lu\n"
1005 "Number of FATs: %u\n"
1006 "Root DIR entries: %u\n"
1007 "Sectors/FAT: %lu\n"
1008 "Number of clusters: %lu\n"
1009 "FAT start (lba): %lu\n"
1010 "DIR start (lba,cluster): %lu\n"
1011 "Data start (lba): %lu\n"),
1012 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1013 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1014 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1018 res
= f_getlabel(path
, buf
, &serial
);
1022 "Volume S/N: %04X-%04X\n"),
1023 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1027 my_puts_P(PSTR("\nCounting... "));
1028 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1031 res
= scan_files(buf
, &statp
);
1034 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1035 "%lu KB total disk space.\n%lu KB available.\n"),
1036 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1037 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1045 return CMD_RET_FAILURE
;
1047 return CMD_RET_SUCCESS
;
1051 * fatread/write - load binary file to/from a dos filesystem
1052 * read <d:/path/filename> <addr> [bytes [pos]]
1053 * write <d:/path/filename> <addr> <bytes>
1055 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1059 unsigned long bytes
;
1061 unsigned long bytes_rw
;
1063 bool dowrite
= (argv
[0][0] == 'w');
1064 FRESULT res
= FR_OK
;
1069 if (argc
< (dowrite
? 4 : 3))
1070 return CMD_RET_USAGE
;
1072 addr
= eval_arg(argv
[2], NULL
);
1073 if (addr
>= MAX_MEMORY
) {
1074 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1075 return CMD_RET_FAILURE
;
1078 bytes
= eval_arg(argv
[3], NULL
);
1082 pos
= eval_arg(argv
[4], NULL
);
1086 if (addr
+ bytes
> MAX_MEMORY
)
1087 bytes
= MAX_MEMORY
- addr
;
1089 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1090 if (buffer
== NULL
) {
1091 printf_P(PSTR("fatstat: Out of Memory!\n"));
1093 return CMD_RET_FAILURE
;
1097 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1101 res
= f_lseek(&File
, pos
);
1104 timer
= get_timer(0);
1106 unsigned int cnt
, br
;
1108 if (bytes
>= BUFFER_SIZE
) {
1110 bytes
-= BUFFER_SIZE
;
1112 cnt
= bytes
; bytes
= 0;
1115 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1119 z80_read_block(buffer
, addr
, cnt
);
1120 z80_bus_cmd(Release
);
1121 res
= f_write(&File
, buffer
, cnt
, &br
);
1125 res
= f_read(&File
, buffer
, cnt
, &br
);
1128 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1132 z80_write_block(buffer
, addr
, br
);
1133 z80_bus_cmd(Release
);
1140 printf_P(PSTR("Disk full?\n"));
1147 FRESULT fr
= f_close(&File
);
1150 timer
= get_timer(timer
);
1151 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1152 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1160 my_puts_P(PSTR("Bus timeout\n"));
1164 return CMD_RET_FAILURE
;
1166 return CMD_RET_SUCCESS
;
1170 * command table for fat subcommands
1173 cmd_tbl_t cmd_tbl_fat
[] = {
1175 stat
, 2, CTBL_RPT
, do_stat
,
1176 "Show logical drive status",
1180 pwd
, 2, CTBL_RPT
, do_pwd
,
1181 "Print name of current/working directory",
1186 "Change the current/working directory.",
1190 ls
, 2, CTBL_RPT
, do_ls
,
1191 "Directory listing",
1195 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1196 "FatFS test function",
1201 "load binary file from a dos filesystem",
1202 "<d:/path/filename> <addr> [bytes [pos]]\n"
1203 " - Load binary file 'path/filename' on logical drive 'd'\n"
1204 " to address 'addr' from dos filesystem.\n"
1205 " 'pos' gives the file position to start loading from.\n"
1206 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1207 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1208 " the load stops on end of file."
1212 "write file into a dos filesystem",
1213 "<d:/path/filename> <addr> <bytes>\n"
1214 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1215 " starting at address 'addr'.\n"
1219 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1221 "[-f | -i | -n] [-prv] source_file target_file\n"
1222 // "[-f | -i | -n] [-prv] source_file target_file\n"
1223 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1224 " -f overwrite existing file ignoring write protection\n"
1225 " this option is ignored when the -n option is also used\n"
1226 " -i prompt before overwrite (overrides a previous -n option)\n"
1227 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1228 " -p preserve attributes and timestamps\n"
1229 " -r copy directories recursively\n"
1230 " -v explain what is being done\n"
1234 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1235 "Print sub command description/usage",
1237 " - print brief description of all sub commands\n"
1238 "fat help command ...\n"
1239 " - print detailed usage of sub cmd 'command'"
1242 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1243 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1244 FSTR("Alias for 'help'"),
1245 #ifdef CONFIG_SYS_LONGHELP
1247 #endif /* CONFIG_SYS_LONGHELP */
1249 #ifdef CONFIG_AUTO_COMPLETE
1253 /* Mark end of table */
1254 CMD_TBL_END(cmd_tbl_fat
)
1258 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1260 puts_P(PSTR("Huch?"));
1262 return CMD_RET_USAGE
;