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