/* * Copyright (c) 2019 Alexander Wachter * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "random_data.h" #include #define NUMBER_OF_REPETITIONS 5 #define DATA_SIZE_SF 7 /* * @addtogroup t_can * @{ * @defgroup t_can_isotp test_can_isotp * @brief TestPurpose: struggle the implementation and see if it breaks apart. * @details * - Test Steps * -# * - Expected Results * -# * @} */ const struct device *const can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus)); const struct isotp_fc_opts fc_opts = { .bs = 8, .stmin = 0 }; const struct isotp_fc_opts fc_opts_single = { .bs = 0, .stmin = 1 }; const struct isotp_msg_id rx_addr = { .std_id = 0x10, }; const struct isotp_msg_id tx_addr = { .std_id = 0x11, }; struct isotp_recv_ctx recv_ctx; struct isotp_send_ctx send_ctx; uint8_t data_buf[128]; void send_complete_cb(int error_nr, void *arg) { zassert_equal(error_nr, ISOTP_N_OK, "Sending failed (%d)", error_nr); } static void send_sf(const struct device *can_dev) { int ret; ret = isotp_send(&send_ctx, can_dev, random_data, DATA_SIZE_SF, &rx_addr, &tx_addr, send_complete_cb, NULL); zassert_equal(ret, 0, "Send returned %d", ret); } static void get_sf_net(struct isotp_recv_ctx *recv_ctx) { struct net_buf *buf; int remaining_len, ret; remaining_len = isotp_recv_net(recv_ctx, &buf, K_MSEC(1000)); zassert_true(remaining_len >= 0, "recv returned %d", remaining_len); zassert_equal(remaining_len, 0, "SF should fit in one frame"); zassert_equal(buf->len, DATA_SIZE_SF, "Data length (%d) should be %d.", buf->len, DATA_SIZE_SF); ret = memcmp(random_data, buf->data, buf->len); zassert_equal(ret, 0, "received data differ"); memset(buf->data, 0, buf->len); net_buf_unref(buf); } static void get_sf(struct isotp_recv_ctx *recv_ctx) { int ret; uint8_t *data_buf_ptr = data_buf; memset(data_buf, 0, sizeof(data_buf)); ret = isotp_recv(recv_ctx, data_buf_ptr++, 1, K_MSEC(1000)); zassert_equal(ret, 1, "recv returned %d", ret); ret = isotp_recv(recv_ctx, data_buf_ptr++, sizeof(data_buf) - 1, K_MSEC(1000)); zassert_equal(ret, DATA_SIZE_SF - 1, "recv returned %d", ret); ret = memcmp(random_data, data_buf, DATA_SIZE_SF); zassert_equal(ret, 0, "received data differ"); } void print_hex(const uint8_t *ptr, size_t len) { while (len--) { printk("%02x", *ptr++); } } static void send_test_data(const struct device *can_dev, const uint8_t *data, size_t len) { int ret; ret = isotp_send(&send_ctx, can_dev, data, len, &rx_addr, &tx_addr, send_complete_cb, NULL); zassert_equal(ret, 0, "Send returned %d", ret); } static const uint8_t *check_frag(struct net_buf *frag, const uint8_t *data) { int ret; ret = memcmp(data, frag->data, frag->len); if (ret) { printk("expected bytes:\n"); print_hex(data, frag->len); printk("\nreceived (%d bytes):\n", frag->len); print_hex(frag->data, frag->len); printk("\n"); } zassert_equal(ret, 0, "Received data differ"); return data + frag->len; } static void receive_test_data_net(struct isotp_recv_ctx *recv_ctx, const uint8_t *data, size_t len, int32_t delay) { int remaining_len; size_t received_len = 0; const uint8_t *data_ptr = data; struct net_buf *buf; do { remaining_len = isotp_recv_net(recv_ctx, &buf, K_MSEC(1000)); zassert_true(remaining_len >= 0, "recv error: %d", remaining_len); received_len += buf->len; zassert_equal(received_len + remaining_len, len, "Length mismatch"); data_ptr = check_frag(buf, data_ptr); if (delay) { k_msleep(delay); } memset(buf->data, 0, buf->len); net_buf_unref(buf); } while (remaining_len); remaining_len = isotp_recv_net(recv_ctx, &buf, K_MSEC(50)); zassert_equal(remaining_len, ISOTP_RECV_TIMEOUT, "Expected timeout but got %d", remaining_len); } static void check_data(const uint8_t *recv_data, const uint8_t *send_data, size_t len) { int ret; ret = memcmp(send_data, recv_data, len); if (ret) { printk("expected bytes:\n"); print_hex(send_data, len); printk("\nreceived (%zu bytes):\n", len); print_hex(recv_data, len); printk("\n"); } zassert_equal(ret, 0, "Received data differ"); } static void receive_test_data(struct isotp_recv_ctx *recv_ctx, const uint8_t *data, size_t len, int32_t delay) { size_t remaining_len = len; int ret; const uint8_t *data_ptr = data; do { memset(data_buf, 0, sizeof(data_buf)); ret = isotp_recv(recv_ctx, data_buf, sizeof(data_buf), K_MSEC(1000)); zassert_true(ret >= 0, "recv error: %d", ret); zassert_true(remaining_len >= ret, "More data then expected"); check_data(data_buf, data_ptr, ret); data_ptr += ret; remaining_len -= ret; if (delay) { k_msleep(delay); } } while (remaining_len); ret = isotp_recv(recv_ctx, data_buf, sizeof(data_buf), K_MSEC(50)); zassert_equal(ret, ISOTP_RECV_TIMEOUT, "Expected timeout but got %d", ret); } ZTEST(isotp_implementation, test_send_receive_net_sf) { int ret, i; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Bind returned %d", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_sf(can_dev); get_sf_net(&recv_ctx); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_send_receive_sf) { int ret, i; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Bind returned %d", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_sf(can_dev); get_sf(&recv_ctx); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_send_receive_net_blocks) { int ret, i; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_test_data(can_dev, random_data, sizeof(random_data)); receive_test_data_net(&recv_ctx, random_data, sizeof(random_data), 0); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_send_receive_blocks) { const size_t data_size = sizeof(data_buf) * 2 + 10; int ret, i; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_test_data(can_dev, random_data, data_size); receive_test_data(&recv_ctx, random_data, data_size, 0); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_send_receive_net_single_blocks) { const size_t send_len = CONFIG_ISOTP_RX_BUF_COUNT * CONFIG_ISOTP_RX_BUF_SIZE + 6; int ret, i; size_t buf_len; struct net_buf *buf, *frag; const uint8_t *data_ptr; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts_single, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_test_data(can_dev, random_data, send_len); data_ptr = random_data; ret = isotp_recv_net(&recv_ctx, &buf, K_MSEC(1000)); zassert_equal(ret, 0, "recv returned %d", ret); buf_len = net_buf_frags_len(buf); zassert_equal(buf_len, send_len, "Data length differ"); frag = buf; do { data_ptr = check_frag(frag, data_ptr); memset(frag->data, 0, frag->len); } while ((frag = frag->frags)); net_buf_unref(buf); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_send_receive_single_block) { const size_t send_len = CONFIG_ISOTP_RX_BUF_COUNT * CONFIG_ISOTP_RX_BUF_SIZE + 6; int ret, i; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts_single, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { send_test_data(can_dev, random_data, send_len); memset(data_buf, 0, sizeof(data_buf)); ret = isotp_recv(&recv_ctx, data_buf, sizeof(data_buf), K_MSEC(1000)); zassert_equal(ret, send_len, "data should be received at once (ret: %d)", ret); ret = memcmp(random_data, data_buf, send_len); zassert_equal(ret, 0, "Data differ"); } isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_bind_unbind) { int ret, i; for (i = 0; i < 100; i++) { ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); isotp_unbind(&recv_ctx); } for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_sf(can_dev); k_sleep(K_MSEC(100)); get_sf_net(&recv_ctx); isotp_unbind(&recv_ctx); } for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_sf(can_dev); k_sleep(K_MSEC(100)); get_sf(&recv_ctx); isotp_unbind(&recv_ctx); } for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_test_data(can_dev, random_data, 60); k_sleep(K_MSEC(100)); receive_test_data_net(&recv_ctx, random_data, 60, 0); isotp_unbind(&recv_ctx); } for (i = 0; i < NUMBER_OF_REPETITIONS; i++) { ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_test_data(can_dev, random_data, 60); k_sleep(K_MSEC(100)); receive_test_data(&recv_ctx, random_data, 60, 0); isotp_unbind(&recv_ctx); } } ZTEST(isotp_implementation, test_buffer_allocation) { int ret; size_t send_data_length = CONFIG_ISOTP_RX_BUF_COUNT * CONFIG_ISOTP_RX_BUF_SIZE * 3 + 6; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_test_data(can_dev, random_data, send_data_length); k_msleep(100); receive_test_data_net(&recv_ctx, random_data, send_data_length, 200); isotp_unbind(&recv_ctx); } ZTEST(isotp_implementation, test_buffer_allocation_wait) { int ret; size_t send_data_length = CONFIG_ISOTP_RX_BUF_COUNT * CONFIG_ISOTP_RX_BUF_SIZE * 2 + 6; ret = isotp_bind(&recv_ctx, can_dev, &rx_addr, &tx_addr, &fc_opts, K_NO_WAIT); zassert_equal(ret, 0, "Binding failed (%d)", ret); send_test_data(can_dev, random_data, send_data_length); k_sleep(K_MSEC(100)); receive_test_data_net(&recv_ctx, random_data, send_data_length, 2000); isotp_unbind(&recv_ctx); } void *isotp_implementation_setup(void) { int ret; zassert_true(sizeof(random_data) >= sizeof(data_buf) * 2 + 10, "Test data size to small"); zassert_true(device_is_ready(can_dev), "CAN device not ready"); ret = can_set_mode(can_dev, CAN_MODE_LOOPBACK); zassert_equal(ret, 0, "Configuring loopback mode failed (%d)", ret); ret = can_start(can_dev); zassert_equal(ret, 0, "Failed to start CAN controller [%d]", ret); return NULL; } ZTEST_SUITE(isotp_implementation, NULL, isotp_implementation_setup, NULL, NULL, NULL);