[FL-520] Filesystem Api and App (#280)

* update fatfs integer types
* fix sector size to 512
* fix sector size calculation
* common fs api
* fs api realization (sd card + fat fs)
* better sector size definition
* more api realization fns
* add error description api, add common api
* fix api flag naming, run app
* add fs_info call
* disable fatfs strfuncs, enable fatfs chmod
* rework filesystem app
* sd detect cycle, sd menu, sd eject feature
* fix sd detect cycle
* sd card format routine
* ui improvements, sd info routine
* properly unmount card
* separate mode flags
* add api folder, move app, rename app
* fix api naming
* update st-card-test to use api
* update path to app
* fixed potential problem of using sizeof union
* updated api documentation, new time/date fns
* update codeowners
* changed app requirements
* changed app order
* sd insert/remove log
This commit is contained in:
DrZlo13 2021-01-12 00:52:35 +10:00 committed by GitHub
parent 6928122650
commit f94633863c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1729 additions and 260 deletions

11
.github/CODEOWNERS vendored
View File

@ -43,6 +43,8 @@ applications/gui/** @skotopes
# iButton # iButton
applications/ibutton/** @DrZlo13 applications/ibutton/** @DrZlo13
lib/cyfral/** @DrZlo13
lib/onewire/** @DrZlo13
# IR # IR
@ -61,9 +63,12 @@ applications/menu/** @skotopes
applications/nfc/** @skotopes applications/nfc/** @skotopes
lib/ST25RFAL002/** @skotopes lib/ST25RFAL002/** @skotopes
# SD Card # SD Card and filesystem
applications/sd-card-test/** @DrZlo13 applications/sd-card-test/** @DrZlo13
applications/sd-filesystem/** @DrZlo13
lib/common-api/filesystem-api.h @DrZlo13
lib/fatfs/** @DrZlo13
# Power control app # Power control app
@ -74,3 +79,7 @@ applications/power/** @skotopes
applications/music-player/** @DrZlo13 applications/music-player/** @DrZlo13
applications/floopper-bloopper/** @glitchcore applications/floopper-bloopper/** @glitchcore
applications/gpio-tester/** @glitchcore applications/gpio-tester/** @glitchcore
lib/app-template/** @DrZlo13
lib/qrcode/** @DrZlo13
lib/callback-connector/** @DrZlo13

View File

@ -41,6 +41,7 @@ void cli_task(void* p);
void music_player(void* p); void music_player(void* p);
void sdnfc(void* p); void sdnfc(void* p);
void floopper_bloopper(void* p); void floopper_bloopper(void* p);
void sd_filesystem(void* p);
const FlipperStartupApp FLIPPER_STARTUP[] = { const FlipperStartupApp FLIPPER_STARTUP[] = {
#ifdef APP_DISPLAY #ifdef APP_DISPLAY
@ -88,6 +89,13 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
.icon = A_Plugins_14}, .icon = A_Plugins_14},
#endif #endif
#ifdef APP_SD_FILESYSTEM
{.app = sd_filesystem,
.name = "sd_filesystem",
.libs = {1, FURI_LIB{"menu_task"}},
.icon = A_Plugins_14},
#endif
#ifdef APP_DOLPHIN #ifdef APP_DOLPHIN
{.app = dolphin_task, {.app = dolphin_task,
.name = "dolphin_task", .name = "dolphin_task",
@ -165,7 +173,7 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
#ifdef APP_SD_TEST #ifdef APP_SD_TEST
{.app = sd_card_test, {.app = sd_card_test,
.name = "sd_card_test", .name = "sd_card_test",
.libs = {1, FURI_LIB{"gui_task"}}, .libs = {2, FURI_LIB{"gui_task", "sd_filesystem"}},
.icon = A_Plugins_14}, .icon = A_Plugins_14},
#endif #endif
@ -257,7 +265,7 @@ const FlipperStartupApp FLIPPER_PLUGINS[] = {
#ifdef BUILD_SD_TEST #ifdef BUILD_SD_TEST
{.app = sd_card_test, {.app = sd_card_test,
.name = "sd_card_test", .name = "sd_card_test",
.libs = {1, FURI_LIB{"gui_task"}}, .libs = {2, FURI_LIB{"gui_task", "sd_filesystem"}},
.icon = A_Plugins_14}, .icon = A_Plugins_14},
#endif #endif

View File

@ -13,6 +13,7 @@ APP_NFC = 1
APP_POWER = 1 APP_POWER = 1
APP_BT = 1 APP_BT = 1
APP_CLI = 1 APP_CLI = 1
APP_SD_FILESYSTEM = 1
BUILD_IRDA = 1 BUILD_IRDA = 1
APP_DOLPHIN = 1 APP_DOLPHIN = 1
BUILD_EXAMPLE_BLINK = 1 BUILD_EXAMPLE_BLINK = 1
@ -220,6 +221,7 @@ CFLAGS += -DBUILD_SD_TEST
CPP_SOURCES += $(wildcard $(APP_DIR)/sd-card-test/*.cpp) CPP_SOURCES += $(wildcard $(APP_DIR)/sd-card-test/*.cpp)
APP_INPUT = 1 APP_INPUT = 1
APP_GUI = 1 APP_GUI = 1
APP_SD_FILESYSTEM = 1
endif endif
APP_SPEAKER_DEMO ?= 0 APP_SPEAKER_DEMO ?= 0
@ -305,6 +307,12 @@ C_SOURCES += $(wildcard $(APP_DIR)/gui/*.c)
C_SOURCES += $(wildcard $(APP_DIR)/backlight-control/*.c) C_SOURCES += $(wildcard $(APP_DIR)/backlight-control/*.c)
endif endif
APP_SD_FILESYSTEM ?= 0
ifeq ($(APP_SD_FILESYSTEM), 1)
CFLAGS += -DAPP_SD_FILESYSTEM
C_SOURCES += $(wildcard $(APP_DIR)/sd-filesystem/*.c)
endif
# deprecated # deprecated
ifeq ($(APP_DISPLAY), 1) ifeq ($(APP_DISPLAY), 1)
CFLAGS += -DAPP_DISPLAY CFLAGS += -DAPP_DISPLAY

View File

@ -1,7 +1,7 @@
#include "app-template.h" #include "app-template.h"
#include "fatfs/ff.h"
#include "stm32_adafruit_sd.h" #include "stm32_adafruit_sd.h"
#include "fnv1a-hash.h" #include "fnv1a-hash.h"
#include "filesystem-api.h"
// event enumeration type // event enumeration type
typedef uint8_t event_t; typedef uint8_t event_t;
@ -43,17 +43,15 @@ public:
// vars // vars
GpioPin* red_led_record; GpioPin* red_led_record;
GpioPin* green_led_record; GpioPin* green_led_record;
FATFS sd_fat_fs;
char sd_path[6];
const uint32_t benchmark_data_size = 4096; const uint32_t benchmark_data_size = 4096;
uint8_t* benchmark_data; uint8_t* benchmark_data;
FS_Api* fs_api;
// funcs // funcs
void run(); void run();
void render(Canvas* canvas); void render(Canvas* canvas);
template <class T> void set_text(std::initializer_list<T> list); template <class T> void set_text(std::initializer_list<T> list);
template <class T> void set_error(std::initializer_list<T> list); template <class T> void set_error(std::initializer_list<T> list);
const char* fatfs_error_desc(FRESULT res);
void wait_for_button(Input input_button); void wait_for_button(Input input_button);
bool ask(Input input_button_cancel, Input input_button_ok); bool ask(Input input_button_cancel, Input input_button_ok);
void blink_red(); void blink_red();
@ -63,11 +61,6 @@ public:
// "tests" // "tests"
void detect_sd_card(); void detect_sd_card();
void show_warning(); void show_warning();
void init_sd_card();
bool is_sd_card_formatted();
void ask_and_format_sd_card();
void mount_sd_card();
void format_sd_card();
void get_sd_card_info(); void get_sd_card_info();
void prepare_benchmark_data(); void prepare_benchmark_data();
@ -76,7 +69,7 @@ public:
uint32_t write_benchmark_internal(const uint32_t size, const uint32_t tcount); uint32_t write_benchmark_internal(const uint32_t size, const uint32_t tcount);
void read_benchmark(); void read_benchmark();
uint32_t read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file); uint32_t read_benchmark_internal(const uint32_t size, const uint32_t count, File* file);
void hash_benchmark(); void hash_benchmark();
}; };
@ -97,16 +90,17 @@ void SdTest::run() {
app_ready(); app_ready();
detect_sd_card(); fs_api = static_cast<FS_Api*>(furi_open("sdcard"));
show_warning();
init_sd_card(); if(fs_api == NULL) {
if(!is_sd_card_formatted()) { set_error({"cannot get sdcard api"});
format_sd_card(); exit();
} else {
ask_and_format_sd_card();
} }
mount_sd_card();
detect_sd_card();
get_sd_card_info(); get_sd_card_info();
show_warning();
prepare_benchmark_data(); prepare_benchmark_data();
write_benchmark(); write_benchmark();
read_benchmark(); read_benchmark();
@ -134,7 +128,7 @@ void SdTest::detect_sd_card() {
uint8_t i = 0; uint8_t i = 0;
// detect sd card pin // detect sd card pin
while(!hal_gpio_read_sd_detect()) { while(fs_api->common.get_fs_info(NULL, NULL) == FSE_NOT_READY) {
delay(100); delay(100);
snprintf(str_buffer, str_buffer_size, "Waiting%s", dots[i]); snprintf(str_buffer, str_buffer_size, "Waiting%s", dots[i]);
@ -155,7 +149,7 @@ void SdTest::show_warning() {
set_text( set_text(
{"!!Warning!!", {"!!Warning!!",
"during the tests", "during the tests",
"card may be formatted", "files can be overwritten",
"or data on card may be lost", "or data on card may be lost",
"", "",
"press UP DOWN OK to continue"}); "press UP DOWN OK to continue"});
@ -165,96 +159,22 @@ void SdTest::show_warning() {
wait_for_button(InputOk); wait_for_button(InputOk);
} }
// init low level driver
void SdTest::init_sd_card() {
uint8_t bsp_result = BSP_SD_Init();
// BSP_SD_OK = 0
if(bsp_result) {
set_error({"SD card init error", "BSP error"});
}
blink_green();
}
// test, if sd card need to be formatted
bool SdTest::is_sd_card_formatted() {
FRESULT result;
set_text({"checking if card needs to be formatted"});
result = f_mount(&sd_fat_fs, sd_path, 1);
if(result == FR_NO_FILESYSTEM) {
return false;
} else {
return true;
}
}
void SdTest::ask_and_format_sd_card() {
set_text({"Want to format sd card?", "", "", "", "", "LEFT to CANCEL | RIGHT to OK"});
if(ask(InputLeft, InputRight)) {
format_sd_card();
}
}
// mount sd card
void SdTest::mount_sd_card() {
FRESULT result;
set_text({"mounting sdcard"});
result = f_mount(&sd_fat_fs, sd_path, 1);
if(result) {
set_error({"SD card mount error", fatfs_error_desc(result)});
}
blink_green();
}
// format sd card
void SdTest::format_sd_card() {
FRESULT result;
BYTE* work_area;
set_text({"formatting sdcard", "procedure can be lengthy", "please wait"});
delay(100);
work_area = static_cast<BYTE*>(malloc(_MAX_SS));
if(work_area == NULL) {
set_error({"SD card format error", "cannot allocate memory"});
}
result = f_mkfs(sd_path, (FM_FAT | FM_FAT32 | FM_EXFAT), 0, work_area, _MAX_SS);
free(work_area);
if(result) {
set_error({"SD card format error", fatfs_error_desc(result)});
}
result = f_setlabel("Flipper SD");
if(result) {
set_error({"SD card set label error", fatfs_error_desc(result)});
}
blink_green();
}
// get info about sd card, label, sn // get info about sd card, label, sn
// sector, cluster, total and free size // sector, cluster, total and free size
void SdTest::get_sd_card_info() { void SdTest::get_sd_card_info() {
const uint8_t str_buffer_size = 26; const uint8_t str_buffer_size = 26;
char str_buffer[4][str_buffer_size]; char str_buffer[2][str_buffer_size];
char volume_label[128]; FS_Error result;
DWORD serial_num; uint64_t bytes_total, bytes_free;
FRESULT result;
FATFS* fs;
DWORD free_clusters, free_sectors, total_sectors;
// suppress "'%s' directive output may be truncated" warning about snprintf
int __attribute__((unused)) snprintf_count = 0; int __attribute__((unused)) snprintf_count = 0;
// get label and s/n result = fs_api->common.get_fs_info(&bytes_total, &bytes_free);
result = f_getlabel(sd_path, volume_label, &serial_num); if(result != FSE_OK) set_error({"get_fs_info error", fs_api->error.get_desc(result)});
if(result) set_error({"f_getlabel error", fatfs_error_desc(result)});
snprintf_count = snprintf(str_buffer[0], str_buffer_size, "Label: %s", volume_label); snprintf(
snprintf(str_buffer[1], str_buffer_size, "S/N: %lu", serial_num); str_buffer[0], str_buffer_size, "%lu KB total", static_cast<uint32_t>(bytes_total / 1024));
snprintf(
str_buffer[1], str_buffer_size, "%lu KB free", static_cast<uint32_t>(bytes_free / 1024));
set_text( set_text(
{static_cast<const char*>(str_buffer[0]), {static_cast<const char*>(str_buffer[0]),
@ -267,30 +187,6 @@ void SdTest::get_sd_card_info() {
blink_green(); blink_green();
wait_for_button(InputOk); wait_for_button(InputOk);
// get total and free space
result = f_getfree(sd_path, &free_clusters, &fs);
if(result) set_error({"f_getfree error", fatfs_error_desc(result)});
total_sectors = (fs->n_fatent - 2) * fs->csize;
free_sectors = free_clusters * fs->csize;
snprintf(str_buffer[0], str_buffer_size, "Cluster: %d sectors", fs->csize);
snprintf(str_buffer[1], str_buffer_size, "Sector: %d bytes", fs->ssize);
snprintf(str_buffer[2], str_buffer_size, "%lu KB total", total_sectors / 1024 * fs->ssize);
snprintf(str_buffer[3], str_buffer_size, "%lu KB free", free_sectors / 1024 * fs->ssize);
set_text(
{static_cast<const char*>(str_buffer[0]),
static_cast<const char*>(str_buffer[1]),
static_cast<const char*>(str_buffer[2]),
static_cast<const char*>(str_buffer[3]),
"",
"press OK to continue"});
blink_green();
wait_for_button(InputOk);
} }
// prepare benchmark data (allocate data in ram) // prepare benchmark data (allocate data in ram)
@ -375,30 +271,27 @@ void SdTest::write_benchmark() {
uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count) { uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count) {
uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_written; uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_written;
FRESULT result; File file;
FIL file;
const uint8_t str_buffer_size = 32; const uint8_t str_buffer_size = 32;
char str_buffer[str_buffer_size]; char str_buffer[str_buffer_size];
result = f_open(&file, "write.test", FA_WRITE | FA_OPEN_ALWAYS); if(!fs_api->file.open(&file, "write.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
if(result) {
snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
set_error({"cannot open file ", static_cast<const char*>(str_buffer)}); set_error({"cannot open file ", static_cast<const char*>(str_buffer)});
} }
start_tick = osKernelGetTickCount(); start_tick = osKernelGetTickCount();
for(size_t i = 0; i < count; i++) { for(size_t i = 0; i < count; i++) {
result = f_write(&file, benchmark_data, size, reinterpret_cast<UINT*>(&bytes_written)); bytes_written = fs_api->file.write(&file, benchmark_data, size);
if(bytes_written != size || result) { if(bytes_written != size || file.error_id != FSE_OK) {
snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
set_error({"cannot write to file ", static_cast<const char*>(str_buffer)}); set_error({"cannot write to file ", static_cast<const char*>(str_buffer)});
} }
} }
stop_tick = osKernelGetTickCount(); stop_tick = osKernelGetTickCount();
result = f_close(&file); if(!fs_api->file.close(&file)) {
if(result) {
snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
set_error({"cannot close file ", static_cast<const char*>(str_buffer)}); set_error({"cannot close file ", static_cast<const char*>(str_buffer)});
} }
@ -425,8 +318,7 @@ void SdTest::read_benchmark() {
static_cast<const char*>(str_buffer[4]), static_cast<const char*>(str_buffer[4]),
static_cast<const char*>(str_buffer[5])}; static_cast<const char*>(str_buffer[5])};
FRESULT result; File file;
FIL file;
const uint32_t b1_size = 1; const uint32_t b1_size = 1;
const uint32_t b8_size = 8; const uint32_t b8_size = 8;
@ -438,21 +330,18 @@ void SdTest::read_benchmark() {
set_text({"prepare data", "for read speed test", "procedure can be lengthy", "please wait"}); set_text({"prepare data", "for read speed test", "procedure can be lengthy", "please wait"});
delay(100); delay(100);
result = f_open(&file, "read.test", FA_WRITE | FA_OPEN_ALWAYS); if(!fs_api->file.open(&file, "read.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
if(result) {
set_error({"cannot open file ", "in prepare read"}); set_error({"cannot open file ", "in prepare read"});
} }
for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) { for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) {
result = bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size);
f_write(&file, benchmark_data, b4096_size, reinterpret_cast<UINT*>(&bytes_written)); if(bytes_written != b4096_size || file.error_id != FSE_OK) {
if(bytes_written != b4096_size || result) {
set_error({"cannot write to file ", "in prepare read"}); set_error({"cannot write to file ", "in prepare read"});
} }
} }
result = f_close(&file); if(!fs_api->file.close(&file)) {
if(result) {
set_error({"cannot close file ", "in prepare read"}); set_error({"cannot close file ", "in prepare read"});
} }
@ -461,8 +350,7 @@ void SdTest::read_benchmark() {
delay(100); delay(100);
// open file // open file
result = f_open(&file, "read.test", FA_READ | FA_OPEN_EXISTING); if(!fs_api->file.open(&file, "read.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
if(result) {
set_error({"cannot open file ", "in read benchmark"}); set_error({"cannot open file ", "in read benchmark"});
} }
@ -497,8 +385,7 @@ void SdTest::read_benchmark() {
set_text(string_list); set_text(string_list);
// close file // close file
result = f_close(&file); if(!fs_api->file.close(&file)) {
if(result) {
set_error({"cannot close file ", "in read test"}); set_error({"cannot close file ", "in read test"});
} }
@ -507,9 +394,9 @@ void SdTest::read_benchmark() {
wait_for_button(InputOk); wait_for_button(InputOk);
} }
uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file) { uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t count, File* file) {
uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_readed; uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_readed;
FRESULT result; //FRESULT result;
const uint8_t str_buffer_size = 32; const uint8_t str_buffer_size = 32;
char str_buffer[str_buffer_size]; char str_buffer[str_buffer_size];
@ -522,12 +409,12 @@ uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t cou
set_error({"cannot allocate memory", static_cast<const char*>(str_buffer)}); set_error({"cannot allocate memory", static_cast<const char*>(str_buffer)});
} }
f_rewind(file); fs_api->file.seek(file, 0, true);
start_tick = osKernelGetTickCount(); start_tick = osKernelGetTickCount();
for(size_t i = 0; i < count; i++) { for(size_t i = 0; i < count; i++) {
result = f_read(file, read_buffer, size, reinterpret_cast<UINT*>(&bytes_readed)); bytes_readed = fs_api->file.read(file, read_buffer, size);
if(bytes_readed != size || result) { if(bytes_readed != size || file->error_id != FSE_OK) {
snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size); snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size);
set_error({"cannot read from file ", static_cast<const char*>(str_buffer)}); set_error({"cannot read from file ", static_cast<const char*>(str_buffer)});
} }
@ -555,8 +442,7 @@ void SdTest::hash_benchmark() {
const uint8_t str_buffer_size = 32; const uint8_t str_buffer_size = 32;
char str_buffer[3][str_buffer_size] = {"", "", ""}; char str_buffer[3][str_buffer_size] = {"", "", ""};
FRESULT result; File file;
FIL file;
const uint32_t b4096_size = 4096; const uint32_t b4096_size = 4096;
const uint32_t benchmark_count = 20; const uint32_t benchmark_count = 20;
@ -566,17 +452,15 @@ void SdTest::hash_benchmark() {
delay(100); delay(100);
// write data to test file and calculate hash // write data to test file and calculate hash
result = f_open(&file, "hash.test", FA_WRITE | FA_OPEN_ALWAYS); if(!fs_api->file.open(&file, "hash.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
if(result) {
set_error({"cannot open file ", "in prepare hash"}); set_error({"cannot open file ", "in prepare hash"});
} }
for(uint32_t i = 0; i < benchmark_count; i++) { for(uint32_t i = 0; i < benchmark_count; i++) {
mcu_data_hash = fnv1a_buffer_hash(benchmark_data, b4096_size, mcu_data_hash); mcu_data_hash = fnv1a_buffer_hash(benchmark_data, b4096_size, mcu_data_hash);
result = bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size);
f_write(&file, benchmark_data, b4096_size, reinterpret_cast<UINT*>(&bytes_written));
if(bytes_written != b4096_size || result) { if(bytes_written != b4096_size || file.error_id != FSE_OK) {
set_error({"cannot write to file ", "in prepare hash"}); set_error({"cannot write to file ", "in prepare hash"});
} }
@ -585,8 +469,7 @@ void SdTest::hash_benchmark() {
delay(100); delay(100);
} }
result = f_close(&file); if(!fs_api->file.close(&file)) {
if(result) {
set_error({"cannot close file ", "in prepare hash"}); set_error({"cannot close file ", "in prepare hash"});
} }
@ -602,16 +485,15 @@ void SdTest::hash_benchmark() {
set_error({"cannot allocate memory", "in hash test"}); set_error({"cannot allocate memory", "in hash test"});
} }
result = f_open(&file, "hash.test", FA_READ | FA_OPEN_EXISTING); if(!fs_api->file.open(&file, "hash.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
if(result) {
set_error({"cannot open file ", "in hash test"}); set_error({"cannot open file ", "in hash test"});
} }
for(uint32_t i = 0; i < benchmark_count; i++) { for(uint32_t i = 0; i < benchmark_count; i++) {
result = f_read(&file, read_buffer, b4096_size, reinterpret_cast<UINT*>(&bytes_readed)); bytes_readed = fs_api->file.read(&file, read_buffer, b4096_size);
sdcard_data_hash = fnv1a_buffer_hash(read_buffer, b4096_size, sdcard_data_hash); sdcard_data_hash = fnv1a_buffer_hash(read_buffer, b4096_size, sdcard_data_hash);
if(bytes_readed != b4096_size || result) { if(bytes_readed != b4096_size || file.error_id != FSE_OK) {
set_error({"cannot read from file ", "in hash test"}); set_error({"cannot read from file ", "in hash test"});
} }
@ -620,9 +502,7 @@ void SdTest::hash_benchmark() {
delay(100); delay(100);
} }
result = f_close(&file); if(!fs_api->file.close(&file)) {
if(result) {
set_error({"cannot close file ", "in hash test"}); set_error({"cannot close file ", "in hash test"});
} }
@ -730,76 +610,6 @@ void SdTest::blink_green() {
gpio_write(green_led_record, 1); gpio_write(green_led_record, 1);
} }
// FatFs errors descriptions
const char* SdTest::fatfs_error_desc(FRESULT res) {
switch(res) {
case FR_OK:
return "ok";
break;
case FR_DISK_ERR:
return "low level error";
break;
case FR_INT_ERR:
return "internal error";
break;
case FR_NOT_READY:
return "not ready";
break;
case FR_NO_FILE:
return "no file";
break;
case FR_NO_PATH:
return "no path";
break;
case FR_INVALID_NAME:
return "invalid name";
break;
case FR_DENIED:
return "denied";
break;
case FR_EXIST:
return "already exist";
break;
case FR_INVALID_OBJECT:
return "invalid file/dir obj";
break;
case FR_WRITE_PROTECTED:
return "write protected";
break;
case FR_INVALID_DRIVE:
return "invalid drive";
break;
case FR_NOT_ENABLED:
return "no work area in volume";
break;
case FR_NO_FILESYSTEM:
return "no valid FS volume";
break;
case FR_MKFS_ABORTED:
return "aborted, any problem";
break;
case FR_TIMEOUT:
return "timeout";
break;
case FR_LOCKED:
return "file locked";
break;
case FR_NOT_ENOUGH_CORE:
return "not enough core memory";
break;
case FR_TOO_MANY_OPEN_FILES:
return "too many open files";
break;
case FR_INVALID_PARAMETER:
return "invalid parameter";
break;
default:
return "unknown error";
break;
}
}
// set text, but with infinite loop // set text, but with infinite loop
template <class T> void SdTest::set_error(std::initializer_list<T> list) { template <class T> void SdTest::set_error(std::initializer_list<T> list) {
set_text(list); set_text(list);

View File

@ -0,0 +1,738 @@
#include "fatfs.h"
#include "filesystem-api.h"
#include "sd-filesystem.h"
/******************* Global vars for api *******************/
static SdFsInfo* fs_info;
/******************* Core Functions *******************/
bool _fs_init(SdFsInfo* _fs_info) {
bool result = true;
_fs_info->mutex = osMutexNew(NULL);
if(_fs_info->mutex == NULL) result = false;
for(uint8_t i = 0; i < SD_FS_MAX_FILES; i++) {
_fs_info->files[i].thread_id = NULL;
}
_fs_info->path = "0:/";
_fs_info->status = SD_NO_CARD;
// store pointer for api fns
fs_info = _fs_info;
return result;
}
bool _fs_lock(SdFsInfo* fs_info) {
return (osMutexAcquire(fs_info->mutex, osWaitForever) == osOK);
}
bool _fs_unlock(SdFsInfo* fs_info) {
return (osMutexRelease(fs_info->mutex) == osOK);
}
SDError _get_filedata(SdFsInfo* fs_info, File* file, FileData** filedata, FiledataFilter filter) {
SDError error = SD_OK;
_fs_lock(fs_info);
if(fs_info->status == SD_OK) {
if(file != NULL && file->file_id < SD_FS_MAX_FILES) {
if(fs_info->files[file->file_id].thread_id == osThreadGetId()) {
if(filter == FDF_ANY) {
// any type
*filedata = &fs_info->files[file->file_id];
} else if(filter == FDF_FILE) {
// file type
if(!fs_info->files[file->file_id].is_dir) {
*filedata = &fs_info->files[file->file_id];
} else {
error = SD_NOT_A_FILE;
}
} else if(filter == FDF_DIR) {
// dir type
if(fs_info->files[file->file_id].is_dir) {
*filedata = &fs_info->files[file->file_id];
} else {
error = SD_NOT_A_DIR;
}
}
} else {
error = SD_OTHER_APP;
}
} else {
error = SD_INVALID_PARAMETER;
}
} else {
error = SD_NO_CARD;
}
_fs_unlock(fs_info);
return error;
}
SDError _get_file(SdFsInfo* fs_info, File* file, FileData** filedata) {
return _get_filedata(fs_info, file, filedata, FDF_FILE);
}
SDError _get_dir(SdFsInfo* fs_info, File* file, FileData** filedata) {
return _get_filedata(fs_info, file, filedata, FDF_DIR);
}
SDError _get_any(SdFsInfo* fs_info, File* file, FileData** filedata) {
return _get_filedata(fs_info, file, filedata, FDF_ANY);
}
SDError _fs_status(SdFsInfo* fs_info) {
SDError result;
_fs_lock(fs_info);
result = fs_info->status;
_fs_unlock(fs_info);
return result;
}
void _fs_on_client_app_exit(SdFsInfo* fs_info) {
_fs_lock(fs_info);
for(uint8_t i = 0; i < SD_FS_MAX_FILES; i++) {
if(fs_info->files[i].thread_id == osThreadGetId()) {
if(fs_info->files[i].is_dir) {
// TODO close dir
} else {
// TODO close file
}
}
}
_fs_unlock(fs_info);
}
FS_Error _fs_parse_error(SDError error) {
FS_Error result;
switch(error) {
case SD_OK:
result = FSE_OK;
break;
case SD_INT_ERR:
result = FSE_INTERNAL;
break;
case SD_NO_FILE:
result = FSE_NOT_EXIST;
break;
case SD_NO_PATH:
result = FSE_NOT_EXIST;
break;
case SD_INVALID_NAME:
result = FSE_INVALID_NAME;
break;
case SD_DENIED:
result = FSE_DENIED;
break;
case SD_EXIST:
result = FSE_EXIST;
break;
case SD_INVALID_OBJECT:
result = FSE_INTERNAL;
break;
case SD_WRITE_PROTECTED:
result = FSE_INTERNAL;
break;
case SD_INVALID_DRIVE:
result = FSE_INTERNAL;
break;
case SD_NOT_ENABLED:
result = FSE_INTERNAL;
break;
case SD_NO_FILESYSTEM:
result = FSE_NOT_READY;
break;
case SD_MKFS_ABORTED:
result = FSE_INTERNAL;
break;
case SD_TIMEOUT:
result = FSE_INTERNAL;
break;
case SD_LOCKED:
result = FSE_INTERNAL;
break;
case SD_NOT_ENOUGH_CORE:
result = FSE_INTERNAL;
break;
case SD_TOO_MANY_OPEN_FILES:
result = FSE_INTERNAL;
break;
case SD_INVALID_PARAMETER:
result = FSE_INVALID_PARAMETER;
break;
case SD_NO_CARD:
result = FSE_NOT_READY;
break;
case SD_NOT_A_FILE:
result = FSE_INVALID_PARAMETER;
break;
case SD_NOT_A_DIR:
result = FSE_INVALID_PARAMETER;
break;
case SD_OTHER_APP:
result = FSE_INTERNAL;
break;
default:
result = FSE_INTERNAL;
break;
}
return result;
}
/******************* File Functions *******************/
// Open/Create a file
bool fs_file_open(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode) {
SDFile* sd_file = NULL;
_fs_lock(fs_info);
for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) {
FileData* filedata = &fs_info->files[index];
if(filedata->thread_id == NULL) {
file->file_id = index;
memset(&(filedata->data), 0, sizeof(SDFileDirStorage));
filedata->thread_id = osThreadGetId();
filedata->is_dir = false;
sd_file = &(filedata->data.file);
break;
}
}
_fs_unlock(fs_info);
if(sd_file == NULL) {
file->internal_error_id = SD_TOO_MANY_OPEN_FILES;
} else {
uint8_t _mode = 0;
if(access_mode & FSAM_READ) _mode |= FA_READ;
if(access_mode & FSAM_WRITE) _mode |= FA_WRITE;
if(open_mode & FSOM_OPEN_EXISTING) _mode |= FA_OPEN_EXISTING;
if(open_mode & FSOM_OPEN_ALWAYS) _mode |= FA_OPEN_ALWAYS;
if(open_mode & FSOM_OPEN_APPEND) _mode |= FA_OPEN_APPEND;
if(open_mode & FSOM_CREATE_NEW) _mode |= FA_CREATE_NEW;
if(open_mode & FSOM_CREATE_ALWAYS) _mode |= FA_CREATE_ALWAYS;
file->internal_error_id = f_open(sd_file, path, _mode);
}
// TODO on exit
//furiac_onexit(_fs_on_client_app_exit, fs_info);
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Close an opened file
bool fs_file_close(File* file) {
FileData* filedata = NULL;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_close(&filedata->data.file);
_fs_lock(fs_info);
filedata->thread_id = NULL;
_fs_unlock(fs_info);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Read data from the file
uint16_t fs_file_read(File* file, void* buff, uint16_t const bytes_to_read) {
FileData* filedata = NULL;
uint16_t bytes_readed = 0;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_read(&filedata->data.file, buff, bytes_to_read, &bytes_readed);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return bytes_readed;
}
// Write data to the file
uint16_t fs_file_write(File* file, void* buff, uint16_t const bytes_to_write) {
FileData* filedata = NULL;
uint16_t bytes_written = 0;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id =
f_write(&filedata->data.file, buff, bytes_to_write, &bytes_written);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return bytes_written;
}
// Move read/write pointer, expand size
bool fs_file_seek(File* file, const uint32_t offset, const bool from_start) {
FileData* filedata = NULL;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
if(from_start) {
file->internal_error_id = f_lseek(&filedata->data.file, offset);
} else {
uint64_t position = f_tell(&filedata->data.file);
position += offset;
file->internal_error_id = f_lseek(&filedata->data.file, position);
}
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Tell pointer position
uint64_t fs_file_tell(File* file) {
FileData* filedata = NULL;
uint64_t position = 0;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
position = f_tell(&filedata->data.file);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return position;
}
// Truncate file size to current pointer value
bool fs_file_truncate(File* file) {
FileData* filedata = NULL;
uint64_t position = 0;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_truncate(&filedata->data.file);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Flush cached data
bool fs_file_sync(File* file) {
FileData* filedata = NULL;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_sync(&filedata->data.file);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Get size
uint64_t fs_file_size(File* file) {
FileData* filedata = NULL;
uint64_t size = 0;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
size = f_size(&filedata->data.file);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return size;
}
// Test EOF
bool fs_file_eof(File* file) {
FileData* filedata = NULL;
bool eof = true;
file->internal_error_id = _get_file(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
eof = f_eof(&filedata->data.file);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return eof;
}
/******************* Dir Functions *******************/
// Open directory
bool fs_dir_open(File* file, const char* path) {
SDDir* sd_dir = NULL;
_fs_lock(fs_info);
for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) {
FileData* filedata = &fs_info->files[index];
if(filedata->thread_id == NULL) {
file->file_id = index;
memset(&(filedata->data), 0, sizeof(SDFileDirStorage));
filedata->thread_id = osThreadGetId();
filedata->is_dir = true;
sd_dir = &(filedata->data.dir);
break;
}
}
_fs_unlock(fs_info);
if(sd_dir == NULL) {
file->internal_error_id = SD_TOO_MANY_OPEN_FILES;
} else {
if(file->internal_error_id == SD_OK) file->internal_error_id = f_opendir(sd_dir, path);
}
// TODO on exit
//furiac_onexit(_fs_on_client_app_exit, fs_info);
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Close directory
bool fs_dir_close(File* file) {
FileData* filedata = NULL;
file->internal_error_id = _get_dir(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_closedir(&filedata->data.dir);
_fs_lock(fs_info);
filedata->thread_id = NULL;
_fs_unlock(fs_info);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
// Read next file info and name from directory
bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, const uint16_t name_length) {
FileData* filedata = NULL;
file->internal_error_id = _get_dir(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
SDFileInfo _fileinfo;
file->internal_error_id = f_readdir(&filedata->data.dir, &_fileinfo);
if(fileinfo != NULL) {
fileinfo->date.value = _fileinfo.fdate;
fileinfo->time.value = _fileinfo.ftime;
fileinfo->size = _fileinfo.fsize;
fileinfo->flags = 0;
if(_fileinfo.fattrib & AM_RDO) fileinfo->flags |= FSF_READ_ONLY;
if(_fileinfo.fattrib & AM_HID) fileinfo->flags |= FSF_HIDDEN;
if(_fileinfo.fattrib & AM_SYS) fileinfo->flags |= FSF_SYSTEM;
if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;
if(_fileinfo.fattrib & AM_ARC) fileinfo->flags |= FSF_ARCHIVE;
}
if(name != NULL && name_length > 0) {
strncpy(name, _fileinfo.fname, name_length);
}
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
bool fs_dir_rewind(File* file) {
FileData* filedata = NULL;
file->internal_error_id = _get_dir(fs_info, file, &filedata);
if(file->internal_error_id == SD_OK) {
file->internal_error_id = f_readdir(&filedata->data.dir, NULL);
}
file->error_id = _fs_parse_error(file->internal_error_id);
return (file->internal_error_id == SD_OK);
}
/******************* Common FS Functions *******************/
// Get info about file/dir
FS_Error
fs_common_info(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length) {
SDFileInfo _fileinfo;
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
fresult = f_stat(path, &_fileinfo);
if(fresult == FR_OK) {
if(fileinfo != NULL) {
fileinfo->date.value = _fileinfo.fdate;
fileinfo->time.value = _fileinfo.ftime;
fileinfo->size = _fileinfo.fsize;
fileinfo->flags = 0;
if(_fileinfo.fattrib & AM_RDO) fileinfo->flags |= FSF_READ_ONLY;
if(_fileinfo.fattrib & AM_HID) fileinfo->flags |= FSF_HIDDEN;
if(_fileinfo.fattrib & AM_SYS) fileinfo->flags |= FSF_SYSTEM;
if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;
if(_fileinfo.fattrib & AM_ARC) fileinfo->flags |= FSF_ARCHIVE;
}
if(name != NULL && name_length > 0) {
strncpy(name, _fileinfo.fname, name_length);
}
}
}
return _fs_parse_error(fresult);
}
// Delete file/dir
// File/dir must not have read-only attribute.
// File/dir must be empty.
// File/dir must not be opened, or the FAT volume can be collapsed. FF_FS_LOCK fix that.
FS_Error fs_common_remove(const char* path) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
fresult = f_unlink(path);
}
return _fs_parse_error(fresult);
}
// Rename file/dir
// File/dir must not be opened, or the FAT volume can be collapsed. FF_FS_LOCK fix that.
FS_Error fs_common_rename(const char* old_path, const char* new_path) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
fresult = f_rename(old_path, new_path);
}
return _fs_parse_error(fresult);
}
// Set attributes of file/dir
// For example:
// set "read only" flag and remove "hidden" flag
// fs_common_set_attr("file.txt", FSF_READ_ONLY, FSF_READ_ONLY | FSF_HIDDEN);
FS_Error fs_common_set_attr(const char* path, uint8_t attr, uint8_t mask) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
uint8_t _mask = 0;
uint8_t _attr = 0;
if(mask & FSF_READ_ONLY) _mask |= AM_RDO;
if(mask & FSF_HIDDEN) _mask |= AM_HID;
if(mask & FSF_SYSTEM) _mask |= AM_SYS;
if(mask & FSF_DIRECTORY) _mask |= AM_DIR;
if(mask & FSF_ARCHIVE) _mask |= AM_ARC;
if(attr & FSF_READ_ONLY) _attr |= AM_RDO;
if(attr & FSF_HIDDEN) _attr |= AM_HID;
if(attr & FSF_SYSTEM) _attr |= AM_SYS;
if(attr & FSF_DIRECTORY) _attr |= AM_DIR;
if(attr & FSF_ARCHIVE) _attr |= AM_ARC;
fresult = f_chmod(path, attr, mask);
}
return _fs_parse_error(fresult);
}
// Set time of file/dir
FS_Error fs_common_set_time(const char* path, FileDateUnion date, FileTimeUnion time) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
SDFileInfo _fileinfo;
_fileinfo.fdate = date.value;
_fileinfo.ftime = time.value;
fresult = f_utime(path, &_fileinfo);
}
return _fs_parse_error(fresult);
}
// Create new directory
FS_Error fs_common_mkdir(const char* path) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
fresult = f_mkdir(path);
}
return _fs_parse_error(fresult);
}
// Get common info about FS
FS_Error fs_get_fs_info(uint64_t* total_space, uint64_t* free_space) {
SDError fresult = _fs_status(fs_info);
if(fresult == SD_OK) {
DWORD free_clusters;
FATFS* fs;
fresult = f_getfree("0:/", &free_clusters, &fs);
if(fresult == FR_OK) {
uint32_t total_sectors = (fs->n_fatent - 2) * fs->csize;
uint32_t free_sectors = free_clusters * fs->csize;
uint16_t sector_size = _MAX_SS;
#if _MAX_SS != _MIN_SS
sector_size = fs->ssize;
#endif
if(total_space != NULL) {
*total_space = (uint64_t)total_sectors * (uint64_t)sector_size;
}
if(free_space != NULL) {
*free_space = (uint64_t)free_sectors * (uint64_t)sector_size;
}
}
}
return _fs_parse_error(fresult);
}
/******************* Error Reporting Functions *******************/
// Get common error description
const char* fs_error_get_desc(FS_Error error_id) {
const char* result;
switch(error_id) {
case(FSE_OK):
result = "OK";
break;
case(FSE_NOT_READY):
result = "filesystem not ready";
break;
case(FSE_EXIST):
result = "file/dir already exist";
break;
case(FSE_NOT_EXIST):
result = "file/dir not exist";
break;
case(FSE_INVALID_PARAMETER):
result = "invalid parameter";
break;
case(FSE_DENIED):
result = "access denied";
break;
case(FSE_INVALID_NAME):
result = "invalid name/path";
break;
case(FSE_INTERNAL):
result = "internal error";
break;
case(FSE_NOT_IMPLEMENTED):
result = "function not implemented";
break;
default:
result = "unknown error";
break;
}
return result;
}
// Get internal error description
const char* fs_error_get_internal_desc(uint32_t internal_error_id) {
const char* result;
switch(internal_error_id) {
case(SD_OK):
result = "OK";
break;
case(SD_DISK_ERR):
result = "disk error";
break;
case(SD_INT_ERR):
result = "internal error";
break;
case(SD_NO_FILE):
result = "no file";
break;
case(SD_NO_PATH):
result = "no path";
break;
case(SD_INVALID_NAME):
result = "invalid name";
break;
case(SD_DENIED):
result = "access denied";
break;
case(SD_EXIST):
result = "file/dir exist";
break;
case(SD_INVALID_OBJECT):
result = "invalid object";
break;
case(SD_WRITE_PROTECTED):
result = "write protected";
break;
case(SD_INVALID_DRIVE):
result = "invalid drive";
break;
case(SD_NOT_ENABLED):
result = "not enabled";
break;
case(SD_NO_FILESYSTEM):
result = "no filesystem";
break;
case(SD_MKFS_ABORTED):
result = "aborted";
break;
case(SD_TIMEOUT):
result = "timeout";
break;
case(SD_LOCKED):
result = "file locked";
break;
case(SD_NOT_ENOUGH_CORE):
result = "not enough memory";
break;
case(SD_TOO_MANY_OPEN_FILES):
result = "too many open files";
break;
case(SD_INVALID_PARAMETER):
result = "invalid parameter";
break;
case(SD_NO_CARD):
result = "no SD Card";
break;
case(SD_NOT_A_FILE):
result = "not a file";
break;
case(SD_NOT_A_DIR):
result = "not a directory";
break;
case(SD_OTHER_APP):
result = "opened by other app";
break;
case(SD_LOW_LEVEL_ERR):
result = "low level error";
break;
default:
result = "unknown error";
break;
}
return result;
}

View File

@ -0,0 +1,433 @@
#include "fatfs.h"
#include "filesystem-api.h"
#include "sd-filesystem.h"
#include "menu/menu.h"
#include "menu/menu_item.h"
FS_Api* fs_api_alloc() {
FS_Api* fs_api = furi_alloc(sizeof(FS_Api));
// fill file api
fs_api->file.open = fs_file_open;
fs_api->file.close = fs_file_close;
fs_api->file.read = fs_file_read;
fs_api->file.write = fs_file_write;
fs_api->file.seek = fs_file_seek;
fs_api->file.tell = fs_file_tell;
fs_api->file.truncate = fs_file_truncate;
fs_api->file.size = fs_file_size;
fs_api->file.sync = fs_file_sync;
fs_api->file.eof = fs_file_eof;
// fill dir api
fs_api->dir.open = fs_dir_open;
fs_api->dir.close = fs_dir_close;
fs_api->dir.read = fs_dir_read;
fs_api->dir.rewind = fs_dir_rewind;
// fill common api
fs_api->common.info = fs_common_info;
fs_api->common.remove = fs_common_remove;
fs_api->common.rename = fs_common_rename;
fs_api->common.set_attr = fs_common_set_attr;
fs_api->common.mkdir = fs_common_mkdir;
fs_api->common.set_time = fs_common_set_time;
fs_api->common.get_fs_info = fs_get_fs_info;
// fill errors api
fs_api->error.get_desc = fs_error_get_desc;
fs_api->error.get_internal_desc = fs_error_get_internal_desc;
return fs_api;
}
void sd_set_lines(SdApp* sd_app, uint8_t count, ...) {
va_list argptr;
count = min(count, SD_STATE_LINES_COUNT);
for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) {
sd_app->line[i] = "";
}
va_start(argptr, count);
for(uint8_t i = 0; i < count; i++) {
sd_app->line[i] = va_arg(argptr, char*);
}
va_end(argptr);
}
void sd_icon_draw_callback(Canvas* canvas, void* context) {
furi_assert(canvas);
furi_assert(context);
SdApp* sd_app = context;
switch(sd_app->info.status) {
case SD_NO_CARD:
break;
case SD_OK:
canvas_draw_icon(canvas, 0, 0, sd_app->icon.mounted);
break;
default:
canvas_draw_icon(canvas, 0, 0, sd_app->icon.fail);
break;
}
}
void sd_app_draw_callback(Canvas* canvas, void* context) {
furi_assert(canvas);
furi_assert(context);
SdApp* sd_app = context;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) {
canvas_draw_str(canvas, 0, (i + 1) * 10, sd_app->line[i]);
}
}
void sd_app_input_callback(InputEvent* event, void* context) {
furi_assert(context);
SdApp* sd_app = context;
osMessageQueuePut(sd_app->event_queue, event, 0, 0);
}
SdApp* sd_app_alloc() {
SdApp* sd_app = furi_alloc(sizeof(SdApp));
// init inner fs data
if(!_fs_init(&sd_app->info)) {
furiac_exit(NULL);
}
sd_app->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL);
// init widget
sd_app->widget = widget_alloc();
widget_draw_callback_set(sd_app->widget, sd_app_draw_callback, sd_app);
widget_input_callback_set(sd_app->widget, sd_app_input_callback, sd_app);
widget_enabled_set(sd_app->widget, false);
// init lines
sd_set_lines(sd_app, 0);
// init icon widget
sd_app->icon.widget = widget_alloc();
sd_app->icon.mounted = assets_icons_get(I_SDcardMounted_11x8);
sd_app->icon.fail = assets_icons_get(I_SDcardFail_11x8);
widget_set_width(sd_app->icon.widget, icon_get_width(sd_app->icon.mounted));
widget_draw_callback_set(sd_app->icon.widget, sd_icon_draw_callback, sd_app);
widget_enabled_set(sd_app->icon.widget, false);
return sd_app;
}
bool app_sd_ask(SdApp* sd_app, Input input_true, Input input_false) {
bool result;
InputEvent event;
while(1) {
osStatus_t event_status =
osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever);
if(event_status == osOK) {
if(event.state && event.input == input_true) {
result = true;
break;
}
if(event.state && event.input == InputBack) {
result = false;
break;
}
}
}
return result;
}
void app_sd_info_callback(void* context) {
furi_assert(context);
SdApp* sd_app = context;
widget_enabled_set(sd_app->widget, true);
// dynamic strings
const uint8_t str_buffer_size = 26;
const uint8_t str_count = 6;
char* str_buffer[str_count];
bool memory_error = false;
// info vars
uint32_t serial_num;
SDError get_label_result, get_free_result;
FATFS* fs;
uint32_t free_clusters, free_sectors, total_sectors;
char volume_label[34];
// init strings
for(uint8_t i = 0; i < str_count; i++) {
str_buffer[i] = malloc(str_buffer_size + 1);
if(str_buffer[i] == NULL) {
memory_error = true;
} else {
snprintf(str_buffer[i], str_buffer_size, "");
}
}
if(memory_error) {
sd_set_lines(sd_app, 1, "not enough memory");
} else {
// get fs info
_fs_lock(&sd_app->info);
get_label_result = f_getlabel(sd_app->info.path, volume_label, &serial_num);
get_free_result = f_getfree(sd_app->info.path, &free_clusters, &fs);
_fs_unlock(&sd_app->info);
// calculate size
total_sectors = (fs->n_fatent - 2) * fs->csize;
free_sectors = free_clusters * fs->csize;
uint16_t sector_size = _MAX_SS;
#if _MAX_SS != _MIN_SS
sector_size = fs->ssize;
#endif
// output info to dynamic strings
if(get_label_result == SD_OK && get_free_result == SD_OK) {
snprintf(str_buffer[0], str_buffer_size, "%s", volume_label);
const char* fs_type = "";
switch(fs->fs_type) {
case(FS_FAT12):
fs_type = "FAT12";
break;
case(FS_FAT16):
fs_type = "FAT16";
break;
case(FS_FAT32):
fs_type = "FAT32";
break;
case(FS_EXFAT):
fs_type = "EXFAT";
break;
default:
fs_type = "UNKNOWN";
break;
}
snprintf(str_buffer[1], str_buffer_size, "%s, S/N: %lu", fs_type, serial_num);
snprintf(str_buffer[2], str_buffer_size, "Cluster: %d sectors", fs->csize);
snprintf(str_buffer[3], str_buffer_size, "Sector: %d bytes", sector_size);
snprintf(
str_buffer[4], str_buffer_size, "%lu KB total", total_sectors / 1024 * sector_size);
snprintf(
str_buffer[5], str_buffer_size, "%lu KB free", free_sectors / 1024 * sector_size);
} else {
snprintf(str_buffer[0], str_buffer_size, "SD status error:");
snprintf(
str_buffer[1],
str_buffer_size,
"%s",
fs_error_get_internal_desc(_fs_status(&sd_app->info)));
snprintf(str_buffer[2], str_buffer_size, "Label error:");
snprintf(
str_buffer[3], str_buffer_size, "%s", fs_error_get_internal_desc(get_label_result));
snprintf(str_buffer[4], str_buffer_size, "Get free error:");
snprintf(
str_buffer[5], str_buffer_size, "%s", fs_error_get_internal_desc(get_free_result));
}
// dynamic strings to screen
sd_set_lines(
sd_app,
6,
str_buffer[0],
str_buffer[1],
str_buffer[2],
str_buffer[3],
str_buffer[4],
str_buffer[5]);
}
app_sd_ask(sd_app, InputBack, InputBack);
sd_set_lines(sd_app, 0);
widget_enabled_set(sd_app->widget, false);
for(uint8_t i = 0; i < str_count; i++) {
free(str_buffer[i]);
}
}
void app_sd_format_callback(void* context) {
furi_assert(context);
SdApp* sd_app = context;
uint8_t* work_area;
// ask to really format
sd_set_lines(sd_app, 2, "Press UP to format", "or BACK to exit");
widget_enabled_set(sd_app->widget, true);
// wait for input
if(!app_sd_ask(sd_app, InputUp, InputBack)) {
widget_enabled_set(sd_app->widget, false);
return;
}
// show warning
sd_set_lines(sd_app, 3, "formatting SD card", "procedure can be lengthy", "please wait");
// format card
_fs_lock(&sd_app->info);
work_area = malloc(_MAX_SS);
if(work_area == NULL) {
sd_app->info.status = SD_NOT_ENOUGH_CORE;
} else {
sd_app->info.status = f_mkfs(sd_app->info.path, FM_ANY, 0, work_area, _MAX_SS);
free(work_area);
if(sd_app->info.status == SD_OK) {
// set label and mount card
f_setlabel("Flipper SD");
sd_app->info.status = f_mount(&sd_app->info.fat_fs, sd_app->info.path, 1);
}
}
if(sd_app->info.status != SD_OK) {
sd_set_lines(
sd_app, 2, "SD card format error", fs_error_get_internal_desc(sd_app->info.status));
} else {
sd_set_lines(sd_app, 1, "SD card formatted");
}
_fs_unlock(&sd_app->info);
// wait for BACK
app_sd_ask(sd_app, InputBack, InputBack);
widget_enabled_set(sd_app->widget, false);
}
void app_sd_unmount_card(SdApp* sd_app) {
_fs_lock(&sd_app->info);
// set status
sd_app->info.status = SD_NO_CARD;
widget_enabled_set(sd_app->icon.widget, false);
// close files
for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) {
FileData* filedata = &sd_app->info.files[index];
if(filedata->thread_id != NULL) {
if(filedata->is_dir) {
f_closedir(&filedata->data.dir);
} else {
f_close(&filedata->data.file);
}
filedata->thread_id = NULL;
}
}
// unmount volume
f_mount(0, sd_app->info.path, 0);
_fs_unlock(&sd_app->info);
}
void app_sd_eject_callback(void* context) {
furi_assert(context);
SdApp* sd_app = context;
sd_set_lines(sd_app, 1, "ejecting SD card");
widget_enabled_set(sd_app->widget, true);
app_sd_unmount_card(sd_app);
sd_set_lines(sd_app, 1, "SD card can be pulled out");
// wait for BACK
app_sd_ask(sd_app, InputBack, InputBack);
widget_enabled_set(sd_app->widget, false);
}
void sd_filesystem(void* p) {
SdApp* sd_app = sd_app_alloc();
FS_Api* fs_api = fs_api_alloc();
Gui* gui = furi_open("gui");
gui_add_widget(gui, sd_app->widget, GuiLayerFullscreen);
gui_add_widget(gui, sd_app->icon.widget, GuiLayerStatusBarLeft);
// add api record
if(!furi_create("sdcard", fs_api)) {
furiac_exit(NULL);
}
// init menu
// TODO menu icon
MenuItem* menu_item;
menu_item = menu_item_alloc_menu("SD Card", assets_icons_get(I_SDcardMounted_11x8));
menu_item_subitem_add(
menu_item, menu_item_alloc_function("Info", NULL, app_sd_info_callback, sd_app));
menu_item_subitem_add(
menu_item, menu_item_alloc_function("Format", NULL, app_sd_format_callback, sd_app));
menu_item_subitem_add(
menu_item, menu_item_alloc_function("Eject", NULL, app_sd_eject_callback, sd_app));
// add item to menu
ValueMutex* menu_vm = furi_open("menu");
furi_check(menu_vm);
with_value_mutex(
menu_vm, (Menu * menu) { menu_item_add(menu, menu_item); });
furiac_ready();
printf("[sd_filesystem] start\n");
// sd card cycle
bool sd_was_present = true;
while(true) {
if(sd_was_present) {
if(hal_gpio_read_sd_detect()) {
printf("[sd_filesystem] card detected\n");
uint8_t bsp_result = BSP_SD_Init();
if(bsp_result) {
sd_app->info.status = SD_LOW_LEVEL_ERR;
printf("[sd_filesystem] bsp error: %x\n", bsp_result);
} else {
printf("[sd_filesystem] bsp ok\n");
sd_app->info.status = f_mount(&sd_app->info.fat_fs, sd_app->info.path, 1);
if(sd_app->info.status != SD_OK) {
printf("[sd_filesystem] mount error: %d\n", sd_app->info.status);
} else {
printf("[sd_filesystem] mount ok\n");
}
}
widget_enabled_set(sd_app->icon.widget, true);
sd_was_present = false;
}
} else {
if(!hal_gpio_read_sd_detect()) {
printf("[sd_filesystem] card removed\n");
widget_enabled_set(sd_app->icon.widget, false);
app_sd_unmount_card(sd_app);
sd_was_present = true;
}
}
delay(1000);
}
}

View File

@ -0,0 +1,122 @@
#pragma once
#include "flipper.h"
#include "flipper_v2.h"
#define SD_FS_MAX_FILES _FS_LOCK
#define SD_STATE_LINES_COUNT 6
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
/* api data */
typedef FIL SDFile;
typedef DIR SDDir;
typedef FILINFO SDFileInfo;
/* storage for file/directory objects*/
typedef union {
SDFile file;
SDDir dir;
} SDFileDirStorage;
typedef enum {
SD_OK = FR_OK,
SD_DISK_ERR = FR_DISK_ERR,
SD_INT_ERR = FR_INT_ERR,
SD_NO_FILE = FR_NO_FILE,
SD_NO_PATH = FR_NO_PATH,
SD_INVALID_NAME = FR_INVALID_NAME,
SD_DENIED = FR_DENIED,
SD_EXIST = FR_EXIST,
SD_INVALID_OBJECT = FR_INVALID_OBJECT,
SD_WRITE_PROTECTED = FR_WRITE_PROTECTED,
SD_INVALID_DRIVE = FR_INVALID_DRIVE,
SD_NOT_ENABLED = FR_NOT_ENABLED,
SD_NO_FILESYSTEM = FR_NO_FILESYSTEM,
SD_MKFS_ABORTED = FR_MKFS_ABORTED,
SD_TIMEOUT = FR_TIMEOUT,
SD_LOCKED = FR_LOCKED,
SD_NOT_ENOUGH_CORE = FR_NOT_ENOUGH_CORE,
SD_TOO_MANY_OPEN_FILES = FR_TOO_MANY_OPEN_FILES,
SD_INVALID_PARAMETER = FR_INVALID_PARAMETER,
SD_NO_CARD,
SD_NOT_A_FILE,
SD_NOT_A_DIR,
SD_OTHER_APP,
SD_LOW_LEVEL_ERR,
} SDError;
typedef enum {
FDF_DIR,
FDF_FILE,
FDF_ANY,
} FiledataFilter;
typedef struct {
osThreadId_t thread_id;
bool is_dir;
SDFileDirStorage data;
} FileData;
/* application data */
typedef struct {
Widget* widget;
Icon* mounted;
Icon* fail;
} SdFsIcon;
typedef struct {
osMutexId_t mutex;
FileData files[SD_FS_MAX_FILES];
SDError status;
char* path;
FATFS fat_fs;
} SdFsInfo;
typedef struct {
SdFsInfo info;
SdFsIcon icon;
Widget* widget;
const char* line[SD_STATE_LINES_COUNT];
osMessageQueueId_t event_queue;
} SdApp;
/* core api fns */
bool _fs_init(SdFsInfo* _fs_info);
bool _fs_lock(SdFsInfo* fs_info);
bool _fs_unlock(SdFsInfo* fs_info);
SDError _fs_status(SdFsInfo* fs_info);
/* sd api fns */
bool fs_file_open(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode);
bool fs_file_close(File* file);
uint16_t fs_file_read(File* file, void* buff, uint16_t bytes_to_read);
uint16_t fs_file_write(File* file, void* buff, uint16_t bytes_to_write);
bool fs_file_seek(File* file, uint32_t offset, bool from_start);
uint64_t fs_file_tell(File* file);
bool fs_file_truncate(File* file);
uint64_t fs_file_size(File* file);
bool fs_file_sync(File* file);
bool fs_file_eof(File* file);
/* dir api */
bool fs_dir_open(File* file, const char* path);
bool fs_dir_close(File* file);
bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length);
bool fs_dir_rewind(File* file);
/* common api */
FS_Error
fs_common_info(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length);
FS_Error fs_common_remove(const char* path);
FS_Error fs_common_rename(const char* old_path, const char* new_path);
FS_Error fs_common_set_attr(const char* path, uint8_t attr, uint8_t mask);
FS_Error fs_common_mkdir(const char* path);
FS_Error fs_common_set_time(const char* path, FileDateUnion date, FileTimeUnion time);
FS_Error fs_get_fs_info(uint64_t* total_space, uint64_t* free_space);
/* errors api */
const char* fs_error_get_desc(FS_Error error_id);
const char* fs_error_get_internal_desc(uint32_t internal_error_id);

View File

@ -47,7 +47,7 @@
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */ / 3: f_lseek() function is removed in addition to 2. */
#define _USE_STRFUNC 2 /* 0:Disable or 1-2:Enable */ #define _USE_STRFUNC 0 /* 0:Disable or 1-2:Enable */
/* This option switches string functions, f_gets(), f_putc(), f_puts() and /* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf(). / f_printf().
/ /
@ -68,7 +68,7 @@
#define _USE_EXPAND 0 #define _USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */ /* This option switches f_expand function. (0:Disable or 1:Enable) */
#define _USE_CHMOD 0 #define _USE_CHMOD 1
/* This option switches attribute manipulation functions, f_chmod() and f_utime(). /* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */
@ -177,7 +177,7 @@
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */ / funciton will be available. */
#define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ #define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */
#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */ #define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */
/* These options configure the range of sector size to be supported. (512, 1024, /* These options configure the range of sector size to be supported. (512, 1024,
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ harddisk. But a larger value may be required for on-board flash memory and some / harddisk. But a larger value may be required for on-board flash memory and some

View File

@ -1,5 +1,6 @@
#include "api-hal-gpio.h" #include "api-hal-gpio.h"
#include "api-hal-resources.h" #include "api-hal-resources.h"
#include "spi.h"
// init GPIO // init GPIO
void hal_gpio_init( void hal_gpio_init(
@ -20,20 +21,27 @@ void hal_gpio_init(
bool hal_gpio_read_sd_detect(void) { bool hal_gpio_read_sd_detect(void) {
bool result = false; bool result = false;
// TODO open record // TODO open record
const GpioPin* sd_cs_record = &sd_cs_gpio; const GpioPin* sd_cs_record = &sd_cs_gpio;
// TODO: SPI manager
api_hal_spi_lock(&SPI_SD_HANDLE);
// configure pin as input // configure pin as input
gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
delay(50); delay(1);
// if gpio_read == 0 return true else return false // if gpio_read == 0 return true else return false
result = !gpio_read(sd_cs_record); result = !gpio_read(sd_cs_record);
// configure pin back // configure pin back
gpio_init_ex(sd_cs_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); gpio_init_ex(sd_cs_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
delay(50); gpio_write(sd_cs_record, 1);
delay(1);
// TODO: SPI manager
api_hal_spi_unlock(&SPI_SD_HANDLE);
return result; return result;
} }

View File

@ -0,0 +1,330 @@
#pragma once
#include "flipper.h"
#include "flipper_v2.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Access mode flags
*/
typedef enum {
FSAM_READ = (1 << 0), /**< Read access */
FSAM_WRITE = (1 << 1), /**< Write access */
} FS_AccessMode;
/**
* @brief Open mode flags
*/
typedef enum {
FSOM_OPEN_EXISTING = 1, /**< Open file, fail if file doesn't exist */
FSOM_OPEN_ALWAYS = 2, /**< Open file. Create new file if not exist */
FSOM_OPEN_APPEND = 4, /**< Open file. Create new file if not exist. Set R/W pointer to EOF */
FSOM_CREATE_NEW = 8, /**< Creates a new file. Fails if the file is exist */
FSOM_CREATE_ALWAYS = 16, /**< Creates a new file. If file exist, truncate to zero size */
} FS_OpenMode;
/**
* @brief API errors enumeration
*/
typedef enum {
FSE_OK, /**< No error */
FSE_NOT_READY, /**< FS not ready */
FSE_EXIST, /**< File/Dir alrady exist */
FSE_NOT_EXIST, /**< File/Dir does not exist */
FSE_INVALID_PARAMETER, /**< Invalid API parameter */
FSE_DENIED, /**< Access denied */
FSE_INVALID_NAME, /**< Invalid name/path */
FSE_INTERNAL, /**< Internal error */
FSE_NOT_IMPLEMENTED, /**< Functon not implemented */
} FS_Error;
/**
* @brief FileInfo flags
*/
typedef enum {
FSF_READ_ONLY = (1 << 0), /**< Readonly */
FSF_HIDDEN = (1 << 1), /**< Hidden */
FSF_SYSTEM = (1 << 2), /**< System */
FSF_DIRECTORY = (1 << 3), /**< Directory */
FSF_ARCHIVE = (1 << 4), /**< Archive */
} FS_Flags;
/**
* @brief Structure that hold file index and returned api errors
*/
typedef struct {
uint32_t file_id; /**< File ID for internal references */
FS_Error error_id; /**< Standart API error from FS_Error enum */
uint32_t internal_error_id; /**< Internal API error value */
} File;
// TODO: solve year 2107 problem
/**
* @brief Structure that hold packed date values
*/
typedef struct __attribute__((packed)) {
uint16_t month_day : 5; /**< month day */
uint16_t month : 4; /**< month index */
uint16_t year : 7; /**< year, year + 1980 to get actual value */
} FileDate;
/**
* @brief Structure that hold packed time values
*/
typedef struct __attribute__((packed)) {
uint16_t second : 5; /**< second, second * 2 to get actual value */
uint16_t minute : 6; /**< minute */
uint16_t hour : 5; /**< hour */
} FileTime;
/**
* @brief Union of simple date and real value
*/
typedef union {
FileDate simple; /**< simple access to date */
uint16_t value; /**< real date value */
} FileDateUnion;
/**
* @brief Union of simple time and real value
*/
typedef union {
FileTime simple; /**< simple access to time */
uint16_t value; /**< real time value */
} FileTimeUnion;
/**
* @brief Structure that hold file info
*/
typedef struct {
uint8_t flags; /**< flags from FS_Flags enum */
uint64_t size; /**< file size */
FileDateUnion date; /**< file date */
FileTimeUnion time; /**< file time */
} FileInfo;
/** @struct FS_File_Api
* @brief File api structure
*
* @var FS_File_Api::open
* @brief Open file
* @param file pointer to file object, filled by api
* @param path path to file
* @param access_mode access mode from FS_AccessMode
* @param open_mode open mode from FS_OpenMode
* @return success flag
*
* @var FS_File_Api::close
* @brief Close file
* @param file pointer to file object
* @return success flag
*
* @var FS_File_Api::read
* @brief Read bytes from file to buffer
* @param file pointer to file object
* @param buff pointer to buffer for reading
* @param bytes_to_read how many bytes to read, must be smaller or equal to buffer size
* @return how many bytes actually has been readed
*
* @var FS_File_Api::write
* @brief Write bytes from buffer to file
* @param file pointer to file object
* @param buff pointer to buffer for writing
* @param bytes_to_read how many bytes to write, must be smaller or equal to buffer size
* @return how many bytes actually has been writed
*
* @var FS_File_Api::seek
* @brief Move r/w pointer
* @param file pointer to file object
* @param offset offset to move r/w pointer
* @param from_start set offset from start, or from current position
* @return success flag
*
* @var FS_File_Api::tell
* @brief Get r/w pointer position
* @param file pointer to file object
* @return current r/w pointer position
*
* @var FS_File_Api::truncate
* @brief Truncate file size to current r/w pointer position
* @param file pointer to file object
* @return success flag
*
* @var FS_File_Api::size
* @brief Fet file size
* @param file pointer to file object
* @return file size
*
* @var FS_File_Api::sync
* @brief Write file cache to storage
* @param file pointer to file object
* @return success flag
*
* @var FS_File_Api::eof
* @brief Checks that the r/w pointer is at the end of the file
* @param file pointer to file object
* @return end of file flag
*/
/**
* @brief File api structure
*/
typedef struct {
bool (*open)(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode);
bool (*close)(File* file);
uint16_t (*read)(File* file, void* buff, uint16_t bytes_to_read);
uint16_t (*write)(File* file, void* buff, uint16_t bytes_to_write);
bool (*seek)(File* file, uint32_t offset, bool from_start);
uint64_t (*tell)(File* file);
bool (*truncate)(File* file);
uint64_t (*size)(File* file);
bool (*sync)(File* file);
bool (*eof)(File* file);
} FS_File_Api;
/** @struct FS_Dir_Api
* @brief Dir api structure
*
* @var FS_Dir_Api::open
* @brief Open directory to get objects from
* @param file pointer to file object, filled by api
* @param path path to directory
* @return success flag
*
* @var FS_Dir_Api::close
* @brief Close directory
* @param file pointer to file object
* @return success flag
*
* @var FS_Dir_Api::read
* @brief Read next object info in directory
* @param file pointer to file object
* @param fileinfo pointer to readed FileInfo, can be NULL
* @param name pointer to name buffer, can be NULL
* @param name_length name buffer length
* @return success flag (if next object not exist also returns false and set error_id to FSE_NOT_EXIST)
*
* @var FS_Dir_Api::rewind
* @brief Rewind to first object info in directory
* @param file pointer to file object
* @return success flag
*/
/**
* @brief Dir api structure
*/
typedef struct {
bool (*open)(File* file, const char* path);
bool (*close)(File* file);
bool (*read)(File* file, FileInfo* fileinfo, char* name, uint16_t name_length);
bool (*rewind)(File* file);
} FS_Dir_Api;
/** @struct FS_Common_Api
* @brief Common api structure
*
* @var FS_Common_Api::info
* @brief Open directory to get objects from
* @param path path to file/directory
* @param fileinfo pointer to readed FileInfo, can be NULL
* @param name pointer to name buffer, can be NULL
* @param name_length name buffer length
* @return FS_Error error info
*
* @var FS_Common_Api::remove
* @brief Remove file/directory from storage,
* directory must be empty,
* file/directory must not be opened,
* file/directory must not have FSF_READ_ONLY flag
* @param path path to file/directory
* @return FS_Error error info
*
* @var FS_Common_Api::rename
* @brief Rename file/directory,
* file/directory must not be opened
* @param path path to file/directory
* @return FS_Error error info
*
* @var FS_Common_Api::set_attr
* @brief Set attributes of file/directory,
* for example:
* @code
* set "read only" flag and remove "hidden" flag
* set_attr("file.txt", FSF_READ_ONLY, FSF_READ_ONLY | FSF_HIDDEN);
* @endcode
* @param path path to file/directory
* @param attr attribute values consist of FS_Flags
* @param mask attribute mask consist of FS_Flags
* @return FS_Error error info
*
* @var FS_Common_Api::mkdir
* @brief Create new directory
* @param path path to new directory
* @return FS_Error error info
*
* @var FS_Common_Api::set_time
* @brief Set file/directory modification time
* @param path path to file/directory
* @param date modification date
* @param time modification time
* @see FileDateUnion
* @see FileTimeUnion
* @return FS_Error error info
*
* @var FS_Common_Api::get_fs_info
* @brief Get total and free space storage values
* @param total_space pointer to total space value
* @param free_space pointer to free space value
* @return FS_Error error info
*/
/**
* @brief Common api structure
*/
typedef struct {
FS_Error (*info)(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length);
FS_Error (*remove)(const char* path);
FS_Error (*rename)(const char* old_path, const char* new_path);
FS_Error (*set_attr)(const char* path, uint8_t attr, uint8_t mask);
FS_Error (*mkdir)(const char* path);
FS_Error (*set_time)(const char* path, FileDateUnion date, FileTimeUnion time);
FS_Error (*get_fs_info)(uint64_t* total_space, uint64_t* free_space);
} FS_Common_Api;
/** @struct FS_Error_Api
* @brief Errors api structure
*
* @var FS_Error_Api::get_desc
* @brief Get error description text
* @param error_id FS_Error error id (for fire/dir functions result can be obtained from File.error_id)
* @return pointer to description text
*
* @var FS_Error_Api::get_internal_desc
* @brief Get internal error description text
* @param internal_error_id error id (for fire/dir functions result can be obtained from File.internal_error_id)
* @return pointer to description text
*/
/**
* @brief Errors api structure
*/
typedef struct {
const char* (*get_desc)(FS_Error error_id);
const char* (*get_internal_desc)(uint32_t internal_error_id);
} FS_Error_Api;
/**
* @brief Full filesystem api structure
*/
typedef struct {
FS_File_Api file;
FS_Dir_Api dir;
FS_Common_Api common;
FS_Error_Api error;
} FS_Api;
#ifdef __cplusplus
}
#endif

View File

@ -5,33 +5,33 @@
#ifndef _FF_INTEGER #ifndef _FF_INTEGER
#define _FF_INTEGER #define _FF_INTEGER
#ifdef _WIN32 /* FatFs development platform */ #ifdef _WIN32 /* FatFs development platform */
#include <windows.h> #include <windows.h>
#include <tchar.h> #include <tchar.h>
typedef unsigned __int64 QWORD; typedef unsigned __int64 QWORD;
#else /* Embedded platform */
#else /* Embedded platform */ #include <stdint.h>
/* These types MUST be 16-bit or 32-bit */ /* These types MUST be 16-bit or 32-bit */
typedef int INT; typedef int16_t INT;
typedef unsigned int UINT; typedef uint16_t UINT;
/* This type MUST be 8-bit */ /* This type MUST be 8-bit */
typedef unsigned char BYTE; typedef uint8_t BYTE;
/* These types MUST be 16-bit */ /* These types MUST be 16-bit */
typedef short SHORT; typedef int16_t SHORT;
typedef unsigned short WORD; typedef uint16_t WORD;
typedef unsigned short WCHAR; typedef uint16_t WCHAR;
/* These types MUST be 32-bit */ /* These types MUST be 32-bit */
typedef long LONG; typedef int32_t LONG;
typedef unsigned long DWORD; typedef uint32_t DWORD;
/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ /* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
typedef unsigned long long QWORD; typedef uint64_t QWORD;
#endif #endif

View File

@ -70,6 +70,9 @@ CFLAGS += -I$(CYFRAL_DIR)
CPP_SOURCES += $(wildcard $(CYFRAL_DIR)/*.cpp) CPP_SOURCES += $(wildcard $(CYFRAL_DIR)/*.cpp)
endif endif
# common apps api
CFLAGS += -I$(LIB_DIR)/common-api
# drivers # drivers
ifneq ($(TARGET), local) ifneq ($(TARGET), local)
ifneq ($(TARGET), f2) ifneq ($(TARGET), f2)