1 /*
2  * Copyright (c) 2024 Argentum Systems Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/uart.h>
10 #include <zephyr/sys/ring_buffer.h>
11 
12 #include <stdio.h>
13 #include <string.h>
14 
15 struct patch_info {
16 	const uint8_t * const name;
17 
18 	const struct device *rx_dev;
19 	struct ring_buf *rx_ring_buf;
20 	bool rx_error;
21 	bool rx_overflow;
22 
23 	const struct device *tx_dev;
24 };
25 
26 #define DEV_CONSOLE DEVICE_DT_GET(DT_CHOSEN(zephyr_console))
27 #define DEV_OTHER   DEVICE_DT_GET(DT_CHOSEN(uart_passthrough))
28 
29 #define RING_BUF_SIZE 64
30 
31 RING_BUF_DECLARE(rb_console, RING_BUF_SIZE);
32 struct patch_info patch_c2o = {
33 	.name = "c2o",
34 
35 	.rx_dev = DEV_CONSOLE,
36 	.rx_ring_buf = &rb_console,
37 	.rx_error = false,
38 	.rx_overflow = false,
39 
40 	.tx_dev = DEV_OTHER,
41 };
42 
43 RING_BUF_DECLARE(rb_other, RING_BUF_SIZE);
44 struct patch_info patch_o2c = {
45 	.name = "o2c",
46 
47 	.rx_dev = DEV_OTHER,
48 	.rx_ring_buf = &rb_other,
49 	.rx_error = false,
50 	.rx_overflow = false,
51 
52 	.tx_dev = DEV_CONSOLE,
53 };
54 
uart_cb(const struct device * dev,void * ctx)55 static void uart_cb(const struct device *dev, void *ctx)
56 {
57 	struct patch_info *patch = (struct patch_info *)ctx;
58 	int ret;
59 	uint8_t *buf;
60 	uint32_t len;
61 
62 	while (uart_irq_update(patch->rx_dev) > 0) {
63 		ret = uart_irq_rx_ready(patch->rx_dev);
64 		if (ret < 0) {
65 			patch->rx_error = true;
66 		}
67 		if (ret <= 0) {
68 			break;
69 		}
70 
71 		len = ring_buf_put_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE);
72 		if (len == 0) {
73 			/* no space for Rx, disable the IRQ */
74 			uart_irq_rx_disable(patch->rx_dev);
75 			patch->rx_overflow = true;
76 			break;
77 		}
78 
79 		ret = uart_fifo_read(patch->rx_dev, buf, len);
80 		if (ret < 0) {
81 			patch->rx_error = true;
82 		}
83 		if (ret <= 0) {
84 			break;
85 		}
86 		len = ret;
87 
88 		ret = ring_buf_put_finish(patch->rx_ring_buf, len);
89 		if (ret != 0) {
90 			patch->rx_error = true;
91 			break;
92 		}
93 	}
94 }
95 
passthrough(struct patch_info * patch)96 static void passthrough(struct patch_info *patch)
97 {
98 	int ret;
99 	uint8_t *buf;
100 	uint32_t len;
101 
102 	if (patch->rx_error) {
103 		printk("<<%s: Rx Error!>>\n", patch->name);
104 		patch->rx_error = false;
105 	}
106 
107 	if (patch->rx_overflow) {
108 		printk("<<%s: Rx Overflow!>>\n", patch->name);
109 		patch->rx_overflow = false;
110 	}
111 
112 	len = ring_buf_get_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE);
113 	if (len == 0) {
114 		goto done;
115 	}
116 
117 	ret = uart_fifo_fill(patch->tx_dev, buf, len);
118 	if (ret < 0) {
119 		goto error;
120 	}
121 	len = ret;
122 
123 	ret = ring_buf_get_finish(patch->rx_ring_buf, len);
124 	if (ret < 0) {
125 		goto error;
126 	}
127 
128 done:
129 	uart_irq_rx_enable(patch->rx_dev);
130 	return;
131 
132 error:
133 	printk("<<%s: Tx Error!>>\n", patch->name);
134 }
135 
main(void)136 int main(void)
137 {
138 	printk("Console Device: %p\n", patch_c2o.rx_dev);
139 	printk("Other Device:   %p\n", patch_o2c.rx_dev);
140 
141 	uart_irq_callback_user_data_set(patch_c2o.rx_dev, uart_cb, (void *)&patch_c2o);
142 	uart_irq_callback_user_data_set(patch_o2c.rx_dev, uart_cb, (void *)&patch_o2c);
143 
144 	for (;;) {
145 		passthrough(&patch_c2o);
146 		passthrough(&patch_o2c);
147 	}
148 
149 	return 0;
150 }
151