]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
Recursive cmd_find(), new command table flag: CTBL_RPT
[z180-stamp.git] / avr / cmd_fat.c
CommitLineData
35edb766 1/*
2d914b45 2 * (C) Copyright 2014,2016 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
8a2b0da0 11#include "cmd_fat.h"
2f53dd65 12
2f53dd65
L
13#include "ff.h"
14#include "z80-if.h"
2d914b45 15#include "eval_arg.h"
4565be9a 16#include "con-utils.h"
2f53dd65 17#include "print-utils.h"
19b9a7d8 18#include "time.h"
2f53dd65 19#include "timer.h"
4f881b02 20#include "debug.h"
8a2b0da0 21#include "env.h"
2f53dd65 22
5f7f3586
L
23/* TODO: use memory size test function (cmd_mem.c) */
24#define MAX_MEMORY (1ul << 19)
19b9a7d8
L
25#define BUFFER_SIZE 512
26
4565be9a 27
8a2b0da0
L
28FATFS FatFs0;
29FATFS FatFs1;
30
31char chur_drv[3];
32
2f53dd65
L
33DWORD get_fattime (void)
34{
19b9a7d8
L
35 time_t timer;
36 struct tm tm_timer;
37
38 time(&timer);
39 gmtime_r(&timer, &tm_timer);
40
41 return fatfs_time(&tm_timer);
2f53dd65
L
42}
43
44
b15d22a4
L
45static bool check_abort(void)
46{
47 bool ret = ctrlc();
48
49 if (ret)
50 printf_P(PSTR("Abort\n"));
51
52 return ret;
53}
54
55
2f53dd65
L
56static const FLASH char * const FLASH rc_names[] = {
57 FSTR("OK"),
58 FSTR("DISK_ERR"),
59 FSTR("INT_ERR"),
60 FSTR("NOT_READY"),
61 FSTR("NO_FILE"),
62 FSTR("NO_PATH"),
63 FSTR("INVALID_NAME"),
64 FSTR("DENIED"),
65 FSTR("EXIST"),
66 FSTR("INVALID_OBJECT"),
67 FSTR("WRITE_PROTECTED"),
68 FSTR("INVALID_DRIVE"),
69 FSTR("NOT_ENABLED"),
70 FSTR("NO_FILE_SYSTEM"),
71 FSTR("MKFS_ABORTED"),
72 FSTR("TIMEOUT"),
73 FSTR("LOCKED"),
74 FSTR("NOT_ENOUGH_CORE"),
7af9364e
L
75 FSTR("TOO_MANY_OPEN_FILES"),
76 FSTR("INVALID_PARAMETER")
2f53dd65
L
77 };
78
79static
80void put_rc (FRESULT rc)
81{
82#if GCC_BUG_61443
7af9364e
L
83 printf_P(PSTR("rc=%u FR_"), rc);
84 my_puts_P(rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
85 my_puts_P(PSTR("\n"));
2f53dd65 86#else
7af9364e
L
87 printf_P(PSTR("rc=%u FR_%S\n"), rc,
88 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
2f53dd65
L
89#endif
90}
91
b15d22a4 92
8a2b0da0
L
93void setup_fatfs(void)
94{
95 f_mount(&FatFs0, "0:", 0);
96 f_mount(&FatFs1, "1:", 0);
97}
98
99
b15d22a4
L
100static void swirl(void)
101{
6204987c 102 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
103 static uint_fast8_t cnt;
104 static uint32_t tstamp;
105
106 if (get_timer(0) > tstamp) {
107 printf_P(PSTR("\b%c"), swirlchar[cnt]);
108 cnt = (cnt+1) % ARRAY_SIZE(swirlchar);
6204987c 109 tstamp = get_timer(0) + 250;
b15d22a4
L
110 }
111}
112
6204987c
L
113/* Work register for fs command */
114struct stat_dat_s {
115 DWORD AccSize;
116 WORD AccFiles, AccDirs;
117 FILINFO Finfo;
118};
119
2f53dd65
L
120static
121FRESULT scan_files (
6204987c
L
122 char *path, /* Pointer to the working buffer with start path */
123 struct stat_dat_s *statp
2f53dd65
L
124)
125{
126 DIR dirs;
127 FRESULT res;
128 int i;
129 char *fn;
130
131 res = f_opendir(&dirs, path);
b15d22a4 132 swirl();
2f53dd65
L
133 if (res == FR_OK) {
134 i = strlen(path);
6204987c
L
135 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
136 statp->Finfo.fname[0]) {
d20e9438 137 if (FF_FS_RPATH && statp->Finfo.fname[0] == '.')
6204987c
L
138 continue;
139 fn = statp->Finfo.fname;
140 if (statp->Finfo.fattrib & AM_DIR) {
141 statp->AccDirs++;
142 path[i] = '/';
143 strcpy(path+i+1, fn);
144 res = scan_files(path, statp);
2f53dd65 145 path[i] = '\0';
6204987c
L
146 if (res != FR_OK)
147 break;
2f53dd65 148 } else {
6204987c
L
149 //printf_P(PSTR("%s/%s\n"), path, fn);
150 statp->AccFiles++;
151 statp->AccSize += statp->Finfo.fsize;
2f53dd65 152 }
b15d22a4
L
153 if (check_abort()) {
154 res = 255;
155 break;
156 }
2f53dd65
L
157 }
158 }
159
160 return res;
161}
162
163
164/*
165 * fatstat path - Show logical drive status
166 *
167 */
7a1ed620 168command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 169{
bbd45c46 170 FATFS *fs;
4565be9a 171 DWORD nfreeclst;
2f53dd65 172 FRESULT res;
6204987c
L
173 char *path;
174 struct stat_dat_s statp;
2f53dd65
L
175
176 (void) cmdtp; (void) flag; (void) argc;
177
6204987c 178 path = (char *) malloc(BUFFER_SIZE);
bbd45c46 179 if (path == NULL) {
7a1ed620 180 printf_P(PSTR("fat stat: Out of Memory!\n"));
6204987c 181 free(path);
2f53dd65
L
182 return CMD_RET_FAILURE;
183 }
184
bbd45c46 185 res = f_getfree(argv[1], &nfreeclst, &fs);
4565be9a 186 if (!res) {
bbd45c46
L
187 printf_P(PSTR(
188 "FAT type: %u\n"
189 "Bytes/Cluster: %lu\n"
190 "Number of FATs: %u\n"
191 "Root DIR entries: %u\n"
192 "Sectors/FAT: %lu\n"
193 "Number of clusters: %lu\n"
194 "FAT start (lba): %lu\n"
195 "DIR start (lba,cluster): %lu\n"
196 "Data start (lba): %lu\n"),
197 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
198 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
199 fs->fatbase, fs->dirbase, fs->database);
2f53dd65
L
200
201#if _USE_LABEL
bbd45c46
L
202 TCHAR label[12];
203 DWORD serial;
204 res = f_getlabel(argv[1], label, &serial);
205 if (!res) {
206 printf_P(PSTR(
207 "Volume name: %s\n"
208 "Volume S/N: %04X-%04X\n"),
209 label, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
210 }
2f53dd65 211#endif
bbd45c46
L
212 if (!res) {
213 my_puts_P(PSTR("\nCounting... "));
214 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
215 strcpy(path, argv[1]);
2f53dd65 216
bbd45c46
L
217 res = scan_files(path, &statp);
218 }
219 if (!res) {
220 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
221 "%lu KB total disk space.\n%lu KB available.\n"),
222 statp.AccFiles, statp.AccSize, statp.AccDirs,
223 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
224 );
4565be9a 225 }
2f53dd65 226 }
2f53dd65 227
6204987c 228 free(path);
2f53dd65
L
229 if (res) {
230 put_rc(res);
231 return CMD_RET_FAILURE;
232 }
233 return CMD_RET_SUCCESS;
234}
235
8a2b0da0
L
236/*
237 * pwd - Print current directory of the current drive.
238 *
239 */
fcf1d5b3 240command_ret_t do_pwd(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
8a2b0da0
L
241{
242 FRESULT res;
243 char *buf;
244
245 (void) cmdtp; (void) flag; (void) argc; (void) argv;
246
247 buf = (char *) malloc(BUFFER_SIZE);
248 if (buf == NULL) {
249 printf_P(PSTR("pwd: Out of Memory!\n"));
250 free(buf);
251 return CMD_RET_FAILURE;
252 }
253
254 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
255
256 if (!res) {
257 puts(buf);
258 }
259 free(buf);
260 if (res) {
261 put_rc(res);
262 return CMD_RET_FAILURE;
263 }
264 return CMD_RET_SUCCESS;
265}
266
267
268/*
269 * cd - Change the current/working directory.
270 *
271 */
fcf1d5b3 272command_ret_t do_cd(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
8a2b0da0
L
273{
274 char *arg;
275 FRESULT res = 0;
276
277 (void) cmdtp; (void) flag; (void) argc;
278
279 if (argc < 2) {
b5251896 280 arg = getenv_str(PSTR(ENV_HOME));
8a2b0da0 281 if (arg == NULL) {
b5251896 282 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
8a2b0da0
L
283 return CMD_RET_FAILURE;
284 }
285 } else
286 arg = argv[1];
287
288 if (arg[1] == ':') {
289 char drv[3];
290 drv[2] = '\0';
291 drv[1] = ':';
292 drv[0] = arg[0];
293 res = f_chdrive(drv);
294 }
295 if (!res) {
296 res = f_chdir(arg);
297 }
298
299 if (res) {
300 put_rc(res);
301 return CMD_RET_FAILURE;
302 }
303 return CMD_RET_SUCCESS;
304}
305
2f53dd65
L
306
307/*
308 * fatls path - Directory listing
309 *
310 */
fcf1d5b3 311command_ret_t do_ls(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
2f53dd65 312{
bbd45c46 313 FATFS *fs;
2f53dd65
L
314 DIR Dir; /* Directory object */
315 FILINFO Finfo;
316 unsigned long p1;
317 unsigned int s1, s2;
8a2b0da0
L
318 FRESULT res = FR_OK;
319 char *buf;
2f53dd65
L
320
321 (void) cmdtp; (void) flag; (void) argc;
322
8a2b0da0
L
323 buf = (char *) malloc(BUFFER_SIZE);
324 if (buf == NULL) {
325 printf_P(PSTR("pwd: Out of Memory!\n"));
326 free(buf);
327 return CMD_RET_FAILURE;
328 }
329
330 if (argc < 2)
331 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
332 else
333 strncpy(buf, argv[1], BUFFER_SIZE);
334
335 if (res == FR_OK)
336 res = f_opendir(&Dir, buf);
337 if (res != FR_OK) {
338 free(buf);
2f53dd65
L
339 put_rc(res);
340 return CMD_RET_FAILURE;
341 }
342
343 p1 = s1 = s2 = 0;
344 for(;;) {
345 res = f_readdir(&Dir, &Finfo);
346 if ((res != FR_OK) || !Finfo.fname[0])
347 break;
348 if (Finfo.fattrib & AM_DIR) {
349 s2++;
350 } else {
351 s1++; p1 += Finfo.fsize;
352 }
7af9364e 353 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
2f53dd65
L
354 (Finfo.fattrib & AM_DIR) ? 'D' : '-',
355 (Finfo.fattrib & AM_RDO) ? 'R' : '-',
356 (Finfo.fattrib & AM_HID) ? 'H' : '-',
357 (Finfo.fattrib & AM_SYS) ? 'S' : '-',
358 (Finfo.fattrib & AM_ARC) ? 'A' : '-',
359 (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
7af9364e
L
360 (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
361 Finfo.fsize, Finfo.fname);
b15d22a4
L
362 if (check_abort())
363 break;
2f53dd65
L
364 }
365
366 if (res == FR_OK) {
367 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
8a2b0da0 368 if (f_getfree(buf, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
369 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
370 }
371
8a2b0da0 372 free(buf);
2f53dd65
L
373 if (res) {
374 put_rc(res);
375 return CMD_RET_FAILURE;
376 }
377
378 return CMD_RET_SUCCESS;
379}
380
35edb766
L
381static
382FRESULT mkpath(TCHAR *path)
383{
384 /* TODO: */
385 (void) path;
386#if 0
387 FILINFO fd
388 TCHAR *p, *q;
389 FRESULT ret;
390
35edb766
L
391 res = f_stat (path, &fd)
392
393 p = strchr(path, ':');
394 if (p == NULL || *++p == '\0' || *p++ != '/')
395 return FR_OK;
396
397 while ((q = strchr(p, '/')) != NULL) {
398 *q = '\0';
399 ret = f_mkdir(path);
400 *q = '/';
401 if (ret != FR_OK && ret != FR_EXIST)
402 return ret;
403 p = q + 1;
404 }
405#endif
406
407 return FR_OK;
408}
2f53dd65 409
4565be9a
L
410/*
411 * fatread/write - load binary file to/from a dos filesystem
412 * read <d:/path/filename> <addr> [bytes [pos]]
413 * write <d:/path/filename> <addr> <bytes>
414 */
fcf1d5b3 415command_ret_t do_rw(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
4565be9a 416{
4565be9a
L
417 FIL File;
418 uint32_t addr;
419 unsigned long bytes;
420 unsigned long pos;
421 unsigned long bytes_rw;
422
fcf1d5b3 423 bool dowrite = (argv[0][0] == 'w');
3b841cea 424 FRESULT res = FR_OK;
4565be9a
L
425 bool buserr = 0;
426 uint32_t timer;
19b9a7d8 427 uint8_t *buffer;
4565be9a
L
428
429 (void) cmdtp; (void) flag;
430
431 if (argc < (dowrite ? 4 : 3))
432 return CMD_RET_USAGE;
433
2d914b45 434 addr = eval_arg(argv[2], NULL);
4565be9a
L
435 if (addr >= MAX_MEMORY) {
436 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
437 return CMD_RET_FAILURE;
438 }
439 if (argc > 3)
2d914b45 440 bytes = eval_arg(argv[3], NULL);
4565be9a
L
441 else
442 bytes = MAX_MEMORY;
443 if (argc > 4)
2d914b45 444 pos = eval_arg(argv[4], NULL);
4565be9a
L
445 else
446 pos = 0;
447
448 if (addr + bytes > MAX_MEMORY)
449 bytes = MAX_MEMORY - addr;
450
19b9a7d8 451 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 452 if (buffer == NULL) {
19b9a7d8 453 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
454 free(buffer);
455 return CMD_RET_FAILURE;
456 }
457
bbd45c46
L
458 if (dowrite) {
459 res = mkpath(argv[1]);
35edb766 460 }
4565be9a
L
461 if (!res) {
462 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
463 : FA_READ );
464
465 if (!res) {
466 res = f_lseek(&File, pos);
467 if (!res) {
468 bytes_rw = 0;
469 timer = get_timer(0);
470 while (bytes) {
471 unsigned int cnt, br;
472
19b9a7d8
L
473 if (bytes >= BUFFER_SIZE) {
474 cnt = BUFFER_SIZE;
475 bytes -= BUFFER_SIZE;
4565be9a
L
476 } else {
477 cnt = bytes; bytes = 0;
478 }
479 if (dowrite) {
480 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
481 buserr = 1;
482 break;
483 }
484 z80_read_block(buffer, addr, cnt);
485 z80_bus_cmd(Release);
486 res = f_write(&File, buffer, cnt, &br);
487 if (res != FR_OK)
488 break;
489 } else {
490 res = f_read(&File, buffer, cnt, &br);
491 if (res != FR_OK)
492 break;
493 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
494 buserr = 1;
495 break;
496 }
497 z80_write_block(buffer, addr, br);
498 z80_bus_cmd(Release);
499 }
500 addr += br;
501
502 bytes_rw += br;
503 if (cnt != br) {
504 if (dowrite)
505 printf_P(PSTR("Disk full?\n"));
506 break;
507 }
b15d22a4 508 if (check_abort())
4565be9a 509 break;
4565be9a
L
510 }
511
512 FRESULT fr = f_close(&File);
513 if (!res)
514 res = fr;
515 timer = get_timer(timer);
516 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
517 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
518 }
519 }
4565be9a
L
520 }
521
19b9a7d8 522 free(buffer);
19b9a7d8 523
4565be9a
L
524 if (buserr)
525 my_puts_P(PSTR("Bus timeout\n"));
526 if (res)
527 put_rc(res);
528 if (buserr || res)
529 return CMD_RET_FAILURE;
530
531 return CMD_RET_SUCCESS;
532}
8a2b0da0
L
533
534
8da60ec5 535cmd_tbl_t cmd_tbl_fat[] = {
8a2b0da0 536CMD_TBL_ITEM(
7a1ed620 537 stat, 2, CTBL_RPT, do_stat,
8a2b0da0
L
538 "Show logical drive status",
539 "dev"
540),
541CMD_TBL_ITEM(
7a1ed620 542 pwd, 2, CTBL_RPT, do_pwd,
8a2b0da0
L
543 "Print name of current/working directory",
544 ""
545),
546CMD_TBL_ITEM(
7a1ed620 547 cd, 2, 0, do_cd,
8a2b0da0
L
548 "Change the current/working directory.",
549 "path"
550),
551CMD_TBL_ITEM(
7a1ed620 552 ls, 2, CTBL_RPT, do_ls,
8a2b0da0
L
553 "Directory listing",
554 "path"
555),
556CMD_TBL_ITEM(
557 load, 5, 0, do_rw,
558 "load binary file from a dos filesystem",
559 "<d:/path/filename> <addr> [bytes [pos]]\n"
560 " - Load binary file 'path/filename' on logical drive 'd'\n"
561 " to address 'addr' from dos filesystem.\n"
562 " 'pos' gives the file position to start loading from.\n"
563 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
564 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
565 " the load stops on end of file."
566),
567CMD_TBL_ITEM(
7a1ed620 568 write, 4, 0, do_rw,
8a2b0da0
L
569 "write file into a dos filesystem",
570 "<d:/path/filename> <addr> <bytes>\n"
571 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
572 " starting at address 'addr'.\n"
573),
574
b5251896 575CMD_TBL_ITEM(
7a1ed620 576 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
b5251896
L
577 "Print sub command description/usage",
578 "\n"
579 " - print brief description of all sub commands\n"
580 "fat help command ...\n"
581 " - print detailed usage of sub cmd 'command'"
582),
583
8a2b0da0
L
584/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
585 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
586 FSTR("Alias for 'help'"),
587#ifdef CONFIG_SYS_LONGHELP
588 FSTR(""),
589#endif /* CONFIG_SYS_LONGHELP */
8da60ec5 590 NULL,
8a2b0da0 591#ifdef CONFIG_AUTO_COMPLETE
8da60ec5 592 NULL,
8a2b0da0
L
593#endif
594},
8da60ec5 595/* Mark end of table */
5caa8c2b 596CMD_TBL_END(cmd_tbl_fat)
8a2b0da0
L
597};
598
8a2b0da0 599
fcf1d5b3 600command_ret_t do_fat(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
8a2b0da0 601{
5caa8c2b 602 puts_P(PSTR("Huch?"));
8a2b0da0
L
603
604 return CMD_RET_USAGE;
605}