Add a test that pairs a J1939 socket with a CAN_RAW socket so the actual
CAN frames on the wire are checked in both directions: a J1939 send
produces the expected CAN id (priority, PGN, source and destination) and
payload, and an injected CAN frame is parsed and delivered to the J1939
socket. The base test only exercises J1939-socket-to-J1939-socket
transfers, which stay on the local delivery path inside one netdevice, so
the on-wire format is otherwise unchecked. Both PDU1 (destination
specific) and PDU2 (broadcast) single frames are covered.

Assisted-by: LLM
Signed-off-by: Oleksij Rempel <[email protected]>
---
 tools/testing/selftests/net/can/.gitignore    |   1 +
 tools/testing/selftests/net/can/Makefile      |   2 +
 .../selftests/net/can/test_j1939_wire.c       | 170 ++++++++++++++++++
 .../selftests/net/can/test_j1939_wire.sh      |   5 +
 4 files changed, 178 insertions(+)
 create mode 100644 tools/testing/selftests/net/can/test_j1939_wire.c
 create mode 100755 tools/testing/selftests/net/can/test_j1939_wire.sh

diff --git a/tools/testing/selftests/net/can/.gitignore 
b/tools/testing/selftests/net/can/.gitignore
index 7ca63a19472c..3f31fa5b7438 100644
--- a/tools/testing/selftests/net/can/.gitignore
+++ b/tools/testing/selftests/net/can/.gitignore
@@ -3,3 +3,4 @@ test_raw_filter
 test_j1939_base
 test_j1939_opts
 test_j1939_addr
+test_j1939_wire
diff --git a/tools/testing/selftests/net/can/Makefile 
b/tools/testing/selftests/net/can/Makefile
index 6e4176a1b985..d80fa114a0cd 100644
--- a/tools/testing/selftests/net/can/Makefile
+++ b/tools/testing/selftests/net/can/Makefile
@@ -11,6 +11,7 @@ TEST_PROGS := \
        test_j1939_base.sh \
        test_j1939_opts.sh \
        test_j1939_addr.sh \
+       test_j1939_wire.sh \
 
 TEST_FILES := j1939_lib.sh
 
@@ -19,5 +20,6 @@ TEST_GEN_FILES := \
        test_j1939_base \
        test_j1939_opts \
        test_j1939_addr \
+       test_j1939_wire \
 
 include ../../lib.mk
diff --git a/tools/testing/selftests/net/can/test_j1939_wire.c 
b/tools/testing/selftests/net/can/test_j1939_wire.c
new file mode 100644
index 000000000000..8f5113394f99
--- /dev/null
+++ b/tools/testing/selftests/net/can/test_j1939_wire.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * J1939 wire-format selftests: a J1939 socket against CAN_RAW.
+ *
+ * Where test_j1939_base keeps transfers on the local path between two J1939
+ * sockets, these pair a J1939 socket with a CAN_RAW socket and check the 
actual
+ * CAN frames on the wire both ways: a J1939 send produces the expected CAN id
+ * (priority, PGN, source and destination) and payload, and an injected CAN 
frame
+ * is parsed and delivered to the J1939 socket. Both destination-specific 
(PDU1)
+ * and broadcast (PDU2) single frames are covered.
+ *
+ * The vcan interface is set up by the caller (see test_j1939_wire.sh); its 
name
+ * is passed via the CANIF environment variable.
+ */
+#include <errno.h>
+
+#include "kselftest_harness.h"
+#include "j1939_tests.h"
+
+#define SA_LOCAL               0x90    /* the J1939 socket under test */
+#define SA_PEER                        0x20    /* the CAN_RAW peer */
+#define PGN_PDU1               0x0ab00 /* PF 0xab (< 240): destination 
specific */
+#define PGN_PDU2               0x0feee /* PF 0xfe (>= 240): broadcast */
+#define PRIO                   J1939_PRIO_DEFAULT
+
+FIXTURE(j1939_wire)
+{
+       int ifindex;
+       int j1939;
+       int raw;
+};
+
+FIXTURE_SETUP(j1939_wire)
+{
+       self->j1939 = -1;
+       self->raw = -1;
+
+       J1939_SETUP_IFINDEX(self);
+
+       J1939_SETUP_RAW(self, 0);
+}
+
+FIXTURE_TEARDOWN(j1939_wire)
+{
+       if (self->j1939 >= 0)
+               close(self->j1939);
+       if (self->raw >= 0)
+               close(self->raw);
+}
+
+/*
+ * Inject one CAN frame from the raw peer and confirm the J1939 socket receives
+ * the identical 8-byte payload. Opens the J1939 RX socket into self->j1939 
(the
+ * fixture teardown closes it). @bcast selects a broadcast (PDU2) socket and
+ * frame; @pgn and @da address the injected frame. Returns 0 on success, -1
+ * otherwise; the caller ASSERTs the result.
+ */
+static int rx_single_frame_check(FIXTURE_DATA(j1939_wire) *self, bool bcast,
+                                pgn_t pgn, uint8_t da, const uint8_t 
payload[8])
+{
+       uint8_t rx_buf[8];
+
+       if (bcast)
+               self->j1939 = j1939_open_flags(self->ifindex, J1939_NO_NAME,
+                                              SA_LOCAL, pgn, J1939_OPEN_BCAST);
+       else
+               self->j1939 = j1939_open(self->ifindex, SA_LOCAL, pgn);
+       if (self->j1939 < 0)
+               return -1;
+
+       if (send_raw8(self->raw, PRIO, pgn, da, SA_PEER, payload))
+               return -1;
+
+       if (recv_timeout(self->j1939, rx_buf, sizeof(rx_buf),
+                        J1939_RECV_TIMEOUT_MS) != (int)sizeof(rx_buf))
+               return -1;
+
+       return memcmp(rx_buf, payload, sizeof(rx_buf)) == 0 ? 0 : -1;
+}
+
+/*
+ * Send one 8-byte payload from the J1939 socket to the raw peer and return the
+ * CAN frame seen on the wire via *frame, so each caller can assert the id 
fields
+ * it cares about. Opens the J1939 TX socket into self->j1939 (the fixture
+ * teardown closes it) and connects it to (@da, @pgn); @bcast opens a broadcast
+ * (PDU2) socket. Returns 0 on success, -1 otherwise; the caller ASSERTs the
+ * result.
+ */
+static int tx_to_raw_send_recv(FIXTURE_DATA(j1939_wire) *self, bool bcast,
+                              pgn_t pgn, uint8_t da, const uint8_t payload[8],
+                              struct can_frame *frame)
+{
+       if (bcast)
+               self->j1939 = j1939_open_flags(self->ifindex, J1939_NO_NAME,
+                                              SA_LOCAL, J1939_NO_PGN,
+                                              J1939_OPEN_BCAST);
+       else
+               self->j1939 = j1939_open(self->ifindex, SA_LOCAL, J1939_NO_PGN);
+       if (self->j1939 < 0)
+               return -1;
+
+       if (j1939_connect(self->j1939, self->ifindex, da, pgn))
+               return -1;
+
+       if (send(self->j1939, payload, 8, 0) != 8)
+               return -1;
+
+       if (recv_timeout(self->raw, frame, sizeof(*frame),
+                        J1939_RECV_TIMEOUT_MS) <= 0)
+               return -1;
+
+       return 0;
+}
+
+/* J1939 TX -> CAN_RAW: destination-specific (PDU1) single frame. */
+TEST_F(j1939_wire, tx_pdu1_to_raw)
+{
+       uint8_t payload[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
+       struct can_frame frame;
+       canid_t id;
+
+       ASSERT_EQ(tx_to_raw_send_recv(self, false, PGN_PDU1, SA_PEER, payload,
+                                     &frame), 0);
+       id = frame.can_id & CAN_EFF_MASK;
+
+       EXPECT_EQ(J1939_ID_PRIO(id), PRIO);
+       EXPECT_EQ(J1939_ID_SA(id), SA_LOCAL);                   /* source 
address */
+       EXPECT_EQ(J1939_ID_PS(id), SA_PEER);            /* destination (PS) */
+       EXPECT_EQ(J1939_ID_PF(id), J1939_PF(PGN_PDU1));
+       ASSERT_EQ(frame.can_dlc, sizeof(payload));
+       EXPECT_EQ(memcmp(frame.data, payload, sizeof(payload)), 0);
+}
+
+/* CAN_RAW -> J1939 RX: destination-specific (PDU1) single frame. */
+TEST_F(j1939_wire, rx_pdu1_from_raw)
+{
+       uint8_t payload[8] = { 0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03, 0x04 };
+
+       ASSERT_EQ(rx_single_frame_check(self, false, PGN_PDU1, SA_LOCAL,
+                                       payload), 0);
+}
+
+/* J1939 TX -> CAN_RAW: broadcast (PDU2) single frame. */
+TEST_F(j1939_wire, tx_pdu2_to_raw)
+{
+       uint8_t payload[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+       struct can_frame frame;
+       canid_t id;
+
+       ASSERT_EQ(tx_to_raw_send_recv(self, true, PGN_PDU2, J1939_NO_ADDR,
+                                     payload, &frame), 0);
+       id = frame.can_id & CAN_EFF_MASK;
+
+       EXPECT_EQ(J1939_ID_PRIO(id), PRIO);
+       EXPECT_EQ(J1939_ID_SA(id), SA_LOCAL);                   /* source 
address */
+       EXPECT_EQ((id >> 8) & J1939_PGN_MAX, PGN_PDU2); /* full broadcast PGN */
+       ASSERT_EQ(frame.can_dlc, sizeof(payload));
+       EXPECT_EQ(memcmp(frame.data, payload, sizeof(payload)), 0);
+}
+
+/* CAN_RAW -> J1939 RX: broadcast (PDU2) single frame. */
+TEST_F(j1939_wire, rx_pdu2_from_raw)
+{
+       uint8_t payload[8] = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7 };
+
+       ASSERT_EQ(rx_single_frame_check(self, true, PGN_PDU2, J1939_NO_ADDR,
+                                       payload), 0);
+}
+
+J1939_TEST_MAIN();
diff --git a/tools/testing/selftests/net/can/test_j1939_wire.sh 
b/tools/testing/selftests/net/can/test_j1939_wire.sh
new file mode 100755
index 000000000000..77a1bddb5854
--- /dev/null
+++ b/tools/testing/selftests/net/can/test_j1939_wire.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+J1939_TEST_PROG=test_j1939_wire
+source "$(dirname "$0")/j1939_lib.sh"
-- 
2.47.3


Reply via email to