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