Chapter 55C: CAN bus (SocketCAN + FlexCAN)¶
What: SocketCAN, Linux’s abstraction that exposes a CAN interface as a network device (
can0) and CAN frames asstruct sockaddr_can/struct can_frameover a normal socket. The FlexCAN driver covers i.MX6ULL’s 2 FlexCAN controllers. User-space speaks the socket API. By the end you cancansend can0 123#DEADBEEFand watch the frame on a scope.Why: CAN is the dominant bus in automotive and a strong second in industrial automation. The SocketCAN abstraction means you write CAN apps with
socket()/sendto()/recvmsg(), same APIs as TCP/UDP. No proprietary library. Tools work across all CAN hardware on Linux.Focus: CAN looks like a network device. Once
can0is “up,” everything is generic, Wireshark, tcpdump-equivalent (candump),iproute2configuration, even SO_TIMESTAMP for nanosecond-accurate receive timestamps.Tooling. This chapter uses
can-utils+iproute2(ip link set canX type can ...).
Ubuntu-base (target):
apt install can-utils iproute2Buildroot:
BR2_PACKAGE_CAN_UTILS=y BR2_PACKAGE_IPROUTE2=yBuildroot: a configuration-driven build system that produces a complete root filesystem and related images.Full per-tool reference: Userspace tooling appendix. MCU bridge: Think of the rootfs as the firmware image’s file-backed runtime environment. On an MCU you link everything into flash. On Linux, programs and config live in this mounted tree. rootfs: root filesystem, the directory tree mounted at / that contains /bin, /etc, /dev, and libraries.
55C.1 CAN basics¶
CAN 2.0 is a differential, multi-master, contention-resolved bus:
2 wires (CAN_H, CAN_L), 60 Ω termination each end.
1 Mbps max (high-speed CAN).
11-bit (standard) or 29-bit (extended) frame identifier.
0–8 data bytes per frame.
CAN-FD (Flexible Data-rate) extends payload to 64 bytes and allows 5 Mbps data-phase. I.MX6ULL FlexCAN supports CAN-FD on the newer revisions.
Physical layer needs a transceiver between SoC and bus: TJA1051 (5V), TJA1463 (CAN-FD), MCP2562. The SoC speaks 3.3V TTL CAN_TX/CAN_RX. The transceiver speaks differential CAN_H/CAN_L.
55C.2 i.MX FlexCAN in DT¶
&can1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
xceiver-supply = <®_can_3v3>;
status = "okay";
};
&can2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan2>;
status = "okay";
};
Once enabled, can0 and can1 appear:
[root@pa-mini:~]# ip link show | grep can
3: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN ...
4: can1: <NOARP,ECHO> mtu 16 qdisc noop state DOWN ...
55C.3 Bringing up the interface¶
[root@pa-mini:~]# ip link set can0 type can bitrate 500000
[root@pa-mini:~]# ip link set can0 up
[root@pa-mini:~]# ip -s link show can0
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc fq_codel state UP qlen 10
link/can
RX: bytes packets errors dropped overrun mcast
0 0 0 0 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
Send and receive:
[root@pa-mini:~]# cansend can0 123#11.22.33.44 # send frame, ID 0x123
[root@pa-mini:~]# candump can0 # passive monitor
can0 123 [4] 11 22 33 44
# CAN-FD with bit-rate switch:
[root@pa-mini:~]# ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on
[root@pa-mini:~]# cansend can0 123##1.11.22.33.44.55.66.77.88
55C.4 Programming with sockets¶
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
struct ifreq ifr;
strncpy(ifr.ifr_name, "can0", IFNAMSIZ);
ioctl(sock, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr = {
.can_family = AF_CAN,
.can_ifindex = ifr.ifr_ifindex,
};
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
/* Send */
struct can_frame frame = {
.can_id = 0x123,
.can_dlc = 4,
.data = { 0x11, 0x22, 0x33, 0x44 },
};
write(sock, &frame, sizeof(frame));
/* Receive */
struct can_frame rxframe;
int n = read(sock, &rxframe, sizeof(rxframe));
printf("rx id=%x dlc=%d\n", rxframe.can_id, rxframe.can_dlc);
For filtering, only receive frames with specific IDs:
struct can_filter rfilter[2];
rfilter[0].can_id = 0x123;
rfilter[0].can_mask = CAN_SFF_MASK;
rfilter[1].can_id = 0x200;
rfilter[1].can_mask = 0x700; /* match 0x200–0x2FF */
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
The kernel filters in software, or in hardware where the controller supports it. FlexCAN has message-buffer (MB) filtering. High-throughput receivers should always set filters. Otherwise, every frame on the bus is delivered to every socket.
55C.5 Higher protocols¶
CAN-RAW is the bottom layer. Real applications use one of:
ISO-TP (ISO-15765-2), fragmentation/reassembly for >8-byte payloads.
linux/can/isotp.h. Used by OBD-II and UDS automotive diagnostics.J1939: heavy-duty truck/agricultural protocol.
linux/can/j1939.h.CAN BCM (Broadcast Manager), kernel-side periodic frame TX/RX with filtering. Reduces user-space wakeups.
/* ISO-TP socket */
int sock = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP);
struct sockaddr_can addr = {
.can_family = AF_CAN,
.can_ifindex = ifr.ifr_ifindex,
.can_addr.tp = { .tx_id = 0x7E0, .rx_id = 0x7E8 },
};
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
write(sock, "Hello, this is an ISO-TP message longer than 8 bytes", 53);
55C.6 Error frames and bus health¶
CAN reports “bus-off” when error count exceeds 255. SocketCAN exposes these as special error frames with can_id flag CAN_ERR_FLAG. Enable:
can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT | CAN_ERR_LOSTARB | CAN_ERR_CRTL | CAN_ERR_PROT | CAN_ERR_TRX | CAN_ERR_BUSOFF;
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
When the bus goes bus-off, restart it:
ip link set can0 type can restart-ms 100 # auto-restart 100 ms after bus-off
Or do it manually with ip link set can0 down. ip link set can0 up; after sorting the wiring/termination.
55C.7 Key Notes¶
SocketCAN exposes CAN through Linux networking while FlexCAN owns controller behavior. The external transceiver and bus wiring remain essential physical dependencies.
Bit timing and termination are shared bus contracts. A local loopback result does not establish correct communication with physical nodes.
CAN arbitration, filtering and higher-level protocols solve different problems. ISO-TP adds transport behavior above frames rather than changing the electrical bus.
Bus-off indicates a fault boundary requiring a deliberate recovery policy. Automatically restarting without addressing a persistent fault can repeat the failure.
Classic CAN on the i.MX6ULL is not CAN FD. Protocol and controller capabilities must match every node on the intended segment.
55C.8 Pitfalls¶
Missing/wrong terminations. Without 120 Ω termination at each end of the bus (60 Ω total), signal integrity collapses. For a single-node bench setup, put one 120 Ω resistor across CAN_H/CAN_L. Robustness is lower but it works.
Wrong bitrate on one end. Symptom: all frames error. Both sides must agree.
CAN_TX/RX swapped at transceiver. Symptom: no transmit. Verify schematic.
No transceiver supply. Many transceivers need their own VCC. Without it, no signaling.
Bus-off and no restart-ms. Bus stuck off after first error storm. Set
restart-ms.Different CAN-FD speeds. Old nodes can’t handle CAN-FD speed-shift frames. Bus collapses. Use CAN-FD only on segments where all nodes support it.
55C.9 Going deeper¶
Documentation/networking/can.rst: SocketCAN documentation.drivers/net/can/flexcan.c: i.MX FlexCAN driver.can-utils:cansend,candump,cangen,canbusload,isotpdump. Required.linux/can/isotp.h,linux/can/j1939.h, higher-protocol headers.OpenXC project, open-source automotive data over CAN.
Next chapter: Chapter 55D: Block device drivers. The other half of “storage”,
gendisk, request queues, blk-mq.