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