blob: 8d3c6160680376e66113bd2f083a3553cefba230 [file] [log] [blame]
/*
* Copyright (c) 2022 Vestas Wind Systems A/S
* Copyright (c) 2019 Alexander Wachter
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/can.h>
#include <ztest.h>
/**
* @addtogroup t_can_driver
* @{
* @defgroup t_can_stm32 test_can_stm32
* @}
*/
/**
* @brief Test timeouts.
*/
#define TEST_SEND_TIMEOUT K_MSEC(100)
#define TEST_RECEIVE_TIMEOUT K_MSEC(100)
/**
* @brief Standard (11-bit) CAN IDs and masks used for testing.
*/
#define TEST_CAN_STD_ID 0x555
#define TEST_CAN_SOME_STD_ID 0x123
/**
* @brief Extended (29-bit) CAN IDs and masks used for testing.
*/
#define TEST_CAN_EXT_ID 0x15555555
#define TEST_CAN_EXT_MASK 0x1FFFFFF0
/**
* @brief Global variables.
*/
CAN_MSGQ_DEFINE(can_msgq, 5);
/**
* @brief Standard (11-bit) CAN ID test frame.
*/
struct zcan_frame test_std_frame = {
.id_type = CAN_STANDARD_IDENTIFIER,
.rtr = CAN_DATAFRAME,
.id = TEST_CAN_STD_ID,
.dlc = 8,
.data = { 1, 2, 3, 4, 5, 6, 7, 8 }
};
/**
* @brief Standard (11-bit) CAN ID filter.
*/
const struct zcan_filter test_std_filter = {
.id_type = CAN_STANDARD_IDENTIFIER,
.rtr = CAN_DATAFRAME,
.id = TEST_CAN_STD_ID,
.rtr_mask = 1,
.id_mask = CAN_STD_ID_MASK
};
/**
* @brief Extended (29-bit) CAN ID filter.
*/
const struct zcan_filter test_ext_filter = {
.id_type = CAN_EXTENDED_IDENTIFIER,
.rtr = CAN_DATAFRAME,
.id = TEST_CAN_EXT_ID,
.rtr_mask = 1,
.id_mask = CAN_EXT_ID_MASK
};
/**
* @brief Extended (29-bit) CAN ID masked filter.
*/
const struct zcan_filter test_ext_masked_filter = {
.id_type = CAN_EXTENDED_IDENTIFIER,
.rtr = CAN_DATAFRAME,
.id = TEST_CAN_EXT_ID,
.rtr_mask = 1,
.id_mask = TEST_CAN_EXT_MASK
};
/**
* @brief Standard (11-bit) CAN ID filter. This filter matches
* ``TEST_CAN_SOME_STD_ID``.
*/
const struct zcan_filter test_std_some_filter = {
.id_type = CAN_STANDARD_IDENTIFIER,
.rtr = CAN_DATAFRAME,
.id = TEST_CAN_SOME_STD_ID,
.rtr_mask = 1,
.id_mask = CAN_STD_ID_MASK
};
/**
* @brief Assert that two CAN frames are equal.
*
* @param frame1 First CAN frame.
* @param frame2 Second CAN frame.
*/
static inline void assert_frame_equal(const struct zcan_frame *frame1,
const struct zcan_frame *frame2)
{
zassert_equal(frame1->id_type, frame2->id_type, "ID type does not match");
zassert_equal(frame1->rtr, frame2->rtr, "RTR bit does not match");
zassert_equal(frame1->id, frame2->id, "ID does not match");
zassert_equal(frame1->dlc, frame2->dlc, "DLC does not match");
zassert_mem_equal(frame1->data, frame2->data, frame1->dlc, "Received data differ");
}
/**
* @brief Send a CAN test frame with asserts.
*
* This function will block until the frame is transmitted or a test timeout
* occurs.
*
* @param dev Pointer to the device structure for the driver instance.
* @param frame Pointer to the CAN frame to send.
*/
static void send_test_frame(const struct device *dev, const struct zcan_frame *frame)
{
int err;
err = can_send(dev, frame, TEST_SEND_TIMEOUT, NULL, NULL);
zassert_not_equal(err, -EBUSY, "arbitration lost in loopback mode");
zassert_equal(err, 0, "failed to send frame (err %d)", err);
}
/**
* @brief Test a more advanced filter handling.
*
* Add more than one filter at the same time, remove and change the filters
* before sending a frame. This tests the internal filter handling of the STM32
* driver.
*/
static void test_filter_handling(void)
{
const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus));
struct zcan_frame frame_buffer;
int filter_id_1;
int filter_id_2;
int err;
zassert_true(device_is_ready(dev), "CAN device not ready");
/* Set driver to loopback mode */
err = can_set_mode(dev, CAN_MODE_LOOPBACK);
zassert_equal(err, 0, "failed to set loopback mode");
/* Add a extended and masked filter (1 bank/filter) */
filter_id_1 = can_add_rx_filter_msgq(dev, &can_msgq, &test_ext_masked_filter);
zassert_not_equal(filter_id_1, -ENOSPC, "no filters available");
zassert_true(filter_id_1 >= 0, "negative filter number");
/* Add a standard non-masked filter (1/4 bank/filter) */
filter_id_2 = can_add_rx_filter_msgq(dev, &can_msgq, &test_std_filter);
zassert_not_equal(filter_id_2, -ENOSPC, "no filters available");
zassert_true(filter_id_2 >= 0, "negative filter number");
/*
* Remove the first filter (first bank gets free) and add a different
* standard none masked filter (1/4 bank/filter). Bank 0 is extended to
* 4 filters/bank which leads to a left shift of the first filter by 3
* and tests the corner case of the last filter is used.
*/
can_remove_rx_filter(dev, filter_id_1);
filter_id_1 = can_add_rx_filter_msgq(dev, &can_msgq, &test_std_some_filter);
zassert_not_equal(filter_id_1, -ENOSPC, "no filters available");
zassert_true((filter_id_1 >= 0), "negative filter number");
/* Test message sending and receiving */
send_test_frame(dev, &test_std_frame);
err = k_msgq_get(&can_msgq, &frame_buffer, TEST_RECEIVE_TIMEOUT);
zassert_equal(err, 0, "receive timeout");
assert_frame_equal(&test_std_frame, &frame_buffer);
err = k_msgq_get(&can_msgq, &frame_buffer, TEST_RECEIVE_TIMEOUT);
zassert_equal(err, -EAGAIN, "more than one frame in the queue");
/*
* Remove first filter (gets free) and add an extended filter. This
* shrinks bank 0 to 2 filters/bank which leads to a right shift of the
* first buffer by two.
*/
can_remove_rx_filter(dev, filter_id_1);
filter_id_1 = can_add_rx_filter_msgq(dev, &can_msgq, &test_ext_filter);
zassert_not_equal(filter_id_1, -ENOSPC, "no filters available");
zassert_true((filter_id_1 >= 0), "negative filter number");
/* Test message sending and receiving */
send_test_frame(dev, &test_std_frame);
err = k_msgq_get(&can_msgq, &frame_buffer, TEST_RECEIVE_TIMEOUT);
zassert_equal(err, 0, "receive timeout");
assert_frame_equal(&test_std_frame, &frame_buffer);
/* Remove both filters */
can_remove_rx_filter(dev, filter_id_1);
can_remove_rx_filter(dev, filter_id_2);
}
void test_main(void)
{
ztest_test_suite(can_stm32_tests,
ztest_unit_test(test_filter_handling));
ztest_run_test_suite(can_stm32_tests);
}