]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
reorg
[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 25
b6cf9ca4 26#define DEBUG_LS 0 /* set to 1 to debug */
465090d4 27#define DEBUG_RM 0 /* set to 1 to debug */
b6cf9ca4 28#define DEBUG_CP 0 /* set to 1 to debug */
66f6a44e 29
dd83e2c8 30#define debug_ls(fmt, args...) \
416bc215 31 debug_cond(DEBUG_LS, fmt, ##args)
dd83e2c8 32#define debug_rm(fmt, args...) \
416bc215 33 debug_cond(DEBUG_RM, fmt, ##args)
dd83e2c8 34#define debug_cp(fmt, args...) \
b6cf9ca4 35 debug_cond(DEBUG_CP, fmt, ##args)
66f6a44e
L
36
37
416bc215 38/* TODO: use memory size test function (z80_memsize_detect() ) */
b6cf9ca4
L
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
7439a2b5 43
19b9a7d8 44
016251b6
L
45/*
46 * Multible (fat) partitions per physical drive are not supported,
47 * but we have up to 2 sdcard slots.
48 */
416bc215
L
49static FATFS FatFs0;
50static FATFS FatFs1;
7439a2b5 51
016251b6
L
52void setup_fatfs(void)
53{
54 f_mount(&FatFs0, "0:", 0);
55 f_mount(&FatFs1, "1:", 0);
56}
8a2b0da0 57
2f53dd65
L
58DWORD get_fattime (void)
59{
19b9a7d8
L
60 time_t timer;
61 struct tm tm_timer;
62
63 time(&timer);
64 gmtime_r(&timer, &tm_timer);
65
bb497e9d 66 return fat_time(&tm_timer);
2f53dd65
L
67}
68
69
416bc215
L
70static
71bool check_abort(void)
b15d22a4
L
72{
73 bool ret = ctrlc();
74
75 if (ret)
76 printf_P(PSTR("Abort\n"));
77
78 return ret;
79}
80
81
022330eb 82static const FLASH char * const FLASH rc_strings[] = {
414caa77
L
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")
022330eb
L
103 };
104
2f53dd65 105
414caa77 106const FLASH char * fat_rctostr(FRESULT rc)
7439a2b5 107{
022330eb 108 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
7439a2b5
L
109}
110
416bc215
L
111static
112void swirl(void)
b15d22a4 113{
6204987c 114 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
115 static uint_fast8_t cnt;
116 static uint32_t tstamp;
117
118 if (get_timer(0) > tstamp) {
6204987c 119 tstamp = get_timer(0) + 250;
81a43faf
L
120 putchar('\b');
121 cnt = (cnt+1) & 3;
122 putchar(swirlchar[cnt]);
b15d22a4
L
123 }
124}
125
416bc215
L
126static
127void 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
dd83e2c8 140static
416bc215
L
141char *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
465090d4 158
b6cf9ca4
L
159typedef void (*fatfunc_t)(const TCHAR* path_old, const TCHAR* path_new);
160
161command_ret_t exit_val;
465090d4
L
162uint8_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
465090d4 169
b6cf9ca4
L
170/*
171 * Copy File
172*/
dd83e2c8 173static
b6cf9ca4
L
174FRESULT 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
b6cf9ca4
L
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
dd83e2c8 220static
b6cf9ca4 221void ff_remove(const TCHAR *file, const TCHAR *dest UNUSED)
465090d4
L
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)
b6cf9ca4 230 printf_P(PSTR("removed: '%s'\n"), file);
465090d4
L
231 } else {
232 cmd_error(0, res, PSTR("cannot remove '%s'"), file);
233 }
234 } else {
b6cf9ca4 235 printf_P(PSTR("not removed: '%s'\n"), file);
465090d4
L
236 }
237
b6cf9ca4
L
238 exit_val = (res != FR_OK);
239 return;
240}
241
dd83e2c8 242static
b6cf9ca4
L
243void 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;
465090d4
L
256}
257
dd83e2c8 258static
64db7d7a
L
259void 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
dd83e2c8 272static
465090d4
L
273void 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];
6cf1cf17
L
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 }
465090d4 288
465090d4
L
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 }
465090d4
L
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 }
b6cf9ca4
L
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 {
465090d4 311
b6cf9ca4
L
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) {
6cf1cf17
L
317 if (destpath[strlen(destpath) - 1] != '/')
318 strcat_P(destpath, PSTR("/"));
b6cf9ca4
L
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);
465090d4
L
329 free(pattern);
330 }
465090d4
L
331}
332
dd83e2c8
L
333/*
334 * pwd - Print current directory of the current drive.
335 *
336 */
337command_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 */
357command_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
383command_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 */
404command_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
465090d4
L
473command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
474{
b6cf9ca4 475 exit_val = CMD_RET_SUCCESS;
465090d4 476 cmd_flags = 0;
b6cf9ca4 477
465090d4
L
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
b6cf9ca4
L
502 return exit_val;
503}
504
64db7d7a 505command_ret_t do_cp_or_mv(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
b6cf9ca4
L
506{
507 exit_val = CMD_RET_SUCCESS;
508 cmd_flags = 0;
64db7d7a 509 fatfunc_t func = (argv[0][0] == 'c') ? ff_copy : ff_move;
b6cf9ca4
L
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
64db7d7a 530 ff_iterate(func, argc - 1, argv, argv[argc - 1]);
b6cf9ca4
L
531
532 return exit_val;
465090d4 533}
416bc215 534
016251b6
L
535/* Work register for fs command */
536struct stat_dat_s {
537 DWORD AccSize;
538 WORD AccFiles, AccDirs;
416bc215 539 FILINFO finfo;
016251b6
L
540};
541
542static
543FRESULT 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);
416bc215
L
557 while (((res = f_readdir(&dirs, &statp->finfo)) == FR_OK) &&
558 statp->finfo.fname[0]) {
559 if (FF_FS_RPATH && statp->finfo.fname[0] == '.')
016251b6 560 continue;
416bc215
L
561 fn = statp->finfo.fname;
562 if (statp->finfo.fattrib & AM_DIR) {
016251b6
L
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++;
416bc215 573 statp->AccSize += statp->finfo.fsize;
016251b6
L
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 */
590command_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
7439a2b5 599 buf = (char *) malloc(PATH_MAX);
86ee5f8b
L
600 if (buf == NULL)
601 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
016251b6
L
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
177aa6a6 621#if FF_USE_LABEL
016251b6
L
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) {
016251b6
L
632 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
633 strcpy(buf, path);
634
81a43faf 635 my_puts_P(PSTR("\nCounting... "));
016251b6 636 res = scan_files(buf, &statp);
81a43faf 637 putchar('\r');
016251b6
L
638 }
639 if (!res) {
81a43faf 640 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
016251b6
L
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);
86ee5f8b
L
649 if (res)
650 cmd_error(CMD_RET_FAILURE, res, NULL);
651
016251b6
L
652 return CMD_RET_SUCCESS;
653}
2f53dd65 654
dd83e2c8
L
655/*
656 * tst path - for debugging: test access with different functions
657 *
658 */
659command_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
4565be9a
L
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 */
016251b6 701command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 702{
4565be9a
L
703 FIL File;
704 uint32_t addr;
705 unsigned long bytes;
706 unsigned long pos;
707 unsigned long bytes_rw;
708
fcf1d5b3 709 bool dowrite = (argv[0][0] == 'w');
3b841cea 710 FRESULT res = FR_OK;
4565be9a
L
711 bool buserr = 0;
712 uint32_t timer;
19b9a7d8 713 uint8_t *buffer;
4565be9a 714
4565be9a
L
715 if (argc < (dowrite ? 4 : 3))
716 return CMD_RET_USAGE;
717
2d914b45 718 addr = eval_arg(argv[2], NULL);
86ee5f8b
L
719 if (addr >= MAX_MEMORY)
720 cmd_error(CMD_RET_FAILURE, 0, PSTR("Address too high: %#lx"), addr);
721
4565be9a 722 if (argc > 3)
2d914b45 723 bytes = eval_arg(argv[3], NULL);
4565be9a
L
724 else
725 bytes = MAX_MEMORY;
726 if (argc > 4)
2d914b45 727 pos = eval_arg(argv[4], NULL);
4565be9a
L
728 else
729 pos = 0;
730
731 if (addr + bytes > MAX_MEMORY)
732 bytes = MAX_MEMORY - addr;
733
19b9a7d8 734 buffer = (uint8_t *) malloc(BUFFER_SIZE);
86ee5f8b
L
735 if (buffer == NULL)
736 cmd_error(CMD_RET_FAILURE, ENOMEM, NULL);
19b9a7d8 737
86ee5f8b
L
738 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
739 : FA_READ );
4565be9a 740
86ee5f8b
L
741 if (!res) {
742 res = f_lseek(&File, pos);
4565be9a 743 if (!res) {
86ee5f8b
L
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;
4565be9a
L
758 break;
759 }
86ee5f8b
L
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;
4565be9a 771 break;
86ee5f8b
L
772 }
773 z80_write_block(buffer, addr, br);
774 z80_bus_cmd(Release);
4565be9a 775 }
86ee5f8b 776 addr += br;
4565be9a 777
86ee5f8b
L
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;
4565be9a 786 }
86ee5f8b
L
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);
4565be9a 794 }
4565be9a
L
795 }
796
19b9a7d8 797 free(buffer);
19b9a7d8 798
4565be9a 799 if (res)
86ee5f8b 800 cmd_error(CMD_RET_FAILURE, res, PSTR("'%s'"), argv[1]);
5e8ac5e0 801 if (buserr)
86ee5f8b 802 cmd_error(CMD_RET_FAILURE, EBUSTO, NULL);
4565be9a
L
803
804 return CMD_RET_SUCCESS;
805}
8a2b0da0 806
016251b6
L
807/*
808 * command table for fat subcommands
809 */
8a2b0da0 810
8da60ec5 811cmd_tbl_t cmd_tbl_fat[] = {
8a2b0da0 812CMD_TBL_ITEM(
d530fed0 813 status, 2, CTBL_RPT, do_stat,
8a2b0da0
L
814 "Show logical drive status",
815 "dev"
816),
817CMD_TBL_ITEM(
04f84937 818 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
8a2b0da0
L
819 "Print name of current/working directory",
820 ""
821),
822CMD_TBL_ITEM(
04f84937 823 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
8a2b0da0
L
824 "Change the current/working directory.",
825 "path"
826),
465090d4
L
827CMD_TBL_ITEM(
828 mkdir, CONFIG_SYS_MAXARGS, 0, do_mkdir,
829 "Create the DIRECTORY(ies), if they do not already exist.",
830 "DIRECTORY..."
831),
dd83e2c8
L
832CMD_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),
8a2b0da0 837CMD_TBL_ITEM(
04f84937 838 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
8a2b0da0
L
839 "Directory listing",
840 "path"
841),
416bc215 842CMD_TBL_ITEM(
dd83e2c8
L
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"
416bc215 849),
465090d4 850CMD_TBL_ITEM(
dd83e2c8 851 rm, CONFIG_SYS_MAXARGS, 0|CTBL_SUBCMDAUTO, do_rm,
b6cf9ca4 852 "Remove FILE(s) or empty DIRECTORY(ies)",
465090d4
L
853 "[OPTION]... [FILE]...\n"
854 //" -i prompt before removal\n"
b6cf9ca4
L
855 " -n do not remove\n"
856 " -v explain what is being done"
857),
858CMD_TBL_ITEM(
64db7d7a 859 cp, CONFIG_SYS_MAXARGS, 0, do_cp_or_mv,
b6cf9ca4
L
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"
465090d4 868),
dd83e2c8
L
869CMD_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),
64db7d7a
L
880CMD_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),
dd83e2c8
L
886CMD_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),
892CMD_TBL_ITEM(
893 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
894 "FatFS test function",
895 "[path [pattern]]"
896),
8a2b0da0
L
897CMD_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),
908CMD_TBL_ITEM(
7a1ed620 909 write, 4, 0, do_rw,
8a2b0da0
L
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
b5251896 916CMD_TBL_ITEM(
7a1ed620 917 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
b5251896
L
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
8a2b0da0
L
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,
04f84937 927 NULL,
8a2b0da0
L
928#ifdef CONFIG_SYS_LONGHELP
929 FSTR(""),
930#endif /* CONFIG_SYS_LONGHELP */
8da60ec5 931 NULL,
8a2b0da0 932#ifdef CONFIG_AUTO_COMPLETE
8da60ec5 933 NULL,
8a2b0da0
L
934#endif
935},
8da60ec5 936/* Mark end of table */
5caa8c2b 937CMD_TBL_END(cmd_tbl_fat)
8a2b0da0
L
938};
939
8a2b0da0 940
016251b6 941command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0 942{
5caa8c2b 943 puts_P(PSTR("Huch?"));
8a2b0da0
L
944
945 return CMD_RET_USAGE;
946}