]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
add fat command mv
[z180-stamp.git] / avr / cmd_fat.c
1 /*
2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 /*
8 * FAT filesystem commands
9 */
10
11 #include "common.h"
12 #include "cmd_fat.h"
13 #include <time.h>
14 uint32_t fat_time(const struct tm * timeptr);
15
16 #include "ff.h"
17 #include "z80-if.h"
18 #include "eval_arg.h"
19 #include "con-utils.h"
20 #include "print-utils.h"
21 #include "timer.h"
22 #include "debug.h"
23 #include "env.h"
24 #include "getopt-min.h"
25
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 */
30
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)
39
40
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
46
47
48 /*
49 * Multible (fat) partitions per physical drive are not supported,
50 * but we have up to 2 sdcard slots.
51 */
52 static FATFS FatFs0;
53 static FATFS FatFs1;
54
55 void setup_fatfs(void)
56 {
57 f_mount(&FatFs0, "0:", 0);
58 f_mount(&FatFs1, "1:", 0);
59 }
60
61 DWORD get_fattime (void)
62 {
63 time_t timer;
64 struct tm tm_timer;
65
66 time(&timer);
67 gmtime_r(&timer, &tm_timer);
68
69 return fat_time(&tm_timer);
70 }
71
72
73 static
74 bool check_abort(void)
75 {
76 bool ret = ctrlc();
77
78 if (ret)
79 printf_P(PSTR("Abort\n"));
80
81 return ret;
82 }
83
84
85 static const FLASH char * const FLASH rc_strings[] = {
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")
106 };
107
108
109 const FLASH char * fat_rctostr(FRESULT rc)
110 {
111 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
112 }
113
114 static
115 void swirl(void)
116 {
117 static const FLASH char swirlchar[] = { '-','\\','|','/' };
118 static uint_fast8_t cnt;
119 static uint32_t tstamp;
120
121 if (get_timer(0) > tstamp) {
122 tstamp = get_timer(0) + 250;
123 putchar('\b');
124 cnt = (cnt+1) & 3;
125 putchar(swirlchar[cnt]);
126 }
127 }
128
129 /*
130 * pwd - Print current directory of the current drive.
131 *
132 */
133 command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
134 {
135 FRESULT res;
136 TCHAR buf[PATH_MAX];
137
138 res = f_getcwd(buf, PATH_MAX); /* Get current directory path */
139
140 if (res != FR_OK)
141 cmd_error(CMD_RET_FAILURE, res, NULL);
142
143 puts(buf);
144
145 return CMD_RET_SUCCESS;
146 }
147
148
149 /*
150 * cd - Change the current/working directory.
151 *
152 */
153 command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
154 {
155 TCHAR *path;
156 FRESULT res = FR_OK;
157
158 if (argc < 2) {
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));
162 }
163 } else
164 path = argv[1];
165
166 if (strlen(path) > 1 && path[1] == ':')
167 res = f_chdrive(path);
168
169 if (res == FR_OK)
170 res = f_chdir(path);
171
172 if (res != FR_OK)
173 cmd_error(CMD_RET_FAILURE, res, NULL);
174
175 return CMD_RET_SUCCESS;
176 }
177
178
179 static
180 void 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
193 char *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
210 command_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
227 /*
228 * ls path - Directory listing
229 *
230 */
231 command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
232 {
233 FATFS *fs;
234 DIR dir; /* Directory object */
235 FILINFO finfo;
236 unsigned long p1;
237 unsigned int s1, s2;
238 FRESULT res = FR_OK;
239 char buf[PATH_MAX];
240 char *path = buf;
241 char *pattern;
242
243
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>");
252
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>");
263
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);
270
271 p1 = s1 = s2 = 0;
272 for(;;) {
273 if (res != FR_OK)
274 cmd_error(CMD_RET_FAILURE, res, NULL);
275 if (!finfo.fname[0])
276 break;
277 if (finfo.fattrib & AM_DIR) {
278 s2++;
279 } else {
280 s1++; p1 += finfo.fsize;
281 }
282 print_dirent(&finfo);
283 if (check_abort())
284 break;
285 res = f_findnext(&dir, &finfo);
286 }
287
288 if (res == FR_OK) {
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);
292 }
293
294 if (res)
295 cmd_error(CMD_RET_FAILURE, res, NULL);
296
297 return CMD_RET_SUCCESS;
298 }
299
300
301 typedef void (*fatfunc_t)(const TCHAR* path_old, const TCHAR* path_new);
302
303 command_ret_t exit_val;
304 uint8_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
311 char* 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
325 /*
326 * Copy File
327 */
328 FRESULT 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
362 debug_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
375 void ff_remove(const TCHAR *file, const TCHAR *dest UNUSED)
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)
384 printf_P(PSTR("removed: '%s'\n"), file);
385 } else {
386 cmd_error(0, res, PSTR("cannot remove '%s'"), file);
387 }
388 } else {
389 printf_P(PSTR("not removed: '%s'\n"), file);
390 }
391
392 exit_val = (res != FR_OK);
393 return;
394 }
395
396 void 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;
409 }
410
411 void ff_move(const TCHAR* path_old, const TCHAR* path_new)
412 {
413 FRESULT res;
414
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);
419
420 exit_val = (res != FR_OK);
421 return;
422 }
423
424 void ff_iterate(fatfunc_t fatfunc, int count, char* const file[], char* dest)
425 {
426 FRESULT res = 0;
427 DIR dir;
428 FILINFO finfo;
429 char srcpath[PATH_MAX], destpath[PATH_MAX];
430
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);
436
437 if (p1 != NULL) {
438 pattern = (char *) malloc(strlen(p1)+1);
439 strcpy(pattern, p1);
440 }
441 if (pattern != NULL) {
442 res = f_findfirst(&dir, &finfo, srcpath, pattern);
443 p1 = srcpath+strlen(srcpath)-1;
444 if (*p1++ != '/')
445 *(p1++) = '/';
446 } else {
447 res = f_findfirst(&dir, &finfo, ".", file[i]);
448 p1 = srcpath;
449 }
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;
453 } else {
454
455 while (res == FR_OK && finfo.fname[0] != 0) {
456 strcpy(p1, finfo.fname);
457 if (dest != NULL) {
458 strcpy(destpath, dest);
459 if (dest_is_dir) {
460 strcat_P(destpath, PSTR("/"));
461 strcat(destpath, finfo.fname);
462 }
463 }
464 fatfunc(srcpath, destpath);
465 res = f_findnext(&dir, &finfo);
466 }
467 if (res != FR_OK)
468 cmd_error(CMD_RET_FAILURE, res, PSTR("error enumerating files"));
469 }
470 f_closedir(&dir);
471 free(pattern);
472 }
473 }
474
475 command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
476 {
477 exit_val = CMD_RET_SUCCESS;
478 cmd_flags = 0;
479
480 int opt;
481 while ((opt = getopt(argc, argv, PSTR("nv"))) != -1) {
482 switch (opt) {
483 case 'n':
484 cmd_flags |= N_FLAG;
485 break;
486 case 'v':
487 cmd_flags |= V_FLAG;
488 break;
489 default:
490 return CMD_RET_USAGE;
491 break;
492 }
493 }
494 argc -= optind;
495 argv += optind;
496
497 debug_rm("==== do_rm: argc, argv[0]: %d, '%s'\n", argc, argv[0]);
498
499 if (argc < 1)
500 return CMD_RET_USAGE;
501
502 ff_iterate(ff_remove, argc, argv, NULL);
503
504 return exit_val;
505 }
506
507 command_ret_t do_cp_or_mv(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
508 {
509 exit_val = CMD_RET_SUCCESS;
510 cmd_flags = 0;
511 fatfunc_t func = (argv[0][0] == 'c') ? ff_copy : ff_move;
512
513 int opt;
514 while ((opt = getopt(argc, argv, PSTR("v"))) != -1) {
515 switch (opt) {
516 case 'v':
517 cmd_flags |= V_FLAG;
518 break;
519 default:
520 return CMD_RET_USAGE;
521 break;
522 }
523 }
524 argc -= optind;
525 argv += optind;
526
527 debug_cp("==== do_rm: argc, argv[0]: %d, '%s'\n", argc, argv[0]);
528
529 if (argc < 2)
530 return CMD_RET_USAGE;
531
532 ff_iterate(func, argc - 1, argv, argv[argc - 1]);
533
534 return exit_val;
535 }
536
537 /*
538 * tst path - for debugging: test access with different functions
539 *
540 */
541 command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
542 {
543 DIR Dir; /* Directory object */
544 FILINFO finfo;
545 FRESULT res = FR_OK;
546 char *path = "";
547 char *pattern = "*";
548
549 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO));
550
551 char * buf = (char *) malloc(BUFFER_SIZE);
552 if (buf == NULL) {
553 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
554 }
555 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
556
557 if (!res) {
558 printf_P(PSTR("cwd: '%s'\n"), buf);
559 }
560 free(buf);
561 if (res)
562 cmd_error(CMD_RET_FAILURE, res, NULL);
563
564 if (argc > 1)
565 path = argv[1];
566 if (argc > 2)
567 pattern = argv[2];
568
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);
573 if (res == FR_OK) {
574 print_dirent(&finfo);
575 }
576
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);
580 if (res == FR_OK) {
581 print_dirent(&finfo);
582 }
583 f_closedir(&Dir);
584
585 printf_P(PSTR("==== f_opendir: "));
586 res = f_opendir(&Dir, path);
587 cmd_error(CMD_RET_FAILURE, res, NULL);
588 f_closedir(&Dir);
589
590 return CMD_RET_SUCCESS;
591 }
592
593 /* Work register for fs command */
594 struct stat_dat_s {
595 DWORD AccSize;
596 WORD AccFiles, AccDirs;
597 FILINFO finfo;
598 };
599
600 static
601 FRESULT scan_files (
602 char *path, /* Pointer to the working buffer with start path */
603 struct stat_dat_s *statp
604 )
605 {
606 DIR dirs;
607 FRESULT res;
608 int i;
609 char *fn;
610
611 res = f_opendir(&dirs, path);
612 swirl();
613 if (res == FR_OK) {
614 i = strlen(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] == '.')
618 continue;
619 fn = statp->finfo.fname;
620 if (statp->finfo.fattrib & AM_DIR) {
621 statp->AccDirs++;
622 path[i] = '/';
623 strcpy(path+i+1, fn);
624 res = scan_files(path, statp);
625 path[i] = '\0';
626 if (res != FR_OK)
627 break;
628 } else {
629 //printf_P(PSTR("%s/%s\n"), path, fn);
630 statp->AccFiles++;
631 statp->AccSize += statp->finfo.fsize;
632 }
633 if (check_abort()) {
634 res = 255;
635 break;
636 }
637 }
638 }
639
640 return res;
641 }
642
643
644 /*
645 * fatstat path - Show logical drive status
646 *
647 */
648 command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
649 {
650 FATFS *fs;
651 DWORD nfreeclst;
652 FRESULT res;
653 char *buf;
654 char *path = "";
655 struct stat_dat_s statp;
656
657 buf = (char *) malloc(PATH_MAX);
658 if (buf == NULL)
659 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
660
661 if (argc > 1)
662 path = argv[1];
663 res = f_getfree(path, &nfreeclst, &fs);
664 if (!res) {
665 printf_P(PSTR(
666 "FAT type: %u\n"
667 "Bytes/Cluster: %lu\n"
668 "Number of FATs: %u\n"
669 "Root DIR entries: %u\n"
670 "Sectors/FAT: %lu\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);
678
679 #if FF_USE_LABEL
680 DWORD serial;
681 res = f_getlabel(path, buf, &serial);
682 if (!res) {
683 printf_P(PSTR(
684 "Volume name: %s\n"
685 "Volume S/N: %04X-%04X\n"),
686 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
687 }
688 #endif
689 if (!res) {
690 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
691 strcpy(buf, path);
692
693 my_puts_P(PSTR("\nCounting... "));
694 res = scan_files(buf, &statp);
695 putchar('\r');
696 }
697 if (!res) {
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)
702 );
703 }
704 }
705
706 free(buf);
707 if (res)
708 cmd_error(CMD_RET_FAILURE, res, NULL);
709
710 return CMD_RET_SUCCESS;
711 }
712
713 /*
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>
717 */
718 command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
719 {
720 FIL File;
721 uint32_t addr;
722 unsigned long bytes;
723 unsigned long pos;
724 unsigned long bytes_rw;
725
726 bool dowrite = (argv[0][0] == 'w');
727 FRESULT res = FR_OK;
728 bool buserr = 0;
729 uint32_t timer;
730 uint8_t *buffer;
731
732 if (argc < (dowrite ? 4 : 3))
733 return CMD_RET_USAGE;
734
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);
738
739 if (argc > 3)
740 bytes = eval_arg(argv[3], NULL);
741 else
742 bytes = MAX_MEMORY;
743 if (argc > 4)
744 pos = eval_arg(argv[4], NULL);
745 else
746 pos = 0;
747
748 if (addr + bytes > MAX_MEMORY)
749 bytes = MAX_MEMORY - addr;
750
751 buffer = (uint8_t *) malloc(BUFFER_SIZE);
752 if (buffer == NULL)
753 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
754
755 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
756 : FA_READ );
757
758 if (!res) {
759 res = f_lseek(&File, pos);
760 if (!res) {
761 bytes_rw = 0;
762 timer = get_timer(0);
763 while (bytes) {
764 unsigned int cnt, br;
765
766 if (bytes >= BUFFER_SIZE) {
767 cnt = BUFFER_SIZE;
768 bytes -= BUFFER_SIZE;
769 } else {
770 cnt = bytes; bytes = 0;
771 }
772 if (dowrite) {
773 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
774 buserr = 1;
775 break;
776 }
777 z80_read_block(buffer, addr, cnt);
778 z80_bus_cmd(Release);
779 res = f_write(&File, buffer, cnt, &br);
780 if (res != FR_OK)
781 break;
782 } else {
783 res = f_read(&File, buffer, cnt, &br);
784 if (res != FR_OK)
785 break;
786 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
787 buserr = 1;
788 break;
789 }
790 z80_write_block(buffer, addr, br);
791 z80_bus_cmd(Release);
792 }
793 addr += br;
794
795 bytes_rw += br;
796 if (cnt != br) {
797 if (dowrite)
798 printf_P(PSTR("Disk full?\n"));
799 break;
800 }
801 if (check_abort())
802 break;
803 }
804
805 FRESULT fr = f_close(&File);
806 if (!res)
807 res = fr;
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);
811 }
812 }
813
814 free(buffer);
815
816 if (res)
817 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
818 if (buserr)
819 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
820
821 return CMD_RET_SUCCESS;
822 }
823
824 /*
825 * command table for fat subcommands
826 */
827
828 cmd_tbl_t cmd_tbl_fat[] = {
829 CMD_TBL_ITEM(
830 status, 2, CTBL_RPT, do_stat,
831 "Show logical drive status",
832 "dev"
833 ),
834 CMD_TBL_ITEM(
835 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
836 "Print name of current/working directory",
837 ""
838 ),
839 CMD_TBL_ITEM(
840 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
841 "Change the current/working directory.",
842 "path"
843 ),
844 CMD_TBL_ITEM(
845 mkdir, CONFIG_SYS_MAXARGS, 0, do_mkdir,
846 "Create the DIRECTORY(ies), if they do not already exist.",
847 "DIRECTORY..."
848 ),
849 CMD_TBL_ITEM(
850 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
851 "Directory listing",
852 "path"
853 ),
854 CMD_TBL_ITEM(
855 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
856 "FatFS test function",
857 "[path [pattern]]"
858 ),
859 CMD_TBL_ITEM(
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"
866 ),
867 CMD_TBL_ITEM(
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"
877 ),
878 CMD_TBL_ITEM(
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"
883 ),
884
885 CMD_TBL_ITEM(
886 load, 5, 0, do_rw,
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."
895 ),
896 CMD_TBL_ITEM(
897 write, 4, 0, do_rw,
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"
902 ),
903
904 CMD_TBL_ITEM(
905 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
906 "Print sub command description/usage",
907 "\n"
908 " - print brief description of all sub commands\n"
909 "fat help command ...\n"
910 " - print detailed usage of sub cmd 'command'"
911 ),
912
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,
915 NULL,
916 #ifdef CONFIG_SYS_LONGHELP
917 FSTR(""),
918 #endif /* CONFIG_SYS_LONGHELP */
919 NULL,
920 #ifdef CONFIG_AUTO_COMPLETE
921 NULL,
922 #endif
923 },
924 /* Mark end of table */
925 CMD_TBL_END(cmd_tbl_fat)
926 };
927
928
929 command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
930 {
931 puts_P(PSTR("Huch?"));
932
933 return CMD_RET_USAGE;
934 }