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