From 9efc7c7f8a66185f09454fdba3025fa379f9a22d Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sat, 7 Mar 2026 13:33:19 +0100 Subject: [PATCH] Fixes --- Devices/m5stack-tab5/Source/Configuration.cpp | 7 ++ .../include/tactility/drivers/esp32_sdmmc.h | 5 ++ .../source/drivers/esp32_gpio.cpp | 36 +++++---- .../source/drivers/esp32_sdmmc.cpp | 18 +++-- .../source/drivers/esp32_sdmmc_fs.cpp | 14 ++-- Tactility/Include/Tactility/Paths.h | 6 +- Tactility/Source/MountPoints.cpp | 1 - Tactility/Source/PartitionsEsp.cpp | 6 +- Tactility/Source/Paths.cpp | 27 ++----- Tactility/Source/Tactility.cpp | 6 +- Tactility/Source/app/fileselection/State.cpp | 9 ++- .../Source/app/systeminfo/SystemInfo.cpp | 2 - Tactility/Source/hal/sdcard/SdCardDevice.cpp | 7 +- Tactility/Source/hal/usb/Usb.cpp | 8 +- Tactility/Source/service/sdcard/Sdcard.cpp | 80 ------------------- .../Source/service/statusbar/Statusbar.cpp | 8 +- .../service/webserver/WebServerService.cpp | 2 +- TactilityC/Source/tt_init.cpp | 4 + .../include/tactility/drivers/gpio.h | 2 +- .../tactility/drivers/gpio_controller.h | 2 +- .../include/tactility/drivers/wifi.h | 39 ++++++--- .../tactility/filesystem/file_system.h | 69 ++++++++++++++++ .../source/drivers/gpio_controller.cpp | 7 +- .../source/filesystem/file_system.cpp | 51 ++++++------ TactilityKernel/source/kernel_symbols.c | 4 + 25 files changed, 229 insertions(+), 191 deletions(-) delete mode 100644 Tactility/Source/service/sdcard/Sdcard.cpp diff --git a/Devices/m5stack-tab5/Source/Configuration.cpp b/Devices/m5stack-tab5/Source/Configuration.cpp index 0f75154e..6a39ad0c 100644 --- a/Devices/m5stack-tab5/Source/Configuration.cpp +++ b/Devices/m5stack-tab5/Source/Configuration.cpp @@ -57,12 +57,19 @@ constexpr auto GPIO_EXP1_PIN_IP2326_CHG_EN = 7; static void initExpander0(::Device* io_expander0) { auto* rf_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL, GPIO_OWNER_GPIO); + check(rf_pin); auto* speaker_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_SPEAKER_ENABLE, GPIO_OWNER_GPIO); + check(speaker_enable_pin); auto* external_5v_bus_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_EXTERNAL_5V_BUS_ENABLE, GPIO_OWNER_GPIO); + check(external_5v_bus_enable_pin); auto* lcd_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_LCD_RESET, GPIO_OWNER_GPIO); + check(lcd_reset_pin); auto* touch_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_TOUCH_RESET, GPIO_OWNER_GPIO); + check(touch_reset_pin); auto* camera_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_CAMERA_RESET, GPIO_OWNER_GPIO); + check(camera_reset_pin); auto* headphone_detect_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_HEADPHONE_DETECT, GPIO_OWNER_GPIO); + check(headphone_detect_pin); gpio_descriptor_set_flags(rf_pin, GPIO_FLAG_DIRECTION_OUTPUT); gpio_descriptor_set_flags(speaker_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT); diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h index ce2d47dd..152b4255 100644 --- a/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h @@ -30,6 +30,11 @@ struct Esp32SdmmcConfig { bool enable_uhs; }; +/** + * @brief Get the SD card handle for the given device. + * @param[in] device the device to get the card handle for + * @return the SD card handle, or NULL if the device is not mounted + */ sdmmc_card_t* esp32_sdmmc_get_card(struct Device* device); #ifdef __cplusplus diff --git a/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp b/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp index 64ae3c63..a4b8098f 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_gpio.cpp @@ -103,16 +103,6 @@ static error_t get_native_pin_number(GpioDescriptor* descriptor, void* pin_numbe } static error_t add_callback(GpioDescriptor* descriptor, void (*callback)(void*), void* arg) { - auto esp_error = gpio_isr_handler_add(static_cast(descriptor->pin), callback, arg); - return esp_err_to_error(esp_error); -} - -static error_t remove_callback(GpioDescriptor* descriptor) { - auto esp_error = gpio_isr_handler_remove(static_cast(descriptor->pin)); - return esp_err_to_error(esp_error); -} - -static error_t enable_interrupt(GpioDescriptor* descriptor) { auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor); if (internal->isr_service_ref_count == 0) { auto esp_error = gpio_install_isr_service(0); @@ -120,17 +110,23 @@ static error_t enable_interrupt(GpioDescriptor* descriptor) { return esp_err_to_error(esp_error); } } - auto esp_error = gpio_intr_enable(static_cast(descriptor->pin)); + + auto esp_error = gpio_isr_handler_add(static_cast(descriptor->pin), callback, arg); + if (esp_error == ESP_OK) { internal->isr_service_ref_count++; + } else if (internal->isr_service_ref_count == 0) { + gpio_uninstall_isr_service(); } + return esp_err_to_error(esp_error); } -static error_t disable_interrupt(GpioDescriptor* descriptor) { - auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor); - auto esp_error = gpio_intr_disable(static_cast(descriptor->pin)); - if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) { +static error_t remove_callback(GpioDescriptor* descriptor) { + auto esp_error = gpio_isr_handler_remove(static_cast(descriptor->pin)); + if (esp_error == ESP_OK) { + auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor); + check(internal->isr_service_ref_count > 0); internal->isr_service_ref_count--; if (internal->isr_service_ref_count == 0) { gpio_uninstall_isr_service(); @@ -139,6 +135,16 @@ static error_t disable_interrupt(GpioDescriptor* descriptor) { return esp_err_to_error(esp_error); } +static error_t enable_interrupt(GpioDescriptor* descriptor) { + auto esp_error = gpio_intr_enable(static_cast(descriptor->pin)); + return esp_err_to_error(esp_error); +} + +static error_t disable_interrupt(GpioDescriptor* descriptor) { + auto esp_error = gpio_intr_disable(static_cast(descriptor->pin)); + return esp_err_to_error(esp_error); +} + static error_t start(Device* device) { ESP_LOGI(TAG, "start %s", device->name); const Esp32GpioConfig* config = GET_CONFIG(device); diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp index 88b77944..5955de1d 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp @@ -1,16 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 #include #if SOC_SDMMC_HOST_SUPPORTED + +#include + +#include #include +#include #include #include -#include -#include - -#include "tactility/drivers/gpio_descriptor.h" -#include -#include +#include #include +#include #define TAG "esp32_sdmmc" @@ -46,7 +47,7 @@ struct Esp32SdmmcInternal { ~Esp32SdmmcInternal() { cleanup_pins(); recursive_mutex_destruct(&mutex); - if (esp32_sdmmc_fs_handle) free(esp32_sdmmc_fs_handle); + if (esp32_sdmmc_fs_handle) esp32_sdmmc_fs_free(esp32_sdmmc_fs_handle); } void cleanup_pins() { @@ -105,6 +106,7 @@ static error_t start(Device* device) { } data->esp32_sdmmc_fs_handle = esp32_sdmmc_fs_alloc(sdmmc_config, "/sdcard"); + check(data->esp32_sdmmc_fs_handle); 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, @@ -117,7 +119,7 @@ static error_t start(Device* device) { } static error_t stop(Device* device) { - ESP_LOGI(TAG, "stop %s", device->name); + LOG_I(TAG, "stop %s", device->name); auto* data = GET_DATA(device); if (file_system_is_mounted(data->file_system)) { diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp index 52ef49d1..cf4b8611 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp @@ -81,12 +81,12 @@ static error_t mount(void* data) { sd_pwr_ctrl_ldo_config_t ldo_config = { .ldo_chan_id = 4, // LDO4 is typically used for SDMMC on ESP32-S3 }; - esp_err_t pwr_err = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &data->pwr_ctrl_handle); + esp_err_t pwr_err = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &fs_data->pwr_ctrl_handle); if (pwr_err != ESP_OK) { LOG_E(TAG, "Failed to create SD power control driver, err=0x%x", pwr_err); return ERROR_NOT_SUPPORTED; } - host.pwr_ctrl_handle = data->pwr_ctrl_handle; + host.pwr_ctrl_handle = fs_data->pwr_ctrl_handle; #endif uint32_t slot_config_flags = 0; @@ -144,9 +144,9 @@ static error_t unmount(void* data) { fs_data->card = nullptr; #if SOC_SD_PWR_CTRL_SUPPORTED - if (data->pwr_ctrl_handle) { - sd_pwr_ctrl_del_on_chip_ldo(data->pwr_ctrl_handle); - data->pwr_ctrl_handle = nullptr; + if (fs_data->pwr_ctrl_handle) { + sd_pwr_ctrl_del_on_chip_ldo(fs_data->pwr_ctrl_handle); + fs_data->pwr_ctrl_handle = nullptr; } #endif @@ -157,13 +157,13 @@ static error_t unmount(void* data) { static bool is_mounted(void* data) { const auto* fs_data = GET_DATA(data); - if (fs_data == nullptr || fs_data->card == nullptr) return false; + if (fs_data->card == nullptr) return false; return sdmmc_get_status(fs_data->card) == ESP_OK; } 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->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; diff --git a/Tactility/Include/Tactility/Paths.h b/Tactility/Include/Tactility/Paths.h index f12c53e3..c3930db9 100644 --- a/Tactility/Include/Tactility/Paths.h +++ b/Tactility/Include/Tactility/Paths.h @@ -9,11 +9,7 @@ namespace tt { bool findFirstMountedSdCardPath(std::string& path); -bool hasMountedSdCard(); - -FileSystem* findFirstMountedSdcardFileSystem(); - -FileSystem* findFirstSdcardFileSystem(); +FileSystem* findSdcardFileSystem(bool mustBeMounted); std::string getSystemRootPath(); diff --git a/Tactility/Source/MountPoints.cpp b/Tactility/Source/MountPoints.cpp index bb9bf098..32a6866b 100644 --- a/Tactility/Source/MountPoints.cpp +++ b/Tactility/Source/MountPoints.cpp @@ -14,7 +14,6 @@ namespace tt::file { std::vector getFileSystemDirents() { std::vector dir_entries; - dir_entries.clear(); file_system_for_each(&dir_entries, [](auto* fs, void* context) { if (!file_system_is_mounted(fs)) return true; diff --git a/Tactility/Source/PartitionsEsp.cpp b/Tactility/Source/PartitionsEsp.cpp index 0d6357ad..73f0f11c 100644 --- a/Tactility/Source/PartitionsEsp.cpp +++ b/Tactility/Source/PartitionsEsp.cpp @@ -92,7 +92,8 @@ 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")); + static auto system_fs_data = PartitionFsData("/system"); + file_system_add(&partition_fs_api, &system_fs_data); } auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle); @@ -100,7 +101,8 @@ 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")); + static auto data_fs_data = PartitionFsData("/data"); + file_system_add(&partition_fs_api, &data_fs_data); } return system_result == ESP_OK && data_result == ESP_OK; diff --git a/Tactility/Source/Paths.cpp b/Tactility/Source/Paths.cpp index 9698c51d..8affa3c0 100644 --- a/Tactility/Source/Paths.cpp +++ b/Tactility/Source/Paths.cpp @@ -9,7 +9,7 @@ namespace tt { bool findFirstMountedSdCardPath(std::string& path) { - auto* fs = findFirstMountedSdcardFileSystem(); + auto* fs = findSdcardFileSystem(true); if (fs == nullptr) return false; char found_path[128]; if (file_system_get_path(fs, found_path, sizeof(found_path)) != ERROR_NONE) return false; @@ -17,27 +17,7 @@ bool findFirstMountedSdCardPath(std::string& 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 found; -} - -FileSystem* findFirstSdcardFileSystem() { +FileSystem* findSdcardFileSystem(bool mustBeMounted) { FileSystem* found = nullptr; file_system_for_each(&found, [](auto* fs, void* context) { char path[128]; @@ -49,6 +29,9 @@ FileSystem* findFirstSdcardFileSystem() { } return true; }); + if (found && mustBeMounted && !file_system_is_mounted(found)) { + return nullptr; + } return found; } diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 475e2e98..52ff1ef4 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -50,7 +50,6 @@ namespace service { // Primary namespace gps { extern const ServiceManifest manifest; } namespace wifi { extern const ServiceManifest manifest; } - namespace sdcard { extern const ServiceManifest manifest; } #ifdef ESP_PLATFORM namespace development { extern const ServiceManifest manifest; } #endif @@ -236,7 +235,7 @@ static void registerInstalledAppsFromFileSystems() { 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); + LOGGER.info("Registering apps from {}", app_path); registerInstalledApps(app_path); } return true; @@ -263,9 +262,6 @@ static void registerAndStartSecondaryServices() { static void registerAndStartPrimaryServices() { LOGGER.info("Registering and starting primary system services"); addService(service::gps::manifest); - if (hasMountedSdCard()) { - addService(service::sdcard::manifest); - } addService(service::wifi::manifest); #ifdef ESP_PLATFORM addService(service::development::manifest); diff --git a/Tactility/Source/app/fileselection/State.cpp b/Tactility/Source/app/fileselection/State.cpp index b2cf4afc..7dfa38ca 100644 --- a/Tactility/Source/app/fileselection/State.cpp +++ b/Tactility/Source/app/fileselection/State.cpp @@ -6,10 +6,11 @@ #include #include +#include #include +#include #include #include -#include namespace tt::app::fileselection { @@ -36,6 +37,12 @@ std::string State::getSelectedChildPath() const { bool State::setEntriesForPath(const std::string& path) { LOGGER.info("Changing path: {} -> {}", current_path, path); + auto lock = mutex.asScopedLock(); + if (!lock.lock(100)) { + LOGGER.error(LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "setEntriesForPath"); + return false; + } + /** * ESP32 does not have a root directory, so we have to create it manually. * We'll add the NVS Flash partitions and the binding for the sdcard. diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 289adb60..8e7f25d3 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -321,7 +321,6 @@ class SystemInfoApp final : public App { bool hasExternalMem = false; bool hasDataStorage = false; - bool hasSdcardStorage = false; bool hasSystemStorage = false; void updateMemory() { @@ -621,7 +620,6 @@ class SystemInfoApp final : public App { 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()); } diff --git a/Tactility/Source/hal/sdcard/SdCardDevice.cpp b/Tactility/Source/hal/sdcard/SdCardDevice.cpp index 15c6388e..1c147f5b 100644 --- a/Tactility/Source/hal/sdcard/SdCardDevice.cpp +++ b/Tactility/Source/hal/sdcard/SdCardDevice.cpp @@ -26,8 +26,10 @@ static bool is_mounted(void* data) { 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); + const auto mount_path = device->getMountPath(); + if (mount_path.size() >= out_path_size) return ERROR_BUFFER_OVERFLOW; + if (mount_path.empty()) return ERROR_INVALID_STATE; + strncpy(out_path, mount_path.c_str(), out_path_size); return ERROR_NONE; } @@ -44,6 +46,7 @@ SdCardDevice::SdCardDevice(MountBehaviour mountBehaviour) : mountBehaviour(mount } SdCardDevice::~SdCardDevice() { + check(!isMounted()); file_system_remove(fileSystem); } diff --git a/Tactility/Source/hal/usb/Usb.cpp b/Tactility/Source/hal/usb/Usb.cpp index 8cf803de..c52a0c52 100644 --- a/Tactility/Source/hal/usb/Usb.cpp +++ b/Tactility/Source/hal/usb/Usb.cpp @@ -42,8 +42,12 @@ sdmmc_card_t* getCard() { 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; + auto* sdmmc_card = esp32_sdmmc_get_card(device); + if (sdmmc_card) { + *sdcard = sdmmc_card; + return false; + } + return true; } return true; }); diff --git a/Tactility/Source/service/sdcard/Sdcard.cpp b/Tactility/Source/service/sdcard/Sdcard.cpp deleted file mode 100644 index fad5b172..00000000 --- a/Tactility/Source/service/sdcard/Sdcard.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tt::service::sdcard { - -static const auto LOGGER = Logger("SdcardService"); - -extern const ServiceManifest manifest; - -class SdCardService final : public Service { - - Mutex mutex; - std::unique_ptr updateTimer; - bool lastMountedState = false; - - bool lock(TickType_t timeout) const { - return mutex.lock(timeout); - } - - void unlock() const { - mutex.unlock(); - } - - void update() { - // TODO: Support multiple SD cards - auto* file_system = findFirstSdcardFileSystem(); - - if (lock(50)) { - auto is_mounted = file_system_is_mounted(file_system); - if (is_mounted != lastMountedState) { - lastMountedState = is_mounted; - } - unlock(); - } else { - LOGGER.warn(LOG_MESSAGE_MUTEX_LOCK_FAILED); - } - } - -public: - - bool onStart(ServiceContext& serviceContext) override { - auto* sdcard_fs = findFirstSdcardFileSystem(); - if (sdcard_fs == nullptr) { - LOGGER.warn("No SD card device found - not starting Service"); - return false; - } - - auto service = findServiceById(manifest.id); - updateTimer = std::make_unique(Timer::Type::Periodic, 1000, [service] { - service->update(); - }); - - // We want to try and scan more often in case of startup or scan lock failure - updateTimer->start(); - - return true; - } - - void onStop(ServiceContext& serviceContext) override { - if (updateTimer != nullptr) { - // Stop thread - updateTimer->stop(); - updateTimer = nullptr; - } - } -}; - -extern const ServiceManifest manifest = { - .id = "sdcard", - .createService = create -}; - -} // namespace diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index f8105c2a..0af4c200 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -164,7 +164,7 @@ class StatusbarService final : public Service { } void updateSdCardIcon() { - auto* sdcard_fs = findFirstSdcardFileSystem(); + auto* sdcard_fs = findSdcardFileSystem(false); // TODO: Support multiple SD cards if (sdcard_fs != nullptr) { auto mounted = file_system_is_mounted(sdcard_fs); @@ -174,7 +174,11 @@ class StatusbarService final : public Service { 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 + } else { + if (sdcard_last_icon != nullptr) { + lvgl::statusbar_icon_set_visibility(sdcard_icon_id, false); + sdcard_last_icon = nullptr; + } } } diff --git a/Tactility/Source/service/webserver/WebServerService.cpp b/Tactility/Source/service/webserver/WebServerService.cpp index c3d9190a..5a2f5523 100644 --- a/Tactility/Source/service/webserver/WebServerService.cpp +++ b/Tactility/Source/service/webserver/WebServerService.cpp @@ -775,7 +775,7 @@ esp_err_t WebServerService::handleFsList(httpd_req_t* request) { 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) { + if (file_system_is_mounted(fs) && file_system_get_path(fs, path, sizeof(path)) == ERROR_NONE && 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}"; diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 796259b2..04072a2f 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -423,6 +423,10 @@ const esp_elfsym main_symbols[] { ESP_ELFSYM_EXPORT(ledc_channel_config), ESP_ELFSYM_EXPORT(ledc_set_duty), ESP_ELFSYM_EXPORT(ledc_set_fade), + ESP_ELFSYM_EXPORT(ledc_set_fade_with_step), + ESP_ELFSYM_EXPORT(ledc_set_fade_with_time), + ESP_ELFSYM_EXPORT(ledc_set_fade_step_and_start), + ESP_ELFSYM_EXPORT(ledc_set_fade_time_and_start), ESP_ELFSYM_EXPORT(ledc_set_pin), ESP_ELFSYM_EXPORT(ledc_timer_config), ESP_ELFSYM_EXPORT(ledc_timer_pause), diff --git a/TactilityKernel/include/tactility/drivers/gpio.h b/TactilityKernel/include/tactility/drivers/gpio.h index e7a35771..2250725f 100644 --- a/TactilityKernel/include/tactility/drivers/gpio.h +++ b/TactilityKernel/include/tactility/drivers/gpio.h @@ -9,7 +9,7 @@ extern "C" { #include #include -#define GPIO_FLAGS_MASK 0x1f +#define GPIO_FLAGS_MASK 0xff #define GPIO_PIN_NONE -1 diff --git a/TactilityKernel/include/tactility/drivers/gpio_controller.h b/TactilityKernel/include/tactility/drivers/gpio_controller.h index 9cdd2883..82a2552e 100644 --- a/TactilityKernel/include/tactility/drivers/gpio_controller.h +++ b/TactilityKernel/include/tactility/drivers/gpio_controller.h @@ -206,7 +206,7 @@ error_t gpio_controller_deinit_descriptors(struct Device* device); * This means that the specific controller implementation cannot set the device's driver data, as it's already set by the GPIO controller base coded. * When calling init descriptors, the caller can pass a controller_context, which is an optional pointer that holds the implementation's internal data. * @param device the GPIO controller device - * @return ERROR_NONE if successful + * @return the context void pointer */ void* gpio_controller_get_controller_context(struct Device* device); diff --git a/TactilityKernel/include/tactility/drivers/wifi.h b/TactilityKernel/include/tactility/drivers/wifi.h index 9ecdcf5c..331ee03b 100644 --- a/TactilityKernel/include/tactility/drivers/wifi.h +++ b/TactilityKernel/include/tactility/drivers/wifi.h @@ -4,17 +4,36 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif struct Device; +enum WifiAuthenticationType { + WIFI_AUTHENTICATION_TYPE_OPEN = 0, + WIFI_AUTHENTICATION_TYPE_WEP, + WIFI_AUTHENTICATION_TYPE_WPA_PSK, + WIFI_AUTHENTICATION_TYPE_WPA2_PSK, + WIFI_AUTHENTICATION_TYPE_WPA_WPA2_PSK, + WIFI_AUTHENTICATION_TYPE_WPA2_ENTERPRISE, + WIFI_AUTHENTICATION_TYPE_WPA3_PSK, + WIFI_AUTHENTICATION_TYPE_WPA2_WPA3_PSK, + WIFI_AUTHENTICATION_TYPE_WAPI_PSK, + WIFI_AUTHENTICATION_TYPE_OWE, + WIFI_AUTHENTICATION_TYPE_WPA3_ENT_192, + WIFI_AUTHENTICATION_TYPE_WPA3_EXT_PSK, + WIFI_AUTHENTICATION_TYPE_WPA3_EXT_PSK_MIXED_MODE, + WIFI_AUTHENTICATION_TYPE_MAX +} wifi_auth_mode_t; + struct WifiApRecord { char ssid[32]; int8_t rssi; int32_t channel; - wifi_auth_mode_t auth_mode; + enum WifiAuthenticationType authentication_type; }; enum WifiRadioState { @@ -35,7 +54,7 @@ enum WifiAccessPointState { WIFI_ACCESS_POINT_STATE_STOPPED, }; -enum class WifiEventType { +enum WifiEventType { /** Radio state changed */ WIFI_EVENT_TYPE_RADIO_STATE_CHANGED, /** WifiStationState changed */ @@ -50,7 +69,7 @@ enum class WifiEventType { WIFI_EVENT_TYPE_SCAN_FINISHED, }; -enum class WifiStationConnectionError { +enum WifiStationConnectionError { WIFI_STATION_CONNECTION_ERROR_NONE, /** Wrong password */ WIFI_STATION_CONNECTION_ERROR_WRONG_CREDENTIALS, @@ -67,10 +86,10 @@ struct WifiEvent { enum WifiStationState station_state; enum WifiAccessPointState access_point_state; enum WifiStationConnectionError connection_error; - } + }; }; -typedef void (*WifiEventCallback)(struct Device* device, void* callback_context, WifiEvent event); +typedef void (*WifiEventCallback)(struct Device* device, void* callback_context, struct WifiEvent event); struct WifiApi { /** @@ -100,9 +119,9 @@ struct WifiApi { /** * Check if the device is currently scanning for access points. * @param[in] device the wifi device - * @return ERROR_NONE on success + * @return true when scanning */ - error_t (*is_scanning)(struct Device* device); + bool (*is_scanning)(struct Device* device); /** * Start a scan for access points. @@ -129,9 +148,9 @@ struct WifiApi { error_t (*station_get_ipv4_address)(struct Device* device, char* ipv4); /** - * Get the IPv6 address of the device. + * Get the IPv4 address of the device. * @param[in] device the device - * @param[out] ipv6 the buffer to store the IPv6 address (must be at least 33 bytes, will be null-terminated) + * @param[out] ipv4 the buffer to store the IPv4 address (must be at least 33 bytes, will be null-terminated) * @return ERROR_NONE on success */ error_t (*station_get_target_ssid)(struct Device* device, char* ssid); @@ -140,7 +159,7 @@ struct WifiApi { * Connect to an access point. * @param[in] device the wifi device * @param[in] ssid the SSID of the access point - * @param[in] password the password of the access point (must be at least 33 characters and null-terminated) + * @param[in] password the password of the access point (33 characters at most, including null-termination) * @param[in] channel the Wi-Fi channel to connect to (0 means "any" / no preference) * @return ERROR_NONE on success */ diff --git a/TactilityKernel/include/tactility/filesystem/file_system.h b/TactilityKernel/include/tactility/filesystem/file_system.h index c928e4b3..40a6bec7 100644 --- a/TactilityKernel/include/tactility/filesystem/file_system.h +++ b/TactilityKernel/include/tactility/filesystem/file_system.h @@ -11,25 +11,94 @@ extern "C" { struct FileSystem; +/** + * @brief File system API. + * + * Provides a set of function pointers to interact with a specific file system implementation. + */ struct FileSystemApi { + /** + * @brief Mounts the file system. + * @param[in] data file system private data + * @return ERROR_NONE on success, or an error code + */ error_t (*mount)(void* data); + + /** + * @brief Unmounts the file system. + * @param[in] data file system private data + * @return ERROR_NONE on success, or an error code + */ error_t (*unmount)(void* data); + + /** + * @brief Checks if the file system is mounted. + * @param[in] data file system private data + * @return true if mounted, false otherwise + */ bool (*is_mounted)(void* data); + + /** + * @brief Gets the mount path. + * @param[in] data file system private data + * @param[out] out_path buffer to store the path + * @param[in] out_path_size size of the output buffer + * @return ERROR_NONE on success, or an error code + */ error_t (*get_path)(void* data, char* out_path, size_t out_path_size); }; +/** + * @brief Registers a new file system. + * @param[in] fs_api the file system API implementation + * @param[in] data private data for the file system + * @return the registered FileSystem object + */ struct FileSystem* file_system_add(const struct FileSystemApi* fs_api, void* data); +/** + * @brief Removes a registered file system. + * @note The file system must be unmounted before removal. + * @param[in] fs the FileSystem object to remove + */ void file_system_remove(struct FileSystem* fs); +/** + * @brief Iterates over all registered file systems. + * @param[in] callback_context context passed to the callback + * @param[in] callback function called for each file system. Return true to continue, false to stop. + */ void file_system_for_each(void* callback_context, bool (*callback)(struct FileSystem* fs, void* context)); +/** + * @brief Mounts the file system. + * @param[in] fs the FileSystem object + * @return ERROR_NONE on success, or an error code + */ error_t file_system_mount(struct FileSystem* fs); +/** + * @warning Unmounting can fail (e.g. when the device is busy), so you might need to retry it. + * @brief Unmounts the file system. + * @param[in] fs the FileSystem object + * @return ERROR_NONE on success, or an error code + */ error_t file_system_unmount(struct FileSystem* fs); +/** + * @brief Checks if the file system is mounted. + * @param[in] fs the FileSystem object + * @return true if mounted, false otherwise + */ bool file_system_is_mounted(struct FileSystem* fs); +/** + * @brief Gets the mount path. + * @param[in] fs the FileSystem object + * @param[out] out_path buffer to store the path + * @param[in] out_path_size size of the output buffer + * @return ERROR_NONE on success, or an error code + */ error_t file_system_get_path(struct FileSystem* fs, char* out_path, size_t out_path_size); #ifdef __cplusplus diff --git a/TactilityKernel/source/drivers/gpio_controller.cpp b/TactilityKernel/source/drivers/gpio_controller.cpp index c2e2bfae..922aad6a 100644 --- a/TactilityKernel/source/drivers/gpio_controller.cpp +++ b/TactilityKernel/source/drivers/gpio_controller.cpp @@ -73,13 +73,18 @@ GpioDescriptor* gpio_descriptor_acquire( } error_t gpio_descriptor_release(GpioDescriptor* descriptor) { + auto* data = static_cast(device_get_driver_data(descriptor->controller)); + mutex_lock(&data->mutex); descriptor->owner_type = GPIO_OWNER_NONE; + mutex_unlock(&data->mutex); return ERROR_NONE; } error_t gpio_controller_get_pin_count(Device* device, uint32_t* count) { auto* data = static_cast(device_get_driver_data(device)); + mutex_lock(&data->mutex); *count = data->pin_count; + mutex_unlock(&data->mutex); return ERROR_NONE; } @@ -98,8 +103,8 @@ error_t gpio_controller_init_descriptors(Device* device, uint32_t pin_count, voi error_t gpio_controller_deinit_descriptors(Device* device) { auto* data = static_cast(device_get_driver_data(device)); - delete data; device_set_driver_data(device, nullptr); + delete data; return ERROR_NONE; } diff --git a/TactilityKernel/source/filesystem/file_system.cpp b/TactilityKernel/source/filesystem/file_system.cpp index b9e5926a..e144ab0a 100644 --- a/TactilityKernel/source/filesystem/file_system.cpp +++ b/TactilityKernel/source/filesystem/file_system.cpp @@ -11,57 +11,62 @@ struct FileSystem { 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; +// Global list of file systems and its mutex +struct FileSystemsLedger { + std::vector file_systems; + Mutex mutex {}; -static void ensure_mutex_initialized() { - if (!fs_mutex_initialized) { - mutex_construct(&fs_mutex); - fs_mutex_initialized = true; - } + FileSystemsLedger() { mutex_construct(&mutex); } + ~FileSystemsLedger() { mutex_destruct(&mutex); } + + void lock() { mutex_lock(&mutex); } + void unlock() { mutex_unlock(&mutex); } +}; + +static FileSystemsLedger& get_ledger() { + static FileSystemsLedger ledger; + return ledger; } extern "C" { FileSystem* file_system_add(const FileSystemApi* fs_api, void* data) { - ensure_mutex_initialized(); - mutex_lock(&fs_mutex); + auto& ledger = get_ledger(); + ledger.lock(); auto* fs = new(std::nothrow) struct FileSystem(); check(fs != nullptr); fs->api = fs_api; fs->data = data; - file_systems.push_back(fs); + ledger.file_systems.push_back(fs); - mutex_unlock(&fs_mutex); + ledger.unlock(); return fs; } void file_system_remove(FileSystem* fs) { check(!file_system_is_mounted(fs)); - ensure_mutex_initialized(); - mutex_lock(&fs_mutex); + auto& ledger = get_ledger(); + ledger.lock(); - auto it = std::ranges::find(file_systems, fs); - if (it != file_systems.end()) { - file_systems.erase(it); + auto it = std::ranges::find(ledger.file_systems, fs); + if (it != ledger.file_systems.end()) { + ledger.file_systems.erase(it); delete fs; } - mutex_unlock(&fs_mutex); + ledger.unlock(); } void file_system_for_each(void* callback_context, bool (*callback)(FileSystem* fs, void* context)) { - ensure_mutex_initialized(); - mutex_lock(&fs_mutex); + auto& ledger = get_ledger(); + ledger.lock(); - for (auto* fs : file_systems) { + for (auto* fs : ledger.file_systems) { if (!callback(fs, callback_context)) break; } - mutex_unlock(&fs_mutex); + ledger.unlock(); } error_t file_system_mount(FileSystem* fs) { diff --git a/TactilityKernel/source/kernel_symbols.c b/TactilityKernel/source/kernel_symbols.c index da2b3f77..7ed2a8eb 100644 --- a/TactilityKernel/source/kernel_symbols.c +++ b/TactilityKernel/source/kernel_symbols.c @@ -14,6 +14,10 @@ #include #include +#ifndef ESP_PLATFORM +#include +#endif + /** * This file is a C file instead of C++, so we can import all headers as C code. * The intent is to catch errors that only show up when compiling as C and not as C++.