]>
cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
c97fd79cdb51b5ba271cc5a159da06d4fab6fad3
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_iterate(fatfunc_t fatfunc
, int count
, char* const file
[], char* dest
)
416 char srcpath
[PATH_MAX
], destpath
[PATH_MAX
];
418 uint8_t dest_is_dir
= dest
!= NULL
&& f_stat(dest
, &finfo
) == FR_OK
&& finfo
.fattrib
& AM_DIR
;
419 for (uint8_t i
= 0; i
< count
; i
++) {
420 char* pattern
= NULL
;
421 strcpy(srcpath
, file
[i
]);
422 char* p1
= path_split(srcpath
);
425 pattern
= (char *) malloc(strlen(p1
)+1);
428 if (pattern
!= NULL
) {
429 res
= f_findfirst(&dir
, &finfo
, srcpath
, pattern
);
430 p1
= srcpath
+strlen(srcpath
)-1;
434 res
= f_findfirst(&dir
, &finfo
, ".", file
[i
]);
437 if ((res
!= FR_OK
) || (finfo
.fname
[0] == 0)) {
438 cmd_error(0, res
, PSTR("'%s': no such file or directory"), file
[i
]);
439 exit_val
= CMD_RET_FAILURE
;
442 while (res
== FR_OK
&& finfo
.fname
[0] != 0) {
443 strcpy(p1
, finfo
.fname
);
445 strcpy(destpath
, dest
);
447 strcat_P(destpath
, PSTR("/"));
448 strcat(destpath
, finfo
.fname
);
451 fatfunc(srcpath
, destpath
);
452 res
= f_findnext(&dir
, &finfo
);
455 cmd_error(CMD_RET_FAILURE
, res
, PSTR("error enumerating files"));
462 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
464 exit_val
= CMD_RET_SUCCESS
;
468 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
477 return CMD_RET_USAGE
;
484 debug_rm("==== do_rm: argc, argv[0]: %d, '%s'\n", argc
, argv
[0]);
487 return CMD_RET_USAGE
;
489 ff_iterate(ff_remove
, argc
, argv
, NULL
);
494 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
496 exit_val
= CMD_RET_SUCCESS
;
500 while ((opt
= getopt(argc
, argv
, PSTR("v"))) != -1) {
506 return CMD_RET_USAGE
;
513 debug_cp("==== do_rm: argc, argv[0]: %d, '%s'\n", argc
, argv
[0]);
516 return CMD_RET_USAGE
;
518 ff_iterate(ff_copy
, argc
- 1, argv
, argv
[argc
- 1]);
524 * tst path - for debugging: test access with different functions
527 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
529 DIR Dir
; /* Directory object */
535 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
537 char * buf
= (char *) malloc(BUFFER_SIZE
);
539 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
541 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
544 printf_P(PSTR("cwd: '%s'\n"), buf
);
548 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
555 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
556 printf_P(PSTR("==== f_stat: "));
557 res
= f_stat(path
, &finfo
);
558 cmd_error(0, res
, NULL
);
560 print_dirent(&finfo
);
563 printf_P(PSTR("==== f_findfirst: "));
564 res
= f_findfirst(&Dir
, &finfo
, path
, pattern
); /* Start to search for files */
565 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
567 print_dirent(&finfo
);
571 printf_P(PSTR("==== f_opendir: "));
572 res
= f_opendir(&Dir
, path
);
573 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
576 return CMD_RET_SUCCESS
;
579 /* Work register for fs command */
582 WORD AccFiles
, AccDirs
;
588 char *path
, /* Pointer to the working buffer with start path */
589 struct stat_dat_s
*statp
597 res
= f_opendir(&dirs
, path
);
601 while (((res
= f_readdir(&dirs
, &statp
->finfo
)) == FR_OK
) &&
602 statp
->finfo
.fname
[0]) {
603 if (FF_FS_RPATH
&& statp
->finfo
.fname
[0] == '.')
605 fn
= statp
->finfo
.fname
;
606 if (statp
->finfo
.fattrib
& AM_DIR
) {
609 strcpy(path
+i
+1, fn
);
610 res
= scan_files(path
, statp
);
615 //printf_P(PSTR("%s/%s\n"), path, fn);
617 statp
->AccSize
+= statp
->finfo
.fsize
;
631 * fatstat path - Show logical drive status
634 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
641 struct stat_dat_s statp
;
643 buf
= (char *) malloc(PATH_MAX
);
645 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
649 res
= f_getfree(path
, &nfreeclst
, &fs
);
653 "Bytes/Cluster: %lu\n"
654 "Number of FATs: %u\n"
655 "Root DIR entries: %u\n"
657 "Number of clusters: %lu\n"
658 "FAT start (lba): %lu\n"
659 "DIR start (lba,cluster): %lu\n"
660 "Data start (lba): %lu\n"),
661 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
662 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
663 fs
->fatbase
, fs
->dirbase
, fs
->database
);
667 res
= f_getlabel(path
, buf
, &serial
);
671 "Volume S/N: %04X-%04X\n"),
672 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
676 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
679 my_puts_P(PSTR("\nCounting... "));
680 res
= scan_files(buf
, &statp
);
684 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
685 "%lu KB total disk space.\n%lu KB available.\n"),
686 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
687 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
694 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
696 return CMD_RET_SUCCESS
;
700 * fatread/write - load binary file to/from a dos filesystem
701 * read <d:/path/filename> <addr> [bytes [pos]]
702 * write <d:/path/filename> <addr> <bytes>
704 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
710 unsigned long bytes_rw
;
712 bool dowrite
= (argv
[0][0] == 'w');
718 if (argc
< (dowrite
? 4 : 3))
719 return CMD_RET_USAGE
;
721 addr
= eval_arg(argv
[2], NULL
);
722 if (addr
>= MAX_MEMORY
)
723 cmd_error(CMD_RET_FAILURE
, 0, PSTR("Address too high: %#lx"), addr
);
726 bytes
= eval_arg(argv
[3], NULL
);
730 pos
= eval_arg(argv
[4], NULL
);
734 if (addr
+ bytes
> MAX_MEMORY
)
735 bytes
= MAX_MEMORY
- addr
;
737 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
739 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
741 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
745 res
= f_lseek(&File
, pos
);
748 timer
= get_timer(0);
750 unsigned int cnt
, br
;
752 if (bytes
>= BUFFER_SIZE
) {
754 bytes
-= BUFFER_SIZE
;
756 cnt
= bytes
; bytes
= 0;
759 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
763 z80_read_block(buffer
, addr
, cnt
);
764 z80_bus_cmd(Release
);
765 res
= f_write(&File
, buffer
, cnt
, &br
);
769 res
= f_read(&File
, buffer
, cnt
, &br
);
772 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
776 z80_write_block(buffer
, addr
, br
);
777 z80_bus_cmd(Release
);
784 printf_P(PSTR("Disk full?\n"));
791 FRESULT fr
= f_close(&File
);
794 timer
= get_timer(timer
);
795 printf_P(PSTR("%lu (%#lx) bytes read/written with %lu bytes/sec.\n"),
796 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
803 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), argv
[1]);
805 cmd_error(CMD_RET_FAILURE
, EBUSTO
, NULL
);
807 return CMD_RET_SUCCESS
;
811 * command table for fat subcommands
814 cmd_tbl_t cmd_tbl_fat
[] = {
816 status
, 2, CTBL_RPT
, do_stat
,
817 "Show logical drive status",
821 pwd
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_pwd
,
822 "Print name of current/working directory",
826 cd
, 2, 0|CTBL_SUBCMDAUTO
, do_cd
,
827 "Change the current/working directory.",
831 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
832 "Create the DIRECTORY(ies), if they do not already exist.",
836 ls
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_ls
,
841 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
842 "FatFS test function",
846 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
847 "Remove FILE(s) or empty DIRECTORY(ies)",
848 "[OPTION]... [FILE]...\n"
849 //" -i prompt before removal\n"
850 " -n do not remove\n"
851 " -v explain what is being done"
854 cp
, CONFIG_SYS_MAXARGS
, 0, do_cp
,
855 "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.",
856 "[OPTION]... SOURCE... DEST\n"
857 // " -f overwrite existing file ignoring write protection\n"
858 // " this option is ignored when the -n option is also used\n"
859 // " -i prompt before overwrite (overrides a previous -n option)\n"
860 // " -n do not overwrite an existing file (overrides a previous -i option)\n"
861 // " -p preserve attributes and timestamps\n"
862 " -v explain what is being done"
867 "load binary file from a dos filesystem",
868 "<d:/path/filename> <addr> [bytes [pos]]\n"
869 " - Load binary file 'path/filename' on logical drive 'd'\n"
870 " to address 'addr' from dos filesystem.\n"
871 " 'pos' gives the file position to start loading from.\n"
872 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
873 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
874 " the load stops on end of file."
878 "write file into a dos filesystem",
879 "<d:/path/filename> <addr> <bytes>\n"
880 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
881 " starting at address 'addr'.\n"
885 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
886 "Print sub command description/usage",
888 " - print brief description of all sub commands\n"
889 "fat help command ...\n"
890 " - print detailed usage of sub cmd 'command'"
893 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
894 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
896 #ifdef CONFIG_SYS_LONGHELP
898 #endif /* CONFIG_SYS_LONGHELP */
900 #ifdef CONFIG_AUTO_COMPLETE
904 /* Mark end of table */
905 CMD_TBL_END(cmd_tbl_fat
)
909 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
911 puts_P(PSTR("Huch?"));
913 return CMD_RET_USAGE
;