mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-14 14:18:35 -07:00
Update mass storage
This commit is contained in:
@@ -8,7 +8,7 @@ App(
|
|||||||
"dialogs",
|
"dialogs",
|
||||||
],
|
],
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
order=20,
|
fap_description="Implements a mass storage device over USB for disk images",
|
||||||
fap_icon="assets/mass_storage_10px.png",
|
fap_icon="assets/mass_storage_10px.png",
|
||||||
fap_icon_assets="assets",
|
fap_icon_assets="assets",
|
||||||
fap_category="USB",
|
fap_category="USB",
|
||||||
|
|||||||
@@ -478,5 +478,4 @@ MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn)
|
|||||||
|
|
||||||
void mass_storage_usb_stop(MassStorageUsb* mass) {
|
void mass_storage_usb_stop(MassStorageUsb* mass) {
|
||||||
furi_hal_usb_set_config(mass->usb_prev, NULL);
|
furi_hal_usb_set_config(mass->usb_prev, NULL);
|
||||||
// freed by usb_deinit asynchronously from usb thread
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
#include <gui/modules/loading.h>
|
#include <gui/modules/loading.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include "views/mass_storage_view.h"
|
#include "views/mass_storage_view.h"
|
||||||
|
#include <mass_storage_icons.h>
|
||||||
|
#include <assets_icons.h>
|
||||||
|
|
||||||
#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
||||||
#define MASS_STORAGE_APP_EXTENSION ".img"
|
#define MASS_STORAGE_APP_EXTENSION ".img"
|
||||||
@@ -33,16 +35,18 @@ struct MassStorageApp {
|
|||||||
Popup* popup;
|
Popup* popup;
|
||||||
Loading* loading;
|
Loading* loading;
|
||||||
|
|
||||||
uint64_t create_image_max;
|
|
||||||
uint8_t create_image_size;
|
|
||||||
char create_image_name[MASS_STORAGE_FILE_NAME_LEN];
|
|
||||||
|
|
||||||
FuriString* file_path;
|
FuriString* file_path;
|
||||||
File* file;
|
File* file;
|
||||||
MassStorage* mass_storage_view;
|
MassStorage* mass_storage_view;
|
||||||
|
|
||||||
FuriMutex* usb_mutex;
|
FuriMutex* usb_mutex;
|
||||||
MassStorageUsb* usb;
|
MassStorageUsb* usb;
|
||||||
|
|
||||||
|
uint64_t create_image_max;
|
||||||
|
uint8_t create_image_size;
|
||||||
|
char create_image_name[MASS_STORAGE_FILE_NAME_LEN];
|
||||||
|
|
||||||
|
uint32_t bytes_read, bytes_written;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -54,4 +58,11 @@ typedef enum {
|
|||||||
MassStorageAppViewWork,
|
MassStorageAppViewWork,
|
||||||
} MassStorageAppView;
|
} MassStorageAppView;
|
||||||
|
|
||||||
|
enum MassStorageCustomEvent {
|
||||||
|
// Reserve first 100 events for button types and indexes, starting from 0
|
||||||
|
MassStorageCustomEventReserved = 100,
|
||||||
|
|
||||||
|
MassStorageCustomEventEject,
|
||||||
|
};
|
||||||
|
|
||||||
void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);
|
void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ void mass_storage_scene_create_image_on_enter(void* context) {
|
|||||||
variable_item_list_set_enter_callback(
|
variable_item_list_set_enter_callback(
|
||||||
var_item_list, mass_storage_scene_create_image_var_item_list_callback, app);
|
var_item_list, mass_storage_scene_create_image_var_item_list_callback, app);
|
||||||
|
|
||||||
variable_item_list_set_header(var_item_list, "Create Disc Image");
|
variable_item_list_set_header(var_item_list, "Create Disk Image");
|
||||||
|
|
||||||
variable_item_list_set_selected_item(
|
variable_item_list_set_selected_item(
|
||||||
var_item_list,
|
var_item_list,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "../mass_storage_app_i.h"
|
#include "../mass_storage_app_i.h"
|
||||||
#include "furi_hal_power.h"
|
#include "furi_hal_power.h"
|
||||||
#include <mass_storage_icons.h>
|
|
||||||
|
|
||||||
static bool mass_storage_file_select(MassStorageApp* mass_storage) {
|
static bool mass_storage_file_select(MassStorageApp* mass_storage) {
|
||||||
furi_assert(mass_storage);
|
furi_assert(mass_storage);
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ void mass_storage_scene_start_on_enter(void* context) {
|
|||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Select Disc Image",
|
"Select Disk Image",
|
||||||
MassStorageSceneFileSelect,
|
MassStorageSceneFileSelect,
|
||||||
mass_storage_scene_start_submenu_callback,
|
mass_storage_scene_start_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Create Disc Image",
|
"Create Disk Image",
|
||||||
MassStorageSceneCreateImage,
|
MassStorageSceneCreateImage,
|
||||||
mass_storage_scene_start_submenu_callback,
|
mass_storage_scene_start_submenu_callback,
|
||||||
app);
|
app);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ static bool file_read(
|
|||||||
uint16_t clamp = MIN(out_cap, count * SCSI_BLOCK_SIZE);
|
uint16_t clamp = MIN(out_cap, count * SCSI_BLOCK_SIZE);
|
||||||
*out_len = storage_file_read(app->file, out, clamp);
|
*out_len = storage_file_read(app->file, out, clamp);
|
||||||
FURI_LOG_T(TAG, "%lu/%lu", *out_len, count * SCSI_BLOCK_SIZE);
|
FURI_LOG_T(TAG, "%lu/%lu", *out_len, count * SCSI_BLOCK_SIZE);
|
||||||
|
app->bytes_read += *out_len;
|
||||||
return *out_len == clamp;
|
return *out_len == clamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, ui
|
|||||||
FURI_LOG_W(TAG, "seek failed");
|
FURI_LOG_W(TAG, "seek failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
app->bytes_written += len;
|
||||||
return storage_file_write(app->file, buf, len) == len;
|
return storage_file_write(app->file, buf, len) == len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,24 +48,23 @@ static uint32_t file_num_blocks(void* ctx) {
|
|||||||
static void file_eject(void* ctx) {
|
static void file_eject(void* ctx) {
|
||||||
MassStorageApp* app = ctx;
|
MassStorageApp* app = ctx;
|
||||||
FURI_LOG_D(TAG, "EJECT");
|
FURI_LOG_D(TAG, "EJECT");
|
||||||
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventEject);
|
||||||
mass_storage_usb_stop(app->usb);
|
|
||||||
app->usb = NULL;
|
|
||||||
furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) {
|
bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||||
MassStorageApp* app = context;
|
MassStorageApp* app = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
if(event.type == SceneManagerEventTypeTick) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
bool ejected;
|
if(event.event == MassStorageCustomEventEject) {
|
||||||
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
ejected = app->usb == NULL;
|
app->scene_manager, MassStorageSceneFileSelect);
|
||||||
furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk);
|
if(!consumed) {
|
||||||
if(ejected) {
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
scene_manager_previous_scene(app->scene_manager);
|
app->scene_manager, MassStorageSceneStart);
|
||||||
consumed = true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeTick) {
|
||||||
|
mass_storage_set_stats(app->mass_storage_view, app->bytes_read, app->bytes_written);
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
app->scene_manager, MassStorageSceneFileSelect);
|
app->scene_manager, MassStorageSceneFileSelect);
|
||||||
@@ -77,6 +78,7 @@ bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
void mass_storage_scene_work_on_enter(void* context) {
|
void mass_storage_scene_work_on_enter(void* context) {
|
||||||
MassStorageApp* app = 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))) {
|
if(!storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
|
||||||
scene_manager_search_and_switch_to_previous_scene(
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
|||||||
@@ -1,16 +1,30 @@
|
|||||||
#include "mass_storage_view.h"
|
#include "mass_storage_view.h"
|
||||||
|
#include "../mass_storage_app_i.h"
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
#include <mass_storage_icons.h>
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
struct MassStorage {
|
struct MassStorage {
|
||||||
View* view;
|
View* view;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FuriString* file_name;
|
FuriString *file_name, *status_string;
|
||||||
|
uint32_t read_speed, write_speed;
|
||||||
|
uint32_t bytes_read, bytes_written;
|
||||||
|
uint32_t update_time;
|
||||||
} MassStorageModel;
|
} MassStorageModel;
|
||||||
|
|
||||||
|
static void append_suffixed_byte_count(FuriString* string, uint32_t count) {
|
||||||
|
if(count < 1024) {
|
||||||
|
furi_string_cat_printf(string, "%luB", count);
|
||||||
|
} else if(count < 1024 * 1024) {
|
||||||
|
furi_string_cat_printf(string, "%luK", count / 1024);
|
||||||
|
} else if(count < 1024 * 1024 * 1024) {
|
||||||
|
furi_string_cat_printf(string, "%.3fM", (double)count / (1024 * 1024));
|
||||||
|
} else {
|
||||||
|
furi_string_cat_printf(string, "%.3fG", (double)count / (1024 * 1024 * 1024));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
|
static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
|
||||||
MassStorageModel* model = _model;
|
MassStorageModel* model = _model;
|
||||||
|
|
||||||
@@ -20,10 +34,28 @@ static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
|
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
|
||||||
|
|
||||||
elements_string_fit_width(canvas, model->file_name, 87 - 2);
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
canvas_draw_str(canvas, 16, 27, "Disc image:");
|
elements_string_fit_width(canvas, model->file_name, 89 - 2);
|
||||||
canvas_draw_str(canvas, 16, 40, furi_string_get_cstr(model->file_name));
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 50, 23, AlignCenter, AlignBottom, furi_string_get_cstr(model->file_name));
|
||||||
|
|
||||||
|
furi_string_set_str(model->status_string, "R:");
|
||||||
|
append_suffixed_byte_count(model->status_string, model->bytes_read);
|
||||||
|
if(model->read_speed) {
|
||||||
|
furi_string_cat_str(model->status_string, "; ");
|
||||||
|
append_suffixed_byte_count(model->status_string, model->read_speed);
|
||||||
|
furi_string_cat_str(model->status_string, "ps");
|
||||||
|
}
|
||||||
|
canvas_draw_str(canvas, 12, 34, furi_string_get_cstr(model->status_string));
|
||||||
|
|
||||||
|
furi_string_set_str(model->status_string, "W:");
|
||||||
|
append_suffixed_byte_count(model->status_string, model->bytes_written);
|
||||||
|
if(model->write_speed) {
|
||||||
|
furi_string_cat_str(model->status_string, "; ");
|
||||||
|
append_suffixed_byte_count(model->status_string, model->write_speed);
|
||||||
|
furi_string_cat_str(model->status_string, "ps");
|
||||||
|
}
|
||||||
|
canvas_draw_str(canvas, 12, 44, furi_string_get_cstr(model->status_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
MassStorage* mass_storage_alloc() {
|
MassStorage* mass_storage_alloc() {
|
||||||
@@ -34,7 +66,10 @@ MassStorage* mass_storage_alloc() {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
mass_storage->view,
|
mass_storage->view,
|
||||||
MassStorageModel * model,
|
MassStorageModel * model,
|
||||||
{ model->file_name = furi_string_alloc(); },
|
{
|
||||||
|
model->file_name = furi_string_alloc();
|
||||||
|
model->status_string = furi_string_alloc();
|
||||||
|
},
|
||||||
false);
|
false);
|
||||||
view_set_context(mass_storage->view, mass_storage);
|
view_set_context(mass_storage->view, mass_storage);
|
||||||
view_set_draw_callback(mass_storage->view, mass_storage_draw_callback);
|
view_set_draw_callback(mass_storage->view, mass_storage_draw_callback);
|
||||||
@@ -47,7 +82,10 @@ void mass_storage_free(MassStorage* mass_storage) {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
mass_storage->view,
|
mass_storage->view,
|
||||||
MassStorageModel * model,
|
MassStorageModel * model,
|
||||||
{ furi_string_free(model->file_name); },
|
{
|
||||||
|
furi_string_free(model->file_name);
|
||||||
|
furi_string_free(model->status_string);
|
||||||
|
},
|
||||||
false);
|
false);
|
||||||
view_free(mass_storage->view);
|
view_free(mass_storage->view);
|
||||||
free(mass_storage);
|
free(mass_storage);
|
||||||
@@ -66,3 +104,19 @@ void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name) {
|
|||||||
{ furi_string_set(model->file_name, name); },
|
{ furi_string_set(model->file_name, name); },
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mass_storage_set_stats(MassStorage* mass_storage, uint32_t read, uint32_t written) {
|
||||||
|
with_view_model(
|
||||||
|
mass_storage->view,
|
||||||
|
MassStorageModel * model,
|
||||||
|
{
|
||||||
|
uint32_t now = furi_get_tick();
|
||||||
|
model->read_speed = (read - model->bytes_read) * 1000 / (now - model->update_time);
|
||||||
|
model->write_speed =
|
||||||
|
(written - model->bytes_written) * 1000 / (now - model->update_time);
|
||||||
|
model->bytes_read = read;
|
||||||
|
model->bytes_written = written;
|
||||||
|
model->update_time = now;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,3 +11,5 @@ void mass_storage_free(MassStorage* mass_storage);
|
|||||||
View* mass_storage_get_view(MassStorage* mass_storage);
|
View* mass_storage_get_view(MassStorage* mass_storage);
|
||||||
|
|
||||||
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name);
|
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name);
|
||||||
|
|
||||||
|
void mass_storage_set_stats(MassStorage* mass_storage, uint32_t read, uint32_t written);
|
||||||
|
|||||||
Reference in New Issue
Block a user