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