mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
openthread: add SPI support in Radio Co-Processor
This commit is contained in:
parent
6cac537791
commit
5a1578e692
@ -111,9 +111,11 @@ if(CONFIG_OPENTHREAD_ENABLED)
|
||||
|
||||
if(CONFIG_OPENTHREAD_RADIO_NATIVE)
|
||||
list(APPEND exclude_srcs
|
||||
"port/esp_openthread_radio_uart.cpp"
|
||||
"port/esp_uart_spinel_interface.cpp")
|
||||
elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART)
|
||||
"port/esp_openthread_radio_spinel.cpp"
|
||||
"port/esp_spi_spinel_interface.cpp"
|
||||
"port/esp_uart_spinel_interface.cpp"
|
||||
)
|
||||
elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
|
||||
list(APPEND exclude_srcs
|
||||
"port/esp_openthread_radio.c")
|
||||
endif()
|
||||
@ -166,9 +168,9 @@ idf_component_register(SRC_DIRS "${src_dirs}"
|
||||
EXCLUDE_SRCS "${exclude_srcs}"
|
||||
INCLUDE_DIRS "${public_include_dirs}"
|
||||
PRIV_INCLUDE_DIRS "${private_include_dirs}"
|
||||
REQUIRES esp_netif lwip
|
||||
REQUIRES esp_netif lwip driver
|
||||
LDFRAGMENTS linker.lf
|
||||
PRIV_REQUIRES console driver esp_event esp_partition esp_timer
|
||||
PRIV_REQUIRES console esp_event esp_partition esp_timer
|
||||
ieee802154 mbedtls spi_flash)
|
||||
|
||||
if(CONFIG_OPENTHREAD_ENABLED)
|
||||
|
@ -61,6 +61,12 @@ menu "OpenThread"
|
||||
bool "Connect via UART"
|
||||
help
|
||||
Select this to connect to a Radio Co-Processor via UART.
|
||||
|
||||
config OPENTHREAD_RADIO_SPINEL_SPI
|
||||
bool "Connect via SPI"
|
||||
help
|
||||
Select this to connect to a Radio Co-Processor via SPI.
|
||||
|
||||
endchoice
|
||||
|
||||
choice OPENTHREAD_DEVICE_TYPE
|
||||
@ -89,6 +95,22 @@ menu "OpenThread"
|
||||
radio only device.
|
||||
endchoice
|
||||
|
||||
choice OPENTHREAD_RCP_TRANSPORT
|
||||
prompt "The RCP transport type"
|
||||
depends on OPENTHREAD_RADIO
|
||||
default OPENTHREAD_RCP_UART
|
||||
|
||||
config OPENTHREAD_RCP_UART
|
||||
bool "UART RCP"
|
||||
help
|
||||
Select this to enable UART connection to host.
|
||||
|
||||
config OPENTHREAD_RCP_SPI
|
||||
bool "SPI RCP"
|
||||
help
|
||||
Select this to enable SPI connection to host.
|
||||
endchoice
|
||||
|
||||
config OPENTHREAD_CLI
|
||||
bool "Enable Openthread Command-Line Interface"
|
||||
depends on OPENTHREAD_ENABLED
|
||||
|
@ -11,6 +11,10 @@
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "esp_event_base.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "hal/uart_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -60,10 +64,33 @@ typedef struct {
|
||||
typedef struct {
|
||||
uart_port_t port; /*!< UART port number */
|
||||
uart_config_t uart_config; /*!< UART configuration, see uart_config_t docs */
|
||||
int rx_pin; /*!< UART RX pin */
|
||||
int tx_pin; /*!< UART TX pin */
|
||||
gpio_num_t rx_pin; /*!< UART RX pin */
|
||||
gpio_num_t tx_pin; /*!< UART TX pin */
|
||||
} esp_openthread_uart_config_t;
|
||||
|
||||
/**
|
||||
* @brief The spi port config for OpenThread.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
spi_host_device_t host_device; /*!< SPI host device */
|
||||
spi_dma_chan_t dma_channel; /*!< DMA channel */
|
||||
spi_bus_config_t spi_interface; /*!< SPI bus */
|
||||
spi_device_interface_config_t spi_device; /*!< SPI peripheral device */
|
||||
gpio_num_t intr_pin; /*!< SPI interrupt pin */
|
||||
} esp_openthread_spi_host_config_t;
|
||||
|
||||
/**
|
||||
* @brief The spi slave config for OpenThread.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
spi_host_device_t host_device; /*!< SPI host device */
|
||||
spi_bus_config_t bus_config; /*!< SPI bus config */
|
||||
spi_slave_interface_config_t slave_config; /*!< SPI slave config */
|
||||
gpio_num_t intr_pin; /*!< SPI interrupt pin */
|
||||
} esp_openthread_spi_slave_config_t;
|
||||
|
||||
/**
|
||||
* @brief The radio mode of OpenThread.
|
||||
*
|
||||
@ -82,6 +109,7 @@ typedef enum {
|
||||
HOST_CONNECTION_MODE_NONE = 0x0, /*!< Disable host connection */
|
||||
HOST_CONNECTION_MODE_CLI_UART = 0x1, /*!< CLI UART connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_UART = 0x2, /*!< RCP UART connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_SPI = 0x3, /*!< RCP SPI connection to the host */
|
||||
} esp_openthread_host_connection_mode_t;
|
||||
|
||||
/**
|
||||
@ -90,7 +118,10 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
esp_openthread_radio_mode_t radio_mode; /*!< The radio mode */
|
||||
union {
|
||||
esp_openthread_uart_config_t radio_uart_config; /*!< The uart configuration to RCP */
|
||||
esp_openthread_spi_host_config_t radio_spi_config; /*!< The spi configuration to RCP */
|
||||
};
|
||||
} esp_openthread_radio_config_t;
|
||||
|
||||
/**
|
||||
@ -99,7 +130,10 @@ typedef struct {
|
||||
*/
|
||||
typedef struct {
|
||||
esp_openthread_host_connection_mode_t host_connection_mode; /*!< The host connection mode */
|
||||
union {
|
||||
esp_openthread_uart_config_t host_uart_config; /*!< The uart configuration to host */
|
||||
esp_openthread_spi_slave_config_t spi_slave_config; /*!< The spi configuration to host */
|
||||
};
|
||||
} esp_openthread_host_connection_config_t;
|
||||
|
||||
/**
|
||||
|
@ -8,3 +8,6 @@ entries:
|
||||
link_metrics (noflash_text)
|
||||
mac (noflash_text)
|
||||
sub_mac (noflash_text)
|
||||
|
||||
if OPENTHREAD_RCP_SPI = y:
|
||||
ncp_spi (noflash_text)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "esp_openthread_flash.h"
|
||||
#include "esp_openthread_lock.h"
|
||||
#include "esp_openthread_radio.h"
|
||||
#include "esp_openthread_spi_slave.h"
|
||||
#include "esp_openthread_task_queue.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "esp_openthread_uart.h"
|
||||
@ -87,30 +88,40 @@ void esp_openthread_platform_workflow_unregister(const char *name)
|
||||
esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_NATIVE ||
|
||||
config->radio_config.radio_mode == RADIO_MODE_UART_RCP,
|
||||
config->radio_config.radio_mode == RADIO_MODE_UART_RCP ||
|
||||
config->radio_config.radio_mode == RADIO_MODE_SPI_RCP,
|
||||
ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "Radio mode not supported");
|
||||
ESP_RETURN_ON_FALSE(config->host_config.host_connection_mode == HOST_CONNECTION_MODE_NONE ||
|
||||
config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
|
||||
config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART,
|
||||
config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART ||
|
||||
config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI,
|
||||
ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "Host connection mode not supported");
|
||||
ESP_RETURN_ON_FALSE(!s_openthread_platform_initialized, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
|
||||
"OpenThread platform already initialized");
|
||||
|
||||
s_openthread_platform_initialized = true;
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
/* Avoid to compile flash in RADIO type device */
|
||||
#if !CONFIG_OPENTHREAD_RADIO
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY,
|
||||
config->port_config.storage_partition_name);
|
||||
|
||||
ESP_RETURN_ON_FALSE(partition, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "OpenThread storage partition not found");
|
||||
esp_openthread_flash_set_partition(partition);
|
||||
#endif
|
||||
|
||||
s_platform_config = *config;
|
||||
esp_openthread_flash_set_partition(partition);
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_lock_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_lock_init failed");
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_alarm_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_alarm_init failed");
|
||||
if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
|
||||
|
||||
if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI) {
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_spi_slave_init(config), exit, OT_PLAT_LOG_TAG,
|
||||
"esp_openthread_spi_slave_init failed");
|
||||
}else if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
|
||||
config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART) {
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_uart_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_uart_init failed");
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_task_queue_init(config), exit, OT_PLAT_LOG_TAG,
|
||||
"esp_openthread_task_queue_init failed");
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_radio_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_radio_init failed");
|
||||
@ -135,9 +146,14 @@ esp_err_t esp_openthread_platform_deinit(void)
|
||||
s_openthread_platform_initialized = false;
|
||||
esp_openthread_task_queue_deinit();
|
||||
esp_openthread_radio_deinit();
|
||||
if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART) {
|
||||
|
||||
if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI){
|
||||
esp_openthread_spi_slave_deinit();
|
||||
}else if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
|
||||
s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART) {
|
||||
esp_openthread_uart_deinit();
|
||||
}
|
||||
|
||||
esp_openthread_lock_deinit();
|
||||
esp_openthread_alarm_deinit();
|
||||
return ESP_OK;
|
||||
|
@ -736,3 +736,12 @@ otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength)
|
||||
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
OT_UNUSED_VARIABLE(aChannel);
|
||||
OT_UNUSED_VARIABLE(aMaxPower);
|
||||
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -13,6 +13,7 @@
|
||||
#include "esp_openthread_platform.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_spi_spinel_interface.hpp"
|
||||
#include "esp_uart_spinel_interface.hpp"
|
||||
#include "openthread-core-config.h"
|
||||
#include "lib/spinel/radio_spinel.hpp"
|
||||
@ -20,19 +21,30 @@
|
||||
#include "openthread/platform/diag.h"
|
||||
#include "openthread/platform/radio.h"
|
||||
|
||||
using esp::openthread::UartSpinelInterface;
|
||||
using ot::Spinel::RadioSpinel;
|
||||
|
||||
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
|
||||
using esp::openthread::UartSpinelInterface;
|
||||
static RadioSpinel<UartSpinelInterface, esp_openthread_mainloop_context_t> s_radio;
|
||||
static const char *radiouart_workflow = "radio_uart";
|
||||
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
using esp::openthread::SpiSpinelInterface;
|
||||
static RadioSpinel<SpiSpinelInterface, esp_openthread_mainloop_context_t> s_radio;
|
||||
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
|
||||
|
||||
static const char *radiospinel_workflow = "radio_spinel";
|
||||
|
||||
esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config)
|
||||
{
|
||||
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
|
||||
ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG,
|
||||
"Spinel interface init falied");
|
||||
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG,
|
||||
"Spinel interface init failed");
|
||||
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
|
||||
s_radio.Init(/*reset_radio=*/true, /*restore_dataset_from_ncp=*/false, /*skip_rcp_compatibility_check=*/false);
|
||||
return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process,
|
||||
radiouart_workflow);
|
||||
radiospinel_workflow);
|
||||
}
|
||||
|
||||
void esp_openthread_register_rcp_failure_handler(esp_openthread_rcp_failure_handler handler)
|
||||
@ -48,7 +60,7 @@ void esp_openthread_rcp_deinit(void)
|
||||
void esp_openthread_radio_deinit(void)
|
||||
{
|
||||
s_radio.Deinit();
|
||||
esp_openthread_platform_workflow_unregister(radiouart_workflow);
|
||||
esp_openthread_platform_workflow_unregister(radiospinel_workflow);
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_radio_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
|
162
components/openthread/port/esp_openthread_spi_slave.c
Normal file
162
components/openthread/port/esp_openthread_spi_slave.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* SPI Slave example, receiver (uses SPI Slave driver to communicate with sender)
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <openthread/platform/spi-slave.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_openthread_common_macro.h"
|
||||
#include "esp_openthread_task_queue.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "esp_private/cache_utils.h"
|
||||
#include "esp_private/spi_slave_internal.h"
|
||||
#include "ncp/ncp_config.h"
|
||||
#include "openthread/error.h"
|
||||
|
||||
static const char *SPI_SLAVE_TAG = "spi_slave";
|
||||
|
||||
static void *s_context = NULL;
|
||||
static uint8_t *s_prev_output_buf;
|
||||
static uint16_t s_prev_output_len;
|
||||
static uint8_t *s_prev_input_buf;
|
||||
static uint16_t s_prev_input_len;
|
||||
static bool s_request_transaction = false;
|
||||
|
||||
static esp_openthread_spi_slave_config_t s_spi_config;
|
||||
static otPlatSpiSlaveTransactionProcessCallback s_process_callback = NULL;
|
||||
static otPlatSpiSlaveTransactionCompleteCallback s_complete_callback = NULL;
|
||||
static spi_slave_transaction_t s_spi_transaction;
|
||||
|
||||
typedef struct {
|
||||
uint16_t output_buf_len;
|
||||
uint16_t input_buf_len;
|
||||
} pending_transaction_t;
|
||||
|
||||
static void IRAM_ATTR handle_spi_setup_done(spi_slave_transaction_t *trans)
|
||||
{
|
||||
if (s_request_transaction) {
|
||||
gpio_set_level(s_spi_config.intr_pin, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR handle_spi_transaction_done(spi_slave_transaction_t *trans)
|
||||
{
|
||||
gpio_set_level(s_spi_config.intr_pin, 1);
|
||||
pending_transaction_t *pending_transaction = (pending_transaction_t *)&(trans->user);
|
||||
trans->trans_len /= CHAR_BIT;
|
||||
|
||||
if (s_complete_callback &&
|
||||
s_complete_callback(s_context, (uint8_t *)trans->tx_buffer, pending_transaction->output_buf_len,
|
||||
trans->rx_buffer, pending_transaction->input_buf_len, trans->trans_len)) {
|
||||
esp_openthread_task_queue_post(s_process_callback, s_context);
|
||||
}
|
||||
trans = NULL;
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_spi_slave_init(const esp_openthread_platform_config_t *config)
|
||||
{
|
||||
s_spi_config = config->host_config.spi_slave_config;
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1 << s_spi_config.intr_pin),
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), OT_PLAT_LOG_TAG, "fail to configure SPI gpio");
|
||||
|
||||
gpio_set_pull_mode(s_spi_config.bus_config.mosi_io_num, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(s_spi_config.bus_config.sclk_io_num, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(s_spi_config.slave_config.spics_io_num, GPIO_PULLUP_ONLY);
|
||||
|
||||
/* Initialize SPI slave interface */
|
||||
s_spi_config.slave_config.post_setup_cb = handle_spi_setup_done;
|
||||
s_spi_config.slave_config.post_trans_cb = handle_spi_transaction_done;
|
||||
ESP_RETURN_ON_ERROR(spi_slave_initialize(s_spi_config.host_device, &s_spi_config.bus_config,
|
||||
&s_spi_config.slave_config, SPI_DMA_CH_AUTO),
|
||||
OT_PLAT_LOG_TAG, "fail to initialize SPI slave");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_openthread_spi_slave_deinit(void)
|
||||
{
|
||||
spi_slave_free(s_spi_config.host_device);
|
||||
s_spi_config.slave_config.post_setup_cb = NULL;
|
||||
s_spi_config.slave_config.post_trans_cb = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback,
|
||||
otPlatSpiSlaveTransactionProcessCallback aProcessCallback, void *aContext)
|
||||
{
|
||||
s_process_callback = aProcessCallback;
|
||||
s_complete_callback = aCompleteCallback;
|
||||
s_context = aContext;
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
otError IRAM_ATTR otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t aOutputBufLen, uint8_t *aInputBuf,
|
||||
uint16_t aInputBufLen, bool aRequestTransactionFlag)
|
||||
{
|
||||
esp_err_t trans_state = ESP_OK;
|
||||
pending_transaction_t *pending_transaction = NULL;
|
||||
if (aOutputBuf == NULL) {
|
||||
aOutputBuf = s_prev_output_buf;
|
||||
aOutputBufLen = s_prev_output_len;
|
||||
}
|
||||
if (aInputBuf == NULL) {
|
||||
aInputBuf = s_prev_input_buf;
|
||||
aInputBufLen = s_prev_input_len;
|
||||
}
|
||||
s_prev_output_buf = aOutputBuf;
|
||||
s_prev_output_len = aOutputBufLen;
|
||||
s_prev_input_buf = aInputBuf;
|
||||
s_prev_input_len = aInputBufLen;
|
||||
|
||||
s_spi_transaction.length = aOutputBufLen > aInputBufLen ? aOutputBufLen : aInputBufLen;
|
||||
s_spi_transaction.length *= CHAR_BIT;
|
||||
s_spi_transaction.rx_buffer = aInputBuf;
|
||||
s_spi_transaction.tx_buffer = aOutputBuf;
|
||||
|
||||
assert(sizeof(s_spi_transaction.user) >= sizeof(pending_transaction_t));
|
||||
pending_transaction = (pending_transaction_t *)&(s_spi_transaction.user);
|
||||
pending_transaction->input_buf_len = aInputBufLen;
|
||||
pending_transaction->output_buf_len = aOutputBufLen;
|
||||
s_spi_transaction.user = pending_transaction;
|
||||
s_request_transaction = aRequestTransactionFlag;
|
||||
|
||||
if ((gpio_get_level(s_spi_config.slave_config.spics_io_num) == 0)) {
|
||||
ESP_EARLY_LOGE(SPI_SLAVE_TAG, "SPI busy");
|
||||
return OT_ERROR_BUSY;
|
||||
}
|
||||
|
||||
if (xPortCanYield()) {
|
||||
spi_slave_queue_reset(s_spi_config.host_device);
|
||||
trans_state = spi_slave_queue_trans(s_spi_config.host_device, &s_spi_transaction, 0);
|
||||
} else {
|
||||
spi_slave_queue_reset_isr(s_spi_config.host_device);
|
||||
trans_state = spi_slave_queue_trans_isr(s_spi_config.host_device, &s_spi_transaction);
|
||||
}
|
||||
|
||||
if (trans_state == ESP_OK) {
|
||||
return OT_ERROR_NONE;
|
||||
} else {
|
||||
return OT_ERROR_FAILED;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
static QueueHandle_t s_task_queue = NULL;
|
||||
@ -36,7 +37,7 @@ esp_err_t esp_openthread_task_queue_init(const esp_openthread_platform_config_t
|
||||
&esp_openthread_task_queue_process, task_queue_workflow);
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
|
||||
esp_err_t IRAM_ATTR esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
|
||||
{
|
||||
task_storage_t task_storage = {
|
||||
.task = task,
|
||||
@ -44,12 +45,21 @@ esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
|
||||
};
|
||||
uint64_t val = 1;
|
||||
ssize_t ret;
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
|
||||
if (!xPortCanYield()) {
|
||||
ESP_RETURN_ON_FALSE_ISR(xQueueSendFromISR(s_task_queue, &task_storage, &task_woken), ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to post task to OpenThread task queue");
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, portMAX_DELAY), ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to post task to OpenThread task queue");
|
||||
}
|
||||
ret = write(s_task_queue_event_fd, &val, sizeof(val));
|
||||
assert(ret == sizeof(val));
|
||||
|
||||
if (task_woken) {
|
||||
portYIELD_FROM_ISR_NO_ARG();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ static int s_uart_fd;
|
||||
static uint8_t s_uart_buffer[ESP_OPENTHREAD_UART_BUFFER_SIZE];
|
||||
static const char *uart_workflow = "uart";
|
||||
|
||||
#if (CONFIG_OPENTHREAD_CLI || (CONFIG_OPENTHREAD_RADIO && CONFIG_OPENTHREAD_RCP_UART))
|
||||
otError otPlatUartEnable(void)
|
||||
{
|
||||
return OT_ERROR_NONE;
|
||||
@ -54,6 +55,7 @@ otError otPlatUartSend(const uint8_t *buf, uint16_t buf_length)
|
||||
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_openthread_uart_init_port(const esp_openthread_uart_config_t *config)
|
||||
{
|
||||
@ -117,7 +119,7 @@ esp_err_t esp_openthread_uart_process(otInstance *instance, const esp_openthread
|
||||
int rval = read(s_uart_fd, s_uart_buffer, sizeof(s_uart_buffer));
|
||||
|
||||
if (rval > 0) {
|
||||
#if CONFIG_OPENTHREAD_CLI || CONFIG_OPENTHREAD_RADIO
|
||||
#if (CONFIG_OPENTHREAD_CLI || (CONFIG_OPENTHREAD_RADIO && CONFIG_OPENTHREAD_RCP_UART))
|
||||
otPlatUartReceived(s_uart_buffer, (uint16_t)rval);
|
||||
#endif
|
||||
} else if (rval < 0) {
|
||||
|
252
components/openthread/port/esp_spi_spinel_interface.cpp
Normal file
252
components/openthread/port/esp_spi_spinel_interface.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_spi_spinel_interface.hpp"
|
||||
|
||||
#include "error.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_openthread_common_macro.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
#include <stdint.h>
|
||||
#include "common/logging.hpp"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "ncp/ncp_spi.hpp"
|
||||
|
||||
using ot::Ncp::SpiFrame;
|
||||
using ot::Spinel::SpinelInterface;
|
||||
|
||||
namespace esp {
|
||||
namespace openthread {
|
||||
|
||||
SpiSpinelInterface::SpiSpinelInterface(SpinelInterface::ReceiveFrameCallback callback, void *callback_context,
|
||||
SpinelInterface::RxFrameBuffer &frame_buffer)
|
||||
: m_event_fd(-1)
|
||||
, m_receiver_frame_callback(callback)
|
||||
, m_receiver_frame_context(callback_context)
|
||||
, m_receive_frame_buffer(frame_buffer)
|
||||
, mRcpFailureHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t SpiSpinelInterface::Init(const esp_openthread_spi_host_config_t &spi_config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(m_event_fd < 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "event fd was initialized");
|
||||
m_spi_config = spi_config;
|
||||
ESP_RETURN_ON_ERROR(spi_bus_initialize(spi_config.host_device, &spi_config.spi_interface, SPI_DMA_CH_AUTO),
|
||||
OT_PLAT_LOG_TAG, "fail to initialize spi bus");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_add_device(spi_config.host_device, &spi_config.spi_device, &m_device), OT_PLAT_LOG_TAG,
|
||||
"fail to add spi bus device");
|
||||
|
||||
gpio_config_t io_conf;
|
||||
memset(&io_conf, 0, sizeof(io_conf));
|
||||
io_conf.intr_type = GPIO_INTR_NEGEDGE;
|
||||
io_conf.pin_bit_mask = (1ULL << spi_config.intr_pin);
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), OT_PLAT_LOG_TAG, "fail to config spi gpio");
|
||||
ESP_RETURN_ON_ERROR(gpio_install_isr_service(0), OT_PLAT_LOG_TAG, "fail to install gpio isr service");
|
||||
ESP_RETURN_ON_ERROR(gpio_isr_handler_add(spi_config.intr_pin, GpioIntrHandler, this), OT_PLAT_LOG_TAG,
|
||||
"fail to add gpio isr handler");
|
||||
m_has_pending_device_frame = false;
|
||||
m_event_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
m_pending_data_len = 0;
|
||||
|
||||
ESP_RETURN_ON_FALSE(m_event_fd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG, "fail to get event fd");
|
||||
|
||||
ESP_LOGI(OT_PLAT_LOG_TAG, "spinel SPI interface initialization completed");
|
||||
|
||||
return ConductSPITransaction(true, 0, 0);
|
||||
}
|
||||
|
||||
esp_err_t SpiSpinelInterface::Deinit(void)
|
||||
{
|
||||
if (m_event_fd >= 0) {
|
||||
close(m_event_fd);
|
||||
m_event_fd = -1;
|
||||
|
||||
ESP_RETURN_ON_ERROR(gpio_isr_handler_remove(m_spi_config.intr_pin), OT_PLAT_LOG_TAG,
|
||||
"fail to remove gpio isr handler");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(m_device), OT_PLAT_LOG_TAG, "fail to remove spi bus device");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(m_spi_config.host_device), OT_PLAT_LOG_TAG, "fail to free spi bus");
|
||||
gpio_uninstall_isr_service();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
SpiSpinelInterface::~SpiSpinelInterface(void)
|
||||
{
|
||||
Deinit();
|
||||
}
|
||||
|
||||
otError SpiSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(frame, OT_ERROR_INVALID_ARGS, OT_PLAT_LOG_TAG, "empty frame");
|
||||
ESP_RETURN_ON_FALSE(length <= SpinelInterface::kMaxFrameSize, OT_ERROR_NO_BUFS, OT_PLAT_LOG_TAG,
|
||||
"send frame is too long");
|
||||
|
||||
memcpy(&m_tx_buffer[kSPIFrameHeaderSize], frame, length);
|
||||
uint16_t rx_data_size =
|
||||
length < kSmallPacketSize ? kSmallPacketSize : length; // We'll use tx_size to receive small packets piggybacked
|
||||
if (ConductSPITransaction(false, length, rx_data_size) == ESP_OK) {
|
||||
return OT_ERROR_NONE;
|
||||
} else {
|
||||
return OT_ERROR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data_size, uint16_t rx_data_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(tx_data_size <= kSPIFrameSize && rx_data_size <= kSPIFrameSize, ESP_ERR_INVALID_ARG,
|
||||
OT_PLAT_LOG_TAG, "invalid arguments");
|
||||
|
||||
SpiFrame tx_frame(m_tx_buffer);
|
||||
|
||||
tx_frame.SetHeaderFlagByte(reset);
|
||||
tx_frame.SetHeaderDataLen(tx_data_size);
|
||||
tx_frame.SetHeaderAcceptLen(rx_data_size);
|
||||
|
||||
uint8_t *rx_buffer;
|
||||
otError err = m_receive_frame_buffer.SetSkipLength(kSPIFrameHeaderSize);
|
||||
|
||||
ESP_RETURN_ON_FALSE(err == OT_ERROR_NONE, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "buffer space is insufficient");
|
||||
|
||||
rx_buffer = m_receive_frame_buffer.GetFrame() - kSPIFrameHeaderSize;
|
||||
if (m_receive_frame_buffer.GetFrameMaxLength() < rx_data_size) {
|
||||
rx_data_size = m_receive_frame_buffer.GetFrameMaxLength();
|
||||
}
|
||||
uint16_t data_size = tx_data_size > rx_data_size ? tx_data_size : rx_data_size;
|
||||
data_size += kSPIFrameHeaderSize;
|
||||
|
||||
spi_transaction_t transaction;
|
||||
memset(&transaction, 0, sizeof(transaction));
|
||||
transaction.length = data_size * CHAR_BIT;
|
||||
transaction.rxlength = (rx_data_size + kSPIFrameHeaderSize) * CHAR_BIT;
|
||||
transaction.tx_buffer = m_tx_buffer;
|
||||
transaction.rx_buffer = rx_buffer;
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG, "SPI transaction failed");
|
||||
SpiFrame rx_frame(rx_buffer);
|
||||
|
||||
if (!rx_frame.IsValid() || rx_frame.GetHeaderAcceptLen() > kSPIFrameSize ||
|
||||
rx_frame.GetHeaderDataLen() > kSPIFrameSize) {
|
||||
vTaskDelay(pdMS_TO_TICKS(15));
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG,
|
||||
"fail to retry SPI invalid transaction");
|
||||
}
|
||||
|
||||
if (rx_frame.IsResetFlagSet()) {
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "RCP Reset");
|
||||
m_receive_frame_buffer.DiscardFrame();
|
||||
return ESP_OK;
|
||||
}
|
||||
if (rx_frame.GetHeaderDataLen() == 0 && rx_frame.GetHeaderAcceptLen() == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(15));
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG,
|
||||
"fail to retry SPI empty transaction");
|
||||
}
|
||||
|
||||
if (rx_frame.GetHeaderDataLen() > 0 && rx_frame.GetHeaderDataLen() < tx_frame.GetHeaderAcceptLen()) {
|
||||
if (gpio_get_level(m_spi_config.intr_pin) == 1) {
|
||||
m_pending_data_len = 0;
|
||||
}
|
||||
if (m_receive_frame_buffer.SetLength(rx_frame.GetHeaderDataLen()) != OT_ERROR_NONE) {
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "insufficient buffer space to hold a frame of length %d...",
|
||||
rx_frame.GetHeaderDataLen());
|
||||
m_receive_frame_buffer.DiscardFrame();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
m_receiver_frame_callback(m_receiver_frame_context);
|
||||
} else {
|
||||
m_pending_data_len = 0;
|
||||
m_receive_frame_buffer.DiscardFrame();
|
||||
}
|
||||
m_pending_data_len = 0;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void SpiSpinelInterface::GpioIntrHandler(void *arg)
|
||||
{
|
||||
SpiSpinelInterface *instance = static_cast<SpiSpinelInterface *>(arg);
|
||||
instance->m_pending_data_len = SpinelInterface::kMaxFrameSize;
|
||||
uint64_t event = SpinelInterface::kMaxFrameSize;
|
||||
write(instance->m_event_fd, &event, sizeof(event));
|
||||
}
|
||||
|
||||
void SpiSpinelInterface::Update(esp_openthread_mainloop_context_t &mainloop)
|
||||
{
|
||||
if (m_pending_data_len > 0) {
|
||||
mainloop.timeout.tv_sec = 0;
|
||||
mainloop.timeout.tv_usec = 0;
|
||||
}
|
||||
FD_SET(m_event_fd, &mainloop.read_fds);
|
||||
FD_SET(m_event_fd, &mainloop.error_fds);
|
||||
if (m_event_fd > mainloop.max_fd) {
|
||||
mainloop.max_fd = m_event_fd;
|
||||
}
|
||||
}
|
||||
|
||||
void SpiSpinelInterface::Process(const esp_openthread_mainloop_context_t &mainloop)
|
||||
{
|
||||
if (FD_ISSET(m_event_fd, &mainloop.error_fds)) {
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "SPI INTR GPIO error event");
|
||||
return;
|
||||
}
|
||||
if (FD_ISSET(m_event_fd, &mainloop.read_fds)) {
|
||||
uint64_t event;
|
||||
read(m_event_fd, &event, sizeof(event));
|
||||
m_pending_data_len = SpinelInterface::kMaxFrameSize;
|
||||
|
||||
if (ConductSPITransaction(false, 0, m_pending_data_len) != ESP_OK) {
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "fail to process SPI transaction");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
otError SpiSpinelInterface::WaitForFrame(uint64_t timeout_us)
|
||||
{
|
||||
fd_set read_fds, error_fds;
|
||||
struct timeval timeout;
|
||||
uint64_t event = 0;
|
||||
if (m_pending_data_len == 0) {
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&error_fds);
|
||||
FD_SET(m_event_fd, &read_fds);
|
||||
FD_SET(m_event_fd, &error_fds);
|
||||
timeout.tv_sec = timeout_us / US_PER_S;
|
||||
timeout.tv_usec = timeout_us % US_PER_S;
|
||||
|
||||
int ret = select(m_event_fd + 1, &read_fds, NULL, &error_fds, &timeout);
|
||||
if (ret <= 0 || !FD_ISSET(m_event_fd, &read_fds)) {
|
||||
if (FD_ISSET(m_event_fd, &error_fds)) {
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "FD error!\n");
|
||||
}
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "SPI transaction timeout for %llu us, result %d\n", timeout_us, ret);
|
||||
return OT_ERROR_RESPONSE_TIMEOUT;
|
||||
}
|
||||
read(m_event_fd, &event, sizeof(event));
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_FALSE(ConductSPITransaction(false, 0, SpinelInterface::kMaxFrameSize) == ESP_OK, OT_ERROR_FAILED,
|
||||
OT_PLAT_LOG_TAG, "fail to complete SPI transaction during wait for frame");
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
void SpiSpinelInterface::OnRcpReset(void)
|
||||
{
|
||||
if (mRcpFailureHandler) {
|
||||
mRcpFailureHandler();
|
||||
ConductSPITransaction(true, 0, 0); // clear
|
||||
}
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esp
|
||||
}
|
@ -45,12 +45,15 @@ UartSpinelInterface::~UartSpinelInterface(void)
|
||||
|
||||
esp_err_t UartSpinelInterface::Init(const esp_openthread_uart_config_t &radio_uart_config)
|
||||
{
|
||||
esp_err_t error = ESP_OK;
|
||||
m_uart_rx_buffer = static_cast<uint8_t *>(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT));
|
||||
if (m_uart_rx_buffer == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return InitUart(radio_uart_config);
|
||||
error = InitUart(radio_uart_config);
|
||||
ESP_LOGI(OT_PLAT_LOG_TAG, "spinel UART interface initialization completed");
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t UartSpinelInterface::Deinit(void)
|
||||
@ -282,5 +285,13 @@ esp_err_t UartSpinelInterface::TryRecoverUart(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void UartSpinelInterface::OnRcpReset(void)
|
||||
{
|
||||
if (mRcpFailureHandler) {
|
||||
mRcpFailureHandler();
|
||||
TryRecoverUart();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esp
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_openthread.h"
|
||||
#include "esp_openthread_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This function initializes the OpenThread spinel SPI slave.
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_spi_slave_init(const esp_openthread_platform_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief This function deinitializes the OpenThread spinel SPI slave.
|
||||
*
|
||||
*/
|
||||
void esp_openthread_spi_slave_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "lib/spinel/spinel_interface.hpp"
|
||||
|
||||
namespace esp {
|
||||
namespace openthread {
|
||||
|
||||
class SpiSpinelInterface {
|
||||
public:
|
||||
/**
|
||||
* @brief This constructor of object.
|
||||
*
|
||||
* @param[in] callback Callback on frame received
|
||||
* @param[in] callback_context Callback context
|
||||
* @param[in] frame_buffer A reference to a `RxFrameBuffer` object.
|
||||
*
|
||||
*/
|
||||
SpiSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, void *callback_context,
|
||||
ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer);
|
||||
|
||||
/**
|
||||
* @brief This destructor of the object.
|
||||
*
|
||||
*/
|
||||
~SpiSpinelInterface(void);
|
||||
|
||||
/**
|
||||
* @brief This method initializes the spinel interface.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if already initialized
|
||||
* - ESP_ERR_NO_MEM if allocation has failed
|
||||
* - ESP_FAIL on failure
|
||||
*/
|
||||
esp_err_t Init(const esp_openthread_spi_host_config_t &spi_config);
|
||||
|
||||
/**
|
||||
* @brief This method deinitializes the HDLC interface.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on failure
|
||||
*/
|
||||
esp_err_t Deinit(void);
|
||||
|
||||
/**
|
||||
* @brief This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
|
||||
*
|
||||
* @param[in] frame A pointer to buffer containing the spinel frame to send.
|
||||
* @param[in] length The length (number of bytes) in the frame.
|
||||
*
|
||||
* @return
|
||||
* -OT_ERROR_NONE Successfully encoded and sent the spinel frame.
|
||||
* -OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame.
|
||||
* -OT_ERROR_FAILED Failed to send due to socket not becoming writable within `kMaxWaitTime`.
|
||||
*
|
||||
*/
|
||||
otError SendFrame(const uint8_t *frame, uint16_t length);
|
||||
|
||||
/**
|
||||
* This method waits for receiving part or all of spinel frame within specified timeout.
|
||||
*
|
||||
* @param[in] timeout_us The timeout value in microseconds.
|
||||
*
|
||||
* @return
|
||||
* -OT_ERROR_NONE Part or all of spinel frame is received.
|
||||
* -OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p timeout_us.
|
||||
*
|
||||
*/
|
||||
otError WaitForFrame(uint64_t timeout_us);
|
||||
|
||||
/**
|
||||
* This method performs spi processing to the RCP.
|
||||
*
|
||||
* @param[in] mainloop The mainloop context
|
||||
*
|
||||
*/
|
||||
void Process(const esp_openthread_mainloop_context_t &mainloop);
|
||||
|
||||
/**
|
||||
* This methods updates the mainloop context.
|
||||
*
|
||||
* @param[inout] mainloop The mainloop context.
|
||||
*
|
||||
*/
|
||||
void Update(esp_openthread_mainloop_context_t &mainloop);
|
||||
|
||||
/**
|
||||
* This methods registers the callback for RCP failure.
|
||||
*
|
||||
* @param[in] handler The RCP failure handler.
|
||||
*
|
||||
*/
|
||||
void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; }
|
||||
|
||||
/**
|
||||
* This method is called when RCP is reset to recreate the connection with it.
|
||||
* Intentionally empty.
|
||||
*
|
||||
*/
|
||||
otError ResetConnection(void) { return OT_ERROR_NONE; }
|
||||
|
||||
/**
|
||||
* This method is called when RCP failure detected and resets internal states of the interface.
|
||||
*
|
||||
*/
|
||||
void OnRcpReset(void);
|
||||
|
||||
private:
|
||||
static constexpr uint8_t kSPIFrameHeaderSize = 5;
|
||||
static constexpr uint16_t kSPIFrameSize = ot::Spinel::SpinelInterface::kMaxFrameSize + kSPIFrameHeaderSize;
|
||||
static constexpr uint8_t kSmallPacketSize = 32;
|
||||
static constexpr uint16_t kSPIDataEvent = 1;
|
||||
|
||||
static void GpioIntrHandler(void *arg);
|
||||
esp_err_t ConductSPITransaction(bool reset, uint16_t tx_data_size, uint16_t rx_data_size);
|
||||
|
||||
esp_openthread_spi_host_config_t m_spi_config;
|
||||
uint8_t m_tx_buffer[kSPIFrameSize];
|
||||
int m_event_fd;
|
||||
volatile uint16_t m_pending_data_len;
|
||||
|
||||
ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback;
|
||||
void *m_receiver_frame_context;
|
||||
ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer;
|
||||
bool m_has_pending_device_frame;
|
||||
|
||||
spi_device_handle_t m_device;
|
||||
|
||||
esp_openthread_rcp_failure_handler mRcpFailureHandler;
|
||||
};
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esp
|
@ -108,12 +108,7 @@ public:
|
||||
*/
|
||||
void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; }
|
||||
|
||||
void OnRcpReset(void)
|
||||
{
|
||||
if (mRcpFailureHandler) {
|
||||
mRcpFailureHandler();
|
||||
}
|
||||
}
|
||||
void OnRcpReset(void);
|
||||
|
||||
otError ResetConnection(void) { return OT_ERROR_NONE; }
|
||||
|
||||
|
@ -224,7 +224,7 @@
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT
|
||||
#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 1
|
||||
#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 3
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -88,7 +88,7 @@
|
||||
* Define to 1 to enable NCP SPI support.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_CONFIG_NCP_SPI_ENABLE 0
|
||||
#define OPENTHREAD_CONFIG_NCP_SPI_ENABLE CONFIG_OPENTHREAD_RCP_SPI
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
|
||||
@ -104,7 +104,7 @@
|
||||
* Define to 1 to enable NCP HDLC support.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE CONFIG_OPENTHREAD_RCP_UART
|
||||
|
||||
/**
|
||||
* @def PACKAGE_NAME
|
||||
|
@ -21,7 +21,7 @@
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_NATIVE, \
|
||||
}
|
||||
#else
|
||||
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_UART
|
||||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_UART_RCP, \
|
||||
@ -41,7 +41,32 @@
|
||||
.tx_pin = 5, \
|
||||
}, \
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_SPI_RCP, \
|
||||
.radio_spi_config = { \
|
||||
.host_device = SPI2_HOST, \
|
||||
.dma_channel = 2, \
|
||||
.spi_interface = \
|
||||
{ \
|
||||
.mosi_io_num = 11, \
|
||||
.sclk_io_num = 12, \
|
||||
.miso_io_num = 13, \
|
||||
}, \
|
||||
.spi_device = \
|
||||
{ \
|
||||
.cs_ena_pretrans = 2, \
|
||||
.input_delay_ns = 100, \
|
||||
.mode = 0, \
|
||||
.clock_speed_hz = 2500 * 1000, \
|
||||
.spics_io_num = 10, \
|
||||
.queue_size = 5, \
|
||||
}, \
|
||||
.intr_pin = 8, \
|
||||
}, \
|
||||
}
|
||||
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C2 && CONFIG_XTAL_FREQ_26
|
||||
#define HOST_BAUD_RATE 74880
|
||||
|
@ -20,6 +20,7 @@
|
||||
.radio_mode = RADIO_MODE_NATIVE, \
|
||||
}
|
||||
|
||||
#if CONFIG_OPENTHREAD_RCP_UART
|
||||
#if CONFIG_OPENTHREAD_UART_PIN_MANUAL
|
||||
#define OPENTHREAD_RCP_UART_RX_PIN CONFIG_OPENTHREAD_UART_RX_PIN
|
||||
#define OPENTHREAD_RCP_UART_TX_PIN CONFIG_OPENTHREAD_UART_TX_PIN
|
||||
@ -47,6 +48,30 @@
|
||||
.tx_pin = OPENTHREAD_RCP_UART_TX_PIN, \
|
||||
}, \
|
||||
}
|
||||
#else // CONFIG_OPENTHREAD_RCP_SPI
|
||||
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
|
||||
{ \
|
||||
.host_connection_mode = HOST_CONNECTION_MODE_RCP_SPI, \
|
||||
.spi_slave_config = { \
|
||||
.host_device = SPI2_HOST, \
|
||||
.bus_config = { \
|
||||
.mosi_io_num = 3, \
|
||||
.miso_io_num = 1, \
|
||||
.sclk_io_num = 0, \
|
||||
.quadhd_io_num = -1, \
|
||||
.quadwp_io_num = -1, \
|
||||
.isr_cpu_id = INTR_CPU_ID_0, \
|
||||
}, \
|
||||
.slave_config = { \
|
||||
.mode = 0, \
|
||||
.spics_io_num = 2, \
|
||||
.queue_size = 3, \
|
||||
.flags = 0, \
|
||||
}, \
|
||||
.intr_pin = 9, \
|
||||
}, \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
|
||||
{ \
|
||||
|
@ -64,5 +64,5 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
|
||||
xTaskCreate(ot_task_worker, "ot_rcp_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL);
|
||||
xTaskCreate(ot_task_worker, "ot_rcp_main", 3072, xTaskGetCurrentTaskHandle(), 5, NULL);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user