1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/ztest.h>
11 #include <zephyr/ipc/ipc_service.h>
12
13 #include <test_commands.h>
14 #include "data_queue.h"
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(ipc_sessions, LOG_LEVEL_INF);
18
19 enum test_ipc_events {
20 TEST_IPC_EVENT_BOUNDED,
21 TEST_IPC_EVENT_UNBOUNDED,
22 TEST_IPC_EVENT_ERROR
23 };
24
25 struct test_ipc_event_state {
26 enum test_ipc_events ev;
27 struct ipc_ep *ep;
28 };
29
30 static const struct device *ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
31 static volatile bool ipc0_bounded;
32 K_MSGQ_DEFINE(ipc_events, sizeof(struct test_ipc_event_state), 16, 4);
33
34 static uint32_t data_queue_memory[ROUND_UP(CONFIG_IPC_TEST_MSG_HEAP_SIZE, sizeof(uint32_t))];
35 static struct data_queue ipc_data_queue;
36
37 struct test_cmd_xdata {
38 struct ipc_test_cmd base;
39 uint8_t data[CONFIG_IPC_TEST_BLOCK_SIZE];
40 };
41
42 static void (*ep_received_override_cb)(const void *data, size_t len, void *priv);
43
ep_bound(void * priv)44 static void ep_bound(void *priv)
45 {
46 int ret;
47 struct test_ipc_event_state ev = {
48 .ev = TEST_IPC_EVENT_BOUNDED,
49 .ep = priv
50 };
51
52 ipc0_bounded = true;
53 ret = k_msgq_put(&ipc_events, &ev, K_NO_WAIT);
54 if (ret) {
55 LOG_ERR("Cannot put event in queue: %d", ret);
56 }
57 }
58
ep_unbound(void * priv)59 static void ep_unbound(void *priv)
60 {
61 int ret;
62 struct test_ipc_event_state ev = {
63 .ev = TEST_IPC_EVENT_UNBOUNDED,
64 .ep = priv
65 };
66
67 ipc0_bounded = false;
68 ret = k_msgq_put(&ipc_events, &ev, K_NO_WAIT);
69 if (ret) {
70 LOG_ERR("Cannot put event in queue: %d", ret);
71 }
72 }
73
ep_recv(const void * data,size_t len,void * priv)74 static void ep_recv(const void *data, size_t len, void *priv)
75 {
76 int ret;
77
78 if (ep_received_override_cb) {
79 ep_received_override_cb(data, len, priv);
80 } else {
81 ret = data_queue_put(&ipc_data_queue, data, len, K_NO_WAIT);
82 __ASSERT(ret >= 0, "Cannot put data into queue: %d", ret);
83 (void)ret;
84 }
85 }
86
ep_error(const char * message,void * priv)87 static void ep_error(const char *message, void *priv)
88 {
89 int ret;
90 struct test_ipc_event_state ev = {
91 .ev = TEST_IPC_EVENT_ERROR,
92 .ep = priv
93 };
94
95 ret = k_msgq_put(&ipc_events, &ev, K_NO_WAIT);
96 if (ret) {
97 LOG_ERR("Cannot put event in queue: %d", ret);
98 }
99 }
100
101 static struct ipc_ept_cfg ep_cfg = {
102 .cb = {
103 .bound = ep_bound,
104 .unbound = ep_unbound,
105 .received = ep_recv,
106 .error = ep_error
107 },
108 };
109
110 static struct ipc_ept ep;
111
112
113
114 /**
115 * @brief Estabilish connection before any test run
116 */
test_suite_setup(void)117 void *test_suite_setup(void)
118 {
119 int ret;
120 struct test_ipc_event_state ev;
121
122 data_queue_init(&ipc_data_queue, data_queue_memory, sizeof(data_queue_memory));
123
124 ret = ipc_service_open_instance(ipc0_instance);
125 zassert_true((ret >= 0) || ret == -EALREADY, "ipc_service_open_instance() failure: %d",
126 ret);
127
128 /* Store the pointer to the endpoint */
129 ep_cfg.priv = &ep;
130 ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg);
131 zassert_true((ret >= 0), "ipc_service_register_endpoint() failure: %d", ret);
132
133 do {
134 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
135 zassert_ok(ret, "Cannot bound to the remote interface");
136 } while (!ipc0_bounded);
137
138 return NULL;
139 }
140
141 /**
142 * @brief Prepare the test structures
143 */
test_suite_before(void * fixture)144 void test_suite_before(void *fixture)
145 {
146 ep_received_override_cb = NULL;
147 k_msgq_purge(&ipc_events);
148 }
149
execute_test_ping_pong(void)150 static void execute_test_ping_pong(void)
151 {
152 int ret;
153 static const struct ipc_test_cmd cmd_ping = { IPC_TEST_CMD_PING };
154 struct ipc_test_cmd *cmd_rsp;
155 size_t cmd_rsp_size;
156
157 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
158 "IPC data queue contains unexpected data");
159 /* Sending data */
160 ret = ipc_service_send(&ep, &cmd_ping, sizeof(cmd_ping));
161 zassert_equal(ret, sizeof(cmd_ping), "ipc_service_send failed: %d, expected: %u", ret,
162 sizeof(cmd_ping));
163 /* Waiting for response */
164 cmd_rsp = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000));
165 zassert_not_null(cmd_rsp, "No command response on time");
166 zassert_equal(cmd_rsp_size, sizeof(struct ipc_test_cmd),
167 "Unexpected response size: %u, expected: %u", cmd_rsp_size,
168 sizeof(struct ipc_test_cmd));
169 zassert_equal(cmd_rsp->cmd, IPC_TEST_CMD_PONG,
170 "Unexpected response cmd value: %u, expected: %u", cmd_rsp->cmd,
171 IPC_TEST_CMD_PONG);
172 data_queue_release(&ipc_data_queue, cmd_rsp);
173 }
174
ZTEST(ipc_sessions,test_ping_pong)175 ZTEST(ipc_sessions, test_ping_pong)
176 {
177 execute_test_ping_pong();
178 }
179
ZTEST(ipc_sessions,test_echo)180 ZTEST(ipc_sessions, test_echo)
181 {
182 int ret;
183 static const struct ipc_test_cmd cmd_echo = {
184 IPC_TEST_CMD_ECHO, {'H', 'e', 'l', 'l', 'o', '!'}
185 };
186 struct ipc_test_cmd *cmd_rsp;
187 size_t cmd_rsp_size;
188
189 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
190 "IPC data queue contains unexpected data");
191 /* Sending data */
192 ret = ipc_service_send(&ep, &cmd_echo, sizeof(cmd_echo));
193 zassert_equal(ret, sizeof(cmd_echo), "ipc_service_send failed: %d, expected: %u", ret,
194 sizeof(cmd_echo));
195 /* Waiting for response */
196 cmd_rsp = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000));
197 zassert_not_null(cmd_rsp, "No command response on time");
198 /* Checking response */
199 zassert_equal(cmd_rsp_size, sizeof(cmd_echo), "Unexpected response size: %u, expected: %u",
200 cmd_rsp_size, sizeof(cmd_echo));
201 zassert_equal(cmd_rsp->cmd, IPC_TEST_CMD_ECHO_RSP,
202 "Unexpected response cmd value: %u, expected: %u", cmd_rsp->cmd,
203 IPC_TEST_CMD_ECHO_RSP);
204 zassert_mem_equal(cmd_rsp->data, cmd_echo.data,
205 sizeof(cmd_echo) - sizeof(struct ipc_test_cmd),
206 "Unexpected response content");
207 data_queue_release(&ipc_data_queue, cmd_rsp);
208 }
209
ZTEST(ipc_sessions,test_reboot)210 ZTEST(ipc_sessions, test_reboot)
211 {
212 Z_TEST_SKIP_IFDEF(CONFIG_IPC_TEST_SKIP_UNBOUND);
213 Z_TEST_SKIP_IFDEF(CONFIG_IPC_TEST_SKIP_CORE_RESET);
214
215 int ret;
216 struct test_ipc_event_state ev;
217 static const struct ipc_test_cmd_reboot cmd_rebond = { { IPC_TEST_CMD_REBOOT }, 10 };
218
219 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
220 "IPC data queue contains unexpected data");
221 /* Sending data */
222 ret = ipc_service_send(&ep, &cmd_rebond, sizeof(cmd_rebond));
223 zassert_equal(ret, sizeof(cmd_rebond), "ipc_service_send failed: %d, expected: %u", ret,
224 sizeof(cmd_rebond));
225 /* Waiting for IPC to unbound */
226 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
227 zassert_ok(ret, "No IPC unbound event on time");
228 zassert_equal(ev.ev, TEST_IPC_EVENT_UNBOUNDED, "Unexpected IPC event: %u, expected: %u",
229 ev.ev, TEST_IPC_EVENT_UNBOUNDED);
230 zassert_equal_ptr(ev.ep, &ep, "Unexpected endpoint (unbound)");
231 /* Reconnecting */
232 ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg);
233 zassert_true((ret >= 0), "ipc_service_register_endpoint() failure: %d", ret);
234 /* Waiting for bound */
235 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
236 zassert_ok(ret, "No IPC bound event on time");
237 zassert_equal(ev.ev, TEST_IPC_EVENT_BOUNDED, "Unexpected IPC event: %u, expected: %u",
238 ev.ev, TEST_IPC_EVENT_UNBOUNDED);
239 zassert_equal_ptr(ev.ep, &ep, "Unexpected endpoint (bound)");
240
241 /* After reconnection - test communication */
242 execute_test_ping_pong();
243 }
244
ZTEST(ipc_sessions,test_rebond)245 ZTEST(ipc_sessions, test_rebond)
246 {
247 Z_TEST_SKIP_IFDEF(CONFIG_IPC_TEST_SKIP_UNBOUND);
248
249 int ret;
250 struct test_ipc_event_state ev;
251 static const struct ipc_test_cmd_reboot cmd_rebond = { { IPC_TEST_CMD_REBOND }, 10 };
252
253 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
254 "IPC data queue contains unexpected data");
255 /* Sending data */
256 ret = ipc_service_send(&ep, &cmd_rebond, sizeof(cmd_rebond));
257 zassert_equal(ret, sizeof(cmd_rebond), "ipc_service_send failed: %d, expected: %u", ret,
258 sizeof(cmd_rebond));
259 /* Waiting for IPC to unbound */
260 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
261 zassert_ok(ret, "No IPC unbound event on time");
262 zassert_equal(ev.ev, TEST_IPC_EVENT_UNBOUNDED, "Unexpected IPC event: %u, expected: %u",
263 ev.ev, TEST_IPC_EVENT_UNBOUNDED);
264 zassert_equal_ptr(ev.ep, &ep, "Unexpected endpoint (unbound)");
265 /* Reconnecting */
266 ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg);
267 zassert_true((ret >= 0), "ipc_service_register_endpoint() failure: %d", ret);
268 /* Waiting for bound */
269 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
270 zassert_ok(ret, "No IPC bound event on time");
271 zassert_equal(ev.ev, TEST_IPC_EVENT_BOUNDED, "Unexpected IPC event: %u, expected: %u",
272 ev.ev, TEST_IPC_EVENT_UNBOUNDED);
273 zassert_equal_ptr(ev.ep, &ep, "Unexpected endpoint (bound)");
274
275 /* After reconnection - test communication */
276 execute_test_ping_pong();
277 }
278
ZTEST(ipc_sessions,test_local_rebond)279 ZTEST(ipc_sessions, test_local_rebond)
280 {
281 Z_TEST_SKIP_IFDEF(CONFIG_IPC_TEST_SKIP_UNBOUND);
282
283 int ret;
284 struct test_ipc_event_state ev;
285
286 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
287 "IPC data queue contains unexpected data");
288 /* Rebond locally */
289 ret = ipc_service_deregister_endpoint(ep_cfg.priv);
290 zassert_ok(ret, "ipc_service_deregister_endpoint() failure: %d", ret);
291 ipc0_bounded = false;
292
293 ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg);
294 zassert_true((ret >= 0), "ipc_service_register_endpoint() failure: %d", ret);
295 do {
296 ret = k_msgq_get(&ipc_events, &ev, K_MSEC(1000));
297 zassert_ok(ret, "Cannot bound to the remote interface");
298 } while (!ipc0_bounded);
299
300 /* After reconnection - test communication */
301 execute_test_ping_pong();
302 }
303
ZTEST(ipc_sessions,test_tx_long)304 ZTEST(ipc_sessions, test_tx_long)
305 {
306 #define SEED_TXSTART_VALUE 1
307 int ret;
308 static const struct ipc_test_cmd_xstart cmd_rxstart = {
309 .base = { .cmd = IPC_TEST_CMD_RXSTART },
310 .blk_size = CONFIG_IPC_TEST_BLOCK_SIZE,
311 .blk_cnt = CONFIG_IPC_TEST_BLOCK_CNT,
312 .seed = SEED_TXSTART_VALUE };
313 static const struct ipc_test_cmd cmd_rxget = { IPC_TEST_CMD_RXGET };
314 struct test_cmd_xdata cmd_txdata = { .base = { .cmd = IPC_TEST_CMD_XDATA } };
315 unsigned int seed = SEED_TXSTART_VALUE;
316
317 struct ipc_test_cmd_xstat *cmd_rxstat;
318 size_t cmd_rsp_size;
319
320 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
321 "IPC data queue contains unexpected data");
322
323 /* Sending command for the remote to start receiving the data */
324 ret = ipc_service_send(&ep, &cmd_rxstart, sizeof(cmd_rxstart));
325 zassert_equal(ret, sizeof(cmd_rxstart), "ipc_service_send failed: %d, expected: %u", ret,
326 sizeof(cmd_rxstart));
327 /* Check current status */
328 ret = ipc_service_send(&ep, &cmd_rxget, sizeof(cmd_rxget));
329 zassert_equal(ret, sizeof(cmd_rxget), "ipc_service_send failed: %d, expected: %u", ret,
330 sizeof(cmd_rxget));
331 cmd_rxstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000));
332 zassert_not_null(cmd_rxstat, "No command response on time");
333 zassert_equal(cmd_rsp_size, sizeof(*cmd_rxstat),
334 "Unexpected response size: %u, expected: %u", cmd_rsp_size,
335 sizeof(cmd_rxstat));
336 zassert_equal(cmd_rxstat->base.cmd, IPC_TEST_CMD_XSTAT,
337 "Unexpected command in response: %u", cmd_rxstat->base.cmd);
338 zassert_ok(cmd_rxstat->result, "RX result not ok: %d", cmd_rxstat->result);
339 zassert_equal(cmd_rxstat->blk_cnt, cmd_rxstart.blk_cnt,
340 "RX blk_cnt in status does not match start command: %u vs %u",
341 cmd_rxstat->blk_cnt, cmd_rxstart.blk_cnt);
342 data_queue_release(&ipc_data_queue, cmd_rxstat);
343
344 /* Sending data */
345 for (size_t blk = 0; blk < cmd_rxstart.blk_cnt; ++blk) {
346 for (size_t n = 0; n < cmd_rxstart.blk_size; ++n) {
347 cmd_txdata.data[n] = (uint8_t)rand_r(&seed);
348 }
349 do {
350 ret = ipc_service_send(&ep, &cmd_txdata, sizeof(cmd_txdata));
351 } while (ret == -ENOMEM);
352 if ((blk % 1000) == 0) {
353 LOG_INF("Transfer number: %u of %u", blk, cmd_rxstart.blk_cnt);
354 }
355 zassert_equal(ret, sizeof(cmd_txdata), "ipc_service_send failed: %d, expected: %u",
356 ret, sizeof(cmd_txdata));
357 }
358
359 /* Check current status */
360 ret = ipc_service_send(&ep, &cmd_rxget, sizeof(cmd_rxget));
361 zassert_equal(ret, sizeof(cmd_rxget), "ipc_service_send failed: %d, expected: %u", ret,
362 sizeof(cmd_rxget));
363 cmd_rxstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000));
364 zassert_not_null(cmd_rxstat, "No command response on time");
365 zassert_equal(cmd_rsp_size, sizeof(*cmd_rxstat),
366 "Unexpected response size: %u, expected: %u", cmd_rsp_size,
367 sizeof(cmd_rxstat));
368 zassert_equal(cmd_rxstat->base.cmd, IPC_TEST_CMD_XSTAT,
369 "Unexpected command in response: %u", cmd_rxstat->base.cmd);
370 zassert_ok(cmd_rxstat->result, "RX result not ok: %d", cmd_rxstat->result);
371 zassert_equal(cmd_rxstat->blk_cnt, 0,
372 "RX blk_cnt in status does not match start command: %u vs %u",
373 cmd_rxstat->blk_cnt, 0);
374 data_queue_release(&ipc_data_queue, cmd_rxstat);
375 }
376
377 static struct {
378 unsigned int seed;
379 size_t blk_left;
380 } test_rx_long_data;
381 K_SEM_DEFINE(test_rx_long_sem, 0, 1);
382
test_rx_long_rec_cb(const void * data,size_t len,void * priv)383 static void test_rx_long_rec_cb(const void *data, size_t len, void *priv)
384 {
385 const struct test_cmd_xdata *cmd_rxdata = data;
386
387 zassert_true(test_rx_long_data.blk_left > 0, "No data left to interpret");
388 zassert_equal(len, sizeof(*cmd_rxdata),
389 "Unexpected response size: %u, expected: %u", len, sizeof(*cmd_rxdata));
390 zassert_equal(cmd_rxdata->base.cmd, IPC_TEST_CMD_XDATA,
391 "Unexpected command in response: %u", cmd_rxdata->base.cmd);
392 for (size_t n = 0; n < CONFIG_IPC_TEST_BLOCK_SIZE; ++n) {
393 uint8_t expected = (uint8_t)rand_r(&test_rx_long_data.seed);
394
395 zassert_equal(cmd_rxdata->data[n], expected,
396 "Data mismatch at %u while %u blocks left", n,
397 test_rx_long_data.blk_left);
398 }
399
400 if (test_rx_long_data.blk_left % 1000 == 0) {
401 LOG_INF("Receivng left: %u", test_rx_long_data.blk_left);
402 }
403 test_rx_long_data.blk_left -= 1;
404 if (test_rx_long_data.blk_left <= 0) {
405 LOG_INF("Interpretation marked finished");
406 ep_received_override_cb = NULL;
407 k_sem_give(&test_rx_long_sem);
408 }
409 }
410
ZTEST(ipc_sessions,test_rx_long)411 ZTEST(ipc_sessions, test_rx_long)
412 {
413 #define SEED_RXSTART_VALUE 1
414 int ret;
415 static const struct ipc_test_cmd_xstart cmd_txstart = {
416 .base = { .cmd = IPC_TEST_CMD_TXSTART },
417 .blk_size = CONFIG_IPC_TEST_BLOCK_SIZE,
418 .blk_cnt = CONFIG_IPC_TEST_BLOCK_CNT,
419 .seed = SEED_RXSTART_VALUE };
420 static const struct ipc_test_cmd cmd_txget = { IPC_TEST_CMD_TXGET };
421 struct ipc_test_cmd_xstat *cmd_txstat;
422 size_t cmd_rsp_size;
423
424 zassert_not_ok(data_queue_is_empty(&ipc_data_queue),
425 "IPC data queue contains unexpected data");
426
427 /* Configuring the callback to interpret the incoming data */
428 test_rx_long_data.seed = SEED_RXSTART_VALUE;
429 test_rx_long_data.blk_left = cmd_txstart.blk_cnt;
430 ep_received_override_cb = test_rx_long_rec_cb;
431
432 /* Sending command for the remote to start sending the data */
433 ret = ipc_service_send(&ep, &cmd_txstart, sizeof(cmd_txstart));
434 zassert_equal(ret, sizeof(cmd_txstart), "ipc_service_send failed: %d, expected: %u", ret,
435 sizeof(cmd_txstart));
436
437 /* Waiting for all the data */
438 ret = k_sem_take(&test_rx_long_sem, K_SECONDS(30));
439 LOG_INF("Interpretation finished");
440 zassert_ok(ret, "Incoming packet interpretation timeout");
441 zassert_is_null(ep_received_override_cb, "Seems like interpretation callback failed");
442
443 /* Check current status */
444 ret = ipc_service_send(&ep, &cmd_txget, sizeof(cmd_txget));
445 zassert_equal(ret, sizeof(cmd_txget), "ipc_service_send failed: %d, expected: %u", ret,
446 sizeof(cmd_txget));
447 cmd_txstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000));
448 zassert_not_null(cmd_txstat, "No command response on time");
449 zassert_equal(cmd_rsp_size, sizeof(*cmd_txstat),
450 "Unexpected response size: %u, expected: %u", cmd_rsp_size,
451 sizeof(cmd_txstat));
452 zassert_equal(cmd_txstat->base.cmd, IPC_TEST_CMD_XSTAT,
453 "Unexpected command in response: %u", cmd_txstat->base.cmd);
454 zassert_ok(cmd_txstat->result, "RX result not ok: %d", cmd_txstat->result);
455 zassert_equal(cmd_txstat->blk_cnt, 0,
456 "RX blk_cnt in status does not match start command: %u vs %u",
457 cmd_txstat->blk_cnt, 0);
458 data_queue_release(&ipc_data_queue, cmd_txstat);
459 }
460
461
462 ZTEST_SUITE(
463 /* suite_name */ ipc_sessions,
464 /* ztest_suite_predicate_t */ NULL,
465 /* ztest_suite_setup_t */ test_suite_setup,
466 /* ztest_suite_before_t */ test_suite_before,
467 /* ztest_suite_after_t */ NULL,
468 /* ztest_suite_teardown_t */ NULL
469 );
470