#include #include #include #include #include #include #include #include constexpr auto* TAG = "file_mutex_lvgl"; struct Device; namespace { std::vector registered_ids; void wrapped_lvgl_lock() { if (!lvgl_is_running()) return; lvgl_lock(); } bool wrapped_lvgl_try_lock(uint32_t timeout) { // Return lock success, so the file operation can continue when LVGL is not running // lvgl_try_lock() fails to lock if LVGL is not running if (!lvgl_is_running()) return true; return lvgl_try_lock(timeout); } void wrapped_lvgl_unlock() { if (!lvgl_is_running()) return; lvgl_unlock(); } const FileMutex lvgl_mutex = { .lock = wrapped_lvgl_lock, .try_lock = wrapped_lvgl_try_lock, .unlock = wrapped_lvgl_unlock, }; } namespace tt { /** * Finds file systems with a device (e.g. sd card) that is owned by a SPI controller. * If the SPI controller has a display on the bus, we create an LVGL lock for the file system path. */ void initFileMutexForLvgl() { file_system_for_each(®istered_ids, [](FileSystem* fs, void* context) { char mount_path[64]; if (file_system_get_path(fs, mount_path, sizeof(mount_path)) != ERROR_NONE) { return true; } LOG_D(TAG, "Mount path %s", mount_path); // We only care about file system with a Device (owner) auto* owner = file_system_get_owner(fs); if (owner == nullptr) { LOG_D(TAG, "Owner: none"); return true; } LOG_D(TAG, "Owner: %s", owner->name); // Ignore devices without a parent (root) auto* parent = device_get_parent(owner); if (parent == nullptr) { LOG_D(TAG, "Owner: no parent"); return true; } LOG_D(TAG, "Owner: parent %s", parent->name); // If the FileSystem is on a SPI bus and there's more than 1 device, we assume the other one is the display. auto* type = device_get_type(parent); if (type != &SPI_CONTROLLER_TYPE || device_get_child_count(parent) <= 1) { LOG_D(TAG, "Owner parent not SPI controller or not enough children"); return true; } struct Context { const char* mountPath; std::vector* registeredIds; }; Context ctx = { .mountPath = mount_path, .registeredIds = static_cast*>(context) }; device_for_each_child(parent, &ctx, [](Device* child, void* context) -> bool { Context* ctx = static_cast(context); if (device_get_type(child) == &DISPLAY_TYPE) { LOG_I(TAG, "Adding file mutex for %s as it shares a bus with a display", ctx->mountPath); ctx->registeredIds->push_back(file_mutex_add(&lvgl_mutex, ctx->mountPath)); return false; } else { LOG_D(TAG, "child of parent, %s: not DISPLAY_TYPE", child->name); } return true; }); return true; }); // SDMMC-backed SD cards aren't parented under SPI_CONTROLLER_TYPE, so the pass above never // sees them - but on some chips (classic ESP32) SDMMC and SPI still contend for DMA/bus // access. Lock every SD card mount if a display exists anywhere, regardless of bus topology. if (!device_exists_of_type(&DISPLAY_TYPE)) { return; } file_system_for_each(®istered_ids, [](FileSystem* fs, void* context) { char mount_path[64]; if (file_system_get_path(fs, mount_path, sizeof(mount_path)) != ERROR_NONE) { return true; } auto* owner = file_system_get_owner(fs); if (owner == nullptr || device_get_type(owner) != &SDCARD_TYPE) { return true; } LOG_I(TAG, "Adding file mutex for %s (SD card) - a display is present and may contend for bus/DMA resources", mount_path); auto* ids = static_cast*>(context); ids->push_back(file_mutex_add(&lvgl_mutex, mount_path)); return true; }); } void deinitFileMutexForLvgl() { for (FileMutexId id : registered_ids) { file_mutex_remove(id); } registered_ids.clear(); } }