2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 * FAT filesystem commands
16 #include "con-utils.h"
17 #include "print-utils.h"
22 #include "getopt-min.h"
24 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
25 /* TODO: detect_ramsize() should be moved to z80-if.c */
26 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
27 #define BUFFER_SIZE 512
28 #define MAXBSIZE 512 /* TODO */
32 * Multible (fat) partitions per physical drive are not supported,
33 * but we have up to 2 sdcard slots.
38 command_ret_t command_ret
;
40 void setup_fatfs(void)
42 f_mount(&FatFs0
, "0:", 0);
43 f_mount(&FatFs1
, "1:", 0);
46 DWORD
get_fattime (void)
52 gmtime_r(&timer
, &tm_timer
);
54 return fatfs_time(&tm_timer
);
58 static bool check_abort(void)
63 printf_P(PSTR("Abort\n"));
69 static const FLASH
char * const FLASH rc_strings
[] = {
72 FSTR("internal error"),
79 FSTR("invalid object"),
80 FSTR("write protected"),
81 FSTR("invalid drive"),
83 FSTR("no file system"),
87 FSTR("not enough core"),
88 FSTR("too many open files"),
89 FSTR("invalid parameter")
92 static const FLASH
char * const FLASH rc_names
[] = {
102 FSTR("INVALID_OBJECT"),
103 FSTR("WRITE_PROTECTED"),
104 FSTR("INVALID_DRIVE"),
106 FSTR("NO_FILE_SYSTEM"),
107 FSTR("MKFS_ABORTED"),
110 FSTR("NOT_ENOUGH_CORE"),
111 FSTR("TOO_MANY_OPEN_FILES"),
112 FSTR("INVALID_PARAMETER")
116 void put_rc (FRESULT rc
)
119 printf_P(PSTR("rc=%u FR_"), rc
);
120 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
121 my_puts_P(PSTR("\n"));
123 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
124 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
128 const FLASH
char * rctostr(FRESULT rc
)
130 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
133 void err(const char *fmt
, ...)
137 // (void)fprintf(stderr, "%s: ", progname);
138 (void)vfprintf_P(stdout
, fmt
, ap
);
140 (void)printf_P(PSTR("\n"));
141 command_ret
= CMD_RET_FAILURE
;
146 static void swirl(void)
148 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
149 static uint_fast8_t cnt
;
150 static uint32_t tstamp
;
152 if (get_timer(0) > tstamp
) {
153 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
154 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
155 tstamp
= get_timer(0) + 250;
160 * pwd - Print current directory of the current drive.
163 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
168 buf
= (char *) malloc(BUFFER_SIZE
);
170 printf_P(PSTR("pwd: Out of Memory!\n"));
172 return CMD_RET_FAILURE
;
175 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
183 return CMD_RET_FAILURE
;
185 return CMD_RET_SUCCESS
;
190 * cd - Change the current/working directory.
193 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
199 arg
= getenv_str(PSTR(ENV_HOME
));
201 printf_P(PSTR("%s: \"%S\" is not set\n"), argv
[0], PSTR(ENV_HOME
));
202 return CMD_RET_FAILURE
;
212 res
= f_chdrive(drv
);
220 return CMD_RET_FAILURE
;
222 return CMD_RET_SUCCESS
;
225 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
228 * Remove trailing slashes,
229 * but keep a leading slash (absolute path)
231 void strip_trailing_slash_relpath(char *p
)
235 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
239 if (n
>= 1 && p
[0] == '/') {
243 while (n
-- != 0 && p
[n
] == '/')
247 int print_dirent(FILINFO
*f
)
249 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
250 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
251 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
252 (f
->fattrib
& AM_HID
) ? 'H' : '-',
253 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
254 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
255 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
256 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
261 * ls path - Directory listing
264 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
267 DIR Dir
; /* Directory object */
276 strip_trailing_slash_relpath(path
);
278 char *p
= strrchr(path
, '/');
284 if ((*q
++ & 0x38) == '0' && *q
++ == ':')
289 if (strpbrk_P(p
, PSTR("*?")) ||
290 (f_stat(path
, &Finfo
) == FR_OK
&& !(Finfo
.fattrib
& AM_DIR
))) {
294 pattern
= strdup("*");
295 strip_trailing_slash_relpath(path
);
297 //printf_P(PSTR("*: |%s| |%s|\n"), path ? path : "<NULL>", pattern ? pattern : "<NULL>");
300 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
301 while (res
== FR_OK
&& Finfo
.fname
[0]) {
302 if (Finfo
.fattrib
& AM_DIR
) {
305 s1
++; p1
+= Finfo
.fsize
;
307 print_dirent(&Finfo
);
310 res
= f_findnext(&Dir
, &Finfo
);
316 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
317 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
318 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
323 return CMD_RET_FAILURE
;
326 return CMD_RET_SUCCESS
;
330 * tst path - for debugging: test access with different functions
333 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
335 DIR Dir
; /* Directory object */
340 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
342 char * buf
= (char *) malloc(BUFFER_SIZE
);
344 printf_P(PSTR("pwd: Out of Memory!\n"));
346 return CMD_RET_FAILURE
;
348 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
351 printf_P(PSTR("cwd: |%s|\n"), buf
);
356 return CMD_RET_FAILURE
;
364 printf_P(PSTR("arg: |%s|\n"), path
);
365 printf_P(PSTR("==== f_stat: "));
366 res
= f_stat(path
, &Finfo
);
369 print_dirent(&Finfo
);
372 printf_P(PSTR("==== f_findfirst: "));
373 res
= f_findfirst(&Dir
, &Finfo
, path
, "*"); /* Start to search for files */
376 print_dirent(&Finfo
);
380 printf_P(PSTR("==== f_opendir: "));
381 res
= f_opendir(&Dir
, path
);
385 return CMD_RET_SUCCESS
;
390 FRESULT
mkpath(TCHAR
*path
)
398 res
= f_stat (path
, &fd
)
400 p
= strchr(path
, ':');
401 if (p
== NULL
|| *++p
== '\0' || *p
++ != '/')
404 while ((q
= strchr(p
, '/')) != NULL
) {
408 if (ret
!= FR_OK
&& ret
!= FR_EXIST
)
417 /******************************************************************************/
420 * These functions manipulate paths in PATH_T structures.
422 * They eliminate multiple slashes in paths when they notice them,
423 * and keep the path non-slash terminated.
425 * Both path_set() and path_append() return 0 if the requested name
431 char *p_end
; /* pointer to NULL at end of path */
432 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
435 static void strip_trailing_slash(PATH_T
*p
)
437 while (p
->p_end
> p
->p_path
&& p
->p_end
[-1] == '/')
442 * Move specified string into path. Convert "" to "." to handle BSD
443 * semantics for a null path. Strip trailing slashes.
446 path_set(PATH_T
*p
, char *string
)
448 if (strlen(string
) > MAX_PATHLEN
) {
449 err(PSTR("%s: name too long"), string
);
453 (void)strcpy(p
->p_path
, string
);
454 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
456 if (p
->p_path
== p
->p_end
) {
461 strip_trailing_slash(p
);
466 * Append specified string to path, inserting '/' if necessary. Return a
467 * pointer to the old end of path for restoration.
470 path_append(PATH_T
*p
, char *name
, int len
)
478 /* The "+ 1" accounts for the '/' between old path and name. */
479 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
480 err(PSTR("%s/%s: name too long"), p
->p_path
, name
);
485 * This code should always be executed, since paths shouldn't
488 if (p
->p_end
[-1] != '/') {
493 (void)strncat(p
->p_end
, name
, len
);
497 strip_trailing_slash(p
);
502 * Restore path to previous value. (As returned by path_append.)
514 * Return basename of path.
522 basename
= strrchr(p
->p_path
, '/');
523 return(basename
? basename
+ 1 : p
->p_path
);
531 #define R_FLAG (1<<0)
532 #define I_FLAG (1<<1)
533 #define N_FLAG (1<<2)
534 #define F_FLAG (1<<3)
535 #define P_FLAG (1<<4)
536 #define V_FLAG (1<<5)
539 setfile(FILINFO
*fs
, FIL
*fd
)
543 static struct timeval tv
[2];
545 fs
->st_mode
&= S_ISUID
|S_ISGID
|S_IRWXU
|S_IRWXG
|S_IRWXO
;
547 tv
[0].tv_sec
= fs
->st_atime
;
548 tv
[1].tv_sec
= fs
->st_mtime
;
549 if (utimes(to
->p_path
, tv
))
550 err(PSTR("utimes: %s: %s"), to
->p_path
, strerror(errno
));
554 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
556 static char buf
[MAXBSIZE
];
563 debug("==== copy_file(): dne: %u\n", dne
);
564 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
566 if ((fr
= f_open(&from_fd
, from
->p_path
, FA_READ
)) != FR_OK
) {
567 err(PSTR("%s: %S"), from
->p_path
, rctostr(fr
));
572 * If the file exists and we're interactive, verify with the user.
573 * If the file DNE, set the mode to be the from file, minus setuid
574 * bits, modified by the umask; arguably wrong, but it makes copying
575 * executables work right and it's been that way forever. (The
576 * other choice is 666 or'ed with the execute bits on the from file
577 * modified by the umask.)
580 if (flags
& I_FLAG
) {
583 printf_P(PSTR("overwrite %s? "), to
->p_path
);
584 checkch
= ch
= getchar();
585 while (ch
!= '\n' && ch
!= EOF
)
587 if (checkch
!= 'y') {
592 fr
= f_open(&to_fd
, to
->p_path
, FA_WRITE
|FA_CREATE_ALWAYS
);
594 fr
= f_open(&to_fd
, to
->p_path
, FA_WRITE
|FA_CREATE_NEW
);
597 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
602 while ((fr
= f_read(&from_fd
, buf
, MAXBSIZE
, &rcount
)) == FR_OK
&&
604 fr
= f_write(&to_fd
, buf
, rcount
, &wcount
);
605 if (fr
|| wcount
< rcount
) {
606 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
611 err(PSTR("%s: S"), from
->p_path
, rctostr(fr
));
617 if ((fr
= f_close(&to_fd
)) != FR_OK
)
618 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
622 static void copy_dir(void)
624 debug("==== copy_dir()");
625 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
627 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
629 command_ret
= CMD_RET_FAILURE
;
632 static void copy_dir(void)
635 struct dirent
*dp
, **dir_list
;
637 char *old_from
, *old_to
;
639 debug("==== copy_file(): dne: %u\n", dne
);
640 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
642 dir_cnt
= scandir(from
->p_path
, &dir_list
, NULL
, NULL
);
644 (void)fprintf(stderr
, "%s: can't read directory %s.\n",
645 progname
, from
->p_path
);
646 command_ret
= CMD_RET_FAILURE
;
650 * Instead of handling directory entries in the order they appear
651 * on disk, do non-directory files before directory files.
652 * There are two reasons to do directories last. The first is
653 * efficiency. Files tend to be in the same cylinder group as
654 * their parent, whereas directories tend not to be. Copying files
655 * all at once reduces seeking. Second, deeply nested tree's
656 * could use up all the file descriptors if we didn't close one
657 * directory before recursivly starting on the next.
660 for (i
= 0; i
< dir_cnt
; ++i
) {
662 if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
663 && (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
666 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
)))
669 if (statfcn(from
->p_path
, &from_stat
) < 0) {
670 err(PSTR("%s: %s"), dp
->d_name
, strerror(errno
));
671 path_restore(&from
, old_from
);
674 if (S_ISDIR(from_stat
.st_mode
)) {
675 path_restore(&from
, old_from
);
678 if (old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
)) {
680 path_restore(&to
, old_to
);
682 path_restore(&from
, old_from
);
683 done
: dir_list
[i
] = NULL
;
687 /* copy directories */
688 for (i
= 0; i
< dir_cnt
; ++i
) {
693 path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
))) {
698 path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
))) {
700 path_restore(&from
, old_from
);
705 path_restore(&from
, old_from
);
706 path_restore(&to
, old_to
);
713 * copy file or directory at "from" to "to".
717 FILINFO from_stat
, to_stat
;
721 debug("==== copy()\n");
722 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
724 fr
= f_stat(from
->p_path
, &from_stat
);
726 err(PSTR("%s: %S"), from
->p_path
, rctostr(fr
));
730 /* not an error, but need to remember it happened */
731 if (f_stat(to
->p_path
, &to_stat
) != FR_OK
)
734 if (strcmp(to
->p_path
, from
->p_path
) == 0) {
735 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
736 to
->p_path
, from
->p_path
);
737 command_ret
= CMD_RET_FAILURE
;
743 if(from_stat
.fattrib
& AM_DIR
) {
744 if (!(flags
& R_FLAG
)) {
745 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
747 command_ret
= CMD_RET_FAILURE
;
752 * If the directory doesn't exist, create the new one.
754 if ((fr
= f_mkdir(to
->p_path
)) != FR_OK
) {
755 err(PSTR("%s: %S"), to
->p_path
, rctostr(fr
));
759 else if (!(to_stat
.fattrib
& AM_DIR
)) {
760 (void)printf_P(PSTR("%s: not a directory.\n"), to
->p_path
);
765 * If not -p and directory didn't exist, set it to be the
766 * same as the from directory, umodified by the umask;
767 * arguably wrong, but it's been that way forever.
771 setfile(&from_stat
, 0);
773 (void)chmod(to
->p_path
, from_stat
.st_mode
);
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 */
789 command_ret
= CMD_RET_SUCCESS
;
795 while ((opt
= getopt(argc
, argv
, PSTR("Rrfip"))) != -1) {
815 return CMD_RET_USAGE
;
823 return CMD_RET_USAGE
;
825 from
= (PATH_T
*) malloc(sizeof(PATH_T
));
826 to
= (PATH_T
*) malloc(sizeof(PATH_T
));
827 if (from
== NULL
|| to
== NULL
) {
828 printf_P(PSTR("cp: Out of Memory!\n"));
829 command_ret
= CMD_RET_FAILURE
;
832 from
->p_end
= from
->p_path
; *from
->p_path
= '\0';
833 to
->p_end
= to
->p_path
; *to
->p_path
= '\0';
835 /* consume last argument first. */
836 if (!path_set(to
, argv
[--argc
])) {
837 command_ret
= CMD_RET_FAILURE
;
842 * Cp has two distinct cases:
844 * % cp [-rip] source target
845 * % cp [-rip] source1 ... directory
847 * In both cases, source can be either a file or a directory.
849 * In (1), the target becomes a copy of the source. That is, if the
850 * source is a file, the target will be a file, and likewise for
853 * In (2), the real target is not directory, but "directory/source".
856 fr
= f_stat(to
->p_path
, &to_stat
);
857 debug("==== main, stat to: fr: %d, attr: %02x\n", fr
, to_stat
.fattrib
);
858 debug(" from:'%s' to:'%s'\n", from
->p_path
, to
->p_path
);
860 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
861 err(PSTR("Test1: %s: %S"), to
->p_path
, rctostr(fr
));
862 command_ret
= CMD_RET_FAILURE
;
865 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
867 * Case (1). Target is not a directory.
870 command_ret
= CMD_RET_USAGE
;
873 if (!path_set(from
, *argv
)) {
874 command_ret
= CMD_RET_FAILURE
;
881 * Case (2). Target is a directory.
884 if (!path_set(from
, *argv
))
886 if (!(old_to
= path_append(to
, path_basename(from
), -1)))
891 path_restore(&to
, old_to
);
904 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
->p_path
)));
913 /******************************************************************************/
916 * Work register for stat command
920 WORD AccFiles
, AccDirs
;
926 char *path
, /* Pointer to the working buffer with start path */
927 struct stat_dat_s
*statp
935 res
= f_opendir(&dirs
, path
);
939 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
940 statp
->Finfo
.fname
[0]) {
941 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
943 fn
= statp
->Finfo
.fname
;
944 if (statp
->Finfo
.fattrib
& AM_DIR
) {
947 strcpy(path
+i
+1, fn
);
948 res
= scan_files(path
, statp
);
953 //printf_P(PSTR("%s/%s\n"), path, fn);
955 statp
->AccSize
+= statp
->Finfo
.fsize
;
969 * fatstat path - Show logical drive status
972 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
979 struct stat_dat_s statp
;
981 buf
= (char *) malloc(BUFFER_SIZE
);
983 printf_P(PSTR("fat stat: Out of Memory!\n"));
984 return CMD_RET_FAILURE
;
989 res
= f_getfree(path
, &nfreeclst
, &fs
);
993 "Bytes/Cluster: %lu\n"
994 "Number of FATs: %u\n"
995 "Root DIR entries: %u\n"
997 "Number of clusters: %lu\n"
998 "FAT start (lba): %lu\n"
999 "DIR start (lba,cluster): %lu\n"
1000 "Data start (lba): %lu\n"),
1001 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1002 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1003 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1007 res
= f_getlabel(path
, buf
, &serial
);
1011 "Volume S/N: %04X-%04X\n"),
1012 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1016 my_puts_P(PSTR("\nCounting... "));
1017 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1020 res
= scan_files(buf
, &statp
);
1023 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1024 "%lu KB total disk space.\n%lu KB available.\n"),
1025 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1026 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1034 return CMD_RET_FAILURE
;
1036 return CMD_RET_SUCCESS
;
1040 * fatread/write - load binary file to/from a dos filesystem
1041 * read <d:/path/filename> <addr> [bytes [pos]]
1042 * write <d:/path/filename> <addr> <bytes>
1044 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1048 unsigned long bytes
;
1050 unsigned long bytes_rw
;
1052 bool dowrite
= (argv
[0][0] == 'w');
1053 FRESULT res
= FR_OK
;
1058 if (argc
< (dowrite
? 4 : 3))
1059 return CMD_RET_USAGE
;
1061 addr
= eval_arg(argv
[2], NULL
);
1062 if (addr
>= MAX_MEMORY
) {
1063 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1064 return CMD_RET_FAILURE
;
1067 bytes
= eval_arg(argv
[3], NULL
);
1071 pos
= eval_arg(argv
[4], NULL
);
1075 if (addr
+ bytes
> MAX_MEMORY
)
1076 bytes
= MAX_MEMORY
- addr
;
1078 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1079 if (buffer
== NULL
) {
1080 printf_P(PSTR("fatstat: Out of Memory!\n"));
1082 return CMD_RET_FAILURE
;
1086 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1090 res
= f_lseek(&File
, pos
);
1093 timer
= get_timer(0);
1095 unsigned int cnt
, br
;
1097 if (bytes
>= BUFFER_SIZE
) {
1099 bytes
-= BUFFER_SIZE
;
1101 cnt
= bytes
; bytes
= 0;
1104 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1108 z80_read_block(buffer
, addr
, cnt
);
1109 z80_bus_cmd(Release
);
1110 res
= f_write(&File
, buffer
, cnt
, &br
);
1114 res
= f_read(&File
, buffer
, cnt
, &br
);
1117 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1121 z80_write_block(buffer
, addr
, br
);
1122 z80_bus_cmd(Release
);
1129 printf_P(PSTR("Disk full?\n"));
1136 FRESULT fr
= f_close(&File
);
1139 timer
= get_timer(timer
);
1140 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1141 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1149 my_puts_P(PSTR("Bus timeout\n"));
1153 return CMD_RET_FAILURE
;
1155 return CMD_RET_SUCCESS
;
1159 * command table for fat subcommands
1162 cmd_tbl_t cmd_tbl_fat
[] = {
1164 stat
, 2, CTBL_RPT
, do_stat
,
1165 "Show logical drive status",
1169 pwd
, 2, CTBL_RPT
, do_pwd
,
1170 "Print name of current/working directory",
1175 "Change the current/working directory.",
1179 ls
, 2, CTBL_RPT
, do_ls
,
1180 "Directory listing",
1184 tst
, 2, CTBL_DBG
|CTBL_RPT
, do_tst
,
1185 "FatFS test function",
1190 "load binary file from a dos filesystem",
1191 "<d:/path/filename> <addr> [bytes [pos]]\n"
1192 " - Load binary file 'path/filename' on logical drive 'd'\n"
1193 " to address 'addr' from dos filesystem.\n"
1194 " 'pos' gives the file position to start loading from.\n"
1195 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1196 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1197 " the load stops on end of file."
1201 "write file into a dos filesystem",
1202 "<d:/path/filename> <addr> <bytes>\n"
1203 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1204 " starting at address 'addr'.\n"
1208 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1210 "[-R] [-f | -i | -n] [-aprv] source_file target_file\n"
1215 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1216 "Print sub command description/usage",
1218 " - print brief description of all sub commands\n"
1219 "fat help command ...\n"
1220 " - print detailed usage of sub cmd 'command'"
1223 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1224 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1225 FSTR("Alias for 'help'"),
1226 #ifdef CONFIG_SYS_LONGHELP
1228 #endif /* CONFIG_SYS_LONGHELP */
1230 #ifdef CONFIG_AUTO_COMPLETE
1234 /* Mark end of table */
1235 CMD_TBL_END(cmd_tbl_fat
)
1239 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1241 puts_P(PSTR("Huch?"));
1243 return CMD_RET_USAGE
;