db48dfe812
- Standardized keyboard input using Unicode-based key codes across supported devices and the simulator. Keyboards don't emit `LV_KEY_*` anymore. - Refactored lilygo encoder driver into a reusable GPIO rotary encoder driver (see `Drivers/gpio-encoder-module/`). Added more features to the config file. - Improved LVGL keyboard device management, including duplicate prevention and reliable reconnects. - LVGL file mutex now registers with lvgl start/stop - Improved LVGL startup/shutdown stability and memory allocation reliability. - Increased simulator LVGL memory capacity and improved USB device-class handling.
136 lines
4.3 KiB
C++
136 lines
4.3 KiB
C++
#include <tactility/device.h>
|
|
#include <tactility/drivers/display.h>
|
|
#include <tactility/drivers/sdcard.h>
|
|
#include <tactility/drivers/spi_controller.h>
|
|
#include <tactility/filesystem/file_mutex.h>
|
|
#include <tactility/filesystem/file_system.h>
|
|
#include <lvgl/lvgl.h>
|
|
|
|
#include <vector>
|
|
|
|
constexpr auto* TAG = "file_mutex_lvgl";
|
|
|
|
struct Device;
|
|
|
|
namespace {
|
|
|
|
std::vector<FileMutexId> 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<FileMutexId>* registeredIds;
|
|
};
|
|
Context ctx = { .mountPath = mount_path, .registeredIds = static_cast<std::vector<FileMutexId>*>(context) };
|
|
|
|
device_for_each_child(parent, &ctx, [](Device* child, void* context) -> bool {
|
|
Context* ctx = static_cast<Context*>(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<std::vector<FileMutexId>*>(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();
|
|
}
|
|
|
|
}
|