1 /*
2 * Copyright (c) 2023 Trackunit Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * This test suite sets up a modem_backend_uart instance connected to a UART which has its
9 * RX and TX pins wired together to provide loopback functionality. A large number of bytes
10 * containing a sequence of pseudo random numbers are then transmitted, received, and validated.
11 *
12 * The test suite repeats three times, opening and clsoing the modem_pipe attached to the
13 * modem_backend_uart instance before and after the tests respectively.
14 */
15
16 /*************************************************************************************************/
17 /* Dependencies */
18 /*************************************************************************************************/
19 #include <zephyr/ztest.h>
20 #include <zephyr/kernel.h>
21 #include <zephyr/sys/atomic.h>
22 #include <zephyr/modem/backend/uart.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef CONFIG_TEST_HW_FLOW_CONTROL
29 #define TEST_BACKEND_RECEIVE_BUFFER_SIZE 256
30 #else
31 #define TEST_BACKEND_RECEIVE_BUFFER_SIZE 4096
32 #endif
33
34 /*************************************************************************************************/
35 /* Mock pipe */
36 /*************************************************************************************************/
37 static const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(dut));
38 static struct modem_backend_uart uart_backend;
39 static struct modem_pipe *pipe;
40 K_SEM_DEFINE(receive_ready_sem, 0, 1);
41
42 /*************************************************************************************************/
43 /* Buffers */
44 /*************************************************************************************************/
45 static uint8_t backend_receive_buffer[TEST_BACKEND_RECEIVE_BUFFER_SIZE];
46 static uint8_t backend_transmit_buffer[4096];
47 RING_BUF_DECLARE(transmit_ring_buf, 4096);
48 static uint8_t receive_buffer[4096];
49
50 /*************************************************************************************************/
51 /* Modem pipe callback */
52 /*************************************************************************************************/
modem_pipe_callback_handler(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)53 static void modem_pipe_callback_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
54 void *user_data)
55 {
56 switch (event) {
57 case MODEM_PIPE_EVENT_RECEIVE_READY:
58 k_sem_give(&receive_ready_sem);
59 break;
60 default:
61 break;
62 }
63 }
64
65 /*************************************************************************************************/
66 /* Helpers */
67 /*************************************************************************************************/
68 static uint32_t transmit_prng_state = 1234;
69 static uint32_t receive_prng_state = 1234;
70 static uint32_t transmit_size_prng_state;
71
transmit_prng_random(void)72 static uint8_t transmit_prng_random(void)
73 {
74 transmit_prng_state = ((1103515245 * transmit_prng_state) + 12345) % (1U << 31);
75 return (uint8_t)(transmit_prng_state & 0xFF);
76 }
77
receive_prng_random(void)78 static uint8_t receive_prng_random(void)
79 {
80 receive_prng_state = ((1103515245 * receive_prng_state) + 12345) % (1U << 31);
81 return (uint8_t)(receive_prng_state & 0xFF);
82 }
83
prng_reset(void)84 static void prng_reset(void)
85 {
86 transmit_prng_state = 1234;
87 receive_prng_state = 1234;
88 transmit_size_prng_state = 0;
89 }
90
fill_transmit_ring_buf(void)91 static void fill_transmit_ring_buf(void)
92 {
93 uint32_t space = ring_buf_space_get(&transmit_ring_buf);
94 uint8_t data;
95
96 for (uint32_t i = 0; i < space; i++) {
97 data = transmit_prng_random();
98 ring_buf_put(&transmit_ring_buf, &data, 1);
99 }
100 }
101
transmit_size_prng_random(void)102 static uint32_t transmit_size_prng_random(void)
103 {
104 uint32_t size = 1;
105
106 for (uint8_t i = 0; i < transmit_size_prng_state; i++) {
107 size = size * 2;
108 }
109
110 transmit_size_prng_state = transmit_size_prng_state == 11
111 ? 0
112 : transmit_size_prng_state + 1;
113
114 return size;
115 }
116
transmit_prng(uint32_t remaining)117 static int transmit_prng(uint32_t remaining)
118 {
119 uint8_t *reserved;
120 uint32_t reserved_size;
121 uint32_t transmit_size;
122 int ret;
123
124 fill_transmit_ring_buf();
125 reserved_size = ring_buf_get_claim(&transmit_ring_buf, &reserved, UINT32_MAX);
126 transmit_size = MIN(transmit_size_prng_random(), reserved_size);
127 transmit_size = MIN(remaining, transmit_size);
128 ret = modem_pipe_transmit(pipe, reserved, transmit_size);
129 if (ret < 0) {
130 return ret;
131 }
132 printk("TX: %u,%u\n", transmit_size, (uint32_t)ret);
133 __ASSERT(ret <= remaining, "Impossible number of bytes sent %u", (uint32_t)ret);
134 ring_buf_get_finish(&transmit_ring_buf, ret);
135 return ret;
136 }
137
receive_prng(void)138 static int receive_prng(void)
139 {
140 int ret;
141
142 if (k_sem_take(&receive_ready_sem, K_SECONDS(1))) {
143 return -ETIMEDOUT;
144 }
145
146 if (IS_ENABLED(CONFIG_TEST_HW_FLOW_CONTROL)) {
147 k_msleep(100);
148 }
149
150 ret = modem_pipe_receive(pipe, receive_buffer, sizeof(receive_buffer));
151 if (ret == 0) {
152 return -ENODATA;
153 }
154
155 if (ret < 0) {
156 return -EFAULT;
157 }
158
159 for (uint32_t i = 0; i < (uint32_t)ret; i++) {
160 if (receive_prng_random() != receive_buffer[i]) {
161 return -EFAULT;
162 }
163 }
164
165 printk("RX: %u\n", (uint32_t)ret);
166 return ret;
167 }
168
169 /*************************************************************************************************/
170 /* Test setup */
171 /*************************************************************************************************/
test_modem_backend_uart_setup(void)172 static void *test_modem_backend_uart_setup(void)
173 {
174 const struct modem_backend_uart_config config = {
175 .uart = uart,
176 .receive_buf = backend_receive_buffer,
177 .receive_buf_size = sizeof(backend_receive_buffer),
178 .transmit_buf = backend_transmit_buffer,
179 .transmit_buf_size = sizeof(backend_transmit_buffer),
180 };
181
182 pipe = modem_backend_uart_init(&uart_backend, &config);
183 modem_pipe_attach(pipe, modem_pipe_callback_handler, NULL);
184 return NULL;
185 }
186
test_modem_backend_uart_before(void * f)187 static void test_modem_backend_uart_before(void *f)
188 {
189 prng_reset();
190 ring_buf_reset(&transmit_ring_buf);
191 k_sem_reset(&receive_ready_sem);
192 __ASSERT_NO_MSG(modem_pipe_open(pipe, K_SECONDS(1)) == 0);
193 }
194
test_modem_backend_uart_after(void * f)195 static void test_modem_backend_uart_after(void *f)
196 {
197 __ASSERT_NO_MSG(modem_pipe_close(pipe, K_SECONDS(1)) == 0);
198 }
199
200 /*************************************************************************************************/
201 /* Tests */
202 /*************************************************************************************************/
ZTEST(modem_backend_uart_suite,test_transmit_receive)203 ZTEST(modem_backend_uart_suite, test_transmit_receive)
204 {
205 int32_t remaining = 8192;
206 uint32_t received = 0;
207 uint32_t transmitted = 0;
208 int ret;
209
210 while ((remaining != 0) || (received < 8192)) {
211 ret = transmit_prng(remaining);
212 zassert(ret > -1, "Failed to transmit data");
213 remaining -= (uint32_t)ret;
214 transmitted += (uint32_t)ret;
215 printk("TX ACC: %u\n", transmitted);
216
217 while (received < transmitted) {
218 ret = receive_prng();
219 zassert(ret > -1, "Received data is corrupted");
220 received += (uint32_t)ret;
221 }
222 }
223 }
224
225 ZTEST_SUITE(modem_backend_uart_suite, NULL, test_modem_backend_uart_setup,
226 test_modem_backend_uart_before, test_modem_backend_uart_after, NULL);
227