ADR-0006 — UART via CDC-ACM only, CAN via gs_usb only¶
Status: Accepted — 2026-04-24 Complements: ADR-0005 — Driver-optional coexistence
Context¶
ADR-0005 established that the
Linux driver is additive: it provides kernel-native abstractions for
the subsystems CDC cannot express (GPIO / I²C / SPI / PWM / ADC), and
does not redefine UART or CAN on this device. The door was left open
for a future, per-instance driver takeover of UART via UART_CLAIM,
and — implicitly — for layering a second CAN protocol on top of RPBP
if the mainline gs_usb protocol turned out to be insufficient.
After weighing the engineering cost of pursuing either option against the concrete feature gap, both doors are now closed.
Decision¶
- UART is CDC-ACM only on both platforms, for the lifetime of v1.
- The
rpbridge.kodriver will NOT register atty_driver. - The firmware keeps
SYS UART_CLAIM/UART_RELEASEopcodes and thecdc_gluearbiter branch as a diagnostic / CI override, but no first-party host code binds them. Third parties may. -
SET_PHY/GET_PHYon the RPBP UART subsystem remain the sole phy-control API for scripts that cannot rely on DTR-driven auto-switch. -
CAN uses
gs_usbonly, for the lifetime of v1. - The RPBP CAN subsystem will NOT be introduced. No CAN opcodes,
no channel mapping, no driver code for CAN in
rpbridge.ko. - Features that mainline
gs_usbalready has flags for —GS_CAN_FEATURE_HW_TIMESTAMP,GS_CAN_FEATURE_FD,GS_CAN_FEATURE_BERR_REPORTING— are implemented inside the existingsrc/usb/gs_usb_glue.ctranslator when the underlying hardware can deliver them. GS_CAN_FEATURE_FDis gated on hardware: the current can2040 PIO stack is Classic-CAN only (struct can2040_msg.data[8]is hard-coded). CAN-FD on this board is a future-hardware question (external SPI-attached MCP2518FD or equivalent), not a software question.
Rationale¶
Why not a custom UART protocol + driver?
- CDC-ACM with the 4-per-phy layout + DTR-driven arbiter already covers ~95 % of terminal workflows (ADR-0005).
- A
tty_driverinrpbridge.kowould duplicate line-coding, DTR/RTS, break, and flow-control state thatusbseralready handles on every Linux distribution we target. Net user benefit for the duplicated code is near zero; Windows parity is lost, not gained. UART_CLAIMis preserved as an opt-in escape hatch because the cost of keeping it is ~50 LoC of firmware and zero driver complexity — but no host code will bind it in v1.
Why not a custom CAN protocol on top of RPBP?
- SocketCAN over
gs_usb.kogives usip link set can0 up,candump,cansend,canplayer, python-can, Kayak, Wireshark CAN dissector, Vector CANoe integration, and the full automotive-tool ecosystem for free. - Windows integration via candle_api already works.
- Every feature we could imagine adding on a custom path
(hardware timestamps, CAN-FD, error counters, filters) is either
already a standardised
gs_usbfeature flag or fits cleanly as an additive flag. Extendinggs_usb_glue.ckeeps us in the mainline-compatible world. - A second parallel path would break the driver-optional story: users would have to choose between SocketCAN and the custom driver; today they don't.
Consequences¶
Positive:
- Smaller surface area. No RPBP CAN subsystem to specify,
test, version, or document. No
tty_driverto maintain. Firmware and driver stay lean. - Clearer driver scope.
rpbridge.kois "GPIO/I²C/SPI/PWM/ADC over RPBP via auxiliary_bus" — a single sentence. - No ecosystem fragmentation. SocketCAN users see
can0; terminal users see/dev/rpbridge-uart1-*. Both paths are standard. - Upstream submission path.
rpbridge.kocan be submitted to linux-usb + linux-rust without CAN or tty code complicating the review.
Negative:
- CAN-FD is gated on hardware. If a user needs CAN-FD today, a
board revision with an external FD-capable CAN controller is
required. The protocol-layer cost to add FD later is small once
the hardware exists (
gs_usbhas FD flags; the controller driver fills incanfd_frameinstead ofcan_frame). - UART_CLAIM is dead-weight for v1. Accepted — ~50 LoC, clearly documented as "reserved for future opt-in"; no runtime cost.
Alternatives reconsidered¶
- Bring up a custom
tty_driverinrpbridge.ko. Rejected — see "Why not a custom UART protocol + driver" above. - Replace
gs_usbwith RPBP-CAN. Rejected — see "Why not a custom CAN protocol on top of RPBP" above. - Dual-path CAN (RPBP + gs_usb simultaneously). Rejected —
forces users to choose, complicates arbitration on shared
hardware, and provides no feature that
gs_usbcannot deliver once advertised via its feature flags.
Implementation sequencing¶
- Firmware M10a —
gs_usb_glueadvertises and emitsGS_CAN_FEATURE_HW_TIMESTAMP. Everygs_host_framecarries a little-endian µs timestamp fromtime_us_64(). Cost: ~30 LoC, mirrors the mainlinecandle_apiwire format. - Firmware M10b —
gs_usb_glueadvertisesGS_CAN_FEATURE_FDiff the compile-time flagRPBRIDGE_HAVE_CANFDis set (default: off). With can2040 the flag stays off. Documented indocs/protocol/hardware-canfd.md. - Driver M10c..i — complete the RPBP client for the
non-UART/non-CAN subsystems. See
driver/TODO.md. - Tests M10j — umockdev recording, KUnit probe paths, and end-to-end smoke tests.
References¶
- ADR-0005 — the coexistence model this ADR further constrains.
- ADR-0001 — RPBP framing stays as-is; no CAN opcodes are added.
firmware/src/usb/gs_usb_glue.c— extended in M10a/M10b.Documentation/networking/devlink/gs_usb.rst(mainline kernel documentation) — source of theGS_CAN_FEATURE_*bit encoding.drivers/net/can/usb/gs_usb.c— the in-tree binding consumer.