mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-10 05:59:08 -07:00
Merge upstream mass storage changes
This commit is contained in:
@@ -7,7 +7,8 @@
|
||||
#define SCSI_TEST_UNIT_READY (0x00)
|
||||
#define SCSI_REQUEST_SENSE (0x03)
|
||||
#define SCSI_INQUIRY (0x12)
|
||||
#define SCSI_READ_CAPACITY_6 (0x25)
|
||||
#define SCSI_READ_FORMAT_CAPACITIES (0x23)
|
||||
#define SCSI_READ_CAPACITY_10 (0x25)
|
||||
#define SCSI_MODE_SENSE_6 (0x1A)
|
||||
#define SCSI_READ_10 (0x28)
|
||||
#define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E)
|
||||
@@ -20,7 +21,7 @@ bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) {
|
||||
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
|
||||
return false;
|
||||
}
|
||||
// FURI_LOG_I(TAG, "START %02x", cmd[0]);
|
||||
FURI_LOG_T(TAG, "START %02X", cmd[0]);
|
||||
scsi->cmd = cmd;
|
||||
scsi->cmd_len = len;
|
||||
scsi->rx_done = false;
|
||||
@@ -30,14 +31,14 @@ bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) {
|
||||
if(len < 10) return false;
|
||||
scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
|
||||
scsi->write_10.count = cmd[7] << 8 | cmd[8];
|
||||
FURI_LOG_I(TAG, "SCSI_WRITE_10 %08lx %04x", scsi->write_10.lba, scsi->write_10.count);
|
||||
FURI_LOG_D(TAG, "SCSI_WRITE_10 %08lX %04X", scsi->write_10.lba, scsi->write_10.count);
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_READ_10: {
|
||||
if(len < 10) return false;
|
||||
scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
|
||||
scsi->read_10.count = cmd[7] << 8 | cmd[8];
|
||||
FURI_LOG_I(TAG, "SCSI_READ_10 %08lx %04x", scsi->read_10.lba, scsi->read_10.count);
|
||||
FURI_LOG_D(TAG, "SCSI_READ_10 %08lX %04X", scsi->read_10.lba, scsi->read_10.count);
|
||||
return true;
|
||||
}; break;
|
||||
}
|
||||
@@ -45,7 +46,7 @@ bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) {
|
||||
}
|
||||
|
||||
bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
|
||||
// FURI_LOG_I(TAG, "RX %02x len %d", scsi->cmd[0], len);
|
||||
FURI_LOG_T(TAG, "RX %02X len %lu", scsi->cmd[0], len);
|
||||
if(scsi->rx_done) return false;
|
||||
switch(scsi->cmd[0]) {
|
||||
case SCSI_WRITE_10: {
|
||||
@@ -61,7 +62,7 @@ bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
|
||||
return result;
|
||||
}; break;
|
||||
default: {
|
||||
FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02x", scsi->cmd[0]);
|
||||
FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02X", scsi->cmd[0]);
|
||||
scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
|
||||
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
|
||||
return false;
|
||||
@@ -70,11 +71,11 @@ bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
|
||||
}
|
||||
|
||||
bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap) {
|
||||
// FURI_LOG_I(TAG, "TX %02x cap %d", scsi->cmd[0], cap);
|
||||
FURI_LOG_T(TAG, "TX %02X cap %lu", scsi->cmd[0], cap);
|
||||
if(scsi->tx_done) return false;
|
||||
switch(scsi->cmd[0]) {
|
||||
case SCSI_REQUEST_SENSE: {
|
||||
FURI_LOG_I(TAG, "SCSI_REQUEST_SENSE");
|
||||
FURI_LOG_D(TAG, "SCSI_REQUEST_SENSE");
|
||||
if(cap < 18) return false;
|
||||
memset(data, 0, cap);
|
||||
data[0] = 0x70; // fixed format sense data
|
||||
@@ -102,31 +103,73 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_INQUIRY: {
|
||||
FURI_LOG_I(TAG, "SCSI_INQUIRY");
|
||||
FURI_LOG_D(TAG, "SCSI_INQUIRY");
|
||||
if(scsi->cmd_len < 5) return false;
|
||||
|
||||
if(cap < 36) return false;
|
||||
|
||||
bool evpd = scsi->cmd[1] & 1;
|
||||
uint8_t page_code = scsi->cmd[2];
|
||||
// uint16_t alloc_len = scsi->cmd[3] << 8 | scsi->cmd[4];
|
||||
if(evpd) return false;
|
||||
if(page_code) return false;
|
||||
data[0] = 0x00; // device type: direct access block device
|
||||
data[1] = 0x80; // removable: true
|
||||
data[2] = 0x04; // version
|
||||
data[3] = 0x02; // response data format
|
||||
data[4] = 31; // additional length (len - 5)
|
||||
data[5] = 0; // flags
|
||||
data[6] = 0; // flags
|
||||
data[7] = 0; // flags
|
||||
memcpy(data + 8, "Flipper ", 8); // vendor id
|
||||
memcpy(data + 16, "Mass Storage ", 16); // product id
|
||||
memcpy(data + 32, "0001", 4); // product revision level
|
||||
*len = 36;
|
||||
if(evpd == 0) {
|
||||
if(page_code != 0) return false;
|
||||
|
||||
data[0] = 0x00; // device type: direct access block device
|
||||
data[1] = 0x80; // removable: true
|
||||
data[2] = 0x04; // version
|
||||
data[3] = 0x02; // response data format
|
||||
data[4] = 31; // additional length (len - 5)
|
||||
data[5] = 0; // flags
|
||||
data[6] = 0; // flags
|
||||
data[7] = 0; // flags
|
||||
memcpy(data + 8, "Flipper ", 8); // vendor id
|
||||
memcpy(data + 16, "Mass Storage ", 16); // product id
|
||||
memcpy(data + 32, "0001", 4); // product revision level
|
||||
*len = 36;
|
||||
scsi->tx_done = true;
|
||||
return true;
|
||||
} else {
|
||||
if(page_code != 0x80) {
|
||||
FURI_LOG_W(TAG, "Unsupported VPD code %02X", page_code);
|
||||
return false;
|
||||
}
|
||||
data[0] = 0x00;
|
||||
data[1] = 0x80;
|
||||
data[2] = 0x00;
|
||||
data[3] = 0x01; // Serial len
|
||||
data[4] = '0';
|
||||
*len = 5;
|
||||
scsi->tx_done = true;
|
||||
return true;
|
||||
}
|
||||
}; break;
|
||||
case SCSI_READ_FORMAT_CAPACITIES: {
|
||||
FURI_LOG_D(TAG, "SCSI_READ_FORMAT_CAPACITIES");
|
||||
if(cap < 12) {
|
||||
return false;
|
||||
}
|
||||
uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
|
||||
uint32_t block_size = SCSI_BLOCK_SIZE;
|
||||
// Capacity List Header
|
||||
data[0] = 0;
|
||||
data[1] = 0;
|
||||
data[2] = 0;
|
||||
data[3] = 8;
|
||||
|
||||
// Capacity Descriptor
|
||||
data[4] = (n_blocks - 1) >> 24;
|
||||
data[5] = (n_blocks - 1) >> 16;
|
||||
data[6] = (n_blocks - 1) >> 8;
|
||||
data[7] = (n_blocks - 1) & 0xFF;
|
||||
data[8] = 0x02; // Formatted media
|
||||
data[9] = block_size >> 16;
|
||||
data[10] = block_size >> 8;
|
||||
data[11] = block_size & 0xFF;
|
||||
*len = 12;
|
||||
scsi->tx_done = true;
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_READ_CAPACITY_6: {
|
||||
FURI_LOG_I(TAG, "SCSI_READ_CAPACITY_6");
|
||||
case SCSI_READ_CAPACITY_10: {
|
||||
FURI_LOG_D(TAG, "SCSI_READ_CAPACITY_10");
|
||||
if(cap < 8) return false;
|
||||
uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
|
||||
uint32_t block_size = SCSI_BLOCK_SIZE;
|
||||
@@ -143,7 +186,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_MODE_SENSE_6: {
|
||||
FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
|
||||
FURI_LOG_D(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
|
||||
if(cap < 4) return false;
|
||||
data[0] = 3; // mode data length (len - 1)
|
||||
data[1] = 0; // medium type
|
||||
@@ -167,7 +210,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
||||
return result;
|
||||
}; break;
|
||||
default: {
|
||||
FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02x", scsi->cmd[0]);
|
||||
FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02X", scsi->cmd[0]);
|
||||
scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
|
||||
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
|
||||
return false;
|
||||
@@ -176,7 +219,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
|
||||
}
|
||||
|
||||
bool scsi_cmd_end(SCSISession* scsi) {
|
||||
// FURI_LOG_I(TAG, "END %02x", scsi->cmd[0]);
|
||||
FURI_LOG_T(TAG, "END %02X", scsi->cmd[0]);
|
||||
uint8_t* cmd = scsi->cmd;
|
||||
uint8_t len = scsi->cmd_len;
|
||||
scsi->cmd = NULL;
|
||||
@@ -187,33 +230,34 @@ bool scsi_cmd_end(SCSISession* scsi) {
|
||||
|
||||
case SCSI_REQUEST_SENSE:
|
||||
case SCSI_INQUIRY:
|
||||
case SCSI_READ_CAPACITY_6:
|
||||
case SCSI_READ_FORMAT_CAPACITIES:
|
||||
case SCSI_READ_CAPACITY_10:
|
||||
case SCSI_MODE_SENSE_6:
|
||||
case SCSI_READ_10:
|
||||
return scsi->tx_done;
|
||||
|
||||
case SCSI_TEST_UNIT_READY: {
|
||||
FURI_LOG_I(TAG, "SCSI_TEST_UNIT_READY");
|
||||
FURI_LOG_D(TAG, "SCSI_TEST_UNIT_READY");
|
||||
return true;
|
||||
}; break;
|
||||
case SCSI_PREVENT_MEDIUM_REMOVAL: {
|
||||
if(len < 6) return false;
|
||||
bool prevent = cmd[5];
|
||||
FURI_LOG_I(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent);
|
||||
FURI_LOG_D(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent);
|
||||
return !prevent;
|
||||
}; break;
|
||||
case SCSI_START_STOP_UNIT: {
|
||||
if(len < 6) return false;
|
||||
bool eject = (cmd[4] & 2) != 0;
|
||||
bool start = (cmd[4] & 1) != 0;
|
||||
FURI_LOG_I(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start);
|
||||
FURI_LOG_D(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start);
|
||||
if(eject) {
|
||||
scsi->fn.eject(scsi->fn.ctx);
|
||||
}
|
||||
return true;
|
||||
}; break;
|
||||
default: {
|
||||
FURI_LOG_W(TAG, "unexpected scsi cmd=%02x", cmd[0]);
|
||||
FURI_LOG_W(TAG, "unexpected scsi cmd=%02X", cmd[0]);
|
||||
scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
|
||||
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
|
||||
return false;
|
||||
|
||||
@@ -44,6 +44,7 @@ typedef struct {
|
||||
uint8_t cmd_len;
|
||||
uint8_t cmd[16];
|
||||
} __attribute__((packed)) CBW;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sig;
|
||||
uint32_t tag;
|
||||
@@ -80,11 +81,11 @@ static int32_t mass_thread_worker(void* context) {
|
||||
while(true) {
|
||||
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||
if(flags & EventExit) {
|
||||
FURI_LOG_I(TAG, "exit");
|
||||
FURI_LOG_D(TAG, "exit");
|
||||
break;
|
||||
}
|
||||
if(flags & EventReset) {
|
||||
FURI_LOG_I(TAG, "reset");
|
||||
FURI_LOG_D(TAG, "reset");
|
||||
scsi.sk = 0;
|
||||
scsi.asc = 0;
|
||||
memset(&cbw, 0, sizeof(cbw));
|
||||
@@ -99,10 +100,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
if(flags & EventRxTx) do {
|
||||
switch(state) {
|
||||
case StateReadCBW: {
|
||||
// FURI_LOG_I(TAG, "StateReadCBW");
|
||||
FURI_LOG_T(TAG, "StateReadCBW");
|
||||
int32_t len = usbd_ep_read(dev, USB_MSC_RX_EP, &cbw, sizeof(cbw));
|
||||
if(len <= 0) {
|
||||
// FURI_LOG_I(TAG, "cbw not ready");
|
||||
FURI_LOG_T(TAG, "cbw not ready");
|
||||
break;
|
||||
}
|
||||
if(len != sizeof(cbw) || cbw.sig != CBW_SIG) {
|
||||
@@ -131,14 +132,14 @@ static int32_t mass_thread_worker(void* context) {
|
||||
continue;
|
||||
}; break;
|
||||
case StateReadData: {
|
||||
// FURI_LOG_I(TAG, "StateReadData %d/%d", buf_len, cbw.len);
|
||||
FURI_LOG_T(TAG, "StateReadData %lu/%lu", buf_len, cbw.len);
|
||||
if(!cbw.len) {
|
||||
state = StateBuildCSW;
|
||||
continue;
|
||||
}
|
||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||
if(buf_clamp > buf_cap) {
|
||||
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
if(buf) {
|
||||
free(buf);
|
||||
}
|
||||
@@ -149,10 +150,10 @@ static int32_t mass_thread_worker(void* context) {
|
||||
int32_t len =
|
||||
usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len);
|
||||
if(len < 0) {
|
||||
// FURI_LOG_I(TAG, "rx not ready %d", len);
|
||||
FURI_LOG_T(TAG, "rx not ready %ld", len);
|
||||
break;
|
||||
}
|
||||
// FURI_LOG_I(TAG, "clamp %ld len %d", buf_clamp, len);
|
||||
FURI_LOG_T(TAG, "clamp %lu len %ld", buf_clamp, len);
|
||||
buf_len += len;
|
||||
}
|
||||
if(buf_len == buf_clamp) {
|
||||
@@ -172,14 +173,14 @@ static int32_t mass_thread_worker(void* context) {
|
||||
continue;
|
||||
}; break;
|
||||
case StateWriteData: {
|
||||
// FURI_LOG_I(TAG, "StateWriteData %d", cbw.len);
|
||||
FURI_LOG_T(TAG, "StateWriteData %lu", cbw.len);
|
||||
if(!cbw.len) {
|
||||
state = StateBuildCSW;
|
||||
continue;
|
||||
}
|
||||
uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
|
||||
if(buf_clamp > buf_cap) {
|
||||
FURI_LOG_I(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
|
||||
if(buf) {
|
||||
free(buf);
|
||||
}
|
||||
@@ -198,7 +199,7 @@ static int32_t mass_thread_worker(void* context) {
|
||||
buf + buf_sent,
|
||||
MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent));
|
||||
if(len < 0) {
|
||||
// FURI_LOG_I(TAG, "tx not ready %d", len);
|
||||
FURI_LOG_T(TAG, "tx not ready %ld", len);
|
||||
break;
|
||||
}
|
||||
buf_sent += len;
|
||||
@@ -210,7 +211,7 @@ static int32_t mass_thread_worker(void* context) {
|
||||
continue;
|
||||
}; break;
|
||||
case StateBuildCSW: {
|
||||
// FURI_LOG_I(TAG, "StateBuildCSW");
|
||||
FURI_LOG_T(TAG, "StateBuildCSW");
|
||||
csw.sig = CSW_SIG;
|
||||
csw.tag = cbw.tag;
|
||||
if(scsi_cmd_end(&scsi)) {
|
||||
@@ -223,7 +224,7 @@ static int32_t mass_thread_worker(void* context) {
|
||||
continue;
|
||||
}; break;
|
||||
case StateWriteCSW: {
|
||||
// FURI_LOG_I(TAG, "StateWriteCSW");
|
||||
FURI_LOG_T(TAG, "StateWriteCSW");
|
||||
if(csw.status) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
@@ -235,7 +236,7 @@ static int32_t mass_thread_worker(void* context) {
|
||||
}
|
||||
int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw));
|
||||
if(len < 0) {
|
||||
// FURI_LOG_I(TAG, "csw not ready");
|
||||
FURI_LOG_T(TAG, "csw not ready");
|
||||
break;
|
||||
}
|
||||
if(len != sizeof(csw)) {
|
||||
|
||||
@@ -21,6 +21,19 @@ static void mass_storage_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewLoading);
|
||||
} else {
|
||||
// Restore default timer priority
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
MassStorageApp* app = malloc(sizeof(MassStorageApp));
|
||||
app->file_path = furi_string_alloc();
|
||||
@@ -36,7 +49,6 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->fs_api = furi_record_open(RECORD_STORAGE);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
@@ -76,6 +88,10 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MassStorageAppViewPopup, popup_get_view(app->popup));
|
||||
|
||||
app->loading = loading_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, MassStorageAppViewLoading, loading_get_view(app->loading));
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
scene_manager_set_scene_state(
|
||||
@@ -94,15 +110,18 @@ void mass_storage_app_free(MassStorageApp* app) {
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork);
|
||||
mass_storage_free(app->mass_storage_view);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewVarItemList);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewTextInput);
|
||||
text_input_free(app->text_input);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewPopup);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewLoading);
|
||||
|
||||
mass_storage_free(app->mass_storage_view);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
submenu_free(app->submenu);
|
||||
text_input_free(app->text_input);
|
||||
popup_free(app->popup);
|
||||
loading_free(app->loading);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
@@ -113,7 +132,6 @@ void mass_storage_app_free(MassStorageApp* app) {
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
free(app);
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <storage/storage.h>
|
||||
#include "views/mass_storage_view.h"
|
||||
|
||||
#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX
|
||||
#define MASS_STORAGE_APP_EXTENSION ".img"
|
||||
#define MASS_STORAGE_FILE_NAME_LEN 40
|
||||
|
||||
typedef enum {
|
||||
@@ -32,14 +32,12 @@ struct MassStorageApp {
|
||||
Storage* fs_api;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
MassStorage* mass_storage_view;
|
||||
VariableItemList* var_item_list;
|
||||
Submenu* submenu;
|
||||
TextInput* text_input;
|
||||
Popup* popup;
|
||||
Loading* loading;
|
||||
|
||||
uint32_t create_image_size;
|
||||
SizeUnit create_size_unit;
|
||||
@@ -47,16 +45,19 @@ struct MassStorageApp {
|
||||
|
||||
FuriString* file_path;
|
||||
File* file;
|
||||
MassStorage* mass_storage_view;
|
||||
|
||||
FuriMutex* usb_mutex;
|
||||
MassStorageUsb* usb;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MassStorageAppViewError,
|
||||
MassStorageAppViewWork,
|
||||
MassStorageAppViewVarItemList,
|
||||
MassStorageAppViewSubmenu,
|
||||
MassStorageAppViewTextInput,
|
||||
MassStorageAppViewPopup,
|
||||
MassStorageAppViewLoading,
|
||||
MassStorageAppViewWork,
|
||||
} MassStorageAppView;
|
||||
|
||||
void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);
|
||||
|
||||
@@ -100,14 +100,7 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
|
||||
scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName);
|
||||
break;
|
||||
case VarItemListIndexCreate: {
|
||||
popup_set_header(app->popup, "Creating Image...", 64, 32, AlignCenter, AlignCenter);
|
||||
popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom);
|
||||
popup_set_callback(app->popup, NULL);
|
||||
popup_set_context(app->popup, NULL);
|
||||
popup_set_timeout(app->popup, 0);
|
||||
popup_disable_timeout(app->popup);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup);
|
||||
|
||||
mass_storage_app_show_loading_popup(app, true);
|
||||
bool default_name = !strnlen(app->create_name, sizeof(app->create_name));
|
||||
if(default_name) {
|
||||
snprintf(
|
||||
@@ -117,7 +110,12 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
|
||||
app->create_image_size,
|
||||
size_unit_names[app->create_size_unit]);
|
||||
}
|
||||
furi_string_printf(app->file_path, APP_DATA_PATH("%s.img"), app->create_name);
|
||||
furi_string_printf(
|
||||
app->file_path,
|
||||
"%s/%s%s",
|
||||
MASS_STORAGE_APP_PATH_FOLDER,
|
||||
app->create_name,
|
||||
MASS_STORAGE_APP_EXTENSION);
|
||||
|
||||
app->file = storage_file_alloc(app->fs_api);
|
||||
const char* error = NULL;
|
||||
@@ -136,6 +134,7 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
}
|
||||
storage_file_free(app->file);
|
||||
mass_storage_app_show_loading_popup(app, false);
|
||||
|
||||
if(error) {
|
||||
popup_set_header(
|
||||
|
||||
@@ -13,7 +13,7 @@ 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, "Leave empty for default");
|
||||
text_input_set_header_text(text_input, "Image name, empty = default");
|
||||
|
||||
text_input_set_minimum_length(text_input, 0);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ void mass_storage_scene_start_on_enter(void* context) {
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Emulate Image",
|
||||
"Select Disc Image",
|
||||
MassStorageSceneFileSelect,
|
||||
mass_storage_scene_start_submenu_callback,
|
||||
app);
|
||||
|
||||
@@ -13,20 +13,20 @@ static bool file_read(
|
||||
uint32_t* out_len,
|
||||
uint32_t out_cap) {
|
||||
MassStorageApp* app = ctx;
|
||||
// FURI_LOG_I(TAG, "file_read lba=%08lx count=%04x out_cap=%04x", lba, count, out_cap);
|
||||
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_I(TAG, "%d/%d", *out_len, count * SCSI_BLOCK_SIZE);
|
||||
FURI_LOG_T(TAG, "%lu/%lu", *out_len, count * SCSI_BLOCK_SIZE);
|
||||
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_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len);
|
||||
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;
|
||||
@@ -45,7 +45,7 @@ static uint32_t file_num_blocks(void* ctx) {
|
||||
|
||||
static void file_eject(void* ctx) {
|
||||
MassStorageApp* app = ctx;
|
||||
FURI_LOG_I(TAG, "EJECT");
|
||||
FURI_LOG_D(TAG, "EJECT");
|
||||
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
mass_storage_usb_stop(app->usb);
|
||||
app->usb = NULL;
|
||||
@@ -54,19 +54,38 @@ static void file_eject(void* ctx) {
|
||||
|
||||
bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) {
|
||||
MassStorageApp* app = context;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeTick) {
|
||||
bool ejected;
|
||||
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
|
||||
ejected = app->usb == NULL;
|
||||
furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk);
|
||||
if(ejected) scene_manager_previous_scene(app->scene_manager);
|
||||
if(ejected) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
} 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 false;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void mass_storage_scene_work_on_enter(void* context) {
|
||||
MassStorageApp* app = context;
|
||||
|
||||
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();
|
||||
@@ -92,13 +111,18 @@ void mass_storage_scene_work_on_enter(void* context) {
|
||||
|
||||
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);
|
||||
|
||||
furi_mutex_free(app->usb_mutex);
|
||||
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;
|
||||
@@ -107,4 +131,5 @@ void mass_storage_scene_work_on_exit(void* context) {
|
||||
storage_file_free(app->file);
|
||||
app->file = NULL;
|
||||
}
|
||||
mass_storage_app_show_loading_popup(app, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user