diff options
Diffstat (limited to 'avr/cmd_fat.c')
-rw-r--r-- | avr/cmd_fat.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/avr/cmd_fat.c b/avr/cmd_fat.c new file mode 100644 index 0000000..6ffbb79 --- /dev/null +++ b/avr/cmd_fat.c @@ -0,0 +1,450 @@ +/* + * (C) Copyright 2014,2016 Leo C. <erbl259-lmu@yahoo.de> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * FAT filesystem commands + */ + +#include "common.h" +#include <string.h> +#include <stdbool.h> + +#include "command.h" +#include "ff.h" +#include "z80-if.h" +#include "eval_arg.h" +#include "con-utils.h" +#include "print-utils.h" +#include "time.h" +#include "timer.h" +#include "debug.h" + +/* TODO: use memory size test function (cmd_mem.c) */ +#define MAX_MEMORY (1ul << 19) +#define BUFFER_SIZE 512 + + +DWORD get_fattime (void) +{ + time_t timer; + struct tm tm_timer; + + time(&timer); + gmtime_r(&timer, &tm_timer); + + return fatfs_time(&tm_timer); +} + + +static bool check_abort(void) +{ + bool ret = ctrlc(); + + if (ret) + printf_P(PSTR("Abort\n")); + + return ret; +} + + +static const FLASH char * const FLASH rc_names[] = { + FSTR("OK"), + FSTR("DISK_ERR"), + FSTR("INT_ERR"), + FSTR("NOT_READY"), + FSTR("NO_FILE"), + FSTR("NO_PATH"), + FSTR("INVALID_NAME"), + FSTR("DENIED"), + FSTR("EXIST"), + FSTR("INVALID_OBJECT"), + FSTR("WRITE_PROTECTED"), + FSTR("INVALID_DRIVE"), + FSTR("NOT_ENABLED"), + FSTR("NO_FILE_SYSTEM"), + FSTR("MKFS_ABORTED"), + FSTR("TIMEOUT"), + FSTR("LOCKED"), + FSTR("NOT_ENOUGH_CORE"), + FSTR("TOO_MANY_OPEN_FILES") + }; + +static +void put_rc (FRESULT rc) +{ + if (rc < ARRAY_SIZE(rc_names)) { +#if GCC_BUG_61443 + printf_P(PSTR("rc=%u FR_"), rc); + my_puts_P(rc_names[rc]); + my_puts_P(PSTR("\n")); +#else + printf_P(PSTR("rc=%u FR_%S\n"), rc, rc_names[rc]); +#endif + } +} + + +static void swirl(void) +{ + static const FLASH char swirlchar[] = { '-','\\','|','/' }; + static uint_fast8_t cnt; + static uint32_t tstamp; + + if (get_timer(0) > tstamp) { + printf_P(PSTR("\b%c"), swirlchar[cnt]); + cnt = (cnt+1) % ARRAY_SIZE(swirlchar); + tstamp = get_timer(0) + 250; + } +} + +/* Work register for fs command */ +struct stat_dat_s { + DWORD AccSize; + WORD AccFiles, AccDirs; + FILINFO Finfo; +}; + +static +FRESULT scan_files ( + char *path, /* Pointer to the working buffer with start path */ + struct stat_dat_s *statp +) +{ + DIR dirs; + FRESULT res; + int i; + char *fn; + + res = f_opendir(&dirs, path); + swirl(); + if (res == FR_OK) { + i = strlen(path); + while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) && + statp->Finfo.fname[0]) { + if (_FS_RPATH && statp->Finfo.fname[0] == '.') + continue; + fn = statp->Finfo.fname; + if (statp->Finfo.fattrib & AM_DIR) { + statp->AccDirs++; + path[i] = '/'; + strcpy(path+i+1, fn); + res = scan_files(path, statp); + path[i] = '\0'; + if (res != FR_OK) + break; + } else { + //printf_P(PSTR("%s/%s\n"), path, fn); + statp->AccFiles++; + statp->AccSize += statp->Finfo.fsize; + } + if (check_abort()) { + res = 255; + break; + } + } + } + + return res; +} + + +/* + * fatstat path - Show logical drive status + * + */ +command_ret_t do_fat_stat(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + FATFS *fs; + DWORD nfreeclst; + FRESULT res; + char *path; + struct stat_dat_s statp; + + (void) cmdtp; (void) flag; (void) argc; + + path = (char *) malloc(BUFFER_SIZE); + if (path == NULL) { + printf_P(PSTR("fatstat: Out of Memory!\n")); + free(path); + return CMD_RET_FAILURE; + } + + res = f_getfree(argv[1], &nfreeclst, &fs); + if (!res) { + printf_P(PSTR( + "FAT type: %u\n" + "Bytes/Cluster: %lu\n" + "Number of FATs: %u\n" + "Root DIR entries: %u\n" + "Sectors/FAT: %lu\n" + "Number of clusters: %lu\n" + "FAT start (lba): %lu\n" + "DIR start (lba,cluster): %lu\n" + "Data start (lba): %lu\n"), + fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats, + fs->n_rootdir, fs->fsize, fs->n_fatent - 2, + fs->fatbase, fs->dirbase, fs->database); + +#if _USE_LABEL + TCHAR label[12]; + DWORD serial; + res = f_getlabel(argv[1], label, &serial); + if (!res) { + printf_P(PSTR( + "Volume name: %s\n" + "Volume S/N: %04X-%04X\n"), + label, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF)); + } +#endif + if (!res) { + my_puts_P(PSTR("\nCounting... ")); + statp.AccSize = statp.AccFiles = statp.AccDirs = 0; + strcpy(path, argv[1]); + + res = scan_files(path, &statp); + } + if (!res) { + printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n" + "%lu KB total disk space.\n%lu KB available.\n"), + statp.AccFiles, statp.AccSize, statp.AccDirs, + (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2) + ); + } + } + + free(path); + if (res) { + put_rc(res); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + + +/* + * fatls path - Directory listing + * + */ +command_ret_t do_fat_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + FATFS *fs; + DIR Dir; /* Directory object */ + FILINFO Finfo; + unsigned long p1; + unsigned int s1, s2; + FRESULT res; +#if _USE_LFN + char Lfname[_MAX_LFN+1]; + Finfo.lfname = Lfname; + Finfo.lfsize = sizeof Lfname; +#endif + + (void) cmdtp; (void) flag; (void) argc; + + res = f_opendir(&Dir, argv[1]); + if (res) { + put_rc(res); + return CMD_RET_FAILURE; + } + + p1 = s1 = s2 = 0; + for(;;) { + res = f_readdir(&Dir, &Finfo); + if ((res != FR_OK) || !Finfo.fname[0]) + break; + if (Finfo.fattrib & AM_DIR) { + s2++; + } else { + s1++; p1 += Finfo.fsize; + } + printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu "), + (Finfo.fattrib & AM_DIR) ? 'D' : '-', + (Finfo.fattrib & AM_RDO) ? 'R' : '-', + (Finfo.fattrib & AM_HID) ? 'H' : '-', + (Finfo.fattrib & AM_SYS) ? 'S' : '-', + (Finfo.fattrib & AM_ARC) ? 'A' : '-', + (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31, + (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63, Finfo.fsize); +#if _USE_LFN + printf_P(PSTR("%s\n"), *Lfname ? Lfname : Finfo.fname); +#else + printf_P(PSTR("%s\n"), Finfo.fname); +#endif + if (check_abort()) + break; + } + + if (res == FR_OK) { + printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2); + if (f_getfree(argv[1], (DWORD*)&p1, &fs) == FR_OK) + printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2); + } + + if (res) { + put_rc(res); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static +FRESULT mkpath(TCHAR *path) +{ + /* TODO: */ + (void) path; +#if 0 + FILINFO fd + TCHAR *p, *q; + FRESULT ret; + +#if _USE_LFN + fd.lfname = 0; +#endif + + + res = f_stat (path, &fd) + + p = strchr(path, ':'); + if (p == NULL || *++p == '\0' || *p++ != '/') + return FR_OK; + + while ((q = strchr(p, '/')) != NULL) { + *q = '\0'; + ret = f_mkdir(path); + *q = '/'; + if (ret != FR_OK && ret != FR_EXIST) + return ret; + p = q + 1; + } +#endif + + return FR_OK; +} + +/* + * fatread/write - load binary file to/from a dos filesystem + * read <d:/path/filename> <addr> [bytes [pos]] + * write <d:/path/filename> <addr> <bytes> + */ +command_ret_t do_fat_rw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + FIL File; + uint32_t addr; + unsigned long bytes; + unsigned long pos; + unsigned long bytes_rw; + + bool dowrite = (argv[0][3] == 'w'); + FRESULT res = FR_OK; + bool buserr = 0; + uint32_t timer; + uint8_t *buffer; + + (void) cmdtp; (void) flag; + + if (argc < (dowrite ? 4 : 3)) + return CMD_RET_USAGE; + + addr = eval_arg(argv[2], NULL); + if (addr >= MAX_MEMORY) { + printf_P(PSTR("address too high: 0x%0lx\n"), addr); + return CMD_RET_FAILURE; + } + if (argc > 3) + bytes = eval_arg(argv[3], NULL); + else + bytes = MAX_MEMORY; + if (argc > 4) + pos = eval_arg(argv[4], NULL); + else + pos = 0; + + if (addr + bytes > MAX_MEMORY) + bytes = MAX_MEMORY - addr; + + buffer = (uint8_t *) malloc(BUFFER_SIZE); + if (buffer == NULL) { + printf_P(PSTR("fatstat: Out of Memory!\n")); + free(buffer); + return CMD_RET_FAILURE; + } + + if (dowrite) { + res = mkpath(argv[1]); + } + if (!res) { + res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS + : FA_READ ); + + if (!res) { + res = f_lseek(&File, pos); + if (!res) { + bytes_rw = 0; + timer = get_timer(0); + while (bytes) { + unsigned int cnt, br; + + if (bytes >= BUFFER_SIZE) { + cnt = BUFFER_SIZE; + bytes -= BUFFER_SIZE; + } else { + cnt = bytes; bytes = 0; + } + if (dowrite) { + if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { + buserr = 1; + break; + } + z80_read_block(buffer, addr, cnt); + z80_bus_cmd(Release); + res = f_write(&File, buffer, cnt, &br); + if (res != FR_OK) + break; + } else { + res = f_read(&File, buffer, cnt, &br); + if (res != FR_OK) + break; + if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { + buserr = 1; + break; + } + z80_write_block(buffer, addr, br); + z80_bus_cmd(Release); + } + addr += br; + + bytes_rw += br; + if (cnt != br) { + if (dowrite) + printf_P(PSTR("Disk full?\n")); + break; + } + if (check_abort()) + break; + } + + FRESULT fr = f_close(&File); + if (!res) + res = fr; + timer = get_timer(timer); + printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"), + bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0); + } + } + } + + free(buffer); + + if (buserr) + my_puts_P(PSTR("Bus timeout\n")); + if (res) + put_rc(res); + if (buserr || res) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} |