blob: fea39030b65718b39f35d9824b1be6d5b45f210c [file]
/*
* Copyright (c) 2020 Tridonic GmbH & Co KG
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LEVEL CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL
#define LOG_MODULE_NAME net_otPlat_uart
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <zephyr/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/usb/usb_device.h>
#include <openthread/ncp.h>
#include <openthread-system.h>
#include <utils/uart.h>
#include "platform-zephyr.h"
#if defined(CONFIG_OPENTHREAD_COPROCESSOR_UART_ASYNC)
#define OT_UART_ASYNC
#endif
struct openthread_uart {
struct ring_buf *rx_ringbuf;
const struct device *dev;
atomic_t tx_busy;
atomic_t tx_finished;
};
#define OT_UART_DEFINE(_name, _ringbuf_size) \
RING_BUF_DECLARE(_name##_rx_ringbuf, _ringbuf_size); \
static struct openthread_uart _name = { \
.rx_ringbuf = &_name##_rx_ringbuf, \
}
OT_UART_DEFINE(ot_uart, CONFIG_OPENTHREAD_COPROCESSOR_UART_RING_BUFFER_SIZE);
static bool is_panic_mode;
static bool uart_enabled;
#ifndef OT_UART_ASYNC
#define RX_FIFO_SIZE 128
static const uint8_t *write_buffer;
static uint16_t write_length;
static void uart_rx_handle(const struct device *dev)
{
uint8_t *data;
uint32_t len;
uint32_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(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 {
uint8_t dummy;
/* No space in the ring buffer - consume byte. */
LOG_WRN("RX ring buffer full.");
rd_len = uart_fifo_read(dev, &dummy, 1);
}
} while (rd_len && (rd_len == len));
if (new_data) {
otSysEventSignalPending();
}
}
static void uart_tx_handle(const struct device *dev)
{
uint32_t len;
if (write_length) {
len = uart_fifo_fill(dev, write_buffer, write_length);
write_buffer += len;
write_length -= len;
} else {
uart_irq_tx_disable(dev);
atomic_clear(&ot_uart.tx_busy);
atomic_set(&ot_uart.tx_finished, 1);
otSysEventSignalPending();
}
}
static void uart_callback(const struct device *dev, void *user_data)
{
ARG_UNUSED(user_data);
while (true) {
uart_irq_update(dev);
if (uart_irq_is_pending(dev) <= 0) {
return;
}
if (uart_irq_rx_ready(dev)) {
uart_rx_handle(dev);
}
if (uart_irq_tx_ready(dev) &&
atomic_get(&ot_uart.tx_busy) == 1) {
uart_tx_handle(dev);
}
}
}
#else
#define RX_TIMEOUT 100
/* UART with async rx buffer */
static uint8_t rx_buf[CONFIG_OPENTHREAD_COPROCESSOR_UART_RX_BUFFER_SIZE];
/* Async API */
static void uart_async_cb(const struct device *dev,
struct uart_event *evt,
void *user_data)
{
ARG_UNUSED(dev);
ARG_UNUSED(user_data);
switch (evt->type) {
case UART_RX_RDY: {
size_t len = ring_buf_put(ot_uart.rx_ringbuf,
evt->data.rx.buf + evt->data.rx.offset,
evt->data.rx.len);
if (len > 0) {
otSysEventSignalPending();
}
if (len < evt->data.rx.len) {
LOG_WRN("RX ring buffer full.");
}
break;
}
case UART_RX_DISABLED:
if (uart_enabled) {
uart_rx_enable(dev, rx_buf, sizeof(rx_buf), RX_TIMEOUT);
}
break;
case UART_TX_DONE:
case UART_TX_ABORTED:
atomic_clear(&ot_uart.tx_busy);
atomic_set(&ot_uart.tx_finished, 1);
otSysEventSignalPending();
break;
default:
break;
}
}
#endif
void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength)
{
otNcpHdlcReceive(aBuf, aBufLength);
}
void otPlatUartSendDone(void)
{
otNcpHdlcSendDone();
}
void platformUartProcess(otInstance *aInstance)
{
uint32_t len = 0;
const uint8_t *data;
/* Process UART RX */
while ((len = ring_buf_get_claim(
ot_uart.rx_ringbuf,
(uint8_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 (atomic_get(&ot_uart.tx_finished)) {
LOG_DBG("UART TX done");
otPlatUartSendDone();
atomic_clear(&ot_uart.tx_finished);
}
}
otError otPlatUartEnable(void)
{
ot_uart.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ot_uart));
if (!device_is_ready(ot_uart.dev)) {
LOG_ERR("UART device not ready");
return OT_ERROR_FAILED;
}
if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) {
/* Data Carrier Detect Modem - mark connection as established */
(void)uart_line_ctrl_set(ot_uart.dev, UART_LINE_CTRL_DCD, 1);
/* Data Set Ready - the NCP SoC is ready to communicate */
(void)uart_line_ctrl_set(ot_uart.dev, UART_LINE_CTRL_DSR, 1);
}
uart_enabled = true;
#ifndef OT_UART_ASYNC
uart_irq_callback_user_data_set(ot_uart.dev,
uart_callback,
(void *)&ot_uart);
uart_irq_rx_enable(ot_uart.dev);
#else
uart_callback_set(ot_uart.dev, uart_async_cb, NULL);
uart_rx_enable(ot_uart.dev, rx_buf, sizeof(rx_buf), RX_TIMEOUT);
#endif
return OT_ERROR_NONE;
}
otError otPlatUartDisable(void)
{
uart_enabled = false;
#ifndef OT_UART_ASYNC
uart_irq_tx_disable(ot_uart.dev);
uart_irq_rx_disable(ot_uart.dev);
#else
uart_rx_disable(ot_uart.dev);
uart_tx_abort(ot_uart.dev);
#endif
return OT_ERROR_NONE;
}
otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
{
if (aBuf == NULL) {
return OT_ERROR_FAILED;
}
if (atomic_cas(&ot_uart.tx_busy, 0, 1)) {
#ifndef OT_UART_ASYNC
write_buffer = aBuf;
write_length = aBufLength;
if (is_panic_mode) {
/* In panic mode all data have to be sent immediately
* without using interrupts
*/
otPlatUartFlush();
} else {
uart_irq_tx_enable(ot_uart.dev);
}
return OT_ERROR_NONE;
#else
int ret = uart_tx(ot_uart.dev, aBuf, aBufLength, SYS_FOREVER_US);
if (ret == 0) {
if (is_panic_mode) {
/* In panic mode all data have to
* be sent before returning
*/
otPlatUartFlush();
}
return OT_ERROR_NONE;
}
/* TX did not start; clear busy flag */
atomic_clear(&ot_uart.tx_busy);
if (ret == -EBUSY) {
return OT_ERROR_BUSY;
}
return OT_ERROR_FAILED;
#endif
}
return OT_ERROR_BUSY;
}
otError otPlatUartFlush(void)
{
otError result = OT_ERROR_NONE;
#ifndef OT_UART_ASYNC
if (write_length) {
for (size_t i = 0; i < write_length; i++) {
uart_poll_out(ot_uart.dev, *(write_buffer+i));
}
}
atomic_clear(&ot_uart.tx_busy);
atomic_set(&ot_uart.tx_finished, 1);
otSysEventSignalPending();
#else
while (atomic_get(&ot_uart.tx_busy)) {
k_sleep(K_MSEC(1));
}
#endif
return result;
}
void platformUartPanic(void)
{
is_panic_mode = true;
/* In panic mode reception is not supported.
* In non-async mode, transmission is done without using interrupts
* (polling API). In async mode, transmission remains async but
* otPlatUartSend() blocks until the transfer completes.
*/
#ifndef OT_UART_ASYNC
uart_irq_tx_disable(ot_uart.dev);
uart_irq_rx_disable(ot_uart.dev);
#else
uart_rx_disable(ot_uart.dev);
#endif
}