1 /*
2  * Copyright (c) 2025 Embeint Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/net_buf.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/drivers/uart.h>
12 #include <zephyr/random/random.h>
13 
14 /* change this to any other UART peripheral if desired */
15 #define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart)
16 
17 /* Maximum number of packets to generate per iteration */
18 #define LOOP_ITER_MAX_TX 4
19 /* Maximum size of our TX packets */
20 #define MAX_TX_LEN 32
21 #define RX_CHUNK_LEN 32
22 
23 /* Buffer pool for our TX payloads */
24 NET_BUF_POOL_DEFINE(tx_pool, LOOP_ITER_MAX_TX, MAX_TX_LEN, 0, NULL);
25 
26 struct k_fifo tx_queue;
27 struct net_buf *tx_pending_buffer;
28 uint8_t async_rx_buffer[2][RX_CHUNK_LEN];
29 volatile uint8_t async_rx_buffer_idx;
30 
31 static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
32 
33 LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
34 
uart_callback(const struct device * dev,struct uart_event * evt,void * user_data)35 static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data)
36 {
37 	struct net_buf *buf;
38 	int rc;
39 
40 	LOG_DBG("EVENT: %d", evt->type);
41 
42 	switch (evt->type) {
43 	case UART_TX_DONE:
44 		LOG_DBG("TX complete %p", tx_pending_buffer);
45 
46 		/* Free TX buffer */
47 		net_buf_unref(tx_pending_buffer);
48 		tx_pending_buffer = NULL;
49 
50 		/* Handle any queued buffers */
51 		buf = k_fifo_get(&tx_queue, K_NO_WAIT);
52 		if (buf != NULL) {
53 			rc = uart_tx(dev, buf->data, buf->len, 0);
54 			if (rc != 0) {
55 				LOG_ERR("TX from ISR failed (%d)", rc);
56 				net_buf_unref(buf);
57 			} else {
58 				tx_pending_buffer = buf;
59 			}
60 		}
61 		break;
62 	case UART_RX_BUF_REQUEST:
63 		/* Return the next buffer index */
64 		LOG_DBG("Providing buffer index %d", async_rx_buffer_idx);
65 		rc = uart_rx_buf_rsp(dev, async_rx_buffer[async_rx_buffer_idx],
66 				     sizeof(async_rx_buffer[0]));
67 		__ASSERT_NO_MSG(rc == 0);
68 		async_rx_buffer_idx = async_rx_buffer_idx ? 0 : 1;
69 		break;
70 	case UART_RX_BUF_RELEASED:
71 	case UART_RX_DISABLED:
72 		break;
73 	case UART_RX_RDY:
74 		LOG_HEXDUMP_INF(evt->data.rx.buf + evt->data.rx.offset,
75 				evt->data.rx.len, "RX_RDY");
76 		break;
77 	default:
78 		LOG_WRN("Unhandled event %d", evt->type);
79 	}
80 }
81 
main(void)82 int main(void)
83 {
84 	bool rx_enabled = false;
85 	struct net_buf *tx_buf;
86 	int loop_counter = 0;
87 	uint8_t num_tx;
88 	int tx_len;
89 	int rc;
90 
91 	/* Register the async interrupt handler */
92 	uart_callback_set(uart_dev, uart_callback, (void *)uart_dev);
93 
94 	while (1) {
95 		/* Wait a while until the next burst transmission */
96 		k_sleep(K_SECONDS(5));
97 
98 		/* Each loop, try to send a random number of packets */
99 		num_tx = (sys_rand32_get() % LOOP_ITER_MAX_TX) + 1;
100 		LOG_INF("Loop %d: Sending %d packets", loop_counter, num_tx);
101 		for (int i = 0; i < num_tx; i++) {
102 			/* Allocate the data packet */
103 			tx_buf = net_buf_alloc(&tx_pool, K_FOREVER);
104 			/* Populate it with data */
105 			tx_len = snprintk(tx_buf->data, net_buf_tailroom(tx_buf),
106 					  "Loop %d: Packet: %d\r\n", loop_counter, i);
107 			net_buf_add(tx_buf, tx_len);
108 
109 			/* Queue packet for transmission */
110 			rc = uart_tx(uart_dev, tx_buf->data, tx_buf->len, SYS_FOREVER_US);
111 			if (rc == 0) {
112 				/* Store the pending buffer */
113 				tx_pending_buffer = tx_buf;
114 			} else if (rc == -EBUSY) {
115 				/* Transmission is already in progress */
116 				LOG_DBG("Queuing buffer %p", tx_buf);
117 				k_fifo_put(&tx_queue, tx_buf);
118 			} else {
119 				LOG_ERR("Unknown error (%d)", rc);
120 			}
121 		}
122 
123 		/* Toggle the RX state */
124 		if (rx_enabled) {
125 			uart_rx_disable(uart_dev);
126 		} else {
127 			async_rx_buffer_idx = 1;
128 			uart_rx_enable(uart_dev, async_rx_buffer[0], RX_CHUNK_LEN, 100);
129 		}
130 		rx_enabled = !rx_enabled;
131 		LOG_INF("RX is now %s", rx_enabled ? "enabled" : "disabled");
132 
133 		loop_counter += 1;
134 	}
135 }
136