From 2d62d1a4f838fd71cbe57dea3973a9553e20ab4f Mon Sep 17 00:00:00 2001 From: mikkeli Date: Thu, 8 Oct 2026 01:38:25 +0900 Subject: [PATCH] Pin CAN interface names by udev; document device naming mttcan and pcan both load at boot, so the PCAN adapter could take can0 and push the onboard controller to can1. A udev rule now names them can_onboard (platform c310000.mttcan) and can_pcan (USB 0c72:000c), both outside the canN pool so a transient canN can never collide. Also records why the SIM7600 needs nothing more: ModemManager and NM don't use ttyUSB numbers, and gpsd already uses a symlink. can_onboard is verified live; can_pcan is checked against the driver source only (adapter not plugged in). Co-Authored-By: Claude Opus 5.5 --- docs/BOM.md | 2 +- docs/impl/reports/DEVICE-NAMING.md | 68 ++++++++++++++++++++++++++++++ docs/impl/reports/PCAN-SETUP.md | 3 ++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 docs/impl/reports/DEVICE-NAMING.md diff --git a/docs/BOM.md b/docs/BOM.md index 332e668..fd73007 100644 --- a/docs/BOM.md +++ b/docs/BOM.md @@ -20,7 +20,7 @@ type: reference - The mainline kernel driver `peak_usb` is not compiled into the NVIDIA Tegra kernel (JetPack r36.4), so PEAK's proprietary driver is required. - Must be built with `NET=NETDEV_SUPPORT` for standard SocketCAN tools (`candump`, `ip link`, etc.). - The module lives at `/lib/modules/$(uname -r)/misc/pcan.ko` and is loaded via `modprobe pcan`. -- Jetson Orin Nano has built-in `can0` (exposed by GPIO pins), so any extra CAN interface should enumerate from `can1`; but this is only heuristic and should not be trusted. +- Jetson Orin Nano has a built-in CAN controller (exposed by GPIO pins). Kernel `canN` order is not stable, so both are pinned by udev: onboard → `can_onboard`, PCAN-USB → `can_pcan` ([device naming](impl/reports/DEVICE-NAMING.md)). ## Waveshare SIM7600G-H-M.2 diff --git a/docs/impl/reports/DEVICE-NAMING.md b/docs/impl/reports/DEVICE-NAMING.md new file mode 100644 index 0000000..9659b38 --- /dev/null +++ b/docs/impl/reports/DEVICE-NAMING.md @@ -0,0 +1,68 @@ +--- +type: report +date: 2026-10-08 +--- +# Stable Device Naming: CAN Interfaces and SIM7600 Ports + +## Problem + +Kernel names come from registration order. That order isn't stable, so anything configured as `can1` or `ttyUSB3` +can point at a different device after a reboot or replug. + +- **CAN:** `mttcan` (onboard) and `pcan` (USB, via `/etc/modules`) both load at boot. The `canN` names go to whichever registers first, + so the PCAN adapter can end up as `can0` and the onboard controller as `can1`. +- **SIM7600:** five `ttyUSB*` ports. Their numbers shift if another USB-serial device enumerates first. + +## Result + +| Device | Identified by | Stable name | +|--------|---------------|-------------| +| Onboard MTTCAN | platform device `c310000.mttcan` | `can_onboard` | +| PEAK PCAN-USB | USB `0c72:000c` | `can_pcan` | +| SIM7600 NMEA port | USB `1e0e:9001`, interface 01 | `/dev/sim7600-gps` (symlink) | +| SIM7600 modem | ModemManager `simtech` plugin (USB IDs and per-interface port probing) | n/a, nothing configured by port name | +| SIM7600 data | NetworkManager `povo` connection with `ifname '*'` | n/a, follows whatever modem ModemManager exposes | + +`can_onboard` was verified with a live rename (`mttcan c310000.mttcan can_onboard: renamed from can0`). +`can_pcan` was checked against the driver source only, because the adapter wasn't plugged in. **To do:** plug the adapter in and confirm with `ip -br link`. + +## CAN: `/etc/udev/rules.d/70-can-names.rules` + +``` +SUBSYSTEM=="net", ACTION=="add", KERNELS=="c310000.mttcan", NAME="can_onboard" +SUBSYSTEM=="net", ACTION=="add", ATTRS{idVendor}=="0c72", ATTRS{idProduct}=="000c", NAME="can_pcan" +``` + +- **Both** interfaces get names outside the kernel's `canN` pool. Renaming only the PCAN and pinning the onboard controller to `can0` would race: + if `pcan` registers first, it briefly holds `can0` and the onboard rename collides. +- Underscores rather than hyphens: systemd escapes `-` in device unit names (`sys-subsystem-net-devices-can\x2dpcan.device`). +- The file must sort before `80-net-setup-link.rules`, which only assigns a name when `NAME` is still empty. +- PCAN matching relies on the netdev's sysfs parent being the USB interface. In PEAK 9.2.0, `pcan_netdev.c` calls `SET_NETDEV_DEV(ndev, pcandev_to_dev(pdev))`, + and for USB devices that returns `&usb_intf->dev`, so `ATTRS{idVendor}` matches. +- PEAK's own `assign=` module parameter (`peak`, `devid`, `pcanX:canY`) can only produce `canN` names, so it doesn't avoid the race. +- **More than one PCAN-USB:** both would match `can_pcan`, and the second rename would fail. Give each adapter a device ID (PEAK tools: `pcan-settings`), + then add a per-ID match and distinct names. + +Apply without a reboot (interface must be DOWN): + +``` +sudo udevadm control --reload +sudo udevadm trigger --action=add /sys/class/net/can0 +``` + +Usage: `sudo ip link set can_pcan up type can bitrate 500000 && candump can_pcan`. +Generic detection still works: `ip -d link show type can` lists both regardless of name. + +## SIM7600: why no further rules are needed + +- ModemManager identifies the modem by USB ID and probes each port, which gives it the `(at)`, `(gps)`, `(qmi)` and `(net)` roles. It never relies on a `ttyUSB` number. +- NetworkManager binds the `povo` GSM connection to the ModemManager modem object (`ifname '*'`), not to a device node. +- gpsd is the only consumer of a raw port, and it uses the `/dev/sim7600-gps` udev symlink + (`/etc/udev/rules.d/99-sim7600-gps.rules`, matching `ENV{ID_USB_INTERFACE_NUM}=="01"`). +- `ttyUSB2` is mentioned in the [SIM7600 report](SIM7600-SETUP.md) only for manual AT sessions. `ls -l /dev/serial/by-id/` shows the stable path + (`...-if02-port0`) if numbering ever shifts. + +## Affected references + +- [PCAN setup report](PCAN-SETUP.md): its `can0`/`can1` examples are from before this change. +- `halogen-workstation-roadmap` phase 17 lists "presence of `can0`" as a CAN heuristic. That heuristic is now false on this host; `ip -d link show type can` still works. diff --git a/docs/impl/reports/PCAN-SETUP.md b/docs/impl/reports/PCAN-SETUP.md index 180b477..87e577c 100644 --- a/docs/impl/reports/PCAN-SETUP.md +++ b/docs/impl/reports/PCAN-SETUP.md @@ -100,6 +100,9 @@ $ ip link show type can - `can0` = onboard MTTCan - `can1` = PCAN-USB adapter +> **2026-10-08:** `canN` order is not stable across boots. Interfaces are now pinned to `can_onboard` / `can_pcan` +> by a udev rule; read `can1` below as `can_pcan`. See [device naming](DEVICE-NAMING.md). + ### Step 5: Bring Up the Interface ```bash