255 lines
5.1 KiB
C
255 lines
5.1 KiB
C
/*
|
|
* 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 <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"
|
|
|
|
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);
|
|
|
|
#define RX_FIFO_SIZE 128
|
|
|
|
static bool is_panic_mode;
|
|
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);
|
|
ot_uart.tx_busy = 0;
|
|
atomic_set(&(ot_uart.tx_finished), 1);
|
|
otSysEventSignalPending();
|
|
}
|
|
}
|
|
|
|
static void uart_callback(const struct device *dev, void *user_data)
|
|
{
|
|
ARG_UNUSED(user_data);
|
|
|
|
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (ot_uart.tx_finished) {
|
|
LOG_DBG("UART TX done");
|
|
otPlatUartSendDone();
|
|
ot_uart.tx_finished = 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
uart_irq_callback_user_data_set(ot_uart.dev,
|
|
uart_callback,
|
|
(void *)&ot_uart);
|
|
|
|
if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) {
|
|
int ret;
|
|
|
|
ret = usb_enable(NULL);
|
|
if (ret != 0 && ret != -EALREADY) {
|
|
LOG_ERR("Failed to enable USB");
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
/* 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_irq_rx_enable(ot_uart.dev);
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatUartDisable(void)
|
|
{
|
|
if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) {
|
|
int ret = usb_disable();
|
|
|
|
if (ret) {
|
|
LOG_WRN("Failed to disable USB (%d)", ret);
|
|
}
|
|
}
|
|
|
|
uart_irq_tx_disable(ot_uart.dev);
|
|
uart_irq_rx_disable(ot_uart.dev);
|
|
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)) {
|
|
write_buffer = aBuf;
|
|
write_length = aBufLength;
|
|
|
|
if (is_panic_mode) {
|
|
/* In panic mode all data have to be send immediately
|
|
* without using interrupts
|
|
*/
|
|
otPlatUartFlush();
|
|
} else {
|
|
uart_irq_tx_enable(ot_uart.dev);
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
return OT_ERROR_BUSY;
|
|
}
|
|
|
|
otError otPlatUartFlush(void)
|
|
{
|
|
otError result = OT_ERROR_NONE;
|
|
|
|
if (write_length) {
|
|
for (size_t i = 0; i < write_length; i++) {
|
|
uart_poll_out(ot_uart.dev, *(write_buffer+i));
|
|
}
|
|
}
|
|
|
|
ot_uart.tx_busy = 0;
|
|
atomic_set(&(ot_uart.tx_finished), 1);
|
|
otSysEventSignalPending();
|
|
return result;
|
|
}
|
|
|
|
void platformUartPanic(void)
|
|
{
|
|
is_panic_mode = true;
|
|
/* In panic mode data are send without using interrupts.
|
|
* Reception in this mode is not supported.
|
|
*/
|
|
uart_irq_tx_disable(ot_uart.dev);
|
|
uart_irq_rx_disable(ot_uart.dev);
|
|
}
|