diff --git a/tests/subsys/ipc/ipc_sessions/Kconfig b/tests/subsys/ipc/ipc_sessions/Kconfig index bcd6d08379f4..3b3236d6dddf 100644 --- a/tests/subsys/ipc/ipc_sessions/Kconfig +++ b/tests/subsys/ipc/ipc_sessions/Kconfig @@ -20,3 +20,11 @@ config IPC_TEST_SKIP_CORE_RESET help Some of the cores cannot be safely restarted. Skip the tests that require it in such a cases. + +config IPC_TEST_BLOCK_SIZE + int "Block size for multiple transfers test" + default 32 + +config IPC_TEST_BLOCK_CNT + int "Number of blocks for multiple transfers test" + default 8000 diff --git a/tests/subsys/ipc/ipc_sessions/common/test_commands.h b/tests/subsys/ipc/ipc_sessions/common/test_commands.h index d3b9928cd84a..f992d34d52ea 100644 --- a/tests/subsys/ipc/ipc_sessions/common/test_commands.h +++ b/tests/subsys/ipc/ipc_sessions/common/test_commands.h @@ -17,6 +17,14 @@ enum ipc_test_commands { IPC_TEST_CMD_ECHO_RSP, /**< Echo respond */ IPC_TEST_CMD_REBOND, /**< Unbond and rebond back whole interface */ IPC_TEST_CMD_REBOOT, /**< Restart remote CPU after a given delay */ + /* Commands used for data transfer test */ + IPC_TEST_CMD_RXSTART, /**< Start receiving data */ + IPC_TEST_CMD_TXSTART, /**< Start sending data */ + IPC_TEST_CMD_RXGET, /**< Get rx status */ + IPC_TEST_CMD_TXGET, /**< Get tx status */ + IPC_TEST_CMD_XSTAT, /**< rx/tx status response */ + IPC_TEST_CMD_XDATA, /**< Transfer data block */ + /* End of commands used for data transfer test */ }; /** @@ -43,4 +51,31 @@ struct ipc_test_cmd_reboot { uint32_t timeout_ms; }; +/** + * @brief Start the rx or tx transfer + */ +struct ipc_test_cmd_xstart { + struct ipc_test_cmd base; + uint32_t blk_size; + uint32_t blk_cnt; + uint32_t seed; +}; + +/** + * @brief Get the status of rx or tx transfer + */ +struct ipc_test_cmd_xstat { + struct ipc_test_cmd base; + uint32_t blk_cnt; /**< Transfers left */ + int32_t result; /**< Current result */ +}; + +/** + * @brief The result of rx or tx transfer + */ +struct ipc_test_cmd_xrsp { + struct ipc_test_cmd base; + int32_t result; +}; + #endif /* TEST_COMMANDS_H */ diff --git a/tests/subsys/ipc/ipc_sessions/prj.conf b/tests/subsys/ipc/ipc_sessions/prj.conf index 0f1ad696b294..e721953414be 100644 --- a/tests/subsys/ipc/ipc_sessions/prj.conf +++ b/tests/subsys/ipc/ipc_sessions/prj.conf @@ -1,6 +1,9 @@ # Copyright 2021 Carlo Caione # SPDX-License-Identifier: Apache-2.0 +# We need rand_r function +CONFIG_GNU_C_EXTENSIONS=y + CONFIG_ZTEST=y CONFIG_MMU=y CONFIG_IPC_SERVICE=y diff --git a/tests/subsys/ipc/ipc_sessions/remote/prj.conf b/tests/subsys/ipc/ipc_sessions/remote/prj.conf index 6006669b0b8a..b25a421c4c6d 100644 --- a/tests/subsys/ipc/ipc_sessions/remote/prj.conf +++ b/tests/subsys/ipc/ipc_sessions/remote/prj.conf @@ -1,6 +1,8 @@ # Copyright (c) 2024 Nordic Semiconductor ASA # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# We need rand_r function +CONFIG_GNU_C_EXTENSIONS=y CONFIG_PRINTK=y CONFIG_EVENTS=y diff --git a/tests/subsys/ipc/ipc_sessions/remote/src/remote.c b/tests/subsys/ipc/ipc_sessions/remote/src/remote.c index 62164d63c900..2561de430709 100644 --- a/tests/subsys/ipc/ipc_sessions/remote/src/remote.c +++ b/tests/subsys/ipc/ipc_sessions/remote/src/remote.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -18,12 +19,23 @@ LOG_MODULE_REGISTER(remote, LOG_LEVEL_INF); #define IPC_TEST_EV_REBOND 0x01 #define IPC_TEST_EV_BOND 0x02 +#define IPC_TEST_EV_TXTEST 0x04 static const struct device *ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0)); static volatile bool ipc0_bounded; K_SEM_DEFINE(bound_sem, 0, 1); K_EVENT_DEFINE(ipc_ev_req); +struct ipc_xfer_params { + uint32_t blk_size; + uint32_t blk_cnt; + unsigned int seed; + int result; +}; + +static struct ipc_xfer_params ipc_rx_params; +static struct ipc_xfer_params ipc_tx_params; + static struct k_timer timer_reboot; static struct k_timer timer_rebond; @@ -136,8 +148,6 @@ static void ep_recv(const void *data, size_t len, void *priv) return; } - LOG_INF("Command received: %u", cmd->cmd); - switch (cmd->cmd) { case IPC_TEST_CMD_NONE: LOG_INF("Command processing: NONE"); @@ -189,6 +199,99 @@ static void ep_recv(const void *data, size_t len, void *priv) k_timer_start(&timer_reboot, K_MSEC(cmd_reboot->timeout_ms), K_FOREVER); break; } + case IPC_TEST_CMD_RXSTART: { + LOG_INF("Command processing: RXSTART"); + + struct ipc_test_cmd_xstart *cmd_rxstart = (struct ipc_test_cmd_xstart *)cmd; + + ipc_rx_params.blk_size = cmd_rxstart->blk_size; + ipc_rx_params.blk_cnt = cmd_rxstart->blk_cnt; + ipc_rx_params.seed = cmd_rxstart->seed; + ipc_rx_params.result = 0; + break; + } + case IPC_TEST_CMD_TXSTART: { + LOG_INF("Command processing: TXSTART"); + + struct ipc_test_cmd_xstart *cmd_txstart = (struct ipc_test_cmd_xstart *)cmd; + + ipc_tx_params.blk_size = cmd_txstart->blk_size; + ipc_tx_params.blk_cnt = cmd_txstart->blk_cnt; + ipc_tx_params.seed = cmd_txstart->seed; + ipc_tx_params.result = 0; + k_event_set(&ipc_ev_req, IPC_TEST_EV_TXTEST); + break; + } + case IPC_TEST_CMD_RXGET: { + LOG_INF("Command processing: RXGET"); + + int ret; + struct ipc_test_cmd_xstat cmd_stat = { + .base.cmd = IPC_TEST_CMD_XSTAT, + .blk_cnt = ipc_rx_params.blk_cnt, + .result = ipc_rx_params.result + }; + + ret = ipc_service_send(ep, &cmd_stat, sizeof(cmd_stat)); + if (ret < 0) { + LOG_ERR("RXGET response send failed"); + } + break; + } + case IPC_TEST_CMD_TXGET: { + LOG_INF("Command processing: TXGET"); + + int ret; + struct ipc_test_cmd_xstat cmd_stat = { + .base.cmd = IPC_TEST_CMD_XSTAT, + .blk_cnt = ipc_tx_params.blk_cnt, + .result = ipc_tx_params.result + }; + + ret = ipc_service_send(ep, &cmd_stat, sizeof(cmd_stat)); + if (ret < 0) { + LOG_ERR("TXGET response send failed"); + } + break; + } + case IPC_TEST_CMD_XDATA: { + if ((ipc_rx_params.blk_cnt % 1000) == 0) { + /* Logging only every N-th command not to slowdown the transfer too much */ + LOG_INF("Command processing: XDATA (left: %u)", ipc_rx_params.blk_cnt); + } + + /* Ignore if there is an error */ + if (ipc_rx_params.result) { + LOG_ERR("There is error in Rx transfer already"); + break; + } + + if (len != ipc_rx_params.blk_size + offsetof(struct ipc_test_cmd, data)) { + LOG_ERR("Size mismatch"); + ipc_rx_params.result = -EMSGSIZE; + break; + } + + if (ipc_rx_params.blk_cnt <= 0) { + LOG_ERR("Data not expected"); + ipc_rx_params.result = -EFAULT; + break; + } + + /* Check the data */ + for (size_t n = 0; n < ipc_rx_params.blk_size; ++n) { + uint8_t expected = (uint8_t)rand_r(&ipc_rx_params.seed); + + if (cmd->data[n] != expected) { + LOG_ERR("Data value error at %u", n); + ipc_rx_params.result = -EINVAL; + break; + } + } + + ipc_rx_params.blk_cnt -= 1; + break; + } default: LOG_ERR("Unhandled command: %u", cmd->cmd); break; @@ -299,6 +402,45 @@ int main(void) } LOG_INF("Bonding done"); } + if (ev & IPC_TEST_EV_TXTEST) { + LOG_INF("Transfer TX test started"); + + size_t cmd_size = ipc_tx_params.blk_size + offsetof(struct ipc_test_cmd, data); + struct ipc_test_cmd *cmd_data = k_malloc(cmd_size); + + if (!cmd_data) { + LOG_ERR("Cannot create TX test buffer"); + ipc_tx_params.result = -ENOMEM; + continue; + } + + LOG_INF("Initial seed: %u", ipc_tx_params.seed); + + cmd_data->cmd = IPC_TEST_CMD_XDATA; + for (/* No init */; ipc_tx_params.blk_cnt > 0; --ipc_tx_params.blk_cnt) { + int ret; + + if (ipc_tx_params.blk_cnt % 1000 == 0) { + LOG_INF("Sending: %u blocks left", ipc_tx_params.blk_cnt); + } + /* Generate the block data */ + for (size_t n = 0; n < ipc_tx_params.blk_size; ++n) { + cmd_data->data[n] = (uint8_t)rand_r(&ipc_tx_params.seed); + } + do { + ret = ipc_service_send(ep_cfg.priv, cmd_data, cmd_size); + } while (ret == -ENOMEM); + if (ret < 0) { + LOG_ERR("Cannot send TX test buffer: %d", ret); + ipc_tx_params.result = -EIO; + continue; + } + } + + k_free(cmd_data); + + LOG_INF("Transfer TX test finished"); + } } diff --git a/tests/subsys/ipc/ipc_sessions/src/main.c b/tests/subsys/ipc/ipc_sessions/src/main.c index 39a7c5b3838a..a05575abb6ad 100644 --- a/tests/subsys/ipc/ipc_sessions/src/main.c +++ b/tests/subsys/ipc/ipc_sessions/src/main.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -33,6 +34,13 @@ K_MSGQ_DEFINE(ipc_events, sizeof(struct test_ipc_event_state), 16, 4); static uint32_t data_queue_memory[ROUND_UP(CONFIG_IPC_TEST_MSG_HEAP_SIZE, sizeof(uint32_t))]; static struct data_queue ipc_data_queue; +struct test_cmd_xdata { + struct ipc_test_cmd base; + uint8_t data[CONFIG_IPC_TEST_BLOCK_SIZE]; +}; + +static void (*ep_received_override_cb)(const void *data, size_t len, void *priv); + static void ep_bound(void *priv) { int ret; @@ -67,9 +75,13 @@ static void ep_recv(const void *data, size_t len, void *priv) { int ret; - ret = data_queue_put(&ipc_data_queue, data, len, K_NO_WAIT); - __ASSERT(ret >= 0, "Cannot put data into queue: %d", ret); - (void)ret; + if (ep_received_override_cb) { + ep_received_override_cb(data, len, priv); + } else { + ret = data_queue_put(&ipc_data_queue, data, len, K_NO_WAIT); + __ASSERT(ret >= 0, "Cannot put data into queue: %d", ret); + (void)ret; + } } static void ep_error(const char *message, void *priv) @@ -130,6 +142,7 @@ void *test_suite_setup(void) */ void test_suite_before(void *fixture) { + ep_received_override_cb = NULL; k_msgq_purge(&ipc_events); } @@ -258,6 +271,135 @@ ZTEST(ipc_sessions, test_local_rebond) execute_test_ping_pong(); } +ZTEST(ipc_sessions, test_tx_long) +{ + #define SEED_TXSTART_VALUE 1 + int ret; + static const struct ipc_test_cmd_xstart cmd_rxstart = { + .base = { .cmd = IPC_TEST_CMD_RXSTART }, + .blk_size = CONFIG_IPC_TEST_BLOCK_SIZE, + .blk_cnt = CONFIG_IPC_TEST_BLOCK_CNT, + .seed = SEED_TXSTART_VALUE }; + static const struct ipc_test_cmd cmd_rxget = { IPC_TEST_CMD_RXGET }; + struct test_cmd_xdata cmd_txdata = { .base = { .cmd = IPC_TEST_CMD_XDATA } }; + unsigned int seed = SEED_TXSTART_VALUE; + + struct ipc_test_cmd_xstat *cmd_rxstat; + size_t cmd_rsp_size; + + zassert_not_ok(data_queue_is_empty(&ipc_data_queue), "IPC data queue contains unexpected data"); + + /* Sending command for the remote to start receiving the data */ + ret = ipc_service_send(&ep, &cmd_rxstart, sizeof(cmd_rxstart)); + zassert_equal(ret, sizeof(cmd_rxstart), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_rxstart)); + /* Check current status */ + ret = ipc_service_send(&ep, &cmd_rxget, sizeof(cmd_rxget)); + zassert_equal(ret, sizeof(cmd_rxget), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_rxget)); + cmd_rxstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000)); + zassert_not_null(cmd_rxstat, "No command response on time"); + zassert_equal(cmd_rsp_size, sizeof(*cmd_rxstat), "Unexpected response size: %u, expected: %u", cmd_rsp_size, sizeof(cmd_rxstat)); + zassert_equal(cmd_rxstat->base.cmd, IPC_TEST_CMD_XSTAT, "Unexpected command in response: %u", cmd_rxstat->base.cmd); + zassert_ok(cmd_rxstat->result, "RX result not ok: %d", cmd_rxstat->result); + zassert_equal(cmd_rxstat->blk_cnt, cmd_rxstart.blk_cnt, "RX blk_cnt in status does not match start command: %u vs %u", cmd_rxstat->blk_cnt, cmd_rxstart.blk_cnt); + data_queue_release(&ipc_data_queue, cmd_rxstat); + + /* Sending data */ + for (size_t blk = 0; blk < cmd_rxstart.blk_cnt; ++blk) { + for (size_t n = 0; n < cmd_rxstart.blk_size; ++n) { + cmd_txdata.data[n] = (uint8_t)rand_r(&seed); + } + do { + ret = ipc_service_send(&ep, &cmd_txdata, sizeof(cmd_txdata)); + } while (ret == -ENOMEM); + if ((blk % 1000) == 0) { + LOG_INF("Transfer number: %u of %u", blk, cmd_rxstart.blk_cnt); + } + zassert_equal(ret, sizeof(cmd_txdata), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_txdata)); + } + + /* Check current status */ + ret = ipc_service_send(&ep, &cmd_rxget, sizeof(cmd_rxget)); + zassert_equal(ret, sizeof(cmd_rxget), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_rxget)); + cmd_rxstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000)); + zassert_not_null(cmd_rxstat, "No command response on time"); + zassert_equal(cmd_rsp_size, sizeof(*cmd_rxstat), "Unexpected response size: %u, expected: %u", cmd_rsp_size, sizeof(cmd_rxstat)); + zassert_equal(cmd_rxstat->base.cmd, IPC_TEST_CMD_XSTAT, "Unexpected command in response: %u", cmd_rxstat->base.cmd); + zassert_ok(cmd_rxstat->result, "RX result not ok: %d", cmd_rxstat->result); + zassert_equal(cmd_rxstat->blk_cnt, 0, "RX blk_cnt in status does not match start command: %u vs %u", cmd_rxstat->blk_cnt, 0); + data_queue_release(&ipc_data_queue, cmd_rxstat); +} + +static struct { + unsigned int seed; + size_t blk_left; +} test_rx_long_data; +K_SEM_DEFINE(test_rx_long_sem, 0, 1); + +static void test_rx_long_rec_cb(const void *data, size_t len, void *priv) +{ + const struct test_cmd_xdata *cmd_rxdata = data; + + zassert_true(test_rx_long_data.blk_left > 0, "No data left to interpret"); + zassert_equal(len, sizeof(*cmd_rxdata), "Unexpected response size: %u, expected: %u", len, sizeof(*cmd_rxdata)); + zassert_equal(cmd_rxdata->base.cmd, IPC_TEST_CMD_XDATA, "Unexpected command in response: %u", cmd_rxdata->base.cmd); + for (size_t n = 0; n < CONFIG_IPC_TEST_BLOCK_SIZE; ++n) { + uint8_t expected = (uint8_t)rand_r(&test_rx_long_data.seed); + zassert_equal(cmd_rxdata->data[n], expected, "Data mismatch at %u while %u blocks left", n, test_rx_long_data.blk_left); + } + + if (test_rx_long_data.blk_left % 1000 == 0) { + LOG_INF("Receivng left: %u", test_rx_long_data.blk_left); + } + test_rx_long_data.blk_left -= 1; + if (test_rx_long_data.blk_left <= 0) { + LOG_INF("Interpretation marked finished"); + ep_received_override_cb = NULL; + k_sem_give(&test_rx_long_sem); + } +} + +ZTEST(ipc_sessions, test_rx_long) +{ + #define SEED_RXSTART_VALUE 1 + int ret; + static const struct ipc_test_cmd_xstart cmd_txstart = { + .base = { .cmd = IPC_TEST_CMD_TXSTART }, + .blk_size = CONFIG_IPC_TEST_BLOCK_SIZE, + .blk_cnt = CONFIG_IPC_TEST_BLOCK_CNT, + .seed = SEED_RXSTART_VALUE }; + static const struct ipc_test_cmd cmd_txget = { IPC_TEST_CMD_TXGET }; + struct ipc_test_cmd_xstat *cmd_txstat; + size_t cmd_rsp_size; + + zassert_not_ok(data_queue_is_empty(&ipc_data_queue), "IPC data queue contains unexpected data"); + + /* Configuring the callback to interpret the incomming data */ + test_rx_long_data.seed = SEED_RXSTART_VALUE; + test_rx_long_data.blk_left = cmd_txstart.blk_cnt; + ep_received_override_cb = test_rx_long_rec_cb; + + /* Sending command for the remote to start sending the data */ + ret = ipc_service_send(&ep, &cmd_txstart, sizeof(cmd_txstart)); + zassert_equal(ret, sizeof(cmd_txstart), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_txstart)); + + /* Waiting for all the data */ + ret = k_sem_take(&test_rx_long_sem, K_SECONDS(30)); + LOG_INF("Interpretation finished"); + zassert_ok(ret, "Incomming packet interpretation timeout"); + zassert_is_null(ep_received_override_cb, "Seems like interpretation callback failed"); + + /* Check current status */ + ret = ipc_service_send(&ep, &cmd_txget, sizeof(cmd_txget)); + zassert_equal(ret, sizeof(cmd_txget), "ipc_service_send failed: %d, expected: %u", ret, sizeof(cmd_txget)); + cmd_txstat = data_queue_get(&ipc_data_queue, &cmd_rsp_size, K_MSEC(1000)); + zassert_not_null(cmd_txstat, "No command response on time"); + zassert_equal(cmd_rsp_size, sizeof(*cmd_txstat), "Unexpected response size: %u, expected: %u", cmd_rsp_size, sizeof(cmd_txstat)); + zassert_equal(cmd_txstat->base.cmd, IPC_TEST_CMD_XSTAT, "Unexpected command in response: %u", cmd_txstat->base.cmd); + zassert_ok(cmd_txstat->result, "RX result not ok: %d", cmd_txstat->result); + zassert_equal(cmd_txstat->blk_cnt, 0, "RX blk_cnt in status does not match start command: %u vs %u", cmd_txstat->blk_cnt, 0); + data_queue_release(&ipc_data_queue, cmd_txstat); +} + ZTEST_SUITE( /* suite_name */ ipc_sessions,