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