blob: 1c597f64ec738b245ffe6b13e62364f1b33dc275 [file] [log] [blame]
/*
* Copyright (c) 2019 Alexander Wachter
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/canbus/isotp.h>
#include <zephyr/ztest.h>
#include <strings.h>
#include "random_data.h"
#include <zephyr/net/buf.h>
#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,
.ide = 0,
.use_ext_addr = 0
};
const struct isotp_msg_id tx_addr = {
.std_id = 0x11,
.ide = 0,
.use_ext_addr = 0
};
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);