mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 10:53:17 +00:00
TactilityKernel improvements and more (#459)
This commit is contained in:
parent
96eccbdc8d
commit
dfe2c865d1
@ -3,6 +3,7 @@
|
|||||||
#include "devices/TrackballDevice.h"
|
#include "devices/TrackballDevice.h"
|
||||||
|
|
||||||
#include <Tactility/hal/gps/GpsConfiguration.h>
|
#include <Tactility/hal/gps/GpsConfiguration.h>
|
||||||
|
#include <Tactility/kernel/Kernel.h>
|
||||||
#include <Tactility/kernel/SystemEvents.h>
|
#include <Tactility/kernel/SystemEvents.h>
|
||||||
#include <Tactility/Logger.h>
|
#include <Tactility/Logger.h>
|
||||||
#include <Tactility/LogMessages.h>
|
#include <Tactility/LogMessages.h>
|
||||||
@ -30,6 +31,9 @@ static bool powerOn() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoids crash when no SD card is inserted. It's unknown why, but likely is related to power draw.
|
||||||
|
tt::kernel::delayMillis(100);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,10 @@ static int stop(Device* device) {
|
|||||||
Driver tlora_pager_driver = {
|
Driver tlora_pager_driver = {
|
||||||
.name = "T-Lora Pager",
|
.name = "T-Lora Pager",
|
||||||
.compatible = (const char*[]) { "lilygo,tlora-pager", nullptr },
|
.compatible = (const char*[]) { "lilygo,tlora-pager", nullptr },
|
||||||
.start_device = start,
|
.startDevice = start,
|
||||||
.stop_device = stop,
|
.stopDevice = stop,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
7
Platforms/PlatformEsp32/Include/Tactility/ErrorEsp32.h
Normal file
7
Platforms/PlatformEsp32/Include/Tactility/ErrorEsp32.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
|
||||||
|
error_t esp_err_to_error(esp_err_t error);
|
||||||
@ -8,7 +8,7 @@ extern "C" {
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct Esp32GpioConfig {
|
struct Esp32GpioConfig {
|
||||||
uint8_t gpio_count;
|
uint8_t gpioCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -9,9 +9,9 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Esp32I2cConfig {
|
struct Esp32I2cConfig {
|
||||||
uint32_t clock_frequency;
|
uint32_t clockFrequency;
|
||||||
struct GpioPinConfig pin_sda;
|
struct GpioPinConfig pinSda;
|
||||||
struct GpioPinConfig pin_scl;
|
struct GpioPinConfig pinScl;
|
||||||
const i2c_port_t port;
|
const i2c_port_t port;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
16
Platforms/PlatformEsp32/Source/ErrorEsp32.cpp
Normal file
16
Platforms/PlatformEsp32/Source/ErrorEsp32.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <Tactility/ErrorEsp32.h>
|
||||||
|
|
||||||
|
error_t esp_err_to_error(esp_err_t error) {
|
||||||
|
switch (error) {
|
||||||
|
case ESP_OK:
|
||||||
|
return ERROR_NONE;
|
||||||
|
case ESP_ERR_INVALID_ARG:
|
||||||
|
return ERROR_INVALID_ARGUMENT;
|
||||||
|
case ESP_ERR_INVALID_STATE:
|
||||||
|
return ERROR_INVALID_STATE;
|
||||||
|
case ESP_ERR_TIMEOUT:
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
|
default:
|
||||||
|
return ERROR_UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
#include <Tactility/Driver.h>
|
#include <Tactility/Driver.h>
|
||||||
#include <Tactility/drivers/Esp32Gpio.h>
|
#include <Tactility/drivers/Esp32Gpio.h>
|
||||||
#include <Tactility/drivers/GpioController.h>
|
|
||||||
#include <Tactility/drivers/Gpio.h>
|
#include <Tactility/ErrorEsp32.h>
|
||||||
#include <Tactility/Log.h>
|
#include <Tactility/Log.h>
|
||||||
|
#include <Tactility/drivers/Gpio.h>
|
||||||
|
#include <Tactility/drivers/GpioController.h>
|
||||||
|
|
||||||
#define TAG LOG_TAG(esp32_gpio)
|
#define TAG LOG_TAG(esp32_gpio)
|
||||||
|
|
||||||
@ -13,20 +15,21 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static bool set_level(Device* device, gpio_pin_t pin, bool high) {
|
static error_t set_level(Device* device, gpio_pin_t pin, bool high) {
|
||||||
return gpio_set_level(static_cast<gpio_num_t>(pin), high) == ESP_OK;
|
auto esp_error = gpio_set_level(static_cast<gpio_num_t>(pin), high);
|
||||||
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_level(Device* device, gpio_pin_t pin, bool* high) {
|
static error_t get_level(Device* device, gpio_pin_t pin, bool* high) {
|
||||||
*high = gpio_get_level(static_cast<gpio_num_t>(pin)) != 0;
|
*high = gpio_get_level(static_cast<gpio_num_t>(pin)) != 0;
|
||||||
return true;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
static error_t set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
||||||
const Esp32GpioConfig* config = GET_CONFIG(device);
|
const Esp32GpioConfig* config = GET_CONFIG(device);
|
||||||
|
|
||||||
if (pin >= config->gpio_count) {
|
if (pin >= config->gpioCount) {
|
||||||
return false;
|
return ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_mode_t mode;
|
gpio_mode_t mode;
|
||||||
@ -37,8 +40,7 @@ static bool set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
|||||||
} else if (options & GPIO_DIRECTION_OUTPUT) {
|
} else if (options & GPIO_DIRECTION_OUTPUT) {
|
||||||
mode = GPIO_MODE_OUTPUT;
|
mode = GPIO_MODE_OUTPUT;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "set_options: no direction flag specified for pin %d", pin);
|
return ERROR_INVALID_ARGUMENT;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const gpio_config_t esp_config = {
|
const gpio_config_t esp_config = {
|
||||||
@ -52,13 +54,14 @@ static bool set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
return gpio_config(&esp_config) == ESP_OK;
|
auto esp_error = gpio_config(&esp_config);
|
||||||
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
static int get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
||||||
gpio_io_config_t esp_config;
|
gpio_io_config_t esp_config;
|
||||||
if (gpio_get_io_config((gpio_num_t)pin, &esp_config) != ESP_OK) {
|
if (gpio_get_io_config(static_cast<gpio_num_t>(pin), &esp_config) != ESP_OK) {
|
||||||
return false;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_flags_t output = 0;
|
gpio_flags_t output = 0;
|
||||||
@ -84,17 +87,17 @@ static bool get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*options = output;
|
*options = output;
|
||||||
return true;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int start(Device* device) {
|
static error_t start(Device* device) {
|
||||||
ESP_LOGI(TAG, "start %s", device->name);
|
ESP_LOGI(TAG, "start %s", device->name);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stop(Device* device) {
|
static error_t stop(Device* device) {
|
||||||
ESP_LOGI(TAG, "stop %s", device->name);
|
ESP_LOGI(TAG, "stop %s", device->name);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const static GpioControllerApi esp32_gpio_api = {
|
const static GpioControllerApi esp32_gpio_api = {
|
||||||
@ -107,10 +110,10 @@ const static GpioControllerApi esp32_gpio_api = {
|
|||||||
Driver esp32_gpio_driver = {
|
Driver esp32_gpio_driver = {
|
||||||
.name = "esp32_gpio",
|
.name = "esp32_gpio",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-gpio", nullptr },
|
.compatible = (const char*[]) { "espressif,esp32-gpio", nullptr },
|
||||||
.start_device = start,
|
.startDevice = start,
|
||||||
.stop_device = stop,
|
.stopDevice = stop,
|
||||||
.api = (void*)&esp32_gpio_api,
|
.api = (void*)&esp32_gpio_api,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3,8 +3,11 @@
|
|||||||
|
|
||||||
#include <Tactility/Driver.h>
|
#include <Tactility/Driver.h>
|
||||||
#include <Tactility/drivers/Esp32I2c.h>
|
#include <Tactility/drivers/Esp32I2c.h>
|
||||||
#include <Tactility/drivers/I2cController.h>
|
|
||||||
|
#include "Tactility/ErrorEsp32.h"
|
||||||
|
|
||||||
#include <Tactility/Log.h>
|
#include <Tactility/Log.h>
|
||||||
|
#include <Tactility/drivers/I2cController.h>
|
||||||
|
|
||||||
#define TAG LOG_TAG(esp32_i2c)
|
#define TAG LOG_TAG(esp32_i2c)
|
||||||
|
|
||||||
@ -28,41 +31,41 @@ struct InternalData {
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static bool read(Device* device, uint8_t address, uint8_t* data, size_t data_size, TickType_t timeout) {
|
static int read(Device* device, uint8_t address, uint8_t* data, size_t data_size, TickType_t timeout) {
|
||||||
vPortAssertIfInISR();
|
vPortAssertIfInISR();
|
||||||
auto* driver_data = GET_DATA(device);
|
auto* driver_data = GET_DATA(device);
|
||||||
lock(driver_data);
|
lock(driver_data);
|
||||||
const esp_err_t result = i2c_master_read_from_device(GET_CONFIG(device)->port, address, data, data_size, timeout);
|
const esp_err_t esp_error = i2c_master_read_from_device(GET_CONFIG(device)->port, address, data, data_size, timeout);
|
||||||
unlock(driver_data);
|
unlock(driver_data);
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_error);
|
||||||
return result == ESP_OK;
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
static int write(Device* device, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||||
vPortAssertIfInISR();
|
vPortAssertIfInISR();
|
||||||
auto* driver_data = GET_DATA(device);
|
auto* driver_data = GET_DATA(device);
|
||||||
lock(driver_data);
|
lock(driver_data);
|
||||||
const esp_err_t result = i2c_master_write_to_device(GET_CONFIG(device)->port, address, data, dataSize, timeout);
|
const esp_err_t esp_error = i2c_master_write_to_device(GET_CONFIG(device)->port, address, data, dataSize, timeout);
|
||||||
unlock(driver_data);
|
unlock(driver_data);
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_error);
|
||||||
return result == ESP_OK;
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
static int 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();
|
vPortAssertIfInISR();
|
||||||
auto* driver_data = GET_DATA(device);
|
auto* driver_data = GET_DATA(device);
|
||||||
lock(driver_data);
|
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);
|
const esp_err_t esp_error = i2c_master_write_read_device(GET_CONFIG(device)->port, address, write_data, write_data_size, read_data, read_data_size, timeout);
|
||||||
unlock(driver_data);
|
unlock(driver_data);
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(result);
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_error);
|
||||||
return result == ESP_OK;
|
return esp_err_to_error(esp_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int start(Device* device) {
|
static int start(Device* device) {
|
||||||
ESP_LOGI(TAG, "start %s", device->name);
|
ESP_LOGI(TAG, "start %s", device->name);
|
||||||
auto* data = new InternalData();
|
auto* data = new InternalData();
|
||||||
device_set_driver_data(device, data);
|
device_set_driver_data(device, data);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stop(Device* device) {
|
static int stop(Device* device) {
|
||||||
@ -70,10 +73,10 @@ static int stop(Device* device) {
|
|||||||
auto* driver_data = static_cast<InternalData*>(device_get_driver_data(device));
|
auto* driver_data = static_cast<InternalData*>(device_get_driver_data(device));
|
||||||
device_set_driver_data(device, nullptr);
|
device_set_driver_data(device, nullptr);
|
||||||
delete driver_data;
|
delete driver_data;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const I2cControllerApi esp32_i2c_api = {
|
const static I2cControllerApi esp32_i2c_api = {
|
||||||
.read = read,
|
.read = read,
|
||||||
.write = write,
|
.write = write,
|
||||||
.write_read = write_read
|
.write_read = write_read
|
||||||
@ -82,10 +85,10 @@ const I2cControllerApi esp32_i2c_api = {
|
|||||||
Driver esp32_i2c_driver = {
|
Driver esp32_i2c_driver = {
|
||||||
.name = "esp32_i2c",
|
.name = "esp32_i2c",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-i2c", nullptr },
|
.compatible = (const char*[]) { "espressif,esp32-i2c", nullptr },
|
||||||
.start_device = start,
|
.startDevice = start,
|
||||||
.stop_device = stop,
|
.stopDevice = stop,
|
||||||
.api = (void*)&esp32_i2c_api,
|
.api = (void*)&esp32_i2c_api,
|
||||||
.device_type = &I2C_CONTROLLER_TYPE,
|
.deviceType = &I2C_CONTROLLER_TYPE,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ int32_t GuiService::guiMain() {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
if (service->threadFlags.wait(GUI_THREAD_FLAG_ALL, false, true, portMAX_DELAY, &flags)) {
|
if (service->threadFlags.wait(GUI_THREAD_FLAG_ALL, false, true, &flags, portMAX_DELAY)) {
|
||||||
// When service not started or starting -> exit
|
// When service not started or starting -> exit
|
||||||
State service_state = getState(manifest.id);
|
State service_state = getState(manifest.id);
|
||||||
if (service_state != State::Started && service_state != State::Starting) {
|
if (service_state != State::Started && service_state != State::Starting) {
|
||||||
|
|||||||
@ -799,11 +799,11 @@ static void dispatchConnect(std::shared_ptr<Wifi> wifi) {
|
|||||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT)
|
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT)
|
||||||
* or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
|
* or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
|
||||||
* The bits are set by wifi_event_handler() */
|
* The bits are set by wifi_event_handler() */
|
||||||
uint32_t bits;
|
uint32_t flags;
|
||||||
if (wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT, false, true, kernel::MAX_TICKS, &bits)) {
|
if (wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT, false, true, &flags, kernel::MAX_TICKS)) {
|
||||||
LOGGER.info("Waiting for EventGroup by event_handler()");
|
LOGGER.info("Waiting for EventGroup by event_handler()");
|
||||||
|
|
||||||
if (bits & WIFI_CONNECTED_BIT) {
|
if (flags & WIFI_CONNECTED_BIT) {
|
||||||
wifi->setSecureConnection(config.sta.password[0] != 0x00U);
|
wifi->setSecureConnection(config.sta.password[0] != 0x00U);
|
||||||
wifi->setRadioState(RadioState::ConnectionActive);
|
wifi->setRadioState(RadioState::ConnectionActive);
|
||||||
publish_event(wifi, WifiEvent::ConnectionSuccess);
|
publish_event(wifi, WifiEvent::ConnectionSuccess);
|
||||||
@ -815,7 +815,7 @@ static void dispatchConnect(std::shared_ptr<Wifi> wifi) {
|
|||||||
LOGGER.info("Stored credentials");
|
LOGGER.info("Stored credentials");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (bits & WIFI_FAIL_BIT) {
|
} else if (flags & WIFI_FAIL_BIT) {
|
||||||
wifi->setRadioState(RadioState::On);
|
wifi->setRadioState(RadioState::On);
|
||||||
publish_event(wifi, WifiEvent::ConnectionFailed);
|
publish_event(wifi, WifiEvent::ConnectionFailed);
|
||||||
LOGGER.info("Failed to connect to {}", wifi->connection_target.ssid.c_str());
|
LOGGER.info("Failed to connect to {}", wifi->connection_target.ssid.c_str());
|
||||||
|
|||||||
@ -67,6 +67,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shutdown) {
|
if (shutdown) {
|
||||||
|
mutex.unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,14 +87,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consume 1 or more dispatched function (if any) until the queue is empty.
|
* Consume 1 or more dispatched functions (if any) until the queue is empty.
|
||||||
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
|
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
|
||||||
* @param[in] timeout the ticks to wait for a message
|
* @param[in] timeout the ticks to wait for a message
|
||||||
* @return the amount of messages that were consumed
|
* @return the amount of messages that were consumed
|
||||||
*/
|
*/
|
||||||
uint32_t consume(TickType_t timeout = kernel::MAX_TICKS) {
|
uint32_t consume(TickType_t timeout = kernel::MAX_TICKS) {
|
||||||
// Wait for signal
|
// Wait for signal
|
||||||
if (!eventFlag.wait(WAIT_FLAG, false, true, timeout)) {
|
if (!eventFlag.wait(WAIT_FLAG, false, true, nullptr, timeout)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,42 +33,27 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class Error {
|
enum class Error {
|
||||||
Unknown,
|
|
||||||
Timeout,
|
Timeout,
|
||||||
Resource,
|
Resource
|
||||||
Parameter,
|
|
||||||
IsrStatus
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the flags.
|
* Set the flags.
|
||||||
* @param[in] flags the flags to set
|
* @param[in] flags the flags to set
|
||||||
* @param[out] outFlags optional resulting flags: this is set when the return value is true
|
|
||||||
* @param[out] outError optional error output: this is set when the return value is false
|
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
bool set(uint32_t flags, uint32_t* outFlags = nullptr, Error* outError = nullptr) const {
|
bool set(uint32_t flags) const {
|
||||||
assert(handle);
|
assert(handle);
|
||||||
if (xPortInIsrContext() == pdTRUE) {
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
uint32_t result;
|
|
||||||
BaseType_t yield = pdFALSE;
|
BaseType_t yield = pdFALSE;
|
||||||
if (xEventGroupSetBitsFromISR(handle.get(), flags, &yield) == pdFAIL) {
|
if (xEventGroupSetBitsFromISR(handle.get(), flags, &yield) == pdFAIL) {
|
||||||
if (outError != nullptr) {
|
|
||||||
*outError = Error::Resource;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (outFlags != nullptr) {
|
|
||||||
*outFlags = flags;
|
|
||||||
}
|
|
||||||
portYIELD_FROM_ISR(yield);
|
portYIELD_FROM_ISR(yield);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto result = xEventGroupSetBits(handle.get(), flags);
|
xEventGroupSetBits(handle.get(), flags);
|
||||||
if (outFlags != nullptr) {
|
|
||||||
*outFlags = result;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,32 +61,20 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Clear flags
|
* Clear flags
|
||||||
* @param[in] flags the flags to clear
|
* @param[in] flags the flags to clear
|
||||||
* @param[out] outFlags optional resulting flags: this is set when the return value is true
|
|
||||||
* @param[out] outError optional error output: this is set when the return value is false
|
|
||||||
* @return true on success
|
* @return true on success
|
||||||
*/
|
*/
|
||||||
bool clear(uint32_t flags, uint32_t* outFlags = nullptr, Error* outError = nullptr) const {
|
bool clear(uint32_t flags) const {
|
||||||
if (xPortInIsrContext() == pdTRUE) {
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
uint32_t result = xEventGroupGetBitsFromISR(handle.get());
|
uint32_t result = xEventGroupGetBitsFromISR(handle.get());
|
||||||
if (xEventGroupClearBitsFromISR(handle.get(), flags) == pdFAIL) {
|
if (xEventGroupClearBitsFromISR(handle.get(), flags) == pdFAIL) {
|
||||||
if (outError != nullptr) {
|
|
||||||
*outError = Error::Resource;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (outFlags != nullptr) {
|
|
||||||
*outFlags = result;
|
|
||||||
}
|
|
||||||
portYIELD_FROM_ISR(pdTRUE);
|
portYIELD_FROM_ISR(pdTRUE);
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
auto result = xEventGroupClearBits(handle.get(), flags);
|
xEventGroupClearBits(handle.get(), flags);
|
||||||
if (outFlags != nullptr) {
|
|
||||||
*outFlags = result;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the current flags
|
* @return the current flags
|
||||||
@ -120,16 +93,14 @@ public:
|
|||||||
* @param[in] awaitAll If true, await for all bits to be set. Otherwise, await for any.
|
* @param[in] awaitAll If true, await for all bits to be set. Otherwise, await for any.
|
||||||
* @param[in] clearOnExit If true, clears all the bits on exit, otherwise don't clear.
|
* @param[in] clearOnExit If true, clears all the bits on exit, otherwise don't clear.
|
||||||
* @param[in] timeout the maximum amount of ticks to wait for flags to be set
|
* @param[in] timeout the maximum amount of ticks to wait for flags to be set
|
||||||
* @param[out] outFlags optional resulting flags: this is set when the return value is true
|
* @param[out] outFlags optional resulting flags: this is set when the return value is true. Only set when return value is true.
|
||||||
* @param[out] outError optional error output: this is set when the return value is false
|
|
||||||
*/
|
*/
|
||||||
bool wait(
|
bool wait(
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
bool awaitAll = false,
|
bool awaitAll = false,
|
||||||
bool clearOnExit = true,
|
bool clearOnExit = true,
|
||||||
TickType_t timeout = kernel::MAX_TICKS,
|
|
||||||
uint32_t* outFlags = nullptr,
|
uint32_t* outFlags = nullptr,
|
||||||
Error* outError = nullptr
|
TickType_t timeout = kernel::MAX_TICKS
|
||||||
) const {
|
) const {
|
||||||
assert(xPortInIsrContext() == pdFALSE);
|
assert(xPortInIsrContext() == pdFALSE);
|
||||||
|
|
||||||
@ -144,14 +115,8 @@ public:
|
|||||||
auto invalid_flags = awaitAll
|
auto invalid_flags = awaitAll
|
||||||
? ((flags & result_flags) != flags) // await all
|
? ((flags & result_flags) != flags) // await all
|
||||||
: ((flags & result_flags) == 0U); // await any
|
: ((flags & result_flags) == 0U); // await any
|
||||||
|
|
||||||
if (invalid_flags) {
|
if (invalid_flags) {
|
||||||
if (outError != nullptr) {
|
|
||||||
if (timeout > 0U) { // assume time-out
|
|
||||||
*outError = Error::Timeout;
|
|
||||||
} else {
|
|
||||||
*outError = Error::Resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,13 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <Tactility/concurrent/Mutex.h>
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "Error.h"
|
||||||
|
#include <Tactility/concurrent/Mutex.h>
|
||||||
|
|
||||||
struct Driver;
|
struct Driver;
|
||||||
|
|
||||||
/** Enables discovering devices of the same type */
|
/** Enables discovering devices of the same type */
|
||||||
@ -52,24 +53,26 @@ struct Device {
|
|||||||
/**
|
/**
|
||||||
* Initialize the properties of a device.
|
* Initialize the properties of a device.
|
||||||
*
|
*
|
||||||
* @param[in] dev a device with all non-internal properties set
|
* @param[in] device a device with all non-internal properties set
|
||||||
* @return the result code (0 for success)
|
* @retval ERROR_OUT_OF_MEMORY
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_construct(struct Device* device);
|
error_t device_construct(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deinitialize the properties of a device.
|
* Deinitialize the properties of a device.
|
||||||
* This fails when a device is busy or has children.
|
* This fails when a device is busy or has children.
|
||||||
*
|
*
|
||||||
* @param[in] dev
|
* @param[in] device
|
||||||
* @return the result code (0 for success)
|
* @retval ERROR_INVALID_STATE
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_destruct(struct Device* device);
|
error_t device_destruct(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the device is in a state where its API is available
|
* Indicates whether the device is in a state where its API is available
|
||||||
*
|
*
|
||||||
* @param[in] dev non-null device pointer
|
* @param[in] device non-null device pointer
|
||||||
* @return true if the device is ready for use
|
* @return true if the device is ready for use
|
||||||
*/
|
*/
|
||||||
static inline bool device_is_ready(const struct Device* device) {
|
static inline bool device_is_ready(const struct Device* device) {
|
||||||
@ -83,9 +86,10 @@ static inline bool device_is_ready(const struct Device* device) {
|
|||||||
* - a bus (if any)
|
* - a bus (if any)
|
||||||
*
|
*
|
||||||
* @param[in] device non-null device pointer
|
* @param[in] device non-null device pointer
|
||||||
* @return 0 on success
|
* @retval ERROR_INVALID_STATE
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_add(struct Device* device);
|
error_t device_add(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deregister a device. Remove it from all relevant systems:
|
* Deregister a device. Remove it from all relevant systems:
|
||||||
@ -94,26 +98,32 @@ int device_add(struct Device* device);
|
|||||||
* - a bus (if any)
|
* - a bus (if any)
|
||||||
*
|
*
|
||||||
* @param[in] device non-null device pointer
|
* @param[in] device non-null device pointer
|
||||||
* @return 0 on success
|
* @retval ERROR_INVALID_STATE
|
||||||
|
* @retval ERROR_NOT_FOUND
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_remove(struct Device* device);
|
error_t device_remove(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach the driver.
|
* Attach the driver.
|
||||||
*
|
*
|
||||||
* @warning must call device_construct() and device_add() first
|
* @warning must call device_construct() and device_add() first
|
||||||
* @param device
|
* @param device
|
||||||
* @return ERROR_INVALID_STATE or otherwise the value of the driver binding result (0 on success)
|
* @retval ERROR_INVALID_STATE
|
||||||
|
* @retval ERROR_RESOURCE when driver binding fails
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_start(struct Device* device);
|
error_t device_start(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detach the driver.
|
* Detach the driver.
|
||||||
*
|
*
|
||||||
* @param device
|
* @param device
|
||||||
* @return ERROR_INVALID_STATE or otherwise the value of the driver unbinding result (0 on success)
|
* @retval ERROR_INVALID_STATE
|
||||||
|
* @retval ERROR_RESOURCE when driver unbinding fails
|
||||||
|
* @retval ERROR_NONE
|
||||||
*/
|
*/
|
||||||
int device_stop(struct Device* device);
|
error_t device_stop(struct Device* device);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or unset a parent.
|
* Set or unset a parent.
|
||||||
@ -147,7 +157,7 @@ static inline void device_lock(struct Device* device) {
|
|||||||
mutex_lock(&device->internal.mutex);
|
mutex_lock(&device->internal.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int device_try_lock(struct Device* device) {
|
static inline bool device_try_lock(struct Device* device) {
|
||||||
return mutex_try_lock(&device->internal.mutex);
|
return mutex_try_lock(&device->internal.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +166,7 @@ static inline void device_unlock(struct Device* device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline const struct DeviceType* device_get_type(struct Device* device) {
|
static inline const struct DeviceType* device_get_type(struct Device* device) {
|
||||||
return device->internal.driver ? device->internal.driver->device_type : NULL;
|
return device->internal.driver ? device->internal.driver->deviceType : NULL;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Iterate through all the known devices
|
* Iterate through all the known devices
|
||||||
@ -167,18 +177,18 @@ void for_each_device(void* callback_context, bool(*on_device)(struct Device* dev
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through all the child devices of the specified device
|
* Iterate through all the child devices of the specified device
|
||||||
* @param callback_context the parameter to pass to the callback. NULL is valid.
|
* @param callbackContext 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.
|
* @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));
|
void for_each_device_child(struct Device* device, void* callbackContext, bool(*on_device)(struct Device* device, void* context));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through all the known devices of a specific type
|
* Iterate through all the known devices of a specific type
|
||||||
* @param type the type to filter
|
* @param type the type to filter
|
||||||
* @param callback_context the parameter to pass to the callback. NULL is valid.
|
* @param callbackContext 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.
|
* @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));
|
void for_each_device_of_type(const struct DeviceType* type, void* callbackContext, bool(*on_device)(struct Device* device, void* context));
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "Error.h"
|
||||||
|
|
||||||
struct Device;
|
struct Device;
|
||||||
struct DeviceType;
|
struct DeviceType;
|
||||||
@ -17,13 +18,13 @@ struct Driver {
|
|||||||
/** Array of const char*, terminated by NULL */
|
/** Array of const char*, terminated by NULL */
|
||||||
const char**compatible;
|
const char**compatible;
|
||||||
/** Function to initialize the driver for a device */
|
/** Function to initialize the driver for a device */
|
||||||
int (*start_device)(struct Device* dev);
|
error_t (*startDevice)(struct Device* dev);
|
||||||
/** Function to deinitialize the driver for a device */
|
/** Function to deinitialize the driver for a device */
|
||||||
int (*stop_device)(struct Device* dev);
|
error_t (*stopDevice)(struct Device* dev);
|
||||||
/** Contains the driver's functions */
|
/** Contains the driver's functions */
|
||||||
const void* api;
|
const void* api;
|
||||||
/** Which type of devices this driver creates (can be NULL) */
|
/** Which type of devices this driver creates (can be NULL) */
|
||||||
const struct DeviceType* device_type;
|
const struct DeviceType* deviceType;
|
||||||
/** Internal data */
|
/** Internal data */
|
||||||
struct {
|
struct {
|
||||||
/** Contains private data */
|
/** Contains private data */
|
||||||
@ -31,20 +32,20 @@ struct Driver {
|
|||||||
} internal;
|
} internal;
|
||||||
};
|
};
|
||||||
|
|
||||||
int driver_construct(struct Driver* driver);
|
error_t driver_construct(struct Driver* driver);
|
||||||
|
|
||||||
int driver_destruct(struct Driver* driver);
|
error_t driver_destruct(struct Driver* driver);
|
||||||
|
|
||||||
int driver_bind(struct Driver* driver, struct Device* device);
|
error_t driver_bind(struct Driver* driver, struct Device* device);
|
||||||
|
|
||||||
int driver_unbind(struct Driver* driver, struct Device* device);
|
error_t driver_unbind(struct Driver* driver, struct Device* device);
|
||||||
|
|
||||||
bool driver_is_compatible(struct Driver* driver, const char* compatible);
|
bool driver_is_compatible(struct Driver* driver, const char* compatible);
|
||||||
|
|
||||||
struct Driver* driver_find_compatible(const char* compatible);
|
struct Driver* driver_find_compatible(const char* compatible);
|
||||||
|
|
||||||
static inline const struct DeviceType* driver_get_device_type(struct Driver* driver) {
|
static inline const struct DeviceType* driver_get_device_type(struct Driver* driver) {
|
||||||
return driver->device_type;
|
return driver->deviceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -2,9 +2,18 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// Avoid potential clash with bits/types/error_t.h
|
||||||
|
#ifndef __error_t_defined
|
||||||
|
typedef int error_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ERROR_NONE 0
|
||||||
#define ERROR_UNDEFINED 1
|
#define ERROR_UNDEFINED 1
|
||||||
#define ERROR_INVALID_STATE 2
|
#define ERROR_INVALID_STATE 2
|
||||||
#define ERROR_INVALID_ARGUMENT 3
|
#define ERROR_INVALID_ARGUMENT 3
|
||||||
#define ERROR_MISSING_PARAMETER 4
|
#define ERROR_MISSING_PARAMETER 4
|
||||||
#define ERROR_NOT_FOUND 5
|
#define ERROR_NOT_FOUND 5
|
||||||
|
#define ERROR_ISR_STATUS 6
|
||||||
|
#define ERROR_RESOURCE 7 // A problem with a resource/dependency
|
||||||
|
#define ERROR_TIMEOUT 8
|
||||||
|
#define ERROR_OUT_OF_MEMORY 9
|
||||||
|
|||||||
78
TactilityKernel/Include/Tactility/concurrent/Dispatcher.h
Normal file
78
TactilityKernel/Include/Tactility/concurrent/Dispatcher.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatcher is a thread-safe code execution queue.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
#include <Tactility/FreeRTOS/FreeRTOS.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*DispatcherCallback)(void* context);
|
||||||
|
typedef void* DispatcherHandle_t;
|
||||||
|
|
||||||
|
DispatcherHandle_t dispatcher_alloc(void);
|
||||||
|
|
||||||
|
void dispatcher_free(DispatcherHandle_t dispatcher);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue a function to be consumed elsewhere.
|
||||||
|
*
|
||||||
|
* @param[in] callbackContext the data to pass to the function upon execution
|
||||||
|
* @param[in] callback the function to execute elsewhere
|
||||||
|
* @param[in] timeout lock acquisition timeout
|
||||||
|
* @retval ERROR_TIMEOUT
|
||||||
|
* @retval ERROR_RESOURCE when failing to set event
|
||||||
|
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
error_t dispatcher_dispatch_timed(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback, TickType_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue a function to be consumed elsewhere.
|
||||||
|
*
|
||||||
|
* @param[in] callbackContext the data to pass to the function upon execution
|
||||||
|
* @param[in] callback the function to execute elsewhere
|
||||||
|
* @retval ERROR_RESOURCE when failing to set event
|
||||||
|
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
|
||||||
|
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
static inline error_t dispatcher_dispatch(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback) {
|
||||||
|
return dispatcher_dispatch_timed(dispatcher, callbackContext, callback, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume 1 or more dispatched functions (if any) until the queue is empty.
|
||||||
|
*
|
||||||
|
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
|
||||||
|
*
|
||||||
|
* @param[in] timeout the ticks to wait for a message
|
||||||
|
* @retval ERROR_TIMEOUT
|
||||||
|
* @retval ERROR_RESOURCE failed to wait for event
|
||||||
|
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
error_t dispatcher_consume_timed(DispatcherHandle_t dispatcher, TickType_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume 1 or more dispatched functions (if any) until the queue is empty.
|
||||||
|
*
|
||||||
|
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
|
||||||
|
*
|
||||||
|
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
|
||||||
|
* @retval ERROR_RESOURCE failed to wait for event
|
||||||
|
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
static inline error_t dispatcher_consume(DispatcherHandle_t dispatcher) {
|
||||||
|
return dispatcher_consume_timed(dispatcher, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
78
TactilityKernel/Include/Tactility/concurrent/EventGroup.h
Normal file
78
TactilityKernel/Include/Tactility/concurrent/EventGroup.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
#include <Tactility/FreeRTOS/event_groups.h>
|
||||||
|
|
||||||
|
static inline void event_group_construct(EventGroupHandle_t* eventGroup) {
|
||||||
|
assert(xPortInIsrContext() == pdFALSE);
|
||||||
|
*eventGroup = xEventGroupCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void event_group_destruct(EventGroupHandle_t* eventGroup) {
|
||||||
|
assert(xPortInIsrContext() == pdFALSE);
|
||||||
|
assert(*eventGroup != NULL);
|
||||||
|
vEventGroupDelete(*eventGroup);
|
||||||
|
*eventGroup = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the flags.
|
||||||
|
*
|
||||||
|
* @param[in] eventGroup the event group
|
||||||
|
* @param[in] flags the flags to set
|
||||||
|
* @retval ERROR_RESOURCE when setting failed
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
error_t event_group_set(EventGroupHandle_t eventGroup, uint32_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear flags
|
||||||
|
*
|
||||||
|
* @param[in] eventGroup the event group
|
||||||
|
* @param[in] flags the flags to clear
|
||||||
|
* @retval ERROR_RESOURCE when clearing failed
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
error_t event_group_clear(EventGroupHandle_t eventGroup, uint32_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param[in] eventGroup the event group
|
||||||
|
* @return the bitset (always succeeds)
|
||||||
|
*/
|
||||||
|
uint32_t event_group_get(EventGroupHandle_t eventGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for flags to be set
|
||||||
|
*
|
||||||
|
* @param[in] eventGroup the event group
|
||||||
|
* @param[in] inFlags the flags to await
|
||||||
|
* @param[in] awaitAll If true, await for all bits to be set. Otherwise, await for any.
|
||||||
|
* @param[in] clearOnExit If true, clears all the bits on exit, otherwise don't clear.
|
||||||
|
* @param[out] outFlags If set to non-NULL value, this will hold the resulting flags. Only set when return value is ERROR_NONE
|
||||||
|
* @param[in] timeout the maximum amount of ticks to wait for flags to be set
|
||||||
|
* @retval ERROR_ISR_STATUS when the function was called from an ISR context
|
||||||
|
* @retval ERROR_TIMEOUT
|
||||||
|
* @retval ERROR_RESOURCE when flags were triggered, but not in a way that was expected (e.g. waiting for all flags, but was only partially set)
|
||||||
|
* @retval ERROR_NONE
|
||||||
|
*/
|
||||||
|
error_t event_group_wait(
|
||||||
|
EventGroupHandle_t eventGroup,
|
||||||
|
uint32_t inFlags,
|
||||||
|
bool awaitAll,
|
||||||
|
bool clearOnExit,
|
||||||
|
uint32_t* outFlags,
|
||||||
|
TickType_t timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -12,6 +12,7 @@ extern "C" {
|
|||||||
|
|
||||||
struct Mutex {
|
struct Mutex {
|
||||||
QueueHandle_t handle;
|
QueueHandle_t handle;
|
||||||
|
// TODO: Debugging functionality
|
||||||
};
|
};
|
||||||
|
|
||||||
inline static void mutex_construct(struct Mutex* mutex) {
|
inline static void mutex_construct(struct Mutex* mutex) {
|
||||||
@ -27,18 +28,30 @@ inline static void mutex_destruct(struct Mutex* mutex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline static void mutex_lock(struct Mutex* mutex) {
|
inline static void mutex_lock(struct Mutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
xSemaphoreTake(mutex->handle, portMAX_DELAY);
|
xSemaphoreTake(mutex->handle, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static bool mutex_try_lock(struct Mutex* mutex) {
|
inline static bool mutex_try_lock(struct Mutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
|
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static bool mutex_try_lock_timed(struct Mutex* mutex, TickType_t timeout) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
|
return xSemaphoreTake(mutex->handle, timeout) == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
inline static bool mutex_is_locked(struct Mutex* mutex) {
|
inline static bool mutex_is_locked(struct Mutex* mutex) {
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
|
||||||
|
} else {
|
||||||
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void mutex_unlock(struct Mutex* mutex) {
|
inline static void mutex_unlock(struct Mutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
xSemaphoreGive(mutex->handle);
|
xSemaphoreGive(mutex->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,18 +26,30 @@ inline static void recursive_mutex_destruct(struct RecursiveMutex* mutex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
|
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
xSemaphoreTakeRecursive(mutex->handle, portMAX_DELAY);
|
xSemaphoreTakeRecursive(mutex->handle, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
|
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
|
||||||
|
} else {
|
||||||
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex) {
|
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
return xSemaphoreTakeRecursive(mutex->handle, 0) == pdTRUE;
|
return xSemaphoreTakeRecursive(mutex->handle, 0) == pdTRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static bool recursive_mutex_try_lock_timed(struct RecursiveMutex* mutex, TickType_t timeout) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
|
return xSemaphoreTakeRecursive(mutex->handle, timeout) == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
|
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
|
||||||
|
assert(xPortInIsrContext() != pdTRUE);
|
||||||
xSemaphoreGiveRecursive(mutex->handle);
|
xSemaphoreGiveRecursive(mutex->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <Tactility/Device.h>
|
#include <Tactility/Device.h>
|
||||||
|
|
||||||
#define GPIO_OPTIONS_MASK 0x1f
|
#define GPIO_OPTIONS_MASK 0x1f
|
||||||
@ -34,57 +36,30 @@ typedef enum {
|
|||||||
GPIO__MAX,
|
GPIO__MAX,
|
||||||
} GpioInterruptType;
|
} GpioInterruptType;
|
||||||
|
|
||||||
/**
|
/** The index of a GPIO pin on a GPIO Controller */
|
||||||
* @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;
|
typedef uint8_t gpio_pin_t;
|
||||||
|
|
||||||
/**
|
/** Specifies the configuration flags for a GPIO pin (or set of pins) */
|
||||||
* @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;
|
typedef uint16_t gpio_flags_t;
|
||||||
|
|
||||||
/**
|
/** A configuration for a single GPIO pin */
|
||||||
* @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 {
|
struct GpioPinConfig {
|
||||||
/** GPIO device controlling the pin */
|
/** GPIO device controlling the pin */
|
||||||
const struct Device* port;
|
const struct Device* port;
|
||||||
/** The pin's number on the device */
|
/** The pin's number on the device */
|
||||||
gpio_pin_t pin;
|
gpio_pin_t pin;
|
||||||
/** The pin's configuration flags as specified in devicetree */
|
/** The pin's configuration flags as specified in devicetree */
|
||||||
gpio_flags_t dt_flags;
|
gpio_flags_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the pin is ready to be used.
|
* Check if the pin is ready to be used.
|
||||||
* @param pin_config the specifications of the pin
|
*
|
||||||
|
* @param pinConfig the specifications of the pin
|
||||||
* @return true if the pin is ready to be used
|
* @return true if the pin is ready to be used
|
||||||
*/
|
*/
|
||||||
static inline bool gpio_is_ready(const struct GpioPinConfig* pin_config) {
|
static inline bool gpio_is_ready(const struct GpioPinConfig* pinConfig) {
|
||||||
return device_is_ready(pin_config->port);
|
return device_is_ready(pinConfig->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -7,22 +7,22 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Gpio.h"
|
#include "Gpio.h"
|
||||||
#include <stdbool.h>
|
#include <Tactility/Error.h>
|
||||||
|
|
||||||
struct GpioControllerApi {
|
struct GpioControllerApi {
|
||||||
bool (*set_level)(struct Device* device, gpio_pin_t pin, bool high);
|
error_t (*set_level)(struct Device* device, gpio_pin_t pin, bool high);
|
||||||
bool (*get_level)(struct Device* device, gpio_pin_t pin, bool* high);
|
error_t (*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);
|
error_t (*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);
|
error_t (*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);
|
error_t 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);
|
error_t 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);
|
error_t 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);
|
error_t 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) {
|
static inline error_t gpio_set_options_config(struct Device* device, const struct GpioPinConfig* config) {
|
||||||
return gpio_controller_set_options(device, config->pin, config->dt_flags);
|
return gpio_controller_set_options(device, config->pin, config->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -6,22 +6,24 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Gpio.h"
|
|
||||||
#include <Tactility/FreeRTOS/FreeRTOS.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "Gpio.h"
|
||||||
|
|
||||||
|
#include <Tactility/FreeRTOS/FreeRTOS.h>
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
|
||||||
struct I2cControllerApi {
|
struct I2cControllerApi {
|
||||||
bool (*read)(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
|
error_t (*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);
|
error_t (*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);
|
error_t (*write_read)(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool i2c_controller_read(struct Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout);
|
error_t 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);
|
error_t 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);
|
error_t i2c_controller_write_read(struct Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout);
|
||||||
|
|
||||||
extern const struct DeviceType I2C_CONTROLLER_TYPE;
|
extern const struct DeviceType I2C_CONTROLLER_TYPE;
|
||||||
|
|
||||||
|
|||||||
@ -44,17 +44,17 @@ extern "C" {
|
|||||||
|
|
||||||
#define get_device_data(device) static_cast<DeviceData*>(device->internal.data)
|
#define get_device_data(device) static_cast<DeviceData*>(device->internal.data)
|
||||||
|
|
||||||
int device_construct(Device* device) {
|
error_t device_construct(Device* device) {
|
||||||
device->internal.data = new(std::nothrow) DeviceData;
|
device->internal.data = new(std::nothrow) DeviceData;
|
||||||
if (device->internal.data == nullptr) {
|
if (device->internal.data == nullptr) {
|
||||||
return ENOMEM;
|
return ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
LOG_I(TAG, "construct %s", device->name);
|
LOG_I(TAG, "construct %s", device->name);
|
||||||
mutex_construct(&device->internal.mutex);
|
mutex_construct(&device->internal.mutex);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_destruct(Device* device) {
|
error_t device_destruct(Device* device) {
|
||||||
if (device->internal.state.started || device->internal.state.added) {
|
if (device->internal.state.started || device->internal.state.added) {
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ int device_destruct(Device* device) {
|
|||||||
mutex_destruct(&device->internal.mutex);
|
mutex_destruct(&device->internal.mutex);
|
||||||
delete get_device_data(device);
|
delete get_device_data(device);
|
||||||
device->internal.data = nullptr;
|
device->internal.data = nullptr;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add a child to the list of children */
|
/** Add a child to the list of children */
|
||||||
@ -87,7 +87,7 @@ static void device_remove_child(struct Device* device, struct Device* child) {
|
|||||||
device_unlock(device);
|
device_unlock(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_add(Device* device) {
|
error_t device_add(Device* device) {
|
||||||
LOG_I(TAG, "add %s", device->name);
|
LOG_I(TAG, "add %s", device->name);
|
||||||
|
|
||||||
// Already added
|
// Already added
|
||||||
@ -107,10 +107,10 @@ int device_add(Device* device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
device->internal.state.added = true;
|
device->internal.state.added = true;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_remove(Device* device) {
|
error_t device_remove(Device* device) {
|
||||||
LOG_I(TAG, "remove %s", device->name);
|
LOG_I(TAG, "remove %s", device->name);
|
||||||
|
|
||||||
if (device->internal.state.started || !device->internal.state.added) {
|
if (device->internal.state.started || !device->internal.state.added) {
|
||||||
@ -133,7 +133,7 @@ int device_remove(Device* device) {
|
|||||||
ledger_unlock();
|
ledger_unlock();
|
||||||
|
|
||||||
device->internal.state.added = false;
|
device->internal.state.added = false;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
|
|
||||||
failed_ledger_lookup:
|
failed_ledger_lookup:
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ failed_ledger_lookup:
|
|||||||
return ERROR_NOT_FOUND;
|
return ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_start(Device* device) {
|
error_t device_start(Device* device) {
|
||||||
if (!device->internal.state.added) {
|
if (!device->internal.state.added) {
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
@ -156,33 +156,31 @@ int device_start(Device* device) {
|
|||||||
|
|
||||||
// Already started
|
// Already started
|
||||||
if (device->internal.state.started) {
|
if (device->internal.state.started) {
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = driver_bind(device->internal.driver, device);
|
error_t bind_error = driver_bind(device->internal.driver, device);
|
||||||
device->internal.state.started = (result == 0);
|
device->internal.state.started = (bind_error == ERROR_NONE);
|
||||||
device->internal.state.start_result = result;
|
device->internal.state.start_result = bind_error;
|
||||||
return result;
|
return bind_error == ERROR_NONE ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_stop(struct Device* device) {
|
error_t device_stop(struct Device* device) {
|
||||||
if (!device->internal.state.added) {
|
if (!device->internal.state.added) {
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not started
|
|
||||||
if (!device->internal.state.started) {
|
if (!device->internal.state.started) {
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = driver_unbind(device->internal.driver, device);
|
if (driver_unbind(device->internal.driver, device) != ERROR_NONE) {
|
||||||
if (result != 0) {
|
return ERROR_RESOURCE;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device->internal.state.started = false;
|
device->internal.state.started = false;
|
||||||
device->internal.state.start_result = 0;
|
device->internal.state.start_result = 0;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_set_parent(Device* device, Device* parent) {
|
void device_set_parent(Device* device, Device* parent) {
|
||||||
@ -200,22 +198,22 @@ void for_each_device(void* callback_context, bool(*on_device)(Device* device, vo
|
|||||||
ledger_unlock();
|
ledger_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void for_each_device_child(Device* device, void* callback_context, bool(*on_device)(struct Device* device, void* context)) {
|
void for_each_device_child(Device* device, void* callbackContext, bool(*on_device)(struct Device* device, void* context)) {
|
||||||
auto* data = get_device_data(device);
|
auto* data = get_device_data(device);
|
||||||
for (auto* child_device : data->children) {
|
for (auto* child_device : data->children) {
|
||||||
if (!on_device(child_device, callback_context)) {
|
if (!on_device(child_device, callbackContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void for_each_device_of_type(const DeviceType* type, void* callback_context, bool(*on_device)(Device* device, void* context)) {
|
void for_each_device_of_type(const DeviceType* type, void* callbackContext, bool(*on_device)(Device* device, void* context)) {
|
||||||
ledger_lock();
|
ledger_lock();
|
||||||
for (auto* device : ledger.devices) {
|
for (auto* device : ledger.devices) {
|
||||||
auto* driver = device->internal.driver;
|
auto* driver = device->internal.driver;
|
||||||
if (driver != nullptr) {
|
if (driver != nullptr) {
|
||||||
if (driver->device_type == type) {
|
if (driver->deviceType == type) {
|
||||||
if (!on_device(device, callback_context)) {
|
if (!on_device(device, callbackContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
struct DriverInternalData {
|
struct DriverInternalData {
|
||||||
Mutex mutex { 0 };
|
Mutex mutex { 0 };
|
||||||
int use_count = 0;
|
int use_count = 0;
|
||||||
|
bool destroying = false;
|
||||||
|
|
||||||
DriverInternalData() {
|
DriverInternalData() {
|
||||||
mutex_construct(&mutex);
|
mutex_construct(&mutex);
|
||||||
@ -64,7 +65,7 @@ static void driver_add(Driver* driver) {
|
|||||||
ledger.unlock();
|
ledger.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool driver_remove(Driver* driver) {
|
static error_t driver_remove(Driver* driver) {
|
||||||
LOG_I(TAG, "remove %s", driver->name);
|
LOG_I(TAG, "remove %s", driver->name);
|
||||||
|
|
||||||
ledger.lock();
|
ledger.lock();
|
||||||
@ -72,35 +73,41 @@ static bool driver_remove(Driver* driver) {
|
|||||||
// check that there actually is a 3 in our vector
|
// check that there actually is a 3 in our vector
|
||||||
if (iterator == ledger.drivers.end()) {
|
if (iterator == ledger.drivers.end()) {
|
||||||
ledger.unlock();
|
ledger.unlock();
|
||||||
return false;
|
return ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
ledger.drivers.erase(iterator);
|
ledger.drivers.erase(iterator);
|
||||||
ledger.unlock();
|
ledger.unlock();
|
||||||
|
|
||||||
return true;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
int driver_construct(Driver* driver) {
|
error_t driver_construct(Driver* driver) {
|
||||||
driver->internal.data = new(std::nothrow) DriverInternalData;
|
driver->internal.data = new(std::nothrow) DriverInternalData;
|
||||||
if (driver->internal.data == nullptr) {
|
if (driver->internal.data == nullptr) {
|
||||||
return ENOMEM;
|
return ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
driver_add(driver);
|
driver_add(driver);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int driver_destruct(Driver* driver) {
|
error_t driver_destruct(Driver* driver) {
|
||||||
// Check if in use
|
driver_lock(driver);
|
||||||
if (driver_internal_data(driver)->use_count != 0) {
|
if (driver_internal_data(driver)->use_count != 0 || driver_internal_data(driver)->destroying) {
|
||||||
|
driver_unlock(driver);
|
||||||
return ERROR_INVALID_STATE;
|
return ERROR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
driver_internal_data(driver)->destroying = true;
|
||||||
|
driver_unlock(driver);
|
||||||
|
|
||||||
|
if (driver_remove(driver) != ERROR_NONE) {
|
||||||
|
LOG_W(TAG, "Failed to remove driver from ledger: %s", driver->name);
|
||||||
|
}
|
||||||
|
|
||||||
driver_remove(driver);
|
|
||||||
delete driver_internal_data(driver);
|
delete driver_internal_data(driver);
|
||||||
driver->internal.data = nullptr;
|
driver->internal.data = nullptr;
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool driver_is_compatible(Driver* driver, const char* compatible) {
|
bool driver_is_compatible(Driver* driver, const char* compatible) {
|
||||||
@ -130,18 +137,18 @@ Driver* driver_find_compatible(const char* compatible) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int driver_bind(Driver* driver, Device* device) {
|
error_t driver_bind(Driver* driver, Device* device) {
|
||||||
driver_lock(driver);
|
driver_lock(driver);
|
||||||
|
|
||||||
int err = 0;
|
error_t error = ERROR_NONE;
|
||||||
if (!device_is_added(device)) {
|
if (driver_internal_data(driver)->destroying || !device_is_added(device)) {
|
||||||
err = ERROR_INVALID_STATE;
|
error = ERROR_INVALID_STATE;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver->start_device != nullptr) {
|
if (driver->startDevice != nullptr) {
|
||||||
err = driver->start_device(device);
|
error = driver->startDevice(device);
|
||||||
if (err != 0) {
|
if (error != ERROR_NONE) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,26 +157,26 @@ int driver_bind(Driver* driver, Device* device) {
|
|||||||
driver_unlock(driver);
|
driver_unlock(driver);
|
||||||
|
|
||||||
LOG_I(TAG, "bound %s to %s", driver->name, device->name);
|
LOG_I(TAG, "bound %s to %s", driver->name, device->name);
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
||||||
driver_unlock(driver);
|
driver_unlock(driver);
|
||||||
return err;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int driver_unbind(Driver* driver, Device* device) {
|
error_t driver_unbind(Driver* driver, Device* device) {
|
||||||
driver_lock(driver);
|
driver_lock(driver);
|
||||||
|
|
||||||
int err = 0;
|
error_t error = ERROR_NONE;
|
||||||
if (!device_is_added(device)) {
|
if (driver_internal_data(driver)->destroying || !device_is_added(device)) {
|
||||||
err = ERROR_INVALID_STATE;
|
error = ERROR_INVALID_STATE;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver->stop_device != nullptr) {
|
if (driver->stopDevice != nullptr) {
|
||||||
err = driver->stop_device(device);
|
error = driver->stopDevice(device);
|
||||||
if (err != 0) {
|
if (error != ERROR_NONE) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,12 +186,12 @@ int driver_unbind(Driver* driver, Device* device) {
|
|||||||
|
|
||||||
LOG_I(TAG, "unbound %s to %s", driver->name, device->name);
|
LOG_I(TAG, "unbound %s to %s", driver->name, device->name);
|
||||||
|
|
||||||
return 0;
|
return ERROR_NONE;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
||||||
driver_unlock(driver);
|
driver_unlock(driver);
|
||||||
return err;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
142
TactilityKernel/Source/concurrent/Dispatcher.cpp
Normal file
142
TactilityKernel/Source/concurrent/Dispatcher.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <Tactility/concurrent/Dispatcher.h>
|
||||||
|
|
||||||
|
#include "Tactility/Error.h"
|
||||||
|
|
||||||
|
#include <Tactility/Log.h>
|
||||||
|
#include <Tactility/concurrent/EventGroup.h>
|
||||||
|
#include <Tactility/concurrent/Mutex.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#define TAG LOG_TAG("Dispatcher")
|
||||||
|
|
||||||
|
static constexpr EventBits_t BACKPRESSURE_WARNING_COUNT = 100U;
|
||||||
|
static constexpr EventBits_t WAIT_FLAG = 1U;
|
||||||
|
|
||||||
|
struct QueuedItem {
|
||||||
|
DispatcherCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DispatcherData {
|
||||||
|
Mutex mutex = { 0 };
|
||||||
|
std::queue<QueuedItem> queue = {};
|
||||||
|
EventGroupHandle_t eventGroup = nullptr;
|
||||||
|
std::atomic<bool> shutdown{false}; // TODO: Use EventGroup
|
||||||
|
|
||||||
|
DispatcherData() {
|
||||||
|
event_group_construct(&eventGroup);
|
||||||
|
mutex_construct(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DispatcherData() {
|
||||||
|
event_group_destruct(&eventGroup);
|
||||||
|
mutex_destruct(&mutex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define dispatcher_data(handle) static_cast<DispatcherData*>(handle)
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
DispatcherHandle_t dispatcher_alloc(void) {
|
||||||
|
return new DispatcherData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatcher_free(DispatcherHandle_t dispatcher) {
|
||||||
|
auto* data = dispatcher_data(dispatcher);
|
||||||
|
data->shutdown.store(true, std::memory_order_release);
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t dispatcher_dispatch_timed(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback, TickType_t timeout) {
|
||||||
|
auto* data = dispatcher_data(dispatcher);
|
||||||
|
|
||||||
|
// Mutate
|
||||||
|
if (!mutex_try_lock_timed(&data->mutex, timeout)) {
|
||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
LOG_E(TAG, "Mutex acquisition timeout");
|
||||||
|
#endif
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->shutdown.load(std::memory_order_acquire)) {
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
return ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->queue.push({
|
||||||
|
.callback = callback,
|
||||||
|
.context = callbackContext
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data->queue.size() == BACKPRESSURE_WARNING_COUNT) {
|
||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
LOG_W(TAG, "Backpressure: You're not consuming fast enough (100 queued)");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
if (event_group_set(data->eventGroup, WAIT_FLAG) != ERROR_NONE) {
|
||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
LOG_E(TAG, "Failed to set flag");
|
||||||
|
#endif
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t dispatcher_consume_timed(DispatcherHandle_t dispatcher, TickType_t timeout) {
|
||||||
|
auto* data = dispatcher_data(dispatcher);
|
||||||
|
|
||||||
|
// TODO: keep track of time and consider the timeout input as total timeout
|
||||||
|
|
||||||
|
// Wait for signal
|
||||||
|
error_t error = event_group_wait(data->eventGroup, WAIT_FLAG, false, true, nullptr, timeout);
|
||||||
|
if (error != ERROR_NONE) {
|
||||||
|
if (error == ERROR_TIMEOUT) {
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
|
} else {
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->shutdown.load(std::memory_order_acquire)) {
|
||||||
|
return ERROR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutate
|
||||||
|
bool processing = true;
|
||||||
|
do {
|
||||||
|
if (mutex_try_lock_timed(&data->mutex, 10)) {
|
||||||
|
if (!data->queue.empty()) {
|
||||||
|
// Make a copy, so it's thread-safe when we unlock
|
||||||
|
auto entry = data->queue.front();
|
||||||
|
data->queue.pop();
|
||||||
|
processing = !data->queue.empty();
|
||||||
|
// Don't keep lock as callback might be slow and we want to allow dispatch in the meanwhile
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
entry.callback(entry.context);
|
||||||
|
} else {
|
||||||
|
processing = false;
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
LOG_W(TAG, "Mutex acquisition timeout");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (processing && !data->shutdown.load(std::memory_order_acquire));
|
||||||
|
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
84
TactilityKernel/Source/concurrent/EventGroup.cpp
Normal file
84
TactilityKernel/Source/concurrent/EventGroup.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#include <Tactility/concurrent/EventGroup.h>
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error_t event_group_set(EventGroupHandle_t eventGroup, uint32_t inFlags) {
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
BaseType_t yield = pdFALSE;
|
||||||
|
if (xEventGroupSetBitsFromISR(eventGroup, inFlags, &yield) == pdFAIL) {
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
portYIELD_FROM_ISR(yield);
|
||||||
|
} else {
|
||||||
|
xEventGroupSetBits(eventGroup, inFlags);
|
||||||
|
}
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t event_group_clear(EventGroupHandle_t eventGroup, uint32_t flags) {
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
if (xEventGroupClearBitsFromISR(eventGroup, flags) == pdFAIL) {
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
portYIELD_FROM_ISR(pdTRUE);
|
||||||
|
} else {
|
||||||
|
xEventGroupClearBits(eventGroup, flags);
|
||||||
|
}
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t event_group_get(EventGroupHandle_t eventGroup) {
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
return xEventGroupGetBitsFromISR(eventGroup);
|
||||||
|
} else {
|
||||||
|
return xEventGroupGetBits(eventGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t event_group_wait(
|
||||||
|
EventGroupHandle_t eventGroup,
|
||||||
|
uint32_t inFlags,
|
||||||
|
bool awaitAll,
|
||||||
|
bool clearOnExit,
|
||||||
|
uint32_t* outFlags,
|
||||||
|
TickType_t timeout
|
||||||
|
) {
|
||||||
|
if (xPortInIsrContext()) {
|
||||||
|
return ERROR_ISR_STATUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t result_flags = xEventGroupWaitBits(
|
||||||
|
eventGroup,
|
||||||
|
inFlags,
|
||||||
|
clearOnExit ? pdTRUE : pdFALSE,
|
||||||
|
awaitAll ? pdTRUE : pdFALSE,
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
auto invalid_flags = awaitAll
|
||||||
|
? ((inFlags & result_flags) != inFlags) // await all
|
||||||
|
: ((inFlags & result_flags) == 0U); // await any
|
||||||
|
|
||||||
|
if (invalid_flags) {
|
||||||
|
const uint32_t matched = inFlags & result_flags;
|
||||||
|
if (matched == 0U) {
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
|
}
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outFlags != nullptr) {
|
||||||
|
*outFlags = result_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -7,22 +7,22 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
bool gpio_controller_set_level(Device* device, gpio_pin_t pin, bool high) {
|
error_t gpio_controller_set_level(Device* device, gpio_pin_t pin, bool high) {
|
||||||
const auto* driver = device_get_driver(device);
|
const auto* driver = device_get_driver(device);
|
||||||
return GPIO_DRIVER_API(driver)->set_level(device, pin, high);
|
return GPIO_DRIVER_API(driver)->set_level(device, pin, high);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gpio_controller_get_level(Device* device, gpio_pin_t pin, bool* high) {
|
error_t gpio_controller_get_level(Device* device, gpio_pin_t pin, bool* high) {
|
||||||
const auto* driver = device_get_driver(device);
|
const auto* driver = device_get_driver(device);
|
||||||
return GPIO_DRIVER_API(driver)->get_level(device, pin, high);
|
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) {
|
error_t gpio_controller_set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
|
||||||
const auto* driver = device_get_driver(device);
|
const auto* driver = device_get_driver(device);
|
||||||
return GPIO_DRIVER_API(driver)->set_options(device, pin, options);
|
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) {
|
error_t gpio_controller_get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
|
||||||
const auto* driver = device_get_driver(device);
|
const auto* driver = device_get_driver(device);
|
||||||
return GPIO_DRIVER_API(driver)->get_options(device, pin, options);
|
return GPIO_DRIVER_API(driver)->get_options(device, pin, options);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,24 +2,25 @@
|
|||||||
|
|
||||||
#include <Tactility/drivers/I2cController.h>
|
#include <Tactility/drivers/I2cController.h>
|
||||||
#include <Tactility/Driver.h>
|
#include <Tactility/Driver.h>
|
||||||
|
#include <Tactility/Error.h>
|
||||||
|
|
||||||
#define I2C_DRIVER_API(driver) ((struct I2cControllerApi*)driver->api)
|
#define I2C_DRIVER_API(driver) ((struct I2cControllerApi*)driver->api)
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
bool i2c_controller_read(Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
error_t i2c_controller_read(Device* device, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||||
const auto* driver = device_get_driver(device);
|
const auto* driver = device_get_driver(device);
|
||||||
return I2C_DRIVER_API(driver)->read(device, address, data, dataSize, timeout);
|
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) {
|
error_t 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);
|
const auto* driver = device_get_driver(device);
|
||||||
return I2C_DRIVER_API(driver)->write(device, address, data, dataSize, timeout);
|
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) {
|
error_t i2c_controller_write_read(Device* device, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) {
|
||||||
const auto* driver = device_get_driver(device);
|
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);
|
return I2C_DRIVER_API(driver)->write_read(device, address, writeData, writeDataSize, readData, readDataSize, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct DeviceType I2C_CONTROLLER_TYPE { 0 };
|
const struct DeviceType I2C_CONTROLLER_TYPE { 0 };
|
||||||
|
|||||||
@ -8,10 +8,10 @@ extern "C" {
|
|||||||
Driver root_driver = {
|
Driver root_driver = {
|
||||||
.name = "root",
|
.name = "root",
|
||||||
.compatible = (const char*[]) { "root", nullptr },
|
.compatible = (const char*[]) { "root", nullptr },
|
||||||
.start_device = nullptr,
|
.startDevice = nullptr,
|
||||||
.stop_device = nullptr,
|
.stopDevice = nullptr,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,13 @@
|
|||||||
TEST_CASE("device_construct and device_destruct should set and unset the correct fields") {
|
TEST_CASE("device_construct and device_destruct should set and unset the correct fields") {
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
|
|
||||||
int error = device_construct(&device);
|
error_t error = device_construct(&device);
|
||||||
CHECK_EQ(error, 0);
|
CHECK_EQ(error, ERROR_NONE);
|
||||||
|
|
||||||
CHECK_NE(device.internal.data, nullptr);
|
CHECK_NE(device.internal.data, nullptr);
|
||||||
CHECK_NE(device.internal.mutex.handle, nullptr);
|
CHECK_NE(device.internal.mutex.handle, nullptr);
|
||||||
|
|
||||||
error = device_destruct(&device);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
CHECK_EQ(error, 0);
|
|
||||||
|
|
||||||
CHECK_EQ(device.internal.data, nullptr);
|
CHECK_EQ(device.internal.data, nullptr);
|
||||||
CHECK_EQ(device.internal.mutex.handle, nullptr);
|
CHECK_EQ(device.internal.mutex.handle, nullptr);
|
||||||
@ -30,13 +29,13 @@ TEST_CASE("device_construct and device_destruct should set and unset the correct
|
|||||||
|
|
||||||
TEST_CASE("device_add should add the device to the list of all devices") {
|
TEST_CASE("device_add should add the device to the list of all devices") {
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
CHECK_EQ(device_construct(&device), 0);
|
CHECK_EQ(device_construct(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&device), 0);
|
CHECK_EQ(device_add(&device), ERROR_NONE);
|
||||||
|
|
||||||
// Gather all devices
|
// Gather all devices
|
||||||
std::vector<Device*> devices;
|
std::vector<Device*> devices;
|
||||||
for_each_device(&devices, [](auto* device, auto* context) {
|
for_each_device(&devices, [](auto* device, auto* context) {
|
||||||
auto* devices_ptr = (std::vector<Device*>*)context;
|
auto* devices_ptr = static_cast<std::vector<Device*>*>(context);
|
||||||
devices_ptr->push_back(device);
|
devices_ptr->push_back(device);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -44,8 +43,8 @@ TEST_CASE("device_add should add the device to the list of all devices") {
|
|||||||
CHECK_EQ(devices.size(), 1);
|
CHECK_EQ(devices.size(), 1);
|
||||||
CHECK_EQ(devices[0], &device);
|
CHECK_EQ(devices[0], &device);
|
||||||
|
|
||||||
CHECK_EQ(device_remove(&device), 0);
|
CHECK_EQ(device_remove(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&device), 0);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_add should add the device to its parent") {
|
TEST_CASE("device_add should add the device to its parent") {
|
||||||
@ -57,11 +56,11 @@ TEST_CASE("device_add should add the device to its parent") {
|
|||||||
.parent = &parent
|
.parent = &parent
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK_EQ(device_construct(&parent), 0);
|
CHECK_EQ(device_construct(&parent), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&parent), 0);
|
CHECK_EQ(device_add(&parent), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_construct(&child), 0);
|
CHECK_EQ(device_construct(&child), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&child), 0);
|
CHECK_EQ(device_add(&child), ERROR_NONE);
|
||||||
|
|
||||||
// Gather all child devices
|
// Gather all child devices
|
||||||
std::vector<Device*> children;
|
std::vector<Device*> children;
|
||||||
@ -74,30 +73,30 @@ TEST_CASE("device_add should add the device to its parent") {
|
|||||||
CHECK_EQ(children.size(), 1);
|
CHECK_EQ(children.size(), 1);
|
||||||
CHECK_EQ(children[0], &child);
|
CHECK_EQ(children[0], &child);
|
||||||
|
|
||||||
CHECK_EQ(device_remove(&child), 0);
|
CHECK_EQ(device_remove(&child), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&child), 0);
|
CHECK_EQ(device_destruct(&child), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_remove(&parent), 0);
|
CHECK_EQ(device_remove(&parent), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&parent), 0);
|
CHECK_EQ(device_destruct(&parent), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_add should set the state to 'added'") {
|
TEST_CASE("device_add should set the state to 'added'") {
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
CHECK_EQ(device_construct(&device), 0);
|
CHECK_EQ(device_construct(&device), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device.internal.state.added, false);
|
CHECK_EQ(device.internal.state.added, false);
|
||||||
CHECK_EQ(device_add(&device), 0);
|
CHECK_EQ(device_add(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.added, true);
|
CHECK_EQ(device.internal.state.added, true);
|
||||||
|
|
||||||
CHECK_EQ(device_remove(&device), 0);
|
CHECK_EQ(device_remove(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&device), 0);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_remove should remove it from the list of all devices") {
|
TEST_CASE("device_remove should remove it from the list of all devices") {
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
CHECK_EQ(device_construct(&device), 0);
|
CHECK_EQ(device_construct(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&device), 0);
|
CHECK_EQ(device_add(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device_remove(&device), 0);
|
CHECK_EQ(device_remove(&device), ERROR_NONE);
|
||||||
|
|
||||||
// Gather all devices
|
// Gather all devices
|
||||||
std::vector<Device*> devices;
|
std::vector<Device*> devices;
|
||||||
@ -109,7 +108,7 @@ TEST_CASE("device_remove should remove it from the list of all devices") {
|
|||||||
|
|
||||||
CHECK_EQ(devices.size(), 0);
|
CHECK_EQ(devices.size(), 0);
|
||||||
|
|
||||||
CHECK_EQ(device_destruct(&device), 0);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_remove should remove the device from its parent") {
|
TEST_CASE("device_remove should remove the device from its parent") {
|
||||||
@ -121,12 +120,12 @@ TEST_CASE("device_remove should remove the device from its parent") {
|
|||||||
.parent = &parent
|
.parent = &parent
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK_EQ(device_construct(&parent), 0);
|
CHECK_EQ(device_construct(&parent), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&parent), 0);
|
CHECK_EQ(device_add(&parent), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_construct(&child), 0);
|
CHECK_EQ(device_construct(&child), ERROR_NONE);
|
||||||
CHECK_EQ(device_add(&child), 0);
|
CHECK_EQ(device_add(&child), ERROR_NONE);
|
||||||
CHECK_EQ(device_remove(&child), 0);
|
CHECK_EQ(device_remove(&child), ERROR_NONE);
|
||||||
|
|
||||||
// Gather all child devices
|
// Gather all child devices
|
||||||
std::vector<Device*> children;
|
std::vector<Device*> children;
|
||||||
@ -138,22 +137,22 @@ TEST_CASE("device_remove should remove the device from its parent") {
|
|||||||
|
|
||||||
CHECK_EQ(children.size(), 0);
|
CHECK_EQ(children.size(), 0);
|
||||||
|
|
||||||
CHECK_EQ(device_destruct(&child), 0);
|
CHECK_EQ(device_destruct(&child), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_remove(&parent), 0);
|
CHECK_EQ(device_remove(&parent), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&parent), 0);
|
CHECK_EQ(device_destruct(&parent), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_remove should clear the state 'added'") {
|
TEST_CASE("device_remove should clear the state 'added'") {
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
CHECK_EQ(device_construct(&device), 0);
|
CHECK_EQ(device_construct(&device), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_add(&device), 0);
|
CHECK_EQ(device_add(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.added, true);
|
CHECK_EQ(device.internal.state.added, true);
|
||||||
CHECK_EQ(device_remove(&device), 0);
|
CHECK_EQ(device_remove(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.added, false);
|
CHECK_EQ(device.internal.state.added, false);
|
||||||
|
|
||||||
CHECK_EQ(device_destruct(&device), 0);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("device_is_ready should return true only when it is started") {
|
TEST_CASE("device_is_ready should return true only when it is started") {
|
||||||
@ -161,30 +160,30 @@ TEST_CASE("device_is_ready should return true only when it is started") {
|
|||||||
Driver driver = {
|
Driver driver = {
|
||||||
.name = "test_driver",
|
.name = "test_driver",
|
||||||
.compatible = compatible,
|
.compatible = compatible,
|
||||||
.start_device = nullptr,
|
.startDevice = nullptr,
|
||||||
.stop_device = nullptr,
|
.stopDevice = nullptr,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
Device device = { 0 };
|
Device device = { 0 };
|
||||||
|
|
||||||
CHECK_EQ(driver_construct(&driver), 0);
|
CHECK_EQ(driver_construct(&driver), ERROR_NONE);
|
||||||
CHECK_EQ(device_construct(&device), 0);
|
CHECK_EQ(device_construct(&device), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device.internal.state.started, false);
|
CHECK_EQ(device.internal.state.started, false);
|
||||||
device_set_driver(&device, &driver);
|
device_set_driver(&device, &driver);
|
||||||
CHECK_EQ(device.internal.state.started, false);
|
CHECK_EQ(device.internal.state.started, false);
|
||||||
CHECK_EQ(device_add(&device), 0);
|
CHECK_EQ(device_add(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.started, false);
|
CHECK_EQ(device.internal.state.started, false);
|
||||||
CHECK_EQ(device_start(&device), 0);
|
CHECK_EQ(device_start(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.started, true);
|
CHECK_EQ(device.internal.state.started, true);
|
||||||
CHECK_EQ(device_stop(&device), 0);
|
CHECK_EQ(device_stop(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.started, false);
|
CHECK_EQ(device.internal.state.started, false);
|
||||||
CHECK_EQ(device_remove(&device), 0);
|
CHECK_EQ(device_remove(&device), ERROR_NONE);
|
||||||
CHECK_EQ(device.internal.state.started, false);
|
CHECK_EQ(device.internal.state.started, false);
|
||||||
|
|
||||||
CHECK_EQ(driver_destruct(&driver), 0);
|
CHECK_EQ(driver_destruct(&driver), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&device), 0);
|
CHECK_EQ(device_destruct(&device), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|||||||
25
Tests/TactilityKernel/DispatcherTest.cpp
Normal file
25
Tests/TactilityKernel/DispatcherTest.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "doctest.h"
|
||||||
|
#include <Tactility/FreeRTOS/task.h>
|
||||||
|
#include <Tactility/concurrent/Dispatcher.h>
|
||||||
|
|
||||||
|
TEST_CASE("dispatcher test") {
|
||||||
|
DispatcherHandle_t dispatcher = dispatcher_alloc();
|
||||||
|
CHECK_NE(dispatcher, nullptr);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
auto error = dispatcher_dispatch(dispatcher, &count, [](void* context) {
|
||||||
|
int* count_ptr = static_cast<int*>(context);
|
||||||
|
(*count_ptr)++;
|
||||||
|
});
|
||||||
|
|
||||||
|
CHECK_EQ(error, ERROR_NONE);
|
||||||
|
vTaskDelay(1);
|
||||||
|
|
||||||
|
CHECK_EQ(count, 0);
|
||||||
|
|
||||||
|
CHECK_EQ(dispatcher_consume(dispatcher), ERROR_NONE);
|
||||||
|
CHECK_EQ(count, 1);
|
||||||
|
|
||||||
|
dispatcher_free(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
@ -26,10 +26,10 @@ static int stop(Device* device) {
|
|||||||
static Driver integration_driver = {
|
static Driver integration_driver = {
|
||||||
.name = "integration_test_driver",
|
.name = "integration_test_driver",
|
||||||
.compatible = (const char*[]) { "integration", nullptr },
|
.compatible = (const char*[]) { "integration", nullptr },
|
||||||
.start_device = start,
|
.startDevice = start,
|
||||||
.stop_device = stop,
|
.stopDevice = stop,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,18 +47,18 @@ TEST_CASE("driver with with start success and stop success should start and stop
|
|||||||
.parent = nullptr,
|
.parent = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK_EQ(driver_construct(&integration_driver), 0);
|
CHECK_EQ(driver_construct(&integration_driver), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(device_construct(&integration_device), 0);
|
CHECK_EQ(device_construct(&integration_device), ERROR_NONE);
|
||||||
device_add(&integration_device);
|
device_add(&integration_device);
|
||||||
CHECK_EQ(startCalled, 0);
|
CHECK_EQ(startCalled, 0);
|
||||||
CHECK_EQ(driver_bind(&integration_driver, &integration_device), 0);
|
CHECK_EQ(driver_bind(&integration_driver, &integration_device), ERROR_NONE);
|
||||||
CHECK_EQ(startCalled, 1);
|
CHECK_EQ(startCalled, 1);
|
||||||
CHECK_EQ(stopCalled, 0);
|
CHECK_EQ(stopCalled, 0);
|
||||||
CHECK_EQ(driver_unbind(&integration_driver, &integration_device), 0);
|
CHECK_EQ(driver_unbind(&integration_driver, &integration_device), ERROR_NONE);
|
||||||
CHECK_EQ(stopCalled, 1);
|
CHECK_EQ(stopCalled, 1);
|
||||||
CHECK_EQ(device_remove(&integration_device), 0);
|
CHECK_EQ(device_remove(&integration_device), ERROR_NONE);
|
||||||
CHECK_EQ(device_destruct(&integration_device), 0);
|
CHECK_EQ(device_destruct(&integration_device), ERROR_NONE);
|
||||||
|
|
||||||
CHECK_EQ(driver_destruct(&integration_driver), 0);
|
CHECK_EQ(driver_destruct(&integration_driver), ERROR_NONE);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,9 @@
|
|||||||
TEST_CASE("driver_construct and driver_destruct should set and unset the correct fields") {
|
TEST_CASE("driver_construct and driver_destruct should set and unset the correct fields") {
|
||||||
Driver driver = { 0 };
|
Driver driver = { 0 };
|
||||||
|
|
||||||
int error = driver_construct(&driver);
|
CHECK_EQ(driver_construct(&driver), ERROR_NONE);
|
||||||
CHECK_EQ(error, 0);
|
|
||||||
CHECK_NE(driver.internal.data, nullptr);
|
CHECK_NE(driver.internal.data, nullptr);
|
||||||
|
CHECK_EQ(driver_destruct(&driver), ERROR_NONE);
|
||||||
error = driver_destruct(&driver);
|
|
||||||
CHECK_EQ(error, 0);
|
|
||||||
CHECK_EQ(driver.internal.data, nullptr);
|
CHECK_EQ(driver.internal.data, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,10 +15,10 @@ TEST_CASE("driver_is_compatible should return true if a compatible value is foun
|
|||||||
Driver driver = {
|
Driver driver = {
|
||||||
.name = "test_driver",
|
.name = "test_driver",
|
||||||
.compatible = compatible,
|
.compatible = compatible,
|
||||||
.start_device = nullptr,
|
.startDevice = nullptr,
|
||||||
.stop_device = nullptr,
|
.stopDevice = nullptr,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
CHECK_EQ(driver_is_compatible(&driver, "test_compatible"), true);
|
CHECK_EQ(driver_is_compatible(&driver, "test_compatible"), true);
|
||||||
@ -34,24 +31,22 @@ TEST_CASE("driver_find should only find a compatible driver when the driver was
|
|||||||
Driver driver = {
|
Driver driver = {
|
||||||
.name = "test_driver",
|
.name = "test_driver",
|
||||||
.compatible = compatible,
|
.compatible = compatible,
|
||||||
.start_device = nullptr,
|
.startDevice = nullptr,
|
||||||
.stop_device = nullptr,
|
.stopDevice = nullptr,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
.device_type = nullptr,
|
.deviceType = nullptr,
|
||||||
.internal = { 0 }
|
.internal = { 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
Driver* found_driver = driver_find_compatible("test_compatible");
|
Driver* found_driver = driver_find_compatible("test_compatible");
|
||||||
CHECK_EQ(found_driver, nullptr);
|
CHECK_EQ(found_driver, nullptr);
|
||||||
|
|
||||||
int error = driver_construct(&driver);
|
CHECK_EQ(driver_construct(&driver), ERROR_NONE);
|
||||||
CHECK_EQ(error, 0);
|
|
||||||
|
|
||||||
found_driver = driver_find_compatible("test_compatible");
|
found_driver = driver_find_compatible("test_compatible");
|
||||||
CHECK_EQ(found_driver, &driver);
|
CHECK_EQ(found_driver, &driver);
|
||||||
|
|
||||||
error = driver_destruct(&driver);
|
CHECK_EQ(driver_destruct(&driver), ERROR_NONE);
|
||||||
CHECK_EQ(error, 0);
|
|
||||||
|
|
||||||
found_driver = driver_find_compatible("test_compatible");
|
found_driver = driver_find_compatible("test_compatible");
|
||||||
CHECK_EQ(found_driver, nullptr);
|
CHECK_EQ(found_driver, nullptr);
|
||||||
|
|||||||
@ -60,7 +60,7 @@ TEST_CASE("mutex_lock in another task should block when a lock is active") {
|
|||||||
CHECK_EQ(task_lock_counter, 0);
|
CHECK_EQ(task_lock_counter, 0);
|
||||||
|
|
||||||
mutex_unlock(&mutex);
|
mutex_unlock(&mutex);
|
||||||
vTaskDelay(1);
|
vTaskDelay(2); // 1 is sufficient most of the time, but not always
|
||||||
CHECK_EQ(task_lock_counter, 1);
|
CHECK_EQ(task_lock_counter, 1);
|
||||||
mutex_destruct(&mutex);
|
mutex_destruct(&mutex);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ TEST_CASE("recursive_mutex_lock in another task should block when a lock is acti
|
|||||||
CHECK_EQ(task_lock_counter, 0);
|
CHECK_EQ(task_lock_counter, 0);
|
||||||
|
|
||||||
recursive_mutex_unlock(&mutex);
|
recursive_mutex_unlock(&mutex);
|
||||||
vTaskDelay(1);
|
vTaskDelay(2); // 1 is sufficient most of the time, but not always
|
||||||
CHECK_EQ(task_lock_counter, 1);
|
CHECK_EQ(task_lock_counter, 1);
|
||||||
recursive_mutex_destruct(&mutex);
|
recursive_mutex_destruct(&mutex);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user