Create hal-device module, fix GPIO, fix tests, fix TactilityC (#471)

* **New Features**
  * Added a HAL device module providing device enumeration, type queries and a HAL↔kernel bridge.

* **Improvements**
  * Integrated HAL module into startup; standardized module names, includes and lifecycle logging; safer file append behavior; broader build support.

* **API Changes**
  * Driver lifecycle now uses explicit add/remove semantics; C and HAL device type/lookup APIs clarified.

* **Chores**
  * Added module README and Apache‑2.0 license; updated build configuration to include the new module.

* **Fixes**
  * Updated tests and object file handling to behave correctly.
This commit is contained in:
Ken Van Hoeylandt
2026-02-01 01:05:16 +01:00
committed by GitHub
parent 5993ceb232
commit 3fe1dc0312
74 changed files with 836 additions and 211 deletions
@@ -37,6 +37,14 @@ error_t driver_construct(struct Driver* driver);
error_t driver_destruct(struct Driver* driver);
error_t driver_add(struct Driver* driver);
error_t driver_remove(struct Driver* driver);
error_t driver_construct_add(struct Driver* driver);
error_t driver_remove_destruct(struct Driver* driver);
error_t driver_bind(struct Driver* driver, struct Device* device);
error_t driver_unbind(struct Driver* driver, struct Device* device);
@@ -5,6 +5,15 @@
#include "freertos.h"
#ifndef ESP_PLATFORM
// TactilityFreeRTOS co-existence check
#ifndef xPortInIsrContext
#define xPortInIsrContext() (pdFALSE)
#endif
// TactilityFreeRTOS co-existence check
#ifndef vPortAssertIfInISR
#define vPortAssertIfInISR()
#endif
#endif
@@ -11,8 +11,8 @@ extern "C" {
/**
* Initialize the kernel with platform and device modules, and a device tree.
* @param platform_module The platform module to start. This module should not be constructed yet.
* @param device_module The device module to start. This module should not be constructed yet.
* @param devicetree_devices The list of generated devices from the devicetree. The array must be terminated by an entry { NULL, NULL }
* @param device_module The device module to start. This module should not be constructed yet. This parameter can be NULL.
* @param devicetree_devices The list of generated devices from the devicetree. The array must be terminated by an entry { NULL, NULL }. This parameter can be NULL.
* @return ERROR_NONE on success, otherwise an error code
*/
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct CompatibleDevice devicetree_devices[]);
@@ -17,6 +17,8 @@ struct Module {
/**
* The name of the module, for logging/debugging purposes
* Should never be NULL.
* Characters allowed: a-z A-Z 0-9 - _ .
* Desirable format "platform-esp32", "lilygo-tdeck", etc.
*/
const char* name;
+2 -1
View File
@@ -1,3 +1,4 @@
#include <cstdlib>
#include <tactility/freertos/task.h>
#include <tactility/log.h>
@@ -31,7 +32,7 @@ __attribute__((noreturn)) void __crash(void) {
#ifdef ESP_PLATFORM
esp_system_abort("System halted. Connect debugger for more info.");
#else
while (true) { /* Indefinite lock-up */ }
exit(1);
#endif
__builtin_unreachable();
}
+4 -4
View File
@@ -49,7 +49,7 @@ error_t device_construct(Device* device) {
if (device->internal.device_private == nullptr) {
return ERROR_OUT_OF_MEMORY;
}
LOG_I(TAG, "construct %s", device->name);
LOG_D(TAG, "construct %s", device->name);
mutex_construct(&device->internal.mutex);
return ERROR_NONE;
}
@@ -61,7 +61,7 @@ error_t device_destruct(Device* device) {
if (!get_device_private(device)->children.empty()) {
return ERROR_INVALID_STATE;
}
LOG_I(TAG, "destruct %s", device->name);
LOG_D(TAG, "destruct %s", device->name);
mutex_destruct(&device->internal.mutex);
delete get_device_private(device);
device->internal.device_private = nullptr;
@@ -88,7 +88,7 @@ static void device_remove_child(struct Device* device, struct Device* child) {
}
error_t device_add(Device* device) {
LOG_I(TAG, "add %s", device->name);
LOG_D(TAG, "add %s", device->name);
// Already added
if (device->internal.state.started || device->internal.state.added) {
@@ -111,7 +111,7 @@ error_t device_add(Device* device) {
}
error_t device_remove(Device* device) {
LOG_I(TAG, "remove %s", device->name);
LOG_D(TAG, "remove %s", device->name);
if (device->internal.state.started || !device->internal.state.added) {
return ERROR_INVALID_STATE;
+35 -27
View File
@@ -58,28 +58,6 @@ static DriverLedger& get_ledger() {
#define driver_lock(driver) mutex_lock(&get_driver_private(driver)->mutex);
#define driver_unlock(driver) mutex_unlock(&get_driver_private(driver)->mutex);
static void driver_add(Driver* driver) {
LOG_I(TAG, "add %s", driver->name);
ledger.lock();
ledger.drivers.push_back(driver);
ledger.unlock();
}
static error_t driver_remove(Driver* driver) {
LOG_I(TAG, "remove %s", driver->name);
ledger.lock();
const auto iterator = std::ranges::find(ledger.drivers, driver);
if (iterator == ledger.drivers.end()) {
ledger.unlock();
return ERROR_NOT_FOUND;
}
ledger.drivers.erase(iterator);
ledger.unlock();
return ERROR_NONE;
}
extern "C" {
error_t driver_construct(Driver* driver) {
@@ -87,7 +65,6 @@ error_t driver_construct(Driver* driver) {
if (driver->driver_private == nullptr) {
return ERROR_OUT_OF_MEMORY;
}
driver_add(driver);
return ERROR_NONE;
}
@@ -104,10 +81,6 @@ error_t driver_destruct(Driver* driver) {
}
get_driver_private(driver)->destroying = true;
if (driver_remove(driver) != ERROR_NONE) {
LOG_W(TAG, "Failed to remove driver from ledger: %s", driver->name);
}
driver_unlock(driver);
delete get_driver_private(driver);
driver->driver_private = nullptr;
@@ -115,6 +88,41 @@ error_t driver_destruct(Driver* driver) {
return ERROR_NONE;
}
error_t driver_add(Driver* driver) {
LOG_I(TAG, "add %s", driver->name);
ledger.lock();
ledger.drivers.push_back(driver);
ledger.unlock();
return ERROR_NONE;
}
error_t driver_remove(Driver* driver) {
LOG_I(TAG, "remove %s", driver->name);
ledger.lock();
const auto iterator = std::ranges::find(ledger.drivers, driver);
if (iterator == ledger.drivers.end()) {
ledger.unlock();
return ERROR_NOT_FOUND;
}
ledger.drivers.erase(iterator);
ledger.unlock();
return ERROR_NONE;
}
error_t driver_construct_add(struct Driver* driver) {
if (driver_construct(driver) != ERROR_NONE) return ERROR_RESOURCE;
if (driver_add(driver) != ERROR_NONE) return ERROR_RESOURCE;
return ERROR_NONE;
}
error_t driver_remove_destruct(struct Driver* driver) {
if (driver_remove(driver) != ERROR_NONE) return ERROR_RESOURCE;
if (driver_destruct(driver) != ERROR_NONE) return ERROR_RESOURCE;
return ERROR_NONE;
}
bool driver_is_compatible(Driver* driver, const char* compatible) {
if (compatible == nullptr || driver->compatible == nullptr) {
return false;
+16 -12
View File
@@ -14,7 +14,7 @@ struct ModuleParent kernel_module_parent = {
static error_t init_kernel_drivers() {
extern Driver root_driver;
if (driver_construct(&root_driver) != ERROR_NONE) return ERROR_RESOURCE;
if (driver_construct_add(&root_driver) != ERROR_NONE) return ERROR_RESOURCE;
return ERROR_NONE;
}
@@ -37,19 +37,23 @@ error_t kernel_init(struct Module* platform_module, struct Module* device_module
return ERROR_RESOURCE;
}
module_set_parent(device_module, &kernel_module_parent);
if (module_start(device_module) != ERROR_NONE) {
LOG_E(TAG, "init failed to start device module");
return ERROR_RESOURCE;
}
CompatibleDevice* compatible_device = devicetree_devices;
while (compatible_device->device != nullptr) {
if (device_construct_add_start(compatible_device->device, compatible_device->compatible) != ERROR_NONE) {
LOG_E(TAG, "kernel_init failed to construct device: %s (%s)", compatible_device->device->name, compatible_device->compatible);
if (device_module != nullptr) {
module_set_parent(device_module, &kernel_module_parent);
if (module_start(device_module) != ERROR_NONE) {
LOG_E(TAG, "init failed to start device module");
return ERROR_RESOURCE;
}
compatible_device++;
}
if (devicetree_devices) {
CompatibleDevice* compatible_device = devicetree_devices;
while (compatible_device->device != nullptr) {
if (device_construct_add_start(compatible_device->device, compatible_device->compatible) != ERROR_NONE) {
LOG_E(TAG, "kernel_init failed to construct device: %s (%s)", compatible_device->device->name, compatible_device->compatible);
return ERROR_RESOURCE;
}
compatible_device++;
}
}
LOG_I(TAG, "init done");
+6
View File
@@ -3,6 +3,8 @@
#include <tactility/concurrent/mutex.h>
#include <tactility/module.h>
#define TAG LOG_TAG(module)
struct ModuleParentPrivate {
std::vector<struct Module*> modules;
struct Mutex mutex = { 0 };
@@ -72,6 +74,8 @@ error_t module_set_parent(struct Module* module, struct ModuleParent* parent) {
}
error_t module_start(struct Module* module) {
LOG_I(TAG, "start %s", module->name);
if (module->internal.started) return ERROR_NONE;
if (!module->internal.parent) return ERROR_INVALID_STATE;
@@ -85,6 +89,8 @@ bool module_is_started(struct Module* module) {
}
error_t module_stop(struct Module* module) {
LOG_I(TAG, "stop %s", module->name);
if (!module->internal.started) return ERROR_NONE;
error_t error = module->stop();