]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
add fat command cp
[z180-stamp.git] / avr / cmd_fat.c
CommitLineData
35edb766 1/*
016251b6 2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
35edb766 3 *
2d914b45 4 * SPDX-License-Identifier: GPL-2.0
35edb766
L
5 */
6
7/*
8 * FAT filesystem commands
9 */
10
416bc215 11#include "common.h"
8a2b0da0 12#include "cmd_fat.h"
bb497e9d
L
13#include <time.h>
14uint32_t fat_time(const struct tm * timeptr);
2f53dd65 15
2f53dd65
L
16#include "ff.h"
17#include "z80-if.h"
2d914b45 18#include "eval_arg.h"
4565be9a 19#include "con-utils.h"
2f53dd65
L
20#include "print-utils.h"
21#include "timer.h"
4f881b02 22#include "debug.h"
8a2b0da0 23#include "env.h"
465090d4 24#include "getopt-min.h"
66f6a44e
L
25
26#define DEBUG_FA 0 /* set to 1 to debug */
b6cf9ca4 27#define DEBUG_LS 0 /* set to 1 to debug */
465090d4 28#define DEBUG_RM 0 /* set to 1 to debug */
b6cf9ca4 29#define DEBUG_CP 0 /* set to 1 to debug */
66f6a44e
L
30
31#define debug_fa(fmt, args...) \
32 debug_cond(DEBUG_FA, fmt, ##args)
416bc215
L
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)
b6cf9ca4
L
37#define debug_cp(fmt, args...) \
38 debug_cond(DEBUG_CP, fmt, ##args)
66f6a44e
L
39
40
416bc215 41/* TODO: use memory size test function (z80_memsize_detect() ) */
b6cf9ca4
L
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
7439a2b5 46
19b9a7d8 47
016251b6
L
48/*
49 * Multible (fat) partitions per physical drive are not supported,
50 * but we have up to 2 sdcard slots.
51 */
416bc215
L
52static FATFS FatFs0;
53static FATFS FatFs1;
7439a2b5 54
016251b6
L
55void setup_fatfs(void)
56{
57 f_mount(&FatFs0, "0:", 0);
58 f_mount(&FatFs1, "1:", 0);
59}
8a2b0da0 60
2f53dd65
L
61DWORD get_fattime (void)
62{
19b9a7d8
L
63 time_t timer;
64 struct tm tm_timer;
65
66 time(&timer);
67 gmtime_r(&timer, &tm_timer);
68
bb497e9d 69 return fat_time(&tm_timer);
2f53dd65
L
70}
71
72
416bc215
L
73static
74bool check_abort(void)
b15d22a4
L
75{
76 bool ret = ctrlc();
77
78 if (ret)
79 printf_P(PSTR("Abort\n"));
80
81 return ret;
82}
83
84
022330eb 85static const FLASH char * const FLASH rc_strings[] = {
414caa77
L
86 FSTR("Success"),
87 FSTR("Disk error"),
88 FSTR("Internal error"),
89 FSTR("Not ready"),
90 FSTR("No file"),
91 FSTR("No path"),
92 FSTR("Invalid name"),
93 FSTR("Denied"),
94 FSTR("Exist"),
95 FSTR("Invalid object"),
96 FSTR("Write protected"),
97 FSTR("Invalid drive"),
98 FSTR("Not enabled"),
99 FSTR("No file system"),
100 FSTR("Mkfs aborted"),
101 FSTR("Timeout"),
102 FSTR("Locked"),
103 FSTR("Not enough core"),
104 FSTR("Too many open files"),
105 FSTR("Invalid parameter")
022330eb
L
106 };
107
2f53dd65 108
414caa77 109const FLASH char * fat_rctostr(FRESULT rc)
7439a2b5 110{
022330eb 111 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
7439a2b5
L
112}
113
416bc215
L
114static
115void swirl(void)
b15d22a4 116{
6204987c 117 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
118 static uint_fast8_t cnt;
119 static uint32_t tstamp;
120
121 if (get_timer(0) > tstamp) {
6204987c 122 tstamp = get_timer(0) + 250;
81a43faf
L
123 putchar('\b');
124 cnt = (cnt+1) & 3;
125 putchar(swirlchar[cnt]);
b15d22a4
L
126 }
127}
128
8a2b0da0
L
129/*
130 * pwd - Print current directory of the current drive.
131 *
132 */
016251b6 133command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0
L
134{
135 FRESULT res;
416bc215 136 TCHAR buf[PATH_MAX];
8a2b0da0 137
7439a2b5 138 res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
8a2b0da0 139
86ee5f8b 140 if (res != FR_OK)
414caa77 141 cmd_error(CMD_RET_FAILURE, res, NULL);
86ee5f8b 142
416bc215
L
143 puts(buf);
144
8a2b0da0
L
145 return CMD_RET_SUCCESS;
146}
147
148
149/*
150 * cd - Change the current/working directory.
151 *
152 */
016251b6 153command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
8a2b0da0 154{
416bc215 155 TCHAR *path;
7439a2b5
L
156 FRESULT res = FR_OK;
157
8a2b0da0 158 if (argc < 2) {
416bc215
L
159 path = getenv_str(PSTR(ENV_HOME));
160 if (path == NULL) {
161 cmd_error(CMD_RET_FAILURE, 0, PSTR("\"%S\" not set"), PSTR(ENV_HOME));
8a2b0da0
L
162 }
163 } else
416bc215 164 path = argv[1];
8a2b0da0 165
416bc215
L
166 if (strlen(path) > 1 && path[1] == ':')
167 res = f_chdrive(path);
168
169 if (res == FR_OK)
170 res = f_chdir(path);
7439a2b5 171
86ee5f8b
L
172 if (res != FR_OK)
173 cmd_error(CMD_RET_FAILURE, res, NULL);
174
8a2b0da0
L
175 return CMD_RET_SUCCESS;
176}
177
2f53dd65 178
416bc215
L
179static
180void print_dirent(FILINFO *f)
181{
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,
190 f->fsize, f->fname);
191}
192
193char *path_split(char *p)
194{
195 if (isdigit(p[0]) && (p[1] == ':'))
196 p += 2;
197
198 char *ps = strrchr(p, '/');
199 if (ps) {
200 if (ps == p) {
201 ++ps;
202 memmove(ps+1, ps, strlen(ps)+1);
203 }
204 *ps++ = '\0';
205 }
206
207 return ps;
208}
209
465090d4
L
210command_ret_t do_mkdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
211{
212 int ret = CMD_RET_SUCCESS;
213 FRESULT res;
214
215 if (argc < 1)
216 return CMD_RET_USAGE;
217
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]);
222 }
223 }
224 return ret;
225}
226
2f53dd65 227/*
016251b6 228 * ls path - Directory listing
2f53dd65
L
229 *
230 */
016251b6 231command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 232{
bbd45c46 233 FATFS *fs;
416bc215
L
234 DIR dir; /* Directory object */
235 FILINFO finfo;
2f53dd65
L
236 unsigned long p1;
237 unsigned int s1, s2;
8a2b0da0 238 FRESULT res = FR_OK;
416bc215
L
239 char buf[PATH_MAX];
240 char *path = buf;
241 char *pattern;
2f53dd65 242
2f53dd65 243
416bc215
L
244 memset(buf, 0, PATH_MAX);
245 if (argc < 2) {
246 buf[0] = '.';
247 } else {
248 strncpy(path, argv[1], PATH_MAX-1);
249 }
250 pattern = path_split(path);
251 debug_ls("### path, pattern: '%s', '%s'\n", path[0] ? path : "<NULL>", pattern ? pattern : "<NULL>");
8a2b0da0 252
416bc215
L
253 if (pattern == NULL) {
254 res = f_opendir(&dir, path);
255 if (res == FR_NO_PATH || res == FR_INVALID_NAME) {
256 pattern = path;
257 path = ".";
258 } else if (res != FR_OK) {
259 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), path);
260 }
261 }
262 debug_ls("### path, pattern: '%s', '%s'\n", path, pattern ? pattern : "<NULL>");
8a2b0da0 263
416bc215
L
264 if (pattern == NULL || *pattern == '\0')
265 pattern = "*";
266
267 debug_ls("### path, pattern: '%s', '%s'\n", path, pattern ? pattern : "<NULL>");
268
269 res = f_findfirst(&dir, &finfo, path, pattern);
2f53dd65
L
270
271 p1 = s1 = s2 = 0;
272 for(;;) {
416bc215
L
273 if (res != FR_OK)
274 cmd_error(CMD_RET_FAILURE, res, NULL);
275 if (!finfo.fname[0])
2f53dd65 276 break;
416bc215 277 if (finfo.fattrib & AM_DIR) {
2f53dd65
L
278 s2++;
279 } else {
416bc215 280 s1++; p1 += finfo.fsize;
2f53dd65 281 }
416bc215 282 print_dirent(&finfo);
b15d22a4
L
283 if (check_abort())
284 break;
416bc215 285 res = f_findnext(&dir, &finfo);
2f53dd65
L
286 }
287
288 if (res == FR_OK) {
289 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
416bc215 290 if (f_getfree(path, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
291 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
292 }
293
86ee5f8b
L
294 if (res)
295 cmd_error(CMD_RET_FAILURE, res, NULL);
2f53dd65
L
296
297 return CMD_RET_SUCCESS;
298}
299
465090d4 300
b6cf9ca4
L
301typedef void (*fatfunc_t)(const TCHAR* path_old, const TCHAR* path_new);
302
303command_ret_t exit_val;
465090d4
L
304uint8_t cmd_flags;
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
310
311char* splitpath(const char* path)
312{
313 char* fs = strrchr(path, '/');
314 char* bs = strrchr(path, '\\');
315 if (fs > bs) {
316 *fs = '\0';
317 return fs + 1;
318 } else if (bs != NULL) {
319 *bs = '\0';
320 return bs + 1;
321 }
322 return NULL;
323}
324
b6cf9ca4
L
325/*
326 * Copy File
327*/
328FRESULT copy_file(const TCHAR* src, const TCHAR* dst)
329{
330 FIL fsrc, fdst;
331 FRESULT res;
332
333 /* File copy buffer */
334 uint8_t *buffer = (uint8_t *) malloc(CPY_BUF_SIZE);
335 if (buffer == NULL) {
336 res = (FRESULT) ENOMEM;
337 } else {
338
339 /* Open source file */
340 res = f_open(&fsrc, src, FA_READ);
341 if (res == FR_OK) {
342
343 /* Create destination file */
344 res = f_open(&fdst, dst, FA_WRITE | FA_CREATE_NEW);
345 if (res == FR_OK) {
346 UINT br, bw; /* File read/write count */
347
348 /* Copy source to destination */
349 for (;;) {
350 res = f_read(&fsrc, buffer, CPY_BUF_SIZE, &br); /* Read a chunk of source file */
351 if (res || br == 0)
352 break; /* error or eof */
353 res = f_write(&fdst, buffer, br, &bw); /* Write it to the destination file */
354 if (res != FR_OK)
355 break;
356 if (bw < br) {
357 res = (FRESULT) EFULL; /* disk full */
358 break;
359 }
360 }
361
362debug_cp("==== copy() res: %d, br: %d, bw: %d\n", res, br, bw);
363 f_close(&fdst);
364 if (res != FR_OK)
365 f_unlink(dst);
366 }
367 f_close(&fsrc);
368 }
369 free(buffer);
370 }
371
372 return res;
373}
374
375void ff_remove(const TCHAR *file, const TCHAR *dest UNUSED)
465090d4
L
376{
377 FRESULT res = FR_OK;
378
379 debug_rm("==== ff_remove: '%s'\n", file);
380
381 if (!(cmd_flags & N_FLAG)) {
382 if ((res = f_unlink(file)) == FR_OK) {
383 if (cmd_flags & V_FLAG)
b6cf9ca4 384 printf_P(PSTR("removed: '%s'\n"), file);
465090d4
L
385 } else {
386 cmd_error(0, res, PSTR("cannot remove '%s'"), file);
387 }
388 } else {
b6cf9ca4 389 printf_P(PSTR("not removed: '%s'\n"), file);
465090d4
L
390 }
391
b6cf9ca4
L
392 exit_val = (res != FR_OK);
393 return;
394}
395
396void ff_copy(const TCHAR* path_old, const TCHAR* path_new)
397{
398 FRESULT res;
399
400 debug_cp("==== ff_copy: '%s' --> '%s'\n", path_old, path_new);
401
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);
406
407 exit_val = (res != FR_OK);
408 return;
465090d4
L
409}
410
411void ff_iterate(fatfunc_t fatfunc, int count, char* const file[], char* dest)
412{
413 FRESULT res = 0;
414 DIR dir;
415 FILINFO finfo;
416 char srcpath[PATH_MAX], destpath[PATH_MAX];
417
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);
423
424 if (p1 != NULL) {
425 pattern = (char *) malloc(strlen(p1)+1);
426 strcpy(pattern, p1);
427 }
465090d4
L
428 if (pattern != NULL) {
429 res = f_findfirst(&dir, &finfo, srcpath, pattern);
430 p1 = srcpath+strlen(srcpath)-1;
431 if (*p1++ != '/')
432 *(p1++) = '/';
433 } else {
434 res = f_findfirst(&dir, &finfo, ".", file[i]);
435 p1 = srcpath;
436 }
b6cf9ca4
L
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;
440 } else {
465090d4 441
b6cf9ca4
L
442 while (res == FR_OK && finfo.fname[0] != 0) {
443 strcpy(p1, finfo.fname);
444 if (dest != NULL) {
445 strcpy(destpath, dest);
446 if (dest_is_dir) {
447 strcat_P(destpath, PSTR("/"));
448 strcat(destpath, finfo.fname);
449 }
450 }
451 fatfunc(srcpath, destpath);
452 res = f_findnext(&dir, &finfo);
453 }
454 if (res != FR_OK)
455 cmd_error(CMD_RET_FAILURE, res, PSTR("error enumerating files"));
456 }
457 f_closedir(&dir);
465090d4
L
458 free(pattern);
459 }
465090d4
L
460}
461
462command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
463{
b6cf9ca4 464 exit_val = CMD_RET_SUCCESS;
465090d4 465 cmd_flags = 0;
b6cf9ca4 466
465090d4
L
467 int opt;
468 while ((opt = getopt(argc, argv, PSTR("nv"))) != -1) {
469 switch (opt) {
470 case 'n':
471 cmd_flags |= N_FLAG;
472 break;
473 case 'v':
474 cmd_flags |= V_FLAG;
475 break;
476 default:
477 return CMD_RET_USAGE;
478 break;
479 }
480 }
481 argc -= optind;
482 argv += optind;
483
484 debug_rm("==== do_rm: argc, argv[0]: %d, '%s'\n", argc, argv[0]);
485
486 if (argc < 1)
487 return CMD_RET_USAGE;
488
489 ff_iterate(ff_remove, argc, argv, NULL);
490
b6cf9ca4
L
491 return exit_val;
492}
493
494command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
495{
496 exit_val = CMD_RET_SUCCESS;
497 cmd_flags = 0;
498
499 int opt;
500 while ((opt = getopt(argc, argv, PSTR("v"))) != -1) {
501 switch (opt) {
502 case 'v':
503 cmd_flags |= V_FLAG;
504 break;
505 default:
506 return CMD_RET_USAGE;
507 break;
508 }
509 }
510 argc -= optind;
511 argv += optind;
512
513 debug_cp("==== do_rm: argc, argv[0]: %d, '%s'\n", argc, argv[0]);
514
515 if (argc < 2)
516 return CMD_RET_USAGE;
517
518 ff_iterate(ff_copy, argc - 1, argv, argv[argc - 1]);
519
520 return exit_val;
465090d4 521}
416bc215
L
522
523/*
524 * tst path - for debugging: test access with different functions
525 *
526 */
527command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
35edb766 528{
416bc215
L
529 DIR Dir; /* Directory object */
530 FILINFO finfo;
531 FRESULT res = FR_OK;
532 char *path = "";
533 char *pattern = "*";
534
535 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO));
536
537 char * buf = (char *) malloc(BUFFER_SIZE);
538 if (buf == NULL) {
539 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
35edb766 540 }
416bc215 541 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
35edb766 542
416bc215
L
543 if (!res) {
544 printf_P(PSTR("cwd: '%s'\n"), buf);
545 }
546 free(buf);
547 if (res)
548 cmd_error(CMD_RET_FAILURE, res, NULL);
549
550 if (argc > 1)
551 path = argv[1];
552 if (argc > 2)
553 pattern = argv[2];
554
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);
559 if (res == FR_OK) {
560 print_dirent(&finfo);
561 }
562
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);
566 if (res == FR_OK) {
567 print_dirent(&finfo);
568 }
569 f_closedir(&Dir);
570
571 printf_P(PSTR("==== f_opendir: "));
572 res = f_opendir(&Dir, path);
573 cmd_error(CMD_RET_FAILURE, res, NULL);
574 f_closedir(&Dir);
575
576 return CMD_RET_SUCCESS;
35edb766 577}
016251b6
L
578
579/* Work register for fs command */
580struct stat_dat_s {
581 DWORD AccSize;
582 WORD AccFiles, AccDirs;
416bc215 583 FILINFO finfo;
016251b6
L
584};
585
586static
587FRESULT scan_files (
588 char *path, /* Pointer to the working buffer with start path */
589 struct stat_dat_s *statp
590)
591{
592 DIR dirs;
593 FRESULT res;
594 int i;
595 char *fn;
596
597 res = f_opendir(&dirs, path);
598 swirl();
599 if (res == FR_OK) {
600 i = strlen(path);
416bc215
L
601 while (((res = f_readdir(&dirs, &statp->finfo)) == FR_OK) &&
602 statp->finfo.fname[0]) {
603 if (FF_FS_RPATH && statp->finfo.fname[0] == '.')
016251b6 604 continue;
416bc215
L
605 fn = statp->finfo.fname;
606 if (statp->finfo.fattrib & AM_DIR) {
016251b6
L
607 statp->AccDirs++;
608 path[i] = '/';
609 strcpy(path+i+1, fn);
610 res = scan_files(path, statp);
611 path[i] = '\0';
612 if (res != FR_OK)
613 break;
614 } else {
615 //printf_P(PSTR("%s/%s\n"), path, fn);
616 statp->AccFiles++;
416bc215 617 statp->AccSize += statp->finfo.fsize;
016251b6
L
618 }
619 if (check_abort()) {
620 res = 255;
621 break;
622 }
623 }
624 }
625
626 return res;
627}
628
629
630/*
631 * fatstat path - Show logical drive status
632 *
633 */
634command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
635{
636 FATFS *fs;
637 DWORD nfreeclst;
638 FRESULT res;
639 char *buf;
640 char *path = "";
641 struct stat_dat_s statp;
642
7439a2b5 643 buf = (char *) malloc(PATH_MAX);
86ee5f8b
L
644 if (buf == NULL)
645 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
016251b6
L
646
647 if (argc > 1)
648 path = argv[1];
649 res = f_getfree(path, &nfreeclst, &fs);
650 if (!res) {
651 printf_P(PSTR(
652 "FAT type: %u\n"
653 "Bytes/Cluster: %lu\n"
654 "Number of FATs: %u\n"
655 "Root DIR entries: %u\n"
656 "Sectors/FAT: %lu\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);
664
177aa6a6 665#if FF_USE_LABEL
016251b6
L
666 DWORD serial;
667 res = f_getlabel(path, buf, &serial);
668 if (!res) {
669 printf_P(PSTR(
670 "Volume name: %s\n"
671 "Volume S/N: %04X-%04X\n"),
672 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
673 }
674#endif
675 if (!res) {
016251b6
L
676 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
677 strcpy(buf, path);
678
81a43faf 679 my_puts_P(PSTR("\nCounting... "));
016251b6 680 res = scan_files(buf, &statp);
81a43faf 681 putchar('\r');
016251b6
L
682 }
683 if (!res) {
81a43faf 684 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
016251b6
L
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)
688 );
689 }
690 }
691
692 free(buf);
86ee5f8b
L
693 if (res)
694 cmd_error(CMD_RET_FAILURE, res, NULL);
695
016251b6
L
696 return CMD_RET_SUCCESS;
697}
2f53dd65 698
4565be9a
L
699/*
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>
703 */
016251b6 704command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 705{
4565be9a
L
706 FIL File;
707 uint32_t addr;
708 unsigned long bytes;
709 unsigned long pos;
710 unsigned long bytes_rw;
711
fcf1d5b3 712 bool dowrite = (argv[0][0] == 'w');
3b841cea 713 FRESULT res = FR_OK;
4565be9a
L
714 bool buserr = 0;
715 uint32_t timer;
19b9a7d8 716 uint8_t *buffer;
4565be9a 717
4565be9a
L
718 if (argc < (dowrite ? 4 : 3))
719 return CMD_RET_USAGE;
720
2d914b45 721 addr = eval_arg(argv[2], NULL);
86ee5f8b
L
722 if (addr >= MAX_MEMORY)
723 cmd_error(CMD_RET_FAILURE, 0, PSTR("Address too high: %#lx"), addr);
724
4565be9a 725 if (argc > 3)
2d914b45 726 bytes = eval_arg(argv[3], NULL);
4565be9a
L
727 else
728 bytes = MAX_MEMORY;
729 if (argc > 4)
2d914b45 730 pos = eval_arg(argv[4], NULL);
4565be9a
L
731 else
732 pos = 0;
733
734 if (addr + bytes > MAX_MEMORY)
735 bytes = MAX_MEMORY - addr;
736
19b9a7d8 737 buffer = (uint8_t *) malloc(BUFFER_SIZE);
86ee5f8b
L
738 if (buffer == NULL)
739 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
19b9a7d8 740
86ee5f8b
L
741 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
742 : FA_READ );
4565be9a 743
86ee5f8b
L
744 if (!res) {
745 res = f_lseek(&File, pos);
4565be9a 746 if (!res) {
86ee5f8b
L
747 bytes_rw = 0;
748 timer = get_timer(0);
749 while (bytes) {
750 unsigned int cnt, br;
751
752 if (bytes >= BUFFER_SIZE) {
753 cnt = BUFFER_SIZE;
754 bytes -= BUFFER_SIZE;
755 } else {
756 cnt = bytes; bytes = 0;
757 }
758 if (dowrite) {
759 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
760 buserr = 1;
4565be9a
L
761 break;
762 }
86ee5f8b
L
763 z80_read_block(buffer, addr, cnt);
764 z80_bus_cmd(Release);
765 res = f_write(&File, buffer, cnt, &br);
766 if (res != FR_OK)
767 break;
768 } else {
769 res = f_read(&File, buffer, cnt, &br);
770 if (res != FR_OK)
771 break;
772 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
773 buserr = 1;
4565be9a 774 break;
86ee5f8b
L
775 }
776 z80_write_block(buffer, addr, br);
777 z80_bus_cmd(Release);
4565be9a 778 }
86ee5f8b 779 addr += br;
4565be9a 780
86ee5f8b
L
781 bytes_rw += br;
782 if (cnt != br) {
783 if (dowrite)
784 printf_P(PSTR("Disk full?\n"));
785 break;
786 }
787 if (check_abort())
788 break;
4565be9a 789 }
86ee5f8b
L
790
791 FRESULT fr = f_close(&File);
792 if (!res)
793 res = fr;
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);
4565be9a 797 }
4565be9a
L
798 }
799
19b9a7d8 800 free(buffer);
19b9a7d8 801
4565be9a 802 if (res)
86ee5f8b 803 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
5e8ac5e0 804 if (buserr)
86ee5f8b 805 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
4565be9a
L
806
807 return CMD_RET_SUCCESS;
808}
8a2b0da0 809
016251b6
L
810/*
811 * command table for fat subcommands
812 */
8a2b0da0 813
8da60ec5 814cmd_tbl_t cmd_tbl_fat[] = {
8a2b0da0 815CMD_TBL_ITEM(
d530fed0 816 status, 2, CTBL_RPT, do_stat,
8a2b0da0
L
817 "Show logical drive status",
818 "dev"
819),
820CMD_TBL_ITEM(
04f84937 821 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
8a2b0da0
L
822 "Print name of current/working directory",
823 ""
824),
825CMD_TBL_ITEM(
04f84937 826 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
8a2b0da0
L
827 "Change the current/working directory.",
828 "path"
829),
465090d4
L
830CMD_TBL_ITEM(
831 mkdir, CONFIG_SYS_MAXARGS, 0, do_mkdir,
832 "Create the DIRECTORY(ies), if they do not already exist.",
833 "DIRECTORY..."
834),
8a2b0da0 835CMD_TBL_ITEM(
04f84937 836 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
8a2b0da0
L
837 "Directory listing",
838 "path"
839),
416bc215
L
840CMD_TBL_ITEM(
841 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
842 "FatFS test function",
843 "[path [pattern]]"
844),
465090d4
L
845CMD_TBL_ITEM(
846 rm, CONFIG_SYS_MAXARGS, 0, do_rm,
b6cf9ca4 847 "Remove FILE(s) or empty DIRECTORY(ies)",
465090d4
L
848 "[OPTION]... [FILE]...\n"
849 //" -i prompt before removal\n"
b6cf9ca4
L
850 " -n do not remove\n"
851 " -v explain what is being done"
852),
853CMD_TBL_ITEM(
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"
465090d4 863),
b6cf9ca4 864
8a2b0da0
L
865CMD_TBL_ITEM(
866 load, 5, 0, do_rw,
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."
875),
876CMD_TBL_ITEM(
7a1ed620 877 write, 4, 0, do_rw,
8a2b0da0
L
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"
882),
883
b5251896 884CMD_TBL_ITEM(
7a1ed620 885 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
b5251896
L
886 "Print sub command description/usage",
887 "\n"
888 " - print brief description of all sub commands\n"
889 "fat help command ...\n"
890 " - print detailed usage of sub cmd 'command'"
891),
892
8a2b0da0
L
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,
04f84937 895 NULL,
8a2b0da0
L
896#ifdef CONFIG_SYS_LONGHELP
897 FSTR(""),
898#endif /* CONFIG_SYS_LONGHELP */
8da60ec5 899 NULL,
8a2b0da0 900#ifdef CONFIG_AUTO_COMPLETE
8da60ec5 901 NULL,
8a2b0da0
L
902#endif
903},
8da60ec5 904/* Mark end of table */
5caa8c2b 905CMD_TBL_END(cmd_tbl_fat)
8a2b0da0
L
906};
907
8a2b0da0 908
016251b6 909command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0 910{
5caa8c2b 911 puts_P(PSTR("Huch?"));
8a2b0da0
L
912
913 return CMD_RET_USAGE;
914}