1 /*
2 * Copyright (c) 2024 DENX Software Engineering GmbH
3 * Lukasz Majewski <lukma@denx.de>
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
ot_uart_rx_cb(struct k_work * item)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
uart_tx_handle(const struct device * dev)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
uart_rx_handle(const struct device * dev)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
uart_callback(const struct device * dev,void * user_data)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
hdlc_iface_init(struct net_if * iface)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
hdlc_register_rx_cb(hdlc_rx_callback_t hdlc_rx_callback,void * param)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
hdlc_send(const uint8_t * frame,uint16_t length)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
hdlc_deinit(void)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 */
220