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:
committed by
GitHub
parent
0d16eb606f
commit
4b6ed871a9
@@ -0,0 +1,6 @@
|
||||
properties:
|
||||
gpio-count:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The number of available GPIOs.
|
||||
@@ -0,0 +1,10 @@
|
||||
bus: i2c
|
||||
|
||||
properties:
|
||||
clock-frequency:
|
||||
type: int
|
||||
description: Initial clock frequency in Hz
|
||||
pin-sda:
|
||||
type: phandle-array
|
||||
pin-scl:
|
||||
type: phandle-array
|
||||
@@ -0,0 +1,6 @@
|
||||
on-bus: i2c
|
||||
|
||||
properties:
|
||||
register:
|
||||
required: true
|
||||
description: device address on the bus
|
||||
@@ -0,0 +1,6 @@
|
||||
compatible: "root"
|
||||
|
||||
properties:
|
||||
model:
|
||||
required: true
|
||||
description: the name of hardware, usually vendor and model name
|
||||
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "Source/*.c**")
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "Include/"
|
||||
)
|
||||
|
||||
else ()
|
||||
|
||||
add_library(TactilityKernel OBJECT ${SOURCES})
|
||||
target_include_directories(TactilityKernel PUBLIC Include/)
|
||||
target_link_libraries(TactilityKernel PUBLIC freertos_kernel)
|
||||
|
||||
endif ()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,157 @@
|
||||
# GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
<https://fsf.org/>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates the
|
||||
terms and conditions of version 3 of the GNU General Public License,
|
||||
supplemented by the additional permissions listed below.
|
||||
|
||||
## 0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the
|
||||
GNU General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License, other
|
||||
than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
## 1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
## 2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
- a) under this License, provided that you make a good faith effort
|
||||
to ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
- b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
## 3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from a
|
||||
header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
- a) Give prominent notice with each copy of the object code that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
- b) Accompany the object code with a copy of the GNU GPL and this
|
||||
license document.
|
||||
|
||||
## 4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that, taken
|
||||
together, effectively do not restrict modification of the portions of
|
||||
the Library contained in the Combined Work and reverse engineering for
|
||||
debugging such modifications, if you also do each of the following:
|
||||
|
||||
- a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
- b) Accompany the Combined Work with a copy of the GNU GPL and this
|
||||
license document.
|
||||
- c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
- d) Do one of the following:
|
||||
- 0) Convey the Minimal Corresponding Source under the terms of
|
||||
this License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
- 1) Use a suitable shared library mechanism for linking with
|
||||
the Library. A suitable mechanism is one that (a) uses at run
|
||||
time a copy of the Library already present on the user's
|
||||
computer system, and (b) will operate properly with a modified
|
||||
version of the Library that is interface-compatible with the
|
||||
Linked Version.
|
||||
- e) Provide Installation Information, but only if you would
|
||||
otherwise be required to provide such information under section 6
|
||||
of the GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the Application
|
||||
with a modified version of the Linked Version. (If you use option
|
||||
4d0, the Installation Information must accompany the Minimal
|
||||
Corresponding Source and Corresponding Application Code. If you
|
||||
use option 4d1, you must provide the Installation Information in
|
||||
the manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.)
|
||||
|
||||
## 5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the Library
|
||||
side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
- a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities, conveyed under the terms of this License.
|
||||
- b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
## 6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
as you received it specifies that a certain numbered version of the
|
||||
GNU Lesser General Public License "or any later version" applies to
|
||||
it, you have the option of following the terms and conditions either
|
||||
of that published version or of any later version published by the
|
||||
Free Software Foundation. If the Library as you received it does not
|
||||
specify a version number of the GNU Lesser General Public License, you
|
||||
may choose any version of the GNU Lesser General Public License ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,225 @@
|
||||
#include <Tactility/Device.h>
|
||||
#include <Tactility/Error.h>
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#include <ranges>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sys/errno.h>
|
||||
#include <vector>
|
||||
|
||||
#define TAG LOG_TAG(device)
|
||||
|
||||
struct DeviceData {
|
||||
std::vector<Device*> children;
|
||||
};
|
||||
|
||||
struct DeviceLedger {
|
||||
std::vector<Device*> devices;
|
||||
Mutex mutex { 0 };
|
||||
|
||||
DeviceLedger() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DeviceLedger() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
static DeviceLedger& get_ledger() {
|
||||
static DeviceLedger ledger;
|
||||
return ledger;
|
||||
}
|
||||
|
||||
#define ledger get_ledger()
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define ledger_lock() mutex_lock(&ledger.mutex)
|
||||
#define ledger_unlock() mutex_unlock(&ledger.mutex)
|
||||
|
||||
#define get_device_data(device) static_cast<DeviceData*>(device->internal.data)
|
||||
|
||||
int device_construct(Device* device) {
|
||||
device->internal.data = new(std::nothrow) DeviceData;
|
||||
if (device->internal.data == nullptr) {
|
||||
return ENOMEM;
|
||||
}
|
||||
LOG_I(TAG, "construct %s", device->name);
|
||||
mutex_construct(&device->internal.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_destruct(Device* device) {
|
||||
if (device->internal.state.started || device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
if (!get_device_data(device)->children.empty()) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
LOG_I(TAG, "destruct %s", device->name);
|
||||
mutex_destruct(&device->internal.mutex);
|
||||
delete get_device_data(device);
|
||||
device->internal.data = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Add a child to the list of children */
|
||||
static void device_add_child(struct Device* device, struct Device* child) {
|
||||
device_lock(device);
|
||||
assert(device->internal.state.added);
|
||||
get_device_data(device)->children.push_back(child);
|
||||
device_unlock(device);
|
||||
}
|
||||
|
||||
/** Remove a child from the list of children */
|
||||
static void device_remove_child(struct Device* device, struct Device* child) {
|
||||
device_lock(device);
|
||||
auto* parent_data = get_device_data(device);
|
||||
const auto iterator = std::ranges::find(parent_data->children, child);
|
||||
if (iterator != parent_data->children.end()) {
|
||||
parent_data->children.erase(iterator);
|
||||
}
|
||||
device_unlock(device);
|
||||
}
|
||||
|
||||
int device_add(Device* device) {
|
||||
LOG_I(TAG, "add %s", device->name);
|
||||
|
||||
// Already added
|
||||
if (device->internal.state.started || device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Add to ledger
|
||||
ledger_lock();
|
||||
ledger.devices.push_back(device);
|
||||
ledger_unlock();
|
||||
|
||||
// Add self to parent's children list
|
||||
auto* parent = device->parent;
|
||||
if (parent != nullptr) {
|
||||
device_add_child(parent, device);
|
||||
}
|
||||
|
||||
device->internal.state.added = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_remove(Device* device) {
|
||||
LOG_I(TAG, "remove %s", device->name);
|
||||
|
||||
if (device->internal.state.started || !device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Remove self from parent's children list
|
||||
auto* parent = device->parent;
|
||||
if (parent != nullptr) {
|
||||
device_remove_child(parent, device);
|
||||
}
|
||||
|
||||
ledger_lock();
|
||||
const auto iterator = std::ranges::find(ledger.devices, device);
|
||||
if (iterator == ledger.devices.end()) {
|
||||
ledger_unlock();
|
||||
goto failed_ledger_lookup;
|
||||
}
|
||||
ledger.devices.erase(iterator);
|
||||
ledger_unlock();
|
||||
|
||||
device->internal.state.added = false;
|
||||
return 0;
|
||||
|
||||
failed_ledger_lookup:
|
||||
|
||||
// Re-add to parent
|
||||
if (parent != nullptr) {
|
||||
device_add_child(parent, device);
|
||||
}
|
||||
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
int device_start(Device* device) {
|
||||
if (!device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (device->internal.driver == nullptr) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Already started
|
||||
if (device->internal.state.started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = driver_bind(device->internal.driver, device);
|
||||
device->internal.state.started = (result == 0);
|
||||
device->internal.state.start_result = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
int device_stop(struct Device* device) {
|
||||
if (!device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Not started
|
||||
if (!device->internal.state.started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = driver_unbind(device->internal.driver, device);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
device->internal.state.started = false;
|
||||
device->internal.state.start_result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_set_parent(Device* device, Device* parent) {
|
||||
assert(!device->internal.state.started);
|
||||
device->parent = parent;
|
||||
}
|
||||
|
||||
void for_each_device(void* callback_context, bool(*on_device)(Device* device, void* context)) {
|
||||
ledger_lock();
|
||||
for (auto* device : ledger.devices) {
|
||||
if (!on_device(device, callback_context)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ledger_unlock();
|
||||
}
|
||||
|
||||
void for_each_device_child(Device* device, void* callback_context, bool(*on_device)(struct Device* device, void* context)) {
|
||||
auto* data = get_device_data(device);
|
||||
for (auto* child_device : data->children) {
|
||||
if (!on_device(child_device, callback_context)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void for_each_device_of_type(const DeviceType* type, void* callback_context, bool(*on_device)(Device* device, void* context)) {
|
||||
ledger_lock();
|
||||
for (auto* device : ledger.devices) {
|
||||
auto* driver = device->internal.driver;
|
||||
if (driver != nullptr) {
|
||||
if (driver->device_type == type) {
|
||||
if (!on_device(device, callback_context)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ledger_unlock();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,188 @@
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include <Tactility/concurrent/Mutex.h>
|
||||
#include <Tactility/Device.h>
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/Error.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#define TAG LOG_TAG(driver)
|
||||
|
||||
struct DriverInternalData {
|
||||
Mutex mutex { 0 };
|
||||
int use_count = 0;
|
||||
|
||||
DriverInternalData() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DriverInternalData() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
struct DriverLedger {
|
||||
std::vector<Driver*> drivers;
|
||||
Mutex mutex { 0 };
|
||||
|
||||
DriverLedger() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DriverLedger() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
mutex_lock(&mutex);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
mutex_unlock(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
static DriverLedger& get_ledger() {
|
||||
static DriverLedger ledger;
|
||||
return ledger;
|
||||
}
|
||||
|
||||
#define ledger get_ledger()
|
||||
|
||||
#define driver_internal_data(driver) static_cast<DriverInternalData*>(driver->internal.data)
|
||||
#define driver_lock(driver) mutex_lock(&driver_internal_data(driver)->mutex);
|
||||
#define driver_unlock(driver) mutex_unlock(&driver_internal_data(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 bool driver_remove(Driver* driver) {
|
||||
LOG_I(TAG, "remove %s", driver->name);
|
||||
|
||||
ledger.lock();
|
||||
const auto iterator = std::ranges::find(ledger.drivers, driver);
|
||||
// check that there actually is a 3 in our vector
|
||||
if (iterator == ledger.drivers.end()) {
|
||||
ledger.unlock();
|
||||
return false;
|
||||
}
|
||||
ledger.drivers.erase(iterator);
|
||||
ledger.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int driver_construct(Driver* driver) {
|
||||
driver->internal.data = new(std::nothrow) DriverInternalData;
|
||||
if (driver->internal.data == nullptr) {
|
||||
return ENOMEM;
|
||||
}
|
||||
driver_add(driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int driver_destruct(Driver* driver) {
|
||||
// Check if in use
|
||||
if (driver_internal_data(driver)->use_count != 0) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
driver_remove(driver);
|
||||
delete driver_internal_data(driver);
|
||||
driver->internal.data = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool driver_is_compatible(Driver* driver, const char* compatible) {
|
||||
if (compatible == nullptr || driver->compatible == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const char** compatible_iterator = driver->compatible;
|
||||
while (*compatible_iterator != nullptr) {
|
||||
if (strcmp(*compatible_iterator, compatible) == 0) {
|
||||
return true;
|
||||
}
|
||||
compatible_iterator++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Driver* driver_find_compatible(const char* compatible) {
|
||||
ledger.lock();
|
||||
Driver* result = nullptr;
|
||||
for (auto* driver : ledger.drivers) {
|
||||
if (driver_is_compatible(driver, compatible)) {
|
||||
result = driver;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ledger.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
int driver_bind(Driver* driver, Device* device) {
|
||||
driver_lock(driver);
|
||||
|
||||
int err = 0;
|
||||
if (!device_is_added(device)) {
|
||||
err = ERROR_INVALID_STATE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (driver->start_device != nullptr) {
|
||||
err = driver->start_device(device);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
driver_internal_data(driver)->use_count++;
|
||||
driver_unlock(driver);
|
||||
|
||||
LOG_I(TAG, "bound %s to %s", driver->name, device->name);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
||||
driver_unlock(driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
int driver_unbind(Driver* driver, Device* device) {
|
||||
driver_lock(driver);
|
||||
|
||||
int err = 0;
|
||||
if (!device_is_added(device)) {
|
||||
err = ERROR_INVALID_STATE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (driver->stop_device != nullptr) {
|
||||
err = driver->stop_device(device);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
driver_internal_data(driver)->use_count--;
|
||||
driver_unlock(driver);
|
||||
|
||||
LOG_I(TAG, "unbound %s to %s", driver->name, device->name);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
||||
driver_unlock(driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef ESP_PLATFORM
|
||||
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void log_generic(const char* tag, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
printf("%s ", tag);
|
||||
vprintf(format, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <Tactility/drivers/GpioController.h>
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
#define GPIO_DRIVER_API(driver) ((struct GpioControllerApi*)driver->api)
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool gpio_controller_set_level(Device* device, gpio_pin_t pin, bool high) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return GPIO_DRIVER_API(driver)->set_level(device, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_level(Device* device, gpio_pin_t pin, bool* high) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return GPIO_DRIVER_API(driver)->get_level(device, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return GPIO_DRIVER_API(driver)->set_options(device, pin, options);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return GPIO_DRIVER_API(driver)->get_options(device, pin, options);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <Tactility/drivers/I2cController.h>
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
#define I2C_DRIVER_API(driver) ((struct I2cControllerApi*)driver->api)
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool i2c_controller_read(Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return I2C_DRIVER_API(driver)->read(device, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool i2c_controller_write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return I2C_DRIVER_API(driver)->write(device, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool i2c_controller_write_read(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) {
|
||||
const auto* driver = device_get_driver(device);
|
||||
return I2C_DRIVER_API(driver)->write_read(device, address, write_data, write_data_size, read_data, read_data_size, timeout);
|
||||
}
|
||||
|
||||
const struct DeviceType I2C_CONTROLLER_TYPE { 0 };
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern void register_kernel_drivers() {
|
||||
extern Driver root_driver;
|
||||
driver_construct(&root_driver);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <Tactility/drivers/Root.h>
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
Driver root_driver = {
|
||||
.name = "root",
|
||||
.compatible = (const char*[]) { "root", nullptr },
|
||||
.start_device = nullptr,
|
||||
.stop_device = nullptr,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.internal = { 0 }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
bindings: Bindings
|
||||
Reference in New Issue
Block a user