Merge upstream mass storage changes

This commit is contained in:
Willy-JL
2023-08-03 14:40:51 +02:00
parent 6e7048f84c
commit f37f43a8bc
8 changed files with 167 additions and 79 deletions

View File

@@ -7,7 +7,8 @@
#define SCSI_TEST_UNIT_READY (0x00) #define SCSI_TEST_UNIT_READY (0x00)
#define SCSI_REQUEST_SENSE (0x03) #define SCSI_REQUEST_SENSE (0x03)
#define SCSI_INQUIRY (0x12) #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_MODE_SENSE_6 (0x1A)
#define SCSI_READ_10 (0x28) #define SCSI_READ_10 (0x28)
#define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E) #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; scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
return false; return false;
} }
// FURI_LOG_I(TAG, "START %02x", cmd[0]); FURI_LOG_T(TAG, "START %02X", cmd[0]);
scsi->cmd = cmd; scsi->cmd = cmd;
scsi->cmd_len = len; scsi->cmd_len = len;
scsi->rx_done = false; 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; if(len < 10) return false;
scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
scsi->write_10.count = cmd[7] << 8 | cmd[8]; 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; return true;
}; break; }; break;
case SCSI_READ_10: { case SCSI_READ_10: {
if(len < 10) return false; if(len < 10) return false;
scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
scsi->read_10.count = cmd[7] << 8 | cmd[8]; 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; return true;
}; break; }; 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) { 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; if(scsi->rx_done) return false;
switch(scsi->cmd[0]) { switch(scsi->cmd[0]) {
case SCSI_WRITE_10: { case SCSI_WRITE_10: {
@@ -61,7 +62,7 @@ bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
return result; return result;
}; break; }; break;
default: { 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->sk = SCSI_SK_ILLEGAL_REQUEST;
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
return false; 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) { 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; if(scsi->tx_done) return false;
switch(scsi->cmd[0]) { switch(scsi->cmd[0]) {
case SCSI_REQUEST_SENSE: { case SCSI_REQUEST_SENSE: {
FURI_LOG_I(TAG, "SCSI_REQUEST_SENSE"); FURI_LOG_D(TAG, "SCSI_REQUEST_SENSE");
if(cap < 18) return false; if(cap < 18) return false;
memset(data, 0, cap); memset(data, 0, cap);
data[0] = 0x70; // fixed format sense data data[0] = 0x70; // fixed format sense data
@@ -102,14 +103,16 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
return true; return true;
}; break; }; break;
case SCSI_INQUIRY: { case SCSI_INQUIRY: {
FURI_LOG_I(TAG, "SCSI_INQUIRY"); FURI_LOG_D(TAG, "SCSI_INQUIRY");
if(scsi->cmd_len < 5) return false; if(scsi->cmd_len < 5) return false;
if(cap < 36) return false; if(cap < 36) return false;
bool evpd = scsi->cmd[1] & 1; bool evpd = scsi->cmd[1] & 1;
uint8_t page_code = scsi->cmd[2]; uint8_t page_code = scsi->cmd[2];
// uint16_t alloc_len = scsi->cmd[3] << 8 | scsi->cmd[4]; if(evpd == 0) {
if(evpd) return false; if(page_code != 0) return false;
if(page_code) return false;
data[0] = 0x00; // device type: direct access block device data[0] = 0x00; // device type: direct access block device
data[1] = 0x80; // removable: true data[1] = 0x80; // removable: true
data[2] = 0x04; // version data[2] = 0x04; // version
@@ -124,9 +127,49 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
*len = 36; *len = 36;
scsi->tx_done = true; scsi->tx_done = true;
return 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; }; break;
case SCSI_READ_CAPACITY_6: { case SCSI_READ_FORMAT_CAPACITIES: {
FURI_LOG_I(TAG, "SCSI_READ_CAPACITY_6"); 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_10: {
FURI_LOG_D(TAG, "SCSI_READ_CAPACITY_10");
if(cap < 8) return false; if(cap < 8) return false;
uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx); uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
uint32_t block_size = SCSI_BLOCK_SIZE; 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; return true;
}; break; }; break;
case SCSI_MODE_SENSE_6: { 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; 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
@@ -167,7 +210,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t
return result; return result;
}; break; }; break;
default: { 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->sk = SCSI_SK_ILLEGAL_REQUEST;
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
return false; 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) { 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* cmd = scsi->cmd;
uint8_t len = scsi->cmd_len; uint8_t len = scsi->cmd_len;
scsi->cmd = NULL; scsi->cmd = NULL;
@@ -187,33 +230,34 @@ bool scsi_cmd_end(SCSISession* scsi) {
case SCSI_REQUEST_SENSE: case SCSI_REQUEST_SENSE:
case SCSI_INQUIRY: 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_MODE_SENSE_6:
case SCSI_READ_10: case SCSI_READ_10:
return scsi->tx_done; return scsi->tx_done;
case SCSI_TEST_UNIT_READY: { case SCSI_TEST_UNIT_READY: {
FURI_LOG_I(TAG, "SCSI_TEST_UNIT_READY"); FURI_LOG_D(TAG, "SCSI_TEST_UNIT_READY");
return true; return true;
}; break; }; break;
case SCSI_PREVENT_MEDIUM_REMOVAL: { case SCSI_PREVENT_MEDIUM_REMOVAL: {
if(len < 6) return false; if(len < 6) return false;
bool prevent = cmd[5]; 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; return !prevent;
}; break; }; break;
case SCSI_START_STOP_UNIT: { case SCSI_START_STOP_UNIT: {
if(len < 6) return false; if(len < 6) return false;
bool eject = (cmd[4] & 2) != 0; bool eject = (cmd[4] & 2) != 0;
bool start = (cmd[4] & 1) != 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) { if(eject) {
scsi->fn.eject(scsi->fn.ctx); scsi->fn.eject(scsi->fn.ctx);
} }
return true; return true;
}; break; }; break;
default: { 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->sk = SCSI_SK_ILLEGAL_REQUEST;
scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
return false; return false;

View File

@@ -44,6 +44,7 @@ typedef struct {
uint8_t cmd_len; uint8_t cmd_len;
uint8_t cmd[16]; uint8_t cmd[16];
} __attribute__((packed)) CBW; } __attribute__((packed)) CBW;
typedef struct { typedef struct {
uint32_t sig; uint32_t sig;
uint32_t tag; uint32_t tag;
@@ -80,11 +81,11 @@ static int32_t mass_thread_worker(void* context) {
while(true) { while(true) {
uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
if(flags & EventExit) { if(flags & EventExit) {
FURI_LOG_I(TAG, "exit"); FURI_LOG_D(TAG, "exit");
break; break;
} }
if(flags & EventReset) { if(flags & EventReset) {
FURI_LOG_I(TAG, "reset"); FURI_LOG_D(TAG, "reset");
scsi.sk = 0; scsi.sk = 0;
scsi.asc = 0; scsi.asc = 0;
memset(&cbw, 0, sizeof(cbw)); memset(&cbw, 0, sizeof(cbw));
@@ -99,10 +100,10 @@ static int32_t mass_thread_worker(void* context) {
if(flags & EventRxTx) do { if(flags & EventRxTx) do {
switch(state) { switch(state) {
case StateReadCBW: { 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)); int32_t len = usbd_ep_read(dev, USB_MSC_RX_EP, &cbw, sizeof(cbw));
if(len <= 0) { if(len <= 0) {
// FURI_LOG_I(TAG, "cbw not ready"); FURI_LOG_T(TAG, "cbw not ready");
break; break;
} }
if(len != sizeof(cbw) || cbw.sig != CBW_SIG) { if(len != sizeof(cbw) || cbw.sig != CBW_SIG) {
@@ -131,14 +132,14 @@ static int32_t mass_thread_worker(void* context) {
continue; continue;
}; break; }; break;
case StateReadData: { 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) { if(!cbw.len) {
state = StateBuildCSW; state = StateBuildCSW;
continue; continue;
} }
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 %lu -> %lu", buf_cap, buf_clamp); FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
if(buf) { if(buf) {
free(buf); free(buf);
} }
@@ -149,10 +150,10 @@ static int32_t mass_thread_worker(void* context) {
int32_t len = int32_t len =
usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len); usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len);
if(len < 0) { if(len < 0) {
// FURI_LOG_I(TAG, "rx not ready %d", len); FURI_LOG_T(TAG, "rx not ready %ld", len);
break; 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; buf_len += len;
} }
if(buf_len == buf_clamp) { if(buf_len == buf_clamp) {
@@ -172,14 +173,14 @@ static int32_t mass_thread_worker(void* context) {
continue; continue;
}; break; }; break;
case StateWriteData: { case StateWriteData: {
// FURI_LOG_I(TAG, "StateWriteData %d", cbw.len); FURI_LOG_T(TAG, "StateWriteData %lu", cbw.len);
if(!cbw.len) { if(!cbw.len) {
state = StateBuildCSW; state = StateBuildCSW;
continue; continue;
} }
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 %lu -> %lu", buf_cap, buf_clamp); FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
if(buf) { if(buf) {
free(buf); free(buf);
} }
@@ -198,7 +199,7 @@ static int32_t mass_thread_worker(void* context) {
buf + buf_sent, buf + buf_sent,
MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent)); MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent));
if(len < 0) { if(len < 0) {
// FURI_LOG_I(TAG, "tx not ready %d", len); FURI_LOG_T(TAG, "tx not ready %ld", len);
break; break;
} }
buf_sent += len; buf_sent += len;
@@ -210,7 +211,7 @@ static int32_t mass_thread_worker(void* context) {
continue; continue;
}; break; }; break;
case StateBuildCSW: { case StateBuildCSW: {
// FURI_LOG_I(TAG, "StateBuildCSW"); FURI_LOG_T(TAG, "StateBuildCSW");
csw.sig = CSW_SIG; csw.sig = CSW_SIG;
csw.tag = cbw.tag; csw.tag = cbw.tag;
if(scsi_cmd_end(&scsi)) { if(scsi_cmd_end(&scsi)) {
@@ -223,7 +224,7 @@ static int32_t mass_thread_worker(void* context) {
continue; continue;
}; break; }; break;
case StateWriteCSW: { case StateWriteCSW: {
// FURI_LOG_I(TAG, "StateWriteCSW"); FURI_LOG_T(TAG, "StateWriteCSW");
if(csw.status) { if(csw.status) {
FURI_LOG_W( FURI_LOG_W(
TAG, 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)); int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw));
if(len < 0) { if(len < 0) {
// FURI_LOG_I(TAG, "csw not ready"); FURI_LOG_T(TAG, "csw not ready");
break; break;
} }
if(len != sizeof(csw)) { if(len != sizeof(csw)) {

View File

@@ -21,6 +21,19 @@ 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);
} }
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* mass_storage_app_alloc(char* arg) {
MassStorageApp* app = malloc(sizeof(MassStorageApp)); MassStorageApp* app = malloc(sizeof(MassStorageApp));
app->file_path = furi_string_alloc(); 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->gui = furi_record_open(RECORD_GUI);
app->fs_api = furi_record_open(RECORD_STORAGE); app->fs_api = furi_record_open(RECORD_STORAGE);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->dialogs = furi_record_open(RECORD_DIALOGS); app->dialogs = furi_record_open(RECORD_DIALOGS);
app->view_dispatcher = view_dispatcher_alloc(); app->view_dispatcher = view_dispatcher_alloc();
@@ -76,6 +88,10 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, MassStorageAppViewPopup, popup_get_view(app->popup)); 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); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_set_scene_state( scene_manager_set_scene_state(
@@ -94,15 +110,18 @@ void mass_storage_app_free(MassStorageApp* app) {
// Views // Views
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork);
mass_storage_free(app->mass_storage_view);
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewVarItemList); view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewVarItemList);
variable_item_list_free(app->var_item_list);
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewSubmenu); view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewSubmenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewTextInput); 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, 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); popup_free(app->popup);
loading_free(app->loading);
// View dispatcher // View dispatcher
view_dispatcher_free(app->view_dispatcher); view_dispatcher_free(app->view_dispatcher);
@@ -113,7 +132,6 @@ void mass_storage_app_free(MassStorageApp* app) {
// Close records // Close records
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
free(app); free(app);

View File

@@ -9,16 +9,16 @@
#include <gui/view_dispatcher.h> #include <gui/view_dispatcher.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
#include <dialogs/dialogs.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/variable_item_list.h>
#include <gui/modules/submenu.h> #include <gui/modules/submenu.h>
#include <gui/modules/text_input.h> #include <gui/modules/text_input.h>
#include <gui/modules/popup.h> #include <gui/modules/popup.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"
#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_FILE_NAME_LEN 40 #define MASS_STORAGE_FILE_NAME_LEN 40
typedef enum { typedef enum {
@@ -32,14 +32,12 @@ struct MassStorageApp {
Storage* fs_api; Storage* fs_api;
ViewDispatcher* view_dispatcher; ViewDispatcher* view_dispatcher;
SceneManager* scene_manager; SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs; DialogsApp* dialogs;
Widget* widget;
MassStorage* mass_storage_view;
VariableItemList* var_item_list; VariableItemList* var_item_list;
Submenu* submenu; Submenu* submenu;
TextInput* text_input; TextInput* text_input;
Popup* popup; Popup* popup;
Loading* loading;
uint32_t create_image_size; uint32_t create_image_size;
SizeUnit create_size_unit; SizeUnit create_size_unit;
@@ -47,16 +45,19 @@ struct MassStorageApp {
FuriString* file_path; FuriString* file_path;
File* file; File* file;
MassStorage* mass_storage_view;
FuriMutex* usb_mutex; FuriMutex* usb_mutex;
MassStorageUsb* usb; MassStorageUsb* usb;
}; };
typedef enum { typedef enum {
MassStorageAppViewError,
MassStorageAppViewWork,
MassStorageAppViewVarItemList, MassStorageAppViewVarItemList,
MassStorageAppViewSubmenu, MassStorageAppViewSubmenu,
MassStorageAppViewTextInput, MassStorageAppViewTextInput,
MassStorageAppViewPopup, MassStorageAppViewPopup,
MassStorageAppViewLoading,
MassStorageAppViewWork,
} MassStorageAppView; } MassStorageAppView;
void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);

View File

@@ -100,14 +100,7 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName); scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName);
break; break;
case VarItemListIndexCreate: { case VarItemListIndexCreate: {
popup_set_header(app->popup, "Creating Image...", 64, 32, AlignCenter, AlignCenter); mass_storage_app_show_loading_popup(app, true);
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);
bool default_name = !strnlen(app->create_name, sizeof(app->create_name)); bool default_name = !strnlen(app->create_name, sizeof(app->create_name));
if(default_name) { if(default_name) {
snprintf( snprintf(
@@ -117,7 +110,12 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
app->create_image_size, app->create_image_size,
size_unit_names[app->create_size_unit]); 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); app->file = storage_file_alloc(app->fs_api);
const char* error = NULL; 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); storage_file_free(app->file);
mass_storage_app_show_loading_popup(app, false);
if(error) { if(error) {
popup_set_header( popup_set_header(

View File

@@ -13,7 +13,7 @@ void mass_storage_scene_create_image_name_on_enter(void* context) {
MassStorageApp* app = context; MassStorageApp* app = context;
TextInput* text_input = app->text_input; 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); text_input_set_minimum_length(text_input, 0);

View File

@@ -12,7 +12,7 @@ void mass_storage_scene_start_on_enter(void* context) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Emulate Image", "Select Disc Image",
MassStorageSceneFileSelect, MassStorageSceneFileSelect,
mass_storage_scene_start_submenu_callback, mass_storage_scene_start_submenu_callback,
app); app);

View File

@@ -13,20 +13,20 @@ static bool file_read(
uint32_t* out_len, uint32_t* out_len,
uint32_t out_cap) { uint32_t out_cap) {
MassStorageApp* app = ctx; 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)) { if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) {
FURI_LOG_W(TAG, "seek failed"); FURI_LOG_W(TAG, "seek failed");
return false; return false;
} }
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_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; return *out_len == clamp;
} }
static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len) { static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len) {
MassStorageApp* app = ctx; 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) { if(len != count * SCSI_BLOCK_SIZE) {
FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len); FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len);
return false; return false;
@@ -45,7 +45,7 @@ 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_I(TAG, "EJECT"); FURI_LOG_D(TAG, "EJECT");
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
mass_storage_usb_stop(app->usb); mass_storage_usb_stop(app->usb);
app->usb = NULL; app->usb = NULL;
@@ -54,19 +54,38 @@ static void file_eject(void* ctx) {
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;
if(event.type == SceneManagerEventTypeTick) { if(event.type == SceneManagerEventTypeTick) {
bool ejected; bool ejected;
furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk);
ejected = app->usb == NULL; ejected = app->usb == NULL;
furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk); 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;
} }
return false; } 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) { void mass_storage_scene_work_on_enter(void* context) {
MassStorageApp* app = 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); app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
FuriString* file_name = furi_string_alloc(); FuriString* file_name = furi_string_alloc();
@@ -92,13 +111,18 @@ void mass_storage_scene_work_on_enter(void* context) {
furi_string_free(file_name); furi_string_free(file_name);
mass_storage_app_show_loading_popup(app, false);
view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork); view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork);
} }
void mass_storage_scene_work_on_exit(void* context) { void mass_storage_scene_work_on_exit(void* context) {
MassStorageApp* app = context; MassStorageApp* app = context;
mass_storage_app_show_loading_popup(app, true);
if(app->usb_mutex) {
furi_mutex_free(app->usb_mutex); furi_mutex_free(app->usb_mutex);
app->usb_mutex = NULL;
}
if(app->usb) { if(app->usb) {
mass_storage_usb_stop(app->usb); mass_storage_usb_stop(app->usb);
app->usb = NULL; app->usb = NULL;
@@ -107,4 +131,5 @@ void mass_storage_scene_work_on_exit(void* context) {
storage_file_free(app->file); storage_file_free(app->file);
app->file = NULL; app->file = NULL;
} }
mass_storage_app_show_loading_popup(app, false);
} }