]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
c97fd79cdb51b5ba271cc5a159da06d4fab6fad3
[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_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 }
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 }
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 {
441
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);
458 free(pattern);
459 }
460 }
461
462 command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
463 {
464 exit_val = CMD_RET_SUCCESS;
465 cmd_flags = 0;
466
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
491 return exit_val;
492 }
493
494 command_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;
521 }
522
523 /*
524 * tst path - for debugging: test access with different functions
525 *
526 */
527 command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
528 {
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);
540 }
541 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
542
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;
577 }
578
579 /* Work register for fs command */
580 struct stat_dat_s {
581 DWORD AccSize;
582 WORD AccFiles, AccDirs;
583 FILINFO finfo;
584 };
585
586 static
587 FRESULT 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);
601 while (((res = f_readdir(&dirs, &statp->finfo)) == FR_OK) &&
602 statp->finfo.fname[0]) {
603 if (FF_FS_RPATH && statp->finfo.fname[0] == '.')
604 continue;
605 fn = statp->finfo.fname;
606 if (statp->finfo.fattrib & AM_DIR) {
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++;
617 statp->AccSize += statp->finfo.fsize;
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 */
634 command_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
643 buf = (char *) malloc(PATH_MAX);
644 if (buf == NULL)
645 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
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
665 #if FF_USE_LABEL
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) {
676 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
677 strcpy(buf, path);
678
679 my_puts_P(PSTR("\nCounting... "));
680 res = scan_files(buf, &statp);
681 putchar('\r');
682 }
683 if (!res) {
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)
688 );
689 }
690 }
691
692 free(buf);
693 if (res)
694 cmd_error(CMD_RET_FAILURE, res, NULL);
695
696 return CMD_RET_SUCCESS;
697 }
698
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 */
704 command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
705 {
706 FIL File;
707 uint32_t addr;
708 unsigned long bytes;
709 unsigned long pos;
710 unsigned long bytes_rw;
711
712 bool dowrite = (argv[0][0] == 'w');
713 FRESULT res = FR_OK;
714 bool buserr = 0;
715 uint32_t timer;
716 uint8_t *buffer;
717
718 if (argc < (dowrite ? 4 : 3))
719 return CMD_RET_USAGE;
720
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);
724
725 if (argc > 3)
726 bytes = eval_arg(argv[3], NULL);
727 else
728 bytes = MAX_MEMORY;
729 if (argc > 4)
730 pos = eval_arg(argv[4], NULL);
731 else
732 pos = 0;
733
734 if (addr + bytes > MAX_MEMORY)
735 bytes = MAX_MEMORY - addr;
736
737 buffer = (uint8_t *) malloc(BUFFER_SIZE);
738 if (buffer == NULL)
739 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
740
741 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
742 : FA_READ );
743
744 if (!res) {
745 res = f_lseek(&File, pos);
746 if (!res) {
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;
761 break;
762 }
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;
774 break;
775 }
776 z80_write_block(buffer, addr, br);
777 z80_bus_cmd(Release);
778 }
779 addr += br;
780
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;
789 }
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);
797 }
798 }
799
800 free(buffer);
801
802 if (res)
803 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
804 if (buserr)
805 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
806
807 return CMD_RET_SUCCESS;
808 }
809
810 /*
811 * command table for fat subcommands
812 */
813
814 cmd_tbl_t cmd_tbl_fat[] = {
815 CMD_TBL_ITEM(
816 status, 2, CTBL_RPT, do_stat,
817 "Show logical drive status",
818 "dev"
819 ),
820 CMD_TBL_ITEM(
821 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
822 "Print name of current/working directory",
823 ""
824 ),
825 CMD_TBL_ITEM(
826 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
827 "Change the current/working directory.",
828 "path"
829 ),
830 CMD_TBL_ITEM(
831 mkdir, CONFIG_SYS_MAXARGS, 0, do_mkdir,
832 "Create the DIRECTORY(ies), if they do not already exist.",
833 "DIRECTORY..."
834 ),
835 CMD_TBL_ITEM(
836 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
837 "Directory listing",
838 "path"
839 ),
840 CMD_TBL_ITEM(
841 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
842 "FatFS test function",
843 "[path [pattern]]"
844 ),
845 CMD_TBL_ITEM(
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"
852 ),
853 CMD_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"
863 ),
864
865 CMD_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 ),
876 CMD_TBL_ITEM(
877 write, 4, 0, do_rw,
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
884 CMD_TBL_ITEM(
885 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
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
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,
895 NULL,
896 #ifdef CONFIG_SYS_LONGHELP
897 FSTR(""),
898 #endif /* CONFIG_SYS_LONGHELP */
899 NULL,
900 #ifdef CONFIG_AUTO_COMPLETE
901 NULL,
902 #endif
903 },
904 /* Mark end of table */
905 CMD_TBL_END(cmd_tbl_fat)
906 };
907
908
909 command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
910 {
911 puts_P(PSTR("Huch?"));
912
913 return CMD_RET_USAGE;
914 }