You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
9.8 KiB

/*
* Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
*
* 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 <michel.rottleuthner@haw-hamburg.de>
*
* @}
*/
#ifdef FATFS_RTC_AVAILABLE
#include "periph/rtc.h"
#endif
#include "fatfs/ff.h"
#include "shell.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#define TEST_FATFS_RTC_YEAR_OFFSET 1900
#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 <volume_idx>\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 <filename>\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 <filename> [<len>]\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 <filename> <string>\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 <volume_idx> <fat|fat32|exfat|any>\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)
{
#ifdef FATFS_RTC_AVAILABLE
/* 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 + 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;
}