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