IsoTp Sockets
Introduction
Under Linux, CAN interfaces can be managed the same way as network interfaces. The support for each CAN driver is written directly in the Linux kernel, inside a module called SocketCAN. Therefore, it is possible to send data over CAN through sockets, which offer a unique interface for all CAN drivers.
SocketCAN allows a user the send CAN messages on the CAN layer and, starting from Linux 5.10.0, ISO-TP frames as well. Meaning that a user can write a payload into an ISO-TP socket, this payload will be subdivided into multiple CAN messages and sent to a receiver following the ISO-15765 standard
For version prior to 5.10.0, this out-of-tree loadable kernel module can be compiled and loaded in the Linux kernel, enabling ISO-TP sockets.
A socket is a standard interface for communication protocols and as anything generic, it can be complex to configure. The isotp.socket
is a wrapper over a native IsoTP socket providing a friendly and pythonic interface for easy configuration. It does not offer any additional functionality that your operating system can’t provide, it makes the syntax clean and warns you in case of wrong usage.
Troubleshooting
My socket module does not include the `CAN_ISOTP` constant
That means that your Python version does not include support for IsoTP protocol. It should be included starting from Python 3.7, under Linux build only. See Python issue and Pull request
When I create the socket, I get `OSError [errno XX] : Protocol not supported`.
Your Linux kernel does not support ISO-TP sockets, you need to manually load it. Follow the steps given it the out-of-tree repository. You needs to compile the module, install the .ko file and then run insmod can-isotp.ko as Super User. Then your OS will accept to create an ISO-TP sockets.
I get a timeout when calling send().
A normal use case for that issue is that the receiver failed to respond with a flow control message.
Also, some kernel versions have been hit with a bug. A race condition within the isotp kernel module can cause the send() function to return an unexpected timeout. The patch has been published in this PR. This github issue also covers the matter
Examples
Without this project
SOL_CAN_ISOTP = 106 # These constants exist in the module header, not in Python.
CAN_ISOTP_RECV_FC = 2
# Many more exists.
import socket
import struct
s = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)
s2 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)
# Configuring the sockets with ugly struct.pack() that requires knowledge of the driver
s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, struct.pack("=BBB", 0x10, 3,0)) #bs, stmin, wftmax
#s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_OPTS, struct.pack(...))
#s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, struct.pack(...))
s.bind(("vcan0", 0x123, 0x456)) #rxid, txid with confusing order.
s2.bind(("vcan0", 0x456, 0x123)) #rxid, txid
s2.send(b"Hello, this is a long payload sent in small chunks of 8 bytes.")
print(s.recv(4095))
With this project
import isotp
s = isotp.socket()
s2 = isotp.socket()
# Configuring the sockets.
s.set_fc_opts(stmin=5, bs=10)
#s.set_general_opts(...)
#s.set_ll_opts(...)
s.bind("vcan0", isotp.Address(rxid=0x123, txid=0x456))
s2.bind("vcan0", isotp.Address(rxid=0x456, txid=0x123))
s2.send(b"Hello, this is a long payload sent in small chunks of 8 bytes.")
print(s.recv())
Usage
- class isotp.socket(timeout=0.1)[source]
A IsoTP socket wrapper for easy configuration
- Parameters:
timeout (int) – The underlying socket timeout set with
settimeout
. Makes the reception thread sleep
- socket.bind(interface, address)[source]
Binds the socket to an address. If no address is provided, all additional parameters will be used to create an address. This is mainly to allow a syntax such as
sock.bind('vcan0', rxid=0x123, txid=0x456)
for backward compatibility.- Parameters:
interface (string) – The network interface to use
address – The address to bind to.
- Type:
To configure a socket, few methods are available
- socket.set_opts(optflag=None, frame_txtime=None, ext_address=None, txpad=None, rxpad=None, rx_ext_address=None, tx_stmin=None)
Sets the general options of the socket
- Parameters:
optflag (int) – A list of flags modifying the protocol behaviour. Refer to
socket.flags
frame_txtime (int) – Frame transmission time (N_As/N_Ar) in nanoseconds.
ext_address (int) – The extended address to use. If not None, flags.EXTEND_ADDR will be set.
txpad (int) – The byte to use to pad the transmitted CAN messages. If not None, flags.TX_PADDING will be set
rxpad (int) – The byte to use to pad the transmitted CAN messages. If not None, flags.RX_PADDING will be set
rx_ext_address (int) – The extended address to use in reception. If not None, flags.RX_EXT_ADDR will be set
tx_stmin (int) – Sets the transmit separation time (time between consecutive frame) in nanoseconds. This value will override the value received through FlowControl frame. If not None, flags.FORCE_TXSTMIN will be set
- socket.set_fc_opts(bs=None, stmin=None, wftmax=None)
Sets the flow control options of the socket
- Parameters:
bs (int) – The block size sent in the flow control message. Indicates the number of consecutive frame a sender can send before the socket sends a new flow control. A block size of 0 means that no additional flow control message will be sent (block size of infinity)
stmin (int) – The minimum separation time sent in the flow control message. Indicates the amount of time to wait between 2 consecutive frame. This value will be sent as is over CAN. Values from 1 to 127 means milliseconds. Values from 0xF1 to 0xF9 means 100us to 900us. 0 Means no timing requirements
wftmax (int) – Maximum number of wait frame (flow control message with flow status=1) allowed before dropping a message. 0 means that wait frame are not allowed
- socket.set_ll_opts(mtu=None, tx_dl=None, tx_flags=None)
Sets the link layer options. Default values are set to work with CAN 2.0. Link layer may be configure to work in CAN FD.
- Parameters:
mtu (int) – The internal CAN frame structure size. Possible values are defined in
isotp.socket.LinkLayerProtocol
tx_dl (int) – The CAN message payload length. For CAN 2.0, this value should be 8. For CAN FD, possible values are 8,12,16,20,24,32,48,64
tx_flags (int) – Link layer flags.
- class isotp.socket.flags
- LISTEN_MODE = 1
Puts the socket in Listen mode, which prevents transmission of data
- EXTEND_ADDR = 2
When set, an address extension byte (set in socket general options) will be added to each payload sent. Unless RX_EXT_ADDR is also set, this value will be expected for reception as well
- TX_PADDING = 4
Enables padding of transmitted data with a byte set in the socket general options
- RX_PADDING = 8
Indicates that data padding is possible in reception. Must be set for CHK_PAD_LEN and CHK_PAD_DATA to have an effect
- CHK_PAD_LEN = 16
Makes the socket validate the padding length of the CAN message
- CHK_PAD_DATA = 32
Makes the socket validate the padding bytes of the CAN message
- HALF_DUPLEX = 64
Sets the socket in half duplex mode, forcing transmission and reception to happen sequentially
- FORCE_TXSTMIN = 128
Forces the socket to use the separation time sets in general options, overriding stmin value received in flow control frames.
- FORCE_RXSTMIN = 256
Forces the socket to ignore any message received faster than stmin given in the flow control frame
- RX_EXT_ADDR = 512
When sets, a different extended address can be used for reception than for transmission.
- WAIT_TX_DONE = 1024
When set, we wait for tx completion to make sure the PDU is completely passed to the CAN netdevice queue.