]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
ENV_HOME macro. 'fat help' command table entry
[z180-stamp.git] / avr / cmd_fat.c
1 /*
2 * (C) Copyright 2014,2016 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 /*
8 * FAT filesystem commands
9 */
10
11 #include "cmd_fat.h"
12 #include "common.h"
13 #include <string.h>
14 #include <stdbool.h>
15
16 #include "command.h"
17 #include "ff.h"
18 #include "z80-if.h"
19 #include "eval_arg.h"
20 #include "con-utils.h"
21 #include "print-utils.h"
22 #include "time.h"
23 #include "timer.h"
24 #include "debug.h"
25 #include "env.h"
26
27 /* TODO: use memory size test function (cmd_mem.c) */
28 #define MAX_MEMORY (1ul << 19)
29 #define BUFFER_SIZE 512
30
31
32 FATFS FatFs0;
33 FATFS FatFs1;
34
35 char chur_drv[3];
36
37 DWORD get_fattime (void)
38 {
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);
46 }
47
48
49 static 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
60 static 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"),
79 FSTR("TOO_MANY_OPEN_FILES"),
80 FSTR("INVALID_PARAMETER")
81 };
82
83 static
84 void put_rc (FRESULT rc)
85 {
86 #if GCC_BUG_61443
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"));
90 #else
91 printf_P(PSTR("rc=%u FR_%S\n"), rc,
92 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
93 #endif
94 }
95
96
97 void setup_fatfs(void)
98 {
99 f_mount(&FatFs0, "0:", 0);
100 f_mount(&FatFs1, "1:", 0);
101 }
102
103
104 static void swirl(void)
105 {
106 static const FLASH char swirlchar[] = { '-','\\','|','/' };
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);
113 tstamp = get_timer(0) + 250;
114 }
115 }
116
117 /* Work register for fs command */
118 struct stat_dat_s {
119 DWORD AccSize;
120 WORD AccFiles, AccDirs;
121 FILINFO Finfo;
122 };
123
124 static
125 FRESULT scan_files (
126 char *path, /* Pointer to the working buffer with start path */
127 struct stat_dat_s *statp
128 )
129 {
130 DIR dirs;
131 FRESULT res;
132 int i;
133 char *fn;
134
135 res = f_opendir(&dirs, path);
136 swirl();
137 if (res == FR_OK) {
138 i = strlen(path);
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);
149 path[i] = '\0';
150 if (res != FR_OK)
151 break;
152 } else {
153 //printf_P(PSTR("%s/%s\n"), path, fn);
154 statp->AccFiles++;
155 statp->AccSize += statp->Finfo.fsize;
156 }
157 if (check_abort()) {
158 res = 255;
159 break;
160 }
161 }
162 }
163
164 return res;
165 }
166
167
168 /*
169 * fatstat path - Show logical drive status
170 *
171 */
172 command_ret_t do_stat(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
173 {
174 FATFS *fs;
175 DWORD nfreeclst;
176 FRESULT res;
177 char *path;
178 struct stat_dat_s statp;
179
180 (void) cmdtp; (void) flag; (void) argc;
181
182 path = (char *) malloc(BUFFER_SIZE);
183 if (path == NULL) {
184 printf_P(PSTR("fatstat: Out of Memory!\n"));
185 free(path);
186 return CMD_RET_FAILURE;
187 }
188
189 res = f_getfree(argv[1], &nfreeclst, &fs);
190 if (!res) {
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);
204
205 #if _USE_LABEL
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 }
215 #endif
216 if (!res) {
217 my_puts_P(PSTR("\nCounting... "));
218 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
219 strcpy(path, argv[1]);
220
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 );
229 }
230 }
231
232 free(path);
233 if (res) {
234 put_rc(res);
235 return CMD_RET_FAILURE;
236 }
237 return CMD_RET_SUCCESS;
238 }
239
240 /*
241 * pwd - Print current directory of the current drive.
242 *
243 */
244 command_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 */
276 command_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) {
284 arg = getenv_str(PSTR(ENV_HOME));
285 if (arg == NULL) {
286 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
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
310
311 /*
312 * fatls path - Directory listing
313 *
314 */
315 command_ret_t do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
316 {
317 FATFS *fs;
318 DIR Dir; /* Directory object */
319 FILINFO Finfo;
320 unsigned long p1;
321 unsigned int s1, s2;
322 FRESULT res = FR_OK;
323 char *buf;
324
325 (void) cmdtp; (void) flag; (void) argc;
326
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);
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 }
357 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
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,
364 (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
365 Finfo.fsize, Finfo.fname);
366 if (check_abort())
367 break;
368 }
369
370 if (res == FR_OK) {
371 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
372 if (f_getfree(buf, (DWORD*)&p1, &fs) == FR_OK)
373 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
374 }
375
376 free(buf);
377 if (res) {
378 put_rc(res);
379 return CMD_RET_FAILURE;
380 }
381
382 return CMD_RET_SUCCESS;
383 }
384
385 static
386 FRESULT mkpath(TCHAR *path)
387 {
388 /* TODO: */
389 (void) path;
390 #if 0
391 FILINFO fd
392 TCHAR *p, *q;
393 FRESULT ret;
394
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 }
413
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 */
419 command_ret_t do_rw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
420 {
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');
428 FRESULT res = FR_OK;
429 bool buserr = 0;
430 uint32_t timer;
431 uint8_t *buffer;
432
433 (void) cmdtp; (void) flag;
434
435 if (argc < (dowrite ? 4 : 3))
436 return CMD_RET_USAGE;
437
438 addr = eval_arg(argv[2], NULL);
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)
444 bytes = eval_arg(argv[3], NULL);
445 else
446 bytes = MAX_MEMORY;
447 if (argc > 4)
448 pos = eval_arg(argv[4], NULL);
449 else
450 pos = 0;
451
452 if (addr + bytes > MAX_MEMORY)
453 bytes = MAX_MEMORY - addr;
454
455 buffer = (uint8_t *) malloc(BUFFER_SIZE);
456 if (buffer == NULL) {
457 printf_P(PSTR("fatstat: Out of Memory!\n"));
458 free(buffer);
459 return CMD_RET_FAILURE;
460 }
461
462 if (dowrite) {
463 res = mkpath(argv[1]);
464 }
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
477 if (bytes >= BUFFER_SIZE) {
478 cnt = BUFFER_SIZE;
479 bytes -= BUFFER_SIZE;
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 }
512 if (check_abort())
513 break;
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 }
524 }
525
526 free(buffer);
527
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 }
537
538
539 static
540 command_ret_t do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
541
542 cmd_tbl_t cmd_fat_sub[] = {
543 CMD_TBL_ITEM(
544 stat, 2, 1, do_stat,
545 "Show logical drive status",
546 "dev"
547 ),
548 CMD_TBL_ITEM(
549 pwd, 2, 1, do_pwd,
550 "Print name of current/working directory",
551 ""
552 ),
553 CMD_TBL_ITEM(
554 cd, 2, 1, do_cd,
555 "Change the current/working directory.",
556 "path"
557 ),
558 CMD_TBL_ITEM(
559 ls, 2, 1, do_ls,
560 "Directory listing",
561 "path"
562 ),
563 CMD_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 ),
574 CMD_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
582 CMD_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
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
603 static
604 command_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
610 command_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 }