[FL-1191][FL-1524] Filesystem rework (#568)

* FS-Api: removed datetime manipulation functions and most of the file flags
* Filesystem: common proxy api
* Filesystem: renamed to Storage. Work has begun on a glue layer. Added functions for reentrance.
* Storage: sd mount and sd file open
* Storage: sd file close
* Storage: temporary test app
* Storage: free filedata on close
* Storage: sd file read and write
* Storage: added internal storage (LittleFS)
* Storage: renamed internal commands
* Storage: seek, tell, truncate, size, sync, eof
* Storage: error descriptions
* Storage: directory management api (open, close, read, rewind)
* Storage: common management api (stat, fs_stat, remove, rename, mkdir)
* Dolphin app and Notifications app now use raw storage.
* Storage: storage statuses renamed. Implemented sd card icon.
* Storage: added raw sd-card api.
* Storage settings: work started
* Assets: use new icons approach
* Storage settings: working storage settings
* Storage: completely redesigned api, no longer sticking out FS_Api
* Storage: more simplified api, getting error_id from file is hidden from user, pointer to api is hidden inside file
* Storage: cli info and format commands
* Storage-cli: file list
* Storage: a simpler and more reliable api
* FatFS: slightly lighter and faster config. Also disabled reentrancy and file locking functions. They moved to a storage service.
* Storage-cli: accommodate to the new cli api.
* Storage: filesystem api is separated into internal and common api.
* Cli: added the ability to print the list of free heap blocks
* Storage: uses a list instead of an array to store the StorageFile. Rewrote api calls to use semaphores instead of thread flags.
* Storage settings: added the ability to benchmark the SD card.
* Gui module file select: uses new storage api
* Apps: removed deprecated sd_card_test application
* Args lib: support for enquoted arguments
* Dialogs: a new gui app for simple non-asynchronous apps
* Dialogs: view holder for easy single view work
* File worker: use new storage api
* IButton and lfrrfid apps: save keys to any storage
* Apps: fix ibutton and lfrfid stack, remove sd_card_test.
* SD filesystem: app removed
* File worker: fixed api pointer type
* Subghz: loading assets using the new storage api
* NFC: use the new storage api
* Dialogs: the better api for the message element
* Archive: use new storage api
* Irda: changed assest path, changed app path
* FileWorker: removed unused file_buf_cnt
* Storage: copying and renaming files now works between storages
* Storage cli: read, copy, remove, rename commands
* Archive: removed commented code
* Storage cli: write command
* Applications: add SRV_STORAGE and SRV_DIALOGS
* Internal-storage: removed
* Storage: improved api
* Storage app: changed api pointer from StorageApp to Storage
* Storage: better file_id handling
* Storage: more consistent errors
* Loader: support for NULL icons
* Storage: do nothing with the lfs file or directory if it is not open
* Storage: fix typo
* Storage: minor float usage cleanup, rename some symbols.
* Storage: compact doxygen comments.

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2021-07-23 22:20:19 +10:00
committed by GitHub
parent a81203941b
commit ad421a81bc
95 changed files with 6451 additions and 4349 deletions

View File

@@ -0,0 +1,8 @@
#pragma once
#define API_LOCK_INIT_LOCKED() osSemaphoreNew(1, 0, NULL);
#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \
osSemaphoreAcquire(_lock, osWaitForever); \
osSemaphoreDelete(_lock);
#define API_LOCK_UNLOCK(_lock) osSemaphoreRelease(_lock);

View File

@@ -0,0 +1,62 @@
#include "dialogs-i.h"
#include "dialogs-api-lock.h"
/****************** File select ******************/
bool dialog_file_select_show(
DialogsApp* context,
const char* path,
const char* extension,
char* result,
uint8_t result_size,
const char* preselected_filename) {
osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED();
furi_check(semaphore != NULL);
DialogsAppData data = {
.file_select = {
.path = path,
.extension = extension,
.result = result,
.result_size = result_size,
.preselected_filename = preselected_filename,
}};
DialogsAppReturn return_data;
DialogsAppMessage message = {
.semaphore = semaphore,
.command = DialogsAppCommandFileOpen,
.data = &data,
.return_data = &return_data,
};
furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore);
return return_data.bool_value;
}
/****************** Message ******************/
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
osSemaphoreId_t semaphore = API_LOCK_INIT_LOCKED();
furi_check(semaphore != NULL);
DialogsAppData data = {
.dialog = {
.message = dialog_message,
}};
DialogsAppReturn return_data;
DialogsAppMessage message = {
.semaphore = semaphore,
.command = DialogsAppCommandDialog,
.data = &data,
.return_data = &return_data,
};
furi_check(osMessageQueuePut(context->message_queue, &message, 0, osWaitForever) == osOK);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(semaphore);
return return_data.dialog_value;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "dialogs.h"
#include "dialogs-message.h"
#include "view-holder.h"
#ifdef __cplusplus
extern "C" {
#endif
struct DialogsApp {
osMessageQueueId_t message_queue;
};
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,45 @@
#pragma once
#include <furi.h>
#include "dialogs-i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const char* path;
const char* extension;
char* result;
uint8_t result_size;
const char* preselected_filename;
} DialogsAppMessageDataFileSelect;
typedef struct {
const DialogMessage* message;
} DialogsAppMessageDataDialog;
typedef union {
DialogsAppMessageDataFileSelect file_select;
DialogsAppMessageDataDialog dialog;
} DialogsAppData;
typedef union {
bool bool_value;
DialogMessageButton dialog_value;
} DialogsAppReturn;
typedef enum {
DialogsAppCommandFileOpen,
DialogsAppCommandDialog,
} DialogsAppCommand;
typedef struct {
osSemaphoreId_t semaphore;
DialogsAppCommand command;
DialogsAppData* data;
DialogsAppReturn* return_data;
} DialogsAppMessage;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,59 @@
#include "dialogs-i.h"
#include "dialogs-api-lock.h"
#include <gui/modules/file_select.h>
typedef struct {
osSemaphoreId_t semaphore;
bool result;
} DialogsAppFileSelectContext;
static void dialogs_app_file_select_back_callback(void* context) {
furi_assert(context);
DialogsAppFileSelectContext* file_select_context = context;
file_select_context->result = false;
API_LOCK_UNLOCK(file_select_context->semaphore);
}
static void dialogs_app_file_select_callback(bool result, void* context) {
furi_assert(context);
DialogsAppFileSelectContext* file_select_context = context;
file_select_context->result = result;
API_LOCK_UNLOCK(file_select_context->semaphore);
}
bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data) {
bool ret = false;
Gui* gui = furi_record_open("gui");
DialogsAppFileSelectContext* file_select_context =
furi_alloc(sizeof(DialogsAppFileSelectContext));
file_select_context->semaphore = API_LOCK_INIT_LOCKED();
ViewHolder* view_holder = view_holder_alloc();
view_holder_attach_to_gui(view_holder, gui);
view_holder_set_back_callback(
view_holder, dialogs_app_file_select_back_callback, file_select_context);
FileSelect* file_select = file_select_alloc();
file_select_set_callback(file_select, dialogs_app_file_select_callback, file_select_context);
file_select_set_filter(file_select, data->path, data->extension);
file_select_set_result_buffer(file_select, data->result, data->result_size);
file_select_init(file_select);
if(data->preselected_filename != NULL) {
file_select_set_selected_file(file_select, data->preselected_filename);
}
view_holder_set_view(view_holder, file_select_get_view(file_select));
view_holder_start(view_holder);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(file_select_context->semaphore);
ret = file_select_context->result;
free(file_select_context);
view_holder_stop(view_holder);
view_holder_free(view_holder);
file_select_free(file_select);
furi_record_close("gui");
return ret;
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "dialogs-message.h"
#ifdef __cplusplus
extern "C" {
#endif
bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,152 @@
#include "dialogs-i.h"
#include "dialogs-api-lock.h"
#include <gui/modules/dialog_ex.h>
typedef struct {
osSemaphoreId_t semaphore;
DialogMessageButton result;
} DialogsAppMessageContext;
struct DialogMessage {
const char* header_text;
uint8_t header_text_x;
uint8_t header_text_y;
Align header_horizontal;
Align header_vertical;
const char* dialog_text;
uint8_t dialog_text_x;
uint8_t dialog_text_y;
Align dialog_text_horizontal;
Align dialog_text_vertical;
const Icon* icon;
uint8_t icon_x;
uint8_t icon_y;
const char* left_button_text;
const char* center_button_text;
const char* right_button_text;
};
static void dialogs_app_message_back_callback(void* context) {
furi_assert(context);
DialogsAppMessageContext* message_context = context;
message_context->result = DialogMessageButtonBack;
API_LOCK_UNLOCK(message_context->semaphore);
}
static void dialogs_app_message_callback(DialogExResult result, void* context) {
furi_assert(context);
DialogsAppMessageContext* message_context = context;
switch(result) {
case DialogExResultLeft:
message_context->result = DialogMessageButtonLeft;
break;
case DialogExResultRight:
message_context->result = DialogMessageButtonRight;
break;
case DialogExResultCenter:
message_context->result = DialogMessageButtonCenter;
break;
}
API_LOCK_UNLOCK(message_context->semaphore);
}
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
DialogMessageButton ret = DialogMessageButtonBack;
Gui* gui = furi_record_open("gui");
const DialogMessage* message = data->message;
DialogsAppMessageContext* message_context = furi_alloc(sizeof(DialogsAppMessageContext));
message_context->semaphore = API_LOCK_INIT_LOCKED();
ViewHolder* view_holder = view_holder_alloc();
view_holder_attach_to_gui(view_holder, gui);
view_holder_set_back_callback(view_holder, dialogs_app_message_back_callback, message_context);
DialogEx* dialog_ex = dialog_ex_alloc();
dialog_ex_set_result_callback(dialog_ex, dialogs_app_message_callback);
dialog_ex_set_context(dialog_ex, message_context);
dialog_ex_set_header(
dialog_ex,
message->header_text,
message->header_text_x,
message->header_text_y,
message->header_horizontal,
message->header_vertical);
dialog_ex_set_text(
dialog_ex,
message->dialog_text,
message->dialog_text_x,
message->dialog_text_y,
message->dialog_text_horizontal,
message->dialog_text_vertical);
dialog_ex_set_icon(dialog_ex, message->icon_x, message->icon_y, message->icon);
dialog_ex_set_left_button_text(dialog_ex, message->left_button_text);
dialog_ex_set_center_button_text(dialog_ex, message->center_button_text);
dialog_ex_set_right_button_text(dialog_ex, message->right_button_text);
view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
view_holder_start(view_holder);
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(message_context->semaphore);
ret = message_context->result;
free(message_context);
view_holder_stop(view_holder);
view_holder_free(view_holder);
dialog_ex_free(dialog_ex);
furi_record_close("gui");
return ret;
}
DialogMessage* dialog_message_alloc() {
DialogMessage* message = furi_alloc(sizeof(DialogMessage));
return message;
}
void dialog_message_free(DialogMessage* message) {
free(message);
}
void dialog_message_set_text(
DialogMessage* message,
const char* text,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical) {
message->dialog_text = text;
message->dialog_text_x = x;
message->dialog_text_y = y;
message->dialog_text_horizontal = horizontal;
message->dialog_text_vertical = vertical;
}
void dialog_message_set_header(
DialogMessage* message,
const char* text,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical) {
message->header_text = text;
message->header_text_x = x;
message->header_text_y = y;
message->header_horizontal = horizontal;
message->header_vertical = vertical;
}
void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y) {
message->icon = icon;
message->icon_x = x;
message->icon_y = y;
}
void dialog_message_set_buttons(
DialogMessage* message,
const char* left,
const char* center,
const char* right) {
message->left_button_text = left;
message->center_button_text = center;
message->right_button_text = right;
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "dialogs-message.h"
#ifdef __cplusplus
extern "C" {
#endif
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,39 @@
#include "dialogs-i.h"
#include "dialogs-api-lock.h"
#include "dialogs-module-file-select.h"
#include "dialogs-module-message.h"
static DialogsApp* dialogs_app_alloc() {
DialogsApp* app = malloc(sizeof(DialogsApp));
app->message_queue = osMessageQueueNew(8, sizeof(DialogsAppMessage), NULL);
return app;
}
static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) {
switch(message->command) {
case DialogsAppCommandFileOpen:
message->return_data->bool_value =
dialogs_app_process_module_file_select(&message->data->file_select);
break;
case DialogsAppCommandDialog:
message->return_data->dialog_value =
dialogs_app_process_module_message(&message->data->dialog);
break;
}
API_LOCK_UNLOCK(message->semaphore);
}
int32_t dialogs_app(void* p) {
DialogsApp* app = dialogs_app_alloc();
furi_record_create("dialogs", app);
DialogsAppMessage message;
while(1) {
if(osMessageQueueGet(app->message_queue, &message, NULL, osWaitForever) == osOK) {
dialogs_app_process_message(app, &message);
}
}
return 0;
}

View File

@@ -0,0 +1,128 @@
#pragma once
#include <furi.h>
#include <gui/canvas.h>
#ifdef __cplusplus
extern "C" {
#endif
/****************** COMMON ******************/
typedef struct DialogsApp DialogsApp;
/****************** FILE SELECT ******************/
/**
* Shows and processes the file selection dialog
* @param context api pointer
* @param path path to directory
* @param extension file extension to be offered for selection
* @param selected_filename buffer where the selected filename will be saved
* @param selected_filename_size and the size of this buffer
* @param preselected_filename filename to be preselected
* @return bool whether a file was selected
*/
bool dialog_file_select_show(
DialogsApp* context,
const char* path,
const char* extension,
char* result,
uint8_t result_size,
const char* preselected_filename);
/****************** MESSAGE ******************/
/**
* Message result type
*/
typedef enum {
DialogMessageButtonBack,
DialogMessageButtonLeft,
DialogMessageButtonCenter,
DialogMessageButtonRight,
} DialogMessageButton;
/**
* Message struct
*/
typedef struct DialogMessage DialogMessage;
/**
* Allocate and fill message
* @return DialogMessage*
*/
DialogMessage* dialog_message_alloc();
/**
* Free message struct
* @param message message pointer
*/
void dialog_message_free(DialogMessage* message);
/**
* Set message text
* @param message message pointer
* @param text text, can be NULL if you don't want to display the text
* @param x x position
* @param y y position
* @param horizontal horizontal alignment
* @param vertical vertical alignment
*/
void dialog_message_set_text(
DialogMessage* message,
const char* text,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical);
/**
* Set message header
* @param message message pointer
* @param text text, can be NULL if you don't want to display the header
* @param x x position
* @param y y position
* @param horizontal horizontal alignment
* @param vertical vertical alignment
*/
void dialog_message_set_header(
DialogMessage* message,
const char* text,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical);
/**
* Set message icon
* @param message message pointer
* @param icon icon pointer, can be NULL if you don't want to display the icon
* @param x x position
* @param y y position
*/
void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y);
/**
* Set message buttons text, button text can be NULL if you don't want to display and process some buttons
* @param message message pointer
* @param left left button text, can be NULL if you don't want to display the left button
* @param center center button text, can be NULL if you don't want to display the center button
* @param right right button text, can be NULL if you don't want to display the right button
*/
void dialog_message_set_buttons(
DialogMessage* message,
const char* left,
const char* center,
const char* right);
/**
* Show message from filled struct
* @param context api pointer
* @param message message struct pointer to be shown
* @return DialogMessageButton type
*/
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,98 @@
#pragma once
#include <gui/view.h>
#include <gui/gui.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ViewHolder ViewHolder;
/**
* @brief Free callback type
*/
typedef void (*FreeCallback)(void* free_context);
/**
* @brief Back callback type
* @warning comes from GUI thread
*/
typedef void (*BackCallback)(void* back_context);
/**
* @brief Allocate ViewHolder
* @return pointer to ViewHolder instance
*/
ViewHolder* view_holder_alloc();
/**
* @brief Free ViewHolder and call Free callback
* @param view_holder pointer to ViewHolder
*/
void view_holder_free(ViewHolder* view_holder);
/**
* @brief Set view for ViewHolder
*
* @param view_holder ViewHolder instance
* @param view View instance
*/
void view_holder_set_view(ViewHolder* view_holder, View* view);
/**
* @brief Set Free callback
*
* @param view_holder ViewHolder instance
* @param free_callback callback pointer
* @param free_context callback context
*/
void view_holder_set_free_callback(
ViewHolder* view_holder,
FreeCallback free_callback,
void* free_context);
/**
* @brief Free callback context getter. Useful if your Free callback is a module destructor, so you can get an instance of the module using this method.
*
* @param view_holder ViewHolder instance
* @return void* free callback context
*/
void* view_holder_get_free_context(ViewHolder* view_holder);
void view_holder_set_back_callback(
ViewHolder* view_holder,
BackCallback back_callback,
void* back_context);
/**
* @brief Attach ViewHolder to GUI
*
* @param view_holder ViewHolder instance
* @param gui GUI instance to attach to
*/
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui);
/**
* @brief Enable view processing
*
* @param view_holder
*/
void view_holder_start(ViewHolder* view_holder);
/**
* @brief Disable view processing
*
* @param view_holder
*/
void view_holder_stop(ViewHolder* view_holder);
/** View Update Handler
* @param view, View Instance
* @param context, ViewHolder instance
*/
void view_holder_update(View* view, void* context);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,130 @@
#include "view-holder.h"
#include <gui/view_i.h>
struct ViewHolder {
View* view;
ViewPort* view_port;
Gui* gui;
FreeCallback free_callback;
void* free_context;
BackCallback back_callback;
void* back_context;
};
static void view_holder_draw_callback(Canvas* canvas, void* context);
static void view_holder_input_callback(InputEvent* event, void* context);
ViewHolder* view_holder_alloc() {
ViewHolder* view_holder = furi_alloc(sizeof(ViewHolder));
view_holder->view_port = view_port_alloc();
view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder);
view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder);
view_port_enabled_set(view_holder->view_port, false);
return view_holder;
}
void view_holder_free(ViewHolder* view_holder) {
furi_assert(view_holder);
if(view_holder->gui) {
gui_remove_view_port(view_holder->gui, view_holder->view_port);
}
view_port_free(view_holder->view_port);
if(view_holder->free_callback) {
view_holder->free_callback(view_holder->free_context);
}
free(view_holder);
}
void view_holder_set_view(ViewHolder* view_holder, View* view) {
furi_assert(view_holder);
if(view_holder->view) {
view_set_update_callback(view_holder->view, NULL);
view_set_update_callback_context(view_holder->view, NULL);
}
view_holder->view = view;
if(view_holder->view) {
view_set_update_callback(view_holder->view, view_holder_update);
view_set_update_callback_context(view_holder->view, view_holder);
}
}
void view_holder_set_free_callback(
ViewHolder* view_holder,
FreeCallback free_callback,
void* free_context) {
furi_assert(view_holder);
view_holder->free_callback = free_callback;
view_holder->free_context = free_context;
}
void* view_holder_get_free_context(ViewHolder* view_holder) {
return view_holder->free_context;
}
void view_holder_set_back_callback(
ViewHolder* view_holder,
BackCallback back_callback,
void* back_context) {
furi_assert(view_holder);
view_holder->back_callback = back_callback;
view_holder->back_context = back_context;
}
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) {
furi_assert(gui);
furi_assert(view_holder);
view_holder->gui = gui;
gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen);
}
void view_holder_start(ViewHolder* view_holder) {
view_port_enabled_set(view_holder->view_port, true);
}
void view_holder_stop(ViewHolder* view_holder) {
view_port_enabled_set(view_holder->view_port, false);
}
void view_holder_update(View* view, void* context) {
furi_assert(view);
furi_assert(context);
ViewHolder* view_holder = context;
if(view == view_holder->view) {
view_port_update(view_holder->view_port);
}
}
static void view_holder_draw_callback(Canvas* canvas, void* context) {
ViewHolder* view_holder = context;
if(view_holder->view) {
view_draw(view_holder->view, canvas);
}
}
static void view_holder_input_callback(InputEvent* event, void* context) {
ViewHolder* view_holder = context;
bool is_consumed = false;
if(view_holder->view) {
is_consumed = view_input(view_holder->view, event);
}
if(!is_consumed && event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
if(view_holder->back_callback) {
view_holder->back_callback(view_holder->back_context);
}
}
}
}