Skip to content

Commit 88e4930

Browse files
author
Lukasz Majewski
committed
drivers: net: ot: Add HDLC HOST support with UART communication to RCP
This patch adds support for HOST OpenThread communication to the RCP co-processor via UART using SPINEL protocol. The aim is to use OpenThread's RCP (Radio Co-Processor) with HOST device (for example imxRT1020). Such configuration is the same as one used with PC program (ot-cli) and RCP. Signed-off-by: Lukasz Majewski <[email protected]>
1 parent 685085a commit 88e4930

File tree

5 files changed

+256
-0
lines changed

5 files changed

+256
-0
lines changed

drivers/hdlc_rcp_if/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
zephyr_library()
44

55
zephyr_library_sources_ifdef(CONFIG_HDLC_RCP_IF_NXP hdlc_rcp_if_nxp.c)
6+
zephyr_library_sources_ifdef(CONFIG_HDLC_RCP_IF_UART hdlc_rcp_if_uart.c)

drivers/hdlc_rcp_if/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ menuconfig HDLC_RCP_IF
1414
if HDLC_RCP_IF
1515

1616
source "drivers/hdlc_rcp_if/Kconfig.nxp"
17+
source "drivers/hdlc_rcp_if/Kconfig.uart"
1718

1819
config HDLC_RCP_IF_DRV_NAME
1920
string "HDLC RCP Interface Driver's name"

drivers/hdlc_rcp_if/Kconfig.uart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Configuration options for NXP HDLC RCP UART communication Interface
2+
3+
# Copyright (c) 2024 DENX Software Engineering GmbH
4+
# Lukasz Majewski <[email protected]>
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
#
8+
# HDLC UART communication Interface used by Zephyr running Openthread RCP host
9+
#
10+
11+
config HDLC_RCP_IF_UART
12+
bool "UART HDLC interface for Zephyr Openthread RCP host"
13+
default y
14+
depends on DT_HAS_UART_HDLC_RCP_IF_ENABLED
15+
16+
config OPENTHREAD_HDLC_RCP_IF_UART_RX_RING_BUFFER_SIZE
17+
int "Set HDLC RCP IF UART RX ring buffer size"
18+
default 4096
19+
help
20+
RX buffer size for the OpenThread HDLC host UART.
21+
22+
config OPENTHREAD_HDLC_RCP_IF_UART_TX_RING_BUFFER_SIZE
23+
int "Set HDLC RCP IF UART TX ring buffer size"
24+
default 1344
25+
help
26+
TX buffer size for the OpenThread HDLC host UART.
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Copyright (c) 2024 DENX Software Engineering GmbH
3+
* Lukasz Majewski <[email protected]>
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* IEEE 802.15.4 HDLC RCP interface - serial communication interface (UART)
10+
*/
11+
12+
/* -------------------------------------------------------------------------- */
13+
/* Includes */
14+
/* -------------------------------------------------------------------------- */
15+
#include <zephyr/drivers/uart.h>
16+
#include <openthread/platform/radio.h>
17+
#include <stdbool.h>
18+
#include <stddef.h>
19+
#include <zephyr/init.h>
20+
#include <zephyr/logging/log.h>
21+
#include <zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h>
22+
#include <zephyr/net/ieee802154_radio.h>
23+
#include <zephyr/net/openthread.h>
24+
#include <zephyr/sys/ring_buffer.h>
25+
26+
/* -------------------------------------------------------------------------- */
27+
/* Definitions */
28+
/* -------------------------------------------------------------------------- */
29+
30+
#define DT_DRV_COMPAT uart_hdlc_rcp_if
31+
32+
#define LOG_MODULE_NAME hdlc_rcp_if_uart
33+
#define LOG_LEVEL CONFIG_HDLC_RCP_IF_DRIVER_LOG_LEVEL
34+
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
35+
36+
struct openthread_uart {
37+
struct k_work work;
38+
struct ring_buf *rx_ringbuf;
39+
struct ring_buf *tx_ringbuf;
40+
const struct device *dev;
41+
atomic_t tx_busy;
42+
43+
hdlc_rx_callback_t cb;
44+
void *param;
45+
};
46+
47+
#define OT_UART_DEFINE(_name, _ringbuf_rx_size, _ringbuf_tx_size) \
48+
RING_BUF_DECLARE(_name##_rx_ringbuf, _ringbuf_rx_size); \
49+
RING_BUF_DECLARE(_name##_tx_ringbuf, _ringbuf_tx_size); \
50+
static struct openthread_uart _name = { \
51+
.rx_ringbuf = &_name##_rx_ringbuf, \
52+
.tx_ringbuf = &_name##_tx_ringbuf, \
53+
}
54+
OT_UART_DEFINE(ot_uart, CONFIG_OPENTHREAD_HDLC_RCP_IF_UART_RX_RING_BUFFER_SIZE,
55+
CONFIG_OPENTHREAD_HDLC_RCP_IF_UART_TX_RING_BUFFER_SIZE);
56+
57+
struct ot_hdlc_rcp_context {
58+
struct net_if *iface;
59+
struct openthread_context *ot_context;
60+
};
61+
62+
/* -------------------------------------------------------------------------- */
63+
/* Private functions */
64+
/* -------------------------------------------------------------------------- */
65+
66+
static void ot_uart_rx_cb(struct k_work *item)
67+
{
68+
struct openthread_uart *otuart =
69+
CONTAINER_OF(item, struct openthread_uart, work);
70+
uint8_t *data;
71+
uint32_t len;
72+
73+
len = ring_buf_get_claim(otuart->rx_ringbuf, &data,
74+
otuart->rx_ringbuf->size);
75+
if (len > 0) {
76+
otuart->cb(data, len, otuart->param);
77+
ring_buf_get_finish(otuart->rx_ringbuf, len);
78+
}
79+
}
80+
81+
static void uart_tx_handle(const struct device *dev)
82+
{
83+
uint32_t tx_len = 0, len;
84+
uint8_t *data;
85+
86+
len = ring_buf_get_claim(
87+
ot_uart.tx_ringbuf, &data,
88+
ot_uart.tx_ringbuf->size);
89+
if (len > 0) {
90+
tx_len = uart_fifo_fill(dev, data, len);
91+
int err = ring_buf_get_finish(ot_uart.tx_ringbuf, tx_len);
92+
(void)err;
93+
__ASSERT_NO_MSG(err == 0);
94+
} else {
95+
uart_irq_tx_disable(dev);
96+
}
97+
}
98+
99+
static void uart_rx_handle(const struct device *dev)
100+
{
101+
uint32_t rd_len = 0, len;
102+
uint8_t *data;
103+
104+
len = ring_buf_put_claim(
105+
ot_uart.rx_ringbuf, &data,
106+
ot_uart.rx_ringbuf->size);
107+
if (len > 0) {
108+
rd_len = uart_fifo_read(dev, data, len);
109+
110+
int err = ring_buf_put_finish(ot_uart.rx_ringbuf, rd_len);
111+
(void)err;
112+
__ASSERT_NO_MSG(err == 0);
113+
}
114+
}
115+
116+
static void uart_callback(const struct device *dev, void *user_data)
117+
{
118+
ARG_UNUSED(user_data);
119+
120+
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
121+
if (uart_irq_rx_ready(dev)) {
122+
uart_rx_handle(dev);
123+
}
124+
125+
if (uart_irq_tx_ready(dev)) {
126+
uart_tx_handle(dev);
127+
}
128+
}
129+
130+
if (ring_buf_size_get(ot_uart.rx_ringbuf) > 0) {
131+
k_work_submit(&ot_uart.work);
132+
}
133+
}
134+
135+
static void hdlc_iface_init(struct net_if *iface)
136+
{
137+
struct ot_hdlc_rcp_context *ctx = net_if_get_device(iface)->data;
138+
otExtAddress eui64;
139+
140+
ot_uart.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ot_uart));
141+
142+
if (!device_is_ready(ot_uart.dev)) {
143+
LOG_ERR("UART device not ready");
144+
}
145+
146+
uart_irq_callback_user_data_set(ot_uart.dev,
147+
uart_callback,
148+
(void *)&ot_uart);
149+
150+
ctx->iface = iface;
151+
ieee802154_init(iface);
152+
ctx->ot_context = net_if_l2_data(iface);
153+
154+
otPlatRadioGetIeeeEui64(ctx->ot_context->instance, eui64.m8);
155+
net_if_set_link_addr(iface, eui64.m8, OT_EXT_ADDRESS_SIZE,
156+
NET_LINK_IEEE802154);
157+
}
158+
159+
static int hdlc_register_rx_cb(hdlc_rx_callback_t hdlc_rx_callback, void *param)
160+
{
161+
ot_uart.cb = hdlc_rx_callback;
162+
ot_uart.param = param;
163+
164+
k_work_init(&ot_uart.work, ot_uart_rx_cb);
165+
uart_irq_rx_enable(ot_uart.dev);
166+
167+
return 0;
168+
}
169+
170+
static int hdlc_send(const uint8_t *frame, uint16_t length)
171+
{
172+
uint32_t ret;
173+
174+
if (frame == NULL) {
175+
return -EIO;
176+
}
177+
178+
ret = ring_buf_put(ot_uart.tx_ringbuf, frame, length);
179+
uart_irq_tx_enable(ot_uart.dev);
180+
181+
if (ret < length) {
182+
LOG_WRN("Cannot store full frame to RB (%d < %d)", ret, length);
183+
return -EIO;
184+
}
185+
186+
return 0;
187+
}
188+
189+
static int hdlc_deinit(void)
190+
{
191+
uart_irq_tx_disable(ot_uart.dev);
192+
uart_irq_rx_disable(ot_uart.dev);
193+
194+
ring_buf_reset(ot_uart.rx_ringbuf);
195+
ring_buf_reset(ot_uart.tx_ringbuf);
196+
197+
return 0;
198+
}
199+
200+
static const struct hdlc_api uart_hdlc_api = {
201+
.iface_api.init = hdlc_iface_init,
202+
.register_rx_cb = hdlc_register_rx_cb,
203+
.send = hdlc_send,
204+
.deinit = hdlc_deinit,
205+
};
206+
207+
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2)
208+
209+
#define MTU 1280
210+
211+
NET_DEVICE_DT_INST_DEFINE(0, NULL, /* Initialization Function */
212+
NULL, /* No PM API support */
213+
NULL, /* No context data */
214+
NULL, /* Configuration info */
215+
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, /* Initial priority */
216+
&uart_hdlc_api, /* API interface functions */
217+
OPENTHREAD_L2, /* Openthread L2 */
218+
NET_L2_GET_CTX_TYPE(OPENTHREAD_L2), /* Openthread L2 context type */
219+
MTU); /* MTU size */
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2024, DENX Software Engineering GmbH
2+
# Lukasz Majewski <[email protected]>
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: UART HDLC RCP interface node
6+
7+
compatible: "uart,hdlc-rcp-if"
8+
9+
include: base.yaml

0 commit comments

Comments
 (0)