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 <noreply@anthropic.com>
This commit is contained in:
mikkeli
2026-10-08 01:38:25 +09:00
parent 4742d4a5bb
commit 2d62d1a4f8
3 changed files with 72 additions and 1 deletions
+68
View File
@@ -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.
+3
View File
@@ -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