Multiple CAN Adapters Under systemd

Multiple CAN Adapters Under systemd

Introduction

When using more than one CAN adapter or a CAN adapter and a board in USB to CAN Bridge mode, it makes sense to ensure:

  1. CAN adapters can easily be identified
  2. CAN adapters do not change enumeration or interface during reboot or reconnecting
  3. Different settings can be applied to different adapters or interfaces

:light_bulb: Note

  • Works with one or multiple adapters (less benefit for a single device).
  • Tested on systemd v255. Required minimum is v248.
  • Alternative: udev rules (not covered here).

Identifying the CAN Adapters

Each connected CAN adapter that is identified as such is automatically assigned a can-networking interface by the Linux Operating System (OS). The first is can0, the second can1, etc.

To distinguish between the different interfaces, a unique property needs to be identified.

Listing all identified adapters

Run the command

 ip -details -s link show

An identified CAN adapter will look like:

7: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0  allmulti 0 minmtu 0 maxmtu 0
    can state STOPPED restart-ms 0
          gs_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..1024 brp_inc 1
          clock 48000000
          re-started bus-errors arbit-lost error-warn error-pass bus-off
          0          0          0          0          0          0         numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 parentbus usb parentdev 2-1.4:1.0
    RX:  bytes packets errors dropped  missed   mcast
             0       0      0       0       0       0
    TX:  bytes packets errors dropped carrier collsns
             0       0      0       0       0       0

Identifying the system path of the Adapter

Run the following command in the Linux shell:

udevadm info -q path -p /sys/class/net/can0

The output will look similar to:

/devices/platform/soc/5310000.usb/usb2/2-1/2-1.4/2-1.4:1.0/net/can0
  • For the following command, exactly this device path is needed.
  • Repeat for each CAN adapter by replacing can0 in the command part /sys/class/net/can0 with can1, etc.

Identifying the Adapters Properties:

Run the command

udevadm info /sys/devices/platform/soc/5310000.usb/usb2/2-1/2-1.4/2-1.4:1.0/net/can0
  • The relevant part is the device path identified above prepended with /sys/
  • Each CAN adapter will have its own unique path

The output will look like:

P: /devices/platform/soc/5310000.usb/usb2/2-1/2-1.4/2-1.4:1.0/net/can0
M: can0
R: 0
U: net
I: 7
E: DEVPATH=/devices/platform/soc/5310000.usb/usb2/2-1/2-1.4/2-1.4:1.0/net/can0
E: SUBSYSTEM=net
E: INTERFACE=can0
E: IFINDEX=7
E: USEC_INITIALIZED=9036766055
E: ID_NET_DRIVER=gs_usb
E: ID_BUS=usb
E: ID_MODEL=FLY-UTOC_USB_to_CAN_adapter
E: ID_MODEL_ENC=FLY-UTOC\x20USB\x20to\x20CAN\x20adapter
E: ID_MODEL_ID=606f
E: ID_SERIAL=Mellow._FLY-UTOC_USB_to_CAN_adapter_002100255842571320323035
E: ID_SERIAL_SHORT=002100255842571320323035
E: ID_VENDOR=Mellow.
E: ID_VENDOR_ENC=Mellow.
E: ID_VENDOR_ID=1d50
E: ID_REVISION=0000
E: ID_TYPE=generic
E: ID_USB_MODEL=FLY-UTOC_USB_to_CAN_adapter
E: ID_USB_MODEL_ENC=FLY-UTOC\x20USB\x20to\x20CAN\x20adapter
E: ID_USB_MODEL_ID=606f
E: ID_USB_SERIAL=Mellow._FLY-UTOC_USB_to_CAN_adapter_002100255842571320323035
E: ID_USB_SERIAL_SHORT=002100255842571320323035
E: ID_USB_VENDOR=Mellow.
E: ID_USB_VENDOR_ENC=Mellow.
E: ID_USB_VENDOR_ID=1d50
E: ID_USB_REVISION=0000
E: ID_USB_TYPE=generic
E: ID_USB_INTERFACES=:ffffff:fe0101:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=gs_usb
E: ID_VENDOR_FROM_DATABASE=OpenMoko, Inc.
E: ID_MODEL_FROM_DATABASE=Geschwister Schneider CAN adapter
E: ID_PATH_WITH_USB_REVISION=platform-5310000.usb-usbv2-0:1.4:1.0
E: ID_PATH=platform-5310000.usb-usb-0:1.4:1.0
E: ID_PATH_TAG=platform-5310000_usb-usb-0_1_4_1_0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/73-usb-net-by-mac.link
E: ID_NET_NAME=can0
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/can0
E: TAGS=:systemd:
E: CURRENT_TAGS=:systemd:
  • Each adapter will have its own properties and values, depending on the hardware, type of connection, etc.
  • You need to identify a property and value that looks unique to this adapter. In the example above, this could be:
    E: ID_SERIAL=Mellow._FLY-UTOC_USB_to_CAN_adapter_002100255842571320323035
    
  • The best properties to use are hardware-specific serial numbers like ID_SERIAL or ID_SERIAL_SHORT, as they are unique to the device. Avoid using path-based properties like ID_PATH if possible, as these can change if you use a different USB port.

Setting up .link and .network file

To configure the adapter, two files are needed: .link and .network files are simple text files with an INI-style format. For this configuration, they must adhere to the following rules:

  • Location: The files must be created in the /etc/systemd/network/ directory to ensure they take precedence over any system defaults.
  • Filename: Each file must have a .link / .network extension.
  • To control the processing order and ensure they are evaluated before the defaults, their names should be prefixed with a number lower than 99 and the .link files before the .network files. A logical naming scheme would be:
    • 10-can0.link and 11-can1.link and so on
    • 20-can0.network and 20-can1.network and so on
  • Content:
    • A .link file is composed of sections. The [Match] section contains keys that udev uses to identify a specific device. The [Link] section contains directives that configure the matched device, most importantly the Name key for renaming it.
    • A .network file contains a [Match] section to identify the interface it applies to (by its now-stable name from the corresponding .link file) and one or more configuration sections. For this task, the key section is [CAN].

Complete Example for the above

:light_bulb: Note

  • Creating or modifying these files, requires sudo

/etc/systemd/network/11-can_mellow.link

[Match]
Property=ID_SERIAL=Mellow._FLY-UTOC_USB_to_CAN_adapter_002100255842571320323035

[Link]
Name=can_mellow
TransmitQueueLength=128
  • Note the Property= notation in the [Match] section. This uniquely identifies the adapter as determined in the previous chapters. This is the systemd-networkd syntax for matching a udev property.
  • Note the Name=can_mellow setting: Here an arbitrary name can be assigned (no blanks or special characters allowed)
  • TransmitQueueLength=128 is a relevant setting for proper Klipper operation.

/etc/systemd/network/21-can_mellow.network

[Match]
Name=can_mellow

[Link]
RequiredForOnline=no

[CAN]
BitRate=1M
RestartSec=500ms
  • Note the Name= notation in the [Match] section. This must match the name assigned in the .link file
  • Note the RequiredForOnline setting. This is required until systemd version 256. Potentially backported in some distributions. It prevents systemd from waiting for CAN links to report “up” before starting dependent services.
  • Other settings like BitRate can now be specified on a stable adapter basis

:light_bulb: Note

  • Reboot for the settings to take effect

Verifying the Settings

Issue the command:

ip -details -s link show

Output:

5: can_mellow: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 128
    link/can  promiscuity 0  allmulti 0 minmtu 0 maxmtu 0
    can state ERROR-ACTIVE restart-ms 500
          bitrate 1000000 sample-point 0.750
          tq 62 prop-seg 5 phase-seg1 6 phase-seg2 4 sjw 2 brp 3
          gs_usb: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..1024 brp_inc 1
          clock 48000000
          re-started bus-errors arbit-lost error-warn error-pass bus-off
          0          0          0          0          0          0         numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 parentbus usb parentdev 2-1.5:1.0
    RX:  bytes packets errors dropped  missed   mcast
             0       0      0       0       0       0
    TX:  bytes packets errors dropped carrier collsns
             0       0      0       0       0       0
  • Note the newly assigned name at the beginning of line 1
  • Note the proper queue length at the end of line 1
  • Note the defined bitrate in line 4

Now, issue the command:

networkctl status can_mellow

Output:

● 3: can_mellow
               Link File: /etc/systemd/network/11-can_mellow.link
            Network File: /etc/systemd/network/21-can_mellow.network
                   State: carrier (configured)
            Online state: offline
                    Type: can
                    Kind: can
                    Path: platform-5311400.usb-usb-0:1:1.0
                  Driver: gs_usb
                  Vendor: OpenMoko, Inc.
                   Model: Geschwister Schneider CAN adapter
                     MTU: 16
                   QDisc: pfifo_fast
Number of Queues (Tx/Rx): 1/1
       Activation Policy: up
     Required For Online: no
  • Note the proper .link and .network files in lines 2 and 3
  • Note the Required For Online: no statement

Usage

Everywhere, e.g. in the printer.cfg or in command line utilities like flashtool.py, the interface now needs to be explicitly called under its new name.

2 Likes

I just needed exactly this, so I’d choose to document my approach. I avoided udev as far as possible because, IMO, it is extremely cryptic.

5 Likes

A small addition. In my Debian 12 network installation, the systemd-networkd service was disabled by default. Without it, the settings from .network will not be applied. To fix this problem, you need to start & enable the service.

sudo systemctl start systemd-networkd
sudo systemctl enable systemd-networkd

PS sorry for my English, it is not my native language

3 Likes