USB mass storage driver (#146)

This commit is contained in:
Ken Van Hoeylandt
2025-01-03 01:48:48 +01:00
committed by GitHub
parent ec90198dbf
commit a9e890a7f3
18 changed files with 455 additions and 16 deletions
+1
View File
@@ -5,6 +5,7 @@
// Splash
#define TT_ASSETS_BOOT_LOGO TT_ASSET("boot_logo.png")
#define TT_ASSETS_BOOT_LOGO_USB TT_ASSET("boot_logo_usb.png")
// UI
#define TT_ASSETS_UI_SPINNER TT_ASSET("spinner.png")
+2
View File
@@ -73,6 +73,8 @@ public:
bool mount(const char* mountPath) override;
bool unmount() override;
State getState() const override;
sdmmc_card_t* _Nullable getCard() { return card; }
};
}
+108
View File
@@ -0,0 +1,108 @@
#ifdef ESP_PLATFORM
#include <Log.h>
#include "Usb.h"
#include "UsbTusb.h"
#include "TactilityHeadless.h"
#include "hal/SpiSdCard.h"
namespace tt::hal::usb {
#define TAG "usb"
#define BOOT_FLAG 42
struct BootMode {
uint32_t flag = 0;
};
static Mode currentMode = ModeDefault;
static RTC_NOINIT_ATTR BootMode bootMode;
sdmmc_card_t* _Nullable getCard() {
auto sdcard = getConfiguration().sdcard;
if (sdcard == nullptr) {
TT_LOG_W(TAG, "No SD card configuration found");
return nullptr;
}
if (!sdcard->isMounted()) {
TT_LOG_W(TAG, "SD card not mounted");
return nullptr;
}
auto spi_sdcard = std::static_pointer_cast<SpiSdCard>(sdcard);
if (spi_sdcard == nullptr) {
TT_LOG_W(TAG, "SD card interface is not supported (must be SpiSdCard)");
return nullptr;
}
auto* card = spi_sdcard->getCard();
if (card == nullptr) {
TT_LOG_W(TAG, "SD card has no card object available");
return nullptr;
}
return card;
}
static bool canStartNewMode() {
return isSupported() && (currentMode == ModeDefault || currentMode == ModeNone);
}
bool isSupported() {
return tusbIsSupported();
}
bool startMassStorageWithSdmmc() {
if (!canStartNewMode()) {
TT_LOG_E(TAG, "Can't start");
return false;
}
auto result = tusbStartMassStorageWithSdmmc();
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to init mass storage: %s", esp_err_to_name(result));
return false;
} else {
currentMode = ModeMassStorageSdmmc;
return true;
}
}
void stop() {
if (canStartNewMode()) {
return;
}
tusbStop();
currentMode = ModeNone;
}
Mode getMode() {
return currentMode;
}
bool canRebootIntoMassStorageSdmmc() {
return tusbIsSupported() && getCard() != nullptr;
}
void rebootIntoMassStorageSdmmc() {
if (tusbIsSupported()) {
bootMode.flag = BOOT_FLAG;
esp_restart();
}
}
bool isUsbBootMode() {
return bootMode.flag == BOOT_FLAG;
}
void resetUsbBootMode() {
bootMode.flag = 0;
}
}
#endif
+21
View File
@@ -0,0 +1,21 @@
#pragma once
namespace tt::hal::usb {
enum Mode {
ModeDefault, // Default state of USB stack
ModeNone, // State after TinyUSB was used and (partially) deinitialized
ModeMassStorageSdmmc
};
bool startMassStorageWithSdmmc();
void stop();
Mode getMode();
bool isSupported();
bool canRebootIntoMassStorageSdmmc();
void rebootIntoMassStorageSdmmc();
bool isUsbBootMode();
void resetUsbBootMode();
}
@@ -0,0 +1,21 @@
#ifndef ESP_PLATFORM
#include "Usb.h"
#define TAG "usb"
namespace tt::hal::usb {
bool startMassStorageWithSdmmc() { return false; }
void stop() {}
Mode getMode() { return ModeDefault; }
bool isSupported() { return false; }
bool canRebootIntoMassStorageSdmmc() { return false; }
void rebootIntoMassStorageSdmmc() {}
bool isUsbBootMode() { return false; }
void resetUsbBootMode() {}
}
#endif
@@ -0,0 +1,167 @@
#ifdef ESP_PLATFORM
#include "UsbTusb.h"
#include "sdkconfig.h"
#if CONFIG_TINYUSB_MSC_ENABLED == 1
#include "Log.h"
#include "tinyusb.h"
#include "tusb_msc_storage.h"
#define TAG "usb"
#define EPNUM_MSC 1
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
namespace tt::hal::usb {
extern sdmmc_card_t* _Nullable getCard();
}
enum {
ITF_NUM_MSC = 0,
ITF_NUM_TOTAL
};
enum {
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80,
EDPT_MSC_OUT = 0x01,
EDPT_MSC_IN = 0x81,
};
static bool driverInstalled = false;
static tusb_desc_device_t descriptor_config = {
.bLength = sizeof(descriptor_config),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x303A, // TODO: Espressif VID. Do we need to change this?
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
static char const* string_desc_arr[] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Espressif", // 1: Manufacturer
"Tactility Device", // 2: Product
"42", // 3: Serials
"Tactility Mass Storage", // 4. MSC
};
static uint8_t const msc_fs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
};
#if (TUD_OPT_HIGH_SPEED)
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0
};
static uint8_t const msc_hs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
};
#endif // TUD_OPT_HIGH_SPEED
static void storage_mount_changed_cb(tinyusb_msc_event_t* event) {
if (event->mount_changed_data.is_mounted) {
TT_LOG_I(TAG, "Mounted");
} else {
TT_LOG_I(TAG, "Unmounted");
}
}
static bool ensureDriverInstalled() {
if (driverInstalled) {
return true;
}
const tinyusb_config_t tusb_cfg = {
.device_descriptor = &descriptor_config,
.string_descriptor = string_desc_arr,
.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),
.external_phy = false,
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = msc_fs_configuration_desc,
.hs_configuration_descriptor = msc_hs_configuration_desc,
.qualifier_descriptor = &device_qualifier,
#else
.configuration_descriptor = msc_fs_configuration_desc,
#endif // TUD_OPT_HIGH_SPEED
.self_powered = false,
.vbus_monitor_io = 0
};
if (tinyusb_driver_install(&tusb_cfg) != ESP_OK) {
TT_LOG_E(TAG, "Failed to install TinyUSB driver");
return false;
}
driverInstalled = true;
return true;
}
bool tusbIsSupported() { return true; }
bool tusbStartMassStorageWithSdmmc() {
ensureDriverInstalled();
auto* card = tt::hal::usb::getCard();
if (card == nullptr) {
TT_LOG_E(TAG, "SD card not mounted");
return false;
}
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
.card = card,
.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
}
};
return tinyusb_msc_storage_init_sdmmc(&config_sdmmc) == ESP_OK;
}
void tusbStop() {
tinyusb_msc_storage_deinit();
}
#else
bool tusbIsSupported() { return false; }
bool tusbStartMassStorageWithSdmmc() { return false; }
void tusbStop() {}
#endif // TinyUSB enabled
#endif // ESP_PLATFORM
@@ -0,0 +1,5 @@
#pragma once
bool tusbIsSupported();
bool tusbStartMassStorageWithSdmmc();
void tusbStop();