Files
tactility/Tactility/Source/hal/usb/UsbTusb.cpp
T
2026-08-12 21:29:35 +02:00

149 lines
4.4 KiB
C++

#ifdef ESP_PLATFORM
#include "Tactility/hal/usb/UsbTusb.h"
#include <Tactility/PartitionsEsp.h>
#include <sdkconfig.h>
#if CONFIG_TINYUSB_MSC_ENABLED == 1
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <wear_levelling.h>
#include <tactility/device.h>
#include <tactility/drivers/esp32_sdcard.h>
#include <tactility/drivers/usb_msc_device.h>
#include <tactility/log.h>
constexpr auto* TAG = "USB";
namespace tt::hal::usb {
// Set when mass storage was started as part of the dedicated reboot-into-MSC boot flow.
// Used to decide whether ejecting the volume should automatically reboot back to normal OS.
static bool startedFromBootMode = false;
static sdmmc_card_t* getCard() {
sdmmc_card_t* card = nullptr;
device_for_each(&card, [](auto* device, void* context) {
auto* driver = device_get_driver(device);
if (driver == nullptr) return true;
if (!driver_is_compatible(driver, "espressif,esp32-sdspi") &&
!driver_is_compatible(driver, "espressif,esp32-sdmmc")) return true;
auto** out = static_cast<sdmmc_card_t**>(context);
*out = esp32_sdcard_get_card(device);
return *out == nullptr;
});
if (card == nullptr) {
LOG_W(TAG, "Couldn't find a mounted SD card");
}
return card;
}
static void onMountChanged(bool mounted, void* /*context*/) {
if (mounted) {
LOG_I(TAG, "MSC Mounted");
// Storage is only (re)mounted into our own filesystem after the host sends a SCSI
// START STOP UNIT eject (see tud_msc_start_stop_cb() in tusb_msc_storage.c). Windows
// is known not to send this reliably, so this is a best-effort path for hosts that do
// (e.g. Linux/macOS) - the "Return to OS" button on the boot screen is the primary one.
// If we got here while booted into MSC mode, it's safe to reboot back into normal OS now.
if (startedFromBootMode) {
LOG_I(TAG, "MSC ejected by host, rebooting into normal OS");
esp_restart();
}
} else {
LOG_I(TAG, "MSC Unmounted");
}
}
} // namespace tt::hal::usb
bool tusbIsSupported() { return true; }
bool tusbStartMassStorageWithSdmmc(bool fromBootMode) {
auto* device = usb_msc_device_get();
if (device == nullptr) {
LOG_E(TAG, "USB MSC device not available");
return false;
}
auto* card = tt::hal::usb::getCard();
if (card == nullptr) {
LOG_E(TAG, "SD card not mounted");
device_put(device);
return false;
}
tt::hal::usb::startedFromBootMode = fromBootMode;
auto result = usb_msc_device_start(device, USB_MSC_DEVICE_SOURCE_SDMMC, card,
tt::hal::usb::onMountChanged, nullptr);
if (result != ERROR_NONE) {
LOG_E(TAG, "TinyUSB SDMMC init failed: %s", error_to_string(result));
} else {
LOG_I(TAG, "TinyUSB SDMMC init success");
}
device_put(device);
return result == ERROR_NONE;
}
bool tusbStartMassStorageWithFlash(bool fromBootMode) {
LOG_I(TAG, "Starting flash MSC");
auto* device = usb_msc_device_get();
if (device == nullptr) {
LOG_E(TAG, "USB MSC device not available");
return false;
}
wl_handle_t handle = tt::getDataPartitionWlHandle();
if (handle == WL_INVALID_HANDLE) {
LOG_E(TAG, "WL not mounted for /data");
device_put(device);
return false;
}
tt::hal::usb::startedFromBootMode = fromBootMode;
auto result = usb_msc_device_start(device, USB_MSC_DEVICE_SOURCE_FLASH,
&handle, tt::hal::usb::onMountChanged, nullptr);
if (result != ERROR_NONE) {
LOG_E(TAG, "TinyUSB flash init failed: %s", error_to_string(result));
} else {
LOG_I(TAG, "TinyUSB flash init success");
}
device_put(device);
return result == ERROR_NONE;
}
void tusbStop() {
auto* device = usb_msc_device_get();
if (device != nullptr) {
usb_msc_device_stop(device);
device_put(device);
}
}
bool tusbCanStartMassStorageWithFlash() {
return tusbIsSupported() && (tt::getDataPartitionWlHandle() != WL_INVALID_HANDLE);
}
#else
bool tusbIsSupported() { return false; }
bool tusbStartMassStorageWithSdmmc(bool /*fromBootMode*/) { return false; }
bool tusbStartMassStorageWithFlash(bool /*fromBootMode*/) { return false; }
void tusbStop() {}
bool tusbCanStartMassStorageWithFlash() { return false; }
#endif // CONFIG_TINYUSB_MSC_ENABLED
#endif // ESP_PLATFORM