Add a functional selftest for the SAE J1939 socket interface. It runs two CAN_J1939 sockets (a sender and a receiver) over a vcan interface and checks payload integrity across the transport-size transitions: single frame (PDU1), Transport Protocol, Extended Transport Protocol, the exact size boundaries around those transitions, and single-frame and BAM broadcast (PDU2).
Declare j1939_tests.h as a local header dependency and enable CONFIG_CAN_J1939 in the test config. Assisted-by: LLM Signed-off-by: Oleksij Rempel <[email protected]> --- tools/testing/selftests/net/can/.gitignore | 1 + tools/testing/selftests/net/can/Makefile | 10 +- tools/testing/selftests/net/can/config | 1 + .../selftests/net/can/test_j1939_base.c | 160 ++++++++++++++++++ .../selftests/net/can/test_j1939_base.sh | 5 + 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/net/can/test_j1939_base.c create mode 100755 tools/testing/selftests/net/can/test_j1939_base.sh diff --git a/tools/testing/selftests/net/can/.gitignore b/tools/testing/selftests/net/can/.gitignore index 764a53fc837f..0ab563fe9ddf 100644 --- a/tools/testing/selftests/net/can/.gitignore +++ b/tools/testing/selftests/net/can/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only test_raw_filter +test_j1939_base diff --git a/tools/testing/selftests/net/can/Makefile b/tools/testing/selftests/net/can/Makefile index e2092c289109..cf4bd2461a6c 100644 --- a/tools/testing/selftests/net/can/Makefile +++ b/tools/testing/selftests/net/can/Makefile @@ -4,10 +4,16 @@ top_srcdir = ../../../../.. CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INCLUDES) -TEST_PROGS := test_raw_filter.sh +LOCAL_HDRS := j1939_tests.h + +TEST_PROGS := \ + test_raw_filter.sh \ + test_j1939_base.sh \ TEST_FILES := j1939_lib.sh -TEST_GEN_FILES := test_raw_filter +TEST_GEN_FILES := \ + test_raw_filter \ + test_j1939_base \ include ../../lib.mk diff --git a/tools/testing/selftests/net/can/config b/tools/testing/selftests/net/can/config index 188f79796670..52a186bd1055 100644 --- a/tools/testing/selftests/net/can/config +++ b/tools/testing/selftests/net/can/config @@ -1,3 +1,4 @@ CONFIG_CAN=m CONFIG_CAN_DEV=m CONFIG_CAN_VCAN=m +CONFIG_CAN_J1939=m diff --git a/tools/testing/selftests/net/can/test_j1939_base.c b/tools/testing/selftests/net/can/test_j1939_base.c new file mode 100644 index 000000000000..59f1448b7fb2 --- /dev/null +++ b/tools/testing/selftests/net/can/test_j1939_base.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * J1939 base functional selftests. + * + * Run two SOCK_DGRAM/CAN_J1939 sockets (a sender and a receiver) over a vcan + * interface and verify payload integrity across the transport-size transitions: + * a single frame (PDU1), Transport Protocol, Extended Transport Protocol, the + * exact boundaries between them, and single-frame and BAM broadcast (PDU2). + * + * The vcan interface is set up by the caller (see test_j1939_base.sh); its name + * is passed via the CANIF environment variable. + */ +#include <errno.h> + +#include "kselftest_harness.h" +#include "j1939_tests.h" + +#define SA_TX 0x80 +#define SA_RX 0x90 +#define PGN_PDU1 0x0ab00 /* PF 0xab (< 240): destination specific */ +#define PGN_PDU2 0x0feee /* PF 0xfe (>= 240): broadcast */ + +FIXTURE(j1939_base) +{ + int ifindex; + int tx; + int rx; +}; + +FIXTURE_SETUP(j1939_base) +{ + self->tx = -1; + self->rx = -1; + J1939_SETUP_IFINDEX(self); +} + +FIXTURE_TEARDOWN(j1939_base) +{ + if (self->tx >= 0) + close(self->tx); + if (self->rx >= 0) + close(self->rx); +} + +/* + * Set up a receiver bound to (rx_addr, pgn) and a sender bound to SA_TX and + * connected to (tx_dst, pgn). Any pair opened by a previous call is closed + * first. Returns 0 on success, -1 otherwise. + */ +static int pair_up(FIXTURE_DATA(j1939_base) *self, uint8_t rx_addr, + uint8_t tx_dst, pgn_t pgn, int bcast) +{ + int flags = bcast ? J1939_OPEN_BCAST : 0; + + if (self->tx >= 0) { + close(self->tx); + self->tx = -1; + } + if (self->rx >= 0) { + close(self->rx); + self->rx = -1; + } + + self->rx = j1939_open_flags(self->ifindex, J1939_NO_NAME, rx_addr, pgn, + flags); + if (self->rx < 0) { + fprintf(stderr, "# open rx failed: %d\n", errno); + return -1; + } + + self->tx = j1939_open_flags(self->ifindex, J1939_NO_NAME, SA_TX, + J1939_NO_PGN, flags); + if (self->tx < 0) { + fprintf(stderr, "# open tx failed: %d\n", errno); + return -1; + } + + if (j1939_connect(self->tx, self->ifindex, tx_dst, pgn)) { + fprintf(stderr, "# connect tx failed: %d\n", errno); + return -1; + } + + return 0; +} + +/* + * Send a deterministic payload of the given size on tx and verify it is + * received byte for byte on rx. Returns 0 on success, -1 otherwise. + */ +static int roundtrip(FIXTURE_DATA(j1939_base) *self, size_t size) +{ + uint8_t *tx_buf; + int rc; + + tx_buf = malloc(size); + if (!tx_buf) { + fprintf(stderr, "# alloc failed\n"); + return -1; + } + fill_pattern(tx_buf, size); + + rc = send_recv_verify(self->tx, self->rx, tx_buf, size, + J1939_RECV_TIMEOUT_MS); + if (rc) + fprintf(stderr, "# round-trip failed at size %zu, errno %d\n", + size, errno); + + free(tx_buf); + return rc; +} + +/* single frame (<= 8 bytes), unicast PDU1. */ +TEST_F(j1939_base, single_frame_pdu1) +{ + ASSERT_EQ(pair_up(self, SA_RX, SA_RX, PGN_PDU1, 0), 0); + EXPECT_EQ(roundtrip(self, 8), 0); +} + +/* Transport Protocol (9 .. 1785 bytes), unicast. */ +TEST_F(j1939_base, tp_roundtrip) +{ + ASSERT_EQ(pair_up(self, SA_RX, SA_RX, PGN_PDU1, 0), 0); + EXPECT_EQ(roundtrip(self, 128), 0); +} + +/* Extended Transport Protocol (> 1785 bytes), unicast. */ +TEST_F(j1939_base, etp_roundtrip) +{ + ASSERT_EQ(pair_up(self, SA_RX, SA_RX, PGN_PDU1, 0), 0); + EXPECT_EQ(roundtrip(self, 2000), 0); +} + +/* exact size boundaries around single / TP / ETP transitions. */ +TEST_F(j1939_base, size_boundaries) +{ + static const size_t sizes[] = { 8, 9, 1785, 1786 }; + size_t k; + + for (k = 0; k < ARRAY_SIZE(sizes); k++) { + ASSERT_EQ(pair_up(self, SA_RX, SA_RX, PGN_PDU1, 0), 0); + EXPECT_EQ(roundtrip(self, sizes[k]), 0) + TH_LOG("failed at boundary size %zu", sizes[k]); + } +} + +/* single-frame broadcast, PDU2. */ +TEST_F(j1939_base, single_frame_broadcast_pdu2) +{ + ASSERT_EQ(pair_up(self, SA_RX, J1939_NO_ADDR, PGN_PDU2, 1), 0); + EXPECT_EQ(roundtrip(self, 8), 0); +} + +/* BAM (broadcast transport). */ +TEST_F(j1939_base, bam_broadcast) +{ + ASSERT_EQ(pair_up(self, SA_RX, J1939_NO_ADDR, PGN_PDU2, 1), 0); + EXPECT_EQ(roundtrip(self, 128), 0); +} + +J1939_TEST_MAIN(); diff --git a/tools/testing/selftests/net/can/test_j1939_base.sh b/tools/testing/selftests/net/can/test_j1939_base.sh new file mode 100755 index 000000000000..3cd2b14a74bb --- /dev/null +++ b/tools/testing/selftests/net/can/test_j1939_base.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +J1939_TEST_PROG=test_j1939_base +source "$(dirname "$0")/j1939_lib.sh" -- 2.47.3

