diff --git a/Tactility/Include/Tactility/hal/usb/Usb.h b/Tactility/Include/Tactility/hal/usb/Usb.h index e98f19e3..cc04d179 100644 --- a/Tactility/Include/Tactility/hal/usb/Usb.h +++ b/Tactility/Include/Tactility/hal/usb/Usb.h @@ -5,7 +5,14 @@ namespace tt::hal::usb { enum class Mode { Default, // Default state of USB stack None, // State after TinyUSB was used and (partially) deinitialized - MassStorageSdmmc + MassStorageSdmmc, + MassStorageFlash // For internal flash /data partition +}; + +enum class BootMode { + None, + Sdmmc, + Flash }; bool startMassStorageWithSdmmc(); @@ -18,4 +25,11 @@ void rebootIntoMassStorageSdmmc(); bool isUsbBootMode(); void resetUsbBootMode(); +BootMode getUsbBootMode(); + +// Flash-based mass storage +bool startMassStorageWithFlash(); +bool canRebootIntoMassStorageFlash(); +void rebootIntoMassStorageFlash(); + } \ No newline at end of file diff --git a/Tactility/Private/Tactility/PartitionsEsp.h b/Tactility/Private/Tactility/PartitionsEsp.h index 45e2298c..d504272d 100644 --- a/Tactility/Private/Tactility/PartitionsEsp.h +++ b/Tactility/Private/Tactility/PartitionsEsp.h @@ -3,11 +3,13 @@ #ifdef ESP_PLATFORM #include "esp_err.h" +#include "esp_vfs_fat.h" namespace tt { esp_err_t initPartitionsEsp(); +wl_handle_t getDataPartitionWlHandle(); } // namespace -#endif // ESP_PLATFORM \ No newline at end of file +#endif // ESP_PLATFORM diff --git a/Tactility/Private/Tactility/hal/usb/UsbTusb.h b/Tactility/Private/Tactility/hal/usb/UsbTusb.h index a9f9a3ee..3b3c746d 100644 --- a/Tactility/Private/Tactility/hal/usb/UsbTusb.h +++ b/Tactility/Private/Tactility/hal/usb/UsbTusb.h @@ -2,4 +2,6 @@ bool tusbIsSupported(); bool tusbStartMassStorageWithSdmmc(); +bool tusbStartMassStorageWithFlash(); void tusbStop(); +bool tusbCanStartMassStorageWithFlash(); \ No newline at end of file diff --git a/Tactility/Source/PartitionsEsp.cpp b/Tactility/Source/PartitionsEsp.cpp index ec8f0895..ac110e02 100644 --- a/Tactility/Source/PartitionsEsp.cpp +++ b/Tactility/Source/PartitionsEsp.cpp @@ -22,6 +22,10 @@ static esp_err_t initNvsFlashSafely() { static wl_handle_t data_wl_handle = WL_INVALID_HANDLE; +wl_handle_t getDataPartitionWlHandle() { + return data_wl_handle; +} + size_t getSectorSize() { #if defined(CONFIG_FATFS_SECTOR_512) return 512; diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index 36f825fa..5970929b 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -71,8 +71,19 @@ class BootApp : public App { } TT_LOG_I(TAG, "Rebooting into mass storage device mode"); + auto mode = hal::usb::getUsbBootMode(); // Get mode before reset hal::usb::resetUsbBootMode(); - hal::usb::startMassStorageWithSdmmc(); + if (mode == hal::usb::BootMode::Flash) { + if (!hal::usb::startMassStorageWithFlash()) { + TT_LOG_E(TAG, "Unable to start flash mass storage"); + return false; + } + } else if (mode == hal::usb::BootMode::Sdmmc) { + if (!hal::usb::startMassStorageWithSdmmc()) { + TT_LOG_E(TAG, "Unable to start SD mass storage"); + return false; + } + } return true; } diff --git a/Tactility/Source/app/usbsettings/UsbSettings.cpp b/Tactility/Source/app/usbsettings/UsbSettings.cpp index 53c2fdd1..049afa2a 100644 --- a/Tactility/Source/app/usbsettings/UsbSettings.cpp +++ b/Tactility/Source/app/usbsettings/UsbSettings.cpp @@ -11,32 +11,50 @@ namespace tt::app::usbsettings { -static void onRebootMassStorage(TT_UNUSED lv_event_t* event) { +static void onRebootMassStorageSdmmc(TT_UNUSED lv_event_t* event) { hal::usb::rebootIntoMassStorageSdmmc(); } +// Flash reboot handler +static void onRebootMassStorageFlash(TT_UNUSED lv_event_t* event) { + hal::usb::rebootIntoMassStorageFlash(); +} + class UsbSettingsApp : public App { void onShow(AppContext& app, lv_obj_t* parent) override { auto* toolbar = lvgl::toolbar_create(parent, app); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); - if (hal::usb::canRebootIntoMassStorageSdmmc()) { - auto* button = lv_button_create(parent); - auto* label = lv_label_create(button); - lv_label_set_text(label, "Reboot as USB storage"); - lv_obj_align(button, LV_ALIGN_CENTER, 0, 0); - lv_obj_add_event_cb(button, onRebootMassStorage, LV_EVENT_SHORT_CLICKED, nullptr); - } else { + // Create a wrapper container for buttons + auto* wrapper = lv_obj_create(parent); + lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(wrapper, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_size(wrapper, lv_pct(100), LV_SIZE_CONTENT); + lv_obj_align(wrapper, LV_ALIGN_CENTER, 0, 0); + + bool hasSd = hal::usb::canRebootIntoMassStorageSdmmc(); + bool hasFlash = hal::usb::canRebootIntoMassStorageFlash(); + + if (hasSd) { + auto* button_sd = lv_button_create(wrapper); + auto* label_sd = lv_label_create(button_sd); + lv_label_set_text(label_sd, "Reboot as USB storage (SD)"); + lv_obj_add_event_cb(button_sd, onRebootMassStorageSdmmc, LV_EVENT_SHORT_CLICKED, nullptr); + } + + if (hasFlash) { + auto* button_flash = lv_button_create(wrapper); + auto* label_flash = lv_label_create(button_flash); + lv_label_set_text(label_flash, "Reboot as USB storage (Flash)"); + lv_obj_add_event_cb(button_flash, onRebootMassStorageFlash, LV_EVENT_SHORT_CLICKED, nullptr); + } + + if (!hasSd && !hasFlash) { bool supported = hal::usb::isSupported(); - const char* first = supported ? "USB storage not available:" : "USB driver not supported"; - const char* second = supported ? "SD card not mounted" : "on this hardware"; - auto* label_a = lv_label_create(parent); - lv_label_set_text(label_a, first); - lv_obj_align(label_a, LV_ALIGN_CENTER, 0, 0); - auto* label_b = lv_label_create(parent); - lv_label_set_text(label_b, second); - lv_obj_align_to(label_b, label_a, LV_ALIGN_OUT_BOTTOM_MID, 0, 4); + const char* message = supported ? "USB storage not available" : "USB driver not supported"; + auto* label = lv_label_create(wrapper); + lv_label_set_text(label, message); } } }; diff --git a/Tactility/Source/hal/usb/Usb.cpp b/Tactility/Source/hal/usb/Usb.cpp index e094f777..a8d5056b 100644 --- a/Tactility/Source/hal/usb/Usb.cpp +++ b/Tactility/Source/hal/usb/Usb.cpp @@ -9,14 +9,15 @@ namespace tt::hal::usb { constexpr auto* TAG = "usb"; -constexpr auto BOOT_FLAG = 42; +constexpr auto BOOT_FLAG_SDMMC = 42; // Existing +constexpr auto BOOT_FLAG_FLASH = 43; // For flash mode -struct BootMode { +struct BootModeData { uint32_t flag = 0; }; static Mode currentMode = Mode::Default; -static RTC_NOINIT_ATTR BootMode bootMode; +static RTC_NOINIT_ATTR BootModeData bootModeData; sdmmc_card_t* _Nullable getCard() { auto sdcards = findDevices(Device::Type::SdCard); @@ -87,17 +88,55 @@ bool canRebootIntoMassStorageSdmmc() { void rebootIntoMassStorageSdmmc() { if (tusbIsSupported()) { - bootMode.flag = BOOT_FLAG; + bootModeData.flag = BOOT_FLAG_SDMMC; + esp_restart(); + } +} + +// NEW: Flash mass storage functions +bool startMassStorageWithFlash() { + if (!canStartNewMode()) { + TT_LOG_E(TAG, "Can't start flash mass storage"); + return false; + } + + if (tusbStartMassStorageWithFlash()) { + currentMode = Mode::MassStorageFlash; + return true; + } else { + TT_LOG_E(TAG, "Failed to init flash mass storage"); + return false; + } +} + +bool canRebootIntoMassStorageFlash() { + return tusbCanStartMassStorageWithFlash(); +} + +void rebootIntoMassStorageFlash() { + if (tusbCanStartMassStorageWithFlash()) { + bootModeData.flag = BOOT_FLAG_FLASH; esp_restart(); } } bool isUsbBootMode() { - return bootMode.flag == BOOT_FLAG; + return bootModeData.flag == BOOT_FLAG_SDMMC || bootModeData.flag == BOOT_FLAG_FLASH; // Support both +} + +BootMode getUsbBootMode() { + switch (bootModeData.flag) { + case BOOT_FLAG_SDMMC: + return BootMode::Sdmmc; + case BOOT_FLAG_FLASH: + return BootMode::Flash; + default: + return BootMode::None; + } } void resetUsbBootMode() { - bootMode.flag = 0; + bootModeData.flag = 0; } } diff --git a/Tactility/Source/hal/usb/UsbMock.cpp b/Tactility/Source/hal/usb/UsbMock.cpp index cb623d0b..3ce6fb9c 100644 --- a/Tactility/Source/hal/usb/UsbMock.cpp +++ b/Tactility/Source/hal/usb/UsbMock.cpp @@ -9,10 +9,14 @@ namespace tt::hal::usb { bool startMassStorageWithSdmmc() { return false; } void stop() {} Mode getMode() { return Mode::Default; } +BootMode getUsbBootMode() { return BootMode::None; } bool isSupported() { return false; } bool canRebootIntoMassStorageSdmmc() { return false; } void rebootIntoMassStorageSdmmc() {} +bool startMassStorageWithFlash() { return false; } +bool canRebootIntoMassStorageFlash() { return false; } +void rebootIntoMassStorageFlash() {} bool isUsbBootMode() { return false; } void resetUsbBootMode() {} diff --git a/Tactility/Source/hal/usb/UsbTusb.cpp b/Tactility/Source/hal/usb/UsbTusb.cpp index 6c725eeb..455ccc08 100644 --- a/Tactility/Source/hal/usb/UsbTusb.cpp +++ b/Tactility/Source/hal/usb/UsbTusb.cpp @@ -1,6 +1,7 @@ #ifdef ESP_PLATFORM #include "Tactility/hal/usb/UsbTusb.h" +#include #include @@ -9,10 +10,12 @@ #include #include #include +#include #define TAG "usb" #define EPNUM_MSC 1 #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) +#define SECTOR_SIZE 512 namespace tt::hal::usb { extern sdmmc_card_t* _Nullable getCard(); @@ -90,9 +93,9 @@ static uint8_t const msc_hs_configuration_desc[] = { static void storage_mount_changed_cb(tinyusb_msc_event_t* event) { if (event->mount_changed_data.is_mounted) { - TT_LOG_I(TAG, "Mounted"); + TT_LOG_I(TAG, "MSC Mounted"); } else { - TT_LOG_I(TAG, "Unmounted"); + TT_LOG_I(TAG, "MSC Unmounted"); } } @@ -152,22 +155,62 @@ bool tusbStartMassStorageWithSdmmc() { auto result = tinyusb_msc_storage_init_sdmmc(&config_sdmmc); if (result != ESP_OK) { - TT_LOG_E(TAG, "TinyUSB init failed: %s", esp_err_to_name(result)); + TT_LOG_E(TAG, "TinyUSB SDMMC init failed: %s", esp_err_to_name(result)); + } else { + TT_LOG_I(TAG, "TinyUSB SDMMC init success"); } return result == ESP_OK; } +bool tusbStartMassStorageWithFlash() { + TT_LOG_I(TAG, "Starting flash MSC"); + ensureDriverInstalled(); + + wl_handle_t handle = tt::getDataPartitionWlHandle(); + if (handle == WL_INVALID_HANDLE) { + TT_LOG_E(TAG, "WL not mounted for /data"); + return false; + } + + const tinyusb_msc_spiflash_config_t config_flash = { + .wl_handle = handle, + .callback_mount_changed = storage_mount_changed_cb, + .callback_premount_changed = nullptr, + .mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + .allocation_unit_size = 0, + .disk_status_check_enable = false, + .use_one_fat = false + } + }; + + esp_err_t result = tinyusb_msc_storage_init_spiflash(&config_flash); + if (result != ESP_OK) { + TT_LOG_E(TAG, "TinyUSB flash init failed: %s", esp_err_to_name(result)); + } else { + TT_LOG_I(TAG, "TinyUSB flash init success"); + } + return result == ESP_OK; +} + void tusbStop() { tinyusb_msc_storage_deinit(); } +bool tusbCanStartMassStorageWithFlash() { + return tusbIsSupported() && (tt::getDataPartitionWlHandle() != WL_INVALID_HANDLE); +} + #else bool tusbIsSupported() { return false; } bool tusbStartMassStorageWithSdmmc() { return false; } +bool tusbStartMassStorageWithFlash() { return false; } void tusbStop() {} +bool tusbCanStartMassStorageWithFlash() { return false; } -#endif // TinyUSB enabled +#endif // CONFIG_TINYUSB_MSC_ENABLED #endif // ESP_PLATFORM