Grove driver implemented

This commit is contained in:
Ken Van Hoeylandt 2026-06-10 22:42:06 +02:00
parent 8dd9bee8d0
commit 5f52372b82
7 changed files with 358 additions and 0 deletions

View File

@ -0,0 +1,29 @@
description: ESP32 Grove Port
compatible: "espressif,esp32-grove"
properties:
defaultMode:
type: int
required: true
description: "One of enum Esp32GroveMode"
pinSclTx:
type: phandle-array
required: true
description: SCL (I2C) or TX (UART) pin
pinSdaRx:
type: phandle-array
required: true
description: SDA (I2C) or RX (UART) pin
uartPort:
type: int
required: true
description: UART port number
i2cPort:
type: int
required: true
description: I2C port number
i2cClockFrequency:
type: int
required: true
description: I2C clock frequency

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <tactility/drivers/esp32_grove.h>
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_DEVICETREE(esp32_grove, struct Esp32GroveConfig)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <driver/uart.h>
#include <hal/i2c_types.h>
#include <tactility/drivers/gpio.h>
#include <tactility/drivers/grove.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Esp32GroveConfig {
enum GroveMode defaultMode;
struct GpioPinSpec pinSclTx;
struct GpioPinSpec pinSdaRx;
uart_port_t uartPort;
i2c_port_t i2cPort;
uint32_t i2cClockFrequency;
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,209 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/device.h>
#include <tactility/driver.h>
#include <tactility/drivers/esp32_grove.h>
#include "../../../../TactilityKernel/include/tactility/check.h"
#include <cstdio>
#include <cstring>
#include <new>
#include <tactility/drivers/esp32_i2c.h>
#include <tactility/drivers/esp32_uart.h>
#include <tactility/log.h>
#define TAG "esp32_grove"
struct Esp32GroveInternal {
struct Device* child_device = nullptr;
void* child_config = nullptr;
char* child_name = nullptr;
enum GroveMode current_mode = GROVE_MODE_DISABLED;
};
#define GET_CONFIG(device) ((const struct Esp32GroveConfig*)device->config)
#define GET_DATA(device) ((struct Esp32GroveInternal*)device_get_driver_data(device))
extern "C" {
static error_t stop_child(struct Device* device) {
auto* data = GET_DATA(device);
if (!data) return ERROR_NONE;
if (data->child_device) {
if (data->child_device->internal) {
if (device_is_added(data->child_device)) {
if (device_is_ready(data->child_device)) {
if (device_stop(data->child_device) != ERROR_NONE) {
LOG_E(TAG, "%s: failed to stop child device", device->name);
return ERROR_RESOURCE;
}
}
if (device_remove(data->child_device) != ERROR_NONE) {
LOG_E(TAG, "%s: failed to remove child device", device->name);
return ERROR_RESOURCE;
}
}
check(device_destruct(data->child_device) == ERROR_NONE);
}
delete data->child_device;
data->child_device = nullptr;
}
if (data->child_config) {
if (data->current_mode == GROVE_MODE_UART) {
delete (struct Esp32UartConfig*)data->child_config;
} else if (data->current_mode == GROVE_MODE_I2C) {
delete (struct Esp32I2cConfig*)data->child_config;
}
data->child_config = nullptr;
}
delete[] data->child_name;
data->child_name = nullptr;
data->current_mode = GROVE_MODE_DISABLED;
return ERROR_NONE;
}
static error_t start_child(struct Device* device, enum GroveMode mode) {
const auto* config = GET_CONFIG(device);
auto* data = GET_DATA(device);
if (mode == GROVE_MODE_DISABLED) {
LOG_I(TAG, "%s: Grove port disabled", device->name);
data->current_mode = GROVE_MODE_DISABLED;
return ERROR_NONE;
}
data->child_device = new(std::nothrow) struct Device();
if (!data->child_device) {
return ERROR_OUT_OF_MEMORY;
}
std::memset(data->child_device, 0, sizeof(struct Device));
size_t name_len = std::strlen(device->name) + 10;
data->child_name = new(std::nothrow) char[name_len];
if (!data->child_name) {
delete data->child_device;
data->child_device = nullptr;
return ERROR_OUT_OF_MEMORY;
}
std::snprintf(data->child_name, name_len, "%s_child", device->name);
data->child_device->name = data->child_name;
data->child_device->parent = device;
const char* compatible = nullptr;
if (mode == GROVE_MODE_UART) {
auto* uart_cfg = new(std::nothrow) struct Esp32UartConfig();
if (!uart_cfg) {
delete[] data->child_name;
data->child_name = nullptr;
delete data->child_device;
data->child_device = nullptr;
return ERROR_OUT_OF_MEMORY;
}
std::memset(uart_cfg, 0, sizeof(struct Esp32UartConfig));
uart_cfg->port = config->uartPort;
uart_cfg->pin_tx = config->pinSclTx;
uart_cfg->pin_rx = config->pinSdaRx;
uart_cfg->pin_cts = GPIO_PIN_SPEC_NONE;
uart_cfg->pin_rts = GPIO_PIN_SPEC_NONE;
data->child_config = uart_cfg;
compatible = "espressif,esp32-uart";
LOG_I(TAG, "%s: starting UART mode on port %d", device->name, (int)uart_cfg->port);
} else if (mode == GROVE_MODE_I2C) {
auto* i2c_cfg = new(std::nothrow) struct Esp32I2cConfig();
if (!i2c_cfg) {
delete[] data->child_name;
data->child_name = nullptr;
delete data->child_device;
data->child_device = nullptr;
return ERROR_OUT_OF_MEMORY;
}
std::memset(i2c_cfg, 0, sizeof(struct Esp32I2cConfig));
i2c_cfg->port = config->i2cPort;
i2c_cfg->clockFrequency = config->i2cClockFrequency;
i2c_cfg->pinSda = config->pinSdaRx;
i2c_cfg->pinScl = config->pinSclTx;
data->child_config = i2c_cfg;
compatible = "espressif,esp32-i2c";
LOG_I(TAG, "%s: starting I2C mode on port %d", device->name, (int)i2c_cfg->port);
} else {
LOG_E(TAG, "%s: unknown mode %d", device->name, mode);
delete[] data->child_name;
data->child_name = nullptr;
delete data->child_device;
data->child_device = nullptr;
return ERROR_INVALID_ARGUMENT;
}
data->child_device->config = data->child_config;
error_t err = device_construct_add_start(data->child_device, compatible);
if (err != ERROR_NONE) {
LOG_E(TAG, "%s: failed to start child device: %d", device->name, err);
stop_child(device);
return err;
}
data->current_mode = mode;
return ERROR_NONE;
}
static error_t start_device(struct Device* device) {
const auto* config = GET_CONFIG(device);
auto* data = new(std::nothrow) Esp32GroveInternal();
if (!data) return ERROR_OUT_OF_MEMORY;
device_set_driver_data(device, data);
return start_child(device, config->defaultMode);
}
static error_t stop_device(struct Device* device) {
auto* data = GET_DATA(device);
if (!data) return ERROR_NONE;
stop_child(device);
delete data;
device_set_driver_data(device, nullptr);
return ERROR_NONE;
}
static error_t esp32_grove_set_mode(struct Device* device, enum GroveMode mode) {
auto* data = GET_DATA(device);
if (data->current_mode == mode) return ERROR_NONE;
error_t err = stop_child(device);
if (err != ERROR_NONE) return err;
return start_child(device, mode);
}
static error_t esp32_grove_get_mode(struct Device* device, enum GroveMode* mode) {
auto* data = GET_DATA(device);
*mode = data->current_mode;
return ERROR_NONE;
}
static const struct GroveApi esp32_grove_api = {
.set_mode = esp32_grove_set_mode,
.get_mode = esp32_grove_get_mode
};
extern struct Module platform_esp32_module;
Driver esp32_grove_driver = {
.name = "esp32_grove",
.compatible = (const char*[]) { "espressif,esp32-grove", nullptr },
.start_device = start_device,
.stop_device = stop_device,
.api = &esp32_grove_api,
.device_type = &GROVE_TYPE,
.owner = &platform_esp32_module,
.internal = nullptr
};
} // extern "C"

View File

@ -19,6 +19,7 @@ extern Driver esp32_sdmmc_driver;
#endif #endif
extern Driver esp32_spi_driver; extern Driver esp32_spi_driver;
extern Driver esp32_uart_driver; extern Driver esp32_uart_driver;
extern Driver esp32_grove_driver;
#if defined(CONFIG_BT_NIMBLE_ENABLED) #if defined(CONFIG_BT_NIMBLE_ENABLED)
extern Driver esp32_bluetooth_driver; extern Driver esp32_bluetooth_driver;
extern Driver esp32_ble_serial_driver; extern Driver esp32_ble_serial_driver;
@ -43,6 +44,7 @@ static error_t start() {
#endif #endif
check(driver_construct_add(&esp32_spi_driver) == ERROR_NONE); check(driver_construct_add(&esp32_spi_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_uart_driver) == ERROR_NONE); check(driver_construct_add(&esp32_uart_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_grove_driver) == ERROR_NONE);
#if defined(CONFIG_BT_NIMBLE_ENABLED) #if defined(CONFIG_BT_NIMBLE_ENABLED)
check(driver_construct_add(&esp32_bluetooth_driver) == ERROR_NONE); check(driver_construct_add(&esp32_bluetooth_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_ble_serial_driver) == ERROR_NONE); check(driver_construct_add(&esp32_ble_serial_driver) == ERROR_NONE);
@ -81,6 +83,7 @@ static error_t stop() {
#endif #endif
check(driver_remove_destruct(&esp32_spi_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_spi_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_uart_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_uart_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_grove_driver) == ERROR_NONE);
return ERROR_NONE; return ERROR_NONE;
} }

View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <tactility/device.h>
#include <tactility/error.h>
/**
* @brief Grove Port modes
*/
enum GroveMode {
GROVE_MODE_DISABLED = 0,
GROVE_MODE_UART = 1,
GROVE_MODE_I2C = 2
};
/**
* @brief API for Grove drivers.
*/
struct GroveApi {
/**
* @brief Sets the Grove port mode.
* @param[in] device the Grove device
* @param[in] mode the new mode
* @return ERROR_NONE on success
*/
error_t (*set_mode)(struct Device* device, enum GroveMode mode);
/**
* @brief Gets the current Grove port mode.
* @param[in] device the Grove device
* @param[out] mode pointer to store the current mode
* @return ERROR_NONE on success
*/
error_t (*get_mode)(struct Device* device, enum GroveMode* mode);
};
/**
* @brief Sets the Grove port mode using the specified device.
*/
error_t grove_set_mode(struct Device* device, enum GroveMode mode);
/**
* @brief Gets the current Grove port mode using the specified device.
*/
error_t grove_get_mode(struct Device* device, enum GroveMode* mode);
extern const struct DeviceType GROVE_TYPE;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/drivers/grove.h>
#include <tactility/device.h>
#define GROVE_DRIVER_API(driver) ((struct GroveApi*)driver->api)
extern "C" {
error_t grove_set_mode(struct Device* device, enum GroveMode mode) {
const auto* driver = device_get_driver(device);
return GROVE_DRIVER_API(driver)->set_mode(device, mode);
}
error_t grove_get_mode(struct Device* device, enum GroveMode* mode) {
const auto* driver = device_get_driver(device);
return GROVE_DRIVER_API(driver)->get_mode(device, mode);
}
const struct DeviceType GROVE_TYPE {
.name = "grove"
};
}