]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
pwd/cd
[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 */
6bc0153d 138 debug_fa("### f_getcwd(): buf: '%s', res: %d\n", buf, res);
8a2b0da0 139
6bc0153d 140 if (res == FR_OK) {
8a2b0da0
L
141 puts(buf);
142 }
143 free(buf);
6bc0153d 144 if (res != FR_OK) {
8a2b0da0
L
145 put_rc(res);
146 return CMD_RET_FAILURE;
147 }
148 return CMD_RET_SUCCESS;
149}
150
151
152/*
153 * cd - Change the current/working directory.
154 *
155 */
016251b6 156command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
8a2b0da0
L
157{
158 char *arg;
159 FRESULT res = 0;
160
8a2b0da0 161 if (argc < 2) {
b5251896 162 arg = getenv_str(PSTR(ENV_HOME));
8a2b0da0 163 if (arg == NULL) {
b5251896 164 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
8a2b0da0
L
165 return CMD_RET_FAILURE;
166 }
167 } else
168 arg = argv[1];
169
6bc0153d 170 if (strlen(arg) > 1 && arg[1] == ':') {
66f6a44e 171 res = f_chdrive(arg);
6bc0153d 172 debug_fa("### f_chdrive(): arg: '%s', res: %d\n", arg, res);
66f6a44e
L
173 if (arg[2] == '\0') {
174 arg[0] = '/';
175 arg[1] = '\0';
176 }
6bc0153d
L
177 }
178 if (res == FR_OK) {
8a2b0da0 179 res = f_chdir(arg);
6bc0153d 180 debug_fa("### f_chdir(): arg: '%s', res: %d\n", arg, res);
8a2b0da0 181 }
66f6a44e 182 if (res != FR_OK) {
8a2b0da0
L
183 put_rc(res);
184 return CMD_RET_FAILURE;
185 }
186 return CMD_RET_SUCCESS;
187}
188
2f53dd65
L
189
190/*
016251b6 191 * ls path - Directory listing
2f53dd65
L
192 *
193 */
016251b6 194command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 195{
bbd45c46 196 FATFS *fs;
2f53dd65
L
197 DIR Dir; /* Directory object */
198 FILINFO Finfo;
199 unsigned long p1;
200 unsigned int s1, s2;
8a2b0da0
L
201 FRESULT res = FR_OK;
202 char *buf;
2f53dd65 203
2f53dd65 204
8a2b0da0
L
205 buf = (char *) malloc(BUFFER_SIZE);
206 if (buf == NULL) {
207 printf_P(PSTR("pwd: Out of Memory!\n"));
208 free(buf);
209 return CMD_RET_FAILURE;
210 }
211
212 if (argc < 2)
213 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
214 else
215 strncpy(buf, argv[1], BUFFER_SIZE);
216
217 if (res == FR_OK)
218 res = f_opendir(&Dir, buf);
219 if (res != FR_OK) {
220 free(buf);
2f53dd65
L
221 put_rc(res);
222 return CMD_RET_FAILURE;
223 }
224
225 p1 = s1 = s2 = 0;
226 for(;;) {
227 res = f_readdir(&Dir, &Finfo);
228 if ((res != FR_OK) || !Finfo.fname[0])
229 break;
230 if (Finfo.fattrib & AM_DIR) {
231 s2++;
232 } else {
233 s1++; p1 += Finfo.fsize;
234 }
7af9364e 235 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
2f53dd65
L
236 (Finfo.fattrib & AM_DIR) ? 'D' : '-',
237 (Finfo.fattrib & AM_RDO) ? 'R' : '-',
238 (Finfo.fattrib & AM_HID) ? 'H' : '-',
239 (Finfo.fattrib & AM_SYS) ? 'S' : '-',
240 (Finfo.fattrib & AM_ARC) ? 'A' : '-',
241 (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
7af9364e
L
242 (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
243 Finfo.fsize, Finfo.fname);
b15d22a4
L
244 if (check_abort())
245 break;
2f53dd65
L
246 }
247
248 if (res == FR_OK) {
249 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
8a2b0da0 250 if (f_getfree(buf, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
251 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
252 }
253
8a2b0da0 254 free(buf);
2f53dd65
L
255 if (res) {
256 put_rc(res);
257 return CMD_RET_FAILURE;
258 }
259
260 return CMD_RET_SUCCESS;
261}
262
016251b6 263#if 0
35edb766
L
264static
265FRESULT mkpath(TCHAR *path)
266{
267 /* TODO: */
268 (void) path;
35edb766
L
269 FILINFO fd
270 TCHAR *p, *q;
271 FRESULT ret;
272
35edb766
L
273 res = f_stat (path, &fd)
274
275 p = strchr(path, ':');
276 if (p == NULL || *++p == '\0' || *p++ != '/')
277 return FR_OK;
278
279 while ((q = strchr(p, '/')) != NULL) {
280 *q = '\0';
281 ret = f_mkdir(path);
282 *q = '/';
283 if (ret != FR_OK && ret != FR_EXIST)
284 return ret;
285 p = q + 1;
286 }
35edb766
L
287
288 return FR_OK;
289}
016251b6
L
290#endif
291
292/* Work register for fs command */
293struct stat_dat_s {
294 DWORD AccSize;
295 WORD AccFiles, AccDirs;
296 FILINFO Finfo;
297};
298
299static
300FRESULT scan_files (
301 char *path, /* Pointer to the working buffer with start path */
302 struct stat_dat_s *statp
303)
304{
305 DIR dirs;
306 FRESULT res;
307 int i;
308 char *fn;
309
310 res = f_opendir(&dirs, path);
311 swirl();
312 if (res == FR_OK) {
313 i = strlen(path);
314 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
315 statp->Finfo.fname[0]) {
316 if (FF_FS_RPATH && statp->Finfo.fname[0] == '.')
317 continue;
318 fn = statp->Finfo.fname;
319 if (statp->Finfo.fattrib & AM_DIR) {
320 statp->AccDirs++;
321 path[i] = '/';
322 strcpy(path+i+1, fn);
323 res = scan_files(path, statp);
324 path[i] = '\0';
325 if (res != FR_OK)
326 break;
327 } else {
328 //printf_P(PSTR("%s/%s\n"), path, fn);
329 statp->AccFiles++;
330 statp->AccSize += statp->Finfo.fsize;
331 }
332 if (check_abort()) {
333 res = 255;
334 break;
335 }
336 }
337 }
338
339 return res;
340}
341
342
343/*
344 * fatstat path - Show logical drive status
345 *
346 */
347command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
348{
349 FATFS *fs;
350 DWORD nfreeclst;
351 FRESULT res;
352 char *buf;
353 char *path = "";
354 struct stat_dat_s statp;
355
356 buf = (char *) malloc(BUFFER_SIZE);
357 if (buf == NULL) {
358 printf_P(PSTR("fat stat: Out of Memory!\n"));
359 return CMD_RET_FAILURE;
360 }
361
362 if (argc > 1)
363 path = argv[1];
364 res = f_getfree(path, &nfreeclst, &fs);
365 if (!res) {
366 printf_P(PSTR(
367 "FAT type: %u\n"
368 "Bytes/Cluster: %lu\n"
369 "Number of FATs: %u\n"
370 "Root DIR entries: %u\n"
371 "Sectors/FAT: %lu\n"
372 "Number of clusters: %lu\n"
373 "FAT start (lba): %lu\n"
374 "DIR start (lba,cluster): %lu\n"
375 "Data start (lba): %lu\n"),
376 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
377 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
378 fs->fatbase, fs->dirbase, fs->database);
379
177aa6a6 380#if FF_USE_LABEL
016251b6
L
381 DWORD serial;
382 res = f_getlabel(path, buf, &serial);
383 if (!res) {
384 printf_P(PSTR(
385 "Volume name: %s\n"
386 "Volume S/N: %04X-%04X\n"),
387 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
388 }
389#endif
390 if (!res) {
016251b6
L
391 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
392 strcpy(buf, path);
393
81a43faf 394 my_puts_P(PSTR("\nCounting... "));
016251b6 395 res = scan_files(buf, &statp);
81a43faf 396 putchar('\r');
016251b6
L
397 }
398 if (!res) {
81a43faf 399 printf_P(PSTR("%u files, %lu bytes.\n%u folders.\n"
016251b6
L
400 "%lu KB total disk space.\n%lu KB available.\n"),
401 statp.AccFiles, statp.AccSize, statp.AccDirs,
402 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
403 );
404 }
405 }
406
407 free(buf);
408 if (res) {
409 put_rc(res);
410 return CMD_RET_FAILURE;
411 }
412 return CMD_RET_SUCCESS;
413}
2f53dd65 414
4565be9a
L
415/*
416 * fatread/write - load binary file to/from a dos filesystem
417 * read <d:/path/filename> <addr> [bytes [pos]]
418 * write <d:/path/filename> <addr> <bytes>
419 */
016251b6 420command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 421{
4565be9a
L
422 FIL File;
423 uint32_t addr;
424 unsigned long bytes;
425 unsigned long pos;
426 unsigned long bytes_rw;
427
fcf1d5b3 428 bool dowrite = (argv[0][0] == 'w');
3b841cea 429 FRESULT res = FR_OK;
4565be9a
L
430 bool buserr = 0;
431 uint32_t timer;
19b9a7d8 432 uint8_t *buffer;
4565be9a 433
4565be9a
L
434 if (argc < (dowrite ? 4 : 3))
435 return CMD_RET_USAGE;
436
2d914b45 437 addr = eval_arg(argv[2], NULL);
4565be9a
L
438 if (addr >= MAX_MEMORY) {
439 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
440 return CMD_RET_FAILURE;
441 }
442 if (argc > 3)
2d914b45 443 bytes = eval_arg(argv[3], NULL);
4565be9a
L
444 else
445 bytes = MAX_MEMORY;
446 if (argc > 4)
2d914b45 447 pos = eval_arg(argv[4], NULL);
4565be9a
L
448 else
449 pos = 0;
450
451 if (addr + bytes > MAX_MEMORY)
452 bytes = MAX_MEMORY - addr;
453
19b9a7d8 454 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 455 if (buffer == NULL) {
19b9a7d8 456 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
457 free(buffer);
458 return CMD_RET_FAILURE;
459 }
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 533
016251b6
L
534/*
535 * command table for fat subcommands
536 */
8a2b0da0 537
8da60ec5 538cmd_tbl_t cmd_tbl_fat[] = {
8a2b0da0 539CMD_TBL_ITEM(
d530fed0 540 status, 2, CTBL_RPT, do_stat,
8a2b0da0
L
541 "Show logical drive status",
542 "dev"
543),
544CMD_TBL_ITEM(
04f84937 545 pwd, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_pwd,
8a2b0da0
L
546 "Print name of current/working directory",
547 ""
548),
549CMD_TBL_ITEM(
04f84937 550 cd, 2, 0|CTBL_SUBCMDAUTO, do_cd,
8a2b0da0
L
551 "Change the current/working directory.",
552 "path"
553),
554CMD_TBL_ITEM(
04f84937 555 ls, 2, CTBL_RPT|CTBL_SUBCMDAUTO, do_ls,
8a2b0da0
L
556 "Directory listing",
557 "path"
558),
559CMD_TBL_ITEM(
560 load, 5, 0, do_rw,
561 "load binary file from a dos filesystem",
562 "<d:/path/filename> <addr> [bytes [pos]]\n"
563 " - Load binary file 'path/filename' on logical drive 'd'\n"
564 " to address 'addr' from dos filesystem.\n"
565 " 'pos' gives the file position to start loading from.\n"
566 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
567 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
568 " the load stops on end of file."
569),
570CMD_TBL_ITEM(
7a1ed620 571 write, 4, 0, do_rw,
8a2b0da0
L
572 "write file into a dos filesystem",
573 "<d:/path/filename> <addr> <bytes>\n"
574 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
575 " starting at address 'addr'.\n"
576),
577
b5251896 578CMD_TBL_ITEM(
7a1ed620 579 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
b5251896
L
580 "Print sub command description/usage",
581 "\n"
582 " - print brief description of all sub commands\n"
583 "fat help command ...\n"
584 " - print detailed usage of sub cmd 'command'"
585),
586
8a2b0da0
L
587/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
588 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
04f84937 589 NULL,
8a2b0da0
L
590#ifdef CONFIG_SYS_LONGHELP
591 FSTR(""),
592#endif /* CONFIG_SYS_LONGHELP */
8da60ec5 593 NULL,
8a2b0da0 594#ifdef CONFIG_AUTO_COMPLETE
8da60ec5 595 NULL,
8a2b0da0
L
596#endif
597},
8da60ec5 598/* Mark end of table */
5caa8c2b 599CMD_TBL_END(cmd_tbl_fat)
8a2b0da0
L
600};
601
8a2b0da0 602
016251b6 603command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
8a2b0da0 604{
5caa8c2b 605 puts_P(PSTR("Huch?"));
8a2b0da0
L
606
607 return CMD_RET_USAGE;
608}