diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml new file mode 100644 index 00000000..6a47d9ec --- /dev/null +++ b/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml @@ -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 diff --git a/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h b/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h new file mode 100644 index 00000000..8d71c662 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(esp32_grove, struct Esp32GroveConfig) + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h new file mode 100644 index 00000000..073b2844 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include +#include + +#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 diff --git a/Platforms/platform-esp32/source/drivers/esp32_grove.cpp b/Platforms/platform-esp32/source/drivers/esp32_grove.cpp new file mode 100644 index 00000000..6322296b --- /dev/null +++ b/Platforms/platform-esp32/source/drivers/esp32_grove.cpp @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include + +#include "../../../../TactilityKernel/include/tactility/check.h" + +#include +#include +#include +#include +#include +#include + +#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" diff --git a/Platforms/platform-esp32/source/module.cpp b/Platforms/platform-esp32/source/module.cpp index b734841d..62cdcac5 100644 --- a/Platforms/platform-esp32/source/module.cpp +++ b/Platforms/platform-esp32/source/module.cpp @@ -19,6 +19,7 @@ extern Driver esp32_sdmmc_driver; #endif extern Driver esp32_spi_driver; extern Driver esp32_uart_driver; +extern Driver esp32_grove_driver; #if defined(CONFIG_BT_NIMBLE_ENABLED) extern Driver esp32_bluetooth_driver; extern Driver esp32_ble_serial_driver; @@ -43,6 +44,7 @@ static error_t start() { #endif check(driver_construct_add(&esp32_spi_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) check(driver_construct_add(&esp32_bluetooth_driver) == ERROR_NONE); check(driver_construct_add(&esp32_ble_serial_driver) == ERROR_NONE); @@ -81,6 +83,7 @@ static error_t stop() { #endif check(driver_remove_destruct(&esp32_spi_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_uart_driver) == ERROR_NONE); + check(driver_remove_destruct(&esp32_grove_driver) == ERROR_NONE); return ERROR_NONE; } diff --git a/TactilityKernel/include/tactility/drivers/grove.h b/TactilityKernel/include/tactility/drivers/grove.h new file mode 100644 index 00000000..0e4f41fd --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/grove.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @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 diff --git a/TactilityKernel/source/drivers/grove.cpp b/TactilityKernel/source/drivers/grove.cpp new file mode 100644 index 00000000..37c1cb79 --- /dev/null +++ b/TactilityKernel/source/drivers/grove.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#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" +}; + +}