mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-22 00:15:05 +00:00
Compare commits
3 Commits
5699d05675
...
99bbe105eb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99bbe105eb | ||
|
|
f50fbfe331 | ||
|
|
cd7ffd3cb1 |
@ -88,7 +88,9 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding]) ->
|
||||
|
||||
def write_config(file, device: Device, bindings: list[Binding], type_name: str):
|
||||
device_identifier = get_device_identifier_safe(device)
|
||||
file.write(f"const static struct {type_name}_config {device_identifier}_config_instance" " = {\n")
|
||||
config_type = f"{type_name}_config_dt"
|
||||
config_variable_name = f"{device_identifier}_config"
|
||||
file.write(f"static const {config_type} {config_variable_name}" " = {\n")
|
||||
config_params = resolve_parameters_from_bindings(device, bindings)
|
||||
# Indent all params
|
||||
for index, config_param in enumerate(config_params):
|
||||
@ -99,49 +101,48 @@ def write_config(file, device: Device, bindings: list[Binding], type_name: str):
|
||||
file.write(f"{config_params_joined}\n")
|
||||
file.write("};\n\n")
|
||||
|
||||
def write_device(file, device: Device, bindings: list[Binding], verbose: bool):
|
||||
def write_device_structs(file, device: Device, bindings: list[Binding], verbose: bool):
|
||||
if verbose:
|
||||
print(f"Processing device '{device.identifier}'")
|
||||
print(f"Writing device struct for '{device.identifier}'")
|
||||
# Assemble some pre-requisites
|
||||
type_name = get_device_type_name(device, bindings)
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
|
||||
identifier = get_device_identifier_safe(device)
|
||||
device_binding = find_binding(compatible_property.value, bindings)
|
||||
config_variable_name = f"{identifier}_config"
|
||||
# Write config struct
|
||||
write_config(file, device, bindings, type_name)
|
||||
# Type & instance names
|
||||
api_type_name = f"{type_name}_api"
|
||||
api_instance_name = f"{type_name}_api_instance"
|
||||
init_function_name = f"{type_name}_init"
|
||||
deinit_function_name = f"{type_name}_deinit"
|
||||
config_instance_name = f"{identifier}_config_instance"
|
||||
# Write device struct
|
||||
file.write(f"extern const struct {api_type_name} {api_instance_name};\n\n")
|
||||
file.write("static struct device " f"{identifier}" " = {\n")
|
||||
file.write("\t.name = \"" f"{identifier}" "\",\n")
|
||||
file.write(f"\t.config = &{config_instance_name},\n")
|
||||
file.write(f"\t.api = &{api_instance_name},\n")
|
||||
file.write("\t.state = { .init_result = 0, .initialized = false },\n")
|
||||
file.write("\t.data = NULL,\n")
|
||||
file.write("\t.operations = { ")
|
||||
file.write(f".init = {init_function_name}, ")
|
||||
file.write(f".deinit = {deinit_function_name}")
|
||||
file.write("},\n")
|
||||
file.write("\t.metadata = {\n")
|
||||
node_label_count = len(device_binding.includes) + 1
|
||||
file.write(f"\t\t.compatible_count = {node_label_count},\n")
|
||||
file.write("\t\t.compatible = (const char*[]) {\n")
|
||||
file.write(f"\t\t\t\"{device_binding.compatible}\",\n")
|
||||
for include in device_binding.includes:
|
||||
include_compatible = include.removesuffix(".yaml")
|
||||
file.write(f"\t\t\t\"{include_compatible}\",\n")
|
||||
file.write("\t\t}\n")
|
||||
file.write("\t}\n")
|
||||
file.write(f"static struct Device {identifier}" " = {\n")
|
||||
file.write(f"\t.name = \"{device.identifier}\",\n") # Use original name
|
||||
file.write(f"\t.config = (void*)&{config_variable_name},\n")
|
||||
file.write("};\n\n")
|
||||
# Child devices
|
||||
for child_device in device.devices:
|
||||
write_device(file, child_device, bindings, verbose)
|
||||
write_device_structs(file, child_device, bindings, verbose)
|
||||
|
||||
def write_device_init(file, device: Device, parent_device: Device, bindings: list[Binding], verbose: bool):
|
||||
if verbose:
|
||||
print(f"Processing device init code for '{device.identifier}'")
|
||||
# Assemble some pre-requisites
|
||||
type_name = get_device_type_name(device, bindings)
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
|
||||
# Type & instance names
|
||||
identifier = get_device_identifier_safe(device)
|
||||
device_variable = identifier
|
||||
if parent_device is not None:
|
||||
parent_identifier = get_device_identifier_safe(parent_device)
|
||||
parent_value = f"&{parent_identifier}"
|
||||
else:
|
||||
parent_value = "NULL"
|
||||
# Write device struct
|
||||
file.write(f"\tif (init_builtin_device(&{device_variable}, \"{compatible_property.value}\", {parent_value}) != 0) return -1;\n")
|
||||
# Write children
|
||||
for child_device in device.devices:
|
||||
write_device_init(file, child_device, device, bindings, verbose)
|
||||
|
||||
def write_device_list_entry(file, device: Device):
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
@ -162,38 +163,67 @@ def write_device_list(file, devices: list[Device]):
|
||||
|
||||
def generate_devicetree_c(filename: str, items: list[object], bindings: list[Binding], verbose: bool):
|
||||
with open(filename, "w") as file:
|
||||
file.write(dedent('''\
|
||||
// Generated includes
|
||||
#include <Tactility/Device.h>
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/Log.h>
|
||||
// DTS includes
|
||||
'''))
|
||||
|
||||
# Write all headers first
|
||||
for item in items:
|
||||
if type(item) is IncludeC:
|
||||
write_include(file, item, verbose)
|
||||
file.write("\n")
|
||||
|
||||
file.write(dedent('''\
|
||||
#define TAG LOG_TAG(devicetree)
|
||||
|
||||
static int init_builtin_device(struct Device* device, const char* compatible, struct Device* parent_device) {
|
||||
struct Driver* driver = driver_find(compatible);
|
||||
if (driver == NULL) {
|
||||
LOG_E(TAG, "Can't find driver: %s", compatible);
|
||||
return -1;
|
||||
}
|
||||
device_construct(device);
|
||||
device_set_driver(device, driver);
|
||||
device_set_parent(device, parent_device);
|
||||
device_add(device);
|
||||
const int err = device_start(device);
|
||||
if (err != 0) {
|
||||
LOG_E(TAG, "Failed to start device %s with driver %s: error code %d", device->name, compatible, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
'''))
|
||||
|
||||
# Then write all devices
|
||||
devices = []
|
||||
for item in items:
|
||||
if type(item) is Device:
|
||||
devices.append(item)
|
||||
write_device(file, item, bindings, verbose)
|
||||
write_device_list(file, devices)
|
||||
file.write(dedent('''\
|
||||
struct device** devices_builtin_get() {
|
||||
return devices_builtin;
|
||||
}
|
||||
'''))
|
||||
write_device_structs(file, item, bindings, verbose)
|
||||
# Init function body start
|
||||
file.write("int devices_builtin_init() {\n")
|
||||
# Init function body logic
|
||||
for item in items:
|
||||
if type(item) is Device:
|
||||
write_device_init(file, item, None, bindings, verbose)
|
||||
file.write("\treturn 0;\n")
|
||||
# Init function body end
|
||||
file.write("}\n")
|
||||
|
||||
def generate_devicetree_h(filename: str):
|
||||
with open(filename, "w") as file:
|
||||
file.write(dedent('''\
|
||||
#pragma once
|
||||
#include <tactility/device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return an array of device* where the last item in the array is NULL
|
||||
*/
|
||||
struct device** devices_builtin_get();
|
||||
extern int devices_builtin_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
15
Devices/lilygo-tlora-pager/Source/bindings/tlora_pager.h
Normal file
15
Devices/lilygo-tlora-pager/Source/bindings/tlora_pager.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Tactility/bindings/bindings.h>
|
||||
#include <Tactility/drivers/Root.h>
|
||||
#include <drivers/TloraPager.h>
|
||||
|
||||
DEFINE_DEVICETREE(tlora_pager, struct RootConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
26
Devices/lilygo-tlora-pager/Source/drivers/TloraPager.cpp
Normal file
26
Devices/lilygo-tlora-pager/Source/drivers/TloraPager.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "TloraPager.h"
|
||||
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
static int start(Device* device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop(Device* device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Driver tlora_pager_driver = {
|
||||
.name = "T-Lora Pager",
|
||||
.compatible = (const char*[]) { "lilygo,tlora-pager", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.internal = { 0 }
|
||||
};
|
||||
|
||||
}
|
||||
11
Devices/lilygo-tlora-pager/Source/drivers/TloraPager.h
Normal file
11
Devices/lilygo-tlora-pager/Source/drivers/TloraPager.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Tactility/drivers/Root.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,18 +0,0 @@
|
||||
#include "tlora_pager.h"
|
||||
#include <esp_log.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "tlora_pager"
|
||||
|
||||
const struct tlora_pager_api tlora_pager_api_instance = {
|
||||
};
|
||||
|
||||
int tlora_pager_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tlora_pager_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/root.h>
|
||||
|
||||
// Inherit base config
|
||||
#define tlora_pager_config root_config
|
||||
|
||||
// Inherit base API
|
||||
#define tlora_pager_api root_api
|
||||
|
||||
int tlora_pager_init(const struct device* device);
|
||||
|
||||
int tlora_pager_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,8 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <drivers/tlora_pager.h>
|
||||
#include <tactility/drivers/esp32_gpio.h>
|
||||
#include <tactility/drivers/esp32_i2c.h>
|
||||
#include <bindings/tlora_pager.h>
|
||||
#include <Tactility/bindings/esp32_gpio.h>
|
||||
#include <Tactility/bindings/esp32_i2c.h>
|
||||
|
||||
/ {
|
||||
compatible = "lilygo,tlora-pager";
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/root.h>
|
||||
#include <Tactility/bindings/root.h>
|
||||
|
||||
/ {
|
||||
model = "Simulator";
|
||||
|
||||
14
Drivers/drivers-esp/include/Tactility/bindings/esp32_gpio.h
Normal file
14
Drivers/drivers-esp/include/Tactility/bindings/esp32_gpio.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/bindings/bindings.h>
|
||||
#include <Tactility/drivers/Esp32Gpio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(esp32_gpio, struct Esp32GpioConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
Drivers/drivers-esp/include/Tactility/bindings/esp32_i2c.h
Normal file
14
Drivers/drivers-esp/include/Tactility/bindings/esp32_i2c.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/bindings/bindings.h>
|
||||
#include <Tactility/drivers/Esp32I2c.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(esp32_i2c, struct Esp32I2cConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
15
Drivers/drivers-esp/include/Tactility/drivers/Esp32Gpio.h
Normal file
15
Drivers/drivers-esp/include/Tactility/drivers/Esp32Gpio.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Esp32GpioConfig {
|
||||
uint8_t gpio_count;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
19
Drivers/drivers-esp/include/Tactility/drivers/Esp32I2c.h
Normal file
19
Drivers/drivers-esp/include/Tactility/drivers/Esp32I2c.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/drivers/Gpio.h>
|
||||
#include <hal/i2c_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Esp32I2cConfig {
|
||||
uint32_t clock_frequency;
|
||||
struct GpioPinConfig pin_sda;
|
||||
struct GpioPinConfig pin_scl;
|
||||
const i2c_port_t port;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
|
||||
struct esp32_gpio_config {
|
||||
uint8_t gpio_count;
|
||||
};
|
||||
|
||||
#define esp32_gpio_api gpio_controller_api
|
||||
|
||||
int esp32_gpio_init(const struct device* device);
|
||||
|
||||
int esp32_gpio_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hal/i2c_types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
|
||||
struct esp32_i2c_config {
|
||||
uint32_t clock_frequency;
|
||||
struct gpio_pin_config pin_sda;
|
||||
struct gpio_pin_config pin_scl;
|
||||
const i2c_port_t port;
|
||||
};
|
||||
|
||||
// Inherit base API
|
||||
#define esp32_i2c_api i2c_controller_api
|
||||
|
||||
int esp32_i2c_init(const struct device* device);
|
||||
|
||||
int esp32_i2c_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,24 +1,28 @@
|
||||
#include <tactility/drivers/esp32_gpio.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "esp32_gpio"
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/drivers/Esp32Gpio.h>
|
||||
#include <Tactility/drivers/GpioController.h>
|
||||
#include <Tactility/drivers/Gpio.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#define GET_CONFIG(dev) ((struct esp32_gpio_config*)dev->config)
|
||||
#define TAG LOG_TAG(esp32_gpio)
|
||||
|
||||
static bool set_level(const struct device* dev, gpio_pin_t pin, bool high) {
|
||||
return gpio_set_level(pin, high) == ESP_OK;
|
||||
#define GET_CONFIG(device) ((struct Esp32GpioConfig*)device->internal.driver_data)
|
||||
|
||||
extern "C" {
|
||||
|
||||
static bool set_level(Device* device, gpio_pin_t pin, bool high) {
|
||||
return gpio_set_level(static_cast<gpio_num_t>(pin), high) == ESP_OK;
|
||||
}
|
||||
|
||||
static bool get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
|
||||
*high = gpio_get_level(pin) != 0;
|
||||
static bool get_level(Device* device, gpio_pin_t pin, bool* high) {
|
||||
*high = gpio_get_level(static_cast<gpio_num_t>(pin)) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
|
||||
const struct esp32_gpio_config* config = GET_CONFIG(dev);
|
||||
static bool set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
||||
const Esp32GpioConfig* config = GET_CONFIG(device);
|
||||
|
||||
if (pin >= config->gpio_count) {
|
||||
return false;
|
||||
@ -49,7 +53,7 @@ static bool set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t o
|
||||
return gpio_config(&esp_config) == ESP_OK;
|
||||
}
|
||||
|
||||
static bool get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
static bool get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
gpio_io_config_t esp_config;
|
||||
if (gpio_get_io_config((gpio_num_t)pin, &esp_config) != ESP_OK) {
|
||||
return false;
|
||||
@ -85,19 +89,30 @@ static bool get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t*
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct esp32_gpio_api esp32_gpio_api_instance = {
|
||||
static int start(Device* device) {
|
||||
ESP_LOGI(TAG, "start %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop(Device* device) {
|
||||
ESP_LOGI(TAG, "stop %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const static GpioControllerApi esp32_gpio_api = {
|
||||
.set_level = set_level,
|
||||
.get_level = get_level,
|
||||
.set_options = set_options,
|
||||
.get_options = get_options
|
||||
};
|
||||
|
||||
int esp32_gpio_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
Driver esp32_gpio_driver = {
|
||||
.name = "esp32_gpio",
|
||||
.compatible = (const char*[]) { "espressif,esp32-gpio", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = (void*)&esp32_gpio_api,
|
||||
.internal = { 0 }
|
||||
};
|
||||
|
||||
int esp32_gpio_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
} // extern "C"
|
||||
90
Drivers/drivers-esp/source/Esp32I2c.cpp
Normal file
90
Drivers/drivers-esp/source/Esp32I2c.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/drivers/Esp32I2c.h>
|
||||
#include <Tactility/drivers/I2cController.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#define TAG LOG_TAG(esp32_i2c)
|
||||
|
||||
struct InternalData {
|
||||
Mutex mutex {};
|
||||
|
||||
InternalData() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~InternalData() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
#define GET_CONFIG(device) ((Esp32I2cConfig*)device->internal.driver_data)
|
||||
#define GET_DATA(device) ((InternalData*)device->internal.driver_data)
|
||||
|
||||
#define lock(data) mutex_lock(&data->mutex);
|
||||
#define unlock(data) mutex_unlock(&data->mutex);
|
||||
|
||||
extern "C" {
|
||||
|
||||
static bool read(Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||
vPortAssertIfInISR();
|
||||
auto* driver_data = GET_DATA(device);
|
||||
lock(driver_data);
|
||||
const esp_err_t result = i2c_master_read_from_device(GET_CONFIG(device)->port, address, data, dataSize, timeout);
|
||||
unlock(driver_data);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
static bool write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||
vPortAssertIfInISR();
|
||||
auto* driver_data = GET_DATA(device);
|
||||
lock(driver_data);
|
||||
const esp_err_t result = i2c_master_write_to_device(GET_CONFIG(device)->port, address, data, dataSize, timeout);
|
||||
unlock(driver_data);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
static bool 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) {
|
||||
vPortAssertIfInISR();
|
||||
auto* driver_data = GET_DATA(device);
|
||||
lock(driver_data);
|
||||
const esp_err_t result = i2c_master_write_read_device(GET_CONFIG(device)->port, address, write_data, write_data_size, read_data, read_data_size, timeout);
|
||||
unlock(driver_data)
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
static int start(Device* device) {
|
||||
ESP_LOGI(TAG, "start %s", device->name);
|
||||
auto* data = new InternalData();
|
||||
device_set_driver_data(device, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop(Device* device) {
|
||||
ESP_LOGI(TAG, "stop %s", device->name);
|
||||
auto* driver_data = static_cast<InternalData*>(device_get_driver_data(device));
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete driver_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const I2cControllerApi esp32_i2c_api = {
|
||||
.read = read,
|
||||
.write = write,
|
||||
.write_read = write_read
|
||||
};
|
||||
|
||||
Driver esp32_i2c_driver = {
|
||||
.name = "esp32_i2c",
|
||||
.compatible = (const char*[]) { "espressif,esp32-i2c", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = (void*)&esp32_i2c_api,
|
||||
.internal = { 0 }
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
@ -1,47 +0,0 @@
|
||||
#include "tactility/drivers/esp32_i2c.h"
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
|
||||
#define TAG "esp32_i2c"
|
||||
|
||||
#define GET_CONFIG(dev) ((struct esp32_i2c_config*)dev->config)
|
||||
|
||||
static bool read(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||
// TODO: mutex
|
||||
esp_err_t result = i2c_master_read_from_device(GET_CONFIG(dev)->port, address, data, dataSize, timeout);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
static bool write(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||
// TODO: mutex
|
||||
esp_err_t result = i2c_master_write_to_device(GET_CONFIG(dev)->port, address, data, dataSize, timeout);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
static bool write_read(struct device* dev, 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) {
|
||||
// TODO: mutex
|
||||
esp_err_t result = i2c_master_write_read_device(GET_CONFIG(dev)->port, address, write_data, write_data_size, read_data, read_data_size, timeout);
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
||||
return result == ESP_OK;
|
||||
}
|
||||
|
||||
const struct esp32_i2c_api esp32_i2c_api_instance = {
|
||||
.read = read,
|
||||
.write = write,
|
||||
.write_read = write_read
|
||||
};
|
||||
|
||||
int esp32_i2c_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int esp32_i2c_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/Driver.h>
|
||||
#include <devicetree.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
@ -25,8 +27,17 @@ void app_main() {
|
||||
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
|
||||
#endif
|
||||
|
||||
auto devices = devices_builtin_get();
|
||||
tt::run(config, devices);
|
||||
extern Driver root_driver;
|
||||
extern Driver tlora_pager_driver;
|
||||
extern Driver esp32_gpio_driver;
|
||||
extern Driver esp32_i2c_driver;
|
||||
driver_construct(&root_driver);
|
||||
driver_construct(&tlora_pager_driver);
|
||||
driver_construct(&esp32_gpio_driver);
|
||||
driver_construct(&esp32_i2c_driver);
|
||||
|
||||
devices_builtin_init();
|
||||
tt::run(config);
|
||||
}
|
||||
|
||||
} // extern
|
||||
@ -5,8 +5,6 @@
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
/** @brief The configuration for the operating system
|
||||
@ -21,7 +19,7 @@ struct Configuration {
|
||||
* Attempts to initialize Tactility and all configured hardware.
|
||||
* @param[in] config
|
||||
*/
|
||||
void run(const Configuration& config, device** devices);
|
||||
void run(const Configuration& config);
|
||||
|
||||
/**
|
||||
* While technically nullable, this instance is always set if tt_init() succeeds.
|
||||
|
||||
@ -306,7 +306,7 @@ void registerApps() {
|
||||
registerInstalledAppsFromSdCards();
|
||||
}
|
||||
|
||||
void run(const Configuration& config, device** devices) {
|
||||
void run(const Configuration& config) {
|
||||
LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
|
||||
|
||||
assert(config.hardware);
|
||||
@ -315,11 +315,6 @@ void run(const Configuration& config, device** devices) {
|
||||
// Assign early so starting services can use it
|
||||
config_instance = &config;
|
||||
|
||||
if (devices != nullptr) {
|
||||
device_add_all(devices);
|
||||
device_init_all(devices);
|
||||
}
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
initEsp();
|
||||
#endif
|
||||
|
||||
49
core/include/Tactility/Bus.h
Normal file
49
core/include/Tactility/Bus.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Device;
|
||||
|
||||
/**
|
||||
* A bus contains a group of devices owned by a parent device.
|
||||
* It is used to make a group of devices discoverable.
|
||||
*
|
||||
* Example usage:
|
||||
* The "i2c0" bus is created by the i2c_controller driver when a device is started by a driver.
|
||||
* The postfix "0" means that it was the first device instance (at index 0).
|
||||
*/
|
||||
struct Bus {
|
||||
/** The name of the bus (e.g. "i2c0" or "spi1"). Valid characters: a-z a-Z 0-9 - _ . */
|
||||
const char* name;
|
||||
/** Internal data */
|
||||
struct {
|
||||
/** The device this bus belongs to */
|
||||
struct Device* parent_device;
|
||||
/** The bus' private data */
|
||||
void* data;
|
||||
} internal;
|
||||
};
|
||||
|
||||
// region Bus management
|
||||
|
||||
int bus_construct(struct Bus* bus);
|
||||
|
||||
int bus_destruct(struct Bus* bus);
|
||||
|
||||
struct Bus* bus_find(const char* name);
|
||||
|
||||
// endregion
|
||||
|
||||
// region Bus device management
|
||||
|
||||
int bus_add_device(struct Bus*, struct Device* dev);
|
||||
|
||||
void bus_remove_device(struct Bus*, struct Device* dev);
|
||||
|
||||
// endregion
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
163
core/include/Tactility/Device.h
Normal file
163
core/include/Tactility/Device.h
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Tactility/Bus.h>
|
||||
#include <Tactility/concurrent/Mutex.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Driver;
|
||||
|
||||
/** 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 */
|
||||
void* config;
|
||||
/** Internal data */
|
||||
struct {
|
||||
/** The parent device that this device belongs to. Can be NULL, but only the root device should have a NULL parent. */
|
||||
struct Device* parent;
|
||||
/** 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 bus that is owned by this device. This bus can contain child devices to make them discoverable. Can be NULL. */
|
||||
struct Bus* bus;
|
||||
/** The mutex for device operations */
|
||||
struct Mutex mutex;
|
||||
/** The device state */
|
||||
struct {
|
||||
uint8_t 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
|
||||
*/
|
||||
void 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
|
||||
*/
|
||||
void 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 true when the device was found and deregistered
|
||||
*/
|
||||
bool 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 struct Bus* device_get_bus(const struct Device* device) {
|
||||
return device->internal.bus;
|
||||
}
|
||||
|
||||
static inline void device_set_bus(struct Device* device, struct Bus* bus) {
|
||||
device->internal.bus = bus;
|
||||
}
|
||||
|
||||
static inline const char* device_get_bus_name(const struct Device* device) {
|
||||
return device->internal.bus ? device->internal.bus->name : "";
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
40
core/include/Tactility/Driver.h
Normal file
40
core/include/Tactility/Driver.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/Bus.h>
|
||||
#include <Tactility/Device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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;
|
||||
/** Internal data */
|
||||
struct {
|
||||
/** Contains private data */
|
||||
void* data;
|
||||
} internal;
|
||||
};
|
||||
|
||||
int driver_construct(struct Driver* drv);
|
||||
|
||||
int driver_destruct(struct Driver* drv);
|
||||
|
||||
struct Driver* driver_find(const char* compatible);
|
||||
|
||||
int driver_bind(struct Driver* drv, struct Device* dev);
|
||||
|
||||
int driver_unbind(struct Driver* drv, struct Device* dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
10
core/include/Tactility/Error.h
Normal file
10
core/include/Tactility/Error.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/errno.h>
|
||||
|
||||
#define CUSTOM_ERROR_CODE(x) (-__ELASTERROR - x)
|
||||
|
||||
#define ERROR_UNDEFINED CUSTOM_ERROR_CODE(1)
|
||||
#define ERROR_INVALID_STATE CUSTOM_ERROR_CODE(2)
|
||||
#define ERROR_INVALID_PARAMETER CUSTOM_ERROR_CODE(3)
|
||||
#define ERROR_MISSING_PARAMETER CUSTOM_ERROR_CODE(4)
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "freertos.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
#define xPortInIsrContext(x) (false)
|
||||
9
core/include/Tactility/FreeRTOS/semphr.h
Normal file
9
core/include/Tactility/FreeRTOS/semphr.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#else
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
#endif
|
||||
35
core/include/Tactility/Log.h
Normal file
35
core/include/Tactility/Log.h
Normal 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
|
||||
8
core/include/Tactility/bindings/bindings.h
Normal file
8
core/include/Tactility/bindings/bindings.h
Normal file
@ -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;
|
||||
3
core/include/Tactility/bindings/gpio.h
Normal file
3
core/include/Tactility/bindings/gpio.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/drivers/Gpio.h>
|
||||
7
core/include/Tactility/bindings/root.h
Normal file
7
core/include/Tactility/bindings/root.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/bindings/bindings.h>
|
||||
#include <Tactility/drivers/Root.h>
|
||||
|
||||
DEFINE_DEVICETREE(root, struct RootConfig)
|
||||
|
||||
48
core/include/Tactility/concurrent/Mutex.h
Normal file
48
core/include/Tactility/concurrent/Mutex.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/FreeRTOS/semphr.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Mutex {
|
||||
QueueHandle_t handle;
|
||||
};
|
||||
|
||||
inline static void mutex_construct(struct Mutex* mutex) {
|
||||
mutex->handle = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
inline static void mutex_destruct(struct Mutex* mutex) {
|
||||
assert(mutex != NULL);
|
||||
assert(mutex->handle != NULL);
|
||||
vPortAssertIfInISR();
|
||||
vSemaphoreDelete(mutex->handle);
|
||||
mutex->handle = NULL;
|
||||
}
|
||||
|
||||
inline static void mutex_lock(struct Mutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
xSemaphoreTake(mutex->handle, portMAX_DELAY);
|
||||
}
|
||||
|
||||
inline static bool mutex_try_lock(struct Mutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
|
||||
}
|
||||
|
||||
inline static bool mutex_is_locked(struct Mutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
||||
}
|
||||
|
||||
inline static void mutex_unlock(struct Mutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
xSemaphoreGive(mutex->handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
46
core/include/Tactility/concurrent/RecursiveMutex.h
Normal file
46
core/include/Tactility/concurrent/RecursiveMutex.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#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 != NULL);
|
||||
assert(mutex->handle != NULL);
|
||||
vSemaphoreDelete(mutex->handle);
|
||||
mutex->handle = NULL;
|
||||
}
|
||||
|
||||
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
xSemaphoreTake(mutex->handle, portMAX_DELAY);
|
||||
}
|
||||
|
||||
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
||||
}
|
||||
|
||||
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
|
||||
}
|
||||
|
||||
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
|
||||
assert(mutex->handle != NULL);
|
||||
xSemaphoreGive(mutex->handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -4,7 +4,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <Tactility/Device.h>
|
||||
|
||||
#define GPIO_OPTIONS_MASK 0x1f
|
||||
|
||||
@ -19,7 +19,7 @@ extern "C" {
|
||||
#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_interrupt_type_t)((options & GPIO_INTERRUPT_BITMASK) >> 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 {
|
||||
@ -30,7 +30,7 @@ typedef enum {
|
||||
GPIO_INTERRUPT_LOW_LEVEL = 4,
|
||||
GPIO_INTERRUPT_HIGH_LEVEL = 5,
|
||||
GPIO__MAX,
|
||||
} gpio_interrupt_type_t;
|
||||
} GpioInterruptType;
|
||||
|
||||
/**
|
||||
* @brief Provides a type to hold a GPIO pin index.
|
||||
@ -67,25 +67,21 @@ typedef uint16_t gpio_flags_t;
|
||||
* controlled by that device, and the subset of pin configuration
|
||||
* flags which may be given in devicetree.
|
||||
*/
|
||||
struct gpio_pin_config {
|
||||
struct GpioPinConfig {
|
||||
/** GPIO device controlling the pin */
|
||||
const struct device* port;
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Validate that GPIO port is ready.
|
||||
*
|
||||
* @param spec GPIO specification from devicetree
|
||||
*
|
||||
* @retval true if the GPIO spec is ready for use.
|
||||
* @retval false if the GPIO spec is not ready for use.
|
||||
* 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
|
||||
*/
|
||||
inline bool gpio_is_ready(const struct gpio_pin_config* pin_config) {
|
||||
static inline bool gpio_is_ready(const struct GpioPinConfig* pin_config) {
|
||||
return device_is_ready(pin_config->port);
|
||||
}
|
||||
|
||||
28
core/include/Tactility/drivers/GpioController.h
Normal file
28
core/include/Tactility/drivers/GpioController.h
Normal file
@ -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
|
||||
26
core/include/Tactility/drivers/I2cController.h
Normal file
26
core/include/Tactility/drivers/I2cController.h
Normal file
@ -0,0 +1,26 @@
|
||||
#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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -4,16 +4,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct root_config {
|
||||
struct RootConfig {
|
||||
const char* model;
|
||||
};
|
||||
|
||||
struct root_api {
|
||||
};
|
||||
|
||||
#define root_init nullptr
|
||||
#define root_deinit nullptr
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,111 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct device;
|
||||
|
||||
struct device_operations {
|
||||
/** Initialization function */
|
||||
int (*init)(const struct device* device);
|
||||
/** De-initialization function */
|
||||
int (*deinit)(const struct device* device);
|
||||
};
|
||||
|
||||
struct device_state {
|
||||
uint8_t init_result;
|
||||
bool initialized : 1;
|
||||
};
|
||||
|
||||
struct device_metadata {
|
||||
/** @brief number of elements in the compatible array */
|
||||
size_t compatible_count;
|
||||
/** @brief array of strings containing the compatible device names */
|
||||
const char** compatible;
|
||||
};
|
||||
|
||||
struct device {
|
||||
/** Name of the device as defined in the dts file */
|
||||
const char* name;
|
||||
/** Address of device instance configuration. This relates to the parameters that are specified in the dts file*/
|
||||
const void* config;
|
||||
/** Address of the API exposed by the device instance */
|
||||
const void* api;
|
||||
/** The device state */
|
||||
struct device_state state;
|
||||
/** Address of the device's private data */
|
||||
void* data;
|
||||
/** Device operations: used for initializing and deinitializing */
|
||||
struct device_operations operations;
|
||||
/** Device metadata */
|
||||
struct device_metadata metadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a device.
|
||||
* @param[in] dev
|
||||
* @return the return code of the device's init function
|
||||
*/
|
||||
uint8_t device_init(struct device* dev);
|
||||
|
||||
/**
|
||||
* Initialize an array of devices.
|
||||
* @param[in] device_array a null-terminated array of devices
|
||||
* @retval true if all devices initialized successfully
|
||||
* @retval false if any device failed to initialize
|
||||
*/
|
||||
bool device_init_all(struct device** device_array);
|
||||
|
||||
/**
|
||||
* Deinitialize a device.
|
||||
* @param[in] dev
|
||||
* @return the return code of the device's deinit function
|
||||
*/
|
||||
uint8_t device_deinit(struct device* dev);
|
||||
|
||||
/**
|
||||
* Indicated 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
|
||||
*/
|
||||
bool device_is_ready(const struct device* dev);
|
||||
|
||||
/**
|
||||
* Register a single device.
|
||||
*
|
||||
* @param[in] dev non-null device pointer
|
||||
*/
|
||||
void device_add(const struct device* dev);
|
||||
|
||||
/**
|
||||
* Register all devices in the specified array.
|
||||
* The array must be null-terminated.
|
||||
*
|
||||
* @param[in] device_array non-null array of devices
|
||||
*/
|
||||
void device_add_all(struct device** device_array);
|
||||
|
||||
/**
|
||||
* Deregister a device
|
||||
* @param[in] dev non-null device pointer
|
||||
* @return true when the device was found and deregistered
|
||||
*/
|
||||
bool device_remove(const struct device* dev);
|
||||
|
||||
/**
|
||||
* Iterate the devicetree. Find the next with the specified label.
|
||||
* @param[in] identifier the identifier of the device, such as "root" or "i2c-controller"
|
||||
* @param[inout] dev a pointer to a device pointer in the tree, or nullptr to start searching for the first device
|
||||
* @return true if a device was found
|
||||
*/
|
||||
bool device_find_next_by_compatible(const char* identifier, const struct device** dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "gpio.h"
|
||||
|
||||
struct gpio_controller_api {
|
||||
bool (*set_level)(const struct device*, gpio_pin_t pin, bool high);
|
||||
bool (*get_level)(const struct device*, gpio_pin_t pin, bool* high);
|
||||
bool (*set_options)(const struct device*, gpio_pin_t pin, gpio_flags_t options);
|
||||
bool (*get_options)(const struct device*, gpio_pin_t pin, gpio_flags_t* options);
|
||||
};
|
||||
|
||||
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high);
|
||||
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high);
|
||||
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options);
|
||||
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options);
|
||||
|
||||
inline bool gpio_set_options_config(const struct gpio_pin_config* config) {
|
||||
return gpio_controller_set_options(config->port, config->pin, config->dt_flags);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <tactility/freertos/freertos.h>
|
||||
#include "gpio.h"
|
||||
|
||||
struct i2c_controller_api {
|
||||
bool (*read)(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
|
||||
bool (*write)(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
|
||||
bool (*write_read)(struct device* dev, 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* dev, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
|
||||
|
||||
bool i2c_controller_write(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout);
|
||||
|
||||
bool i2c_controller_write_read(struct device* dev, 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#else
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct SemaphoreHandleDeleter {
|
||||
static void operator()(QueueHandle_t handleToDelete) {
|
||||
assert(xPortInIsrContext() == pdFALSE);
|
||||
vSemaphoreDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
|
||||
void log_generic(const char* tag, const char* format, ...);
|
||||
|
||||
#define LOG_E(x, ...) log(x, ##__VA_ARGS__)
|
||||
#define LOG_W(x, ...) log(x, ##__VA_ARGS__)
|
||||
#define LOG_I(x, ...) log(x, ##__VA_ARGS__)
|
||||
#define LOG_D(x, ...) log(x, ##__VA_ARGS__)
|
||||
#define LOG_V(x, ...) log(x, ##__VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define LOG_E(x, ...) ESP_LOGD(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
|
||||
115
core/source/Bus.cpp
Normal file
115
core/source/Bus.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <Tactility/concurrent/Mutex.h>
|
||||
#include <Tactility/Bus.h>
|
||||
#include <Tactility/Device.h>
|
||||
#include <Tactility/Driver.h>
|
||||
|
||||
/** Keeps track of all existing buses */
|
||||
struct BusLedger {
|
||||
std::vector<Bus*> buses = {};
|
||||
Mutex mutex {};
|
||||
|
||||
BusLedger() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~BusLedger() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
/* Internal data for a Bus */
|
||||
struct BusData {
|
||||
std::vector<Device*> devices = {};
|
||||
std::vector<Driver*> drivers = {};
|
||||
Mutex mutex {};
|
||||
|
||||
BusData() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~BusData() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
static BusLedger ledger;
|
||||
|
||||
#define ledger_lock() mutex_lock(&ledger.mutex);
|
||||
#define ledger_unlock() mutex_unlock(&ledger.mutex);
|
||||
|
||||
#define bus_lock(bus_data) mutex_lock(&bus_data->mutex);
|
||||
#define bus_unlock(bus_data) mutex_unlock(&bus_data->mutex);
|
||||
|
||||
#define BUS_INSTANCE_DATA(bus) ((struct BusData*)bus->internal.data)
|
||||
|
||||
extern "C" {
|
||||
|
||||
// region Bus management
|
||||
|
||||
static int bus_add(Bus* bus) {
|
||||
ledger_lock();
|
||||
ledger.buses.push_back(bus);
|
||||
ledger_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bus_remove(Bus* bus) {
|
||||
ledger_lock();
|
||||
const auto iterator = std::ranges::find(ledger.buses, bus);
|
||||
if (iterator != ledger.buses.end()) {
|
||||
ledger.buses.erase(iterator);
|
||||
}
|
||||
ledger_unlock();
|
||||
}
|
||||
|
||||
int bus_construct(Bus* bus) {
|
||||
bus->internal.data = new BusData();
|
||||
bus_add(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_destruct(Bus* bus) {
|
||||
delete BUS_INSTANCE_DATA(bus);
|
||||
bus_remove(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Bus device management
|
||||
|
||||
Bus* bus_find(const char* name) {
|
||||
ledger_lock();
|
||||
const auto it = std::ranges::find_if(ledger.buses, [name](Bus* bus) {
|
||||
return strcmp(name, bus->name) == 0;
|
||||
});
|
||||
Bus* result = (it != ledger.buses.end()) ? *it : nullptr;
|
||||
ledger_unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
int bus_add_device(Bus* bus, Device* dev) {
|
||||
auto* bus_data = BUS_INSTANCE_DATA(bus);
|
||||
bus_lock(bus_data);
|
||||
bus_data->devices.push_back(dev);
|
||||
bus_unlock(bus_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bus_remove_device(Bus* bus, Device* dev) {
|
||||
auto* bus_data = BUS_INSTANCE_DATA(bus);
|
||||
bus_lock(bus_data);
|
||||
const auto iterator = std::ranges::find(bus_data->devices, dev);
|
||||
if (iterator != bus_data->devices.end()) {
|
||||
bus_data->devices.erase(iterator);
|
||||
}
|
||||
bus_unlock(bus_data);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
} // extern "C"
|
||||
202
core/source/Device.cpp
Normal file
202
core/source/Device.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#include <Tactility/Device.h>
|
||||
#include <Tactility/Error.h>
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#include <algorithm>
|
||||
#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 {};
|
||||
|
||||
DeviceLedger() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DeviceLedger() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
static DeviceLedger 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)
|
||||
|
||||
void device_construct(Device* device) {
|
||||
LOG_I(TAG, "construct %s", device->name);
|
||||
device->internal.data = new DeviceData();
|
||||
mutex_construct(&device->internal.mutex);
|
||||
}
|
||||
|
||||
int device_destruct(Device* device) {
|
||||
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);
|
||||
get_device_data(device)->children.push_back(device);
|
||||
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, device);
|
||||
if (iterator != parent_data->children.end()) {
|
||||
parent_data->children.erase(iterator);
|
||||
}
|
||||
device_unlock(device);
|
||||
}
|
||||
|
||||
void device_add(Device* device) {
|
||||
LOG_I(TAG, "add %s", device->name);
|
||||
|
||||
assert(!device->internal.state.started);
|
||||
|
||||
// Already added
|
||||
if (device->internal.state.added) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to ledger
|
||||
ledger_lock();
|
||||
ledger.devices.push_back(device);
|
||||
ledger_unlock();
|
||||
|
||||
// Add self to parent's children list
|
||||
auto* parent = device->internal.parent;
|
||||
if (parent != nullptr) {
|
||||
device_add_child(parent, device);
|
||||
}
|
||||
|
||||
auto* bus = device->internal.bus;
|
||||
if (bus != nullptr) {
|
||||
bus_add_device(bus, device);
|
||||
}
|
||||
|
||||
device->internal.state.added = true;
|
||||
}
|
||||
|
||||
bool device_remove(Device* device) {
|
||||
LOG_I(TAG, "remove %s", device->name);
|
||||
|
||||
assert(!device->internal.state.started);
|
||||
|
||||
// Already removed
|
||||
if (!device->internal.state.added) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* bus = device->internal.bus;
|
||||
if (bus != nullptr) {
|
||||
bus_remove_device(bus, device);
|
||||
}
|
||||
|
||||
// Remove self from parent's children list
|
||||
auto* parent = device->internal.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 true;
|
||||
|
||||
failed_ledger_lookup:
|
||||
|
||||
// Re-add to parent
|
||||
if (parent != nullptr) {
|
||||
device_add_child(parent, device);
|
||||
}
|
||||
|
||||
// Re-add to bus
|
||||
if (bus != nullptr) {
|
||||
bus_add_device(bus, device);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int device_start(Device* device) {
|
||||
if (!device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Already started
|
||||
if (device->internal.state.started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (device->internal.driver == nullptr) {
|
||||
LOG_E(TAG, "start error: no driver for %s", device->name);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
int result = driver_bind(device->internal.driver, device);
|
||||
if (result != 0) {
|
||||
device->internal.state.started = true;
|
||||
device->internal.state.start_result = result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_stop(struct Device* device) {
|
||||
if (!device->internal.state.added) {
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Already stopped
|
||||
if (!device->internal.state.started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = driver_unbind(device->internal.driver, device);
|
||||
if (result != 0) {
|
||||
// Re-add to bus
|
||||
if (device->internal.bus != nullptr) {
|
||||
bus_add_device(device->internal.bus, device);
|
||||
}
|
||||
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->internal.parent = parent;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
161
core/source/Driver.cpp
Normal file
161
core/source/Driver.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include <sys/errno.h>
|
||||
#include <vector>
|
||||
|
||||
#include <Tactility/concurrent/Mutex.h>
|
||||
#include <Tactility/Driver.h>
|
||||
#include <Tactility/Error.h>
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#define TAG LOG_TAG(driver)
|
||||
|
||||
struct DriverInternalData {
|
||||
Mutex mutex {};
|
||||
int use_count = 0;
|
||||
|
||||
DriverInternalData() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DriverInternalData() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
struct DriverLedger {
|
||||
std::vector<Driver*> drivers = {};
|
||||
Mutex mutex {};
|
||||
|
||||
DriverLedger() {
|
||||
mutex_construct(&mutex);
|
||||
}
|
||||
|
||||
~DriverLedger() {
|
||||
mutex_destruct(&mutex);
|
||||
}
|
||||
};
|
||||
|
||||
static DriverLedger ledger;
|
||||
|
||||
#define ledger_lock() mutex_lock(&ledger.mutex);
|
||||
#define ledger_unlock() mutex_unlock(&ledger.mutex);
|
||||
|
||||
#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* dev) {
|
||||
LOG_I(TAG, "add %s", dev->name);
|
||||
ledger_lock();
|
||||
ledger.drivers.push_back(dev);
|
||||
ledger_unlock();
|
||||
}
|
||||
|
||||
static bool driver_remove(Driver* dev) {
|
||||
LOG_I(TAG, "remove %s", dev->name);
|
||||
|
||||
ledger_lock();
|
||||
const auto iterator = std::ranges::find(ledger.drivers, dev);
|
||||
// check that there actually is a 3 in our vector
|
||||
if (iterator == ledger.drivers.end()) {
|
||||
return false;
|
||||
}
|
||||
ledger.drivers.erase(iterator);
|
||||
ledger_unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int driver_construct(Driver* driver) {
|
||||
driver->internal.data = new DriverInternalData();
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Driver* driver_find(const char* compatible) {
|
||||
ledger_lock();
|
||||
const auto it = std::ranges::find_if(ledger.drivers, [compatible](Driver* driver) {
|
||||
const char** current_compatible = driver->compatible;
|
||||
assert(current_compatible != nullptr);
|
||||
while (*current_compatible != nullptr) {
|
||||
if (strcmp(compatible, *current_compatible) == 0) {
|
||||
return true;
|
||||
}
|
||||
current_compatible++;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
auto* driver = (it != ledger.drivers.end()) ? *it : nullptr;
|
||||
ledger_unlock();
|
||||
return driver;
|
||||
}
|
||||
|
||||
int driver_bind(Driver* driver, Device* device) {
|
||||
driver_lock(driver);
|
||||
|
||||
int err = 0;
|
||||
if (!device_is_added(device)) {
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
device_set_driver(device, driver);
|
||||
|
||||
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);
|
||||
|
||||
if (driver->stop_device == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int err = driver->stop_device(device);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
device_set_driver(device, nullptr);
|
||||
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"
|
||||
@ -1,7 +1,7 @@
|
||||
#include <tactility/log.h>
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CONFIG_DEVICE_INDEX_SIZE 64
|
||||
#define TAG "device"
|
||||
|
||||
// TODO: Automatically increase allocated size
|
||||
static const struct device* device_index[CONFIG_DEVICE_INDEX_SIZE ] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
uint8_t device_init(struct device* const dev) {
|
||||
assert(dev != NULL);
|
||||
LOG_I(TAG, "init %s", dev->name);
|
||||
|
||||
if (!dev->state.initialized) {
|
||||
if (dev->operations.init == NULL) {
|
||||
dev->state.initialized = true;
|
||||
dev->state.init_result = 0U;
|
||||
} else {
|
||||
dev->state.init_result = dev->operations.init(dev);
|
||||
dev->state.initialized = dev->state.init_result == 0U;
|
||||
}
|
||||
}
|
||||
|
||||
return dev->state.init_result;
|
||||
}
|
||||
|
||||
bool device_init_all(struct device** const device_array) {
|
||||
assert(device_array != NULL);
|
||||
struct device** current_device = device_array;
|
||||
bool all_succeeded = true;
|
||||
while (*current_device != NULL) {
|
||||
struct device* device = *current_device;
|
||||
if (device_init(device) != 0U) {
|
||||
all_succeeded = false;
|
||||
}
|
||||
current_device++;
|
||||
}
|
||||
return all_succeeded;
|
||||
}
|
||||
|
||||
uint8_t device_deinit(struct device* const dev) {
|
||||
assert(dev != NULL);
|
||||
LOG_I(TAG, "deinit %s", dev->name);
|
||||
|
||||
if (dev->state.initialized) {
|
||||
if (dev->operations.deinit != NULL) {
|
||||
dev->state.init_result = dev->operations.deinit(dev);
|
||||
if (dev->state.init_result == 0U) {
|
||||
dev->state.initialized = false;
|
||||
}
|
||||
} else {
|
||||
dev->state.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
return !dev->state.init_result;
|
||||
}
|
||||
|
||||
bool device_is_ready(const struct device* const dev) {
|
||||
assert(dev != NULL);
|
||||
return dev->state.initialized && (dev->state.init_result == 0U);
|
||||
}
|
||||
|
||||
|
||||
void device_add(const struct device* dev) {
|
||||
assert(dev != NULL);
|
||||
LOG_I(TAG, "add %s", dev->name);
|
||||
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
|
||||
if (device_index[i] == NULL) {
|
||||
device_index[i] = dev;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false); // out of space
|
||||
}
|
||||
|
||||
void device_add_all(struct device** const device_array) {
|
||||
assert(device_array != NULL);
|
||||
struct device** current_device = device_array;
|
||||
while (*current_device != NULL) {
|
||||
struct device* device = *current_device;
|
||||
device_add(device);
|
||||
current_device++;
|
||||
}
|
||||
}
|
||||
|
||||
bool device_remove(const struct device* dev) {
|
||||
assert(dev != NULL);
|
||||
LOG_I(TAG, "remove %s", dev->name);
|
||||
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
|
||||
if (device_index[i] == dev) {
|
||||
device_index[i] = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_find_next_by_compatible(const char* identifier, const struct device** dev) {
|
||||
bool found_first = (*dev == NULL);
|
||||
for (int device_idx = 0; device_idx < CONFIG_DEVICE_INDEX_SIZE; device_idx++) {
|
||||
const struct device* indexed_device = device_index[device_idx];
|
||||
if (indexed_device != NULL) {
|
||||
if (!found_first) {
|
||||
if (indexed_device == *dev) {
|
||||
found_first = true;
|
||||
}
|
||||
} else {
|
||||
for (int label_idx = 0; label_idx< indexed_device->metadata.compatible_count; ++label_idx) {
|
||||
const char* indexed_device_label = indexed_device->metadata.compatible[label_idx];
|
||||
if (strcmp(indexed_device_label, identifier) == 0) {
|
||||
*dev = indexed_device;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
28
core/source/drivers/GpioController.cpp
Normal file
28
core/source/drivers/GpioController.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
23
core/source/drivers/I2cController.cpp
Normal file
23
core/source/drivers/I2cController.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
||||
15
core/source/drivers/Root.cpp
Normal file
15
core/source/drivers/Root.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#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,
|
||||
.internal = { 0 }
|
||||
};
|
||||
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
|
||||
#define GPIO_API(dev) ((struct gpio_controller_api*)dev->api)
|
||||
|
||||
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high) {
|
||||
return GPIO_API(dev)->set_level(dev, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
|
||||
return GPIO_API(dev)->get_level(dev, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
|
||||
return GPIO_API(dev)->set_options(dev, pin, options);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
return GPIO_API(dev)->get_options(dev, pin, options);
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
|
||||
#define GPIO_API(dev) ((struct i2c_controller_api*)dev->api)
|
||||
|
||||
bool i2c_controller_read(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||
return GPIO_API(dev)->read(dev, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool i2c_controller_write(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||
return GPIO_API(dev)->write(dev, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool i2c_controller_write_read(struct device* dev, 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) {
|
||||
return GPIO_API(dev)->write_read(dev, address, write_data, write_data_size, read_data, read_data_size, timeout);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "tactility/drivers/root.h"
|
||||
|
||||
const struct root_api root_api_instance = {};
|
||||
Loading…
x
Reference in New Issue
Block a user