Remove Bus and add DeviceType

This commit is contained in:
Ken Van Hoeylandt 2026-01-21 22:25:02 +01:00
parent c85c4da300
commit b4966c36f6
12 changed files with 57 additions and 210 deletions

View File

@ -20,6 +20,7 @@ Driver tlora_pager_driver = {
.start_device = start,
.stop_device = stop,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};

View File

@ -112,6 +112,7 @@ Driver esp32_gpio_driver = {
.start_device = start,
.stop_device = stop,
.api = (void*)&esp32_gpio_api,
.device_type = nullptr,
.internal = { 0 }
};

View File

@ -84,6 +84,7 @@ Driver esp32_i2c_driver = {
.start_device = start,
.stop_device = stop,
.api = (void*)&esp32_i2c_api,
.device_type = &I2C_CONTROLLER_TYPE,
.internal = { 0 }
};

View File

@ -1,49 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
struct Device;
/**
* A bus contains a group of devices owned by a parent device.
* It is used to make a group of devices discoverable.
*
* Example usage:
* The "i2c0" bus is created by the i2c_controller driver when a device is started by a driver.
* The postfix "0" means that it was the first device instance (at index 0).
*/
struct Bus {
/** The name of the bus (e.g. "i2c0" or "spi1"). Valid characters: a-z a-Z 0-9 - _ . */
const char* name;
/** Internal data */
struct {
/** The device this bus belongs to */
struct Device* parent_device;
/** The bus' private data */
void* data;
} internal;
};
// region Bus management
int bus_construct(struct Bus* bus);
int bus_destruct(struct Bus* bus);
struct Bus* bus_find(const char* name);
// endregion
// region Bus device management
int bus_add_device(struct Bus*, struct Device* dev);
void bus_remove_device(struct Bus*, struct Device* dev);
// endregion
#ifdef __cplusplus
}
#endif

View File

@ -1,10 +1,11 @@
#pragma once
#include "Driver.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <Tactility/Bus.h>
#include <Tactility/concurrent/Mutex.h>
#include <stdbool.h>
@ -13,6 +14,12 @@ extern "C" {
struct Driver;
/** Enables discovering devices of the same type */
struct DeviceType {
/* Placeholder because empty structs have a different size with C vs C++ compilers */
uint8_t _;
};
/** Represents a piece of hardware */
struct Device {
/** The name of the device. Valid characters: a-z a-Z 0-9 - _ . */
@ -27,8 +34,6 @@ struct Device {
struct Driver* driver;
/** The driver data for this device (e.g. a mutex) */
void* driver_data;
/** The bus that is owned by this device. This bus can contain child devices to make them discoverable. Can be NULL. */
struct Bus* bus;
/** The mutex for device operations */
struct Mutex mutex;
/** The device state */
@ -134,18 +139,6 @@ static inline bool device_is_added(const struct Device* device) {
return device->internal.state.added;
}
static inline struct Bus* device_get_bus(const struct Device* device) {
return device->internal.bus;
}
static inline void device_set_bus(struct Device* device, struct Bus* bus) {
device->internal.bus = bus;
}
static inline const char* device_get_bus_name(const struct Device* device) {
return device->internal.bus ? device->internal.bus->name : "";
}
static inline void device_lock(struct Device* device) {
mutex_lock(&device->internal.mutex);
}
@ -158,6 +151,18 @@ static inline void device_unlock(struct Device* device) {
mutex_unlock(&device->internal.mutex);
}
static inline const struct DeviceType* device_get_type(struct Device* device) {
return device->internal.driver->device_type;
}
/**
* Iterate through all the known devices of a specific type
* @param type the type to filter
* @param callback_context the parameter to pass to the callback. NULL is valid.
* @param on_device the function to call for each filtered device. return true to continue iterating or false to stop.
*/
void for_each_device_of_type(const struct DeviceType* type, void* callback_context, bool(*on_device)(struct Device* device, void* context));
#ifdef __cplusplus
}
#endif

View File

@ -1,12 +1,12 @@
#pragma once
#include <Tactility/Bus.h>
#include <Tactility/Device.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Device;
struct DeviceType;
struct Driver {
/** The driver name */
const char* name;
@ -18,6 +18,8 @@ struct Driver {
int (*stop_device)(struct Device* dev);
/** Contains the driver's functions */
const void* api;
/** Which type of devices this driver creates (can be NULL) */
const struct DeviceType* device_type;
/** Internal data */
struct {
/** Contains private data */
@ -25,15 +27,19 @@ struct Driver {
} internal;
};
int driver_construct(struct Driver* drv);
int driver_construct(struct Driver* driver);
int driver_destruct(struct Driver* drv);
int driver_destruct(struct Driver* driver);
struct Driver* driver_find(const char* compatible);
int driver_bind(struct Driver* drv, struct Device* dev);
int driver_bind(struct Driver* driver, struct Device* device);
int driver_unbind(struct Driver* drv, struct Device* dev);
int driver_unbind(struct Driver* driver, struct Device* device);
static inline const struct DeviceType* driver_get_device_type(struct Driver* driver) {
return driver->device_type;
}
#ifdef __cplusplus
}

View File

@ -21,6 +21,8 @@ bool i2c_controller_write(struct Device* device, uint8_t address, const uint8_t*
bool i2c_controller_write_read(struct Device* device, uint8_t address, const uint8_t* write_data, size_t write_data_size, uint8_t* read_data, size_t read_data_size, TickType_t timeout);
extern const struct DeviceType I2C_CONTROLLER_TYPE;
#ifdef __cplusplus
}
#endif

View File

@ -1,115 +0,0 @@
#include <algorithm>
#include <cstring>
#include <vector>
#include <Tactility/concurrent/Mutex.h>
#include <Tactility/Bus.h>
#include <Tactility/Device.h>
#include <Tactility/Driver.h>
/** Keeps track of all existing buses */
struct BusLedger {
std::vector<Bus*> buses = {};
Mutex mutex {};
BusLedger() {
mutex_construct(&mutex);
}
~BusLedger() {
mutex_destruct(&mutex);
}
};
/* Internal data for a Bus */
struct BusData {
std::vector<Device*> devices = {};
std::vector<Driver*> drivers = {};
Mutex mutex {};
BusData() {
mutex_construct(&mutex);
}
~BusData() {
mutex_destruct(&mutex);
}
};
static BusLedger ledger;
#define ledger_lock() mutex_lock(&ledger.mutex);
#define ledger_unlock() mutex_unlock(&ledger.mutex);
#define bus_lock(bus_data) mutex_lock(&bus_data->mutex);
#define bus_unlock(bus_data) mutex_unlock(&bus_data->mutex);
#define BUS_INSTANCE_DATA(bus) ((struct BusData*)bus->internal.data)
extern "C" {
// region Bus management
static int bus_add(Bus* bus) {
ledger_lock();
ledger.buses.push_back(bus);
ledger_unlock();
return 0;
}
static void bus_remove(Bus* bus) {
ledger_lock();
const auto iterator = std::ranges::find(ledger.buses, bus);
if (iterator != ledger.buses.end()) {
ledger.buses.erase(iterator);
}
ledger_unlock();
}
int bus_construct(Bus* bus) {
bus->internal.data = new BusData();
bus_add(bus);
return 0;
}
int bus_destruct(Bus* bus) {
delete BUS_INSTANCE_DATA(bus);
bus_remove(bus);
return 0;
}
// endregion
// region Bus device management
Bus* bus_find(const char* name) {
ledger_lock();
const auto it = std::ranges::find_if(ledger.buses, [name](Bus* bus) {
return strcmp(name, bus->name) == 0;
});
Bus* result = (it != ledger.buses.end()) ? *it : nullptr;
ledger_unlock();
return result;
}
int bus_add_device(Bus* bus, Device* dev) {
auto* bus_data = BUS_INSTANCE_DATA(bus);
bus_lock(bus_data);
bus_data->devices.push_back(dev);
bus_unlock(bus_data);
return 0;
}
void bus_remove_device(Bus* bus, Device* dev) {
auto* bus_data = BUS_INSTANCE_DATA(bus);
bus_lock(bus_data);
const auto iterator = std::ranges::find(bus_data->devices, dev);
if (iterator != bus_data->devices.end()) {
bus_data->devices.erase(iterator);
}
bus_unlock(bus_data);
}
// endregion
} // extern "C"

View File

@ -90,11 +90,6 @@ void device_add(Device* device) {
device_add_child(parent, device);
}
auto* bus = device->internal.bus;
if (bus != nullptr) {
bus_add_device(bus, device);
}
device->internal.state.added = true;
}
@ -108,11 +103,6 @@ bool device_remove(Device* device) {
return true;
}
auto* bus = device->internal.bus;
if (bus != nullptr) {
bus_remove_device(bus, device);
}
// Remove self from parent's children list
auto* parent = device->parent;
if (parent != nullptr) {
@ -138,11 +128,6 @@ failed_ledger_lookup:
device_add_child(parent, device);
}
// Re-add to bus
if (bus != nullptr) {
bus_add_device(bus, device);
}
return false;
}
@ -180,15 +165,6 @@ int device_stop(struct Device* device) {
return 0;
}
int result = driver_unbind(device->internal.driver, device);
if (result != 0) {
// Re-add to bus
if (device->internal.bus != nullptr) {
bus_add_device(device->internal.bus, device);
}
return result;
}
device->internal.state.started = false;
device->internal.state.start_result = 0;
return 0;
@ -199,4 +175,19 @@ void device_set_parent(Device* device, Device* parent) {
device->parent = parent;
}
void for_each_device_of_type(const DeviceType* type, void* callback_context, bool(*on_device)(Device* device, void* context)) {
ledger_lock();
for (auto* device : ledger.devices) {
auto* driver = device->internal.driver;
if (driver != nullptr) {
if (driver->device_type == type) {
if (!on_device(device, callback_context)) {
break;
}
}
}
}
ledger_unlock()
}
} // extern "C"

View File

@ -2,6 +2,7 @@
#include <vector>
#include <Tactility/concurrent/Mutex.h>
#include <Tactility/Device.h>
#include <Tactility/Driver.h>
#include <Tactility/Error.h>
#include <Tactility/Log.h>

View File

@ -20,4 +20,6 @@ bool i2c_controller_write_read(Device* device, uint8_t address, const uint8_t* w
return I2C_DRIVER_API(driver)->write_read(device, address, write_data, write_data_size, read_data, read_data_size, timeout);
}
const struct DeviceType I2C_CONTROLLER_TYPE { 0 };
}

View File

@ -9,6 +9,7 @@ Driver root_driver = {
.start_device = nullptr,
.stop_device = nullptr,
.api = nullptr,
.device_type = nullptr,
.internal = { 0 }
};