diff --git a/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts b/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts index 50f8a5e5..60b38590 100644 --- a/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts +++ b/Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,18 @@ pin-sclk = <&gpio0 5 GPIO_FLAG_NONE>; }; + sdmmc0 { + compatible = "espressif,sdmmc"; + pin-clk = <&gpio 12 GPIO_FLAG_NONE>; + pin-cmd = <&gpio 16 GPIO_FLAG_NONE>; + pin-d0 = <&gpio 14 GPIO_FLAG_NONE>; + pin-d1 = <&gpio 17 GPIO_FLAG_NONE>; + pin-d2 = <&gpio 21 GPIO_FLAG_NONE>; + pin-d3 = <&gpio 18 GPIO_FLAG_NONE>; + bus-width = <4>; + max-open-files = <4>; + }; + stemma_qt: uart1 { compatible = "espressif,esp32-uart"; port = ; diff --git a/Platforms/platform-esp32/CMakeLists.txt b/Platforms/platform-esp32/CMakeLists.txt index e0445b4d..ac049c58 100644 --- a/Platforms/platform-esp32/CMakeLists.txt +++ b/Platforms/platform-esp32/CMakeLists.txt @@ -6,5 +6,5 @@ idf_component_register( SRCS ${SOURCES} INCLUDE_DIRS "include/" PRIV_INCLUDE_DIRS "private/" - REQUIRES TactilityKernel driver + REQUIRES TactilityKernel driver vfs fatfs ) diff --git a/Platforms/platform-esp32/bindings/espressif,sdmmc.yaml b/Platforms/platform-esp32/bindings/espressif,sdmmc.yaml new file mode 100644 index 00000000..9237402d --- /dev/null +++ b/Platforms/platform-esp32/bindings/espressif,sdmmc.yaml @@ -0,0 +1,57 @@ +description: ESP32 SDMMC + +compatible: "espressif,sdmmc" + +properties: + pin-clk: + type: phandle-array + required: true + pin-cmd: + type: phandle-array + required: true + pin-d0: + type: phandle-array + required: true + pin-d1: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d2: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d3: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d4: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d5: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d6: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-d7: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-cd: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + pin-wp: + type: phandle-array + default: GPIO_PIN_SPEC_NONE + bus-width: + type: int + required: true + description: Bus width in bits + max-open-files: + type: int + required: true + description: Maximum number of open files + wp-active-high: + type: boolean + default: false + description: Whether the WP pin is active high + enable-uhs: + type: boolean + default: false + description: Enable UHS mode \ No newline at end of file diff --git a/Platforms/platform-esp32/include/tactility/bindings/esp32_sdmmc.h b/Platforms/platform-esp32/include/tactility/bindings/esp32_sdmmc.h new file mode 100644 index 00000000..6b045aad --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/bindings/esp32_sdmmc.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(esp32_sdmmc, struct Esp32SdmmcConfig) + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h new file mode 100644 index 00000000..e2079c76 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_sdmmc.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct Esp32SdmmcConfig { + struct GpioPinSpec pin_clk; + struct GpioPinSpec pin_cmd; + struct GpioPinSpec pin_d0; + struct GpioPinSpec pin_d1; + struct GpioPinSpec pin_d2; + struct GpioPinSpec pin_d3; + struct GpioPinSpec pin_d4; + struct GpioPinSpec pin_d5; + struct GpioPinSpec pin_d6; + struct GpioPinSpec pin_d7; + struct GpioPinSpec pin_cd; + struct GpioPinSpec pin_wp; + uint8_t bus_width; + uint8_t max_open_files; + bool wp_active_high; + bool enable_uhs; +}; + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp new file mode 100644 index 00000000..2222bf48 --- /dev/null +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc.cpp @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include + +#include "tactility/drivers/gpio_descriptor.h" +#include +#include +#include + +#define TAG "esp32_sdmmc" + +#define GET_CONFIG(device) ((const struct Esp32SdmmcConfig*)device->config) +#define GET_DATA(device) ((struct Esp32SdmmcInternal*)device_get_driver_data(device)) + +extern "C" { + +error_t mount(Device* device) { + return ERROR_NONE; +} + +error_t unmount(Device* device) { + return ERROR_NONE; +} + +bool is_mounted(Device* device) { + return true; +} + +error_t get_mount_path(Device*, char* out_path) { + return ERROR_NONE; +} + +static const FileSystemApi sdmmc_filesystem_api = { + .mount = mount, + .unmount = unmount, + .is_mounted = is_mounted, + .get_mount_path = get_mount_path +}; + +struct Esp32SdmmcInternal { + RecursiveMutex mutex = {}; + bool initialized = false; + + // Pin descriptors + GpioDescriptor* pin_clk_descriptor = nullptr; + GpioDescriptor* pin_cmd_descriptor = nullptr; + GpioDescriptor* pin_d0_descriptor = nullptr; + GpioDescriptor* pin_d1_descriptor = nullptr; + GpioDescriptor* pin_d2_descriptor = nullptr; + GpioDescriptor* pin_d3_descriptor = nullptr; + GpioDescriptor* pin_d4_descriptor = nullptr; + GpioDescriptor* pin_d5_descriptor = nullptr; + GpioDescriptor* pin_d6_descriptor = nullptr; + GpioDescriptor* pin_d7_descriptor = nullptr; + GpioDescriptor* pin_cd_descriptor = nullptr; + GpioDescriptor* pin_wp_descriptor = nullptr; + + explicit Esp32SdmmcInternal() { + recursive_mutex_construct(&mutex); + } + + ~Esp32SdmmcInternal() { + cleanup_pins(); + recursive_mutex_destruct(&mutex); + } + + void cleanup_pins() { + release_pin(&pin_clk_descriptor); + release_pin(&pin_cmd_descriptor); + release_pin(&pin_d0_descriptor); + release_pin(&pin_d1_descriptor); + release_pin(&pin_d2_descriptor); + release_pin(&pin_d3_descriptor); + release_pin(&pin_d4_descriptor); + release_pin(&pin_d5_descriptor); + release_pin(&pin_d6_descriptor); + release_pin(&pin_d7_descriptor); + release_pin(&pin_cd_descriptor); + release_pin(&pin_wp_descriptor); + } + + void lock() { recursive_mutex_lock(&mutex); } + + error_t try_lock(TickType_t timeout) { return recursive_mutex_try_lock(&mutex, timeout); } + + void unlock() { recursive_mutex_unlock(&mutex); } +}; + + +static error_t start(Device* device) { + LOG_I(TAG, "start %s", device->name); + auto* data = new (std::nothrow) Esp32SdmmcInternal(); + if (!data) return ERROR_OUT_OF_MEMORY; + + device_set_driver_data(device, data); + + auto* sdmmc_config = GET_CONFIG(device); + + // Acquire pins from the specified GPIO pin specs. Optional pins are allowed. + bool pins_ok = + acquire_pin_or_set_null(sdmmc_config->pin_clk, &data->pin_clk_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_cmd, &data->pin_cmd_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d0, &data->pin_d0_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d1, &data->pin_d1_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d2, &data->pin_d2_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d3, &data->pin_d3_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d4, &data->pin_d4_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d5, &data->pin_d5_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d6, &data->pin_d6_descriptor) && + acquire_pin_or_set_null(sdmmc_config->pin_d7, &data->pin_d7_descriptor) && + 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(); + device_set_driver_data(device, nullptr); + delete data; + return ERROR_RESOURCE; + } + + // TODO: filesystem + + data->initialized = true; + return ERROR_NONE; +} + +static error_t stop(Device* device) { + ESP_LOGI(TAG, "stop %s", device->name); + auto* driver_data = GET_DATA(device); + auto* dts_config = GET_CONFIG(device); + + // TODO: filesystem + + driver_data->cleanup_pins(); + device_set_driver_data(device, nullptr); + delete driver_data; + return ERROR_NONE; +} + +extern Module platform_esp32_module; + +Driver esp32_spi_driver = { + .name = "esp32_sdmmc", + .compatible = (const char*[]) { "espressif,esp32-sdmmc", nullptr }, + .start_device = start, + .stop_device = stop, + .api = nullptr, + .device_type = nullptr, + .owner = &platform_esp32_module, + .internal = nullptr +}; + +} // extern "C" diff --git a/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp new file mode 100644 index 00000000..e7487e69 --- /dev/null +++ b/Platforms/platform-esp32/source/drivers/esp32_sdmmc_fs.cpp @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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) + +struct Esp32SdmmcFsInternal { + std::string mount_path {}; + sdmmc_card_t* card = nullptr; +}; + +static gpio_num_t to_native_pin(GpioPinSpec pin_spec) { + if (pin_spec.gpio_controller == nullptr) { return GPIO_NUM_NC; } + return static_cast(pin_spec.pin); +} + +extern "C" { + +error_t mount(Device* device, const char* mount_path) { + LOG_I(TAG, "Mounting %s", mount_path); + + auto* data = GET_DATA(device); + auto* config = GET_CONFIG(device); + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = config->max_open_files, + .allocation_unit_size = 0, // Default is sector size + .disk_status_check_enable = false, + .use_one_fat = false + }; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + uint32_t slot_config_flags = 0; + if (config->enable_uhs) slot_config_flags |= SDMMC_SLOT_FLAG_UHS1; + if (config->wp_active_high) slot_config_flags |= SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH; + + sdmmc_slot_config_t slot_config = { + .clk = to_native_pin(config->pin_clk), + .cmd = to_native_pin(config->pin_cmd), + .d0 = to_native_pin(config->pin_d0), + .d1 = to_native_pin(config->pin_d1), + .d2 = to_native_pin(config->pin_d2), + .d3 = to_native_pin(config->pin_d3), + .d4 = to_native_pin(config->pin_d4), + .d5 = to_native_pin(config->pin_d5), + .d6 = to_native_pin(config->pin_d6), + .d7 = to_native_pin(config->pin_d7), + .cd = to_native_pin(config->pin_cd), + .wp = to_native_pin(config->pin_wp), + .width = config->bus_width, + .flags = slot_config_flags + }; + + esp_err_t result = esp_vfs_fat_sdmmc_mount(mount_path, &host, &slot_config, &mount_config, &data->card); + + if (result != ESP_OK || data->card == nullptr) { + if (result == ESP_FAIL) { + LOG_E(TAG, "Mounting failed. Ensure the card is formatted with FAT."); + } else { + LOG_E(TAG, "Mounting failed: %s", esp_err_to_name(result)); + } + return false; + } + + data->mount_path = mount_path; + + return ERROR_NONE; +} + +error_t unmount(Device* device) { + auto* data = GET_DATA(device); + + 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()); + return ERROR_UNDEFINED; + } + + LOG_I(TAG, "Unmounted %s", data->mount_path.c_str()); + data->mount_path = ""; + data->card = nullptr; + + 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); + + // TODO: filesystem + + return ERROR_NONE; +} + +static error_t stop(Device* device) { + ESP_LOGI(TAG, "stop %s", device->name); + auto* driver_data = GET_DATA(device); + + // TODO: filesystem + + device_set_driver_data(device, nullptr); + delete driver_data; + return ERROR_NONE; +} + +extern Module platform_esp32_module; + +static const FileSystemApi 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 +}; + +} // extern "C" diff --git a/Platforms/platform-esp32/source/drivers/esp32_spi.cpp b/Platforms/platform-esp32/source/drivers/esp32_spi.cpp index e3f5440c..14551a27 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_spi.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_spi.cpp @@ -2,11 +2,11 @@ #include #include #include +#include #include "tactility/drivers/gpio_descriptor.h" #include #include -#include #include #include @@ -84,7 +84,7 @@ static error_t start(Device* device) { acquire_pin_or_set_null(dts_config->pin_hd, &data->hd_descriptor); if (!pins_ok) { - ESP_LOGE(TAG, "Failed to acquire required SPI pins"); + LOG_E(TAG, "Failed to acquire required SPI pins"); data->cleanup_pins(); device_set_driver_data(device, nullptr); delete data; @@ -113,7 +113,7 @@ static error_t start(Device* device) { data->cleanup_pins(); device_set_driver_data(device, nullptr); delete data; - ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret)); + LOG_E(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret)); return ERROR_RESOURCE; } @@ -122,7 +122,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* driver_data = GET_DATA(device); auto* dts_config = GET_CONFIG(device); diff --git a/TactilityKernel/include/tactility/drivers/file_system.h b/TactilityKernel/include/tactility/drivers/file_system.h index f1d8dee1..86ab008c 100644 --- a/TactilityKernel/include/tactility/drivers/file_system.h +++ b/TactilityKernel/include/tactility/drivers/file_system.h @@ -1,14 +1,21 @@ +// 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); + 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); + error_t (*get_mount_path)(struct Device*, char* out_path, size_t out_path_size); }; extern const struct DeviceType FILE_SYSTEM_TYPE; @@ -20,3 +27,7 @@ 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); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/source/drivers/file_system.cpp b/TactilityKernel/source/drivers/file_system.cpp index 09a9f8ac..4b30aa47 100644 --- a/TactilityKernel/source/drivers/file_system.cpp +++ b/TactilityKernel/source/drivers/file_system.cpp @@ -1,27 +1,32 @@ #include +#include -#define INTERNAL_API(driver) ((struct FileSystem*)(driver)->api) +#define INTERNAL_API(driver) ((FileSystemApi*)(driver)->api) -error_t file_system_mount(struct Device* device) { +extern "C" { + +error_t file_system_mount(Device* device) { const auto* driver = device_get_driver(device); return INTERNAL_API(driver)->mount(device); } -error_t file_system_unmount(struct Device* device) { +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(struct Device* 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(struct Device*, char* out_path) { +error_t file_system_get_mount_path(Device* device, char* out_path) { const auto* driver = device_get_driver(device); return INTERNAL_API(driver)->get_mount_path(device, out_path); } -const struct DeviceType FILE_SYSTEM_TYPE { +const DeviceType FILE_SYSTEM_TYPE { .name = "file-system" }; + +} // extern "C"