]>
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 1 /* set to 1 to debug */
28 #define DEBUG_RM 0 /* set to 1 to debug */
30 #define debug_fa(fmt, args...) \
31 debug_cond(DEBUG_FA, fmt, ##args)
32 #define debug_ls(fmt, args...) \
33 debug_cond(DEBUG_LS, fmt, ##args)
34 #define debug_rm(fmt, args...) \
35 debug_cond(DEBUG_RM, fmt, ##args)
38 /* TODO: use memory size test function (z80_memsize_detect() ) */
39 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
40 #define BUFFER_SIZE FF_MAX_SS
41 #define PATH_MAX CONFIG_SYS_MAX_PATHLEN
45 * Multible (fat) partitions per physical drive are not supported,
46 * but we have up to 2 sdcard slots.
51 void setup_fatfs(void)
53 f_mount(&FatFs0
, "0:", 0);
54 f_mount(&FatFs1
, "1:", 0);
57 DWORD
get_fattime (void)
63 gmtime_r(&timer
, &tm_timer
);
65 return fat_time(&tm_timer
);
70 bool check_abort(void)
75 printf_P(PSTR("Abort\n"));
81 static const FLASH
char * const FLASH rc_strings
[] = {
84 FSTR("Internal error"),
91 FSTR("Invalid object"),
92 FSTR("Write protected"),
93 FSTR("Invalid drive"),
95 FSTR("No file system"),
99 FSTR("Not enough core"),
100 FSTR("Too many open files"),
101 FSTR("Invalid parameter")
105 const FLASH
char * fat_rctostr(FRESULT rc
)
107 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
113 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
114 static uint_fast8_t cnt
;
115 static uint32_t tstamp
;
117 if (get_timer(0) > tstamp
) {
118 tstamp
= get_timer(0) + 250;
121 putchar(swirlchar
[cnt
]);
126 * pwd - Print current directory of the current drive.
129 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
134 res
= f_getcwd(buf
, PATH_MAX
); /* Get current directory path */
137 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
141 return CMD_RET_SUCCESS
;
146 * cd - Change the current/working directory.
149 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
155 path
= getenv_str(PSTR(ENV_HOME
));
157 cmd_error(CMD_RET_FAILURE
, 0, PSTR("\"%S\" not set"), PSTR(ENV_HOME
));
162 if (strlen(path
) > 1 && path
[1] == ':')
163 res
= f_chdrive(path
);
169 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
171 return CMD_RET_SUCCESS
;
176 void print_dirent(FILINFO
*f
)
178 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
179 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
180 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
181 (f
->fattrib
& AM_HID
) ? 'H' : '-',
182 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
183 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
184 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
185 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
189 char *path_split(char *p
)
191 if (isdigit(p
[0]) && (p
[1] == ':'))
194 char *ps
= strrchr(p
, '/');
198 memmove(ps
+1, ps
, strlen(ps
)+1);
206 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
208 int ret
= CMD_RET_SUCCESS
;
212 return CMD_RET_USAGE
;
214 for (uint8_t i
= 1; i
< argc
; i
++) {
215 if ((res
= f_mkdir(argv
[i
])) != FR_OK
) {
216 ret
= CMD_RET_FAILURE
;
217 cmd_error(0, res
, PSTR("cannot create directory '%s'"), argv
[i
]);
224 * ls path - Directory listing
227 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
230 DIR dir
; /* Directory object */
240 memset(buf
, 0, PATH_MAX
);
244 strncpy(path
, argv
[1], PATH_MAX
-1);
246 pattern
= path_split(path
);
247 debug_ls("### path, pattern: '%s', '%s'\n", path
[0] ? path
: "<NULL>", pattern
? pattern
: "<NULL>");
249 if (pattern
== NULL
) {
250 res
= f_opendir(&dir
, path
);
251 if (res
== FR_NO_PATH
|| res
== FR_INVALID_NAME
) {
254 } else if (res
!= FR_OK
) {
255 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), path
);
258 debug_ls("### path, pattern: '%s', '%s'\n", path
, pattern
? pattern
: "<NULL>");
260 if (pattern
== NULL
|| *pattern
== '\0')
263 debug_ls("### path, pattern: '%s', '%s'\n", path
, pattern
? pattern
: "<NULL>");
265 res
= f_findfirst(&dir
, &finfo
, path
, pattern
);
270 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
273 if (finfo
.fattrib
& AM_DIR
) {
276 s1
++; p1
+= finfo
.fsize
;
278 print_dirent(&finfo
);
281 res
= f_findnext(&dir
, &finfo
);
285 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
286 if (f_getfree(path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
287 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
291 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
293 return CMD_RET_SUCCESS
;
296 typedef FRESULT (*fatfunc_t
)(const TCHAR
* path_old
, const TCHAR
* path_new
);
299 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
300 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
301 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
302 #define P_FLAG (1<<4) // preserve attributes and timestamps
303 #define V_FLAG (1<<5) // explain what is being done
305 char* splitpath(const char* path
)
307 char* fs
= strrchr(path
, '/');
308 char* bs
= strrchr(path
, '\\');
312 } else if (bs
!= NULL
) {
319 FRESULT
ff_remove(const TCHAR
*file
, const TCHAR
*dest UNUSED
)
323 debug_rm("==== ff_remove: '%s'\n", file
);
325 if (!(cmd_flags
& N_FLAG
)) {
326 if ((res
= f_unlink(file
)) == FR_OK
) {
327 if (cmd_flags
& V_FLAG
)
328 printf_P(PSTR("removed '%s'\n"), file
);
330 cmd_error(0, res
, PSTR("cannot remove '%s'"), file
);
333 printf_P(PSTR("not removed '%s'\n"), file
);
339 void ff_iterate(fatfunc_t fatfunc
, int count
, char* const file
[], char* dest
)
344 char srcpath
[PATH_MAX
], destpath
[PATH_MAX
];
346 uint8_t dest_is_dir
= dest
!= NULL
&& f_stat(dest
, &finfo
) == FR_OK
&& finfo
.fattrib
& AM_DIR
;
347 for (uint8_t i
= 0; i
< count
; i
++) {
348 char* pattern
= NULL
;
349 strcpy(srcpath
, file
[i
]);
350 char* p1
= path_split(srcpath
);
353 pattern
= (char *) malloc(strlen(p1
)+1);
357 debug_rm("### iter1 srcpath, pattern: '%s', '%s'\n", srcpath
, pattern
? pattern
: "<NULL>");
359 if (pattern
!= NULL
) {
360 res
= f_findfirst(&dir
, &finfo
, srcpath
, pattern
);
361 p1
= srcpath
+strlen(srcpath
)-1;
365 res
= f_findfirst(&dir
, &finfo
, ".", file
[i
]);
368 if (finfo
.fname
[0] == 0)
369 cmd_error(0, res
, PSTR("cannot remove '%s': no such file or directory"), file
[i
]);
372 while (res
== FR_OK
&& finfo
.fname
[0] != 0) {
373 strcpy(p1
, finfo
.fname
);
375 debug_rm("### iter3 srcpath: '%s'\n", srcpath
);
378 strcpy(destpath
, dest
);
380 strcat_P(destpath
, PSTR("/"));
381 strcat(destpath
, finfo
.fname
);
384 fatfunc(srcpath
, destpath
);
385 res
= f_findnext(&dir
, &finfo
);
386 debug_rm("### iter4 res, .fname: %d, '%s'\n", res
, finfo
.fname
);
389 res
= f_closedir(&dir
);
393 cmd_error(CMD_RET_FAILURE
, res
, PSTR("error enumerating files"));
394 //printf_P(PSTR("error enumerating files: %S\n"), ffw_error(res));
397 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
402 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
411 return CMD_RET_USAGE
;
418 debug_rm("==== do_rm: argc, argv[0]: %d, '%s'\n", argc
, argv
[0]);
421 return CMD_RET_USAGE
;
423 ff_iterate(ff_remove
, argc
, argv
, NULL
);
425 return CMD_RET_SUCCESS
;
429 * tst path - for debugging: test access with different functions
432 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
434 DIR Dir
; /* Directory object */
440 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
442 char * buf
= (char *) malloc(BUFFER_SIZE
);
444 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
446 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
449 printf_P(PSTR("cwd: '%s'\n"), buf
);
453 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
460 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
461 printf_P(PSTR("==== f_stat: "));
462 res
= f_stat(path
, &finfo
);
463 cmd_error(0, res
, NULL
);
465 print_dirent(&finfo
);
468 printf_P(PSTR("==== f_findfirst: "));
469 res
= f_findfirst(&Dir
, &finfo
, path
, pattern
); /* Start to search for files */
470 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
472 print_dirent(&finfo
);
476 printf_P(PSTR("==== f_opendir: "));
477 res
= f_opendir(&Dir
, path
);
478 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
481 return CMD_RET_SUCCESS
;
484 /* Work register for fs command */
487 WORD AccFiles
, AccDirs
;
493 char *path
, /* Pointer to the working buffer with start path */
494 struct stat_dat_s
*statp
502 res
= f_opendir(&dirs
, path
);
506 while (((res
= f_readdir(&dirs
, &statp
->finfo
)) == FR_OK
) &&
507 statp
->finfo
.fname
[0]) {
508 if (FF_FS_RPATH
&& statp
->finfo
.fname
[0] == '.')
510 fn
= statp
->finfo
.fname
;
511 if (statp
->finfo
.fattrib
& AM_DIR
) {
514 strcpy(path
+i
+1, fn
);
515 res
= scan_files(path
, statp
);
520 //printf_P(PSTR("%s/%s\n"), path, fn);
522 statp
->AccSize
+= statp
->finfo
.fsize
;
536 * fatstat path - Show logical drive status
539 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
546 struct stat_dat_s statp
;
548 buf
= (char *) malloc(PATH_MAX
);
550 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
554 res
= f_getfree(path
, &nfreeclst
, &fs
);
558 "Bytes/Cluster: %lu\n"
559 "Number of FATs: %u\n"
560 "Root DIR entries: %u\n"
562 "Number of clusters: %lu\n"
563 "FAT start (lba): %lu\n"
564 "DIR start (lba,cluster): %lu\n"
565 "Data start (lba): %lu\n"),
566 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
567 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
568 fs
->fatbase
, fs
->dirbase
, fs
->database
);
572 res
= f_getlabel(path
, buf
, &serial
);
576 "Volume S/N: %04X-%04X\n"),
577 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
581 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
584 my_puts_P(PSTR("\nCounting... "));
585 res
= scan_files(buf
, &statp
);
589 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
590 "%lu KB total disk space.\n%lu KB available.\n"),
591 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
592 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
599 cmd_error(CMD_RET_FAILURE
, res
, NULL
);
601 return CMD_RET_SUCCESS
;
605 * fatread/write - load binary file to/from a dos filesystem
606 * read <d:/path/filename> <addr> [bytes [pos]]
607 * write <d:/path/filename> <addr> <bytes>
609 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
615 unsigned long bytes_rw
;
617 bool dowrite
= (argv
[0][0] == 'w');
623 if (argc
< (dowrite
? 4 : 3))
624 return CMD_RET_USAGE
;
626 addr
= eval_arg(argv
[2], NULL
);
627 if (addr
>= MAX_MEMORY
)
628 cmd_error(CMD_RET_FAILURE
, 0, PSTR("Address too high: %#lx"), addr
);
631 bytes
= eval_arg(argv
[3], NULL
);
635 pos
= eval_arg(argv
[4], NULL
);
639 if (addr
+ bytes
> MAX_MEMORY
)
640 bytes
= MAX_MEMORY
- addr
;
642 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
644 cmd_error(CMD_RET_FAILURE
, ENOMEM
, NULL
);
646 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
650 res
= f_lseek(&File
, pos
);
653 timer
= get_timer(0);
655 unsigned int cnt
, br
;
657 if (bytes
>= BUFFER_SIZE
) {
659 bytes
-= BUFFER_SIZE
;
661 cnt
= bytes
; bytes
= 0;
664 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
668 z80_read_block(buffer
, addr
, cnt
);
669 z80_bus_cmd(Release
);
670 res
= f_write(&File
, buffer
, cnt
, &br
);
674 res
= f_read(&File
, buffer
, cnt
, &br
);
677 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
681 z80_write_block(buffer
, addr
, br
);
682 z80_bus_cmd(Release
);
689 printf_P(PSTR("Disk full?\n"));
696 FRESULT fr
= f_close(&File
);
699 timer
= get_timer(timer
);
700 printf_P(PSTR("%lu (%#lx) bytes read/written with %lu bytes/sec.\n"),
701 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
708 cmd_error(CMD_RET_FAILURE
, res
, PSTR("'%s'"), argv
[1]);
710 cmd_error(CMD_RET_FAILURE
, EBUSTO
, NULL
);
712 return CMD_RET_SUCCESS
;
716 * command table for fat subcommands
719 cmd_tbl_t cmd_tbl_fat
[] = {
721 status
, 2, CTBL_RPT
, do_stat
,
722 "Show logical drive status",
726 pwd
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_pwd
,
727 "Print name of current/working directory",
731 cd
, 2, 0|CTBL_SUBCMDAUTO
, do_cd
,
732 "Change the current/working directory.",
736 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
737 "Create the DIRECTORY(ies), if they do not already exist.",
741 ls
, 2, CTBL_RPT
|CTBL_SUBCMDAUTO
, do_ls
,
746 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
747 "FatFS test function",
751 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
753 "[OPTION]... [FILE]...\n"
754 //" -i prompt before removal\n"
755 " -v explain what is being done\n"
757 "rm removes directories, if they are empty."
761 "load binary file from a dos filesystem",
762 "<d:/path/filename> <addr> [bytes [pos]]\n"
763 " - Load binary file 'path/filename' on logical drive 'd'\n"
764 " to address 'addr' from dos filesystem.\n"
765 " 'pos' gives the file position to start loading from.\n"
766 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
767 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
768 " the load stops on end of file."
772 "write file into a dos filesystem",
773 "<d:/path/filename> <addr> <bytes>\n"
774 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
775 " starting at address 'addr'.\n"
779 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
780 "Print sub command description/usage",
782 " - print brief description of all sub commands\n"
783 "fat help command ...\n"
784 " - print detailed usage of sub cmd 'command'"
787 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
788 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
790 #ifdef CONFIG_SYS_LONGHELP
792 #endif /* CONFIG_SYS_LONGHELP */
794 #ifdef CONFIG_AUTO_COMPLETE
798 /* Mark end of table */
799 CMD_TBL_END(cmd_tbl_fat
)
803 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
805 puts_P(PSTR("Huch?"));
807 return CMD_RET_USAGE
;