]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
Add copyright notice
[z180-stamp.git] / avr / cmd_fat.c
1 /*
2 * (C) Copyright 2014 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 "common.h"
12 #include <stdlib.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 "con-utils.h"
20 #include "print-utils.h"
21 #include "time.h"
22 #include "timer.h"
23 #include "debug.h"
24
25
26 #define MAX_MEMORY (1ul << 20)
27 #define BUFFER_SIZE 512
28
29
30 DWORD get_fattime (void)
31 {
32 time_t timer;
33 struct tm tm_timer;
34
35 time(&timer);
36 gmtime_r(&timer, &tm_timer);
37
38 return fatfs_time(&tm_timer);
39 }
40
41
42 static bool check_abort(void)
43 {
44 bool ret = ctrlc();
45
46 if (ret)
47 printf_P(PSTR("Abort\n"));
48
49 return ret;
50 }
51
52
53 static const FLASH char * const FLASH rc_names[] = {
54 FSTR("OK"),
55 FSTR("DISK_ERR"),
56 FSTR("INT_ERR"),
57 FSTR("NOT_READY"),
58 FSTR("NO_FILE"),
59 FSTR("NO_PATH"),
60 FSTR("INVALID_NAME"),
61 FSTR("DENIED"),
62 FSTR("EXIST"),
63 FSTR("INVALID_OBJECT"),
64 FSTR("WRITE_PROTECTED"),
65 FSTR("INVALID_DRIVE"),
66 FSTR("NOT_ENABLED"),
67 FSTR("NO_FILE_SYSTEM"),
68 FSTR("MKFS_ABORTED"),
69 FSTR("TIMEOUT"),
70 FSTR("LOCKED"),
71 FSTR("NOT_ENOUGH_CORE"),
72 FSTR("TOO_MANY_OPEN_FILES")
73 };
74
75 static
76 void put_rc (FRESULT rc)
77 {
78 if (rc < ARRAY_SIZE(rc_names)) {
79 #if GCC_BUG_61443
80 printf_P(PSTR("rc=%u FR_"), rc);
81 my_puts_P(rc_names[rc]);
82 my_puts_P(PSTR("\n"));
83 #else
84 printf_P(PSTR("rc=%u FR_%S\n"), rc, rc_names[rc]);
85 #endif
86 }
87 }
88
89
90 static void swirl(void)
91 {
92 static const FLASH char swirlchar[] = { '-','\\','|','/' };
93 static uint_fast8_t cnt;
94 static uint32_t tstamp;
95
96 if (get_timer(0) > tstamp) {
97 printf_P(PSTR("\b%c"), swirlchar[cnt]);
98 cnt = (cnt+1) % ARRAY_SIZE(swirlchar);
99 tstamp = get_timer(0) + 250;
100 }
101 }
102
103 /* Work register for fs command */
104 struct stat_dat_s {
105 DWORD AccSize;
106 WORD AccFiles, AccDirs;
107 FILINFO Finfo;
108 };
109
110 static
111 FRESULT scan_files (
112 char *path, /* Pointer to the working buffer with start path */
113 struct stat_dat_s *statp
114 )
115 {
116 DIR dirs;
117 FRESULT res;
118 int i;
119 char *fn;
120
121 res = f_opendir(&dirs, path);
122 swirl();
123 if (res == FR_OK) {
124 i = strlen(path);
125 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
126 statp->Finfo.fname[0]) {
127 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
128 continue;
129 fn = statp->Finfo.fname;
130 if (statp->Finfo.fattrib & AM_DIR) {
131 statp->AccDirs++;
132 path[i] = '/';
133 strcpy(path+i+1, fn);
134 res = scan_files(path, statp);
135 path[i] = '\0';
136 if (res != FR_OK)
137 break;
138 } else {
139 //printf_P(PSTR("%s/%s\n"), path, fn);
140 statp->AccFiles++;
141 statp->AccSize += statp->Finfo.fsize;
142 }
143 if (check_abort()) {
144 res = 255;
145 break;
146 }
147 }
148 }
149
150 return res;
151 }
152
153
154 /*
155 * fatstat path - Show logical drive status
156 *
157 */
158 command_ret_t do_fat_stat(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
159 {
160 FATFS *FatFs, *fs;
161 DWORD nfreeclst;
162 FRESULT res;
163 char *path;
164 struct stat_dat_s statp;
165
166 (void) cmdtp; (void) flag; (void) argc;
167
168 FatFs = (FATFS *) malloc(sizeof (FATFS));
169 path = (char *) malloc(BUFFER_SIZE);
170 if (FatFs == NULL || path == NULL) {
171 printf_P(PSTR("fatstat: Out of Memory!\n"));
172 free(path);
173 free(FatFs);
174 return CMD_RET_FAILURE;
175 }
176
177 res = f_mount(FatFs, argv[1], 0);
178 if (!res) {
179 res = f_getfree(argv[1], &nfreeclst, &fs);
180 if (!res) {
181 printf_P(PSTR(
182 "FAT type: %u\n"
183 "Bytes/Cluster: %lu\n"
184 "Number of FATs: %u\n"
185 "Root DIR entries: %u\n"
186 "Sectors/FAT: %lu\n"
187 "Number of clusters: %lu\n"
188 "FAT start (lba): %lu\n"
189 "DIR start (lba,cluster): %lu\n"
190 "Data start (lba): %lu\n"),
191 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
192 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
193 fs->fatbase, fs->dirbase, fs->database);
194
195 #if _USE_LABEL
196 TCHAR label[12];
197 DWORD serial;
198 res = f_getlabel(argv[1], label, &serial);
199 if (!res) {
200 printf_P(PSTR(
201 "Volume name: %s\n"
202 "Volume S/N: %04X-%04X\n"),
203 label, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
204 }
205 #endif
206 if (!res) {
207 my_puts_P(PSTR("\nCounting... "));
208 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
209 strcpy(path, argv[1]);
210
211 res = scan_files(path, &statp);
212 }
213 if (!res) {
214 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
215 "%lu KB total disk space.\n%lu KB available.\n"),
216 statp.AccFiles, statp.AccSize, statp.AccDirs,
217 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
218 );
219 }
220 }
221 }
222
223 free(path);
224 free(FatFs);
225 f_mount(NULL, argv[1], 0);
226 if (res) {
227 put_rc(res);
228 return CMD_RET_FAILURE;
229 }
230 return CMD_RET_SUCCESS;
231 }
232
233
234 /*
235 * fatls path - Directory listing
236 *
237 */
238 command_ret_t do_fat_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
239 {
240 FATFS FatFs, *fs;
241 DIR Dir; /* Directory object */
242 FILINFO Finfo;
243 unsigned long p1;
244 unsigned int s1, s2;
245 FRESULT res;
246 #if _USE_LFN
247 char Lfname[_MAX_LFN+1];
248 Finfo.lfname = Lfname;
249 Finfo.lfsize = sizeof Lfname;
250 #endif
251
252 (void) cmdtp; (void) flag; (void) argc;
253
254 res = f_mount(&FatFs, argv[1], 0);
255 if (!res)
256 res = f_opendir(&Dir, argv[1]);
257 if (res) {
258 put_rc(res);
259 return CMD_RET_FAILURE;
260 }
261
262 p1 = s1 = s2 = 0;
263 for(;;) {
264 res = f_readdir(&Dir, &Finfo);
265 if ((res != FR_OK) || !Finfo.fname[0])
266 break;
267 if (Finfo.fattrib & AM_DIR) {
268 s2++;
269 } else {
270 s1++; p1 += Finfo.fsize;
271 }
272 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu "),
273 (Finfo.fattrib & AM_DIR) ? 'D' : '-',
274 (Finfo.fattrib & AM_RDO) ? 'R' : '-',
275 (Finfo.fattrib & AM_HID) ? 'H' : '-',
276 (Finfo.fattrib & AM_SYS) ? 'S' : '-',
277 (Finfo.fattrib & AM_ARC) ? 'A' : '-',
278 (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
279 (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63, Finfo.fsize);
280 #if _USE_LFN
281 printf_P(PSTR("%s\n"), *Lfname ? Lfname : Finfo.fname);
282 #else
283 printf_P(PSTR("%s\n"), Finfo.fname);
284 #endif
285 if (check_abort())
286 break;
287 }
288
289 if (res == FR_OK) {
290 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
291 if (f_getfree(argv[1], (DWORD*)&p1, &fs) == FR_OK)
292 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
293 }
294
295 if (res) {
296 put_rc(res);
297 return CMD_RET_FAILURE;
298 }
299
300 return CMD_RET_SUCCESS;
301 }
302
303 static
304 FRESULT mkpath(TCHAR *path)
305 {
306 /* TODO: */
307 (void) path;
308 #if 0
309 FILINFO fd
310 TCHAR *p, *q;
311 FRESULT ret;
312
313 #if _USE_LFN
314 fd.lfname = 0;
315 #endif
316
317
318 res = f_stat (path, &fd)
319
320 p = strchr(path, ':');
321 if (p == NULL || *++p == '\0' || *p++ != '/')
322 return FR_OK;
323
324 while ((q = strchr(p, '/')) != NULL) {
325 *q = '\0';
326 ret = f_mkdir(path);
327 *q = '/';
328 if (ret != FR_OK && ret != FR_EXIST)
329 return ret;
330 p = q + 1;
331 }
332 #endif
333
334 return FR_OK;
335 }
336
337 /*
338 * fatread/write - load binary file to/from a dos filesystem
339 * read <d:/path/filename> <addr> [bytes [pos]]
340 * write <d:/path/filename> <addr> <bytes>
341 */
342 command_ret_t do_fat_rw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
343 {
344 FATFS *FatFs;
345 FIL File;
346 uint32_t addr;
347 unsigned long bytes;
348 unsigned long pos;
349 unsigned long bytes_rw;
350
351 bool dowrite = (argv[0][3] == 'w');
352 FRESULT res;
353 bool buserr = 0;
354 uint32_t timer;
355 uint8_t *buffer;
356
357 (void) cmdtp; (void) flag;
358
359 if (argc < (dowrite ? 4 : 3))
360 return CMD_RET_USAGE;
361
362 addr = strtoul(argv[2], 0, 16);
363 if (addr >= MAX_MEMORY) {
364 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
365 return CMD_RET_FAILURE;
366 }
367 if (argc > 3)
368 bytes = strtoul(argv[3], 0, 16);
369 else
370 bytes = MAX_MEMORY;
371 if (argc > 4)
372 pos = strtoul(argv[4], 0, 16);
373 else
374 pos = 0;
375
376 if (addr + bytes > MAX_MEMORY)
377 bytes = MAX_MEMORY - addr;
378
379 FatFs = (FATFS *) malloc(sizeof (FATFS));
380 buffer = (uint8_t *) malloc(BUFFER_SIZE);
381 if (FatFs == NULL || buffer == NULL) {
382 printf_P(PSTR("fatstat: Out of Memory!\n"));
383 free(FatFs);
384 free(buffer);
385 return CMD_RET_FAILURE;
386 }
387
388 res = f_mount(FatFs, argv[1], 0);
389
390 if (!res) {
391 if (dowrite) {
392 res = mkpath(argv[1]);
393 }
394 }
395 if (!res) {
396 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
397 : FA_READ );
398
399 if (!res) {
400 res = f_lseek(&File, pos);
401 if (!res) {
402 bytes_rw = 0;
403 timer = get_timer(0);
404 while (bytes) {
405 unsigned int cnt, br;
406
407 if (bytes >= BUFFER_SIZE) {
408 cnt = BUFFER_SIZE;
409 bytes -= BUFFER_SIZE;
410 } else {
411 cnt = bytes; bytes = 0;
412 }
413 if (dowrite) {
414 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
415 buserr = 1;
416 break;
417 }
418 z80_read_block(buffer, addr, cnt);
419 z80_bus_cmd(Release);
420 res = f_write(&File, buffer, cnt, &br);
421 if (res != FR_OK)
422 break;
423 } else {
424 res = f_read(&File, buffer, cnt, &br);
425 if (res != FR_OK)
426 break;
427 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
428 buserr = 1;
429 break;
430 }
431 z80_write_block(buffer, addr, br);
432 z80_bus_cmd(Release);
433 }
434 addr += br;
435
436 bytes_rw += br;
437 if (cnt != br) {
438 if (dowrite)
439 printf_P(PSTR("Disk full?\n"));
440 break;
441 }
442 if (check_abort())
443 break;
444 }
445
446 FRESULT fr = f_close(&File);
447 if (!res)
448 res = fr;
449 timer = get_timer(timer);
450 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
451 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
452 }
453 }
454 f_mount(NULL, argv[1], 0);
455 }
456
457 free(buffer);
458 free(FatFs);
459
460 if (buserr)
461 my_puts_P(PSTR("Bus timeout\n"));
462 if (res)
463 put_rc(res);
464 if (buserr || res)
465 return CMD_RET_FAILURE;
466
467 return CMD_RET_SUCCESS;
468 }