mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 09:25:06 +00:00
Compare commits
4 Commits
f12a52ebb9
...
f8f01edc48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8f01edc48 | ||
|
|
4b48ec8746 | ||
|
|
9efc7c7f8a | ||
|
|
c42b43e1c3 |
@ -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);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
#include <soc/soc_caps.h>
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
|
||||
#include <sd_protocol_types.h>
|
||||
@ -29,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
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
#include <soc/soc_caps.h>
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
|
||||
|
||||
@ -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<gpio_num_t>(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<gpio_num_t>(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<gpio_num_t>(descriptor->pin));
|
||||
|
||||
auto esp_error = gpio_isr_handler_add(static_cast<gpio_num_t>(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) {
|
||||
static error_t remove_callback(GpioDescriptor* descriptor) {
|
||||
auto esp_error = gpio_isr_handler_remove(static_cast<gpio_num_t>(descriptor->pin));
|
||||
if (esp_error == ESP_OK) {
|
||||
auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
|
||||
auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin));
|
||||
if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) {
|
||||
check(internal->isr_service_ref_count > 0);
|
||||
internal->isr_service_ref_count--;
|
||||
if (internal->isr_service_ref_count == 0) {
|
||||
gpio_uninstall_isr_service();
|
||||
@ -139,21 +135,42 @@ 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<gpio_num_t>(descriptor->pin));
|
||||
return esp_err_to_error(esp_error);
|
||||
}
|
||||
|
||||
static error_t disable_interrupt(GpioDescriptor* descriptor) {
|
||||
auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin));
|
||||
return esp_err_to_error(esp_error);
|
||||
}
|
||||
|
||||
static error_t start(Device* device) {
|
||||
ESP_LOGI(TAG, "start %s", device->name);
|
||||
LOG_I(TAG, "start %s", device->name);
|
||||
const Esp32GpioConfig* config = GET_CONFIG(device);
|
||||
auto* internal = new Esp32GpioInternal();
|
||||
return gpio_controller_init_descriptors(device, config->gpioCount, internal);
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
ESP_LOGI(TAG, "stop %s", device->name);
|
||||
LOG_I(TAG, "stop %s", device->name);
|
||||
const Esp32GpioConfig* config = GET_CONFIG(device);
|
||||
auto* internal = static_cast<Esp32GpioInternal*>(gpio_controller_get_controller_context(device));
|
||||
|
||||
// First, remove all ISR handlers to prevent callbacks during cleanup
|
||||
for (uint8_t i = 0; i < config->gpioCount; ++i) {
|
||||
gpio_isr_handler_remove(static_cast<gpio_num_t>(i));
|
||||
}
|
||||
|
||||
// Then uninstall ISR service
|
||||
if (internal->isr_service_ref_count > 0) {
|
||||
gpio_uninstall_isr_service();
|
||||
}
|
||||
delete internal;
|
||||
|
||||
// Now safe to deinit descriptors and delete internal
|
||||
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
|
||||
delete internal;
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <soc/soc_caps.h>
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <tactility/concurrent/recursive_mutex.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/esp32_gpio_helpers.h>
|
||||
#include <tactility/drivers/esp32_sdmmc.h>
|
||||
#include <tactility/drivers/esp32_sdmmc_fs.h>
|
||||
#include <tactility/concurrent/recursive_mutex.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include "tactility/drivers/gpio_descriptor.h"
|
||||
#include <new>
|
||||
#include <tactility/drivers/esp32_gpio_helpers.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#define TAG "esp32_sdmmc"
|
||||
|
||||
@ -45,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() {
|
||||
@ -65,8 +67,6 @@ struct Esp32SdmmcInternal {
|
||||
|
||||
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); }
|
||||
};
|
||||
|
||||
@ -76,6 +76,7 @@ static error_t start(Device* device) {
|
||||
auto* data = new (std::nothrow) Esp32SdmmcInternal();
|
||||
if (!data) return ERROR_OUT_OF_MEMORY;
|
||||
|
||||
data->lock();
|
||||
device_set_driver_data(device, data);
|
||||
|
||||
auto* sdmmc_config = GET_CONFIG(device);
|
||||
@ -99,11 +100,20 @@ static error_t start(Device* device) {
|
||||
LOG_E(TAG, "Failed to acquire required one or more pins");
|
||||
data->cleanup_pins();
|
||||
device_set_driver_data(device, nullptr);
|
||||
data->unlock();
|
||||
delete data;
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
data->esp32_sdmmc_fs_handle = esp32_sdmmc_fs_alloc(sdmmc_config, "/sdcard");
|
||||
if (!data->esp32_sdmmc_fs_handle) {
|
||||
data->cleanup_pins();
|
||||
device_set_driver_data(device, nullptr);
|
||||
data->unlock();
|
||||
delete data;
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
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,
|
||||
@ -112,17 +122,21 @@ static error_t start(Device* device) {
|
||||
}
|
||||
|
||||
data->initialized = true;
|
||||
data->unlock();
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
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);
|
||||
auto* dts_config = GET_CONFIG(device);
|
||||
if (!data) return ERROR_NONE;
|
||||
|
||||
data->lock();
|
||||
|
||||
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");
|
||||
data->unlock();
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
}
|
||||
@ -136,6 +150,8 @@ static error_t stop(Device* device) {
|
||||
|
||||
data->cleanup_pins();
|
||||
device_set_driver_data(device, nullptr);
|
||||
|
||||
data->unlock();
|
||||
delete data;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
@ -143,7 +159,13 @@ static error_t stop(Device* device) {
|
||||
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);
|
||||
if (!data) return nullptr;
|
||||
|
||||
data->lock();
|
||||
auto* card = esp32_sdmmc_fs_get_card(data->esp32_sdmmc_fs_handle);
|
||||
data->unlock();
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
extern Module platform_esp32_module;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <soc/soc_caps.h>
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/esp32_sdmmc.h>
|
||||
@ -80,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;
|
||||
@ -143,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
|
||||
|
||||
@ -156,13 +157,12 @@ 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->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;
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
#include <soc/soc_caps.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver esp32_gpio_driver;
|
||||
|
||||
@ -9,11 +9,7 @@ namespace tt {
|
||||
|
||||
bool findFirstMountedSdCardPath(std::string& path);
|
||||
|
||||
bool hasMountedSdCard();
|
||||
|
||||
FileSystem* findFirstMountedSdcardFileSystem();
|
||||
|
||||
FileSystem* findFirstSdcardFileSystem();
|
||||
FileSystem* findSdcardFileSystem(bool mustBeMounted);
|
||||
|
||||
std::string getSystemRootPath();
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ namespace tt::file {
|
||||
|
||||
std::vector<dirent> getFileSystemDirents() {
|
||||
std::vector<dirent> dir_entries;
|
||||
dir_entries.clear();
|
||||
|
||||
file_system_for_each(&dir_entries, [](auto* fs, void* context) {
|
||||
if (!file_system_is_mounted(fs)) return true;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<FileSystem**>(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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -6,10 +6,11 @@
|
||||
#include <Tactility/MountPoints.h>
|
||||
#include <Tactility/kernel/Platform.h>
|
||||
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <dirent.h>
|
||||
|
||||
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.
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
@ -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<SdCardDevice*>(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;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include <soc/soc_caps.h>
|
||||
|
||||
#include <Tactility/hal/usb/Usb.h>
|
||||
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
||||
#include <Tactility/hal/usb/UsbTusb.h>
|
||||
@ -40,10 +42,14 @@ 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<sdmmc_card_t**>(context);
|
||||
*sdcard = esp32_sdmmc_get_card(device);
|
||||
auto* sdmmc_card = esp32_sdmmc_get_card(device);
|
||||
if (sdmmc_card) {
|
||||
*sdcard = sdmmc_card;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/Paths.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
#include <Tactility/service/ServiceContext.h>
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
|
||||
namespace tt::service::sdcard {
|
||||
|
||||
static const auto LOGGER = Logger("SdcardService");
|
||||
|
||||
extern const ServiceManifest manifest;
|
||||
|
||||
class SdCardService final : public Service {
|
||||
|
||||
Mutex mutex;
|
||||
std::unique_ptr<Timer> 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<SdCardService>(manifest.id);
|
||||
updateTimer = std::make_unique<Timer>(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<SdCardService>
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<FsIterContext*>(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}";
|
||||
|
||||
@ -416,7 +416,6 @@ const esp_elfsym main_symbols[] {
|
||||
// miniz.h
|
||||
ESP_ELFSYM_EXPORT(tinfl_decompress),
|
||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_callback),
|
||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_heap),
|
||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_mem),
|
||||
// ledc
|
||||
ESP_ELFSYM_EXPORT(ledc_update_duty),
|
||||
@ -424,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),
|
||||
|
||||
@ -9,7 +9,7 @@ extern "C" {
|
||||
#include <tactility/device.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define GPIO_FLAGS_MASK 0x1f
|
||||
#define GPIO_FLAGS_MASK 0xff
|
||||
|
||||
#define GPIO_PIN_NONE -1
|
||||
|
||||
|
||||
@ -203,10 +203,10 @@ error_t gpio_controller_deinit_descriptors(struct Device* device);
|
||||
|
||||
/**
|
||||
* Unlike other drivers, a GPIO controller's internal data is created and set by gpio_controller_init_descriptors()
|
||||
* 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.
|
||||
* 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);
|
||||
|
||||
|
||||
@ -4,17 +4,36 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
struct WifiApRecord {
|
||||
char ssid[32];
|
||||
char ssid[33]; // 32 bytes + null terminator
|
||||
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.
|
||||
* @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)
|
||||
* Get the SSID of the access point the device is currently connected to.
|
||||
* @param[in] device the wifi device
|
||||
* @param[out] ssid the buffer to store the SSID (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
|
||||
* @param[in] channel the Wi-Fi channel to connect to (0 means "any" / no preference)
|
||||
* @return ERROR_NONE on success
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -73,13 +73,18 @@ GpioDescriptor* gpio_descriptor_acquire(
|
||||
}
|
||||
|
||||
error_t gpio_descriptor_release(GpioDescriptor* descriptor) {
|
||||
auto* data = static_cast<struct GpioControllerData*>(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<struct GpioControllerData*>(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<struct GpioControllerData*>(device_get_driver_data(device));
|
||||
delete data;
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete data;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <tactility/concurrent/recursive_mutex.h>
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <vector>
|
||||
|
||||
// Define the internal FileSystem structure
|
||||
struct FileSystem {
|
||||
@ -11,57 +12,61 @@ 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<FileSystem*> file_systems;
|
||||
// Global list of file systems and its mutex
|
||||
struct FileSystemsLedger {
|
||||
std::vector<FileSystem*> file_systems;
|
||||
// Use recursive mutex so that file_system_for_each() can lock within the callback
|
||||
RecursiveMutex mutex {};
|
||||
|
||||
static void ensure_mutex_initialized() {
|
||||
if (!fs_mutex_initialized) {
|
||||
mutex_construct(&fs_mutex);
|
||||
fs_mutex_initialized = true;
|
||||
}
|
||||
FileSystemsLedger() { recursive_mutex_construct(&mutex); }
|
||||
~FileSystemsLedger() { recursive_mutex_destruct(&mutex); }
|
||||
|
||||
void lock() { recursive_mutex_lock(&mutex); }
|
||||
void unlock() { recursive_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);
|
||||
|
||||
for (auto* fs : file_systems) {
|
||||
auto& ledger = get_ledger();
|
||||
ledger.lock();
|
||||
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) {
|
||||
|
||||
@ -14,6 +14,10 @@
|
||||
#include <tactility/filesystem/file_system.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
#include <tactility/log.h>
|
||||
#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++.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user