1136 lines
44 KiB
ReStructuredText
1136 lines
44 KiB
ReStructuredText
|
|
.. SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|||
|
|
|
|||
|
|
===================
|
|||
|
|
J1939 Documentation
|
|||
|
|
===================
|
|||
|
|
|
|||
|
|
Overview / What Is J1939
|
|||
|
|
========================
|
|||
|
|
|
|||
|
|
SAE J1939 defines a higher layer protocol on CAN. It implements a more
|
|||
|
|
sophisticated addressing scheme and extends the maximum packet size above 8
|
|||
|
|
bytes. Several derived specifications exist, which differ from the original
|
|||
|
|
J1939 on the application level, like MilCAN A, NMEA2000, and especially
|
|||
|
|
ISO-11783 (ISOBUS). This last one specifies the so-called ETP (Extended
|
|||
|
|
Transport Protocol), which has been included in this implementation. This
|
|||
|
|
results in a maximum packet size of ((2 ^ 24) - 1) * 7 bytes == 111 MiB.
|
|||
|
|
|
|||
|
|
Specifications used
|
|||
|
|
-------------------
|
|||
|
|
|
|||
|
|
* SAE J1939-21 : data link layer
|
|||
|
|
* SAE J1939-81 : network management
|
|||
|
|
* ISO 11783-6 : Virtual Terminal (Extended Transport Protocol)
|
|||
|
|
|
|||
|
|
.. _j1939-motivation:
|
|||
|
|
|
|||
|
|
Motivation
|
|||
|
|
==========
|
|||
|
|
|
|||
|
|
Given the fact there's something like SocketCAN with an API similar to BSD
|
|||
|
|
sockets, we found some reasons to justify a kernel implementation for the
|
|||
|
|
addressing and transport methods used by J1939.
|
|||
|
|
|
|||
|
|
* **Addressing:** when a process on an ECU communicates via J1939, it should
|
|||
|
|
not necessarily know its source address. Although, at least one process per
|
|||
|
|
ECU should know the source address. Other processes should be able to reuse
|
|||
|
|
that address. This way, address parameters for different processes
|
|||
|
|
cooperating for the same ECU, are not duplicated. This way of working is
|
|||
|
|
closely related to the UNIX concept, where programs do just one thing and do
|
|||
|
|
it well.
|
|||
|
|
|
|||
|
|
* **Dynamic addressing:** Address Claiming in J1939 is time critical.
|
|||
|
|
Furthermore, data transport should be handled properly during the address
|
|||
|
|
negotiation. Putting this functionality in the kernel eliminates it as a
|
|||
|
|
requirement for _every_ user space process that communicates via J1939. This
|
|||
|
|
results in a consistent J1939 bus with proper addressing.
|
|||
|
|
|
|||
|
|
* **Transport:** both TP & ETP reuse some PGNs to relay big packets over them.
|
|||
|
|
Different processes may thus use the same TP & ETP PGNs without actually
|
|||
|
|
knowing it. The individual TP & ETP sessions _must_ be serialized
|
|||
|
|
(synchronized) between different processes. The kernel solves this problem
|
|||
|
|
properly and eliminates the serialization (synchronization) as a requirement
|
|||
|
|
for _every_ user space process that communicates via J1939.
|
|||
|
|
|
|||
|
|
J1939 defines some other features (relaying, gateway, fast packet transport,
|
|||
|
|
...). In-kernel code for these would not contribute to protocol stability.
|
|||
|
|
Therefore, these parts are left to user space.
|
|||
|
|
|
|||
|
|
The J1939 sockets operate on CAN network devices (see SocketCAN). Any J1939
|
|||
|
|
user space library operating on CAN raw sockets will still operate properly.
|
|||
|
|
Since such a library does not communicate with the in-kernel implementation, care
|
|||
|
|
must be taken that these two do not interfere. In practice, this means they
|
|||
|
|
cannot share ECU addresses. A single ECU (or virtual ECU) address is used by
|
|||
|
|
the library exclusively, or by the in-kernel system exclusively.
|
|||
|
|
|
|||
|
|
J1939 concepts
|
|||
|
|
==============
|
|||
|
|
|
|||
|
|
Data Sent to the J1939 Stack
|
|||
|
|
----------------------------
|
|||
|
|
|
|||
|
|
The data buffers sent to the J1939 stack from user space are not CAN frames
|
|||
|
|
themselves. Instead, they are payloads that the J1939 stack converts into
|
|||
|
|
proper CAN frames based on the size of the buffer and the type of transfer. The
|
|||
|
|
size of the buffer influences how the stack processes the data and determines
|
|||
|
|
the internal code path used for the transfer.
|
|||
|
|
|
|||
|
|
**Handling of Different Buffer Sizes:**
|
|||
|
|
|
|||
|
|
- **Buffers with a size of 8 bytes or less:**
|
|||
|
|
|
|||
|
|
- These are handled as simple sessions internally within the stack.
|
|||
|
|
|
|||
|
|
- The stack converts the buffer directly into a single CAN frame without
|
|||
|
|
fragmentation.
|
|||
|
|
|
|||
|
|
- This type of transfer does not require an actual client (receiver) on the
|
|||
|
|
receiving side.
|
|||
|
|
|
|||
|
|
- **Buffers up to 1785 bytes:**
|
|||
|
|
|
|||
|
|
- These are automatically handled as J1939 Transport Protocol (TP) transfers.
|
|||
|
|
|
|||
|
|
- Internally, the stack splits the buffer into multiple 8-byte CAN frames.
|
|||
|
|
|
|||
|
|
- TP transfers can be unicast or broadcast.
|
|||
|
|
|
|||
|
|
- **Broadcast TP:** Does not require a receiver on the other side and can be
|
|||
|
|
used in broadcast scenarios.
|
|||
|
|
|
|||
|
|
- **Unicast TP:** Requires an active receiver (client) on the other side to
|
|||
|
|
acknowledge the transfer.
|
|||
|
|
|
|||
|
|
- **Buffers from 1786 bytes up to 111 MiB:**
|
|||
|
|
|
|||
|
|
- These are handled as ISO 11783 Extended Transport Protocol (ETP) transfers.
|
|||
|
|
|
|||
|
|
- ETP transfers are used for larger payloads and are split into multiple CAN
|
|||
|
|
frames internally.
|
|||
|
|
|
|||
|
|
- **ETP transfers (unicast):** Require a receiver on the other side to
|
|||
|
|
process the incoming data and acknowledge each step of the transfer.
|
|||
|
|
|
|||
|
|
- ETP transfers cannot be broadcast like TP transfers, and always require a
|
|||
|
|
receiver for operation.
|
|||
|
|
|
|||
|
|
**Non-Blocking Operation with `MSG_DONTWAIT`:**
|
|||
|
|
|
|||
|
|
The J1939 stack supports non-blocking operation when used in combination with
|
|||
|
|
the `MSG_DONTWAIT` flag. In this mode, the stack attempts to take as much data
|
|||
|
|
as the available memory for the socket allows. It returns the amount of data
|
|||
|
|
that was successfully taken, and it is the responsibility of user space to
|
|||
|
|
monitor this value and handle partial transfers.
|
|||
|
|
|
|||
|
|
- If the stack cannot take the entire buffer, it returns the number of bytes
|
|||
|
|
successfully taken, and user space should handle the remainder.
|
|||
|
|
|
|||
|
|
- **Error handling:** When using `MSG_DONTWAIT`, the user must rely on the
|
|||
|
|
error queue to detect transfer errors. See the **SO_J1939_ERRQUEUE** section
|
|||
|
|
for details on how to subscribe to error notifications. Without the error
|
|||
|
|
queue, there is no other way for user space to be notified of transfer errors
|
|||
|
|
during non-blocking operations.
|
|||
|
|
|
|||
|
|
**Behavior and Requirements:**
|
|||
|
|
|
|||
|
|
- **Simple transfers (<= 8 bytes):** Do not require a receiver on the other
|
|||
|
|
side, making them easy to send without needing address claiming or
|
|||
|
|
coordination with a destination.
|
|||
|
|
|
|||
|
|
- **Unicast TP/ETP:** Requires a receiver on the other side to complete the
|
|||
|
|
transfer. The receiver must acknowledge the transfer for the session to
|
|||
|
|
proceed successfully.
|
|||
|
|
|
|||
|
|
- **Broadcast TP:** Allows sending data without a receiver, but only works for
|
|||
|
|
TP transfers. ETP cannot be broadcast and always needs a receiving client.
|
|||
|
|
|
|||
|
|
These different behaviors depend heavily on the size of the buffer provided to
|
|||
|
|
the stack, and the appropriate transport mechanism (TP or ETP) is selected
|
|||
|
|
based on the payload size. The stack automatically manages the fragmentation
|
|||
|
|
and reassembly of large payloads and ensures that the correct CAN frames are
|
|||
|
|
generated and transmitted for each session.
|
|||
|
|
|
|||
|
|
PGN
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
The J1939 protocol uses the 29-bit CAN identifier with the following structure:
|
|||
|
|
|
|||
|
|
============ ============== ====================
|
|||
|
|
29 bit CAN-ID
|
|||
|
|
--------------------------------------------------
|
|||
|
|
Bit positions within the CAN-ID
|
|||
|
|
--------------------------------------------------
|
|||
|
|
28 ... 26 25 ... 8 7 ... 0
|
|||
|
|
============ ============== ====================
|
|||
|
|
Priority PGN SA (Source Address)
|
|||
|
|
============ ============== ====================
|
|||
|
|
|
|||
|
|
The PGN (Parameter Group Number) is a number to identify a packet. The PGN
|
|||
|
|
is composed as follows:
|
|||
|
|
|
|||
|
|
============ ============== ================= =================
|
|||
|
|
PGN
|
|||
|
|
------------------------------------------------------------------
|
|||
|
|
Bit positions within the CAN-ID
|
|||
|
|
------------------------------------------------------------------
|
|||
|
|
25 24 23 ... 16 15 ... 8
|
|||
|
|
============ ============== ================= =================
|
|||
|
|
R (Reserved) DP (Data Page) PF (PDU Format) PS (PDU Specific)
|
|||
|
|
============ ============== ================= =================
|
|||
|
|
|
|||
|
|
In J1939-21 distinction is made between PDU1 format (where PF < 240) and PDU2
|
|||
|
|
format (where PF >= 240). Furthermore, when using the PDU2 format, the PS-field
|
|||
|
|
contains a so-called Group Extension, which is part of the PGN. When using PDU2
|
|||
|
|
format, the Group Extension is set in the PS-field.
|
|||
|
|
|
|||
|
|
============== ========================
|
|||
|
|
PDU1 Format (specific) (peer to peer)
|
|||
|
|
----------------------------------------
|
|||
|
|
Bit positions within the CAN-ID
|
|||
|
|
----------------------------------------
|
|||
|
|
23 ... 16 15 ... 8
|
|||
|
|
============== ========================
|
|||
|
|
00h ... EFh DA (Destination address)
|
|||
|
|
============== ========================
|
|||
|
|
|
|||
|
|
============== ========================
|
|||
|
|
PDU2 Format (global) (broadcast)
|
|||
|
|
----------------------------------------
|
|||
|
|
Bit positions within the CAN-ID
|
|||
|
|
----------------------------------------
|
|||
|
|
23 ... 16 15 ... 8
|
|||
|
|
============== ========================
|
|||
|
|
F0h ... FFh GE (Group Extension)
|
|||
|
|
============== ========================
|
|||
|
|
|
|||
|
|
On the other hand, when using PDU1 format, the PS-field contains a so-called
|
|||
|
|
Destination Address, which is _not_ part of the PGN. When communicating a PGN
|
|||
|
|
from user space to kernel (or vice versa) and PDU1 format is used, the PS-field
|
|||
|
|
of the PGN shall be set to zero. The Destination Address shall be set
|
|||
|
|
elsewhere.
|
|||
|
|
|
|||
|
|
Regarding PGN mapping to 29-bit CAN identifier, the Destination Address shall
|
|||
|
|
be get/set from/to the appropriate bits of the identifier by the kernel.
|
|||
|
|
|
|||
|
|
|
|||
|
|
Addressing
|
|||
|
|
----------
|
|||
|
|
|
|||
|
|
Both static and dynamic addressing methods can be used.
|
|||
|
|
|
|||
|
|
For static addresses, no extra checks are made by the kernel and provided
|
|||
|
|
addresses are considered right. This responsibility is for the OEM or system
|
|||
|
|
integrator.
|
|||
|
|
|
|||
|
|
For dynamic addressing, so-called Address Claiming, extra support is foreseen
|
|||
|
|
in the kernel. In J1939 any ECU is known by its 64-bit NAME. At the moment of
|
|||
|
|
a successful address claim, the kernel keeps track of both NAME and source
|
|||
|
|
address being claimed. This serves as a base for filter schemes. By default,
|
|||
|
|
packets with a destination that is not locally will be rejected.
|
|||
|
|
|
|||
|
|
Mixed mode packets (from a static to a dynamic address or vice versa) are
|
|||
|
|
allowed. The BSD sockets define separate API calls for getting/setting the
|
|||
|
|
local & remote address and are applicable for J1939 sockets.
|
|||
|
|
|
|||
|
|
Filtering
|
|||
|
|
---------
|
|||
|
|
|
|||
|
|
J1939 defines white list filters per socket that a user can set in order to
|
|||
|
|
receive a subset of the J1939 traffic. Filtering can be based on:
|
|||
|
|
|
|||
|
|
* SA
|
|||
|
|
* SOURCE_NAME
|
|||
|
|
* PGN
|
|||
|
|
|
|||
|
|
When multiple filters are in place for a single socket, and a packet comes in
|
|||
|
|
that matches several of those filters, the packet is only received once for
|
|||
|
|
that socket.
|
|||
|
|
|
|||
|
|
How to Use J1939
|
|||
|
|
================
|
|||
|
|
|
|||
|
|
API Calls
|
|||
|
|
---------
|
|||
|
|
|
|||
|
|
On CAN, you first need to open a socket for communicating over a CAN network.
|
|||
|
|
To use J1939, ``#include <linux/can/j1939.h>``. From there, ``<linux/can.h>`` will be
|
|||
|
|
included too. To open a socket, use:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
s = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
|
|||
|
|
|
|||
|
|
J1939 does use ``SOCK_DGRAM`` sockets. In the J1939 specification, connections are
|
|||
|
|
mentioned in the context of transport protocol sessions. These still deliver
|
|||
|
|
packets to the other end (using several CAN packets). ``SOCK_STREAM`` is not
|
|||
|
|
supported.
|
|||
|
|
|
|||
|
|
After the successful creation of the socket, you would normally use the ``bind(2)``
|
|||
|
|
and/or ``connect(2)`` system call to bind the socket to a CAN interface. After
|
|||
|
|
binding and/or connecting the socket, you can ``read(2)`` and ``write(2)`` from/to the
|
|||
|
|
socket or use ``send(2)``, ``sendto(2)``, ``sendmsg(2)`` and the ``recv*()`` counterpart
|
|||
|
|
operations on the socket as usual. There are also J1939 specific socket options
|
|||
|
|
described below.
|
|||
|
|
|
|||
|
|
In order to send data, a ``bind(2)`` must have been successful. ``bind(2)`` assigns a
|
|||
|
|
local address to a socket.
|
|||
|
|
|
|||
|
|
Different from CAN is that the payload data is just the data that get sends,
|
|||
|
|
without its header info. The header info is derived from the sockaddr supplied
|
|||
|
|
to ``bind(2)``, ``connect(2)``, ``sendto(2)`` and ``recvfrom(2)``. A ``write(2)`` with size 4 will
|
|||
|
|
result in a packet with 4 bytes.
|
|||
|
|
|
|||
|
|
The sockaddr structure has extensions for use with J1939 as specified below:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
struct sockaddr_can {
|
|||
|
|
sa_family_t can_family;
|
|||
|
|
int can_ifindex;
|
|||
|
|
union {
|
|||
|
|
struct {
|
|||
|
|
__u64 name;
|
|||
|
|
/* pgn:
|
|||
|
|
* 8 bit: PS in PDU2 case, else 0
|
|||
|
|
* 8 bit: PF
|
|||
|
|
* 1 bit: DP
|
|||
|
|
* 1 bit: reserved
|
|||
|
|
*/
|
|||
|
|
__u32 pgn;
|
|||
|
|
__u8 addr;
|
|||
|
|
} j1939;
|
|||
|
|
} can_addr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
``can_family`` & ``can_ifindex`` serve the same purpose as for other SocketCAN sockets.
|
|||
|
|
|
|||
|
|
``can_addr.j1939.pgn`` specifies the PGN (max 0x3ffff). Individual bits are
|
|||
|
|
specified above.
|
|||
|
|
|
|||
|
|
``can_addr.j1939.name`` contains the 64-bit J1939 NAME.
|
|||
|
|
|
|||
|
|
``can_addr.j1939.addr`` contains the address.
|
|||
|
|
|
|||
|
|
The ``bind(2)`` system call assigns the local address, i.e. the source address when
|
|||
|
|
sending packages. If a PGN during ``bind(2)`` is set, it's used as a RX filter.
|
|||
|
|
I.e. only packets with a matching PGN are received. If an ADDR or NAME is set
|
|||
|
|
it is used as a receive filter, too. It will match the destination NAME or ADDR
|
|||
|
|
of the incoming packet. The NAME filter will work only if appropriate Address
|
|||
|
|
Claiming for this name was done on the CAN bus and registered/cached by the
|
|||
|
|
kernel.
|
|||
|
|
|
|||
|
|
On the other hand ``connect(2)`` assigns the remote address, i.e. the destination
|
|||
|
|
address. The PGN from ``connect(2)`` is used as the default PGN when sending
|
|||
|
|
packets. If ADDR or NAME is set it will be used as the default destination ADDR
|
|||
|
|
or NAME. Further a set ADDR or NAME during ``connect(2)`` is used as a receive
|
|||
|
|
filter. It will match the source NAME or ADDR of the incoming packet.
|
|||
|
|
|
|||
|
|
Both ``write(2)`` and ``send(2)`` will send a packet with local address from ``bind(2)`` and the
|
|||
|
|
remote address from ``connect(2)``. Use ``sendto(2)`` to overwrite the destination
|
|||
|
|
address.
|
|||
|
|
|
|||
|
|
If ``can_addr.j1939.name`` is set (!= 0) the NAME is looked up by the kernel and
|
|||
|
|
the corresponding ADDR is used. If ``can_addr.j1939.name`` is not set (== 0),
|
|||
|
|
``can_addr.j1939.addr`` is used.
|
|||
|
|
|
|||
|
|
When creating a socket, reasonable defaults are set. Some options can be
|
|||
|
|
modified with ``setsockopt(2)`` & ``getsockopt(2)``.
|
|||
|
|
|
|||
|
|
RX path related options:
|
|||
|
|
|
|||
|
|
- ``SO_J1939_FILTER`` - configure array of filters
|
|||
|
|
- ``SO_J1939_PROMISC`` - disable filters set by ``bind(2)`` and ``connect(2)``
|
|||
|
|
|
|||
|
|
By default no broadcast packets can be send or received. To enable sending or
|
|||
|
|
receiving broadcast packets use the socket option ``SO_BROADCAST``:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
int value = 1;
|
|||
|
|
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
|
|||
|
|
|
|||
|
|
The following diagram illustrates the RX path:
|
|||
|
|
|
|||
|
|
.. code::
|
|||
|
|
|
|||
|
|
+--------------------+
|
|||
|
|
| incoming packet |
|
|||
|
|
+--------------------+
|
|||
|
|
|
|
|||
|
|
V
|
|||
|
|
+--------------------+
|
|||
|
|
| SO_J1939_PROMISC? |
|
|||
|
|
+--------------------+
|
|||
|
|
| |
|
|||
|
|
no | | yes
|
|||
|
|
| |
|
|||
|
|
.---------' `---------.
|
|||
|
|
| |
|
|||
|
|
+---------------------------+ |
|
|||
|
|
| bind() + connect() + | |
|
|||
|
|
| SOCK_BROADCAST filter | |
|
|||
|
|
+---------------------------+ |
|
|||
|
|
| |
|
|||
|
|
|<---------------------'
|
|||
|
|
V
|
|||
|
|
+---------------------------+
|
|||
|
|
| SO_J1939_FILTER |
|
|||
|
|
+---------------------------+
|
|||
|
|
|
|
|||
|
|
V
|
|||
|
|
+---------------------------+
|
|||
|
|
| socket recv() |
|
|||
|
|
+---------------------------+
|
|||
|
|
|
|||
|
|
TX path related options:
|
|||
|
|
``SO_J1939_SEND_PRIO`` - change default send priority for the socket
|
|||
|
|
|
|||
|
|
Message Flags during send() and Related System Calls
|
|||
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|||
|
|
|
|||
|
|
``send(2)``, ``sendto(2)`` and ``sendmsg(2)`` take a 'flags' argument. Currently
|
|||
|
|
supported flags are:
|
|||
|
|
|
|||
|
|
* ``MSG_DONTWAIT``, i.e. non-blocking operation.
|
|||
|
|
|
|||
|
|
recvmsg(2)
|
|||
|
|
^^^^^^^^^^
|
|||
|
|
|
|||
|
|
In most cases ``recvmsg(2)`` is needed if you want to extract more information than
|
|||
|
|
``recvfrom(2)`` can provide. For example package priority and timestamp. The
|
|||
|
|
Destination Address, name and packet priority (if applicable) are attached to
|
|||
|
|
the msghdr in the ``recvmsg(2)`` call. They can be extracted using ``cmsg(3)`` macros,
|
|||
|
|
with ``cmsg_level == SOL_J1939 && cmsg_type == SCM_J1939_DEST_ADDR``,
|
|||
|
|
``SCM_J1939_DEST_NAME`` or ``SCM_J1939_PRIO``. The returned data is a ``uint8_t`` for
|
|||
|
|
``priority`` and ``dst_addr``, and ``uint64_t`` for ``dst_name``.
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
uint8_t priority, dst_addr;
|
|||
|
|
uint64_t dst_name;
|
|||
|
|
|
|||
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|||
|
|
switch (cmsg->cmsg_level) {
|
|||
|
|
case SOL_CAN_J1939:
|
|||
|
|
if (cmsg->cmsg_type == SCM_J1939_DEST_ADDR)
|
|||
|
|
dst_addr = *CMSG_DATA(cmsg);
|
|||
|
|
else if (cmsg->cmsg_type == SCM_J1939_DEST_NAME)
|
|||
|
|
memcpy(&dst_name, CMSG_DATA(cmsg), cmsg->cmsg_len - CMSG_LEN(0));
|
|||
|
|
else if (cmsg->cmsg_type == SCM_J1939_PRIO)
|
|||
|
|
priority = *CMSG_DATA(cmsg);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setsockopt(2)
|
|||
|
|
^^^^^^^^^^^^^
|
|||
|
|
|
|||
|
|
The ``setsockopt(2)`` function is used to configure various socket-level
|
|||
|
|
options for J1939 communication. The following options are supported:
|
|||
|
|
|
|||
|
|
``SO_J1939_FILTER``
|
|||
|
|
~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_J1939_FILTER`` option is essential when the default behavior of
|
|||
|
|
``bind(2)`` and ``connect(2)`` is insufficient for specific use cases. By
|
|||
|
|
default, ``bind(2)`` and ``connect(2)`` allow a socket to be associated with a
|
|||
|
|
single unicast or broadcast address. However, there are scenarios where finer
|
|||
|
|
control over the incoming messages is required, such as filtering by Parameter
|
|||
|
|
Group Number (PGN) rather than by addresses.
|
|||
|
|
|
|||
|
|
For example, in a system where multiple types of J1939 messages are being
|
|||
|
|
transmitted, a process might only be interested in a subset of those messages,
|
|||
|
|
such as specific PGNs, and not want to receive all messages destined for its
|
|||
|
|
address or broadcast to the bus.
|
|||
|
|
|
|||
|
|
By applying the ``SO_J1939_FILTER`` option, you can filter messages based on:
|
|||
|
|
|
|||
|
|
- **Source Address (SA)**: Filter messages coming from specific source
|
|||
|
|
addresses.
|
|||
|
|
|
|||
|
|
- **Source Name**: Filter messages coming from ECUs with specific NAME
|
|||
|
|
identifiers.
|
|||
|
|
|
|||
|
|
- **Parameter Group Number (PGN)**: Focus on receiving messages with specific
|
|||
|
|
PGNs, filtering out irrelevant ones.
|
|||
|
|
|
|||
|
|
This filtering mechanism is particularly useful when:
|
|||
|
|
|
|||
|
|
- You want to receive a subset of messages based on their PGNs, even if the
|
|||
|
|
address is the same.
|
|||
|
|
|
|||
|
|
- You need to handle both broadcast and unicast messages but only care about
|
|||
|
|
certain message types or parameters.
|
|||
|
|
|
|||
|
|
- The ``bind(2)`` and ``connect(2)`` functions only allow binding to a single
|
|||
|
|
address, which might not be sufficient if the process needs to handle multiple
|
|||
|
|
PGNs but does not want to open multiple sockets.
|
|||
|
|
|
|||
|
|
To remove existing filters, you can pass ``optval == NULL`` or ``optlen == 0``
|
|||
|
|
to ``setsockopt(2)``. This will clear all currently set filters. If you want to
|
|||
|
|
**update** the set of filters, you must pass the updated filter set to
|
|||
|
|
``setsockopt(2)``, as the new filter set will **replace** the old one entirely.
|
|||
|
|
This behavior ensures that any previous filter configuration is discarded and
|
|||
|
|
only the new set is applied.
|
|||
|
|
|
|||
|
|
Example of removing all filters:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER, NULL, 0);
|
|||
|
|
|
|||
|
|
**Maximum number of filters:** The maximum amount of filters that can be
|
|||
|
|
applied using ``SO_J1939_FILTER`` is defined by ``J1939_FILTER_MAX``, which is
|
|||
|
|
set to 512. This means you can configure up to 512 individual filters to match
|
|||
|
|
your specific filtering needs.
|
|||
|
|
|
|||
|
|
Practical use case: **Monitoring Address Claiming**
|
|||
|
|
|
|||
|
|
One practical use case is monitoring the J1939 address claiming process by
|
|||
|
|
filtering for specific PGNs related to address claiming. This allows a process
|
|||
|
|
to monitor and handle address claims without processing unrelated messages.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
struct j1939_filter filt[] = {
|
|||
|
|
{
|
|||
|
|
.pgn = J1939_PGN_ADDRESS_CLAIMED,
|
|||
|
|
.pgn_mask = J1939_PGN_PDU1_MAX,
|
|||
|
|
}, {
|
|||
|
|
.pgn = J1939_PGN_REQUEST,
|
|||
|
|
.pgn_mask = J1939_PGN_PDU1_MAX,
|
|||
|
|
}, {
|
|||
|
|
.pgn = J1939_PGN_ADDRESS_COMMANDED,
|
|||
|
|
.pgn_mask = J1939_PGN_MAX,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER, &filt, sizeof(filt));
|
|||
|
|
|
|||
|
|
In this example, the socket will only receive messages with the PGNs related to
|
|||
|
|
address claiming: ``J1939_PGN_ADDRESS_CLAIMED``, ``J1939_PGN_REQUEST``, and
|
|||
|
|
``J1939_PGN_ADDRESS_COMMANDED``. This is particularly useful in scenarios where
|
|||
|
|
you want to monitor and process address claims without being overwhelmed by
|
|||
|
|
other traffic on the J1939 network.
|
|||
|
|
|
|||
|
|
``SO_J1939_PROMISC``
|
|||
|
|
~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_J1939_PROMISC`` option enables socket-level promiscuous mode. When
|
|||
|
|
this option is enabled, the socket will receive all J1939 traffic, regardless
|
|||
|
|
of any filters set by ``bind()`` or ``connect()``. This is analogous to
|
|||
|
|
enabling promiscuous mode for an Ethernet interface, where all traffic on the
|
|||
|
|
network segment is captured.
|
|||
|
|
|
|||
|
|
However, **`SO_J1939_FILTER` has a higher priority** compared to
|
|||
|
|
``SO_J1939_PROMISC``. This means that even in promiscuous mode, you can reduce
|
|||
|
|
the number of packets received by applying specific filters with
|
|||
|
|
`SO_J1939_FILTER`. The filters will limit which packets are passed to the
|
|||
|
|
socket, allowing for more refined traffic selection while promiscuous mode is
|
|||
|
|
active.
|
|||
|
|
|
|||
|
|
The acceptable value size for this option is ``sizeof(int)``, and the value is
|
|||
|
|
only differentiated between `0` and non-zero. A value of `0` disables
|
|||
|
|
promiscuous mode, while any non-zero value enables it.
|
|||
|
|
|
|||
|
|
This combination can be useful for debugging or monitoring specific types of
|
|||
|
|
traffic while still capturing a broad set of messages.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
int value = 1;
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_PROMISC, &value, sizeof(value));
|
|||
|
|
|
|||
|
|
In this example, setting ``value`` to any non-zero value (e.g., `1`) enables
|
|||
|
|
promiscuous mode, allowing the socket to receive all J1939 traffic on the
|
|||
|
|
network.
|
|||
|
|
|
|||
|
|
``SO_BROADCAST``
|
|||
|
|
~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_BROADCAST`` option enables the sending and receiving of broadcast
|
|||
|
|
messages. By default, broadcast messages are disabled for J1939 sockets. When
|
|||
|
|
this option is enabled, the socket will be allowed to send and receive
|
|||
|
|
broadcast packets on the J1939 network.
|
|||
|
|
|
|||
|
|
Due to the nature of the CAN bus as a shared medium, all messages transmitted
|
|||
|
|
on the bus are visible to all participants. In the context of J1939,
|
|||
|
|
broadcasting refers to using a specific destination address field, where the
|
|||
|
|
destination address is set to a value that indicates the message is intended
|
|||
|
|
for all participants (usually a global address such as 0xFF). Enabling the
|
|||
|
|
broadcast option allows the socket to send and receive such broadcast messages.
|
|||
|
|
|
|||
|
|
The acceptable value size for this option is ``sizeof(int)``, and the value is
|
|||
|
|
only differentiated between `0` and non-zero. A value of `0` disables the
|
|||
|
|
ability to send and receive broadcast messages, while any non-zero value
|
|||
|
|
enables it.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
int value = 1;
|
|||
|
|
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
|
|||
|
|
|
|||
|
|
In this example, setting ``value`` to any non-zero value (e.g., `1`) enables
|
|||
|
|
the socket to send and receive broadcast messages.
|
|||
|
|
|
|||
|
|
``SO_J1939_SEND_PRIO``
|
|||
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_J1939_SEND_PRIO`` option sets the priority of outgoing J1939 messages
|
|||
|
|
for the socket. In J1939, messages can have different priorities, and lower
|
|||
|
|
numerical values indicate higher priority. This option allows the user to
|
|||
|
|
control the priority of messages sent from the socket by adjusting the priority
|
|||
|
|
bits in the CAN identifier.
|
|||
|
|
|
|||
|
|
The acceptable value **size** for this option is ``sizeof(int)``, and the value
|
|||
|
|
is expected to be in the range of 0 to 7, where `0` is the highest priority,
|
|||
|
|
and `7` is the lowest. By default, the priority is set to `6` if this option is
|
|||
|
|
not explicitly configured.
|
|||
|
|
|
|||
|
|
Note that the priority values `0` and `1` can only be set if the process has
|
|||
|
|
the `CAP_NET_ADMIN` capability. These are reserved for high-priority traffic
|
|||
|
|
and require administrative privileges.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
int prio = 3; // Priority value between 0 (highest) and 7 (lowest)
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &prio, sizeof(prio));
|
|||
|
|
|
|||
|
|
In this example, the priority is set to `3`, meaning the outgoing messages will
|
|||
|
|
be sent with a moderate priority level.
|
|||
|
|
|
|||
|
|
``SO_J1939_ERRQUEUE``
|
|||
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_J1939_ERRQUEUE`` option enables the socket to receive error messages
|
|||
|
|
from the error queue, providing diagnostic information about transmission
|
|||
|
|
failures, protocol violations, or other issues that occur during J1939
|
|||
|
|
communication. Once this option is set, user space is required to handle
|
|||
|
|
``MSG_ERRQUEUE`` messages.
|
|||
|
|
|
|||
|
|
Setting ``SO_J1939_ERRQUEUE`` to ``0`` will purge any currently present error
|
|||
|
|
messages in the error queue. When enabled, error messages can be retrieved
|
|||
|
|
using the ``recvmsg(2)`` system call.
|
|||
|
|
|
|||
|
|
When subscribing to the error queue, the following error events can be
|
|||
|
|
accessed:
|
|||
|
|
|
|||
|
|
- **``J1939_EE_INFO_TX_ABORT``**: Transmission abort errors.
|
|||
|
|
- **``J1939_EE_INFO_RX_RTS``**: Reception of RTS (Request to Send) control
|
|||
|
|
frames.
|
|||
|
|
- **``J1939_EE_INFO_RX_DPO``**: Reception of data packets with Data Page Offset
|
|||
|
|
(DPO).
|
|||
|
|
- **``J1939_EE_INFO_RX_ABORT``**: Reception abort errors.
|
|||
|
|
|
|||
|
|
The error queue can be used to correlate errors with specific message transfer
|
|||
|
|
sessions using the session ID (``tskey``). The session ID is assigned via the
|
|||
|
|
``SOF_TIMESTAMPING_OPT_ID`` flag, which is set by enabling the
|
|||
|
|
``SO_TIMESTAMPING`` option.
|
|||
|
|
|
|||
|
|
If ``SO_J1939_ERRQUEUE`` is activated, the user is required to pull messages
|
|||
|
|
from the error queue, meaning that using plain ``recv(2)`` is not sufficient
|
|||
|
|
anymore. The user must use ``recvmsg(2)`` with appropriate flags to handle
|
|||
|
|
error messages. Failure to do so can result in the socket becoming blocked with
|
|||
|
|
unprocessed error messages in the queue.
|
|||
|
|
|
|||
|
|
It is **recommended** that ``SO_J1939_ERRQUEUE`` be used in combination with
|
|||
|
|
``SO_TIMESTAMPING`` in most cases. This enables proper error handling along
|
|||
|
|
with session tracking and timestamping, providing a more detailed analysis of
|
|||
|
|
message transfers and errors.
|
|||
|
|
|
|||
|
|
The acceptable value **size** for this option is ``sizeof(int)``, and the value
|
|||
|
|
is only differentiated between ``0`` and non-zero. A value of ``0`` disables
|
|||
|
|
error queue reception and purges any existing error messages, while any
|
|||
|
|
non-zero value enables it.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
int enable = 1; // Enable error queue reception
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_ERRQUEUE, &enable, sizeof(enable));
|
|||
|
|
|
|||
|
|
// Enable timestamping with session tracking via tskey
|
|||
|
|
int timestamping = SOF_TIMESTAMPING_OPT_ID | SOF_TIMESTAMPING_TX_ACK |
|
|||
|
|
SOF_TIMESTAMPING_TX_SCHED |
|
|||
|
|
SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_OPT_CMSG;
|
|||
|
|
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, ×tamping,
|
|||
|
|
sizeof(timestamping));
|
|||
|
|
|
|||
|
|
When enabled, error messages can be retrieved using ``recvmsg(2)``. By
|
|||
|
|
combining ``SO_J1939_ERRQUEUE`` with ``SO_TIMESTAMPING`` (with
|
|||
|
|
``SOF_TIMESTAMPING_OPT_ID`` and ``SOF_TIMESTAMPING_OPT_CMSG`` enabled), the
|
|||
|
|
user can track message transfers, retrieve precise timestamps, and correlate
|
|||
|
|
errors with specific sessions.
|
|||
|
|
|
|||
|
|
For more information on enabling timestamps and session tracking, refer to the
|
|||
|
|
`SO_TIMESTAMPING` section.
|
|||
|
|
|
|||
|
|
``SO_TIMESTAMPING``
|
|||
|
|
~~~~~~~~~~~~~~~~~~~
|
|||
|
|
|
|||
|
|
The ``SO_TIMESTAMPING`` option allows the socket to receive timestamps for
|
|||
|
|
various events related to message transmissions and receptions in J1939. This
|
|||
|
|
option is often used in combination with ``SO_J1939_ERRQUEUE`` to provide
|
|||
|
|
detailed diagnostic information, session tracking, and precise timing data for
|
|||
|
|
message transfers.
|
|||
|
|
|
|||
|
|
In J1939, all payloads provided by user space, regardless of size, are
|
|||
|
|
processed by the kernel as **sessions**. This includes both single-frame
|
|||
|
|
messages (up to 8 bytes) and multi-frame protocols such as the Transport
|
|||
|
|
Protocol (TP) and Extended Transport Protocol (ETP). Even for small,
|
|||
|
|
single-frame messages, the kernel creates a session to manage the transmission
|
|||
|
|
and reception. The concept of sessions allows the kernel to manage various
|
|||
|
|
aspects of the protocol, such as reassembling multi-frame messages and tracking
|
|||
|
|
the status of transmissions.
|
|||
|
|
|
|||
|
|
When receiving extended error messages from the error queue, the error
|
|||
|
|
information is delivered through a `struct sock_extended_err`, accessible via
|
|||
|
|
the control message (``cmsg``) retrieved using the ``recvmsg(2)`` system call.
|
|||
|
|
|
|||
|
|
There are two typical origins for the extended error messages in J1939:
|
|||
|
|
|
|||
|
|
1. ``serr->ee_origin == SO_EE_ORIGIN_TIMESTAMPING``:
|
|||
|
|
|
|||
|
|
In this case, the `serr->ee_info` field will contain one of the following
|
|||
|
|
timestamp types:
|
|||
|
|
|
|||
|
|
- ``SCM_TSTAMP_SCHED``: This timestamp is valid for Extended Transport
|
|||
|
|
Protocol (ETP) transfers and simple transfers (8 bytes or less). It
|
|||
|
|
indicates when a message or set of frames has been scheduled for
|
|||
|
|
transmission.
|
|||
|
|
|
|||
|
|
- For simple transfers (8 bytes or less), it marks the point when the
|
|||
|
|
message is queued and ready to be sent onto the CAN bus.
|
|||
|
|
|
|||
|
|
- For ETP transfers, it is sent after receiving a CTS (Clear to Send)
|
|||
|
|
frame on the sender side, indicating that a new set of frames has been
|
|||
|
|
scheduled for transmission.
|
|||
|
|
|
|||
|
|
- The Transport Protocol (TP) case is currently not implemented for this
|
|||
|
|
timestamp.
|
|||
|
|
|
|||
|
|
- On the receiver side, the counterpart to this event for ETP is
|
|||
|
|
represented by the ``J1939_EE_INFO_RX_DPO`` message, which indicates the
|
|||
|
|
reception of a Data Page Offset (DPO) control frame.
|
|||
|
|
|
|||
|
|
- ``SCM_TSTAMP_ACK``: This timestamp indicates the acknowledgment of the
|
|||
|
|
message or session.
|
|||
|
|
|
|||
|
|
- For simple transfers (8 bytes or less), it marks when the message has
|
|||
|
|
been sent and an echo confirmation has been received from the CAN
|
|||
|
|
controller, indicating that the frame was transmitted onto the bus.
|
|||
|
|
|
|||
|
|
- For multi-frame transfers (TP or ETP), it signifies that the entire
|
|||
|
|
session has been acknowledged, typically after receiving the End of
|
|||
|
|
Message Acknowledgment (EOMA) packet.
|
|||
|
|
|
|||
|
|
2. ``serr->ee_origin == SO_EE_ORIGIN_LOCAL``:
|
|||
|
|
|
|||
|
|
In this case, the `serr->ee_info` field will contain one of the following
|
|||
|
|
J1939 stack-specific message types:
|
|||
|
|
|
|||
|
|
- ``J1939_EE_INFO_TX_ABORT``: This message indicates that the transmission
|
|||
|
|
of a message or session was aborted. The cause of the abort can come from
|
|||
|
|
various sources:
|
|||
|
|
|
|||
|
|
- **CAN stack failure**: The J1939 stack was unable to pass the frame to
|
|||
|
|
the CAN framework for transmission.
|
|||
|
|
|
|||
|
|
- **Echo failure**: The J1939 stack did not receive an echo confirmation
|
|||
|
|
from the CAN controller, meaning the frame may not have been successfully
|
|||
|
|
transmitted to the CAN bus.
|
|||
|
|
|
|||
|
|
- **Protocol-level issues**: For multi-frame transfers (TP/ETP), this
|
|||
|
|
could include protocol-related errors, such as an abort signaled by the
|
|||
|
|
receiver or a timeout at the protocol level, which causes the session to
|
|||
|
|
terminate prematurely.
|
|||
|
|
|
|||
|
|
- The corresponding error code is stored in ``serr->ee_data``
|
|||
|
|
(``session->err`` on kernel side), providing additional details about
|
|||
|
|
the specific reason for the abort.
|
|||
|
|
|
|||
|
|
- ``J1939_EE_INFO_RX_RTS``: This message indicates that the J1939 stack has
|
|||
|
|
received a Request to Send (RTS) control frame, signaling the start of a
|
|||
|
|
multi-frame transfer using the Transport Protocol (TP) or Extended
|
|||
|
|
Transport Protocol (ETP).
|
|||
|
|
|
|||
|
|
- It informs the receiver that the sender is ready to transmit a
|
|||
|
|
multi-frame message and includes details about the total message size
|
|||
|
|
and the number of frames to be sent.
|
|||
|
|
|
|||
|
|
- Statistics such as ``J1939_NLA_TOTAL_SIZE``, ``J1939_NLA_PGN``,
|
|||
|
|
``J1939_NLA_SRC_NAME``, and ``J1939_NLA_DEST_NAME`` are provided along
|
|||
|
|
with the ``J1939_EE_INFO_RX_RTS`` message, giving detailed information
|
|||
|
|
about the incoming transfer.
|
|||
|
|
|
|||
|
|
- ``J1939_EE_INFO_RX_DPO``: This message indicates that the J1939 stack has
|
|||
|
|
received a Data Page Offset (DPO) control frame, which is part of the
|
|||
|
|
Extended Transport Protocol (ETP).
|
|||
|
|
|
|||
|
|
- The DPO frame signals the continuation of an ETP multi-frame message by
|
|||
|
|
indicating the offset position in the data being transferred. It helps
|
|||
|
|
the receiver manage large data sets by identifying which portion of the
|
|||
|
|
message is being received.
|
|||
|
|
|
|||
|
|
- It is typically paired with a corresponding ``SCM_TSTAMP_SCHED`` event
|
|||
|
|
on the sender side, which indicates when the next set of frames is
|
|||
|
|
scheduled for transmission.
|
|||
|
|
|
|||
|
|
- This event includes statistics such as ``J1939_NLA_BYTES_ACKED``, which
|
|||
|
|
tracks the number of bytes acknowledged up to that point in the session.
|
|||
|
|
|
|||
|
|
- ``J1939_EE_INFO_RX_ABORT``: This message indicates that the reception of a
|
|||
|
|
multi-frame message (Transport Protocol or Extended Transport Protocol) has
|
|||
|
|
been aborted.
|
|||
|
|
|
|||
|
|
- The abort can be triggered by protocol-level errors such as timeouts, an
|
|||
|
|
unexpected frame, or a specific abort request from the sender.
|
|||
|
|
|
|||
|
|
- This message signals that the receiver cannot continue processing the
|
|||
|
|
transfer, and the session is terminated.
|
|||
|
|
|
|||
|
|
- The corresponding error code is stored in ``serr->ee_data``
|
|||
|
|
(``session->err`` on kernel side ), providing further details about the
|
|||
|
|
reason for the abort, such as protocol violations or timeouts.
|
|||
|
|
|
|||
|
|
- After receiving this message, the receiver discards the partially received
|
|||
|
|
frames, and the multi-frame session is considered incomplete.
|
|||
|
|
|
|||
|
|
In both cases, if ``SOF_TIMESTAMPING_OPT_ID`` is enabled, ``serr->ee_data``
|
|||
|
|
will be set to the session’s unique identifier (``session->tskey``). This
|
|||
|
|
allows user space to track message transfers by their session identifier across
|
|||
|
|
multiple frames or stages.
|
|||
|
|
|
|||
|
|
In all other cases, ``serr->ee_errno`` will be set to ``ENOMSG``, except for
|
|||
|
|
the ``J1939_EE_INFO_TX_ABORT`` and ``J1939_EE_INFO_RX_ABORT`` cases, where the
|
|||
|
|
kernel sets ``serr->ee_data`` to the error stored in ``session->err``. All
|
|||
|
|
protocol-specific errors are converted to standard kernel error values and
|
|||
|
|
stored in ``session->err``. These error values are unified across system calls
|
|||
|
|
and ``serr->ee_errno``. Some of the known error values are described in the
|
|||
|
|
`Error Codes in the J1939 Stack` section.
|
|||
|
|
|
|||
|
|
When the `J1939_EE_INFO_RX_RTS` message is provided, it will include the
|
|||
|
|
following statistics for multi-frame messages (TP and ETP):
|
|||
|
|
|
|||
|
|
- ``J1939_NLA_TOTAL_SIZE``: Total size of the message in the session.
|
|||
|
|
- ``J1939_NLA_PGN``: Parameter Group Number (PGN) identifying the message type.
|
|||
|
|
- ``J1939_NLA_SRC_NAME``: 64-bit name of the source ECU.
|
|||
|
|
- ``J1939_NLA_DEST_NAME``: 64-bit name of the destination ECU.
|
|||
|
|
- ``J1939_NLA_SRC_ADDR``: 8-bit source address of the sending ECU.
|
|||
|
|
- ``J1939_NLA_DEST_ADDR``: 8-bit destination address of the receiving ECU.
|
|||
|
|
|
|||
|
|
- For other messages (including single-frame messages), only the following
|
|||
|
|
statistic is included:
|
|||
|
|
|
|||
|
|
- ``J1939_NLA_BYTES_ACKED``: Number of bytes successfully acknowledged in the
|
|||
|
|
session.
|
|||
|
|
|
|||
|
|
The key flags for ``SO_TIMESTAMPING`` include:
|
|||
|
|
|
|||
|
|
- ``SOF_TIMESTAMPING_OPT_ID``: Enables the use of a unique session identifier
|
|||
|
|
(``tskey``) for each transfer. This identifier helps track message transfers
|
|||
|
|
and errors as distinct sessions in user space. When this option is enabled,
|
|||
|
|
``serr->ee_data`` will be set to ``session->tskey``.
|
|||
|
|
|
|||
|
|
- ``SOF_TIMESTAMPING_OPT_CMSG``: Sends timestamp information through control
|
|||
|
|
messages (``struct scm_timestamping``), allowing the application to retrieve
|
|||
|
|
timestamps alongside the data.
|
|||
|
|
|
|||
|
|
- ``SOF_TIMESTAMPING_TX_SCHED``: Provides the timestamp for when a message is
|
|||
|
|
scheduled for transmission (``SCM_TSTAMP_SCHED``).
|
|||
|
|
|
|||
|
|
- ``SOF_TIMESTAMPING_TX_ACK``: Provides the timestamp for when a message
|
|||
|
|
transmission is fully acknowledged (``SCM_TSTAMP_ACK``).
|
|||
|
|
|
|||
|
|
- ``SOF_TIMESTAMPING_RX_SOFTWARE``: Provides timestamps for reception-related
|
|||
|
|
events (e.g., ``J1939_EE_INFO_RX_RTS``, ``J1939_EE_INFO_RX_DPO``,
|
|||
|
|
``J1939_EE_INFO_RX_ABORT``).
|
|||
|
|
|
|||
|
|
These flags enable detailed monitoring of message lifecycles, including
|
|||
|
|
transmission scheduling, acknowledgments, reception timestamps, and gathering
|
|||
|
|
detailed statistics about the communication session, especially for multi-frame
|
|||
|
|
payloads like TP and ETP.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
.. code-block:: c
|
|||
|
|
|
|||
|
|
// Enable timestamping with various options, including session tracking and
|
|||
|
|
// statistics
|
|||
|
|
int sock_opt = SOF_TIMESTAMPING_OPT_CMSG |
|
|||
|
|
SOF_TIMESTAMPING_TX_ACK |
|
|||
|
|
SOF_TIMESTAMPING_TX_SCHED |
|
|||
|
|
SOF_TIMESTAMPING_OPT_ID |
|
|||
|
|
SOF_TIMESTAMPING_RX_SOFTWARE;
|
|||
|
|
|
|||
|
|
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &sock_opt, sizeof(sock_opt));
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
Dynamic Addressing
|
|||
|
|
------------------
|
|||
|
|
|
|||
|
|
Distinction has to be made between using the claimed address and doing an
|
|||
|
|
address claim. To use an already claimed address, one has to fill in the
|
|||
|
|
``j1939.name`` member and provide it to ``bind(2)``. If the name had claimed an address
|
|||
|
|
earlier, all further messages being sent will use that address. And the
|
|||
|
|
``j1939.addr`` member will be ignored.
|
|||
|
|
|
|||
|
|
An exception on this is PGN 0x0ee00. This is the "Address Claim/Cannot Claim
|
|||
|
|
Address" message and the kernel will use the ``j1939.addr`` member for that PGN if
|
|||
|
|
necessary.
|
|||
|
|
|
|||
|
|
To claim an address following code example can be used:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
struct sockaddr_can baddr = {
|
|||
|
|
.can_family = AF_CAN,
|
|||
|
|
.can_addr.j1939 = {
|
|||
|
|
.name = name,
|
|||
|
|
.addr = J1939_IDLE_ADDR,
|
|||
|
|
.pgn = J1939_NO_PGN, /* to disable bind() rx filter for PGN */
|
|||
|
|
},
|
|||
|
|
.can_ifindex = if_nametoindex("can0"),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
bind(sock, (struct sockaddr *)&baddr, sizeof(baddr));
|
|||
|
|
|
|||
|
|
/* for Address Claiming broadcast must be allowed */
|
|||
|
|
int value = 1;
|
|||
|
|
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
|
|||
|
|
|
|||
|
|
/* configured advanced RX filter with PGN needed for Address Claiming */
|
|||
|
|
const struct j1939_filter filt[] = {
|
|||
|
|
{
|
|||
|
|
.pgn = J1939_PGN_ADDRESS_CLAIMED,
|
|||
|
|
.pgn_mask = J1939_PGN_PDU1_MAX,
|
|||
|
|
}, {
|
|||
|
|
.pgn = J1939_PGN_REQUEST,
|
|||
|
|
.pgn_mask = J1939_PGN_PDU1_MAX,
|
|||
|
|
}, {
|
|||
|
|
.pgn = J1939_PGN_ADDRESS_COMMANDED,
|
|||
|
|
.pgn_mask = J1939_PGN_MAX,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
setsockopt(sock, SOL_CAN_J1939, SO_J1939_FILTER, &filt, sizeof(filt));
|
|||
|
|
|
|||
|
|
uint64_t dat = htole64(name);
|
|||
|
|
const struct sockaddr_can saddr = {
|
|||
|
|
.can_family = AF_CAN,
|
|||
|
|
.can_addr.j1939 = {
|
|||
|
|
.pgn = J1939_PGN_ADDRESS_CLAIMED,
|
|||
|
|
.addr = J1939_NO_ADDR,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/* Afterwards do a sendto(2) with data set to the NAME (Little Endian). If the
|
|||
|
|
* NAME provided, does not match the j1939.name provided to bind(2), EPROTO
|
|||
|
|
* will be returned.
|
|||
|
|
*/
|
|||
|
|
sendto(sock, dat, sizeof(dat), 0, (const struct sockaddr *)&saddr, sizeof(saddr));
|
|||
|
|
|
|||
|
|
If no-one else contests the address claim within 250ms after transmission, the
|
|||
|
|
kernel marks the NAME-SA assignment as valid. The valid assignment will be kept
|
|||
|
|
among other valid NAME-SA assignments. From that point, any socket bound to the
|
|||
|
|
NAME can send packets.
|
|||
|
|
|
|||
|
|
If another ECU claims the address, the kernel will mark the NAME-SA expired.
|
|||
|
|
No socket bound to the NAME can send packets (other than address claims). To
|
|||
|
|
claim another address, some socket bound to NAME, must ``bind(2)`` again, but with
|
|||
|
|
only ``j1939.addr`` changed to the new SA, and must then send a valid address claim
|
|||
|
|
packet. This restarts the state machine in the kernel (and any other
|
|||
|
|
participant on the bus) for this NAME.
|
|||
|
|
|
|||
|
|
``can-utils`` also include the ``j1939acd`` tool, so it can be used as code example or as
|
|||
|
|
default Address Claiming daemon.
|
|||
|
|
|
|||
|
|
Send Examples
|
|||
|
|
-------------
|
|||
|
|
|
|||
|
|
Static Addressing
|
|||
|
|
^^^^^^^^^^^^^^^^^
|
|||
|
|
|
|||
|
|
This example will send a PGN (0x12300) from SA 0x20 to DA 0x30.
|
|||
|
|
|
|||
|
|
Bind:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
struct sockaddr_can baddr = {
|
|||
|
|
.can_family = AF_CAN,
|
|||
|
|
.can_addr.j1939 = {
|
|||
|
|
.name = J1939_NO_NAME,
|
|||
|
|
.addr = 0x20,
|
|||
|
|
.pgn = J1939_NO_PGN,
|
|||
|
|
},
|
|||
|
|
.can_ifindex = if_nametoindex("can0"),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
bind(sock, (struct sockaddr *)&baddr, sizeof(baddr));
|
|||
|
|
|
|||
|
|
Now, the socket 'sock' is bound to the SA 0x20. Since no ``connect(2)`` was called,
|
|||
|
|
at this point we can use only ``sendto(2)`` or ``sendmsg(2)``.
|
|||
|
|
|
|||
|
|
Send:
|
|||
|
|
|
|||
|
|
.. code-block:: C
|
|||
|
|
|
|||
|
|
const struct sockaddr_can saddr = {
|
|||
|
|
.can_family = AF_CAN,
|
|||
|
|
.can_addr.j1939 = {
|
|||
|
|
.name = J1939_NO_NAME;
|
|||
|
|
.addr = 0x30,
|
|||
|
|
.pgn = 0x12300,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
sendto(sock, dat, sizeof(dat), 0, (const struct sockaddr *)&saddr, sizeof(saddr));
|
|||
|
|
|
|||
|
|
|
|||
|
|
Error Codes in the J1939 Stack
|
|||
|
|
------------------------------
|
|||
|
|
|
|||
|
|
This section lists all potential kernel error codes that can be exposed to user
|
|||
|
|
space when interacting with the J1939 stack. It includes both standard error
|
|||
|
|
codes and those derived from protocol-specific abort codes.
|
|||
|
|
|
|||
|
|
- ``EAGAIN``: Operation would block; retry may succeed. One common reason is
|
|||
|
|
that an active TP or ETP session exists, and an attempt was made to start a
|
|||
|
|
new overlapping TP or ETP session between the same peers.
|
|||
|
|
|
|||
|
|
- ``ENETDOWN``: Network is down. This occurs when the CAN interface is switched
|
|||
|
|
to the "down" state.
|
|||
|
|
|
|||
|
|
- ``ENOBUFS``: No buffer space available. This error occurs when the CAN
|
|||
|
|
interface's transmit (TX) queue is full, and no more messages can be queued.
|
|||
|
|
|
|||
|
|
- ``EOVERFLOW``: Value too large for defined data type. In J1939, this can
|
|||
|
|
happen if the requested data lies outside of the queued buffer. For example,
|
|||
|
|
if a CTS (Clear to Send) requests an offset not available in the kernel buffer
|
|||
|
|
because user space did not provide enough data.
|
|||
|
|
|
|||
|
|
- ``EBUSY``: Device or resource is busy. For example, this occurs if an
|
|||
|
|
identical session is already active and the stack is unable to recover from
|
|||
|
|
the condition.
|
|||
|
|
|
|||
|
|
- ``EACCES``: Permission denied. This error can occur, for example, when
|
|||
|
|
attempting to send broadcast messages, but the socket is not configured with
|
|||
|
|
``SO_BROADCAST``.
|
|||
|
|
|
|||
|
|
- ``EADDRNOTAVAIL``: Address not available. This error occurs in cases such as:
|
|||
|
|
|
|||
|
|
- When attempting to use ``getsockname(2)`` to retrieve the peer's address,
|
|||
|
|
but the socket is not connected.
|
|||
|
|
|
|||
|
|
- When trying to send data to or from a NAME, but address claiming for the
|
|||
|
|
NAME was not performed or detected by the stack.
|
|||
|
|
|
|||
|
|
- ``EBADFD``: File descriptor in bad state. This error can occur if:
|
|||
|
|
|
|||
|
|
- Attempting to send data to an unbound socket.
|
|||
|
|
|
|||
|
|
- The socket is bound but has no source name, and the source address is
|
|||
|
|
``J1939_NO_ADDR``.
|
|||
|
|
|
|||
|
|
- The ``can_ifindex`` is incorrect.
|
|||
|
|
|
|||
|
|
- ``EFAULT``: Bad address. Occurs mostly when the stack can't copy from or to a
|
|||
|
|
sockptr, when there is insufficient data from user space, or when the buffer
|
|||
|
|
provided by user space is not large enough for the requested data.
|
|||
|
|
|
|||
|
|
- ``EINTR``: A signal occurred before any data was transmitted; see ``signal(7)``.
|
|||
|
|
|
|||
|
|
- ``EINVAL``: Invalid argument passed. For example:
|
|||
|
|
|
|||
|
|
- ``msg->msg_namelen`` is less than ``J1939_MIN_NAMELEN``.
|
|||
|
|
|
|||
|
|
- ``addr->can_family`` is not equal to ``AF_CAN``.
|
|||
|
|
|
|||
|
|
- An incorrect PGN was provided.
|
|||
|
|
|
|||
|
|
- ``ENODEV``: No such device. This happens when the CAN network device cannot
|
|||
|
|
be found for the provided ``can_ifindex`` or if ``can_ifindex`` is 0.
|
|||
|
|
|
|||
|
|
- ``ENOMEM``: Out of memory. Typically related to issues with memory allocation
|
|||
|
|
in the stack.
|
|||
|
|
|
|||
|
|
- ``ENOPROTOOPT``: Protocol not available. This can occur when using
|
|||
|
|
``getsockopt(2)`` or ``setsockopt(2)`` if the requested socket option is not
|
|||
|
|
available.
|
|||
|
|
|
|||
|
|
- ``EDESTADDRREQ``: Destination address required. This error occurs:
|
|||
|
|
|
|||
|
|
- In the case of ``connect(2)``, if the ``struct sockaddr *uaddr`` is ``NULL``.
|
|||
|
|
|
|||
|
|
- In the case of ``send*(2)``, if there is an attempt to send an ETP message
|
|||
|
|
to a broadcast address.
|
|||
|
|
|
|||
|
|
- ``EDOM``: Argument out of domain. This error may happen if attempting to send
|
|||
|
|
a TP or ETP message to a PGN that is reserved for control PGNs for TP or ETP
|
|||
|
|
operations.
|
|||
|
|
|
|||
|
|
- ``EIO``: I/O error. This can occur if the amount of data provided to the
|
|||
|
|
socket for a TP or ETP session does not match the announced amount of data for
|
|||
|
|
the session.
|
|||
|
|
|
|||
|
|
- ``ENOENT``: No such file or directory. This can happen when the stack
|
|||
|
|
attempts to transfer CTS or EOMA but cannot find a matching receiving socket
|
|||
|
|
anymore.
|
|||
|
|
|
|||
|
|
- ``ENOIOCTLCMD``: No ioctls are available for the socket layer.
|
|||
|
|
|
|||
|
|
- ``EPERM``: Operation not permitted. For example, this can occur if a
|
|||
|
|
requested action requires ``CAP_NET_ADMIN`` privileges.
|
|||
|
|
|
|||
|
|
- ``ENETUNREACH``: Network unreachable. Most likely, this occurs when frames
|
|||
|
|
cannot be transmitted to the CAN bus.
|
|||
|
|
|
|||
|
|
- ``ETIME``: Timer expired. This can happen if a timeout occurs while
|
|||
|
|
attempting to send a simple message, for example, when an echo message from
|
|||
|
|
the controller is not received.
|
|||
|
|
|
|||
|
|
- ``EPROTO``: Protocol error.
|
|||
|
|
|
|||
|
|
- Used for various protocol-level errors in J1939, including:
|
|||
|
|
|
|||
|
|
- Duplicate sequence number.
|
|||
|
|
|
|||
|
|
- Unexpected EDPO or ECTS packet.
|
|||
|
|
|
|||
|
|
- Invalid PGN or offset in EDPO/ECTS.
|
|||
|
|
|
|||
|
|
- Number of EDPO packets exceeded CTS allowance.
|
|||
|
|
|
|||
|
|
- Any other protocol-level error.
|
|||
|
|
|
|||
|
|
- ``EMSGSIZE``: Message too long.
|
|||
|
|
|
|||
|
|
- ``ENOMSG``: No message available.
|
|||
|
|
|
|||
|
|
- ``EALREADY``: The ECU is already engaged in one or more connection-managed
|
|||
|
|
sessions and cannot support another.
|
|||
|
|
|
|||
|
|
- ``EHOSTUNREACH``: A timeout occurred, and the session was aborted.
|
|||
|
|
|
|||
|
|
- ``EBADMSG``: CTS (Clear to Send) messages were received during an active data
|
|||
|
|
transfer, causing an abort.
|
|||
|
|
|
|||
|
|
- ``ENOTRECOVERABLE``: The maximum retransmission request limit was reached,
|
|||
|
|
and the session cannot recover.
|
|||
|
|
|
|||
|
|
- ``ENOTCONN``: An unexpected data transfer packet was received.
|
|||
|
|
|
|||
|
|
- ``EILSEQ``: A bad sequence number was received, and the software could not
|
|||
|
|
recover.
|
|||
|
|
|