You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.8 KiB
219 lines
5.8 KiB
/* |
|
* Copyright (c) 2024 DENX Software Engineering GmbH |
|
* Lukasz Majewski <lukma@denx.de> |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/** |
|
* @file |
|
* IEEE 802.15.4 HDLC RCP interface - serial communication interface (UART) |
|
*/ |
|
|
|
/* -------------------------------------------------------------------------- */ |
|
/* Includes */ |
|
/* -------------------------------------------------------------------------- */ |
|
#include <zephyr/drivers/uart.h> |
|
#include <openthread/platform/radio.h> |
|
#include <stdbool.h> |
|
#include <stddef.h> |
|
#include <zephyr/init.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h> |
|
#include <zephyr/net/ieee802154_radio.h> |
|
#include <zephyr/net/openthread.h> |
|
#include <zephyr/sys/ring_buffer.h> |
|
|
|
/* -------------------------------------------------------------------------- */ |
|
/* Definitions */ |
|
/* -------------------------------------------------------------------------- */ |
|
|
|
#define DT_DRV_COMPAT uart_hdlc_rcp_if |
|
|
|
#define LOG_MODULE_NAME hdlc_rcp_if_uart |
|
#define LOG_LEVEL CONFIG_HDLC_RCP_IF_DRIVER_LOG_LEVEL |
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
|
|
|
struct openthread_uart { |
|
struct k_work work; |
|
struct ring_buf *rx_ringbuf; |
|
struct ring_buf *tx_ringbuf; |
|
const struct device *dev; |
|
atomic_t tx_busy; |
|
|
|
hdlc_rx_callback_t cb; |
|
void *param; |
|
}; |
|
|
|
#define OT_UART_DEFINE(_name, _ringbuf_rx_size, _ringbuf_tx_size) \ |
|
RING_BUF_DECLARE(_name##_rx_ringbuf, _ringbuf_rx_size); \ |
|
RING_BUF_DECLARE(_name##_tx_ringbuf, _ringbuf_tx_size); \ |
|
static struct openthread_uart _name = { \ |
|
.rx_ringbuf = &_name##_rx_ringbuf, \ |
|
.tx_ringbuf = &_name##_tx_ringbuf, \ |
|
} |
|
OT_UART_DEFINE(ot_uart, CONFIG_OPENTHREAD_HDLC_RCP_IF_UART_RX_RING_BUFFER_SIZE, |
|
CONFIG_OPENTHREAD_HDLC_RCP_IF_UART_TX_RING_BUFFER_SIZE); |
|
|
|
static struct ot_hdlc_rcp_context { |
|
struct net_if *iface; |
|
struct openthread_context *ot_context; |
|
} ot_hdlc_rcp_ctx; |
|
|
|
/* -------------------------------------------------------------------------- */ |
|
/* Private functions */ |
|
/* -------------------------------------------------------------------------- */ |
|
|
|
static void ot_uart_rx_cb(struct k_work *item) |
|
{ |
|
struct openthread_uart *otuart = |
|
CONTAINER_OF(item, struct openthread_uart, work); |
|
uint8_t *data; |
|
uint32_t len; |
|
|
|
len = ring_buf_get_claim(otuart->rx_ringbuf, &data, |
|
otuart->rx_ringbuf->size); |
|
if (len > 0) { |
|
otuart->cb(data, len, otuart->param); |
|
ring_buf_get_finish(otuart->rx_ringbuf, len); |
|
} |
|
} |
|
|
|
static void uart_tx_handle(const struct device *dev) |
|
{ |
|
uint32_t tx_len = 0, len; |
|
uint8_t *data; |
|
|
|
len = ring_buf_get_claim( |
|
ot_uart.tx_ringbuf, &data, |
|
ot_uart.tx_ringbuf->size); |
|
if (len > 0) { |
|
tx_len = uart_fifo_fill(dev, data, len); |
|
int err = ring_buf_get_finish(ot_uart.tx_ringbuf, tx_len); |
|
(void)err; |
|
__ASSERT_NO_MSG(err == 0); |
|
} else { |
|
uart_irq_tx_disable(dev); |
|
} |
|
} |
|
|
|
static void uart_rx_handle(const struct device *dev) |
|
{ |
|
uint32_t rd_len = 0, len; |
|
uint8_t *data; |
|
|
|
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); |
|
|
|
int err = ring_buf_put_finish(ot_uart.rx_ringbuf, rd_len); |
|
(void)err; |
|
__ASSERT_NO_MSG(err == 0); |
|
} |
|
} |
|
|
|
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)) { |
|
uart_tx_handle(dev); |
|
} |
|
} |
|
|
|
if (ring_buf_size_get(ot_uart.rx_ringbuf) > 0) { |
|
k_work_submit(&ot_uart.work); |
|
} |
|
} |
|
|
|
static void hdlc_iface_init(struct net_if *iface) |
|
{ |
|
struct ot_hdlc_rcp_context *ctx = net_if_get_device(iface)->data; |
|
otExtAddress eui64; |
|
|
|
ot_uart.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ot_uart)); |
|
|
|
if (!device_is_ready(ot_uart.dev)) { |
|
LOG_ERR("UART device not ready"); |
|
} |
|
|
|
uart_irq_callback_user_data_set(ot_uart.dev, |
|
uart_callback, |
|
(void *)&ot_uart); |
|
|
|
ctx->iface = iface; |
|
ieee802154_init(iface); |
|
ctx->ot_context = net_if_l2_data(iface); |
|
|
|
otPlatRadioGetIeeeEui64(ctx->ot_context->instance, eui64.m8); |
|
net_if_set_link_addr(iface, eui64.m8, OT_EXT_ADDRESS_SIZE, |
|
NET_LINK_IEEE802154); |
|
} |
|
|
|
static int hdlc_register_rx_cb(hdlc_rx_callback_t hdlc_rx_callback, void *param) |
|
{ |
|
ot_uart.cb = hdlc_rx_callback; |
|
ot_uart.param = param; |
|
|
|
k_work_init(&ot_uart.work, ot_uart_rx_cb); |
|
uart_irq_rx_enable(ot_uart.dev); |
|
|
|
return 0; |
|
} |
|
|
|
static int hdlc_send(const uint8_t *frame, uint16_t length) |
|
{ |
|
uint32_t ret; |
|
|
|
if (frame == NULL) { |
|
return -EIO; |
|
} |
|
|
|
ret = ring_buf_put(ot_uart.tx_ringbuf, frame, length); |
|
uart_irq_tx_enable(ot_uart.dev); |
|
|
|
if (ret < length) { |
|
LOG_WRN("Cannot store full frame to RB (%d < %d)", ret, length); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int hdlc_deinit(void) |
|
{ |
|
uart_irq_tx_disable(ot_uart.dev); |
|
uart_irq_rx_disable(ot_uart.dev); |
|
|
|
ring_buf_reset(ot_uart.rx_ringbuf); |
|
ring_buf_reset(ot_uart.tx_ringbuf); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct hdlc_api uart_hdlc_api = { |
|
.iface_api.init = hdlc_iface_init, |
|
.register_rx_cb = hdlc_register_rx_cb, |
|
.send = hdlc_send, |
|
.deinit = hdlc_deinit, |
|
}; |
|
|
|
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2) |
|
|
|
#define MTU 1280 |
|
|
|
NET_DEVICE_DT_INST_DEFINE(0, NULL, /* Initialization Function */ |
|
NULL, /* No PM API support */ |
|
&ot_hdlc_rcp_ctx, /* HDLC RCP context data */ |
|
NULL, /* Configuration info */ |
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, /* Initial priority */ |
|
&uart_hdlc_api, /* API interface functions */ |
|
OPENTHREAD_L2, /* Openthread L2 */ |
|
NET_L2_GET_CTX_TYPE(OPENTHREAD_L2), /* Openthread L2 context type */ |
|
MTU); /* MTU size */
|
|
|