diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h index ce0b8d68..c5cc50f9 100644 --- a/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include -#include +#include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -27,6 +28,8 @@ struct Esp32SdmmcConfig { bool enable_uhs; }; +sdmmc_card_t* esp32_sdmmc_get_card(struct Device* device); + #ifdef __cplusplus } #endif diff --git a/Platforms/platform-esp32/private/tactility/drivers/esp32_sdmmc_fs.h b/Platforms/platform-esp32/private/tactility/drivers/esp32_sdmmc_fs.h new file mode 100644 index 00000000..2f5d3f0f --- /dev/null +++ b/Platforms/platform-esp32/private/tactility/drivers/esp32_sdmmc_fs.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct Esp32SdmmcConfig; +typedef void* Esp32SdmmcHandle; + +extern Esp32SdmmcHandle esp32_sdmmc_fs_alloc(const struct Esp32SdmmcConfig* config, const char* mount_path); + +extern void esp32_sdmmc_fs_free(Esp32SdmmcHandle handle); + +extern sdmmc_card_t* esp32_sdmmc_fs_get_card(Esp32SdmmcHandle handle); + +extern const FileSystemApi esp32_sdmmc_fs_api; + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp index aae48634..3facba29 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp @@ -1,12 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 #include #include +#include #include #include #include "tactility/drivers/gpio_descriptor.h" #include #include +#include #define TAG "esp32_sdmmc" @@ -18,13 +20,8 @@ extern "C" { struct Esp32SdmmcInternal { RecursiveMutex mutex = {}; bool initialized = false; - char fs_device_name[16] = "esp32_sdmmc_fs0"; - Device fs_device = { - .name = fs_device_name, - .config = nullptr, - .parent = nullptr, - .internal = nullptr - }; + Esp32SdmmcHandle esp32_sdmmc_fs_handle = nullptr; + FileSystem* file_system = nullptr; // Pin descriptors GpioDescriptor* pin_clk_descriptor = nullptr; @@ -47,6 +44,7 @@ struct Esp32SdmmcInternal { ~Esp32SdmmcInternal() { cleanup_pins(); recursive_mutex_destruct(&mutex); + if (esp32_sdmmc_fs_handle) free(esp32_sdmmc_fs_handle); } void cleanup_pins() { @@ -96,7 +94,6 @@ static error_t start(Device* device) { acquire_pin_or_set_null(sdmmc_config->pin_cd, &data->pin_cd_descriptor) && acquire_pin_or_set_null(sdmmc_config->pin_wp, &data->pin_wp_descriptor); - if (!pins_ok) { LOG_E(TAG, "Failed to acquire required one or more pins"); data->cleanup_pins(); @@ -105,11 +102,13 @@ static error_t start(Device* device) { return ERROR_RESOURCE; } - // Create filesystem child device - auto* fs_device = &data->fs_device; - fs_device->parent = device; - fs_device->config = sdmmc_config; - check(device_construct_add_start(fs_device, "espressif,esp32-sdmmc-fs") == ERROR_NONE); + data->esp32_sdmmc_fs_handle = esp32_sdmmc_fs_alloc(sdmmc_config, "/sdcard"); + data->file_system = file_system_add(&esp32_sdmmc_fs_api, data->esp32_sdmmc_fs_handle); + if (file_system_mount(data->file_system) != ERROR_NONE) { + // Error is not recoverable at the time, but it might be recoverable later, + // so we don't return start() failure. + LOG_E(TAG, "Failed to mount SD card filesystem"); + } data->initialized = true; return ERROR_NONE; @@ -120,11 +119,19 @@ static error_t stop(Device* device) { auto* data = GET_DATA(device); auto* dts_config = GET_CONFIG(device); - // Create filesystem child device - auto* fs_device = &data->fs_device; - check(device_stop(fs_device) == ERROR_NONE); - check(device_remove(fs_device) == ERROR_NONE); - check(device_destruct(fs_device) == ERROR_NONE); + if (file_system_is_mounted(data->file_system)) { + if (file_system_unmount(data->file_system) != ERROR_NONE) { + LOG_E(TAG, "Failed to unmount SD card filesystem"); + return ERROR_RESOURCE; + } + } + + // Free file system + file_system_remove(data->file_system); + data->file_system = nullptr; + // Free file system data + esp32_sdmmc_fs_free(data->esp32_sdmmc_fs_handle); + data->esp32_sdmmc_fs_handle = nullptr; data->cleanup_pins(); device_set_driver_data(device, nullptr); @@ -132,6 +139,12 @@ static error_t stop(Device* device) { return ERROR_NONE; } +sdmmc_card_t* esp32_sdmmc_get_card(Device* device) { + if (!device_is_ready(device)) return nullptr; + auto* data = GET_DATA(device); + return esp32_sdmmc_fs_get_card(data->esp32_sdmmc_fs_handle); +} + extern Module platform_esp32_module; Driver esp32_sdmmc_driver = { diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp index 79d5101c..456cf3b0 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp @@ -1,13 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 #include #include -#include +#include +#include #include #include #include #include -#include #include #include @@ -17,16 +17,24 @@ #define TAG "esp32_sdmmc_fs" -#define GET_DATA(device) ((struct Esp32SdmmcFsInternal*)device_get_driver_data(device)) -// We re-use the config from the parent device -#define GET_CONFIG(device) ((const struct Esp32SdmmcConfig*)device->config) +#define GET_DATA(data) static_cast(data) -struct Esp32SdmmcFsInternal { - std::string mount_path {}; - sdmmc_card_t* card = nullptr; +struct Esp32SdmmcFsData { + const std::string mount_path; + const Esp32SdmmcConfig* config; + sdmmc_card_t* card; #if SOC_SD_PWR_CTRL_SUPPORTED - sd_pwr_ctrl_handle_t pwr_ctrl_handle = nullptr; + sd_pwr_ctrl_handle_t pwr_ctrl_handle; #endif + + Esp32SdmmcFsData(const Esp32SdmmcConfig* config, const std::string& mount_path) : + mount_path(mount_path), + config(config), + card(nullptr) +#if SOC_SD_PWR_CTRL_SUPPORTED + ,pwr_ctrl_handle(nullptr) +#endif + {} }; static gpio_num_t to_native_pin(GpioPinSpec pin_spec) { @@ -36,11 +44,26 @@ static gpio_num_t to_native_pin(GpioPinSpec pin_spec) { extern "C" { -error_t mount(Device* device, const char* mount_path) { - LOG_I(TAG, "Mounting %s", mount_path); +static error_t get_path(void* data, char* out_path, size_t out_path_size); - auto* data = GET_DATA(device); - auto* config = GET_CONFIG(device); +Esp32SdmmcHandle esp32_sdmmc_fs_alloc(const Esp32SdmmcConfig* config, const char* mount_path) { + return new(std::nothrow) Esp32SdmmcFsData(config, mount_path); +} + +sdmmc_card_t* esp32_sdmmc_fs_get_card(Esp32SdmmcHandle handle) { + return GET_DATA(handle)->card; +} + +void esp32_sdmmc_fs_free(Esp32SdmmcHandle handle) { + auto* fs_data = GET_DATA(handle); + delete fs_data; +} + +static error_t mount(void* data) { + auto* fs_data = GET_DATA(data); + auto* config = fs_data->config; + + LOG_I(TAG, "Mounting %s", fs_data->mount_path.c_str()); esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, @@ -85,9 +108,9 @@ error_t mount(Device* device, const char* mount_path) { .flags = slot_config_flags }; - esp_err_t result = esp_vfs_fat_sdmmc_mount(mount_path, &host, &slot_config, &mount_config, &data->card); + esp_err_t result = esp_vfs_fat_sdmmc_mount(fs_data->mount_path.c_str(), &host, &slot_config, &mount_config, &fs_data->card); - if (result != ESP_OK || data->card == nullptr) { + if (result != ESP_OK || fs_data->card == nullptr) { if (result == ESP_FAIL) { LOG_E(TAG, "Mounting failed. Ensure the card is formatted with FAT."); } else { @@ -102,23 +125,21 @@ error_t mount(Device* device, const char* mount_path) { return ERROR_UNDEFINED; } - LOG_I(TAG, "Mounted %s", mount_path); - data->mount_path = mount_path; + LOG_I(TAG, "Mounted %s", fs_data->mount_path.c_str()); return ERROR_NONE; } -error_t unmount(Device* device) { - auto* data = GET_DATA(device); +static error_t unmount(void* data) { + auto* fs_data = GET_DATA(data); + LOG_I(TAG, "Unmounting %s", fs_data->mount_path.c_str()); - if (esp_vfs_fat_sdcard_unmount(data->mount_path.c_str(), data->card) != ESP_OK) { - LOG_E(TAG, "Unmount failed for %s", data->mount_path.c_str()); + if (esp_vfs_fat_sdcard_unmount(fs_data->mount_path.c_str(), fs_data->card) != ESP_OK) { + LOG_E(TAG, "Unmount failed for %s", fs_data->mount_path.c_str()); return ERROR_UNDEFINED; } - LOG_I(TAG, "Unmounted %s", data->mount_path.c_str()); - data->mount_path = ""; - data->card = nullptr; + fs_data->card = nullptr; #if SOC_SD_PWR_CTRL_SUPPORTED if (data->pwr_ctrl_handle) { @@ -127,69 +148,30 @@ error_t unmount(Device* device) { } #endif - return ERROR_NONE; -} - -bool is_mounted(Device* device) { - const auto* data = GET_DATA(device); - return data != nullptr && data->card != nullptr; -} - -error_t get_mount_path(Device* device, char* out_path, size_t out_path_size) { - const auto* data = GET_DATA(device); - if (data == nullptr || data->card == nullptr) return ERROR_INVALID_STATE; - if (data->mount_path.size() >= out_path_size) return ERROR_BUFFER_OVERFLOW; - strncpy(out_path, data->mount_path.c_str(), out_path_size); - return ERROR_NONE; -} - -static error_t start(Device* device) { - LOG_I(TAG, "start %s", device->name); - auto* data = new (std::nothrow) Esp32SdmmcFsInternal(); - if (!data) return ERROR_OUT_OF_MEMORY; - - device_set_driver_data(device, data); - - if (mount(device, "/sdcard") != ERROR_NONE) { - LOG_E(TAG, "Failed to mount SD card"); - } + LOG_I(TAG, "Unmounted %s", fs_data->mount_path.c_str()); return ERROR_NONE; } -static error_t stop(Device* device) { - ESP_LOGI(TAG, "stop %s", device->name); - auto* driver_data = GET_DATA(device); +static bool is_mounted(void* data) { + const auto* fs_data = GET_DATA(data); + if (fs_data == nullptr || fs_data->card == nullptr) return false; + return sdmmc_get_status(fs_data->card) == ESP_OK; +} - if (is_mounted(device)) { - if (unmount(device) != ERROR_NONE) { - LOG_E(TAG, "Failed to unmount SD card"); - } - } - - device_set_driver_data(device, nullptr); - delete driver_data; +static error_t get_path(void* data, char* out_path, size_t out_path_size) { + const auto* fs_data = GET_DATA(data); + if (fs_data == nullptr || fs_data->card == nullptr) return ERROR_INVALID_STATE; + if (fs_data->mount_path.size() >= out_path_size) return ERROR_BUFFER_OVERFLOW; + strncpy(out_path, fs_data->mount_path.c_str(), out_path_size); return ERROR_NONE; } -extern Module platform_esp32_module; - -static const FileSystemApi sdmmc_fs_api = { +const FileSystemApi esp32_sdmmc_fs_api = { .mount = mount, .unmount = unmount, .is_mounted = is_mounted, - .get_mount_path = get_mount_path -}; - -Driver esp32_sdmmc_fs_driver = { - .name = "esp32_sdmmc_fs", - .compatible = (const char*[]) { "espressif,esp32-sdmmc-fs", nullptr }, - .start_device = start, - .stop_device = stop, - .api = &sdmmc_fs_api, - .device_type = &FILE_SYSTEM_TYPE, - .owner = &platform_esp32_module, - .internal = nullptr + .get_path = get_path }; } // extern "C" diff --git a/Platforms/platform-esp32/source/module.cpp b/Platforms/platform-esp32/source/module.cpp index 3d1ce73e..6a49f1fa 100644 --- a/Platforms/platform-esp32/source/module.cpp +++ b/Platforms/platform-esp32/source/module.cpp @@ -19,7 +19,6 @@ static error_t start() { check(driver_construct_add(&esp32_i2c_driver) == ERROR_NONE); check(driver_construct_add(&esp32_i2s_driver) == ERROR_NONE); check(driver_construct_add(&esp32_sdmmc_driver) == ERROR_NONE); - check(driver_construct_add(&esp32_sdmmc_fs_driver) == ERROR_NONE); check(driver_construct_add(&esp32_spi_driver) == ERROR_NONE); check(driver_construct_add(&esp32_uart_driver) == ERROR_NONE); return ERROR_NONE; @@ -32,7 +31,6 @@ static error_t stop() { check(driver_remove_destruct(&esp32_i2c_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_i2s_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_sdmmc_driver) == ERROR_NONE); - check(driver_remove_destruct(&esp32_sdmmc_fs_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_spi_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_uart_driver) == ERROR_NONE); return ERROR_NONE; diff --git a/Tactility/Include/Tactility/MountPoints.h b/Tactility/Include/Tactility/MountPoints.h index d2a3a9a8..809c8bd4 100644 --- a/Tactility/Include/Tactility/MountPoints.h +++ b/Tactility/Include/Tactility/MountPoints.h @@ -21,6 +21,6 @@ constexpr auto* MOUNT_POINT_DATA = "/data"; constexpr auto* MOUNT_POINT_DATA = "data"; #endif -std::vector getMountPoints(); +std::vector getFileSystemDirents(); } // namespace diff --git a/Tactility/Include/Tactility/Paths.h b/Tactility/Include/Tactility/Paths.h index 005bd6e5..f12c53e3 100644 --- a/Tactility/Include/Tactility/Paths.h +++ b/Tactility/Include/Tactility/Paths.h @@ -2,10 +2,19 @@ #include +#include + + namespace tt { bool findFirstMountedSdCardPath(std::string& path); +bool hasMountedSdCard(); + +FileSystem* findFirstMountedSdcardFileSystem(); + +FileSystem* findFirstSdcardFileSystem(); + std::string getSystemRootPath(); std::string getTempPath(); diff --git a/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h b/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h index a9588e24..d57ffdb7 100644 --- a/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h +++ b/Tactility/Include/Tactility/hal/sdcard/SdCardDevice.h @@ -2,9 +2,11 @@ #include -#include #include +#include + +struct FileSystem; namespace tt::hal::sdcard { /** @@ -31,11 +33,12 @@ public: private: MountBehaviour mountBehaviour; + FileSystem* fileSystem; public: - explicit SdCardDevice(MountBehaviour mountBehaviour) : mountBehaviour(mountBehaviour) {} - ~SdCardDevice() override = default; + explicit SdCardDevice(MountBehaviour mountBehaviour); + ~SdCardDevice() override; Type getType() const final { return Type::SdCard; }; diff --git a/Tactility/Include/Tactility/hal/sdcard/SdmmcDevice.h b/Tactility/Include/Tactility/hal/sdcard/SdmmcDevice.h deleted file mode 100644 index 32ee8934..00000000 --- a/Tactility/Include/Tactility/hal/sdcard/SdmmcDevice.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifdef ESP_PLATFORM - -#pragma once - -#include "SdCardDevice.h" - -#include -#include -#include -#include -#include -#include - -namespace tt::hal::sdcard { - -/** - * SD card interface for the SDMMC interface. - */ -class SdmmcDevice final : public SdCardDevice { - - std::shared_ptr mutex = std::make_shared(); - -public: - - struct Config { - Config( - gpio_num_t pinClock, - gpio_num_t pinCmd, - gpio_num_t pinD0, - gpio_num_t pinD1, - gpio_num_t pinD2, - gpio_num_t pinD3, - MountBehaviour mountBehaviourAtBoot, - uint8_t busWidth = 4 - ) : - pinClock(pinClock), - pinCmd(pinCmd), - pinD0(pinD0), - pinD1(pinD1), - pinD2(pinD2), - pinD3(pinD3), - mountBehaviourAtBoot(mountBehaviourAtBoot), - busWidth(busWidth) - {} - - gpio_num_t pinClock; - gpio_num_t pinCmd; - gpio_num_t pinD0; - gpio_num_t pinD1; - gpio_num_t pinD2; - gpio_num_t pinD3; - MountBehaviour mountBehaviourAtBoot; - uint8_t busWidth; - bool formatOnMountFailed = false; - uint16_t maxOpenFiles = 4; - uint16_t allocUnitSize = 16 * 1024; - bool statusCheckEnabled = false; - }; - -private: - - std::string mountPath; - sdmmc_card_t* card = nullptr; - std::shared_ptr config; - - bool applyGpioWorkAround(); - bool mountInternal(const std::string& mountPath); - -public: - - explicit SdmmcDevice(std::unique_ptr config) : SdCardDevice(config->mountBehaviourAtBoot), - config(std::move(config)) - {} - - std::string getName() const override { return "SDMMC"; } - std::string getDescription() const override { return "SD card via SDMMC interface"; } - - bool mount(const std::string& mountPath) override; - bool unmount() override; - std::string getMountPath() const override { return mountPath; } - - std::shared_ptr getLock() const override { return mutex; } - - State getState(TickType_t timeout) const override; - - /** return card when mounted, otherwise return nullptr */ - sdmmc_card_t* getCard() { return card; } -}; - -} - -#endif diff --git a/Tactility/Source/MountPoints.cpp b/Tactility/Source/MountPoints.cpp index ac298fbc..bb9bf098 100644 --- a/Tactility/Source/MountPoints.cpp +++ b/Tactility/Source/MountPoints.cpp @@ -2,56 +2,37 @@ #include "Tactility/TactilityConfig.h" #include -#include "Tactility/hal/sdcard/SdCardDevice.h" #include #include -#include #include +#include +#include namespace tt::file { -std::vector getMountPoints() { +std::vector getFileSystemDirents() { std::vector dir_entries; dir_entries.clear(); - // Data partition - auto data_dirent = dirent{ - .d_ino = 1, - .d_type = TT_DT_DIR, - .d_name = { 0 } - }; - strcpy(data_dirent.d_name, DATA_PARTITION_NAME); - dir_entries.push_back(data_dirent); - - // SD card partitions - auto sdcards = tt::hal::findDevices(hal::Device::Type::SdCard); - for (auto& sdcard : sdcards) { - auto state = sdcard->getState(); - if (state == hal::sdcard::SdCardDevice::State::Mounted) { - auto mount_name = sdcard->getMountPath().substr(1); - auto dir_entry = dirent { - .d_ino = 2, - .d_type = TT_DT_DIR, - .d_name = { 0 } - }; - assert(mount_name.length() < sizeof(dirent::d_name)); - strcpy(dir_entry.d_name, mount_name.c_str()); - dir_entries.push_back(dir_entry); - } - } - - if (config::SHOW_SYSTEM_PARTITION) { - // System partition - auto system_dirent = dirent{ - .d_ino = 0, + file_system_for_each(&dir_entries, [](auto* fs, void* context) { + if (!file_system_is_mounted(fs)) return true; + char path[128]; + if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true; + auto mount_name = std::string(path).substr(1); + if (!config::SHOW_SYSTEM_PARTITION && mount_name.starts_with(SYSTEM_PARTITION_NAME)) return true; + auto dir_entry = dirent { + .d_ino = 2, .d_type = TT_DT_DIR, .d_name = { 0 } }; - strcpy(system_dirent.d_name, SYSTEM_PARTITION_NAME); - dir_entries.push_back(system_dirent); - } + auto& dir_entries = *static_cast*>(context); + strcpy(dir_entry.d_name, mount_name.c_str()); + dir_entries.push_back(dir_entry); + + return true; + }); return dir_entries; } diff --git a/Tactility/Source/PartitionsEsp.cpp b/Tactility/Source/PartitionsEsp.cpp index b1b79a5a..0d6357ad 100644 --- a/Tactility/Source/PartitionsEsp.cpp +++ b/Tactility/Source/PartitionsEsp.cpp @@ -5,11 +5,47 @@ #include #include +#include +#include namespace tt { static const auto LOGGER = Logger("Partitions"); +// region file_system stub + +struct PartitionFsData { + const char* path; +}; + +static error_t mount(void* data) { + return ERROR_NOT_SUPPORTED; +} + +static error_t unmount(void* data) { + return ERROR_NOT_SUPPORTED; +} + +static bool is_mounted(void* data) { + return true; +} + +static error_t get_path(void* data, char* out_path, size_t out_path_size) { + auto* fs_data = static_cast(data); + if (strlen(fs_data->path) >= out_path_size) return ERROR_BUFFER_OVERFLOW; + strncpy(out_path, fs_data->path, out_path_size); + return ERROR_NONE; +} + +FileSystemApi partition_fs_api = { + .mount = mount, + .unmount = unmount, + .is_mounted = is_mounted, + .get_path = get_path +}; + +// endregion file_system stub + static esp_err_t initNvsFlashSafely() { esp_err_t result = nvs_flash_init(); if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -56,6 +92,7 @@ esp_err_t initPartitionsEsp() { LOGGER.error("Failed to mount /system ({})", esp_err_to_name(system_result)); } else { LOGGER.info("Mounted /system"); + file_system_add(&partition_fs_api, new PartitionFsData("/system")); } auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle); @@ -63,6 +100,7 @@ esp_err_t initPartitionsEsp() { LOGGER.error("Failed to mount /data ({})", esp_err_to_name(data_result)); } else { LOGGER.info("Mounted /data"); + file_system_add(&partition_fs_api, new PartitionFsData("/data")); } return system_result == ESP_OK && data_result == ESP_OK; diff --git a/Tactility/Source/Paths.cpp b/Tactility/Source/Paths.cpp index b35a847b..9698c51d 100644 --- a/Tactility/Source/Paths.cpp +++ b/Tactility/Source/Paths.cpp @@ -2,25 +2,54 @@ #include #include -#include #include +#include namespace tt { bool findFirstMountedSdCardPath(std::string& path) { - // const auto sdcards = hal::findDevices(hal::Device::Type::SdCard); - bool is_set = false; - hal::findDevices(hal::Device::Type::SdCard, [&is_set, &path](const auto& device) { - if (device->isMounted()) { - path = device->getMountPath(); - is_set = true; - return false; // stop iterating - } else { - return true; + auto* fs = findFirstMountedSdcardFileSystem(); + if (fs == nullptr) return false; + char found_path[128]; + if (file_system_get_path(fs, found_path, sizeof(found_path)) != ERROR_NONE) return false; + path = found_path; + return true; +} + +bool hasMountedSdCard() { + auto* fs = findFirstMountedSdcardFileSystem(); + return fs != nullptr; +} + +FileSystem* findFirstMountedSdcardFileSystem() { + FileSystem* found = nullptr; + file_system_for_each(&found, [](auto* fs, void* context) { + char path[128]; + if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true; + // TODO: Find a better way to identify SD card paths + if (std::string(path).starts_with("/sdcard") && file_system_is_mounted(fs)) { + *static_cast(context) = fs; + return false; } + return true; }); - return is_set; + return found; +} + +FileSystem* findFirstSdcardFileSystem() { + FileSystem* found = nullptr; + file_system_for_each(&found, [](auto* fs, void* context) { + char path[128]; + if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true; + // TODO: Find a better way to identify SD card paths + if (std::string(path).starts_with("/sdcard")) { + *static_cast(context) = fs; + return false; + } + return true; + }); + return found; } std::string getSystemRootPath() { diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 85b81afe..475e2e98 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -8,19 +8,19 @@ #include #include -#include -#include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #include -#include #include #include #include @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -228,22 +229,18 @@ static void registerInstalledApps(const std::string& path) { }); } -static void registerInstalledAppsFromSdCard(const std::shared_ptr& sdcard) { - auto sdcard_root_path = sdcard->getMountPath(); - auto app_path = std::format("{}/app", sdcard_root_path); - if (file::isDirectory(app_path)) { - registerInstalledApps(app_path); - } -} - -static void registerInstalledAppsFromSdCards() { - auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted()) { - LOGGER.info("Registering apps from {}", sdcard->getMountPath()); - registerInstalledAppsFromSdCard(sdcard); +static void registerInstalledAppsFromFileSystems() { + file_system_for_each(nullptr, [](auto* fs, void* context) { + if (!file_system_is_mounted(fs)) return true; + char path[128]; + if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true; + const auto app_path = std::format("{}/app", path); + if (!app_path.starts_with(file::MOUNT_POINT_SYSTEM) && file::isDirectory(app_path)) { + LOGGER.info("Registering apps from {}", path); + registerInstalledApps(app_path); } - } + return true; + }); } static void registerAndStartSecondaryServices() { @@ -266,7 +263,7 @@ static void registerAndStartSecondaryServices() { static void registerAndStartPrimaryServices() { LOGGER.info("Registering and starting primary system services"); addService(service::gps::manifest); - if (hal::hasDevice(hal::Device::Type::SdCard)) { + if (hasMountedSdCard()) { addService(service::sdcard::manifest); } addService(service::wifi::manifest); @@ -301,26 +298,19 @@ void createTempDirectory(const std::string& rootPath) { } void prepareFileSystems() { - // Temporary directories for SD cards - auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted()) { - createTempDirectory(sdcard->getMountPath()); - } - } - // Temporary directory for /data - if (file::isDirectory(file::MOUNT_POINT_DATA)) { - createTempDirectory(file::MOUNT_POINT_DATA); - } + file_system_for_each(nullptr, [](auto* fs, void* context) { + if (!file_system_is_mounted(fs)) return true; + char path[128]; + if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true; + if (std::string(path) == file::MOUNT_POINT_SYSTEM) return true; + createTempDirectory(path); + return true; + }); } void registerApps() { registerInternalApps(); - auto data_apps_path = std::format("{}/app", file::MOUNT_POINT_DATA); - if (file::isDirectory(data_apps_path)) { - registerInstalledApps(data_apps_path); - } - registerInstalledAppsFromSdCards(); + registerInstalledAppsFromFileSystems(); } void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices[]) { diff --git a/Tactility/Source/app/files/FilesApp.cpp b/Tactility/Source/app/files/FilesApp.cpp index ee263774..5f353cbf 100644 --- a/Tactility/Source/app/files/FilesApp.cpp +++ b/Tactility/Source/app/files/FilesApp.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/Tactility/Source/app/files/State.cpp b/Tactility/Source/app/files/State.cpp index 8a7bb485..86f88a59 100644 --- a/Tactility/Source/app/files/State.cpp +++ b/Tactility/Source/app/files/State.cpp @@ -51,7 +51,7 @@ bool State::setEntriesForPath(const std::string& path) { bool get_mount_points = (kernel::getPlatform() == kernel::PlatformEsp) && (path == "/"); if (get_mount_points) { LOGGER.info("Setting custom root"); - dir_entries = file::getMountPoints(); + dir_entries = file::getFileSystemDirents(); current_path = path; selected_child_entry = ""; action = ActionNone; diff --git a/Tactility/Source/app/fileselection/State.cpp b/Tactility/Source/app/fileselection/State.cpp index 78b99747..b2cf4afc 100644 --- a/Tactility/Source/app/fileselection/State.cpp +++ b/Tactility/Source/app/fileselection/State.cpp @@ -43,7 +43,7 @@ bool State::setEntriesForPath(const std::string& path) { bool show_custom_root = (kernel::getPlatform() == kernel::PlatformEsp) && (path == "/"); if (show_custom_root) { LOGGER.info("Setting custom root"); - dir_entries = file::getMountPoints(); + dir_entries = file::getFileSystemDirents(); current_path = path; selected_child_entry = ""; return true; diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 8394b80d..5ec43fae 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -1,18 +1,18 @@ #include #include - #if TT_FEATURE_SCREENSHOT_ENABLED #include #include -#include #include #include #include #include #include #include + +#include #include #include @@ -204,12 +204,9 @@ void ScreenshotApp::createFilePathWidgets(lv_obj_t* parent) { lv_textarea_set_one_line(pathTextArea, true); lv_obj_set_flex_grow(pathTextArea, 1); if (kernel::getPlatform() == kernel::PlatformEsp) { - auto sdcard_devices = tt::hal::findDevices(tt::hal::Device::Type::SdCard); - if (sdcard_devices.size() > 1) { - LOGGER.warn("Found multiple SD card devices - picking first"); - } - if (!sdcard_devices.empty() && sdcard_devices.front()->isMounted()) { - std::string lvgl_mount_path = lvgl::PATH_PREFIX + sdcard_devices.front()->getMountPath(); + std::string sdcard_path; + if (findFirstMountedSdCardPath(sdcard_path)) { + std::string lvgl_mount_path = lvgl::PATH_PREFIX + sdcard_path + "/screenshots"; lv_textarea_set_text(pathTextArea, lvgl_mount_path.c_str()); } else { lv_textarea_set_text(pathTextArea, "Error: no SD card"); diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 6d5c3171..289adb60 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -1,10 +1,10 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -343,14 +343,9 @@ class SystemInfoApp final : public App { } } - if (hasSdcardStorage) { - const auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted() && esp_vfs_fat_info(sdcard->getMountPath().c_str(), &storage_total, &storage_free) == ESP_OK) { - updateMemoryBar(sdcardStorageBar, storage_free, storage_total); - break; // Only update first SD card - } - } + std::string sdcard_path; + if (findFirstMountedSdCardPath(sdcard_path) && esp_vfs_fat_info(sdcard_path.c_str(), &storage_total, &storage_free) == ESP_OK) { + updateMemoryBar(sdcardStorageBar, storage_free, storage_total); } if (hasSystemStorage) { @@ -624,13 +619,10 @@ class SystemInfoApp final : public App { dataStorageBar = createMemoryBar(storage_tab, file::MOUNT_POINT_DATA); } - const auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted() && esp_vfs_fat_info(sdcard->getMountPath().c_str(), &storage_total, &storage_free) == ESP_OK) { - hasSdcardStorage = true; - sdcardStorageBar = createMemoryBar(storage_tab, sdcard->getMountPath().c_str()); - break; // Only show first SD card - } + std::string sdcard_path; + if (findFirstMountedSdCardPath(sdcard_path) && esp_vfs_fat_info(sdcard_path.c_str(), &storage_total, &storage_free) == ESP_OK) { + hasSdcardStorage = true; + sdcardStorageBar = createMemoryBar(storage_tab, sdcard_path.c_str()); } if (config::SHOW_SYSTEM_PARTITION) { diff --git a/Tactility/Source/hal/sdcard/SdCardDevice.cpp b/Tactility/Source/hal/sdcard/SdCardDevice.cpp new file mode 100644 index 00000000..b370e37b --- /dev/null +++ b/Tactility/Source/hal/sdcard/SdCardDevice.cpp @@ -0,0 +1,48 @@ +#include + +#include + +namespace tt::hal::sdcard { + +static error_t mount(void* data) { + auto* device = static_cast(data); + auto path = device->getMountPath(); + if (!device->mount(path)) return ERROR_UNDEFINED; + return ERROR_NONE; +} + +static error_t unmount(void* data) { + auto* device = static_cast(data); + if (!device->unmount()) return ERROR_UNDEFINED; + return ERROR_NONE; +} + +static bool is_mounted(void* data) { + auto* device = static_cast(data); + return device->isMounted(); +} + +static error_t get_path(void* data, char* out_path, size_t out_path_size) { + auto* device = static_cast(data); + if (device->getMountPath().size() >= out_path_size) return ERROR_BUFFER_OVERFLOW; + strncpy(out_path, device->getMountPath().c_str(), out_path_size); + return ERROR_NONE; +} + +FileSystemApi sdCardDeviceApi = { + .mount = mount, + .unmount = unmount, + .is_mounted = is_mounted, + .get_path = get_path +}; + +SdCardDevice::SdCardDevice(MountBehaviour mountBehaviour) : mountBehaviour(mountBehaviour) { + fileSystem = file_system_add(&sdCardDeviceApi, this); + check(fileSystem != nullptr); +} + +SdCardDevice::~SdCardDevice() { + file_system_remove(fileSystem); +} + +} diff --git a/Tactility/Source/hal/sdcard/SdmmcDevice.cpp b/Tactility/Source/hal/sdcard/SdmmcDevice.cpp deleted file mode 100644 index dbd17091..00000000 --- a/Tactility/Source/hal/sdcard/SdmmcDevice.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#ifdef ESP_PLATFORM -#include -#endif - -#if defined(ESP_PLATFORM) && defined(SOC_SDMMC_HOST_SUPPORTED) - -#include -#include - -#include -#include -#include - -namespace tt::hal::sdcard { - -static const auto LOGGER = Logger("SdmmcDevice"); - -bool SdmmcDevice::mountInternal(const std::string& newMountPath) { - LOGGER.info("Mounting {}", newMountPath); - - esp_vfs_fat_sdmmc_mount_config_t mount_config = { - .format_if_mount_failed = config->formatOnMountFailed, - .max_files = config->maxOpenFiles, - .allocation_unit_size = 512 * 8, - .disk_status_check_enable = config->statusCheckEnabled, - .use_one_fat = false - }; - - sdmmc_host_t host = SDMMC_HOST_DEFAULT(); - - sdmmc_slot_config_t slot_config = { - .clk = config->pinClock, - .cmd = config->pinCmd, - .d0 = config->pinD0, - .d1 = config->pinD1, - .d2 = config->pinD2, - .d3 = config->pinD3, - .d4 = static_cast(0), - .d5 = static_cast(0), - .d6 = static_cast(0), - .d7 = static_cast(0), - .cd = GPIO_NUM_NC, - .wp = GPIO_NUM_NC, - .width = config->busWidth, - .flags = 0 - }; - - esp_err_t result = esp_vfs_fat_sdmmc_mount(newMountPath.c_str(), &host, &slot_config, &mount_config, &card); - - if (result != ESP_OK || card == nullptr) { - if (result == ESP_FAIL) { - LOGGER.error("Mounting failed. Ensure the card is formatted with FAT."); - } else { - LOGGER.error("Mounting failed ({})", esp_err_to_name(result)); - } - return false; - } - - mountPath = newMountPath; - - return true; -} - -bool SdmmcDevice::mount(const std::string& newMountPath) { - auto lock = getLock()->asScopedLock(); - lock.lock(); - - if (mountInternal(newMountPath)) { - LOGGER.info("Mounted at {}", newMountPath); - sdmmc_card_print_info(stdout, card); - return true; - } else { - LOGGER.error("Mount failed for {}", newMountPath); - return false; - } -} - -bool SdmmcDevice::unmount() { - auto lock = getLock()->asScopedLock(); - lock.lock(); - - if (card == nullptr) { - LOGGER.error("Can't unmount: not mounted"); - return false; - } - - if (esp_vfs_fat_sdcard_unmount(mountPath.c_str(), card) != ESP_OK) { - LOGGER.error("Unmount failed for {}", mountPath); - return false; - } - - LOGGER.info("Unmounted {}", mountPath); - mountPath = ""; - card = nullptr; - return true; -} - -SdmmcDevice::State SdmmcDevice::getState(TickType_t timeout) const { - if (card == nullptr) { - return State::Unmounted; - } - - /** - * The SD card and the screen are on the same SPI bus. - * Writing and reading to the bus from 2 devices at the same time causes crashes. - * This work-around ensures that this check is only happening when LVGL isn't rendering. - */ - auto lock = getLock()->asScopedLock(); - bool locked = lock.lock(timeout); - if (!locked) { - return State::Timeout; - } - - if (sdmmc_get_status(card) != ESP_OK) { - return State::Error; - } - - return State::Mounted; -} - -} - -#endif diff --git a/Tactility/Source/hal/usb/Usb.cpp b/Tactility/Source/hal/usb/Usb.cpp index 6138ff3c..6e7cdc58 100644 --- a/Tactility/Source/hal/usb/Usb.cpp +++ b/Tactility/Source/hal/usb/Usb.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace tt::hal::usb { @@ -21,29 +22,35 @@ static Mode currentMode = Mode::Default; static RTC_NOINIT_ATTR BootModeData bootModeData; sdmmc_card_t* getCard() { - auto sdcards = findDevices(Device::Type::SdCard); + sdmmc_card_t* sdcard = nullptr; - std::shared_ptr usable_sdcard; - for (auto& sdcard : sdcards) { - auto sdcard_candidate = std::static_pointer_cast(sdcard); - if (sdcard_candidate != nullptr && sdcard_candidate->isMounted() && sdcard_candidate->getCard() != nullptr) { - usable_sdcard = sdcard_candidate; + // Find old HAL SD card device: + auto sdcards = findDevices(Device::Type::SdCard); + for (auto& device : sdcards) { + auto sdcard_device= std::static_pointer_cast(device); + if (sdcard_device != nullptr && sdcard_device->isMounted() && sdcard_device->getCard() != nullptr) { + sdcard = sdcard_device->getCard(); break; } } - if (usable_sdcard == nullptr) { - LOGGER.warn("Couldn't find a mounted SpiSdCard"); - return nullptr; + // Find ESP32 SDMMC device: + if (sdcard == nullptr) { + device_for_each(&sdcard, [](auto* device, void* context) { + if (device_is_ready(device) && device_is_compatible(device, "espressif,esp32-sdmmc")) { + auto** sdcard = static_cast(context); + *sdcard = esp32_sdmmc_get_card(device); + return false; + } + return true; + }); } - auto* sdmmc_card = usable_sdcard->getCard(); - if (sdmmc_card == nullptr) { - LOGGER.warn("SD card has no card object available"); - return nullptr; + if (sdcard == nullptr) { + LOGGER.warn("Couldn't find a mounted SD card"); } - return sdmmc_card; + return sdcard; } static bool canStartNewMode() { diff --git a/Tactility/Source/service/sdcard/Sdcard.cpp b/Tactility/Source/service/sdcard/Sdcard.cpp index 1cbddb82..fad5b172 100644 --- a/Tactility/Source/service/sdcard/Sdcard.cpp +++ b/Tactility/Source/service/sdcard/Sdcard.cpp @@ -1,11 +1,12 @@ -#include -#include #include +#include #include -#include -#include +#include #include #include +#include +#include +#include namespace tt::service::sdcard { @@ -17,7 +18,7 @@ class SdCardService final : public Service { Mutex mutex; std::unique_ptr updateTimer; - hal::sdcard::SdCardDevice::State lastState = hal::sdcard::SdCardDevice::State::Unmounted; + bool lastMountedState = false; bool lock(TickType_t timeout) const { return mutex.lock(timeout); @@ -29,23 +30,13 @@ class SdCardService final : public Service { void update() { // TODO: Support multiple SD cards - auto sdcard = hal::findFirstDevice(hal::Device::Type::SdCard); - if (sdcard == nullptr) { - return; - } + auto* file_system = findFirstSdcardFileSystem(); if (lock(50)) { - auto new_state = sdcard->getState(); - - if (new_state == hal::sdcard::SdCardDevice::State::Error) { - LOGGER.error("Sdcard error - unmounting. Did you eject the card in an unsafe manner?"); - sdcard->unmount(); + auto is_mounted = file_system_is_mounted(file_system); + if (is_mounted != lastMountedState) { + lastMountedState = is_mounted; } - - if (new_state != lastState) { - lastState = new_state; - } - unlock(); } else { LOGGER.warn(LOG_MESSAGE_MUTEX_LOCK_FAILED); @@ -55,7 +46,8 @@ class SdCardService final : public Service { public: bool onStart(ServiceContext& serviceContext) override { - if (hal::findFirstDevice(hal::Device::Type::SdCard) == nullptr) { + auto* sdcard_fs = findFirstSdcardFileSystem(); + if (sdcard_fs == nullptr) { LOGGER.warn("No SD card device found - not starting Service"); return false; } diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 23c44073..f8105c2a 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -2,8 +2,8 @@ #include #include +#include #include -#include #include #include #include @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -56,18 +57,9 @@ static const char* getWifiStatusIcon(wifi::RadioState state) { } } -static const char* getSdCardStatusIcon(hal::sdcard::SdCardDevice::State state) { - switch (state) { - using enum hal::sdcard::SdCardDevice::State; - case Mounted: - return LVGL_ICON_STATUSBAR_SD_CARD; - case Error: - case Unmounted: - case Timeout: - return LVGL_ICON_STATUSBAR_SD_CARD_ALERT; - default: - check(false, "Unhandled SdCard state"); - } +static const char* getSdCardStatusIcon(bool mounted) { + if (mounted) return LVGL_ICON_STATUSBAR_SD_CARD; + return LVGL_ICON_STATUSBAR_SD_CARD_ALERT; } static const char* getPowerStatusIcon() { @@ -172,18 +164,15 @@ class StatusbarService final : public Service { } void updateSdCardIcon() { - auto sdcards = hal::findDevices(hal::Device::Type::SdCard); + auto* sdcard_fs = findFirstSdcardFileSystem(); // TODO: Support multiple SD cards - auto sdcard = sdcards.empty() ? nullptr : sdcards[0]; - if (sdcard != nullptr) { - auto state = sdcard->getState(50 / portTICK_PERIOD_MS); - if (state != hal::sdcard::SdCardDevice::State::Timeout) { - auto* desired_icon = getSdCardStatusIcon(state); - if (sdcard_last_icon != desired_icon) { - lvgl::statusbar_icon_set_image(sdcard_icon_id, desired_icon); - lvgl::statusbar_icon_set_visibility(sdcard_icon_id, true); - sdcard_last_icon = desired_icon; - } + if (sdcard_fs != nullptr) { + auto mounted = file_system_is_mounted(sdcard_fs); + auto* desired_icon = getSdCardStatusIcon(mounted); + if (sdcard_last_icon != desired_icon) { + lvgl::statusbar_icon_set_image(sdcard_icon_id, desired_icon); + lvgl::statusbar_icon_set_visibility(sdcard_icon_id, true); + sdcard_last_icon = desired_icon; } // TODO: Consider tracking how long the SD card has been in unknown status and then show error } diff --git a/Tactility/Source/service/webserver/WebServerService.cpp b/Tactility/Source/service/webserver/WebServerService.cpp index 90b8bdfe..d85a6856 100644 --- a/Tactility/Source/service/webserver/WebServerService.cpp +++ b/Tactility/Source/service/webserver/WebServerService.cpp @@ -26,8 +26,7 @@ #include #include -#include -#include +#include #if TT_FEATURE_SCREENSHOT_ENABLED #include @@ -762,26 +761,20 @@ esp_err_t WebServerService::handleFsList(httpd_req_t* request) { std::ostringstream json; json << "{\"path\":\"" << norm << "\",\"entries\":["; - + struct FsIterContext { + std::ostringstream& json; + uint16_t count = 0; + }; + FsIterContext fs_iter_context { json }; // Special handling for root: show available mount points if (norm == "/") { - // Always show /data - json << "{\"name\":\"data\",\"type\":\"dir\",\"size\":0}"; - - // Show /sdcard if mounted - const auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted()) { - json << ",{\"name\":\"sdcard\",\"type\":\"dir\",\"size\":0}"; - break; - } - } - - device_for_each_of_type(&FILE_SYSTEM_TYPE, &json, [] (auto* fs_device, void* context) { - if (file_system_is_mounted(fs_device)) { - auto* json_context_ptr = static_cast(context); - auto& json_context = *json_context_ptr; - json_context << ",{\"name\":\"sdcard\",\"type\":\"dir\",\"size\":0}"; + file_system_for_each(&fs_iter_context, [] (auto* fs, void* context) { + auto* fs_iter_context = static_cast(context); + char path[128]; + if (file_system_is_mounted(fs) && file_system_get_path(fs, path, sizeof(path)) == ESP_OK && strcmp(path, "/system") != 0) { + fs_iter_context->count++; + if (fs_iter_context->count != 1) fs_iter_context->json << ","; // add separator between json array entries + fs_iter_context->json << "{\"name\":\"" << path << "\",\"type\":\"dir\",\"size\":0}"; } return true; }); @@ -1168,58 +1161,39 @@ esp_err_t WebServerService::handleApiSysinfo(httpd_req_t* request) { json << "\"storage\":{"; uint64_t storage_total = 0, storage_free = 0; - // Data partition - json << "\"data\":{"; - if (esp_vfs_fat_info(file::MOUNT_POINT_DATA, &storage_total, &storage_free) == ESP_OK) { - json << "\"free\":" << storage_free << ","; - json << "\"total\":" << storage_total << ","; - json << "\"mounted\":true"; - } else { - json << "\"mounted\":false"; - } - json << "},"; - - // SD card - check all sdcard devices - json << "\"sdcard\":{"; - bool sdcard_found = false; - const auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted() && esp_vfs_fat_info(sdcard->getMountPath().c_str(), &storage_total, &storage_free) == ESP_OK) { - json << "\"free\":" << storage_free << ","; - json << "\"total\":" << storage_total << ","; - json << "\"mounted\":true"; - sdcard_found = true; - break; - } - } - struct FsIterContext { std::ostringstream& json; - bool sdcard_found; + uint16_t count = 0; }; - FsIterContext fs_iter_context { json, sdcard_found }; - device_for_each_of_type(&FILE_SYSTEM_TYPE, &fs_iter_context, [] (auto* fs_device, void* context) { - if (!file_system_is_mounted(fs_device)) return true; - char mount_path[128]; - if (file_system_get_mount_path(fs_device, mount_path, sizeof(mount_path)) != ESP_OK) return true; - uint64_t storage_total = 0, storage_free = 0; - if (esp_vfs_fat_info(mount_path, &storage_total, &storage_free) != ESP_OK) return true; + FsIterContext fs_iter_context { json }; + file_system_for_each(&fs_iter_context, [] (auto* fs, void* context) { + char mount_path[128] = ""; + if (file_system_get_path(fs, mount_path, sizeof(mount_path)) != ERROR_NONE) return true; + if (strcmp(mount_path, "/system") == 0) return true; // Hide system partition + + bool mounted = file_system_is_mounted(fs); auto* fs_iter_context = static_cast(context); auto& json_context = fs_iter_context->json; - json_context << "\"free\":" << storage_free << ","; - json_context << "\"total\":" << storage_total << ","; - json_context << "\"mounted\":true"; - fs_iter_context->sdcard_found = true; + std::string mount_path_cpp = mount_path; + + fs_iter_context->count++; + if (fs_iter_context->count != 1) json_context << ","; // add separator between json array entries + json_context << "\"" << mount_path_cpp.substr(1) << "\":{"; + + uint64_t storage_total = 0, storage_free = 0; + if (esp_vfs_fat_info(mount_path, &storage_total, &storage_free) == ESP_OK) { + json_context << "\"free\":" << storage_free << ","; + json_context << "\"total\":" << storage_total << ","; + } else { + json_context << "\"free\":0,"; + json_context << "\"total\":0,"; + } + + json_context << "\"mounted\":" << (mounted ? "true" : "false") << ""; + json_context << "}"; return true; }); - if (fs_iter_context.sdcard_found) sdcard_found = true; - - if (!sdcard_found) { - json << "\"mounted\":false"; - } - json << "}"; - json << "},"; // end storage // Uptime (in seconds) @@ -1490,14 +1464,7 @@ esp_err_t WebServerService::handleApiScreenshot(httpd_req_t* request) { #if TT_FEATURE_SCREENSHOT_ENABLED // Determine save location: prefer SD card root if mounted, otherwise /data std::string save_path; - auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); - for (const auto& sdcard : sdcard_devices) { - if (sdcard->isMounted()) { - save_path = sdcard->getMountPath(); - break; - } - } - if (save_path.empty()) { + if (!findFirstMountedSdCardPath(save_path)) { save_path = file::MOUNT_POINT_DATA; } @@ -1574,7 +1541,7 @@ esp_err_t WebServerService::handleFsTree(httpd_req_t* request) { std::ostringstream json; json << "{"; // Gather mount points - auto mounts = file::getMountPoints(); + auto mounts = file::getFileSystemDirents(); json << "\"mounts\": ["; bool firstMount = true; for (auto& m : mounts) { diff --git a/Tactility/Source/service/wifi/WifiBootSplashInit.cpp b/Tactility/Source/service/wifi/WifiBootSplashInit.cpp index 04bd0417..86625684 100644 --- a/Tactility/Source/service/wifi/WifiBootSplashInit.cpp +++ b/Tactility/Source/service/wifi/WifiBootSplashInit.cpp @@ -6,13 +6,14 @@ #include #include +#include +#include +#include #include #include #include #include #include -#include -#include namespace tt::service::wifi { @@ -118,18 +119,14 @@ static void importWifiApSettingsFromDir(const std::string& path) { void bootSplashInit() { getMainDispatcher().dispatch([] { // First import any provisioning files placed on the system data partition. - const std::string settings_path = file::getChildPath(file::MOUNT_POINT_DATA, "settings"); - importWifiApSettingsFromDir(settings_path); + const std::string data_settings_path = file::getChildPath(file::MOUNT_POINT_DATA, "settings"); + importWifiApSettingsFromDir(data_settings_path); // Then scan attached SD cards as before. - const auto sdcards = hal::findDevices(hal::Device::Type::SdCard); - for (auto& sdcard : sdcards) { - if (sdcard->isMounted()) { - const std::string settings_path = file::getChildPath(sdcard->getMountPath(), "settings"); - importWifiApSettingsFromDir(settings_path); - } else { - LOGGER.warn("Skipping unmounted SD card {}", sdcard->getMountPath()); - } + std::string sdcard_path; + if (findFirstMountedSdCardPath((sdcard_path))) { + const std::string sd_settings_path = file::getChildPath(sdcard_path, "settings"); + importWifiApSettingsFromDir(sd_settings_path); } }); } diff --git a/Tactility/Source/settings/BootSettings.cpp b/Tactility/Source/settings/BootSettings.cpp index 1b30ba36..29cdbcf4 100644 --- a/Tactility/Source/settings/BootSettings.cpp +++ b/Tactility/Source/settings/BootSettings.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -18,9 +19,9 @@ constexpr auto* PROPERTIES_KEY_LAUNCHER_APP_ID = "launcherAppId"; constexpr auto* PROPERTIES_KEY_AUTO_START_APP_ID = "autoStartAppId"; static std::string getPropertiesFilePath() { - const auto sdcards = hal::findDevices(hal::Device::Type::SdCard); - for (auto& sdcard : sdcards) { - std::string path = std::format(PROPERTIES_FILE_FORMAT, sdcard->getMountPath()); + std::string sdcard_path; + if (findFirstMountedSdCardPath(sdcard_path)) { + std::string path = std::format(PROPERTIES_FILE_FORMAT, sdcard_path); if (file::isFile(path)) { return path; } diff --git a/TactilityKernel/include/tactility/drivers/file_system.h b/TactilityKernel/include/tactility/drivers/file_system.h deleted file mode 100644 index 9a487ffe..00000000 --- a/TactilityKernel/include/tactility/drivers/file_system.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct Device; - -struct FileSystemApi { - error_t (*mount)(struct Device* device, const char* mount_path); - error_t (*unmount)(struct Device* device); - bool (*is_mounted)(struct Device* device); - error_t (*get_mount_path)(struct Device*, char* out_path, size_t out_path_size); -}; - -extern const struct DeviceType FILE_SYSTEM_TYPE; - -error_t file_system_mount(struct Device* device, const char* mount_path); - -error_t file_system_unmount(struct Device* device); - -bool file_system_is_mounted(struct Device* device); - -error_t file_system_get_mount_path(struct Device*, char* out_path, size_t out_path_size); - -#ifdef __cplusplus -} -#endif diff --git a/TactilityKernel/include/tactility/filesystem/file_system.h b/TactilityKernel/include/tactility/filesystem/file_system.h new file mode 100644 index 00000000..c928e4b3 --- /dev/null +++ b/TactilityKernel/include/tactility/filesystem/file_system.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct FileSystem; + +struct FileSystemApi { + error_t (*mount)(void* data); + error_t (*unmount)(void* data); + bool (*is_mounted)(void* data); + error_t (*get_path)(void* data, char* out_path, size_t out_path_size); +}; + +struct FileSystem* file_system_add(const struct FileSystemApi* fs_api, void* data); + +void file_system_remove(struct FileSystem* fs); + +void file_system_for_each(void* callback_context, bool (*callback)(struct FileSystem* fs, void* context)); + +error_t file_system_mount(struct FileSystem* fs); + +error_t file_system_unmount(struct FileSystem* fs); + +bool file_system_is_mounted(struct FileSystem* fs); + +error_t file_system_get_path(struct FileSystem* fs, char* out_path, size_t out_path_size); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/source/drivers/file_system.cpp b/TactilityKernel/source/drivers/file_system.cpp deleted file mode 100644 index 5bca5bb2..00000000 --- a/TactilityKernel/source/drivers/file_system.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -#define INTERNAL_API(driver) ((FileSystemApi*)(driver)->api) - -extern "C" { - -error_t file_system_mount(Device* device, const char* mount_path) { - const auto* driver = device_get_driver(device); - return INTERNAL_API(driver)->mount(device, mount_path); -} - -error_t file_system_unmount(Device* device) { - const auto* driver = device_get_driver(device); - return INTERNAL_API(driver)->unmount(device); -} - -bool file_system_is_mounted(Device* device) { - const auto* driver = device_get_driver(device); - return INTERNAL_API(driver)->is_mounted(device); -} - -error_t file_system_get_mount_path(Device* device, char* out_path, size_t out_path_size) { - const auto* driver = device_get_driver(device); - return INTERNAL_API(driver)->get_mount_path(device, out_path, out_path_size); -} - -const DeviceType FILE_SYSTEM_TYPE { - .name = "file-system" -}; - -} // extern "C" diff --git a/TactilityKernel/source/filesystem/file_system.cpp b/TactilityKernel/source/filesystem/file_system.cpp new file mode 100644 index 00000000..b9e5926a --- /dev/null +++ b/TactilityKernel/source/filesystem/file_system.cpp @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +// Define the internal FileSystem structure +struct FileSystem { + const FileSystemApi* api; + void* data; +}; + +// Global Mutex and the master list of file systems +static Mutex fs_mutex; +static bool fs_mutex_initialized = false; +static std::vector file_systems; + +static void ensure_mutex_initialized() { + if (!fs_mutex_initialized) { + mutex_construct(&fs_mutex); + fs_mutex_initialized = true; + } +} + +extern "C" { + +FileSystem* file_system_add(const FileSystemApi* fs_api, void* data) { + ensure_mutex_initialized(); + mutex_lock(&fs_mutex); + + auto* fs = new(std::nothrow) struct FileSystem(); + check(fs != nullptr); + fs->api = fs_api; + fs->data = data; + file_systems.push_back(fs); + + mutex_unlock(&fs_mutex); + return fs; +} + +void file_system_remove(FileSystem* fs) { + check(!file_system_is_mounted(fs)); + ensure_mutex_initialized(); + mutex_lock(&fs_mutex); + + auto it = std::ranges::find(file_systems, fs); + if (it != file_systems.end()) { + file_systems.erase(it); + delete fs; + } + + mutex_unlock(&fs_mutex); +} + +void file_system_for_each(void* callback_context, bool (*callback)(FileSystem* fs, void* context)) { + ensure_mutex_initialized(); + mutex_lock(&fs_mutex); + + for (auto* fs : file_systems) { + if (!callback(fs, callback_context)) break; + } + + mutex_unlock(&fs_mutex); +} + +error_t file_system_mount(FileSystem* fs) { + // Assuming 'device' is accessible or passed via a different mechanism + // as it's required by the FileSystemApi signatures. + return fs->api->mount(fs->data); +} + +error_t file_system_unmount(FileSystem* fs) { + return fs->api->unmount(fs->data); +} + +bool file_system_is_mounted(FileSystem* fs) { + return fs->api->is_mounted(fs->data); +} + +error_t file_system_get_path(FileSystem* fs, char* out_path, size_t out_path_size) { + return fs->api->get_path(fs->data, out_path, out_path_size); +} + +} diff --git a/TactilityKernel/source/kernel_symbols.c b/TactilityKernel/source/kernel_symbols.c index 829ac5c5..da2b3f77 100644 --- a/TactilityKernel/source/kernel_symbols.c +++ b/TactilityKernel/source/kernel_symbols.c @@ -5,13 +5,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include /** @@ -58,11 +58,6 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(driver_is_compatible), DEFINE_MODULE_SYMBOL(driver_find_compatible), DEFINE_MODULE_SYMBOL(driver_get_device_type), - // file system - DEFINE_MODULE_SYMBOL(file_system_mount), - DEFINE_MODULE_SYMBOL(file_system_unmount), - DEFINE_MODULE_SYMBOL(file_system_is_mounted), - DEFINE_MODULE_SYMBOL(file_system_get_mount_path), // drivers/gpio_controller DEFINE_MODULE_SYMBOL(gpio_descriptor_acquire), DEFINE_MODULE_SYMBOL(gpio_descriptor_release), @@ -158,6 +153,11 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(timer_set_callback_priority), // error DEFINE_MODULE_SYMBOL(error_to_string), + // file system + DEFINE_MODULE_SYMBOL(file_system_mount), + DEFINE_MODULE_SYMBOL(file_system_unmount), + DEFINE_MODULE_SYMBOL(file_system_is_mounted), + DEFINE_MODULE_SYMBOL(file_system_get_path), // log #ifndef ESP_PLATFORM DEFINE_MODULE_SYMBOL(log_generic),