GPIO refactored (#495)

* **Refactor**
  * GPIO subsystem moved to a descriptor-based model for per-pin ownership and runtime pin management; many platform drivers now acquire/release descriptors.
  * Device trees and drivers now use GPIO phandle-style pin specifications across all boards and all drivers.

* **Behavior**
  * Device list now encodes per-device status (ok/disabled); boot will skip disabled devices accordingly.

* **Deprecation**
  * Legacy GPIO HAL marked deprecated and replaced with descriptor-based interfaces.

* **Chores**
  * Bindings and platform configs updated to the new GPIO pin-spec format.
This commit is contained in:
Ken Van Hoeylandt
2026-02-11 20:34:54 +01:00
committed by GitHub
parent dff93cb655
commit 26c17986c6
80 changed files with 1129 additions and 665 deletions
@@ -43,16 +43,6 @@ struct Device {
struct DeviceInternal* internal;
};
/**
* Holds a device pointer and a compatible string.
* The device must not be constructed, added or started yet.
* This is used by the devicetree code generator and the application init sequence.
*/
struct CompatibleDevice {
struct Device* device;
const char* compatible;
};
/**
* Initialize the properties of a device.
*
@@ -10,24 +10,24 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
#define GPIO_OPTIONS_MASK 0x1f
#define GPIO_ACTIVE_HIGH (0 << 0)
#define GPIO_ACTIVE_LOW (1 << 0)
#define GPIO_DIRECTION_INPUT (1 << 1)
#define GPIO_DIRECTION_OUTPUT (1 << 2)
#define GPIO_DIRECTION_INPUT_OUTPUT (GPIO_DIRECTION_INPUT | GPIO_DIRECTION_OUTPUT)
#define GPIO_PULL_UP (0 << 3)
#define GPIO_PULL_DOWN (1 << 4)
#define GPIO_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5]
#define GPIO_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_INTERRUPT_BITMASK) >> 5)
#define GPIO_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5))
#define GPIO_FLAGS_MASK 0x1f
#define GPIO_PIN_NONE -1
#define GPIO_PIN_SPEC_NONE ((struct GpioPinSpec) { NULL, 0, GPIO_FLAG_NONE })
#define GPIO_FLAG_NONE 0
#define GPIO_FLAG_ACTIVE_HIGH (0 << 0)
#define GPIO_FLAG_ACTIVE_LOW (1 << 0)
#define GPIO_FLAG_DIRECTION_INPUT (1 << 1)
#define GPIO_FLAG_DIRECTION_OUTPUT (1 << 2)
#define GPIO_FLAG_DIRECTION_INPUT_OUTPUT (GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_DIRECTION_OUTPUT)
#define GPIO_FLAG_PULL_UP (0 << 3)
#define GPIO_FLAG_PULL_DOWN (1 << 4)
#define GPIO_FLAG_INTERRUPT_BITMASK (0b111 << 5) // 3 bits to hold the values [0, 5]
#define GPIO_FLAG_INTERRUPT_FROM_OPTIONS(options) (gpio_int_type_t)((options & GPIO_FLAG_INTERRUPT_BITMASK) >> 5)
#define GPIO_FLAG_INTERRUPT_TO_OPTIONS(options, interrupt) (options | (interrupt << 5))
typedef enum {
GPIO_INTERRUPT_DISABLE = 0,
GPIO_INTERRUPT_POS_EDGE = 1,
@@ -35,35 +35,39 @@ typedef enum {
GPIO_INTERRUPT_ANY_EDGE = 3,
GPIO_INTERRUPT_LOW_LEVEL = 4,
GPIO_INTERRUPT_HIGH_LEVEL = 5,
GPIO__MAX,
GPIO_MAX,
} GpioInterruptType;
enum GpioOwnerType {
/** @brief Pin is unclaimed/free */
GPIO_OWNER_NONE,
/** @brief Pin is owned by a hog */
GPIO_OWNER_HOG,
/** @brief Pin is claimed by a regular consumer */
GPIO_OWNER_GPIO,
/** @brief Pin is owned by SPI. This is a special case because of CS pin transfer from hog to SPI controller. */
GPIO_OWNER_SPI
};
/** The index of a GPIO pin on a GPIO Controller */
typedef uint8_t gpio_pin_t;
/** Specifies the configuration flags for a GPIO pin (or set of pins) */
typedef uint16_t gpio_flags_t;
/** A configuration for a single GPIO pin */
struct GpioPinConfig {
/**
* Specifies a pin and its properties for a specific GPIO controller.
* Used by the devicetree, drivers and application code to refer to GPIO pins and acquire them via the gpio_controller API.
*/
struct GpioPinSpec {
/** GPIO device controlling the pin */
const struct Device* port;
struct Device* gpio_controller;
/** The pin's number on the device */
gpio_pin_t pin;
/** The pin's configuration flags as specified in devicetree */
gpio_flags_t flags;
};
/**
* Check if the pin is ready to be used.
*
* @param pinConfig the specifications of the pin
* @return true if the pin is ready to be used
*/
static inline bool gpio_is_ready(const struct GpioPinConfig* pinConfig) {
return device_is_ready(pinConfig->port);
}
#ifdef __cplusplus
}
#endif
@@ -10,106 +10,136 @@ extern "C" {
#include <tactility/error.h>
struct Device;
struct GpioDescriptor;
struct GpioControllerApi {
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in,out] descriptor the pin descriptor
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t (*set_level)(struct Device* device, gpio_pin_t pin, bool high);
error_t (*set_level)(struct GpioDescriptor* descriptor, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] descriptor the pin descriptor
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t (*get_level)(struct Device* device, gpio_pin_t pin, bool* high);
error_t (*get_level)(struct GpioDescriptor* descriptor, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] options configuration flags (direction, pull-up/down, etc.)
* @param[in,out] descriptor the pin descriptor
* @param[in] flags configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t (*set_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
error_t (*set_flags)(struct GpioDescriptor* descriptor, gpio_flags_t flags);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] options pointer to store the configuration flags
* @param[in] descriptor the pin descriptor
* @param[out] flags pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t (*get_options)(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
error_t (*get_flags)(struct GpioDescriptor* descriptor, gpio_flags_t* flags);
/**
* @brief Gets the number of pins supported by the controller.
* @param[in] device the GPIO controller device
* @param[out] count pointer to store the number of pins
* @brief Gets the native pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin_number pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t (*get_pin_count)(struct Device* device, uint32_t* count);
error_t (*get_native_pin_number)(struct GpioDescriptor* descriptor, void* pin_number);
};
struct GpioDescriptor* gpio_descriptor_acquire(
struct Device* controller,
gpio_pin_t pin_number,
enum GpioOwnerType owner
);
error_t gpio_descriptor_release(struct GpioDescriptor* descriptor);
/**
* @brief Gets the pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_pin_number(struct GpioDescriptor* descriptor, gpio_pin_t* pin);
/**
* @brief Gets the pin owner type associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] owner_type pointer to output owner type
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_owner_type(struct GpioDescriptor* descriptor, enum GpioOwnerType* owner_type);
/**
* @brief Sets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] descriptor the pin descriptor
* @param[in] high true to set the pin high, false to set it low
* @return ERROR_NONE if successful
*/
error_t gpio_controller_set_level(struct Device* device, gpio_pin_t pin, bool high);
error_t gpio_descriptor_set_level(struct GpioDescriptor* descriptor, bool high);
/**
* @brief Gets the logical level of a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] descriptor the pin descriptor
* @param[out] high pointer to store the pin level
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_level(struct Device* device, gpio_pin_t pin, bool* high);
error_t gpio_descriptor_get_level(struct GpioDescriptor* descriptor, bool* high);
/**
* @brief Configures the options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[in] options configuration flags (direction, pull-up/down, etc.)
* @param[in] descriptor the pin descriptor
* @param[in] flags configuration flags (direction, pull-up/down, etc.)
* @return ERROR_NONE if successful
*/
error_t gpio_controller_set_options(struct Device* device, gpio_pin_t pin, gpio_flags_t options);
error_t gpio_descriptor_set_flags(struct GpioDescriptor* descriptor, gpio_flags_t flags);
/**
* @brief Gets the configuration options for a GPIO pin.
* @param[in] device the GPIO controller device
* @param[in] pin the pin index
* @param[out] options pointer to store the configuration flags
* @param[in] descriptor the pin descriptor
* @param[out] flags pointer to store the configuration flags
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_options(struct Device* device, gpio_pin_t pin, gpio_flags_t* options);
error_t gpio_descriptor_get_flags(struct GpioDescriptor* descriptor, gpio_flags_t* flags);
/**
* @brief Gets the number of pins supported by the controller.
* @param[in] device the GPIO controller device
* @param[out] count pointer to store the number of pins
* @return ERROR_NONE if successful
*/
* @brief Gets the native pin number associated with a descriptor.
* @param[in] descriptor the pin descriptor
* @param[out] pin_number pointer to store the pin number
* @return ERROR_NONE if successful
*/
error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, void* pin_number);
/**
* @brief Gets the number of pins supported by the controller.
* @param[in] device the GPIO controller device
* @param[out] count pointer to store the number of pins
* @return ERROR_NONE if successful
*/
error_t gpio_controller_get_pin_count(struct Device* device, uint32_t* count);
/**
* @brief Configures the options for a GPIO pin using a pin configuration structure.
* @param[in] device the GPIO controller device
* @param[in] config the pin configuration structure
* @brief Initializes GPIO descriptors for a controller.
* @param[in,out] device the GPIO controller device
* @param[in] controller_context pointer to store in the controller's internal data
* @return ERROR_NONE if successful
*/
static inline error_t gpio_set_options_config(struct Device* device, const struct GpioPinConfig* config) {
return gpio_controller_set_options(device, config->pin, config->flags);
}
error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_count, void* controller_context);
/**
* @brief Deinitializes GPIO descriptors for a controller.
* @param[in,out] device the GPIO controller device
* @return ERROR_NONE if successful
*/
error_t gpio_controller_deinit_descriptors(struct Device* device);
extern const struct DeviceType GPIO_CONTROLLER_TYPE;
@@ -0,0 +1,16 @@
#pragma once
#include "gpio.h"
struct Device;
struct GpioDescriptor {
/** @brief The controller that owns this pin */
struct Device* controller;
/** @brief Physical pin number */
gpio_pin_t pin;
/** @brief Current owner */
enum GpioOwnerType owner_type;
/** @brief Implementation-specific context (e.g. from esp32 controller internally) */
void* controller_context;
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
struct Device;
/** Signals the intended state of a device. */
enum DtsDeviceStatus {
/** Device should be constructed, added and started. */
DTS_DEVICE_STATUS_OKAY,
/** Device should be constructed and added, but not started. */
DTS_DEVICE_STATUS_DISABLED
};
/**
* Holds a device pointer and a compatible string.
* The device must not be constructed, added or started yet.
* This is used by the devicetree code generator and the application init sequence.
*/
struct DtsDevice {
/** A pointer to a device. */
struct Device* device;
/** The compatible string contains the identifier of the driver that this device is compatible with. */
const char* compatible;
/** The intended state of the device. */
const enum DtsDeviceStatus status;
};
/** Signals the end of the device array in the generated dts code. */
#define DTS_DEVICE_TERMINATOR { NULL, NULL, DTS_DEVICE_STATUS_DISABLED }
@@ -12,10 +12,10 @@ extern "C" {
* Initialize the kernel with platform and device modules, and a device tree.
* @param platform_module The platform module to start. This module should not be constructed yet.
* @param device_module The device module to start. This module should not be constructed yet. This parameter can be NULL.
* @param devicetree_devices The list of generated devices from the devicetree. The array must be terminated by an entry { NULL, NULL }. This parameter can be NULL.
* @param dts_devices The list of generated devices from the devicetree. The array must be terminated with DTS_DEVICE_TERMINATOR. This parameter can be NULL.
* @return ERROR_NONE on success, otherwise an error code
*/
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct CompatibleDevice devicetree_devices[]);
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct DtsDevice dts_devices[]);
#ifdef __cplusplus
}
@@ -3,33 +3,136 @@
#include <tactility/driver.h>
#include <tactility/drivers/gpio_controller.h>
#define GPIO_DRIVER_API(driver) ((struct GpioControllerApi*)driver->api)
#include <tactility/concurrent/mutex.h>
#include <tactility/drivers/gpio_descriptor.h>
#include <cstdlib>
#include <new>
#define GPIO_INTERNAL_API(driver) ((struct GpioControllerApi*)(driver)->api)
extern "C" {
error_t gpio_controller_set_level(Device* device, gpio_pin_t pin, bool high) {
const auto* driver = device_get_driver(device);
return GPIO_DRIVER_API(driver)->set_level(device, pin, high);
struct GpioControllerData {
struct Mutex mutex {};
uint32_t pin_count;
struct GpioDescriptor* descriptors = nullptr;
explicit GpioControllerData(uint32_t pin_count) : pin_count(pin_count) {
mutex_construct(&mutex);
}
error_t init_descriptors(Device* device, void* controller_context) {
descriptors = (struct GpioDescriptor*)calloc(pin_count, sizeof(struct GpioDescriptor));
if (!descriptors) return ERROR_OUT_OF_MEMORY;
for (uint32_t i = 0; i < pin_count; ++i) {
descriptors[i].controller = device;
descriptors[i].pin = (gpio_pin_t)i;
descriptors[i].owner_type = GPIO_OWNER_NONE;
descriptors[i].controller_context = controller_context;
}
return ERROR_NONE;
}
~GpioControllerData() {
if (descriptors != nullptr) {
free(descriptors);
}
mutex_destruct(&mutex);
}
};
struct GpioDescriptor* gpio_descriptor_acquire(
struct Device* controller,
gpio_pin_t pin_number,
enum GpioOwnerType owner
) {
check(owner != GPIO_OWNER_NONE);
auto* data = (struct GpioControllerData*)device_get_driver_data(controller);
mutex_lock(&data->mutex);
if (pin_number >= data->pin_count) {
mutex_unlock(&data->mutex);
return nullptr;
}
struct GpioDescriptor* desc = &data->descriptors[pin_number];
if (desc->owner_type != GPIO_OWNER_NONE) {
mutex_unlock(&data->mutex);
return nullptr;
}
desc->owner_type = owner;
mutex_unlock(&data->mutex);
return desc;
}
error_t gpio_controller_get_level(Device* device, gpio_pin_t pin, bool* high) {
const auto* driver = device_get_driver(device);
return GPIO_DRIVER_API(driver)->get_level(device, pin, high);
}
error_t gpio_controller_set_options(Device* device, gpio_pin_t pin, gpio_flags_t options) {
const auto* driver = device_get_driver(device);
return GPIO_DRIVER_API(driver)->set_options(device, pin, options);
}
error_t gpio_controller_get_options(Device* device, gpio_pin_t pin, gpio_flags_t* options) {
const auto* driver = device_get_driver(device);
return GPIO_DRIVER_API(driver)->get_options(device, pin, options);
error_t gpio_descriptor_release(struct GpioDescriptor* descriptor) {
descriptor->owner_type = GPIO_OWNER_NONE;
return ERROR_NONE;
}
error_t gpio_controller_get_pin_count(struct Device* device, uint32_t* count) {
const auto* driver = device_get_driver(device);
return GPIO_DRIVER_API(driver)->get_pin_count(device, count);
auto* data = (struct GpioControllerData*)device_get_driver_data(device);
*count = data->pin_count;
return ERROR_NONE;
}
error_t gpio_controller_init_descriptors(struct Device* device, uint32_t pin_count, void* controller_context) {
auto* data = new(std::nothrow) GpioControllerData(pin_count);
if (!data) return ERROR_OUT_OF_MEMORY;
if (data->init_descriptors(device, controller_context) != ERROR_NONE) {
delete data;
return ERROR_OUT_OF_MEMORY;
}
device_set_driver_data(device, data);
return ERROR_NONE;
}
error_t gpio_controller_deinit_descriptors(struct Device* device) {
auto* data = static_cast<struct GpioControllerData*>(device_get_driver_data(device));
delete data;
device_set_driver_data(device, nullptr);
return ERROR_NONE;
}
error_t gpio_descriptor_set_level(struct GpioDescriptor* descriptor, bool high) {
const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->set_level(descriptor, high);
}
error_t gpio_descriptor_get_level(struct GpioDescriptor* descriptor, bool* high) {
const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_level(descriptor, high);
}
error_t gpio_descriptor_set_flags(struct GpioDescriptor* descriptor, gpio_flags_t flags) {
const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->set_flags(descriptor, flags);
}
error_t gpio_descriptor_get_flags(struct GpioDescriptor* descriptor, gpio_flags_t* flags) {
const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_flags(descriptor, flags);
}
error_t gpio_descriptor_get_pin_number(struct GpioDescriptor* descriptor, gpio_pin_t* pin) {
*pin = descriptor->pin;
return ERROR_NONE;
}
error_t gpio_descriptor_get_native_pin_number(struct GpioDescriptor* descriptor, void* pin_number) {
const auto* driver = device_get_driver(descriptor->controller);
return GPIO_INTERNAL_API(driver)->get_native_pin_number(descriptor, pin_number);
}
error_t gpio_descriptor_get_owner_type(struct GpioDescriptor* descriptor, GpioOwnerType* owner_type) {
*owner_type = descriptor->owner_type;
return ERROR_NONE;
}
const struct DeviceType GPIO_CONTROLLER_TYPE {
+18 -8
View File
@@ -1,3 +1,4 @@
#include "tactility/dts.h"
#include <tactility/kernel_init.h>
#include <tactility/log.h>
@@ -27,7 +28,7 @@ struct Module root_module = {
.internal = nullptr
};
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct CompatibleDevice devicetree_devices[]) {
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct DtsDevice dts_devices[]) {
LOG_I(TAG, "init");
if (module_construct_add_start(&root_module) != ERROR_NONE) {
@@ -47,14 +48,23 @@ error_t kernel_init(struct Module* platform_module, struct Module* device_module
}
}
if (devicetree_devices) {
CompatibleDevice* compatible_device = devicetree_devices;
while (compatible_device->device != nullptr) {
if (device_construct_add_start(compatible_device->device, compatible_device->compatible) != ERROR_NONE) {
LOG_E(TAG, "kernel_init failed to construct device: %s (%s)", compatible_device->device->name, compatible_device->compatible);
return ERROR_RESOURCE;
if (dts_devices) {
DtsDevice* dts_device = dts_devices;
while (dts_device->device != nullptr) {
if (dts_device->status == DTS_DEVICE_STATUS_OKAY) {
if (device_construct_add_start(dts_device->device, dts_device->compatible) != ERROR_NONE) {
LOG_E(TAG, "kernel_init failed to construct+add+start device: %s (%s)", dts_device->device->name, dts_device->compatible);
return ERROR_RESOURCE;
}
} else if (dts_device->status == DTS_DEVICE_STATUS_DISABLED) {
if (device_construct_add(dts_device->device, dts_device->compatible) != ERROR_NONE) {
LOG_E(TAG, "kernel_init failed to construct+add device: %s (%s)", dts_device->device->name, dts_device->compatible);
return ERROR_RESOURCE;
}
} else {
check(false, "DTS status not implemented");
}
compatible_device++;
dts_device++;
}
}
+11 -4
View File
@@ -59,11 +59,18 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
DEFINE_MODULE_SYMBOL(driver_find_compatible),
DEFINE_MODULE_SYMBOL(driver_get_device_type),
// drivers/gpio_controller
DEFINE_MODULE_SYMBOL(gpio_controller_set_level),
DEFINE_MODULE_SYMBOL(gpio_controller_get_level),
DEFINE_MODULE_SYMBOL(gpio_controller_set_options),
DEFINE_MODULE_SYMBOL(gpio_controller_get_options),
DEFINE_MODULE_SYMBOL(gpio_descriptor_acquire),
DEFINE_MODULE_SYMBOL(gpio_descriptor_release),
DEFINE_MODULE_SYMBOL(gpio_descriptor_set_level),
DEFINE_MODULE_SYMBOL(gpio_descriptor_get_level),
DEFINE_MODULE_SYMBOL(gpio_descriptor_set_flags),
DEFINE_MODULE_SYMBOL(gpio_descriptor_get_flags),
DEFINE_MODULE_SYMBOL(gpio_descriptor_get_native_pin_number),
DEFINE_MODULE_SYMBOL(gpio_descriptor_get_pin_number),
DEFINE_MODULE_SYMBOL(gpio_descriptor_get_owner_type),
DEFINE_MODULE_SYMBOL(gpio_controller_get_pin_count),
DEFINE_MODULE_SYMBOL(gpio_controller_init_descriptors),
DEFINE_MODULE_SYMBOL(gpio_controller_deinit_descriptors),
DEFINE_MODULE_SYMBOL(GPIO_CONTROLLER_TYPE),
// drivers/i2c_controller
DEFINE_MODULE_SYMBOL(i2c_controller_read),