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/devicetree.h>
11 #include <zephyr/ipc/ipc_service.h>
12 #include <zephyr/drivers/watchdog.h>
13 #include <zephyr/sys/reboot.h>
14 
15 #include <test_commands.h>
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(remote, LOG_LEVEL_INF);
19 
20 #define IPC_TEST_EV_REBOND 0x01
21 #define IPC_TEST_EV_BOND   0x02
22 #define IPC_TEST_EV_TXTEST 0x04
23 
24 static const struct device *ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
25 static volatile bool ipc0_bounded;
26 K_SEM_DEFINE(bound_sem, 0, 1);
27 K_EVENT_DEFINE(ipc_ev_req);
28 
29 struct ipc_xfer_params {
30 	uint32_t blk_size;
31 	uint32_t blk_cnt;
32 	unsigned int seed;
33 	int result;
34 };
35 
36 static struct ipc_xfer_params ipc_rx_params;
37 static struct ipc_xfer_params ipc_tx_params;
38 
39 static struct k_timer timer_reboot;
40 static struct k_timer timer_rebond;
41 
42 static void ep_bound(void *priv);
43 static void ep_unbound(void *priv);
44 static void ep_recv(const void *data, size_t len, void *priv);
45 static void ep_error(const char *message, void *priv);
46 
47 static struct ipc_ept_cfg ep_cfg = {
48 	.cb = {
49 		.bound = ep_bound,
50 		.unbound = ep_unbound,
51 		.received = ep_recv,
52 		.error = ep_error
53 	},
54 };
55 
56 /**
57  * @brief Trying to reset by WDT
58  *
59  * @note If this function return, it means it fails
60  */
reboot_by_wdt(void)61 static int reboot_by_wdt(void)
62 {
63 	int err;
64 	static const struct device *const wdt =
65 		COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_ALIAS(watchdog0)),
66 			    (DEVICE_DT_GET(DT_ALIAS(watchdog0))), (NULL));
67 	static const struct wdt_timeout_cfg m_cfg_wdt = {
68 		.callback = NULL,
69 		.flags = WDT_FLAG_RESET_SOC,
70 		.window.max = 10,
71 	};
72 	static const uint8_t wdt_options[] = {
73 		WDT_OPT_PAUSE_HALTED_BY_DBG | WDT_OPT_PAUSE_IN_SLEEP,
74 		WDT_OPT_PAUSE_IN_SLEEP,
75 		0
76 	};
77 
78 	if (!wdt) {
79 		return -ENOTSUP;
80 	}
81 
82 	if (!device_is_ready(wdt)) {
83 		LOG_ERR("WDT device is not ready");
84 		return -EIO;
85 	}
86 
87 	err = wdt_install_timeout(wdt, &m_cfg_wdt);
88 	if (err < 0) {
89 		LOG_ERR("WDT install error");
90 		return -EIO;
91 	}
92 
93 	for (size_t i = 0; i < ARRAY_SIZE(wdt_options); ++i) {
94 		err = wdt_setup(wdt, wdt_options[i]);
95 		if (err < 0) {
96 			LOG_ERR("Failed WDT setup with options = %u", wdt_options[i]);
97 		} else {
98 			/* We are ok with the configuration:
99 			 * just wait for the WDT to trigger
100 			 */
101 			for (;;) {
102 				k_cpu_idle();
103 			}
104 		}
105 	}
106 
107 	return -EIO;
108 }
109 
110 /**
111  * @brief Just force to reboot, anyway you find possible
112  */
reboot_anyway(void)113 FUNC_NORETURN static void reboot_anyway(void)
114 {
115 	reboot_by_wdt();
116 	/* If WDT restart fails - try another way */
117 	sys_reboot(SYS_REBOOT_COLD);
118 }
119 
ep_bound(void * priv)120 static void ep_bound(void *priv)
121 {
122 	ipc0_bounded = true;
123 	k_sem_give(&bound_sem);
124 
125 	LOG_INF("Endpoint bounded");
126 }
127 
ep_unbound(void * priv)128 static void ep_unbound(void *priv)
129 {
130 	ipc0_bounded = false;
131 	k_sem_give(&bound_sem);
132 
133 	LOG_INF("Endpoint unbounded");
134 
135 	/* Try to restore the connection */
136 	k_event_set(&ipc_ev_req, IPC_TEST_EV_BOND);
137 }
138 
ep_recv(const void * data,size_t len,void * priv)139 static void ep_recv(const void *data, size_t len, void *priv)
140 {
141 	int ret;
142 	const struct ipc_test_cmd *cmd = data;
143 	struct ipc_ept *ep = priv;
144 
145 	if (len < sizeof(struct ipc_test_cmd)) {
146 		LOG_ERR("The unexpected size of received data: %u < %u", len,
147 			sizeof(struct ipc_test_cmd));
148 		/* Dropping further processing */
149 		return;
150 	}
151 
152 	switch (cmd->cmd) {
153 	case IPC_TEST_CMD_NONE:
154 		LOG_INF("Command processing: NONE");
155 		/* Ignore */
156 		break;
157 	case IPC_TEST_CMD_PING: {
158 		LOG_INF("Command processing: PING");
159 
160 		static const struct ipc_test_cmd cmd_pong = {IPC_TEST_CMD_PONG};
161 
162 		ret = ipc_service_send(ep, &cmd_pong, sizeof(cmd_pong));
163 		if (ret < 0) {
164 			LOG_ERR("PONG response failed: %d", ret);
165 		}
166 		break;
167 	}
168 	case IPC_TEST_CMD_ECHO: {
169 		LOG_INF("Command processing: ECHO");
170 
171 		struct ipc_test_cmd *cmd_rsp = k_malloc(len);
172 
173 		if (!cmd_rsp) {
174 			LOG_ERR("ECHO response failed: memory allocation");
175 			break;
176 		}
177 
178 		cmd_rsp->cmd = IPC_TEST_CMD_ECHO_RSP;
179 		memcpy(cmd_rsp->data, cmd->data, len - sizeof(struct ipc_test_cmd));
180 		ret = ipc_service_send(ep, cmd_rsp, len);
181 		k_free(cmd_rsp);
182 		if (ret < 0) {
183 			LOG_ERR("ECHO response failed: %d", ret);
184 		}
185 		break;
186 	}
187 	case IPC_TEST_CMD_REBOND: {
188 		LOG_INF("Command processing: REBOOT");
189 
190 		struct ipc_test_cmd_rebond *cmd_rebond = (struct ipc_test_cmd_rebond *)cmd;
191 
192 		k_timer_start(&timer_rebond, K_MSEC(cmd_rebond->timeout_ms), K_FOREVER);
193 		break;
194 	}
195 	case IPC_TEST_CMD_REBOOT: {
196 		LOG_INF("Command processing: REBOOT");
197 
198 		struct ipc_test_cmd_reboot *cmd_reboot = (struct ipc_test_cmd_reboot *)cmd;
199 
200 		k_timer_start(&timer_reboot, K_MSEC(cmd_reboot->timeout_ms), K_FOREVER);
201 		break;
202 	}
203 	case IPC_TEST_CMD_RXSTART: {
204 		LOG_INF("Command processing: RXSTART");
205 
206 		struct ipc_test_cmd_xstart *cmd_rxstart = (struct ipc_test_cmd_xstart *)cmd;
207 
208 		ipc_rx_params.blk_size = cmd_rxstart->blk_size;
209 		ipc_rx_params.blk_cnt = cmd_rxstart->blk_cnt;
210 		ipc_rx_params.seed = cmd_rxstart->seed;
211 		ipc_rx_params.result = 0;
212 		break;
213 	}
214 	case IPC_TEST_CMD_TXSTART: {
215 		LOG_INF("Command processing: TXSTART");
216 
217 		struct ipc_test_cmd_xstart *cmd_txstart = (struct ipc_test_cmd_xstart *)cmd;
218 
219 		ipc_tx_params.blk_size = cmd_txstart->blk_size;
220 		ipc_tx_params.blk_cnt = cmd_txstart->blk_cnt;
221 		ipc_tx_params.seed = cmd_txstart->seed;
222 		ipc_tx_params.result = 0;
223 		k_event_set(&ipc_ev_req, IPC_TEST_EV_TXTEST);
224 		break;
225 	}
226 	case IPC_TEST_CMD_RXGET: {
227 		LOG_INF("Command processing: RXGET");
228 
229 		int ret;
230 		struct ipc_test_cmd_xstat cmd_stat = {
231 			.base.cmd = IPC_TEST_CMD_XSTAT,
232 			.blk_cnt = ipc_rx_params.blk_cnt,
233 			.result = ipc_rx_params.result
234 		};
235 
236 		ret = ipc_service_send(ep, &cmd_stat, sizeof(cmd_stat));
237 		if (ret < 0) {
238 			LOG_ERR("RXGET response send failed");
239 		}
240 		break;
241 	}
242 	case IPC_TEST_CMD_TXGET: {
243 		LOG_INF("Command processing: TXGET");
244 
245 		int ret;
246 		struct ipc_test_cmd_xstat cmd_stat = {
247 			.base.cmd = IPC_TEST_CMD_XSTAT,
248 			.blk_cnt = ipc_tx_params.blk_cnt,
249 			.result = ipc_tx_params.result
250 		};
251 
252 		ret = ipc_service_send(ep, &cmd_stat, sizeof(cmd_stat));
253 		if (ret < 0) {
254 			LOG_ERR("TXGET response send failed");
255 		}
256 		break;
257 	}
258 	case IPC_TEST_CMD_XDATA: {
259 		if ((ipc_rx_params.blk_cnt % 1000) == 0) {
260 			/* Logging only every N-th command not to slowdown the transfer too much */
261 			LOG_INF("Command processing: XDATA (left: %u)", ipc_rx_params.blk_cnt);
262 		}
263 
264 		/* Ignore if there is an error */
265 		if (ipc_rx_params.result) {
266 			LOG_ERR("There is error in Rx transfer already");
267 			break;
268 		}
269 
270 		if (len != ipc_rx_params.blk_size + offsetof(struct ipc_test_cmd, data)) {
271 			LOG_ERR("Size mismatch");
272 			ipc_rx_params.result = -EMSGSIZE;
273 			break;
274 		}
275 
276 		if (ipc_rx_params.blk_cnt <= 0) {
277 			LOG_ERR("Data not expected");
278 			ipc_rx_params.result = -EFAULT;
279 			break;
280 		}
281 
282 		/* Check the data */
283 		for (size_t n = 0; n < ipc_rx_params.blk_size; ++n) {
284 			uint8_t expected = (uint8_t)rand_r(&ipc_rx_params.seed);
285 
286 			if (cmd->data[n] != expected) {
287 				LOG_ERR("Data value error at %u", n);
288 				ipc_rx_params.result = -EINVAL;
289 				break;
290 			}
291 		}
292 
293 		ipc_rx_params.blk_cnt -= 1;
294 		break;
295 	}
296 	default:
297 		LOG_ERR("Unhandled command: %u", cmd->cmd);
298 		break;
299 	}
300 }
301 
ep_error(const char * message,void * priv)302 static void ep_error(const char *message, void *priv)
303 {
304 	LOG_ERR("EP error: \"%s\"", message);
305 }
306 
init_ipc(void)307 static int init_ipc(void)
308 {
309 	int ret;
310 	static struct ipc_ept ep;
311 
312 	/* Store the pointer to the endpoint */
313 	ep_cfg.priv = &ep;
314 
315 	LOG_INF("IPC-sessions test remote started");
316 
317 	ret = ipc_service_open_instance(ipc0_instance);
318 	if ((ret < 0) && (ret != -EALREADY)) {
319 		LOG_ERR("ipc_service_open_instance() failure: %d", ret);
320 		return ret;
321 	}
322 
323 	ret = ipc_service_register_endpoint(ipc0_instance, &ep, &ep_cfg);
324 	if (ret < 0) {
325 		LOG_ERR("ipc_service_register_endpoint() failure: %d", ret);
326 		return ret;
327 	}
328 
329 	do {
330 		k_sem_take(&bound_sem, K_FOREVER);
331 	} while (!ipc0_bounded);
332 
333 	LOG_INF("IPC connection estabilished");
334 
335 	return 0;
336 }
337 
timer_rebond_cb(struct k_timer * timer)338 static void timer_rebond_cb(struct k_timer *timer)
339 {
340 	(void)timer;
341 	LOG_INF("Setting rebond request");
342 	k_event_set(&ipc_ev_req, IPC_TEST_EV_REBOND);
343 }
344 
timer_reboot_cb(struct k_timer * timer)345 static void timer_reboot_cb(struct k_timer *timer)
346 {
347 	(void)timer;
348 	LOG_INF("Resetting CPU");
349 	reboot_anyway();
350 	__ASSERT(0, "Still working after reboot request");
351 }
352 
353 
main(void)354 int main(void)
355 {
356 	int ret;
357 
358 	k_timer_init(&timer_rebond, timer_rebond_cb, NULL);
359 	k_timer_init(&timer_reboot, timer_reboot_cb, NULL);
360 	ret = init_ipc();
361 	if (ret) {
362 		return ret;
363 	}
364 
365 	while (1) {
366 		uint32_t ev;
367 
368 		ev = k_event_wait(&ipc_ev_req, ~0U, false, K_FOREVER);
369 		k_event_clear(&ipc_ev_req, ev);
370 
371 		if (ev & IPC_TEST_EV_REBOND) {
372 			/* Rebond now */
373 			ret = ipc_service_deregister_endpoint(ep_cfg.priv);
374 			if (ret) {
375 				LOG_ERR("ipc_service_deregister_endpoint() failure: %d", ret);
376 				continue;
377 			}
378 			ipc0_bounded = false;
379 
380 			ret = ipc_service_register_endpoint(ipc0_instance, ep_cfg.priv, &ep_cfg);
381 			if (ret < 0) {
382 				LOG_ERR("ipc_service_register_endpoint() failure: %d", ret);
383 				return ret;
384 			}
385 
386 			do {
387 				k_sem_take(&bound_sem, K_FOREVER);
388 			} while (!ipc0_bounded);
389 		}
390 		if (ev & IPC_TEST_EV_BOND) {
391 			LOG_INF("Bonding endpoint");
392 			/* Bond missing endpoint */
393 			if (!ipc0_bounded) {
394 				ret = ipc_service_register_endpoint(ipc0_instance, ep_cfg.priv,
395 								    &ep_cfg);
396 				if (ret < 0) {
397 					LOG_ERR("ipc_service_register_endpoint() failure: %d", ret);
398 					return ret;
399 				}
400 
401 				do {
402 					k_sem_take(&bound_sem, K_FOREVER);
403 				} while (!ipc0_bounded);
404 			}
405 			LOG_INF("Bonding done");
406 		}
407 		if (ev & IPC_TEST_EV_TXTEST) {
408 			LOG_INF("Transfer TX test started");
409 
410 			size_t cmd_size = ipc_tx_params.blk_size + offsetof(struct ipc_test_cmd,
411 									    data);
412 			struct ipc_test_cmd *cmd_data = k_malloc(cmd_size);
413 
414 			if (!cmd_data) {
415 				LOG_ERR("Cannot create TX test buffer");
416 				ipc_tx_params.result = -ENOMEM;
417 				continue;
418 			}
419 
420 			LOG_INF("Initial seed: %u", ipc_tx_params.seed);
421 
422 			cmd_data->cmd = IPC_TEST_CMD_XDATA;
423 			while (ipc_tx_params.blk_cnt > 0) {
424 				int ret;
425 
426 				if (ipc_tx_params.blk_cnt % 1000 == 0) {
427 					LOG_INF("Sending: %u blocks left", ipc_tx_params.blk_cnt);
428 				}
429 				/* Generate the block data */
430 				for (size_t n = 0; n < ipc_tx_params.blk_size; ++n) {
431 					cmd_data->data[n] = (uint8_t)rand_r(&ipc_tx_params.seed);
432 				}
433 				--ipc_tx_params.blk_cnt;
434 				do {
435 					ret = ipc_service_send(ep_cfg.priv, cmd_data, cmd_size);
436 					Z_SPIN_DELAY(1);
437 				} while (ret == -ENOMEM);
438 				if (ret < 0) {
439 					LOG_ERR("Cannot send TX test buffer: %d", ret);
440 					ipc_tx_params.result = -EIO;
441 					continue;
442 				}
443 			}
444 
445 			k_free(cmd_data);
446 
447 			LOG_INF("Transfer TX test finished");
448 		}
449 
450 
451 	}
452 
453 	return 0;
454 }
455