mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 17:35:05 +00:00
Compare commits
No commits in common. "f8f01edc485234314e7e13d6099485d2eb444786" and "f12a52ebb96e34e2d557e8d55a2b3d347bdce047" have entirely different histories.
f8f01edc48
...
f12a52ebb9
@ -57,19 +57,12 @@ 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,6 +1,5 @@
|
|||||||
// 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>
|
||||||
@ -30,11 +29,6 @@ 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,6 +1,5 @@
|
|||||||
// 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,6 +103,16 @@ 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);
|
||||||
@ -110,23 +120,17 @@ static error_t add_callback(GpioDescriptor* descriptor, void (*callback)(void*),
|
|||||||
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 remove_callback(GpioDescriptor* descriptor) {
|
static error_t disable_interrupt(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* internal = GET_INTERNAL_FROM_DESCRIPTOR(descriptor);
|
||||||
check(internal->isr_service_ref_count > 0);
|
auto esp_error = gpio_intr_disable(static_cast<gpio_num_t>(descriptor->pin));
|
||||||
|
if (esp_error == ESP_OK && 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();
|
||||||
@ -135,42 +139,21 @@ static error_t remove_callback(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) {
|
||||||
LOG_I(TAG, "start %s", device->name);
|
ESP_LOGI(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) {
|
||||||
LOG_I(TAG, "stop %s", device->name);
|
ESP_LOGI(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();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now safe to deinit descriptors and delete internal
|
|
||||||
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
|
|
||||||
delete internal;
|
delete internal;
|
||||||
|
check(gpio_controller_deinit_descriptors(device) == ERROR_NONE);
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,16 @@
|
|||||||
// 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/drivers/gpio_descriptor.h>
|
#include <tactility/concurrent/recursive_mutex.h>
|
||||||
#include <tactility/filesystem/file_system.h>
|
|
||||||
#include <tactility/log.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>
|
||||||
|
|
||||||
#define TAG "esp32_sdmmc"
|
#define TAG "esp32_sdmmc"
|
||||||
|
|
||||||
#define GET_CONFIG(device) ((const struct Esp32SdmmcConfig*)device->config)
|
#define GET_CONFIG(device) ((const struct Esp32SdmmcConfig*)device->config)
|
||||||
@ -47,7 +45,7 @@ struct Esp32SdmmcInternal {
|
|||||||
~Esp32SdmmcInternal() {
|
~Esp32SdmmcInternal() {
|
||||||
cleanup_pins();
|
cleanup_pins();
|
||||||
recursive_mutex_destruct(&mutex);
|
recursive_mutex_destruct(&mutex);
|
||||||
if (esp32_sdmmc_fs_handle) esp32_sdmmc_fs_free(esp32_sdmmc_fs_handle);
|
if (esp32_sdmmc_fs_handle) free(esp32_sdmmc_fs_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_pins() {
|
void cleanup_pins() {
|
||||||
@ -67,6 +65,8 @@ 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,7 +76,6 @@ 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);
|
||||||
@ -100,20 +99,11 @@ 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,
|
||||||
@ -122,21 +112,17 @@ 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) {
|
||||||
LOG_I(TAG, "stop %s", device->name);
|
ESP_LOGI(TAG, "stop %s", device->name);
|
||||||
auto* data = GET_DATA(device);
|
auto* data = GET_DATA(device);
|
||||||
if (!data) return ERROR_NONE;
|
auto* dts_config = GET_CONFIG(device);
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,8 +136,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -159,13 +143,7 @@ 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);
|
||||||
if (!data) return nullptr;
|
return esp32_sdmmc_fs_get_card(data->esp32_sdmmc_fs_handle);
|
||||||
|
|
||||||
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,5 +1,4 @@
|
|||||||
// 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>
|
||||||
@ -81,12 +80,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, &fs_data->pwr_ctrl_handle);
|
esp_err_t pwr_err = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &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 = fs_data->pwr_ctrl_handle;
|
host.pwr_ctrl_handle = data->pwr_ctrl_handle;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t slot_config_flags = 0;
|
uint32_t slot_config_flags = 0;
|
||||||
@ -144,9 +143,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 (fs_data->pwr_ctrl_handle) {
|
if (data->pwr_ctrl_handle) {
|
||||||
sd_pwr_ctrl_del_on_chip_ldo(fs_data->pwr_ctrl_handle);
|
sd_pwr_ctrl_del_on_chip_ldo(data->pwr_ctrl_handle);
|
||||||
fs_data->pwr_ctrl_handle = nullptr;
|
data->pwr_ctrl_handle = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -157,12 +156,13 @@ 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->card == nullptr) return false;
|
if (fs_data == nullptr || 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,8 +2,6 @@
|
|||||||
#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,7 +9,11 @@ namespace tt {
|
|||||||
|
|
||||||
bool findFirstMountedSdCardPath(std::string& path);
|
bool findFirstMountedSdCardPath(std::string& path);
|
||||||
|
|
||||||
FileSystem* findSdcardFileSystem(bool mustBeMounted);
|
bool hasMountedSdCard();
|
||||||
|
|
||||||
|
FileSystem* findFirstMountedSdcardFileSystem();
|
||||||
|
|
||||||
|
FileSystem* findFirstSdcardFileSystem();
|
||||||
|
|
||||||
std::string getSystemRootPath();
|
std::string getSystemRootPath();
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ 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,8 +92,7 @@ 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");
|
||||||
static auto system_fs_data = PartitionFsData("/system");
|
file_system_add(&partition_fs_api, new 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);
|
||||||
@ -101,8 +100,7 @@ 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");
|
||||||
static auto data_fs_data = PartitionFsData("/data");
|
file_system_add(&partition_fs_api, new 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 = findSdcardFileSystem(true);
|
auto* fs = findFirstMountedSdcardFileSystem();
|
||||||
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,7 +17,27 @@ bool findFirstMountedSdCardPath(std::string& path) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystem* findSdcardFileSystem(bool mustBeMounted) {
|
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* 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];
|
||||||
@ -29,9 +49,6 @@ FileSystem* findSdcardFileSystem(bool mustBeMounted) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (found && mustBeMounted && !file_system_is_mounted(found)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,6 +50,7 @@ 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
|
||||||
@ -235,7 +236,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 {}", app_path);
|
LOGGER.info("Registering apps from {}", path);
|
||||||
registerInstalledApps(app_path);
|
registerInstalledApps(app_path);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -262,6 +263,9 @@ 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,11 +6,10 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@ -37,12 +36,6 @@ 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,6 +321,7 @@ 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() {
|
||||||
@ -620,6 +621,7 @@ 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,10 +26,8 @@ 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);
|
||||||
const auto mount_path = device->getMountPath();
|
if (device->getMountPath().size() >= out_path_size) return ERROR_BUFFER_OVERFLOW;
|
||||||
if (mount_path.size() >= out_path_size) return ERROR_BUFFER_OVERFLOW;
|
strncpy(out_path, device->getMountPath().c_str(), out_path_size);
|
||||||
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,7 +1,5 @@
|
|||||||
#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>
|
||||||
@ -42,14 +40,10 @@ 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);
|
||||||
auto* sdmmc_card = esp32_sdmmc_get_card(device);
|
*sdcard = esp32_sdmmc_get_card(device);
|
||||||
if (sdmmc_card) {
|
|
||||||
*sdcard = sdmmc_card;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
80
Tactility/Source/service/sdcard/Sdcard.cpp
Normal file
80
Tactility/Source/service/sdcard/Sdcard.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#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 = findSdcardFileSystem(false);
|
auto* sdcard_fs = findFirstSdcardFileSystem();
|
||||||
// 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,11 +174,7 @@ 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;
|
||||||
}
|
}
|
||||||
} else {
|
// TODO: Consider tracking how long the SD card has been in unknown status and then show error
|
||||||
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)) == ERROR_NONE && strcmp(path, "/system") != 0) {
|
if (file_system_is_mounted(fs) && file_system_get_path(fs, path, sizeof(path)) == ESP_OK && 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,6 +416,7 @@ 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),
|
||||||
@ -423,10 +424,6 @@ 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 0xff
|
#define GPIO_FLAGS_MASK 0x1f
|
||||||
|
|
||||||
#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 the context void pointer
|
* @return ERROR_NONE if successful
|
||||||
*/
|
*/
|
||||||
void* gpio_controller_get_controller_context(struct Device* device);
|
void* gpio_controller_get_controller_context(struct Device* device);
|
||||||
|
|
||||||
|
|||||||
@ -4,36 +4,17 @@
|
|||||||
#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[33]; // 32 bytes + null terminator
|
char ssid[32];
|
||||||
int8_t rssi;
|
int8_t rssi;
|
||||||
int32_t channel;
|
int32_t channel;
|
||||||
enum WifiAuthenticationType authentication_type;
|
wifi_auth_mode_t auth_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WifiRadioState {
|
enum WifiRadioState {
|
||||||
@ -54,7 +35,7 @@ enum WifiAccessPointState {
|
|||||||
WIFI_ACCESS_POINT_STATE_STOPPED,
|
WIFI_ACCESS_POINT_STATE_STOPPED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WifiEventType {
|
enum class WifiEventType {
|
||||||
/** Radio state changed */
|
/** Radio state changed */
|
||||||
WIFI_EVENT_TYPE_RADIO_STATE_CHANGED,
|
WIFI_EVENT_TYPE_RADIO_STATE_CHANGED,
|
||||||
/** WifiStationState changed */
|
/** WifiStationState changed */
|
||||||
@ -69,7 +50,7 @@ enum WifiEventType {
|
|||||||
WIFI_EVENT_TYPE_SCAN_FINISHED,
|
WIFI_EVENT_TYPE_SCAN_FINISHED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WifiStationConnectionError {
|
enum class 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,
|
||||||
@ -86,10 +67,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, struct WifiEvent event);
|
typedef void (*WifiEventCallback)(struct Device* device, void* callback_context, WifiEvent event);
|
||||||
|
|
||||||
struct WifiApi {
|
struct WifiApi {
|
||||||
/**
|
/**
|
||||||
@ -119,9 +100,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 true when scanning
|
* @return ERROR_NONE on success
|
||||||
*/
|
*/
|
||||||
bool (*is_scanning)(struct Device* device);
|
error_t (*is_scanning)(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a scan for access points.
|
* Start a scan for access points.
|
||||||
@ -148,9 +129,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 SSID of the access point the device is currently connected to.
|
* Get the IPv6 address of the device.
|
||||||
* @param[in] device the wifi device
|
* @param[in] device the device
|
||||||
* @param[out] ssid the buffer to store the SSID (must be at least 33 bytes, will be null-terminated)
|
* @param[out] ipv6 the buffer to store the IPv6 address (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);
|
||||||
@ -159,7 +140,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
|
* @param[in] password the password of the access point (must be at least 33 characters and null-terminated)
|
||||||
* @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,94 +11,25 @@ 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,18 +73,13 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +98,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));
|
||||||
device_set_driver_data(device, nullptr);
|
|
||||||
delete data;
|
delete data;
|
||||||
|
device_set_driver_data(device, nullptr);
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <tactility/concurrent/mutex.h>
|
|
||||||
#include <tactility/concurrent/recursive_mutex.h>
|
|
||||||
#include <tactility/filesystem/file_system.h>
|
#include <tactility/filesystem/file_system.h>
|
||||||
|
#include <tactility/concurrent/mutex.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// Define the internal FileSystem structure
|
// Define the internal FileSystem structure
|
||||||
struct FileSystem {
|
struct FileSystem {
|
||||||
@ -12,61 +11,57 @@ struct FileSystem {
|
|||||||
void* data;
|
void* data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global list of file systems and its mutex
|
// Global Mutex and the master list of file systems
|
||||||
struct FileSystemsLedger {
|
static Mutex fs_mutex;
|
||||||
std::vector<FileSystem*> file_systems;
|
static bool fs_mutex_initialized = false;
|
||||||
// Use recursive mutex so that file_system_for_each() can lock within the callback
|
static std::vector<FileSystem*> file_systems;
|
||||||
RecursiveMutex mutex {};
|
|
||||||
|
|
||||||
FileSystemsLedger() { recursive_mutex_construct(&mutex); }
|
static void ensure_mutex_initialized() {
|
||||||
~FileSystemsLedger() { recursive_mutex_destruct(&mutex); }
|
if (!fs_mutex_initialized) {
|
||||||
|
mutex_construct(&fs_mutex);
|
||||||
void lock() { recursive_mutex_lock(&mutex); }
|
fs_mutex_initialized = true;
|
||||||
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) {
|
||||||
auto& ledger = get_ledger();
|
ensure_mutex_initialized();
|
||||||
ledger.lock();
|
mutex_lock(&fs_mutex);
|
||||||
|
|
||||||
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;
|
||||||
ledger.file_systems.push_back(fs);
|
file_systems.push_back(fs);
|
||||||
|
|
||||||
ledger.unlock();
|
mutex_unlock(&fs_mutex);
|
||||||
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));
|
||||||
auto& ledger = get_ledger();
|
ensure_mutex_initialized();
|
||||||
ledger.lock();
|
mutex_lock(&fs_mutex);
|
||||||
|
|
||||||
auto it = std::ranges::find(ledger.file_systems, fs);
|
auto it = std::ranges::find(file_systems, fs);
|
||||||
if (it != ledger.file_systems.end()) {
|
if (it != file_systems.end()) {
|
||||||
ledger.file_systems.erase(it);
|
file_systems.erase(it);
|
||||||
delete fs;
|
delete fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ledger.unlock();
|
mutex_unlock(&fs_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) {
|
||||||
auto& ledger = get_ledger();
|
ensure_mutex_initialized();
|
||||||
ledger.lock();
|
mutex_lock(&fs_mutex);
|
||||||
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,10 +14,6 @@
|
|||||||
#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