deploy: bootable slim Sintra image + shared UP Board serial hardware #72

Merged
padreug merged 3 commits from deploy/sintra-boot-hardware into dev 2026-07-02 21:53:29 +00:00
3 changed files with 75 additions and 45 deletions
Showing only changes of commit 7e90719508 - Show all commits

refactor(deploy): share UP Board serial hardware between installed + live ISO

The sintra live ISO (live.nix) had no serial support — ftdi_sio and the
ttyJ5/ttyJ7 udev symlinks were only in hardware/upboard.nix (installed), so
booting iso-sintra on real hardware failed on the validator + F56 dispenser
while the disk image worked. The two definitions had already drifted (live's
tejo block lacked ttyS4).

Extract the UP Board serial peripherals (usbserial/ftdi_sio/cp210x, the
ttyJ4/ttyJ5/ttyJ7 udev symlinks + permissions, console=tty0) into
hardware/upboard-serial.nix and import it from both upboard.nix (installed
tejo + sintra) and live.nix (sintra only). Single source of truth — the two
artifacts can't drift again. Named upboard-serial (not sintra-serial) since
upboard.nix serves both tejo-installed and sintra-installed.

Camera + LED/SPI rules stay inline in upboard.nix (installed-specific; the
pairing camera works via getUserMedia without the scanner symlink). Verified
by eval: live sintra now carries ftdi_sio + console=tty0 + ttyJ7; installed
sintra/tejo unchanged (serial present, camera present, no console dupe).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Padreug 2026-07-02 18:40:12 +02:00

View file

@ -0,0 +1,61 @@
# UP Board serial peripherals — the validator / dispenser / printer wiring
# shared by the INSTALLED configs (hardware/upboard.nix, used by both
# tejo-installed and sintra-installed) AND the sintra live ISO (live.nix).
# Single source of truth so the two artifacts can't drift — the earlier bug
# was exactly this drift (the sintra live ISO lacked ftdi_sio + the ttyJ7
# symlink, so the F56 dispenser failed while the installed image worked).
#
# Sintra IS a UP Board, so these are the UP Board rules; ttyS1/ttyS5 cover the
# older UP Board / UP4000 (Tejo) dispenser nodes and ttyS4 covers the Sintra
# (Apollo Lake) where the F56 is on the SoC MMIO UART. Only the device that
# actually exists at runtime gets the symlink, so all three coexist safely.
#
# Serial port mapping:
# ttyJ4 = Printer (Nippon NP-2511D-2)
# ttyJ5 = Validator (iVIZION, ID003)
# ttyJ7 = Dispenser (Fujitsu F53/F56)
{ lib, ... }:
{
boot.kernelModules = [
"usbserial" # USB-to-serial adapters
"ftdi_sio" # FTDI USB serial (the iVIZION validator bridge)
"cp210x" # CP210x USB serial (alternative adapter)
];
boot.kernelParams = [
# Do NOT route the kernel console through ttyS4 on Sintra. ttyS4 is the
# SoC's MMIO 16550A (the only real UART besides the legacy ttyS0 at I/O
# 0x3f8) and is wired to the Fujitsu F56 dispenser's RS-232 header. Holding
# it as console prevents userspace opening it at 9600 baud and HAL fails
# with "Input/output error setting custom baud rate of 9600". For serial
# debug, point console at ttyS0 instead.
"console=tty0"
];
services.udev.extraRules = lib.mkAfter ''
# Generic serial port permissions (so the non-root HAL user can open them)
KERNEL=="ttyS[0-9]*", MODE="0666"
KERNEL=="ttyUSB[0-9]*", MODE="0666"
KERNEL=="ttyACM[0-9]*", MODE="0666"
# Printer (ttyJ4)
KERNELS=="1-7.2:1.0", SYMLINK+="ttyJ4"
KERNEL=="ttyUSB0", SYMLINK+="ttyJ4"
# Validator (ttyJ5)
KERNELS=="1-7.3:1.0", SYMLINK+="ttyJ5"
KERNEL=="ttyUSB1", SYMLINK+="ttyJ5"
# Dispenser (ttyJ7). ttyS1/ttyS5 = older UP Board / UP4000; ttyS4 = Sintra.
KERNEL=="ttyS1", SYMLINK+="ttyJ7"
KERNEL=="ttyS4", SYMLINK+="ttyJ7"
KERNEL=="ttyS5", SYMLINK+="ttyJ7"
# Legacy ttyAMA0 alias
SUBSYSTEM=="tty", KERNEL=="ttyS1", SYMLINK+="ttyAMA0", GROUP="dialout"
# Disable USB autosuspend (prevents serial adapters from sleeping)
ACTION=="add", SUBSYSTEM=="usb", TEST=="power/control", ATTR{power/control}="on"
'';
}

View file

@ -11,6 +11,10 @@
{ config, lib, pkgs, ... }:
{
# Serial peripherals (validator/dispenser/printer modules + udev symlinks +
# console=tty0) are shared with the live ISO via ./upboard-serial.nix.
imports = [ ./upboard-serial.nix ];
boot = {
loader = {
systemd-boot.enable = true;
@ -42,21 +46,12 @@
"kvm-intel"
"i2c-dev"
"spi-dev"
"usbserial" # USB-to-serial adapters
"ftdi_sio" # FTDI USB serial
"cp210x" # CP210x USB serial
# Serial modules (usbserial/ftdi_sio/cp210x) → ./upboard-serial.nix.
];
kernelParams = [
"i915.enable_psr=0"
# NOTE: do NOT route the kernel console through ttyS4 on Sintra.
# ttyS4 is the SoC's MMIO 16550A (the only real UART besides the
# legacy ttyS0 at I/O 0x3f8) and is wired to the Fujitsu F56
# dispenser's RS-232 header on Sintra. Holding it as console
# prevents userspace from opening it at 9600 baud and HAL fails
# with "Input/output error setting custom baud rate of 9600".
# If you want serial debug, point console at ttyS0 instead.
"console=tty0"
# console=tty0 (keeps ttyS4 free for the F56) → ./upboard-serial.nix.
"quiet"
"splash"
];
@ -104,37 +99,9 @@
hybrid-sleep.enable = false;
};
# Serial port permissions + tejo-specific symlinks
# Camera + LED/SPI peripherals. The serial rules (validator/dispenser/printer
# symlinks + permissions) are shared with the live ISO in ./upboard-serial.nix.
services.udev.extraRules = lib.mkAfter ''
# Generic serial port permissions
KERNEL=="ttyS[0-9]*", MODE="0666"
KERNEL=="ttyUSB[0-9]*", MODE="0666"
KERNEL=="ttyACM[0-9]*", MODE="0666"
# ── Tejo serial port symlinks ──────────────────────────────────────
# Both UP Board and UP4000 rules included (match different kernel paths)
# Printer (ttyJ4)
KERNELS=="1-7.2:1.0", SYMLINK+="ttyJ4"
KERNEL=="ttyUSB0", SYMLINK+="ttyJ4"
# Validator (ttyJ5)
KERNELS=="1-7.3:1.0", SYMLINK+="ttyJ5"
KERNEL=="ttyUSB1", SYMLINK+="ttyJ5"
# Dispenser (ttyJ7).
# ttyS1 / ttyS5 cover earlier UP Board variants where the dispenser
# lands on those kernel-enumerated serial nodes; ttyS4 covers the
# Sintra (UP Board Atom/Apollo Lake) where the dispenser is wired
# to the SoC's MMIO UART. Whichever device actually exists at
# runtime gets the ttyJ7 symlink.
KERNEL=="ttyS1", SYMLINK+="ttyJ7"
KERNEL=="ttyS4", SYMLINK+="ttyJ7"
KERNEL=="ttyS5", SYMLINK+="ttyJ7"
# Legacy ttyAMA0 alias
SUBSYSTEM=="tty", KERNEL=="ttyS1", SYMLINK+="ttyAMA0", GROUP="dialout"
# ── Camera devices ─────────────────────────────────────────────────
SUBSYSTEM=="video4linux", ATTR{index}=="0", KERNELS=="1-5", ATTRS{idVendor}=="0ac8", ATTRS{idProduct}=="0345", SYMLINK+="video-scan"
SUBSYSTEM=="video4linux", ATTR{index}=="0", KERNELS=="1-2", ATTRS{idVendor}=="0ac8", ATTRS{idProduct}=="0345", SYMLINK+="video-scan"
@ -147,8 +114,5 @@
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
SUBSYSTEM=="i2c-dev", GROUP="i2c", MODE="0660"
SUBSYSTEM=="leds", KERNEL=="upboard:*", ACTION=="add|change", RUN+="${pkgs.findutils}/bin/find /sys$devpath -type f -exec ${pkgs.coreutils}/bin/chmod g+u {} + -exec ${pkgs.coreutils}/bin/chown :leds {} +"
# Disable USB autosuspend (prevents serial adapters from sleeping)
ACTION=="add", SUBSYSTEM=="usb", TEST=="power/control", ATTR{power/control}="on"
'';
}

View file

@ -48,7 +48,12 @@ in
# Reuse ATM systemd service module
./bitspire-atm.nix
];
]
# Sintra: share the UP Board serial hardware (validator/dispenser/printer
# modules + udev symlinks + console=tty0) with the installed image so the
# live ISO drives the same hardware. Safe to import here — unlike upboard.nix
# it declares no fileSystems, so there's no live-boot mount conflict.
++ lib.optionals (machineModel == "sintra") [ ./hardware/upboard-serial.nix ];
# ISO image settings
image.fileName = "bitspire-${machineModel}-live.iso";