/* * Copyright (C) 2016 Michel Rottleuthner * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup tests * @{ * * @file * @brief Test application for the fatfs package. * * @author Michel Rottleuthner * * @} */ #if FATFS_FFCONF_OPT_FS_NORTC == 0 #include "periph/rtc.h" #endif #include "fatfs_diskio_common.h" #include "fatfs/ff.h" #include "shell.h" #include #include #include #include #include #define TEST_FATFS_READ_BUFFER_SIZE 64 #define TEST_FATFS_MAX_LBL_SIZE 64 #define TEST_FATFS_MAX_VOL_STR_LEN 8 #define TEST_FATFS_FIXED_SECTOR_SIZE 512 #define TEST_FATFS_FATENT_OFFSET 2 #define TEST_FATFS_SHIFT_B_TO_GIB 30 #define TEST_FATFS_SHIFT_B_TO_MIB 20 #define TEST_FATFS_RTC_MON_OFFSET 1 #define TEST_FATFS_RTC_YEAR 2000 #define TEST_FATFS_RTC_MON 1 #define TEST_FATFS_RTC_DAY 1 #define TEST_FATFS_RTC_H 0 #define TEST_FATFS_RTC_M 0 #define TEST_FATFS_RTC_S 0 #define IEC_KIBI 1024 #define SI_KILO 1000 FATFS fat_fs; /* FatFs work area needed for each volume */ static int _mount(int argc, char **argv) { int vol_idx; if (argc != 2) { printf("usage: %s \n", argv[0]); return -1; } vol_idx = (int)atoi(argv[1]); char volume_str[TEST_FATFS_MAX_VOL_STR_LEN]; sprintf(volume_str, "%d:/", vol_idx); puts("mounting file system image..."); /* "0:/" points to the root dir of drive 0 */ FRESULT mountresu = f_mount(&fat_fs, volume_str, 1); TCHAR label[TEST_FATFS_MAX_LBL_SIZE]; if (mountresu == FR_OK) { puts("[OK]"); if (f_getlabel("", label, NULL) == FR_OK) { printf("Volume name: %s\n", label); } FATFS *fs; DWORD fre_clust; /* Get volume information and free clusters of selected drive */ if (f_getfree(volume_str, &fre_clust, &fs) != FR_OK) { puts("wasn't able to get volume size info!"); } else { #if _MAX_SS == _MIN_SS uint16_t sector_size = TEST_FATFS_FIXED_SECTOR_SIZE; #else uint16_t sector_size = fs->ssize; #endif uint64_t total_bytes = (fs->n_fatent - TEST_FATFS_FATENT_OFFSET) * fs->csize; total_bytes *= sector_size; uint64_t free_bytes = fre_clust * fs->csize; free_bytes *= sector_size; uint32_t to_gib_i = total_bytes >> TEST_FATFS_SHIFT_B_TO_GIB; uint32_t to_gib_f = ((((total_bytes >> TEST_FATFS_SHIFT_B_TO_MIB) - to_gib_i * IEC_KIBI) * SI_KILO) / IEC_KIBI); uint32_t fr_gib_i = free_bytes >> TEST_FATFS_SHIFT_B_TO_GIB; uint32_t fr_gib_f = ((((free_bytes >> TEST_FATFS_SHIFT_B_TO_MIB) - fr_gib_i * IEC_KIBI) * SI_KILO) / IEC_KIBI); printf("%" PRIu32 ",%03" PRIu32 " GiB of %" PRIu32 ",%03" PRIu32 " GiB available\n", fr_gib_i, fr_gib_f, to_gib_i, to_gib_f); } } else { puts("[FAILED]"); switch (mountresu) { case FR_NO_FILESYSTEM: puts("no filesystem -> you need to format the card to FAT"); break; case FR_DISK_ERR: puts("error in the low-level disk driver!"); break; default: printf("error %d -> see ff.h of fatfs package for " "further details\n", mountresu); } return -1; } return 0; } static int _touch(int argc, char **argv) { FIL fd; if (argc != 2) { printf("usage: %s \n", argv[0]); return -1; } FRESULT open_resu = f_open(&fd, argv[1], FA_WRITE | FA_CREATE_ALWAYS); if (open_resu == FR_OK) { FRESULT close_resu = f_close(&fd); if (close_resu == FR_OK) { puts("[OK]"); return 0; } printf("[FAILED] (f_close error %d)\n", close_resu); return -2; } printf("[FAILED] (f_open error %d)\n", open_resu); return -3; } static int _read(int argc, char **argv) { FIL fd; int resu = 0; if ((argc < 2) || (argc > 3)) { printf("usage: %s []\n", argv[0]); return -1; } FRESULT open_resu = f_open(&fd, argv[1], FA_READ | FA_OPEN_EXISTING); if (open_resu == FR_OK) { UINT read_chunk; uint32_t len = ((argc == 3) ? (uint32_t)atoi(argv[2]) : f_size(&fd)); char buffer[TEST_FATFS_READ_BUFFER_SIZE]; for (uint32_t read = 0; read < len; read += read_chunk) { uint32_t to_read = len - read; if (to_read > sizeof(buffer)) { to_read = sizeof(buffer); } FRESULT lseek_resu = f_lseek(&fd, read); if (lseek_resu != FR_OK) { printf("[FAILED] f_lseek error %d\n", lseek_resu); resu = -3; break; } FRESULT read_resu = f_read(&fd, buffer, to_read, &read_chunk); if (read_resu != FR_OK) { printf("[FAILED] (f_read error %d)\n", read_resu); resu = -4; break; } for (uint32_t i = 0; i < read_chunk; i++) { printf("%c", buffer[i]); } } puts(""); FRESULT close_resu = f_close(&fd); if (close_resu == FR_OK) { puts("[OK]"); resu = 0; } else { printf("[FAILED] (f_close error %d)\n", open_resu); resu = -5; } } else { printf("[FAILED] (f_open error %d)\n", open_resu); resu = -2; } return resu; } static int _write(int argc, char **argv) { FIL fd; UINT bw; if (argc != 3) { printf("usage: %s \n", argv[0]); return -1; } uint32_t len = strlen(argv[2]); FRESULT open_resu = f_open(&fd, argv[1], FA_WRITE | FA_OPEN_APPEND); if (open_resu == FR_OK) { printf("writing %" PRId32 " bytes to %s ...", len, argv[1]); FRESULT write_resu = f_write(&fd, argv[2], len, &bw); if ((write_resu != FR_OK) || (bw < len)) { printf("[FAILED] (f_write error %d)\n", write_resu); return -2; } else { FRESULT close_resu = f_close(&fd); if (close_resu == FR_OK) { puts("[OK]"); return 0; } printf("[FAILED] (f_close error %d)\n", open_resu); return -3; } } printf("[FAILED] (f_open error %d)\n", open_resu); return -1; } static int _ls(int argc, char **argv) { char *path; FRESULT res; DIR dir; static FILINFO fno; if (argc == 2) { path = argv[1]; } else { path = "/"; } res = f_opendir(&dir, path);/* Open the directory */ if (res == FR_OK) { while (true) { res = f_readdir(&dir, &fno); /* Read a directory item */ if ((res != FR_OK) || fno.fname[0] == 0) { break; /* Break on error or end of dir */ } if (fno.fattrib & AM_DIR) { /* if this element is a directory */ printf("%s%s/\n", path, fno.fname); } else { printf("%s/%s\n", path, fno.fname); } } f_closedir(&dir); return 0; } printf("[FAILED] error %d\n", res); return -1; } static int _mkfs(int argc, char **argv) { int vol_idx; BYTE opt; if (argc == 3) { vol_idx = (int)atoi(argv[1]); if (strcmp(argv[2], "fat") == 0) { opt = FM_FAT; } else if (strcmp(argv[2], "fat32") == 0) { opt = FM_FAT32; } else if (strcmp(argv[2], "exfat") == 0) { opt = FM_EXFAT; } else { opt = FM_ANY; } } else { printf("usage: %s \n", argv[0]); return -1; } char volume_str[TEST_FATFS_MAX_VOL_STR_LEN]; sprintf(volume_str, "%d:/", vol_idx); BYTE work[_MAX_SS]; puts("formatting media..."); /* au = 0: use default allocation unit size depending on volume size */ FRESULT mkfs_resu = f_mkfs(volume_str, opt, 0, work, sizeof(work)); if (mkfs_resu == FR_OK) { puts("[OK]"); return 0; } printf("[FAILED] error %d\n", mkfs_resu); return -1; } static const shell_command_t shell_commands[] = { { "mount", "mount file system", _mount }, { "mkfs", "format volume", _mkfs }, { "touch", "create file", _touch }, { "read", "print file content to console", _read }, { "write", "append string to file", _write }, { "ls", "list files", _ls }, { NULL, NULL, NULL } }; int main(void) { #if FATFS_FFCONF_OPT_FS_NORTC == 0 /* the rtc is used in diskio.c for timestamps of files */ puts("Initializing the RTC driver"); rtc_poweron(); rtc_init(); struct tm time; time.tm_year = TEST_FATFS_RTC_YEAR - RTC_YEAR_OFFSET; /* years are counted from 1900 */ time.tm_mon = TEST_FATFS_RTC_MON; /* 0 = January, 11 = December */ time.tm_mday = TEST_FATFS_RTC_DAY; time.tm_hour = TEST_FATFS_RTC_H; time.tm_min = TEST_FATFS_RTC_M; time.tm_sec = TEST_FATFS_RTC_S; printf("Setting RTC to %04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year + RTC_YEAR_OFFSET, time.tm_mon + TEST_FATFS_RTC_MON_OFFSET, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); rtc_set_time(&time); #endif char line_buf[SHELL_DEFAULT_BUFSIZE]; shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); return 0; }