Linux driver — overview¶
The RPBridge Linux driver is a single C kernel module
(rpbridge.ko) that matches the RPBridge USB composite device,
discovers its capabilities, and spawns one auxiliary_device per
advertised subsystem. Each child registers into the standard
Linux subsystem core.
| Aux child | Registers into | Userspace interface |
|---|---|---|
rpbridge-gpio |
gpiolib |
/dev/gpiochipN |
rpbridge-i2c |
i2c-core → i2c-dev |
/dev/i2c-N |
rpbridge-spi |
spi-core → spidev |
/dev/spidevN.M |
rpbridge-pwm |
pwm sysfs |
/sys/class/pwm/pwmchipN |
rpbridge-iio |
iio (single-shot) |
/sys/bus/iio/devices/… |
rpbridge-led |
misc cdev | /dev/rpbridge-led<N> |
rpbridge-dmx |
misc cdev | /dev/rpbridge-dmx<N> |
rpbridge-onewire |
w1_bus_master |
/sys/bus/w1/ |
rpbridge-i2s |
misc cdev | /dev/rpbridge-i2s<N> |
The UART side (4× CDC-ACM — three UART1 transceiver banks plus
UART2 via PIO) is handled by mainline cdc_acm. The CAN side is
handled by mainline drivers/net/can/usb/gs_usb.ko. Neither
lives in this module.
Why C¶
The driver was first attempted in Rust-for-Linux. R4L's API is
still stabilising — between Linux 6.18 and 7.0 the
kernel::usb::Driver trait was restructured, KString removed,
#[vtable] mechanics dropped, and the entire probe lifecycle
moved to a PinInit-style construction model. Out-of-tree R4L
modules pay this drift cost on every major kernel release for as
long as the API surface stays unstable.
For a driver that needs to land on whatever Linux Ubuntu ships today — currently 7.0, 8.x within a year — Linux's hard ABI stability guarantee for C makes the maintenance equation flip. A C driver compiles unchanged across years of kernel releases.
The earlier Rust scaffold remains on the archive/rust-driver
git branch for reference. See
adr/0003-language-choice.md
Amendment for the full reasoning.¶
Kernel baseline¶
- Minimum: Linux 5.15 LTS (auxiliary_bus baseline).
- Primary CI target: Linux 7.0 LTS (Ubuntu 26.04 LTS HWE default).
- No
CONFIG_RUSTrequirement. The driver compiles via plain kbuild against any modern kernel-headers package.
See driver/kernel-baseline/SUPPORT.md
for the full distribution / kernel-version compatibility matrix.
Lifecycle¶
usb_driver.probe()
│
├─ kzalloc per-device state, kref_init
├─ usb_get_dev() the parent USB device
├─ find bulk IN/OUT endpoints
├─ usb_set_intfdata() so disconnect can find us
├─ HELLO (version gate; -EPROTONOSUPPORT on mismatch)
├─ GET_CAPABILITIES, parse CBOR map
├─ for each advertised supported class
│ auxiliary_device_register(rpbridge-<class>.<idx>)
└─ return 0
usb_driver.disconnect()
│
├─ usb_set_intfdata(NULL) // block latent callbacks
├─ unregister every aux child // each kref_put's parent
└─ rpbridge_dev_put() // last drop frees state
Refcounting: every aux child takes an extra reference on the
parent state via rpbridge_dev_get() in spawn_one(), dropped
in the aux device's release callback. The kref guarantees that a
late callback firing after disconnect() returns still finds
valid state, and that the final teardown happens exactly once.
Packaging¶
DKMS¶
sudo dkms add driver
sudo dkms build rpbridge/0.1.0
sudo dkms install rpbridge/0.1.0
sudo modprobe rpbridge
Debian / Ubuntu¶
Built by the driver-package.yml CI workflow:
Fedora / Rocky¶
udev¶
The module package installs
/usr/lib/udev/rules.d/70-rpbridge.rules, which grants
plugdev-group write access and sets stable symlinks
(/dev/rpbridge-uart1, /dev/rpbridge-debug,
/dev/rpbridge-bootsel).
Testing¶
Test ladders that run (or will run) in CI:
- Compile gate —
tools/build.sh driveragainst the Linux 7.0 LTS kernel-headers shipped with Ubuntu 26.04 LTS in the builder Docker image. 0 warnings, 0 errors enforced. - checkpatch —
make checkpatchruns the kernel tree's ownscripts/checkpatch.pl --strictagainstdriver/src/. Local today, CI gate planned. - umockdev replay — recorded USB sessions exercise probe /
disconnect paths without real hardware. Fixture capture is
pending on first hardware availability (see
driver/TODO.mdP1). - Hardware end-to-end —
tests/e2e.md10-section checklist to be executed against rev-B for sign-off.
See driver/README.md
for the source-tree layout and
driver/TODO.md
for the prioritised open-work list.