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