]>
cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 * FAT filesystem commands
14 uint32_t fat_time(const struct tm
* timeptr
);
19 #include "con-utils.h"
20 #include "print-utils.h"
24 #include "getopt-min.h"
26 #define DEBUG_FA 0 /* set to 1 to debug */
27 #define DEBUG_LS 0 /* set to 1 to debug */
28 #define DEBUG_RM 0 /* set to 1 to debug */
29 #define DEBUG_CP 0 /* set to 1 to debug */
31 #define debug_fa(fmt, args...) \
32 debug_cond(DEBUG_FA, fmt, ##args)
33 #define debug_ls(fmt, args...) \
34 debug_cond(DEBUG_LS, fmt, ##args)
35 #define debug_rm(fmt, args...) \
36 debug_cond(DEBUG_RM, fmt, ##args)
37 #define debug_cp(fmt, args...) \
38 debug_cond(DEBUG_CP, fmt, ##args)
41 /* TODO: use memory size test function (z80_memsize_detect() ) */
42 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
43 #define BUFFER_SIZE FF_MAX_SS
44 #define CPY_BUF_SIZE (2*FF_MAX_SS)
45 #define PATH_MAX CONFIG_SYS_MAX_PATHLEN
49 * Multible (fat) partitions per physical drive are not supported,
50 * but we have up to 2 sdcard slots.
55 void setup_fatfs(void)
57 f_mount(&FatFs0
, "0:", 0);
58 f_mount(&FatFs1
, "1:", 0);
61 DWORD
get_fattime (void)
67 gmtime_r(&timer
, &tm_timer
);
69 return fat_time(&tm_timer
);
74 bool check_abort(void)
79 printf_P(PSTR("Abort\n"));
85 static const FLASH
char * const FLASH rc_strings
[] = {
88 FSTR("Internal error"),
95 FSTR("Invalid object"),
96 FSTR("Write protected"),
97 FSTR("Invalid drive"),
99 FSTR("No file system"),
100 FSTR("Mkfs aborted"),
103 FSTR("Not enough core"),
104 FSTR("Too many open files"),
105 FSTR("Invalid parameter")
109 const FLASH
char * fat_rctostr(FRESULT rc
)
111 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
117 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
118 static uint_fast8_t cnt
;
119 static uint32_t tstamp
;
121 if (get_timer(0) > tstamp
) {
122 tstamp
= get_timer(0) + 250;
125 putchar(swirlchar
[cnt
]);
130 * pwd - Print current directory of the current drive.
133 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
138 res
= f_getcwd(buf
, PATH_MAX
); /* Get current directory path */
141 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
145 return CMD_RET_SUCCESS
;
150 * cd - Change the current/working directory.
153 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
159 path
= getenv_str(PSTR(ENV_HOME
));
161 cmd_error(CMD_RET_FAILURE
, 0, PSTR("\"%S\" not set"), PSTR(ENV_HOME
));
166 if (strlen(path
) > 1 && path
[1] == ':')
167 res
= f_chdrive(path
);
173 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
175 return CMD_RET_SUCCESS
;
180 void print_dirent(FILINFO
*f
)
182 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
183 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
184 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
185 (f
->fattrib
& AM_HID
) ? 'H' : '-',
186 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
187 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
188 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
189 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
193 char *path_split(char *p
)
195 if (isdigit(p
[0]) && (p
[1] == ':'))
198 char *ps
= strrchr(p
, '/');
202 memmove(ps
+1, ps
, strlen(ps
)+1);
210 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
212 int ret
= CMD_RET_SUCCESS
;
216 return CMD_RET_USAGE
;
218 for (uint8_t i
= 1; i
< argc
; i
++) {
219 if ((res
= f_mkdir(argv
[i
])) != FR_OK
) {
220 ret
= CMD_RET_FAILURE
;
221 cmd_error(0, res
, PSTR("cannot create directory '%s'"), argv
[i
]);
228 * ls path - Directory listing
231 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
234 DIR dir
; /* Directory object */
244 memset(buf
, 0, PATH_MAX
);
248 strncpy(path
, argv
[1], PATH_MAX
-1);
250 pattern
= path_split(path
);
251 debug_ls("### path, pattern: '%s', '%s'\n", path
[0] ? path
: "<NULL>", pattern
? pattern
: "<NULL>");
253 if (pattern
== NULL
) {
254 res
= f_opendir(&dir
, path
);
255 if (res
== FR_NO_PATH
|| res
== FR_INVALID_NAME
) {
258 } else if (res
!= FR_OK
) {
259 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), path
);
262 debug_ls("### path, pattern: '%s', '%s'\n", path
, pattern
? pattern
: "<NULL>");
264 if (pattern
== NULL
|| *pattern
== '\0')
267 debug_ls("### path, pattern: '%s', '%s'\n", path
, pattern
? pattern
: "<NULL>");
269 res
= f_findfirst(&dir
, &finfo
, path
, pattern
);
274 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
277 if (finfo
.fattrib
& AM_DIR
) {
280 s1
++; p1
+= finfo
.fsize
;
282 print_dirent(&finfo
);
285 res
= f_findnext(&dir
, &finfo
);
289 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
290 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
291 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
295 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
297 return CMD_RET_SUCCESS
;
301 typedef void (*fatfunc_t
)(const TCHAR
* path_old
, const TCHAR
* path_new
);
303 command_ret_t exit_val
;
305 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
306 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
307 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
308 #define P_FLAG (1<<4) // preserve attributes and timestamps
309 #define V_FLAG (1<<5) // explain what is being done
311 char* splitpath(const char* path
)
313 char* fs
= strrchr(path
, '/');
314 char* bs
= strrchr(path
, '\\');
318 } else if (bs
!= NULL
) {
328 FRESULT
copy_file(const TCHAR
* src
, const TCHAR
* dst
)
333 /* File copy buffer */
334 uint8_t *buffer
= (uint8_t *) malloc(CPY_BUF_SIZE
);
335 if (buffer
== NULL
) {
336 res
= (FRESULT
) ENOMEM
;
339 /* Open source file */
340 res
= f_open(&fsrc
, src
, FA_READ
);
343 /* Create destination file */
344 res
= f_open(&fdst
, dst
, FA_WRITE
| FA_CREATE_NEW
);
346 UINT br
, bw
; /* File read/write count */
348 /* Copy source to destination */
350 res
= f_read(&fsrc
, buffer
, CPY_BUF_SIZE
, &br
); /* Read a chunk of source file */
352 break; /* error or eof */
353 res
= f_write(&fdst
, buffer
, br
, &bw
); /* Write it to the destination file */
357 res
= (FRESULT
) EFULL
; /* disk full */
362 debug_cp("==== copy() res: %d, br: %d, bw: %d\n", res
, br
, bw
);
375 void ff_remove(const TCHAR
*file
, const TCHAR
*dest UNUSED
)
379 debug_rm("==== ff_remove: '%s'\n", file
);
381 if (!(cmd_flags
& N_FLAG
)) {
382 if ((res
= f_unlink(file
)) == FR_OK
) {
383 if (cmd_flags
& V_FLAG
)
384 printf_P(PSTR("removed: '%s'\n"), file
);
386 cmd_error(0, res
, PSTR("cannot remove '%s'"), file
);
389 printf_P(PSTR("not removed: '%s'\n"), file
);
392 exit_val
= (res
!= FR_OK
);
396 void ff_copy(const TCHAR
* path_old
, const TCHAR
* path_new
)
400 debug_cp("==== ff_copy: '%s' --> '%s'\n", path_old
, path_new
);
402 if (cmd_flags
& V_FLAG
)
403 printf_P(PSTR("'%s' -> '%s'\n"), path_old
, path_new
);
404 if ((res
= copy_file(path_old
, path_new
)) != FR_OK
)
405 cmd_error(0, res
, PSTR("error copying '%s' to '%s'"), path_old
, path_new
);
407 exit_val
= (res
!= FR_OK
);
411 void ff_move(const TCHAR
* path_old
, const TCHAR
* path_new
)
415 if (cmd_flags
& V_FLAG
)
416 printf_P(PSTR("'%s' -> '%s'\n"), path_old
, path_new
);
417 if ((res
= f_rename(path_old
, path_new
)) != FR_OK
)
418 cmd_error(0, res
, PSTR("error copying '%s' to '%s'"), path_old
, path_new
);
420 exit_val
= (res
!= FR_OK
);
424 void ff_iterate(fatfunc_t fatfunc
, int count
, char* const file
[], char* dest
)
429 char srcpath
[PATH_MAX
], destpath
[PATH_MAX
];
431 uint8_t dest_is_dir
= dest
!= NULL
&& f_stat(dest
, &finfo
) == FR_OK
&& finfo
.fattrib
& AM_DIR
;
432 for (uint8_t i
= 0; i
< count
; i
++) {
433 char* pattern
= NULL
;
434 strcpy(srcpath
, file
[i
]);
435 char* p1
= path_split(srcpath
);
438 pattern
= (char *) malloc(strlen(p1
)+1);
441 if (pattern
!= NULL
) {
442 res
= f_findfirst(&dir
, &finfo
, srcpath
, pattern
);
443 p1
= srcpath
+strlen(srcpath
)-1;
447 res
= f_findfirst(&dir
, &finfo
, ".", file
[i
]);
450 if ((res
!= FR_OK
) || (finfo
.fname
[0] == 0)) {
451 cmd_error(0, res
, PSTR("'%s': no such file or directory"), file
[i
]);
452 exit_val
= CMD_RET_FAILURE
;
455 while (res
== FR_OK
&& finfo
.fname
[0] != 0) {
456 strcpy(p1
, finfo
.fname
);
458 strcpy(destpath
, dest
);
460 strcat_P(destpath
, PSTR("/"));
461 strcat(destpath
, finfo
.fname
);
464 fatfunc(srcpath
, destpath
);
465 res
= f_findnext(&dir
, &finfo
);
468 cmd_error(CMD_RET_FAILURE
, res
, PSTR("error enumerating files"));
475 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
477 exit_val
= CMD_RET_SUCCESS
;
481 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
490 return CMD_RET_USAGE
;
497 debug_rm("==== do_rm: argc, argv[0]: %d, '%s'\n", argc
, argv
[0]);
500 return CMD_RET_USAGE
;
502 ff_iterate(ff_remove
, argc
, argv
, NULL
);
507 command_ret_t
do_cp_or_mv(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
509 exit_val
= CMD_RET_SUCCESS
;
511 fatfunc_t func
= (argv
[0][0] == 'c') ? ff_copy
: ff_move
;
514 while ((opt
= getopt(argc
, argv
, PSTR("v"))) != -1) {
520 return CMD_RET_USAGE
;
527 debug_cp("==== do_rm: argc, argv[0]: %d, '%s'\n", argc
, argv
[0]);
530 return CMD_RET_USAGE
;
532 ff_iterate(func
, argc
- 1, argv
, argv
[argc
- 1]);
538 * tst path - for debugging: test access with different functions
541 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
543 DIR Dir
; /* Directory object */
549 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
551 char * buf
= (char *) malloc(BUFFER_SIZE
);
553 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
555 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
558 printf_P(PSTR("cwd: '%s'\n"), buf
);
562 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
569 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
570 printf_P(PSTR("==== f_stat: "));
571 res
= f_stat(path
, &finfo
);
572 cmd_error(0, res
, NULL
);
574 print_dirent(&finfo
);
577 printf_P(PSTR("==== f_findfirst: "));
578 res
= f_findfirst(&Dir
, &finfo
, path
, pattern
); /* Start to search for files */
579 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
581 print_dirent(&finfo
);
585 printf_P(PSTR("==== f_opendir: "));
586 res
= f_opendir(&Dir
, path
);
587 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
590 return CMD_RET_SUCCESS
;
593 /* Work register for fs command */
596 WORD AccFiles
, AccDirs
;
602 char *path
, /* Pointer to the working buffer with start path */
603 struct stat_dat_s
*statp
611 res
= f_opendir(&dirs
, path
);
615 while (((res
= f_readdir(&dirs
, &statp
->finfo
)) == FR_OK
) &&
616 statp
->finfo
.fname
[0]) {
617 if (FF_FS_RPATH
&& statp
->finfo
.fname
[0] == '.')
619 fn
= statp
->finfo
.fname
;
620 if (statp
->finfo
.fattrib
& AM_DIR
) {
623 strcpy(path
+i
+1, fn
);
624 res
= scan_files(path
, statp
);
629 //printf_P(PSTR("%s/%s\n"), path, fn);
631 statp
->AccSize
+= statp
->finfo
.fsize
;
645 * fatstat path - Show logical drive status
648 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
655 struct stat_dat_s statp
;
657 buf
= (char *) malloc(PATH_MAX
);
659 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
663 res
= f_getfree(path
, &nfreeclst
, &fs
);
667 "Bytes/Cluster: %lu\n"
668 "Number of FATs: %u\n"
669 "Root DIR entries: %u\n"
671 "Number of clusters: %lu\n"
672 "FAT start (lba): %lu\n"
673 "DIR start (lba,cluster): %lu\n"
674 "Data start (lba): %lu\n"),
675 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
676 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
677 fs
->fatbase
, fs
->dirbase
, fs
->database
);
681 res
= f_getlabel(path
, buf
, &serial
);
685 "Volume S/N: %04X-%04X\n"),
686 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
690 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
693 my_puts_P(PSTR("\nCounting... "));
694 res
= scan_files(buf
, &statp
);
698 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
699 "%lu KB total disk space.\n%lu KB available.\n"),
700 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
701 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
708 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
710 return CMD_RET_SUCCESS
;
714 * fatread/write - load binary file to/from a dos filesystem
715 * read <d:/path/filename> <addr> [bytes [pos]]
716 * write <d:/path/filename> <addr> <bytes>
718 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
724 unsigned long bytes_rw
;
726 bool dowrite
= (argv
[0][0] == 'w');
732 if (argc
< (dowrite
? 4 : 3))
733 return CMD_RET_USAGE
;
735 addr
= eval_arg(argv
[2], NULL
);
736 if (addr
>= MAX_MEMORY
)
737 cmd_error(CMD_RET_FAILURE
, 0, PSTR("Address too high: %#lx"), addr
);
740 bytes
= eval_arg(argv
[3], NULL
);
744 pos
= eval_arg(argv
[4], NULL
);
748 if (addr
+ bytes
> MAX_MEMORY
)
749 bytes
= MAX_MEMORY
- addr
;
751 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
753 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
755 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
759 res
= f_lseek(&File
, pos
);
762 timer
= get_timer(0);
764 unsigned int cnt
, br
;
766 if (bytes
>= BUFFER_SIZE
) {
768 bytes
-= BUFFER_SIZE
;
770 cnt
= bytes
; bytes
= 0;
773 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
777 z80_read_block(buffer
, addr
, cnt
);
778 z80_bus_cmd(Release
);
779 res
= f_write(&File
, buffer
, cnt
, &br
);
783 res
= f_read(&File
, buffer
, cnt
, &br
);
786 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
790 z80_write_block(buffer
, addr
, br
);
791 z80_bus_cmd(Release
);
798 printf_P(PSTR("Disk full?\n"));
805 FRESULT fr
= f_close(&File
);
808 timer
= get_timer(timer
);
809 printf_P(PSTR("%lu (%#lx) bytes read/written with %lu bytes/sec.\n"),
810 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
817 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), argv
[1]);
819 cmd_error(CMD_RET_FAILURE
, EBUSTO
, NULL
);
821 return CMD_RET_SUCCESS
;
825 * command table for fat subcommands
828 cmd_tbl_t cmd_tbl_fat
[] = {
830 status
, 2, CTBL_RPT
, do_stat
,
831 "Show logical drive status",
835 pwd
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_pwd
,
836 "Print name of current/working directory",
840 cd
, 2, 0|CTBL_SUBCMDAUTO
, do_cd
,
841 "Change the current/working directory.",
845 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
846 "Create the DIRECTORY(ies), if they do not already exist.",
850 ls
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_ls
,
855 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
856 "FatFS test function",
860 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
861 "Remove FILE(s) or empty DIRECTORY(ies)",
862 "[OPTION]... [FILE]...\n"
863 //" -i prompt before removal\n"
864 " -n do not remove\n"
865 " -v explain what is being done"
868 cp
, CONFIG_SYS_MAXARGS
, 0, do_cp_or_mv
,
869 "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.",
870 "[OPTION]... SOURCE... DEST\n"
871 // " -f overwrite existing file ignoring write protection\n"
872 // " this option is ignored when the -n option is also used\n"
873 // " -i prompt before overwrite (overrides a previous -n option)\n"
874 // " -n do not overwrite an existing file (overrides a previous -i option)\n"
875 // " -p preserve attributes and timestamps\n"
876 " -v explain what is being done"
879 mv
, CONFIG_SYS_MAXARGS
, 0, do_cp_or_mv
,
880 "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.",
881 "[OPTION]... SOURCE DEST\n"
882 " -v explain what is being done"
887 "load binary file from a dos filesystem",
888 "<d:/path/filename> <addr> [bytes [pos]]\n"
889 " - Load binary file 'path/filename' on logical drive 'd'\n"
890 " to address 'addr' from dos filesystem.\n"
891 " 'pos' gives the file position to start loading from.\n"
892 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
893 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
894 " the load stops on end of file."
898 "write file into a dos filesystem",
899 "<d:/path/filename> <addr> <bytes>\n"
900 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
901 " starting at address 'addr'.\n"
905 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
906 "Print sub command description/usage",
908 " - print brief description of all sub commands\n"
909 "fat help command ...\n"
910 " - print detailed usage of sub cmd 'command'"
913 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
914 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
916 #ifdef CONFIG_SYS_LONGHELP
918 #endif /* CONFIG_SYS_LONGHELP */
920 #ifdef CONFIG_AUTO_COMPLETE
924 /* Mark end of table */
925 CMD_TBL_END(cmd_tbl_fat
)
929 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
931 puts_P(PSTR("Huch?"));
933 return CMD_RET_USAGE
;