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