Move Mass Storage app to firmware repo

This commit is contained in:
Willy-JL
2024-02-20 06:11:51 +00:00
parent b58bcac1a2
commit 39bd3f4e6d
20 changed files with 1736 additions and 1 deletions

View File

@@ -0,0 +1,30 @@
#include "mass_storage_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const mass_storage_scene_on_enter_handlers[])(void*) = {
#include "mass_storage_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const mass_storage_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "mass_storage_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const mass_storage_scene_on_exit_handlers[])(void* context) = {
#include "mass_storage_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers mass_storage_scene_handlers = {
.on_enter_handlers = mass_storage_scene_on_enter_handlers,
.on_event_handlers = mass_storage_scene_on_event_handlers,
.on_exit_handlers = mass_storage_scene_on_exit_handlers,
.scene_num = MassStorageSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) MassStorageScene##id,
typedef enum {
#include "mass_storage_scene_config.h"
MassStorageSceneNum,
} MassStorageScene;
#undef ADD_SCENE
extern const SceneManagerHandlers mass_storage_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "mass_storage_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "mass_storage_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "mass_storage_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,5 @@
ADD_SCENE(mass_storage, start, Start)
ADD_SCENE(mass_storage, file_select, FileSelect)
ADD_SCENE(mass_storage, work, Work)
ADD_SCENE(mass_storage, create_image, CreateImage)
ADD_SCENE(mass_storage, create_image_name, CreateImageName)

View File

@@ -0,0 +1,188 @@
#include "../mass_storage_app_i.h"
#include <lib/toolbox/value_index.h>
enum VarItemListIndex {
VarItemListIndexImageSize,
VarItemListIndexImageName,
VarItemListIndexCreateImage,
};
void mass_storage_scene_create_image_variable_item_list_callback(void* context, uint32_t index) {
MassStorageApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
static const struct {
char* name;
uint64_t value;
} image_sizes[] = {
{"1MB", 1LL * 1024 * 1024},
{"2MB", 2LL * 1024 * 1024},
{"4MB", 4LL * 1024 * 1024},
{"8MB", 8LL * 1024 * 1024},
{"16MB", 16LL * 1024 * 1024},
{"32MB", 32LL * 1024 * 1024},
{"64MB", 64LL * 1024 * 1024},
{"128MB", 128LL * 1024 * 1024},
{"256MB", 256LL * 1024 * 1024},
{"512MB", 512LL * 1024 * 1024},
{"1GB", 1LL * 1024 * 1024 * 1024},
{"2GB", 2LL * 1024 * 1024 * 1024},
{"4GB", 4LL * 1024 * 1024 * 1024},
{"8GB", 8LL * 1024 * 1024 * 1024},
{"16GB", 16LL * 1024 * 1024 * 1024},
{"32GB", 32LL * 1024 * 1024 * 1024},
{"64GB", 64LL * 1024 * 1024 * 1024},
{"128GB", 128LL * 1024 * 1024 * 1024},
{"256GB", 256LL * 1024 * 1024 * 1024},
{"512GB", 512LL * 1024 * 1024 * 1024},
};
static void mass_storage_scene_create_image_image_size_changed(VariableItem* item) {
MassStorageApp* app = variable_item_get_context(item);
app->create_image_size = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
}
void mass_storage_scene_create_image_on_enter(void* context) {
MassStorageApp* app = context;
VariableItemList* variable_item_list = app->variable_item_list;
VariableItem* item;
uint8_t size_count = COUNT_OF(image_sizes);
if(app->create_image_max) {
for(size_t i = 1; i < size_count; i++) {
if(image_sizes[i].value > app->create_image_max) {
size_count = i;
break;
}
}
}
if(app->create_image_size == (uint8_t)-1) {
app->create_image_size = CLAMP(7, size_count - 2, 0); // 7 = 128MB
}
item = variable_item_list_add(
variable_item_list,
"Image Size",
size_count,
mass_storage_scene_create_image_image_size_changed,
app);
variable_item_set_current_value_index(item, app->create_image_size);
variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
item = variable_item_list_add(variable_item_list, "Image Name", 0, NULL, app);
variable_item_set_current_value_text(item, app->create_image_name);
variable_item_list_add(variable_item_list, "Create Image", 0, NULL, app);
variable_item_list_set_enter_callback(
variable_item_list, mass_storage_scene_create_image_variable_item_list_callback, app);
variable_item_list_set_header(variable_item_list, "Create Disk Image");
variable_item_list_set_selected_item(
variable_item_list,
scene_manager_get_scene_state(app->scene_manager, MassStorageSceneCreateImage));
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
}
static void popup_callback_ok(void* context) {
MassStorageApp* app = context;
scene_manager_set_scene_state(
app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect);
scene_manager_previous_scene(app->scene_manager);
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
}
static void popup_callback_error(void* context) {
MassStorageApp* app = context;
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
}
bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent event) {
MassStorageApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(
app->scene_manager, MassStorageSceneCreateImage, event.event);
consumed = true;
switch(event.event) {
case VarItemListIndexImageName:
scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName);
break;
case VarItemListIndexCreateImage: {
mass_storage_app_show_loading_popup(app, true);
const char* name = strnlen(app->create_image_name, sizeof(app->create_image_name)) ?
app->create_image_name :
image_sizes[app->create_image_size].name;
furi_string_printf(
app->file_path,
"%s/%s%s",
MASS_STORAGE_APP_PATH_FOLDER,
name,
MASS_STORAGE_APP_EXTENSION);
app->file = storage_file_alloc(app->fs_api);
const char* error = NULL;
bool success = false;
do {
if(!storage_file_open(
app->file,
furi_string_get_cstr(app->file_path),
FSAM_WRITE,
FSOM_CREATE_NEW))
break;
uint64_t size = image_sizes[app->create_image_size].value;
if(size == app->create_image_max) size--;
if(!storage_file_expand(app->file, size)) break;
// Format as exFAT
if(storage_virtual_init(app->fs_api, app->file) != FSE_OK) break;
if(storage_virtual_format(app->fs_api) != FSE_OK) break;
if(storage_virtual_quit(app->fs_api) != FSE_OK) break;
success = true;
} while(false);
if(!success) {
error = storage_file_get_error_desc(app->file);
FS_Error error = storage_file_get_error(app->file);
storage_file_close(app->file);
if(error != FSE_EXIST) {
storage_common_remove(app->fs_api, furi_string_get_cstr(app->file_path));
}
}
storage_file_free(app->file);
mass_storage_app_show_loading_popup(app, false);
if(error) {
popup_set_header(
app->popup, "Error Creating Image!", 64, 26, AlignCenter, AlignCenter);
popup_set_text(app->popup, error, 64, 40, AlignCenter, AlignCenter);
popup_set_callback(app->popup, popup_callback_error);
} else {
popup_set_header(app->popup, "Image Created!", 64, 32, AlignCenter, AlignCenter);
popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom);
popup_set_callback(app->popup, popup_callback_ok);
}
popup_set_context(app->popup, app);
popup_set_timeout(app->popup, 0);
popup_disable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup);
break;
}
default:
break;
}
}
return consumed;
}
void mass_storage_scene_create_image_on_exit(void* context) {
MassStorageApp* app = context;
variable_item_list_reset(app->variable_item_list);
}

View File

@@ -0,0 +1,52 @@
#include "../mass_storage_app_i.h"
enum TextInputIndex {
TextInputResultOk,
};
static void mass_storage_scene_create_image_name_text_input_callback(void* context) {
MassStorageApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
}
void mass_storage_scene_create_image_name_on_enter(void* context) {
MassStorageApp* app = context;
TextInput* text_input = app->text_input;
text_input_set_header_text(text_input, "Image name, empty = default");
text_input_set_minimum_length(text_input, 0);
text_input_set_result_callback(
text_input,
mass_storage_scene_create_image_name_text_input_callback,
app,
app->create_image_name,
sizeof(app->create_image_name),
false);
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput);
}
bool mass_storage_scene_create_image_name_on_event(void* context, SceneManagerEvent event) {
MassStorageApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
switch(event.event) {
case TextInputResultOk:
scene_manager_previous_scene(app->scene_manager);
break;
default:
break;
}
}
return consumed;
}
void mass_storage_scene_create_image_name_on_exit(void* context) {
MassStorageApp* app = context;
text_input_reset(app->text_input);
}

View File

@@ -0,0 +1,37 @@
#include "../mass_storage_app_i.h"
#include "furi_hal_power.h"
static bool mass_storage_file_select(MassStorageApp* mass_storage) {
furi_assert(mass_storage);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, ".img|.iso", &I_floppydisk_10px);
browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
browser_options.hide_ext = false;
// Input events and views are managed by file_select
bool res = dialog_file_browser_show(
mass_storage->dialogs, mass_storage->file_path, mass_storage->file_path, &browser_options);
return res;
}
void mass_storage_scene_file_select_on_enter(void* context) {
MassStorageApp* mass_storage = context;
if(mass_storage_file_select(mass_storage)) {
scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork);
} else {
scene_manager_previous_scene(mass_storage->scene_manager);
}
}
bool mass_storage_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
// MassStorageApp* mass_storage = context;
return false;
}
void mass_storage_scene_file_select_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,63 @@
#include "../mass_storage_app_i.h"
enum VarItemListIndex {
VarItemListIndexSelectDiskImage,
VarItemListIndexCreateDiskImage,
};
static void mass_storage_scene_start_variable_item_list_callback(void* context, uint32_t index) {
MassStorageApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void mass_storage_scene_start_on_enter(void* context) {
MassStorageApp* app = context;
VariableItemList* variable_item_list = app->variable_item_list;
variable_item_list_add(variable_item_list, "Select Disk Image", 0, NULL, app);
variable_item_list_add(variable_item_list, "Create Disk Image", 0, NULL, app);
variable_item_list_set_enter_callback(
variable_item_list, mass_storage_scene_start_variable_item_list_callback, app);
variable_item_list_set_header(variable_item_list, "USB Mass Storage");
variable_item_list_set_selected_item(
variable_item_list,
scene_manager_get_scene_state(app->scene_manager, MassStorageSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
}
bool mass_storage_scene_start_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
MassStorageApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, MassStorageSceneStart, event.event);
consumed = true;
switch(event.event) {
case VarItemListIndexSelectDiskImage:
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
break;
case VarItemListIndexCreateDiskImage:
scene_manager_set_scene_state(app->scene_manager, MassStorageSceneCreateImage, 0);
scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImage);
break;
default:
break;
}
}
return consumed;
}
void mass_storage_scene_start_on_exit(void* context) {
UNUSED(context);
MassStorageApp* app = context;
variable_item_list_reset(app->variable_item_list);
}

View File

@@ -0,0 +1,137 @@
#include "../mass_storage_app_i.h"
#include "../views/mass_storage_view.h"
#include "../helpers/mass_storage_usb.h"
#include <lib/toolbox/path.h>
#define TAG "MassStorageSceneWork"
static bool file_read(
void* ctx,
uint32_t lba,
uint16_t count,
uint8_t* out,
uint32_t* out_len,
uint32_t out_cap) {
MassStorageApp* app = ctx;
FURI_LOG_T(TAG, "file_read lba=%08lX count=%04X out_cap=%08lX", lba, count, out_cap);
if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
FURI_LOG_W(TAG, "seek failed");
return false;
}
uint16_t clamp = MIN(out_cap, count * SCSI_BLOCK_SIZE);
*out_len = storage_file_read(app->file, out, clamp);
FURI_LOG_T(TAG, "%lu/%lu", *out_len, count * SCSI_BLOCK_SIZE);
app->bytes_read += *out_len;
return *out_len == clamp;
}
static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len) {
MassStorageApp* app = ctx;
FURI_LOG_T(TAG, "file_write lba=%08lX count=%04X len=%08lX", lba, count, len);
if(len != count * SCSI_BLOCK_SIZE) {
FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len);
return false;
}
if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
FURI_LOG_W(TAG, "seek failed");
return false;
}
app->bytes_written += len;
return storage_file_write(app->file, buf, len) == len;
}
static uint32_t file_num_blocks(void* ctx) {
MassStorageApp* app = ctx;
return storage_file_size(app->file) / SCSI_BLOCK_SIZE;
}
static void file_eject(void* ctx) {
MassStorageApp* app = ctx;
FURI_LOG_D(TAG, "EJECT");
view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventEject);
}
bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) {
MassStorageApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == MassStorageCustomEventEject) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, MassStorageSceneFileSelect);
if(!consumed) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, MassStorageSceneStart);
}
}
} else if(event.type == SceneManagerEventTypeTick) {
mass_storage_set_stats(app->mass_storage_view, app->bytes_read, app->bytes_written);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, MassStorageSceneFileSelect);
if(!consumed) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, MassStorageSceneStart);
}
}
return consumed;
}
void mass_storage_scene_work_on_enter(void* context) {
MassStorageApp* app = context;
app->bytes_read = app->bytes_written = 0;
if(!storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, MassStorageSceneStart);
return;
}
mass_storage_app_show_loading_popup(app, true);
app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
FuriString* file_name = furi_string_alloc();
path_extract_filename(app->file_path, file_name, true);
mass_storage_set_file_name(app->mass_storage_view, file_name);
app->file = storage_file_alloc(app->fs_api);
furi_assert(storage_file_open(
app->file,
furi_string_get_cstr(app->file_path),
FSAM_READ | FSAM_WRITE,
FSOM_OPEN_EXISTING));
SCSIDeviceFunc fn = {
.ctx = app,
.read = file_read,
.write = file_write,
.num_blocks = file_num_blocks,
.eject = file_eject,
};
app->usb = mass_storage_usb_start(furi_string_get_cstr(file_name), fn);
furi_string_free(file_name);
mass_storage_app_show_loading_popup(app, false);
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork);
}
void mass_storage_scene_work_on_exit(void* context) {
MassStorageApp* app = context;
mass_storage_app_show_loading_popup(app, true);
if(app->usb_mutex) {
furi_mutex_free(app->usb_mutex);
app->usb_mutex = NULL;
}
if(app->usb) {
mass_storage_usb_stop(app->usb);
app->usb = NULL;
}
if(app->file) {
storage_file_free(app->file);
app->file = NULL;
}
mass_storage_app_show_loading_popup(app, false);
}