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 /*************************************************************************************************/
29 /* Mock pipe */
30 /*************************************************************************************************/
31 static const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(dut));
32 static struct modem_backend_uart uart_backend;
33 static struct modem_pipe *pipe;
34 K_SEM_DEFINE(receive_ready_sem, 0, 1);
35
36 /*************************************************************************************************/
37 /* Buffers */
38 /*************************************************************************************************/
39 static uint8_t backend_receive_buffer[4096];
40 static uint8_t backend_transmit_buffer[4096];
41 RING_BUF_DECLARE(transmit_ring_buf, 4096);
42 static uint8_t receive_buffer[4096];
43
44 /*************************************************************************************************/
45 /* Modem pipe callback */
46 /*************************************************************************************************/
modem_pipe_callback_handler(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)47 static void modem_pipe_callback_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
48 void *user_data)
49 {
50 switch (event) {
51 case MODEM_PIPE_EVENT_RECEIVE_READY:
52 k_sem_give(&receive_ready_sem);
53 break;
54 default:
55 break;
56 }
57 }
58
59 /*************************************************************************************************/
60 /* Helpers */
61 /*************************************************************************************************/
62 static uint32_t transmit_prng_state = 1234;
63 static uint32_t receive_prng_state = 1234;
64 static uint32_t transmit_size_prng_state;
65
transmit_prng_random(void)66 static uint8_t transmit_prng_random(void)
67 {
68 transmit_prng_state = ((1103515245 * transmit_prng_state) + 12345) % (1U << 31);
69 return (uint8_t)(transmit_prng_state & 0xFF);
70 }
71
receive_prng_random(void)72 static uint8_t receive_prng_random(void)
73 {
74 receive_prng_state = ((1103515245 * receive_prng_state) + 12345) % (1U << 31);
75 return (uint8_t)(receive_prng_state & 0xFF);
76 }
77
prng_reset(void)78 static void prng_reset(void)
79 {
80 transmit_prng_state = 1234;
81 receive_prng_state = 1234;
82 transmit_size_prng_state = 0;
83 }
84
fill_transmit_ring_buf(void)85 static void fill_transmit_ring_buf(void)
86 {
87 uint32_t space = ring_buf_space_get(&transmit_ring_buf);
88 uint8_t data;
89
90 for (uint32_t i = 0; i < space; i++) {
91 data = transmit_prng_random();
92 ring_buf_put(&transmit_ring_buf, &data, 1);
93 }
94 }
95
transmit_size_prng_random(void)96 static uint32_t transmit_size_prng_random(void)
97 {
98 uint32_t size = 1;
99
100 for (uint8_t i = 0; i < transmit_size_prng_state; i++) {
101 size = size * 2;
102 }
103
104 transmit_size_prng_state = transmit_size_prng_state == 11
105 ? 0
106 : transmit_size_prng_state + 1;
107
108 return size;
109 }
110
transmit_prng(uint32_t remaining)111 static int transmit_prng(uint32_t remaining)
112 {
113 uint8_t *reserved;
114 uint32_t reserved_size;
115 uint32_t transmit_size;
116 int ret;
117
118 fill_transmit_ring_buf();
119 reserved_size = ring_buf_get_claim(&transmit_ring_buf, &reserved, UINT32_MAX);
120 transmit_size = MIN(transmit_size_prng_random(), reserved_size);
121 transmit_size = MIN(remaining, transmit_size);
122 ret = modem_pipe_transmit(pipe, reserved, transmit_size);
123 if (ret < 0) {
124 return ret;
125 }
126 printk("TX: %u,%u\n", transmit_size, (uint32_t)ret);
127 __ASSERT(ret <= remaining, "Impossible number of bytes sent %u", (uint32_t)ret);
128 ring_buf_get_finish(&transmit_ring_buf, ret);
129 return ret;
130 }
131
receive_prng(void)132 static int receive_prng(void)
133 {
134 int ret = 0;
135
136 if (k_sem_take(&receive_ready_sem, K_NO_WAIT) == 0) {
137 ret = modem_pipe_receive(pipe, receive_buffer, sizeof(receive_buffer));
138 if (ret < 0) {
139 return -EFAULT;
140 }
141 for (uint32_t i = 0; i < (uint32_t)ret; i++) {
142 if (receive_prng_random() != receive_buffer[i]) {
143 return -EFAULT;
144 }
145 }
146 printk("RX: %u\n", (uint32_t)ret);
147 }
148
149 return ret;
150 }
151
152 /*************************************************************************************************/
153 /* Test setup */
154 /*************************************************************************************************/
test_modem_backend_uart_setup(void)155 static void *test_modem_backend_uart_setup(void)
156 {
157 const struct modem_backend_uart_config config = {
158 .uart = uart,
159 .receive_buf = backend_receive_buffer,
160 .receive_buf_size = 1024,
161 .transmit_buf = backend_transmit_buffer,
162 .transmit_buf_size = 1024,
163 };
164
165 pipe = modem_backend_uart_init(&uart_backend, &config);
166 modem_pipe_attach(pipe, modem_pipe_callback_handler, NULL);
167 return NULL;
168 }
169
test_modem_backend_uart_before(void * f)170 static void test_modem_backend_uart_before(void *f)
171 {
172 prng_reset();
173 ring_buf_reset(&transmit_ring_buf);
174 k_sem_reset(&receive_ready_sem);
175 __ASSERT_NO_MSG(modem_pipe_open(pipe, K_SECONDS(10)) == 0);
176 }
177
test_modem_backend_uart_after(void * f)178 static void test_modem_backend_uart_after(void *f)
179 {
180 __ASSERT_NO_MSG(modem_pipe_close(pipe, K_SECONDS(10)) == 0);
181 }
182
183 /*************************************************************************************************/
184 /* Tests */
185 /*************************************************************************************************/
ZTEST(modem_backend_uart_suite,test_transmit_receive)186 ZTEST(modem_backend_uart_suite, test_transmit_receive)
187 {
188 int32_t remaining = 8192;
189 uint32_t received = 0;
190 uint32_t transmitted = 0;
191 int ret;
192
193 while ((remaining != 0) || (received < 8192)) {
194 ret = transmit_prng(remaining);
195 zassert(ret > -1, "Failed to transmit data");
196 remaining -= (uint32_t)ret;
197 transmitted += (uint32_t)ret;
198 printk("TX ACC: %u\n", transmitted);
199
200 while (received < transmitted) {
201 ret = receive_prng();
202 zassert(ret > -1, "Received data is corrupted");
203 received += (uint32_t)ret;
204 k_yield();
205 }
206 }
207 }
208
209 ZTEST_SUITE(modem_backend_uart_suite, NULL, test_modem_backend_uart_setup,
210 test_modem_backend_uart_before, test_modem_backend_uart_after, NULL);
211