From 71ce94e0edab7d9d16e362aac33cb90694f65d40 Mon Sep 17 00:00:00 2001 From: Markus Becker Date: Tue, 17 Mar 2020 17:46:03 +0100 Subject: [PATCH] net: openthread: Add UART platform backend for NCP Currently based on CDC-ACM. Can possibly be used with plain UART as well. Signed-off-by: Markus Becker --- subsys/net/l2/openthread/Kconfig | 64 +++++ .../lib/openthread/platform/CMakeLists.txt | 1 + .../platform/openthread-core-zephyr-config.h | 8 + .../lib/openthread/platform/platform-zephyr.h | 8 + subsys/net/lib/openthread/platform/uart.c | 219 ++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 subsys/net/lib/openthread/platform/uart.c diff --git a/subsys/net/l2/openthread/Kconfig b/subsys/net/l2/openthread/Kconfig index 13f4c2e29e3..d0a041e846a 100644 --- a/subsys/net/l2/openthread/Kconfig +++ b/subsys/net/l2/openthread/Kconfig @@ -201,6 +201,70 @@ config OPENTHREAD_DHCP6_SERVER help Enable DHCPv6 server capability in OpenThread stack +config OPENTHREAD_SLAAC + bool "SLAAC support" + help + Enable SLAAC capability in OpenThread stack + +config OPENTHREAD_ENABLE_SERVICE + bool "Service support" + help + Enable service capability in OpenThread stack + +config OPENTHREAD_RAW + bool "Raw Link support" + help + Enable raw link support in OpenThread stack + +config OPENTHREAD_BORDER_AGENT + bool "Border Agent support" + help + Enable border agent support in OpenThread stack + +config OPENTHREAD_BORDER_ROUTER + bool "Border Router support" + help + Enable border router support in OpenThread stack + +config OPENTHREAD_NCP + bool "Network Co-Processor" + select RING_BUFFER + select UART_INTERRUPT_DRIVEN + help + Enable NCP in OpenThread stack + +config OPENTHREAD_NCP_RADIO + bool "Network Co-Processor as Radio" + depends on OPENTHREAD_NCP + help + Enable NCP in OpenThread stack as radio-only + + +config OPENTHREAD_NCP_SPINEL_ON_UART_DEV_NAME + string "UART device to use for NCP SPINEL" + default "UART_0" + depends on OPENTHREAD_NCP + help + UART device to use for NCP SPINEL + +config OPENTHREAD_NCP_UART_RING_BUFFER_SIZE + int "Set NCP UART ring buffer size" + default 4096 + depends on OPENTHREAD_NCP + help + TX buffer size for the OpenThread NCP UART + +config OPENTHREAD_NCP_SPINEL_ON_UART_ACM + bool "Run SPINEL over USB-ACM" + depends on OPENTHREAD_NCP && USB_CDC_ACM + help + Is the SPINEL device a USB-CDC-ACM device + +config OPENTHREAD_UDP_FORWARD + bool "UDP forward support" + help + Enable UDP forward support in OpenThread stack + config OPENTHREAD_PLATFORM_INFO string "Platform information for OpenThread" default "ZEPHYR" diff --git a/subsys/net/lib/openthread/platform/CMakeLists.txt b/subsys/net/lib/openthread/platform/CMakeLists.txt index 51a056452d0..2c9bd472c1b 100644 --- a/subsys/net/lib/openthread/platform/CMakeLists.txt +++ b/subsys/net/lib/openthread/platform/CMakeLists.txt @@ -10,6 +10,7 @@ zephyr_library_sources( radio.c settings.c spi.c + uart.c ) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_SHELL shell.c) diff --git a/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h b/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h index 8a00026be4a..75c95e4e0e5 100644 --- a/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h +++ b/subsys/net/lib/openthread/platform/openthread-core-zephyr-config.h @@ -142,4 +142,12 @@ */ #define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0 +/** + * @def OPENTHREAD_CONFIG_NCP_BUFFER_SIZE + * + * The size of the NCP buffers. + * + */ +#define OPENTHREAD_CONFIG_NCP_BUFFER_SIZE 2048 + #endif /* OPENTHREAD_CORE_NRF52840_CONFIG_H_ */ diff --git a/subsys/net/lib/openthread/platform/platform-zephyr.h b/subsys/net/lib/openthread/platform/platform-zephyr.h index 54c2422bdfb..e5abbe6d559 100644 --- a/subsys/net/lib/openthread/platform/platform-zephyr.h +++ b/subsys/net/lib/openthread/platform/platform-zephyr.h @@ -45,6 +45,14 @@ void platformRadioInit(void); */ void platformRadioProcess(otInstance *aInstance); +/** + * This function performs UART driver processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void platformUartProcess(otInstance *aInstance); + /** * Get current channel from radio driver. * diff --git a/subsys/net/lib/openthread/platform/uart.c b/subsys/net/lib/openthread/platform/uart.c new file mode 100644 index 00000000000..9a25d931f73 --- /dev/null +++ b/subsys/net/lib/openthread/platform/uart.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2020 Tridonic GmbH & Co KG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_LEVEL CONFIG_OPENTHREAD_LOG_LEVEL +#define LOG_MODULE_NAME net_otPlat_uart + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include + +#include + +#include +#include + +#ifdef CONFIG_OPENTHREAD_NCP_SPINEL_ON_UART_ACM +#include +#endif + +#include +#include + +#include "platform-zephyr.h" + +struct openthread_uart { + struct ring_buf *tx_ringbuf; + struct ring_buf *rx_ringbuf; + struct device *dev; + atomic_t tx_busy; + atomic_t tx_finished; +}; + +#define OT_UART_DEFINE(_name, _ringbuf_size) \ + RING_BUF_DECLARE(_name##_tx_ringbuf, _ringbuf_size); \ + RING_BUF_DECLARE(_name##_rx_ringbuf, _ringbuf_size); \ + static struct openthread_uart _name = { \ + .tx_ringbuf = &_name##_tx_ringbuf, \ + .rx_ringbuf = &_name##_rx_ringbuf, \ + } + +OT_UART_DEFINE(ot_uart, CONFIG_OPENTHREAD_NCP_UART_RING_BUFFER_SIZE); + +#define RX_FIFO_SIZE 128 + +static void uart_rx_handle(void) +{ + u8_t *data; + u32_t len; + u32_t rd_len; + bool new_data = false; + + do { + len = ring_buf_put_claim( + ot_uart.rx_ringbuf, &data, + ot_uart.rx_ringbuf->size); + if (len > 0) { + rd_len = uart_fifo_read( + ot_uart.dev, data, len); + + if (rd_len > 0) { + new_data = true; + } + + int err = ring_buf_put_finish( + ot_uart.rx_ringbuf, rd_len); + (void)err; + __ASSERT_NO_MSG(err == 0); + } else { + u8_t dummy; + + /* No space in the ring buffer - consume byte. */ + LOG_WRN("RX ring buffer full."); + + rd_len = uart_fifo_read( + ot_uart.dev, &dummy, 1); + } + } while (rd_len && (rd_len == len)); + + if (new_data) { + otSysEventSignalPending(); + } +} + +static void uart_tx_handle(void) +{ + u32_t len; + const u8_t *data; + + len = ring_buf_get_claim(ot_uart.tx_ringbuf, (u8_t **)&data, + ot_uart.tx_ringbuf->size); + if (len) { + int err; + + len = uart_fifo_fill(ot_uart.dev, data, len); + err = ring_buf_get_finish(ot_uart.tx_ringbuf, len); + (void)err; + __ASSERT_NO_MSG(err == 0); + } else { + uart_irq_tx_disable(ot_uart.dev); + ot_uart.tx_busy = 0; + atomic_set(&(ot_uart.tx_finished), 1); + otSysEventSignalPending(); + } +} + +static void uart_callback(void *user_data) +{ + ARG_UNUSED(user_data); + + while (uart_irq_update(ot_uart.dev) && + uart_irq_is_pending(ot_uart.dev)) { + + if (uart_irq_rx_ready(ot_uart.dev)) { + uart_rx_handle(); + } + + if (uart_irq_tx_ready(ot_uart.dev)) { + uart_tx_handle(); + } + } +} + +void platformUartProcess(otInstance *aInstance) +{ + u32_t len = 0; + const u8_t *data; + + /* Process UART RX */ + while ((len = ring_buf_get_claim( + ot_uart.rx_ringbuf, + (u8_t **)&data, + ot_uart.rx_ringbuf->size)) > 0) { + int err; + + otPlatUartReceived(data, len); + err = ring_buf_get_finish( + ot_uart.rx_ringbuf, + len); + (void)err; + __ASSERT_NO_MSG(err == 0); + } + + /* Process UART TX */ + if (ot_uart.tx_finished) { + LOG_DBG("UART TX done"); + otPlatUartSendDone(); + ot_uart.tx_finished = 0; + } +}; + +otError otPlatUartEnable(void) +{ + ot_uart.dev = device_get_binding( + CONFIG_OPENTHREAD_NCP_SPINEL_ON_UART_DEV_NAME); + + if ((&ot_uart)->dev == NULL) { + LOG_ERR("UART device not found"); + return OT_ERROR_FAILED; + } + +#ifdef CONFIG_OPENTHREAD_NCP_SPINEL_ON_UART_ACM + int ret = usb_enable(NULL); + u32_t baudrate = 0U; + + if (ret != 0) { + LOG_ERR("Failed to enable USB"); + return OT_ERROR_FAILED; + } + + LOG_INF("Wait for host to settle"); + k_sleep(MSEC_PER_SEC * 1); + + ret = uart_line_ctrl_get(ot_uart.dev, + UART_LINE_CTRL_BAUD_RATE, + &baudrate); + if (ret) { + LOG_WRN("Failed to get baudrate, ret code %d", ret); + } else { + LOG_INF("Baudrate detected: %d", baudrate); + } +#endif + + uart_irq_callback_user_data_set( + ot_uart.dev, + uart_callback, + (void *)&ot_uart); + uart_irq_rx_enable(ot_uart.dev); + + return OT_ERROR_NONE; +}; + +otError otPlatUartDisable(void) +{ + /* TODO: uninit UART */ + LOG_WRN("%s not implemented.", __func__); + return OT_ERROR_NOT_IMPLEMENTED; +}; + + +otError otPlatUartSend(const u8_t *aBuf, u16_t aBufLength) +{ + size_t cnt = ring_buf_put(ot_uart.tx_ringbuf, aBuf, aBufLength); + + if (atomic_set(&(ot_uart.tx_busy), 1) == 0) { + uart_irq_tx_enable(ot_uart.dev); + } + + if (cnt == aBufLength) { + return OT_ERROR_NONE; + } else { + return OT_ERROR_BUSY; + } +};