mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 10:08:36 -07:00
Update and uncomment mass storage
This commit is contained in:
@@ -1,12 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="mass_storage",
|
|
||||||
name="USB Mass Storage",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="mass_storage_app",
|
|
||||||
cdefines=["APP_MASS_STORAGE"],
|
|
||||||
requires=["gui"],
|
|
||||||
stack_size=1 * 1024,
|
|
||||||
order=2,
|
|
||||||
# fap_icon="",
|
|
||||||
fap_category="USB",
|
|
||||||
)
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#include "../mass_storage_app_i.h"
|
|
||||||
#include <assets_icons.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SubghzCustomEventErrorBack,
|
|
||||||
} MassStorageCustomEvent;
|
|
||||||
|
|
||||||
static void
|
|
||||||
mass_storage_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
MassStorageApp* app = context;
|
|
||||||
|
|
||||||
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
|
||||||
view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mass_storage_scene_error_on_enter(void* context) {
|
|
||||||
MassStorageApp* app = context;
|
|
||||||
|
|
||||||
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
|
|
||||||
|
|
||||||
widget_add_string_multiline_element(
|
|
||||||
app->widget,
|
|
||||||
81,
|
|
||||||
4,
|
|
||||||
AlignCenter,
|
|
||||||
AlignTop,
|
|
||||||
FontSecondary,
|
|
||||||
"No SD card or\napp data found.\nThis app requires a\nmass_storage/\ndirectory.");
|
|
||||||
|
|
||||||
widget_add_button_element(
|
|
||||||
app->widget, GuiButtonTypeLeft, "Back", mass_storage_scene_error_event_callback, app);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewError);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mass_storage_scene_error_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
MassStorageApp* app = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == SubghzCustomEventErrorBack) {
|
|
||||||
view_dispatcher_stop(app->view_dispatcher);
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mass_storage_scene_error_on_exit(void* context) {
|
|
||||||
MassStorageApp* app = context;
|
|
||||||
widget_reset(app->widget);
|
|
||||||
}
|
|
||||||
15
applications/external/mass_storage/application.fam
vendored
Normal file
15
applications/external/mass_storage/application.fam
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
App(
|
||||||
|
appid="mass_storage",
|
||||||
|
name="Mass Storage",
|
||||||
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
|
entry_point="mass_storage_app",
|
||||||
|
requires=[
|
||||||
|
"gui",
|
||||||
|
"dialogs",
|
||||||
|
],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
order=20,
|
||||||
|
fap_icon="assets/mass_storage_10px.png",
|
||||||
|
fap_icon_assets="assets",
|
||||||
|
fap_category="USB",
|
||||||
|
)
|
||||||
BIN
applications/external/mass_storage/assets/mass_storage_10px.png
vendored
Normal file
BIN
applications/external/mass_storage/assets/mass_storage_10px.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
@@ -1,5 +1,7 @@
|
|||||||
#include "mass_storage_scsi.h"
|
#include "mass_storage_scsi.h"
|
||||||
|
|
||||||
|
#include <core/log.h>
|
||||||
|
|
||||||
#define TAG "MassStorageSCSI"
|
#define TAG "MassStorageSCSI"
|
||||||
|
|
||||||
#define SCSI_TEST_UNIT_READY (0x00)
|
#define SCSI_TEST_UNIT_READY (0x00)
|
||||||
@@ -131,17 +133,17 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
|||||||
data[0] = (n_blocks - 1) >> 24;
|
data[0] = (n_blocks - 1) >> 24;
|
||||||
data[1] = (n_blocks - 1) >> 16;
|
data[1] = (n_blocks - 1) >> 16;
|
||||||
data[2] = (n_blocks - 1) >> 8;
|
data[2] = (n_blocks - 1) >> 8;
|
||||||
data[3] = (n_blocks - 1);
|
data[3] = (n_blocks - 1) & 0xFF;
|
||||||
data[4] = block_size >> 24;
|
data[4] = block_size >> 24;
|
||||||
data[5] = block_size >> 16;
|
data[5] = block_size >> 16;
|
||||||
data[6] = block_size >> 8;
|
data[6] = block_size >> 8;
|
||||||
data[7] = block_size;
|
data[7] = block_size & 0xFF;
|
||||||
*len = 8;
|
*len = 8;
|
||||||
scsi->tx_done = true;
|
scsi->tx_done = true;
|
||||||
return true;
|
return true;
|
||||||
}; break;
|
}; break;
|
||||||
case SCSI_MODE_SENSE_6: {
|
case SCSI_MODE_SENSE_6: {
|
||||||
FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %ld", cap);
|
FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
|
||||||
if(cap < 4) return false;
|
if(cap < 4) return false;
|
||||||
data[0] = 3; // mode data length (len - 1)
|
data[0] = 3; // mode data length (len - 1)
|
||||||
data[1] = 0; // medium type
|
data[1] = 0; // medium type
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
#define SCSI_BLOCK_SIZE (0x200u)
|
#define SCSI_BLOCK_SIZE (0x200UL)
|
||||||
|
|
||||||
#define SCSI_SK_ILLEGAL_REQUEST (5)
|
#define SCSI_SK_ILLEGAL_REQUEST (5)
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "mass_storage_usb.h"
|
#include "mass_storage_usb.h"
|
||||||
|
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#define TAG "MassStorageUsb"
|
#define TAG "MassStorageUsb"
|
||||||
@@ -7,8 +6,8 @@
|
|||||||
#define USB_MSC_RX_EP (0x01)
|
#define USB_MSC_RX_EP (0x01)
|
||||||
#define USB_MSC_TX_EP (0x82)
|
#define USB_MSC_TX_EP (0x82)
|
||||||
|
|
||||||
#define USB_MSC_RX_EP_SIZE (64)
|
#define USB_MSC_RX_EP_SIZE (64UL)
|
||||||
#define USB_MSC_TX_EP_SIZE (64u)
|
#define USB_MSC_TX_EP_SIZE (64UL)
|
||||||
|
|
||||||
#define USB_MSC_BOT_GET_MAX_LUN (0xFE)
|
#define USB_MSC_BOT_GET_MAX_LUN (0xFE)
|
||||||
#define USB_MSC_BOT_RESET (0xFF)
|
#define USB_MSC_BOT_RESET (0xFF)
|
||||||
@@ -23,7 +22,7 @@
|
|||||||
|
|
||||||
// must be SCSI_BLOCK_SIZE aligned
|
// must be SCSI_BLOCK_SIZE aligned
|
||||||
// larger than 0x10000 exceeds size_t, storage_file_* ops fail
|
// larger than 0x10000 exceeds size_t, storage_file_* ops fail
|
||||||
#define USB_MSC_BUF_MAX (0x10000u - SCSI_BLOCK_SIZE)
|
#define USB_MSC_BUF_MAX (0x10000UL - SCSI_BLOCK_SIZE)
|
||||||
|
|
||||||
static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg);
|
static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg);
|
||||||
static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
||||||
@@ -79,11 +78,10 @@ static int32_t mass_thread_worker(void* context) {
|
|||||||
StateWriteCSW,
|
StateWriteCSW,
|
||||||
} state = StateReadCBW;
|
} state = StateReadCBW;
|
||||||
while(true) {
|
while(true) {
|
||||||
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriFlagWaitAny);
|
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||||
if(flags & EventExit) {
|
if(flags & EventExit) {
|
||||||
FURI_LOG_I(TAG, "exit");
|
FURI_LOG_I(TAG, "exit");
|
||||||
free(buf);
|
break;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if(flags & EventReset) {
|
if(flags & EventReset) {
|
||||||
FURI_LOG_I(TAG, "reset");
|
FURI_LOG_I(TAG, "reset");
|
||||||
@@ -91,8 +89,10 @@ static int32_t mass_thread_worker(void* context) {
|
|||||||
scsi.asc = 0;
|
scsi.asc = 0;
|
||||||
memset(&cbw, 0, sizeof(cbw));
|
memset(&cbw, 0, sizeof(cbw));
|
||||||
memset(&csw, 0, sizeof(csw));
|
memset(&csw, 0, sizeof(csw));
|
||||||
free(buf);
|
if(buf) {
|
||||||
buf = NULL;
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
buf_len = buf_cap = buf_sent = 0;
|
buf_len = buf_cap = buf_sent = 0;
|
||||||
state = StateReadCBW;
|
state = StateReadCBW;
|
||||||
}
|
}
|
||||||
@@ -138,8 +138,10 @@ static int32_t mass_thread_worker(void* context) {
|
|||||||
}
|
}
|
||||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||||
if(buf_clamp > buf_cap) {
|
if(buf_clamp > buf_cap) {
|
||||||
// FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp);
|
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||||
free(buf);
|
if(buf) {
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
buf_cap = buf_clamp;
|
buf_cap = buf_clamp;
|
||||||
buf = malloc(buf_cap);
|
buf = malloc(buf_cap);
|
||||||
}
|
}
|
||||||
@@ -177,8 +179,10 @@ static int32_t mass_thread_worker(void* context) {
|
|||||||
}
|
}
|
||||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||||
if(buf_clamp > buf_cap) {
|
if(buf_clamp > buf_cap) {
|
||||||
// FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp);
|
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||||
free(buf);
|
if(buf) {
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
buf_cap = buf_clamp;
|
buf_cap = buf_clamp;
|
||||||
buf = malloc(buf_cap);
|
buf = malloc(buf_cap);
|
||||||
}
|
}
|
||||||
@@ -248,6 +252,10 @@ static int32_t mass_thread_worker(void* context) {
|
|||||||
break;
|
break;
|
||||||
} while(true);
|
} while(true);
|
||||||
}
|
}
|
||||||
|
if(buf) {
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control,
|
// needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control,
|
||||||
@@ -307,8 +315,8 @@ static void usb_suspend(usbd_device* dev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||||
UNUSED(event);
|
|
||||||
UNUSED(ep);
|
UNUSED(ep);
|
||||||
|
UNUSED(event);
|
||||||
MassStorageUsb* mass = mass_cur;
|
MassStorageUsb* mass = mass_cur;
|
||||||
if(!mass || mass->dev != dev) return;
|
if(!mass || mass->dev != dev) return;
|
||||||
furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx);
|
furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx);
|
||||||
@@ -21,31 +21,14 @@ static void mass_storage_app_tick_event_callback(void* context) {
|
|||||||
scene_manager_handle_tick_event(app->scene_manager);
|
scene_manager_handle_tick_event(app->scene_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mass_storage_check_assets(Storage* fs_api) {
|
|
||||||
File* dir = storage_file_alloc(fs_api);
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
if(storage_dir_open(dir, MASS_STORAGE_APP_PATH_FOLDER)) {
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
storage_dir_close(dir);
|
|
||||||
storage_file_free(dir);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
MassStorageApp* mass_storage_app_alloc(char* arg) {
|
MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||||
MassStorageApp* app = malloc(sizeof(MassStorageApp));
|
MassStorageApp* app = malloc(sizeof(MassStorageApp));
|
||||||
memset(app, 0, sizeof(MassStorageApp));
|
app->file_path = furi_string_alloc();
|
||||||
|
|
||||||
if(arg != NULL) {
|
if(arg != NULL) {
|
||||||
FuriString* filename = furi_string_alloc_set(arg);
|
furi_string_set_str(app->file_path, arg);
|
||||||
if(furi_string_start_with_str(filename, MASS_STORAGE_APP_PATH_FOLDER)) {
|
} else {
|
||||||
furi_string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1);
|
furi_string_set_str(app->file_path, MASS_STORAGE_APP_PATH_FOLDER);
|
||||||
}
|
|
||||||
strlcpy(app->file_name, furi_string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN);
|
|
||||||
furi_string_free(filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
@@ -53,8 +36,6 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
|||||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|
||||||
storage_simply_mkdir(app->fs_api, MASS_STORAGE_APP_PATH_FOLDER);
|
|
||||||
|
|
||||||
app->view_dispatcher = view_dispatcher_alloc();
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
|
||||||
@@ -68,11 +49,6 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
|||||||
view_dispatcher_set_navigation_event_callback(
|
view_dispatcher_set_navigation_event_callback(
|
||||||
app->view_dispatcher, mass_storage_app_back_event_callback);
|
app->view_dispatcher, mass_storage_app_back_event_callback);
|
||||||
|
|
||||||
// Custom Widget
|
|
||||||
app->widget = widget_alloc();
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, MassStorageAppViewError, widget_get_view(app->widget));
|
|
||||||
|
|
||||||
app->mass_storage_view = mass_storage_alloc();
|
app->mass_storage_view = mass_storage_alloc();
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher,
|
app->view_dispatcher,
|
||||||
@@ -81,12 +57,10 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
|||||||
|
|
||||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
|
||||||
if(*app->file_name != '\0') {
|
if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
|
||||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
|
scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
|
||||||
} else if(mass_storage_check_assets(app->fs_api)) {
|
|
||||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
|
|
||||||
} else {
|
} else {
|
||||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneError);
|
scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
@@ -99,14 +73,12 @@ void mass_storage_app_free(MassStorageApp* app) {
|
|||||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork);
|
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork);
|
||||||
mass_storage_free(app->mass_storage_view);
|
mass_storage_free(app->mass_storage_view);
|
||||||
|
|
||||||
// Custom Widget
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewError);
|
|
||||||
widget_free(app->widget);
|
|
||||||
|
|
||||||
// View dispatcher
|
// View dispatcher
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
scene_manager_free(app->scene_manager);
|
scene_manager_free(app->scene_manager);
|
||||||
|
|
||||||
|
furi_string_free(app->file_path);
|
||||||
|
|
||||||
// Close records
|
// Close records
|
||||||
furi_record_close(RECORD_GUI);
|
furi_record_close(RECORD_GUI);
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "views/mass_storage_view.h"
|
#include "views/mass_storage_view.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 ".iso"
|
#define MASS_STORAGE_APP_EXTENSION ".img"
|
||||||
#define MASS_STORAGE_FILE_NAME_LEN 40
|
#define MASS_STORAGE_FILE_NAME_LEN 40
|
||||||
|
|
||||||
struct MassStorageApp {
|
struct MassStorageApp {
|
||||||
@@ -29,7 +29,7 @@ struct MassStorageApp {
|
|||||||
DialogsApp* dialogs;
|
DialogsApp* dialogs;
|
||||||
Widget* widget;
|
Widget* widget;
|
||||||
|
|
||||||
char file_name[MASS_STORAGE_FILE_NAME_LEN];
|
FuriString* file_path;
|
||||||
File* file;
|
File* file;
|
||||||
MassStorage* mass_storage_view;
|
MassStorage* mass_storage_view;
|
||||||
|
|
||||||
@@ -39,6 +39,5 @@ struct MassStorageApp {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MassStorageAppViewError,
|
MassStorageAppViewError,
|
||||||
MassStorageAppViewFileSelect,
|
|
||||||
MassStorageAppViewWork,
|
MassStorageAppViewWork,
|
||||||
} MassStorageAppView;
|
} MassStorageAppView;
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
ADD_SCENE(mass_storage, file_select, FileSelect)
|
ADD_SCENE(mass_storage, file_select, FileSelect)
|
||||||
ADD_SCENE(mass_storage, work, Work)
|
ADD_SCENE(mass_storage, work, Work)
|
||||||
ADD_SCENE(mass_storage, error, Error)
|
|
||||||
@@ -1,25 +1,18 @@
|
|||||||
#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);
|
||||||
|
|
||||||
FuriString* file_path = furi_string_alloc();
|
|
||||||
DialogsFileBrowserOptions browser_options;
|
DialogsFileBrowserOptions browser_options;
|
||||||
dialog_file_browser_set_basic_options(&browser_options, "*", NULL);
|
dialog_file_browser_set_basic_options(
|
||||||
|
&browser_options, MASS_STORAGE_APP_EXTENSION, &I_mass_storage_10px);
|
||||||
browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
|
browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
|
||||||
furi_string_set(file_path, MASS_STORAGE_APP_PATH_FOLDER);
|
|
||||||
|
|
||||||
bool res =
|
// Input events and views are managed by file_select
|
||||||
dialog_file_browser_show(mass_storage->dialogs, file_path, file_path, &browser_options);
|
bool res = dialog_file_browser_show(
|
||||||
|
mass_storage->dialogs, mass_storage->file_path, mass_storage->file_path, &browser_options);
|
||||||
if(res) {
|
|
||||||
strlcpy(
|
|
||||||
mass_storage->file_name,
|
|
||||||
furi_string_get_cstr(file_path),
|
|
||||||
sizeof(mass_storage->file_name));
|
|
||||||
}
|
|
||||||
furi_string_free(file_path);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "../mass_storage_app_i.h"
|
#include "../mass_storage_app_i.h"
|
||||||
#include "../views/mass_storage_view.h"
|
#include "../views/mass_storage_view.h"
|
||||||
#include "../helpers/mass_storage_usb.h"
|
#include "../helpers/mass_storage_usb.h"
|
||||||
|
#include <lib/toolbox/path.h>
|
||||||
|
|
||||||
#define TAG "MassStorageSceneWork"
|
#define TAG "MassStorageSceneWork"
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, ui
|
|||||||
MassStorageApp* app = ctx;
|
MassStorageApp* app = ctx;
|
||||||
// FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len);
|
// FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len);
|
||||||
if(len != count * SCSI_BLOCK_SIZE) {
|
if(len != count * SCSI_BLOCK_SIZE) {
|
||||||
FURI_LOG_W(TAG, "bad write params count=%d len=%ld", count, len);
|
FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
|
if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
|
||||||
@@ -69,13 +70,15 @@ void mass_storage_scene_work_on_enter(void* context) {
|
|||||||
app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
|
|
||||||
FuriString* file_name = furi_string_alloc();
|
FuriString* file_name = furi_string_alloc();
|
||||||
|
path_extract_filename(app->file_path, file_name, true);
|
||||||
|
|
||||||
mass_storage_set_file_name(app->mass_storage_view, app->file_name);
|
mass_storage_set_file_name(app->mass_storage_view, file_name);
|
||||||
furi_string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name);
|
|
||||||
app->file = storage_file_alloc(app->fs_api);
|
app->file = storage_file_alloc(app->fs_api);
|
||||||
furi_assert(storage_file_open(
|
furi_assert(storage_file_open(
|
||||||
app->file, furi_string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING));
|
app->file,
|
||||||
furi_string_free(file_name);
|
furi_string_get_cstr(app->file_path),
|
||||||
|
FSAM_READ | FSAM_WRITE,
|
||||||
|
FSOM_OPEN_EXISTING));
|
||||||
|
|
||||||
SCSIDeviceFunc fn = {
|
SCSIDeviceFunc fn = {
|
||||||
.ctx = app,
|
.ctx = app,
|
||||||
@@ -84,7 +87,10 @@ void mass_storage_scene_work_on_enter(void* context) {
|
|||||||
.num_blocks = file_num_blocks,
|
.num_blocks = file_num_blocks,
|
||||||
.eject = file_eject,
|
.eject = file_eject,
|
||||||
};
|
};
|
||||||
app->usb = mass_storage_usb_start(app->file_name, fn);
|
|
||||||
|
app->usb = mass_storage_usb_start(furi_string_get_cstr(file_name), fn);
|
||||||
|
|
||||||
|
furi_string_free(file_name);
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork);
|
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "mass_storage_view.h"
|
#include "mass_storage_view.h"
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
#include <mass_storage_icons.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
|
||||||
struct MassStorage {
|
struct MassStorage {
|
||||||
@@ -7,21 +8,22 @@ struct MassStorage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char* file_name;
|
FuriString* file_name;
|
||||||
} MassStorageModel;
|
} MassStorageModel;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
FuriString* disp_str = furi_string_alloc_set(model->file_name);
|
canvas_draw_icon(canvas, 8, 14, &I_Drive_112x35);
|
||||||
elements_string_fit_width(canvas, disp_str, 128 - 2);
|
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
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, 2, 8, furi_string_get_cstr(disp_str));
|
canvas_draw_str(canvas, 12, 25, "Disc image:");
|
||||||
furi_string_reset(disp_str);
|
canvas_draw_str(canvas, 12, 40, furi_string_get_cstr(model->file_name));
|
||||||
|
|
||||||
canvas_draw_icon(canvas, 40, 20, &I_UsbTree_48x22);
|
|
||||||
|
|
||||||
furi_string_free(disp_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MassStorage* mass_storage_alloc() {
|
MassStorage* mass_storage_alloc() {
|
||||||
@@ -29,6 +31,11 @@ MassStorage* mass_storage_alloc() {
|
|||||||
|
|
||||||
mass_storage->view = view_alloc();
|
mass_storage->view = view_alloc();
|
||||||
view_allocate_model(mass_storage->view, ViewModelTypeLocking, sizeof(MassStorageModel));
|
view_allocate_model(mass_storage->view, ViewModelTypeLocking, sizeof(MassStorageModel));
|
||||||
|
with_view_model(
|
||||||
|
mass_storage->view,
|
||||||
|
MassStorageModel * model,
|
||||||
|
{ model->file_name = furi_string_alloc(); },
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -37,6 +44,11 @@ MassStorage* mass_storage_alloc() {
|
|||||||
|
|
||||||
void mass_storage_free(MassStorage* mass_storage) {
|
void mass_storage_free(MassStorage* mass_storage) {
|
||||||
furi_assert(mass_storage);
|
furi_assert(mass_storage);
|
||||||
|
with_view_model(
|
||||||
|
mass_storage->view,
|
||||||
|
MassStorageModel * model,
|
||||||
|
{ furi_string_free(model->file_name); },
|
||||||
|
false);
|
||||||
view_free(mass_storage->view);
|
view_free(mass_storage->view);
|
||||||
free(mass_storage);
|
free(mass_storage);
|
||||||
}
|
}
|
||||||
@@ -46,8 +58,11 @@ View* mass_storage_get_view(MassStorage* mass_storage) {
|
|||||||
return mass_storage->view;
|
return mass_storage->view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mass_storage_set_file_name(MassStorage* mass_storage, char* name) {
|
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name) {
|
||||||
furi_assert(name);
|
furi_assert(name);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
mass_storage->view, MassStorageModel * model, { model->file_name = name; }, true);
|
mass_storage->view,
|
||||||
|
MassStorageModel * model,
|
||||||
|
{ furi_string_set(model->file_name, name); },
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
@@ -10,4 +10,4 @@ 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, char* name);
|
void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name);
|
||||||
Reference in New Issue
Block a user