Implemented TactilityKernel and DevicetreeCompiler, updated licenses & copyrights (#452)

**New features**

- Created a devicetree DTS and YAML parser in Python
- Created new modules:
  - TactilityKernel (LGPL v3.0 license)
  - Platforms/PlatformEsp32 (LGPL v3.0 license) 
  - Platforms/PlatformPosix (LGPL v3.0 license)
  - Tests/TactilityKernelTests

Most boards have a placeholder DTS file, while T-Lora Pager has a few devices attached.

**Licenses**

Clarified licenses and copyrights better.

- Add explanation about the intent behind them.
- Added explanation about licenses for past and future subprojects
- Added more details explanations with regards to the logo usage
- Copied licenses to subprojects to make it more explicit
This commit is contained in:
Ken Van Hoeylandt
2026-01-24 15:47:11 +01:00
committed by GitHub
parent 0d16eb606f
commit 4b6ed871a9
194 changed files with 7807 additions and 741 deletions
+183
View File
@@ -0,0 +1,183 @@
#pragma once
#include "Driver.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <Tactility/concurrent/Mutex.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct Driver;
/** Enables discovering devices of the same type */
struct DeviceType {
/* Placeholder because empty structs have a different size with C vs C++ compilers */
uint8_t _;
};
/** Represents a piece of hardware */
struct Device {
/** The name of the device. Valid characters: a-z a-Z 0-9 - _ . */
const char* name;
/** The configuration data for the device's driver */
const void* config;
/** The parent device that this device belongs to. Can be NULL, but only the root device should have a NULL parent. */
struct Device* parent;
/** Internal data */
struct {
/** Address of the API exposed by the device instance. */
struct Driver* driver;
/** The driver data for this device (e.g. a mutex) */
void* driver_data;
/** The mutex for device operations */
struct Mutex mutex;
/** The device state */
struct {
int start_result;
bool started : 1;
bool added : 1;
} state;
/** Private data */
void* data;
} internal;
};
/**
* Initialize the properties of a device.
*
* @param[in] dev a device with all non-internal properties set
* @return the result code (0 for success)
*/
int device_construct(struct Device* device);
/**
* Deinitialize the properties of a device.
* This fails when a device is busy or has children.
*
* @param[in] dev
* @return the result code (0 for success)
*/
int device_destruct(struct Device* device);
/**
* Indicates whether the device is in a state where its API is available
*
* @param[in] dev non-null device pointer
* @return true if the device is ready for use
*/
static inline bool device_is_ready(const struct Device* device) {
return device->internal.state.started;
}
/**
* Register a device to all relevant systems:
* - the global ledger
* - its parent (if any)
* - a bus (if any)
*
* @param[in] device non-null device pointer
* @return 0 on success
*/
int device_add(struct Device* device);
/**
* Deregister a device. Remove it from all relevant systems:
* - the global ledger
* - its parent (if any)
* - a bus (if any)
*
* @param[in] device non-null device pointer
* @return 0 on success
*/
int device_remove(struct Device* device);
/**
* Attach the driver.
*
* @warning must call device_construct() and device_add() first
* @param device
* @return ERROR_INVALID_STATE or otherwise the value of the driver binding result (0 on success)
*/
int device_start(struct Device* device);
/**
* Detach the driver.
*
* @param device
* @return ERROR_INVALID_STATE or otherwise the value of the driver unbinding result (0 on success)
*/
int device_stop(struct Device* device);
/**
* Set or unset a parent.
* @warning must call before device_add()
* @param device non-NULL device
* @param parent nullable parent device
*/
void device_set_parent(struct Device* device, struct Device* parent);
static inline void device_set_driver(struct Device* device, struct Driver* driver) {
device->internal.driver = driver;
}
static inline struct Driver* device_get_driver(struct Device* device) {
return device->internal.driver;
}
static inline void device_set_driver_data(struct Device* device, void* driver_data) {
device->internal.driver_data = driver_data;
}
static inline void* device_get_driver_data(struct Device* device) {
return device->internal.driver_data;
}
static inline bool device_is_added(const struct Device* device) {
return device->internal.state.added;
}
static inline void device_lock(struct Device* device) {
mutex_lock(&device->internal.mutex);
}
static inline int device_try_lock(struct Device* device) {
return mutex_try_lock(&device->internal.mutex);
}
static inline void device_unlock(struct Device* device) {
mutex_unlock(&device->internal.mutex);
}
static inline const struct DeviceType* device_get_type(struct Device* device) {
return device->internal.driver ? device->internal.driver->device_type : NULL;
}
/**
* Iterate through all the known devices
* @param callback_context the parameter to pass to the callback. NULL is valid.
* @param on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void for_each_device(void* callback_context, bool(*on_device)(struct Device* device, void* context));
/**
* Iterate through all the child devices of the specified device
* @param callback_context the parameter to pass to the callback. NULL is valid.
* @param on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void for_each_device_child(struct Device* device, void* callback_context, bool(*on_device)(struct Device* device, void* context));
/**
* Iterate through all the known devices of a specific type
* @param type the type to filter
* @param callback_context the parameter to pass to the callback. NULL is valid.
* @param on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void for_each_device_of_type(const struct DeviceType* type, void* callback_context, bool(*on_device)(struct Device* device, void* context));
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,50 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
struct Device;
struct DeviceType;
struct Driver {
/** The driver name */
const char* name;
/** Array of const char*, terminated by NULL */
const char**compatible;
/** Function to initialize the driver for a device */
int (*start_device)(struct Device* dev);
/** Function to deinitialize the driver for a device */
int (*stop_device)(struct Device* dev);
/** Contains the driver's functions */
const void* api;
/** Which type of devices this driver creates (can be NULL) */
const struct DeviceType* device_type;
/** Internal data */
struct {
/** Contains private data */
void* data;
} internal;
};
int driver_construct(struct Driver* driver);
int driver_destruct(struct Driver* driver);
int driver_bind(struct Driver* driver, struct Device* device);
int driver_unbind(struct Driver* driver, struct Device* device);
bool driver_is_compatible(struct Driver* driver, const char* compatible);
struct Driver* driver_find_compatible(const char* compatible);
static inline const struct DeviceType* driver_get_device_type(struct Driver* driver) {
return driver->device_type;
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,8 @@
#pragma once
#define ERROR_UNDEFINED 1
#define ERROR_INVALID_STATE 2
#define ERROR_INVALID_ARGUMENT 3
#define ERROR_MISSING_PARAMETER 4
#define ERROR_NOT_FOUND 5
@@ -0,0 +1,10 @@
#pragma once
#ifdef ESP_PLATFORM
#include <freertos/FreeRTOS.h>
#else
#include <FreeRTOS.h>
#endif
// Custom port compatibility definitins, mainly for PC compatibility
#include "port.h"
@@ -0,0 +1,3 @@
Compatibility include files for FreeRTOS.
Custom FreeRTOS from ESP-IDF prefixes paths with "freertos/",
but this isn't the normal behaviour for the regular FreeRTOS project.
@@ -0,0 +1,10 @@
#pragma once
#include "FreeRTOS.h"
#ifdef ESP_PLATFORM
#include <freertos/event_groups.h>
#else
#include <event_groups.h>
#endif
@@ -0,0 +1,8 @@
#pragma once
#include "FreeRTOS.h"
#ifndef ESP_PLATFORM
#define xPortInIsrContext(x) (false)
#define vPortAssertIfInISR()
#endif
@@ -0,0 +1,10 @@
#pragma once
#include "FreeRTOS.h"
#ifdef ESP_PLATFORM
#include <freertos/queue.h>
#else
#include <queue.h>
#endif
@@ -0,0 +1,9 @@
#pragma once
#include "FreeRTOS.h"
#ifdef ESP_PLATFORM
#include <freertos/semphr.h>
#else
#include <semphr.h>
#endif
@@ -0,0 +1,10 @@
#pragma once
#include "FreeRTOS.h"
#ifdef ESP_PLATFORM
#include <freertos/task.h>
#else
#include <task.h>
#endif
@@ -0,0 +1,9 @@
#pragma once
#include "FreeRTOS.h"
#ifdef ESP_PLATFORM
#include <freertos/timers.h>
#else
#include <timers.h>
#endif
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#ifdef ESP_PLATFORM
#include <esp_log.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define LOG_TAG(x) "\033[37m"#x"\033[0m"
#ifndef ESP_PLATFORM
void log_generic(const char* tag, const char* format, ...);
#define LOG_E(x, ...) log_generic(x, ##__VA_ARGS__)
#define LOG_W(x, ...) log_generic(x, ##__VA_ARGS__)
#define LOG_I(x, ...) log_generic(x, ##__VA_ARGS__)
#define LOG_D(x, ...) log_generic(x, ##__VA_ARGS__)
#define LOG_V(x, ...) log_generic(x, ##__VA_ARGS__)
#else
#define LOG_E(x, ...) ESP_LOGE(x, ##__VA_ARGS__)
#define LOG_W(x, ...) ESP_LOGW(x, ##__VA_ARGS__)
#define LOG_I(x, ...) ESP_LOGI(x, ##__VA_ARGS__)
#define LOG_D(x, ...) ESP_LOGD(x, ##__VA_ARGS__)
#define LOG_V(x, ...) ESP_LOGV(x, ##__VA_ARGS__)
#endif
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,8 @@
#pragma once
/**
* Creates required aliases for the devicetree generation.
* @param compatible_name the "compatible" value for the related driver
* @param config_type the internal configuration type for a device
*/
#define DEFINE_DEVICETREE(compatible_name, config_type) typedef config_type compatible_name##_config_dt;
@@ -0,0 +1,3 @@
#pragma once
#include <Tactility/drivers/Gpio.h>
@@ -0,0 +1,7 @@
#pragma once
#include <Tactility/bindings/bindings.h>
#include <Tactility/drivers/Root.h>
DEFINE_DEVICETREE(root, struct RootConfig)
@@ -0,0 +1,45 @@
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <Tactility/FreeRTOS/semphr.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Mutex {
QueueHandle_t handle;
};
inline static void mutex_construct(struct Mutex* mutex) {
assert(mutex->handle == NULL);
mutex->handle = xSemaphoreCreateMutex();
}
inline static void mutex_destruct(struct Mutex* mutex) {
assert(mutex->handle != NULL);
vPortAssertIfInISR();
vSemaphoreDelete(mutex->handle);
mutex->handle = NULL;
}
inline static void mutex_lock(struct Mutex* mutex) {
xSemaphoreTake(mutex->handle, portMAX_DELAY);
}
inline static bool mutex_try_lock(struct Mutex* mutex) {
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
}
inline static bool mutex_is_locked(struct Mutex* mutex) {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
inline static void mutex_unlock(struct Mutex* mutex) {
xSemaphoreGive(mutex->handle);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,44 @@
#pragma once
#include <stdbool.h>
#include <assert.h>
#include <Tactility/FreeRTOS/semphr.h>
#ifdef __cplusplus
extern "C" {
#endif
struct RecursiveMutex {
QueueHandle_t handle;
};
inline static void recursive_mutex_construct(struct RecursiveMutex* mutex) {
mutex->handle = xSemaphoreCreateRecursiveMutex();
}
inline static void recursive_mutex_destruct(struct RecursiveMutex* mutex) {
assert(mutex->handle != NULL);
vPortAssertIfInISR();
vSemaphoreDelete(mutex->handle);
mutex->handle = NULL;
}
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
xSemaphoreTakeRecursive(mutex->handle, portMAX_DELAY);
}
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex) {
return xSemaphoreTakeRecursive(mutex->handle, 0) == pdTRUE;
}
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
xSemaphoreGiveRecursive(mutex->handle);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,90 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <Tactility/Device.h>
#define GPIO_OPTIONS_MASK 0x1f
#define GPIO_ACTIVE_HIGH (0 << 0)
#define GPIO_ACTIVE_LOW (1 << 0)
#define GPIO_DIRECTION_INPUT (1 << 1)
#define GPIO_DIRECTION_OUTPUT (1 << 2)
#define GPIO_DIRECTION_INPUT_OUTPUT (GPIO_DIRECTION_INPUT | GPIO_DIRECTION_OUTPUT)
#define GPIO_PULL_UP (0 << 3)
#define GPIO_PULL_DOWN (1 << 4)
#define GPIO_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5]
#define GPIO_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_INTERRUPT_BITMASK) >> 5)
#define GPIO_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5))
typedef enum {
GPIO_INTERRUPT_DISABLE = 0,
GPIO_INTERRUPT_POS_EDGE = 1,
GPIO_INTERRUPT_NEG_EDGE = 2,
GPIO_INTERRUPT_ANY_EDGE = 3,
GPIO_INTERRUPT_LOW_LEVEL = 4,
GPIO_INTERRUPT_HIGH_LEVEL = 5,
GPIO__MAX,
} GpioInterruptType;
/**
* @brief Provides a type to hold a GPIO pin index.
*
* This reduced-size type is sufficient to record a pin number,
* e.g. from a devicetree GPIOS property.
*/
typedef uint8_t gpio_pin_t;
/**
* @brief Identifies a set of pins associated with a port.
*
* The pin with index n is present in the set if and only if the bit
* identified by (1U << n) is set.
*/
typedef uint32_t gpio_pinset_t;
/**
* @brief Provides a type to hold GPIO devicetree flags.
*
* All GPIO flags that can be expressed in devicetree fit in the low 16
* bits of the full flags field, so use a reduced-size type to record
* that part of a GPIOS property.
*
* The lower 8 bits are used for standard flags. The upper 8 bits are reserved
* for SoC specific flags.
*/
typedef uint16_t gpio_flags_t;
/**
* @brief Container for GPIO pin information specified in dts files
*
* This type contains a pointer to a GPIO device, pin identifier for a pin
* controlled by that device, and the subset of pin configuration
* flags which may be given in devicetree.
*/
struct GpioPinConfig {
/** GPIO device controlling the pin */
const struct Device* port;
/** The pin's number on the device */
gpio_pin_t pin;
/** The pin's configuration flags as specified in devicetree */
gpio_flags_t dt_flags;
};
/**
* Check if the pin is ready to be used.
* @param pin_config the specifications of the pin
* @return true if the pin is ready to be used
*/
static inline bool gpio_is_ready(const struct GpioPinConfig* pin_config) {
return device_is_ready(pin_config->port);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,28 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "Gpio.h"
#include <stdbool.h>
struct GpioControllerApi {
bool (*set_level)(struct Device* device, gpio_pin_t pin, bool high);
bool (*get_level)(struct Device* device, gpio_pin_t pin, bool* high);
bool (*set_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
bool (*get_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
};
bool gpio_controller_set_level(struct Device* device, gpio_pin_t pin, bool high);
bool gpio_controller_get_level(struct Device* device, gpio_pin_t pin, bool* high);
bool gpio_controller_set_options(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
bool gpio_controller_get_options(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
inline bool gpio_set_options_config(struct Device* device, struct GpioPinConfig* config) {
return gpio_controller_set_options(device, config->pin, config->dt_flags);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,28 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "Gpio.h"
#include <Tactility/FreeRTOS/FreeRTOS.h>
#include <stdbool.h>
#include <stdint.h>
struct I2cControllerApi {
bool (*read)(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
bool (*write)(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
bool (*write_read)(struct Device* device, uint8_t address, const uint8_t* write_data, size_t write_data_size, uint8_t* read_data, size_t read_data_size, TickType_t timeout);
};
bool i2c_controller_read(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
bool i2c_controller_write(struct Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
bool i2c_controller_write_read(struct Device* device, uint8_t address, const uint8_t* write_data, size_t write_data_size, uint8_t* read_data, size_t read_data_size, TickType_t timeout);
extern const struct DeviceType I2C_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
struct RootConfig {
const char* model;
};
#ifdef __cplusplus
}
#endif