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) {
|
static void initExpander0(::Device* io_expander0) {
|
||||||
auto* rf_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL, GPIO_OWNER_GPIO);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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(rf_pin, GPIO_FLAG_DIRECTION_OUTPUT);
|
||||||
gpio_descriptor_set_flags(speaker_enable_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
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
#if SOC_SDMMC_HOST_SUPPORTED
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
|
||||||
#include <sd_protocol_types.h>
|
#include <sd_protocol_types.h>
|
||||||
@ -29,6 +30,11 @@ struct Esp32SdmmcConfig {
|
|||||||
bool enable_uhs;
|
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);
|
sdmmc_card_t* esp32_sdmmc_get_card(struct Device* device);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
#if SOC_SDMMC_HOST_SUPPORTED
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
#include <tactility/filesystem/file_system.h>
|
#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) {
|
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);
|
auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
|
||||||
if (internal->isr_service_ref_count == 0) {
|
if (internal->isr_service_ref_count == 0) {
|
||||||
auto esp_error = gpio_install_isr_service(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);
|
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) {
|
if (esp_error == ESP_OK) {
|
||||||
internal->isr_service_ref_count++;
|
internal->isr_service_ref_count++;
|
||||||
|
} else if (internal->isr_service_ref_count == 0) {
|
||||||
|
gpio_uninstall_isr_service();
|
||||||
}
|
}
|
||||||
|
|
||||||
return esp_err_to_error(esp_error);
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t disable_interrupt(GpioDescriptor* descriptor) {
|
static error_t remove_callback(GpioDescriptor* descriptor) {
|
||||||
auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
|
auto esp_error = gpio_isr_handler_remove(static_cast<gpio_num_t>(descriptor->pin));
|
||||||
auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin));
|
if (esp_error == ESP_OK) {
|
||||||
if (esp_error == ESP_OK && internal->isr_service_ref_count > 0) {
|
auto* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
|
||||||
|
check(internal->isr_service_ref_count > 0);
|
||||||
internal->isr_service_ref_count--;
|
internal->isr_service_ref_count--;
|
||||||
if (internal->isr_service_ref_count == 0) {
|
if (internal->isr_service_ref_count == 0) {
|
||||||
gpio_uninstall_isr_service();
|
gpio_uninstall_isr_service();
|
||||||
@ -139,21 +135,42 @@ static error_t disable_interrupt(GpioDescriptor* descriptor) {
|
|||||||
return esp_err_to_error(esp_error);
|
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) {
|
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);
|
const Esp32GpioConfig* config = GET_CONFIG(device);
|
||||||
auto* internal = new Esp32GpioInternal();
|
auto* internal = new Esp32GpioInternal();
|
||||||
return gpio_controller_init_descriptors(device, config->gpioCount, internal);
|
return gpio_controller_init_descriptors(device, config->gpioCount, internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t stop(Device* device) {
|
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));
|
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) {
|
if (internal->isr_service_ref_count > 0) {
|
||||||
gpio_uninstall_isr_service();
|
gpio_uninstall_isr_service();
|
||||||
}
|
}
|
||||||
delete internal;
|
|
||||||
|
// Now safe to deinit descriptors and delete internal
|
||||||
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
|
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
|
||||||
|
delete internal;
|
||||||
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
#if SOC_SDMMC_HOST_SUPPORTED
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <tactility/concurrent/recursive_mutex.h>
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/drivers/esp32_gpio_helpers.h>
|
||||||
#include <tactility/drivers/esp32_sdmmc.h>
|
#include <tactility/drivers/esp32_sdmmc.h>
|
||||||
#include <tactility/drivers/esp32_sdmmc_fs.h>
|
#include <tactility/drivers/esp32_sdmmc_fs.h>
|
||||||
#include <tactility/concurrent/recursive_mutex.h>
|
#include <tactility/drivers/gpio_descriptor.h>
|
||||||
#include <tactility/log.h>
|
|
||||||
|
|
||||||
#include "tactility/drivers/gpio_descriptor.h"
|
|
||||||
#include <new>
|
|
||||||
#include <tactility/drivers/esp32_gpio_helpers.h>
|
|
||||||
#include <tactility/filesystem/file_system.h>
|
#include <tactility/filesystem/file_system.h>
|
||||||
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#define TAG "esp32_sdmmc"
|
#define TAG "esp32_sdmmc"
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ struct Esp32SdmmcInternal {
|
|||||||
~Esp32SdmmcInternal() {
|
~Esp32SdmmcInternal() {
|
||||||
cleanup_pins();
|
cleanup_pins();
|
||||||
recursive_mutex_destruct(&mutex);
|
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() {
|
void cleanup_pins() {
|
||||||
@ -65,8 +67,6 @@ struct Esp32SdmmcInternal {
|
|||||||
|
|
||||||
void lock() { recursive_mutex_lock(&mutex); }
|
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); }
|
void unlock() { recursive_mutex_unlock(&mutex); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,6 +76,7 @@ static error_t start(Device* device) {
|
|||||||
auto* data = new (std::nothrow) Esp32SdmmcInternal();
|
auto* data = new (std::nothrow) Esp32SdmmcInternal();
|
||||||
if (!data) return ERROR_OUT_OF_MEMORY;
|
if (!data) return ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
data->lock();
|
||||||
device_set_driver_data(device, data);
|
device_set_driver_data(device, data);
|
||||||
|
|
||||||
auto* sdmmc_config = GET_CONFIG(device);
|
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");
|
LOG_E(TAG, "Failed to acquire required one or more pins");
|
||||||
data->cleanup_pins();
|
data->cleanup_pins();
|
||||||
device_set_driver_data(device, nullptr);
|
device_set_driver_data(device, nullptr);
|
||||||
|
data->unlock();
|
||||||
delete data;
|
delete data;
|
||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->esp32_sdmmc_fs_handle = esp32_sdmmc_fs_alloc(sdmmc_config, "/sdcard");
|
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);
|
data->file_system = file_system_add(&esp32_sdmmc_fs_api, data->esp32_sdmmc_fs_handle);
|
||||||
if (file_system_mount(data->file_system) != ERROR_NONE) {
|
if (file_system_mount(data->file_system) != ERROR_NONE) {
|
||||||
// Error is not recoverable at the time, but it might be recoverable later,
|
// 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->initialized = true;
|
||||||
|
data->unlock();
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t stop(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);
|
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_is_mounted(data->file_system)) {
|
||||||
if (file_system_unmount(data->file_system) != ERROR_NONE) {
|
if (file_system_unmount(data->file_system) != ERROR_NONE) {
|
||||||
LOG_E(TAG, "Failed to unmount SD card filesystem");
|
LOG_E(TAG, "Failed to unmount SD card filesystem");
|
||||||
|
data->unlock();
|
||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +150,8 @@ static error_t stop(Device* device) {
|
|||||||
|
|
||||||
data->cleanup_pins();
|
data->cleanup_pins();
|
||||||
device_set_driver_data(device, nullptr);
|
device_set_driver_data(device, nullptr);
|
||||||
|
|
||||||
|
data->unlock();
|
||||||
delete data;
|
delete data;
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
@ -143,7 +159,13 @@ static error_t stop(Device* device) {
|
|||||||
sdmmc_card_t* esp32_sdmmc_get_card(Device* device) {
|
sdmmc_card_t* esp32_sdmmc_get_card(Device* device) {
|
||||||
if (!device_is_ready(device)) return nullptr;
|
if (!device_is_ready(device)) return nullptr;
|
||||||
auto* data = GET_DATA(device);
|
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;
|
extern Module platform_esp32_module;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
#if SOC_SDMMC_HOST_SUPPORTED
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/drivers/esp32_sdmmc.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 = {
|
sd_pwr_ctrl_ldo_config_t ldo_config = {
|
||||||
.ldo_chan_id = 4, // LDO4 is typically used for SDMMC on ESP32-S3
|
.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) {
|
if (pwr_err != ESP_OK) {
|
||||||
LOG_E(TAG, "Failed to create SD power control driver, err=0x%x", pwr_err);
|
LOG_E(TAG, "Failed to create SD power control driver, err=0x%x", pwr_err);
|
||||||
return ERROR_NOT_SUPPORTED;
|
return ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
host.pwr_ctrl_handle = data->pwr_ctrl_handle;
|
host.pwr_ctrl_handle = fs_data->pwr_ctrl_handle;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t slot_config_flags = 0;
|
uint32_t slot_config_flags = 0;
|
||||||
@ -143,9 +144,9 @@ static error_t unmount(void* data) {
|
|||||||
fs_data->card = nullptr;
|
fs_data->card = nullptr;
|
||||||
|
|
||||||
#if SOC_SD_PWR_CTRL_SUPPORTED
|
#if SOC_SD_PWR_CTRL_SUPPORTED
|
||||||
if (data->pwr_ctrl_handle) {
|
if (fs_data->pwr_ctrl_handle) {
|
||||||
sd_pwr_ctrl_del_on_chip_ldo(data->pwr_ctrl_handle);
|
sd_pwr_ctrl_del_on_chip_ldo(fs_data->pwr_ctrl_handle);
|
||||||
data->pwr_ctrl_handle = nullptr;
|
fs_data->pwr_ctrl_handle = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -156,13 +157,12 @@ static error_t unmount(void* data) {
|
|||||||
|
|
||||||
static bool is_mounted(void* data) {
|
static bool is_mounted(void* data) {
|
||||||
const auto* fs_data = GET_DATA(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;
|
return sdmmc_get_status(fs_data->card) == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t get_path(void* data, char* out_path, size_t out_path_size) {
|
static error_t get_path(void* data, char* out_path, size_t out_path_size) {
|
||||||
const auto* fs_data = GET_DATA(data);
|
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;
|
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);
|
strncpy(out_path, fs_data->mount_path.c_str(), out_path_size);
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
#include <tactility/driver.h>
|
#include <tactility/driver.h>
|
||||||
#include <tactility/module.h>
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
extern Driver esp32_gpio_driver;
|
extern Driver esp32_gpio_driver;
|
||||||
|
|||||||
@ -9,11 +9,7 @@ namespace tt {
|
|||||||
|
|
||||||
bool findFirstMountedSdCardPath(std::string& path);
|
bool findFirstMountedSdCardPath(std::string& path);
|
||||||
|
|
||||||
bool hasMountedSdCard();
|
FileSystem* findSdcardFileSystem(bool mustBeMounted);
|
||||||
|
|
||||||
FileSystem* findFirstMountedSdcardFileSystem();
|
|
||||||
|
|
||||||
FileSystem* findFirstSdcardFileSystem();
|
|
||||||
|
|
||||||
std::string getSystemRootPath();
|
std::string getSystemRootPath();
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,6 @@ namespace tt::file {
|
|||||||
|
|
||||||
std::vector<dirent> getFileSystemDirents() {
|
std::vector<dirent> getFileSystemDirents() {
|
||||||
std::vector<dirent> dir_entries;
|
std::vector<dirent> dir_entries;
|
||||||
dir_entries.clear();
|
|
||||||
|
|
||||||
file_system_for_each(&dir_entries, [](auto* fs, void* context) {
|
file_system_for_each(&dir_entries, [](auto* fs, void* context) {
|
||||||
if (!file_system_is_mounted(fs)) return true;
|
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));
|
LOGGER.error("Failed to mount /system ({})", esp_err_to_name(system_result));
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Mounted /system");
|
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);
|
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));
|
LOGGER.error("Failed to mount /data ({})", esp_err_to_name(data_result));
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Mounted /data");
|
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;
|
return system_result == ESP_OK && data_result == ESP_OK;
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
namespace tt {
|
namespace tt {
|
||||||
|
|
||||||
bool findFirstMountedSdCardPath(std::string& path) {
|
bool findFirstMountedSdCardPath(std::string& path) {
|
||||||
auto* fs = findFirstMountedSdcardFileSystem();
|
auto* fs = findSdcardFileSystem(true);
|
||||||
if (fs == nullptr) return false;
|
if (fs == nullptr) return false;
|
||||||
char found_path[128];
|
char found_path[128];
|
||||||
if (file_system_get_path(fs, found_path, sizeof(found_path)) != ERROR_NONE) return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasMountedSdCard() {
|
FileSystem* findSdcardFileSystem(bool mustBeMounted) {
|
||||||
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* found = nullptr;
|
FileSystem* found = nullptr;
|
||||||
file_system_for_each(&found, [](auto* fs, void* context) {
|
file_system_for_each(&found, [](auto* fs, void* context) {
|
||||||
char path[128];
|
char path[128];
|
||||||
@ -49,6 +29,9 @@ FileSystem* findFirstSdcardFileSystem() {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
if (found && mustBeMounted && !file_system_is_mounted(found)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,6 @@ namespace service {
|
|||||||
// Primary
|
// Primary
|
||||||
namespace gps { extern const ServiceManifest manifest; }
|
namespace gps { extern const ServiceManifest manifest; }
|
||||||
namespace wifi { extern const ServiceManifest manifest; }
|
namespace wifi { extern const ServiceManifest manifest; }
|
||||||
namespace sdcard { extern const ServiceManifest manifest; }
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
namespace development { extern const ServiceManifest manifest; }
|
namespace development { extern const ServiceManifest manifest; }
|
||||||
#endif
|
#endif
|
||||||
@ -236,7 +235,7 @@ static void registerInstalledAppsFromFileSystems() {
|
|||||||
if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true;
|
if (file_system_get_path(fs, path, sizeof(path)) != ERROR_NONE) return true;
|
||||||
const auto app_path = std::format("{}/app", path);
|
const auto app_path = std::format("{}/app", path);
|
||||||
if (!app_path.starts_with(file::MOUNT_POINT_SYSTEM) && file::isDirectory(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);
|
registerInstalledApps(app_path);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -263,9 +262,6 @@ static void registerAndStartSecondaryServices() {
|
|||||||
static void registerAndStartPrimaryServices() {
|
static void registerAndStartPrimaryServices() {
|
||||||
LOGGER.info("Registering and starting primary system services");
|
LOGGER.info("Registering and starting primary system services");
|
||||||
addService(service::gps::manifest);
|
addService(service::gps::manifest);
|
||||||
if (hasMountedSdCard()) {
|
|
||||||
addService(service::sdcard::manifest);
|
|
||||||
}
|
|
||||||
addService(service::wifi::manifest);
|
addService(service::wifi::manifest);
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
addService(service::development::manifest);
|
addService(service::development::manifest);
|
||||||
|
|||||||
@ -6,10 +6,11 @@
|
|||||||
#include <Tactility/MountPoints.h>
|
#include <Tactility/MountPoints.h>
|
||||||
#include <Tactility/kernel/Platform.h>
|
#include <Tactility/kernel/Platform.h>
|
||||||
|
|
||||||
|
#include <Tactility/LogMessages.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
namespace tt::app::fileselection {
|
namespace tt::app::fileselection {
|
||||||
|
|
||||||
@ -36,6 +37,12 @@ std::string State::getSelectedChildPath() const {
|
|||||||
bool State::setEntriesForPath(const std::string& path) {
|
bool State::setEntriesForPath(const std::string& path) {
|
||||||
LOGGER.info("Changing path: {} -> {}", current_path, 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.
|
* 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.
|
* 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 hasExternalMem = false;
|
||||||
bool hasDataStorage = false;
|
bool hasDataStorage = false;
|
||||||
bool hasSdcardStorage = false;
|
|
||||||
bool hasSystemStorage = false;
|
bool hasSystemStorage = false;
|
||||||
|
|
||||||
void updateMemory() {
|
void updateMemory() {
|
||||||
@ -621,7 +620,6 @@ class SystemInfoApp final : public App {
|
|||||||
|
|
||||||
std::string sdcard_path;
|
std::string sdcard_path;
|
||||||
if (findFirstMountedSdCardPath(sdcard_path) && esp_vfs_fat_info(sdcard_path.c_str(), &storage_total, &storage_free) == ESP_OK) {
|
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());
|
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) {
|
static error_t get_path(void* data, char* out_path, size_t out_path_size) {
|
||||||
auto* device = static_cast<SdCardDevice*>(data);
|
auto* device = static_cast<SdCardDevice*>(data);
|
||||||
if (device->getMountPath().size() >= out_path_size) return ERROR_BUFFER_OVERFLOW;
|
const auto mount_path = device->getMountPath();
|
||||||
strncpy(out_path, device->getMountPath().c_str(), out_path_size);
|
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;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
|
|
||||||
|
#include <soc/soc_caps.h>
|
||||||
|
|
||||||
#include <Tactility/hal/usb/Usb.h>
|
#include <Tactility/hal/usb/Usb.h>
|
||||||
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
||||||
#include <Tactility/hal/usb/UsbTusb.h>
|
#include <Tactility/hal/usb/UsbTusb.h>
|
||||||
@ -40,8 +42,12 @@ sdmmc_card_t* getCard() {
|
|||||||
device_for_each(&sdcard, [](auto* device, void* context) {
|
device_for_each(&sdcard, [](auto* device, void* context) {
|
||||||
if (device_is_ready(device) && device_is_compatible(device, "espressif,esp32-sdmmc")) {
|
if (device_is_ready(device) && device_is_compatible(device, "espressif,esp32-sdmmc")) {
|
||||||
auto** sdcard = static_cast<sdmmc_card_t**>(context);
|
auto** sdcard = static_cast<sdmmc_card_t**>(context);
|
||||||
*sdcard = esp32_sdmmc_get_card(device);
|
auto* sdmmc_card = esp32_sdmmc_get_card(device);
|
||||||
return false;
|
if (sdmmc_card) {
|
||||||
|
*sdcard = sdmmc_card;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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() {
|
void updateSdCardIcon() {
|
||||||
auto* sdcard_fs = findFirstSdcardFileSystem();
|
auto* sdcard_fs = findSdcardFileSystem(false);
|
||||||
// TODO: Support multiple SD cards
|
// TODO: Support multiple SD cards
|
||||||
if (sdcard_fs != nullptr) {
|
if (sdcard_fs != nullptr) {
|
||||||
auto mounted = file_system_is_mounted(sdcard_fs);
|
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);
|
lvgl::statusbar_icon_set_visibility(sdcard_icon_id, true);
|
||||||
sdcard_last_icon = desired_icon;
|
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) {
|
file_system_for_each(&fs_iter_context, [] (auto* fs, void* context) {
|
||||||
auto* fs_iter_context = static_cast<FsIterContext*>(context);
|
auto* fs_iter_context = static_cast<FsIterContext*>(context);
|
||||||
char path[128];
|
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++;
|
fs_iter_context->count++;
|
||||||
if (fs_iter_context->count != 1) fs_iter_context->json << ","; // add separator between json array entries
|
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}";
|
fs_iter_context->json << "{\"name\":\"" << path << "\",\"type\":\"dir\",\"size\":0}";
|
||||||
|
|||||||
@ -416,7 +416,6 @@ const esp_elfsym main_symbols[] {
|
|||||||
// miniz.h
|
// miniz.h
|
||||||
ESP_ELFSYM_EXPORT(tinfl_decompress),
|
ESP_ELFSYM_EXPORT(tinfl_decompress),
|
||||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_callback),
|
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_callback),
|
||||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_heap),
|
|
||||||
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_mem),
|
ESP_ELFSYM_EXPORT(tinfl_decompress_mem_to_mem),
|
||||||
// ledc
|
// ledc
|
||||||
ESP_ELFSYM_EXPORT(ledc_update_duty),
|
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_channel_config),
|
||||||
ESP_ELFSYM_EXPORT(ledc_set_duty),
|
ESP_ELFSYM_EXPORT(ledc_set_duty),
|
||||||
ESP_ELFSYM_EXPORT(ledc_set_fade),
|
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_set_pin),
|
||||||
ESP_ELFSYM_EXPORT(ledc_timer_config),
|
ESP_ELFSYM_EXPORT(ledc_timer_config),
|
||||||
ESP_ELFSYM_EXPORT(ledc_timer_pause),
|
ESP_ELFSYM_EXPORT(ledc_timer_pause),
|
||||||
|
|||||||
@ -9,7 +9,7 @@ extern "C" {
|
|||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define GPIO_FLAGS_MASK 0x1f
|
#define GPIO_FLAGS_MASK 0xff
|
||||||
|
|
||||||
#define GPIO_PIN_NONE -1
|
#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()
|
* 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.
|
* 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
|
* @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);
|
void* gpio_controller_get_controller_context(struct Device* device);
|
||||||
|
|
||||||
|
|||||||
@ -4,17 +4,36 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tactility/error.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Device;
|
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 {
|
struct WifiApRecord {
|
||||||
char ssid[32];
|
char ssid[33]; // 32 bytes + null terminator
|
||||||
int8_t rssi;
|
int8_t rssi;
|
||||||
int32_t channel;
|
int32_t channel;
|
||||||
wifi_auth_mode_t auth_mode;
|
enum WifiAuthenticationType authentication_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WifiRadioState {
|
enum WifiRadioState {
|
||||||
@ -35,7 +54,7 @@ enum WifiAccessPointState {
|
|||||||
WIFI_ACCESS_POINT_STATE_STOPPED,
|
WIFI_ACCESS_POINT_STATE_STOPPED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class WifiEventType {
|
enum WifiEventType {
|
||||||
/** Radio state changed */
|
/** Radio state changed */
|
||||||
WIFI_EVENT_TYPE_RADIO_STATE_CHANGED,
|
WIFI_EVENT_TYPE_RADIO_STATE_CHANGED,
|
||||||
/** WifiStationState changed */
|
/** WifiStationState changed */
|
||||||
@ -50,7 +69,7 @@ enum class WifiEventType {
|
|||||||
WIFI_EVENT_TYPE_SCAN_FINISHED,
|
WIFI_EVENT_TYPE_SCAN_FINISHED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class WifiStationConnectionError {
|
enum WifiStationConnectionError {
|
||||||
WIFI_STATION_CONNECTION_ERROR_NONE,
|
WIFI_STATION_CONNECTION_ERROR_NONE,
|
||||||
/** Wrong password */
|
/** Wrong password */
|
||||||
WIFI_STATION_CONNECTION_ERROR_WRONG_CREDENTIALS,
|
WIFI_STATION_CONNECTION_ERROR_WRONG_CREDENTIALS,
|
||||||
@ -67,10 +86,10 @@ struct WifiEvent {
|
|||||||
enum WifiStationState station_state;
|
enum WifiStationState station_state;
|
||||||
enum WifiAccessPointState access_point_state;
|
enum WifiAccessPointState access_point_state;
|
||||||
enum WifiStationConnectionError connection_error;
|
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 {
|
struct WifiApi {
|
||||||
/**
|
/**
|
||||||
@ -100,9 +119,9 @@ struct WifiApi {
|
|||||||
/**
|
/**
|
||||||
* Check if the device is currently scanning for access points.
|
* Check if the device is currently scanning for access points.
|
||||||
* @param[in] device the wifi device
|
* @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.
|
* Start a scan for access points.
|
||||||
@ -129,9 +148,9 @@ struct WifiApi {
|
|||||||
error_t (*station_get_ipv4_address)(struct Device* device, char* ipv4);
|
error_t (*station_get_ipv4_address)(struct Device* device, char* ipv4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the IPv6 address of the device.
|
* Get the SSID of the access point the device is currently connected to.
|
||||||
* @param[in] device the device
|
* @param[in] device the wifi device
|
||||||
* @param[out] ipv6 the buffer to store the IPv6 address (must be at least 33 bytes, will be null-terminated)
|
* @param[out] ssid the buffer to store the SSID (must be at least 33 bytes, will be null-terminated)
|
||||||
* @return ERROR_NONE on success
|
* @return ERROR_NONE on success
|
||||||
*/
|
*/
|
||||||
error_t (*station_get_target_ssid)(struct Device* device, char* ssid);
|
error_t (*station_get_target_ssid)(struct Device* device, char* ssid);
|
||||||
@ -140,7 +159,7 @@ struct WifiApi {
|
|||||||
* Connect to an access point.
|
* Connect to an access point.
|
||||||
* @param[in] device the wifi device
|
* @param[in] device the wifi device
|
||||||
* @param[in] ssid the SSID of the access point
|
* @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)
|
* @param[in] channel the Wi-Fi channel to connect to (0 means "any" / no preference)
|
||||||
* @return ERROR_NONE on success
|
* @return ERROR_NONE on success
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -11,25 +11,94 @@ extern "C" {
|
|||||||
|
|
||||||
struct FileSystem;
|
struct FileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief File system API.
|
||||||
|
*
|
||||||
|
* Provides a set of function pointers to interact with a specific file system implementation.
|
||||||
|
*/
|
||||||
struct FileSystemApi {
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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));
|
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);
|
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);
|
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);
|
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);
|
error_t file_system_get_path(struct FileSystem* fs, char* out_path, size_t out_path_size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -73,13 +73,18 @@ GpioDescriptor* gpio_descriptor_acquire(
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_t gpio_descriptor_release(GpioDescriptor* descriptor) {
|
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;
|
descriptor->owner_type = GPIO_OWNER_NONE;
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t gpio_controller_get_pin_count(Device* device, uint32_t* count) {
|
error_t gpio_controller_get_pin_count(Device* device, uint32_t* count) {
|
||||||
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
|
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
*count = data->pin_count;
|
*count = data->pin_count;
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
return ERROR_NONE;
|
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) {
|
error_t gpio_controller_deinit_descriptors(Device* device) {
|
||||||
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
|
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
|
||||||
delete data;
|
|
||||||
device_set_driver_data(device, nullptr);
|
device_set_driver_data(device, nullptr);
|
||||||
|
delete data;
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#include <tactility/filesystem/file_system.h>
|
|
||||||
#include <tactility/concurrent/mutex.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
#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
|
// Define the internal FileSystem structure
|
||||||
struct FileSystem {
|
struct FileSystem {
|
||||||
@ -11,57 +12,61 @@ struct FileSystem {
|
|||||||
void* data;
|
void* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global Mutex and the master list of file systems
|
// Global list of file systems and its mutex
|
||||||
static Mutex fs_mutex;
|
struct FileSystemsLedger {
|
||||||
static bool fs_mutex_initialized = false;
|
std::vector<FileSystem*> file_systems;
|
||||||
static 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() {
|
FileSystemsLedger() { recursive_mutex_construct(&mutex); }
|
||||||
if (!fs_mutex_initialized) {
|
~FileSystemsLedger() { recursive_mutex_destruct(&mutex); }
|
||||||
mutex_construct(&fs_mutex);
|
|
||||||
fs_mutex_initialized = true;
|
void lock() { recursive_mutex_lock(&mutex); }
|
||||||
}
|
void unlock() { recursive_mutex_unlock(&mutex); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static FileSystemsLedger& get_ledger() {
|
||||||
|
static FileSystemsLedger ledger;
|
||||||
|
return ledger;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
FileSystem* file_system_add(const FileSystemApi* fs_api, void* data) {
|
FileSystem* file_system_add(const FileSystemApi* fs_api, void* data) {
|
||||||
ensure_mutex_initialized();
|
auto& ledger = get_ledger();
|
||||||
mutex_lock(&fs_mutex);
|
ledger.lock();
|
||||||
|
|
||||||
auto* fs = new(std::nothrow) struct FileSystem();
|
auto* fs = new(std::nothrow) struct FileSystem();
|
||||||
check(fs != nullptr);
|
check(fs != nullptr);
|
||||||
fs->api = fs_api;
|
fs->api = fs_api;
|
||||||
fs->data = data;
|
fs->data = data;
|
||||||
file_systems.push_back(fs);
|
ledger.file_systems.push_back(fs);
|
||||||
|
|
||||||
mutex_unlock(&fs_mutex);
|
ledger.unlock();
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_system_remove(FileSystem* fs) {
|
void file_system_remove(FileSystem* fs) {
|
||||||
check(!file_system_is_mounted(fs));
|
check(!file_system_is_mounted(fs));
|
||||||
ensure_mutex_initialized();
|
auto& ledger = get_ledger();
|
||||||
mutex_lock(&fs_mutex);
|
ledger.lock();
|
||||||
|
|
||||||
auto it = std::ranges::find(file_systems, fs);
|
auto it = std::ranges::find(ledger.file_systems, fs);
|
||||||
if (it != file_systems.end()) {
|
if (it != ledger.file_systems.end()) {
|
||||||
file_systems.erase(it);
|
ledger.file_systems.erase(it);
|
||||||
delete fs;
|
delete fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&fs_mutex);
|
ledger.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_system_for_each(void* callback_context, bool (*callback)(FileSystem* fs, void* context)) {
|
void file_system_for_each(void* callback_context, bool (*callback)(FileSystem* fs, void* context)) {
|
||||||
ensure_mutex_initialized();
|
auto& ledger = get_ledger();
|
||||||
mutex_lock(&fs_mutex);
|
ledger.lock();
|
||||||
|
for (auto* fs : ledger.file_systems) {
|
||||||
for (auto* fs : file_systems) {
|
|
||||||
if (!callback(fs, callback_context)) break;
|
if (!callback(fs, callback_context)) break;
|
||||||
}
|
}
|
||||||
|
ledger.unlock();
|
||||||
mutex_unlock(&fs_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_t file_system_mount(FileSystem* fs) {
|
error_t file_system_mount(FileSystem* fs) {
|
||||||
|
|||||||
@ -14,6 +14,10 @@
|
|||||||
#include <tactility/filesystem/file_system.h>
|
#include <tactility/filesystem/file_system.h>
|
||||||
#include <tactility/module.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.
|
* 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++.
|
* 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