fix(batm3): eGalax touchscreen (kernel 6.6) + USB-bootable image #80
2 changed files with 156 additions and 8 deletions
|
|
@ -12,13 +12,36 @@
|
||||||
timeout = 3;
|
timeout = 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Pin the 6.6 LTS kernel. The Dell 9030 AIO's eGalax SAW touch panel
|
||||||
|
# (0eef:0001) works with the usbtouchscreen driver on 6.6 (the known-good
|
||||||
|
# internal-SATA install runs 6.6.68). On 25.11's default 6.12 kernel this
|
||||||
|
# old controller regressed: hid-multitouch grabs it and mis-parses the HID
|
||||||
|
# report ("failed to fetch feature 7", axes read stuck), usbtouchscreen
|
||||||
|
# refuses it, and touch is unusable regardless of udev/X config. Matching
|
||||||
|
# douro.nix's per-hardware kernel pin. Re-test touch before bumping this.
|
||||||
|
kernelPackages = pkgs.linuxPackages_6_6;
|
||||||
|
|
||||||
initrd.availableKernelModules = [
|
initrd.availableKernelModules = [
|
||||||
"xhci_pci"
|
"xhci_pci"
|
||||||
"ahci"
|
"ahci"
|
||||||
"usbhid"
|
"usbhid"
|
||||||
"sd_mod"
|
"sd_mod"
|
||||||
|
# USB mass-storage: required to boot the dd'd image from a USB stick
|
||||||
|
# (stage-1 must bind the flash drive as a SCSI disk so
|
||||||
|
# /dev/disk/by-label/nixos appears). Harmless on the internal-SATA
|
||||||
|
# install, where ahci+sd_mod already cover the root device.
|
||||||
|
#
|
||||||
|
# NOTE: deliberately NO "uas" here. Many USB sticks/bridges advertise
|
||||||
|
# UAS but drop off the bus ("device offline error, dev sdb") under the
|
||||||
|
# sustained write load of first-boot growPartition/journal/swapfile.
|
||||||
|
# Blacklisting uas below forces the slower-but-reliable usb-storage
|
||||||
|
# (Bulk-Only Transport) path. SATA/eMMC installs don't use uas anyway.
|
||||||
|
"usb_storage"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Keep the USB flash drive off the flaky UAS driver (see note above).
|
||||||
|
blacklistedKernelModules = [ "uas" ];
|
||||||
|
|
||||||
kernelModules = [
|
kernelModules = [
|
||||||
"kvm-intel"
|
"kvm-intel"
|
||||||
"usbtouchscreen"
|
"usbtouchscreen"
|
||||||
|
|
@ -27,6 +50,9 @@
|
||||||
kernelParams = [
|
kernelParams = [
|
||||||
"quiet"
|
"quiet"
|
||||||
"splash"
|
"splash"
|
||||||
|
# Disable USB autosuspend so the boot medium (and kiosk peripherals)
|
||||||
|
# aren't power-suspended mid-I/O — another cause of "device offline".
|
||||||
|
"usbcore.autosuspend=-1"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -105,10 +131,20 @@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# eGalax touchscreen (Dell 9030 AIO built-in panel)
|
# eGalax touchscreen (Dell 9030 AIO built-in panel)
|
||||||
# The eGalax HID descriptor confuses libinput (treats it as touchpad).
|
# By default usbhid/hid-multitouch claim the eGalax and mis-parse its
|
||||||
# Fix: unbind from usbhid at boot, bind to usbtouchscreen kernel module,
|
# HID report descriptor (X axis reads as stuck), so touch is unusable.
|
||||||
# then apply calibration matrix after X11 starts.
|
# Fix: hand the device to the usbtouchscreen kernel driver, which parses
|
||||||
# Unbind eGalax from usbhid, bind to usbtouchscreen
|
# the raw eGalax protocol into a clean single-touch ABS device that the
|
||||||
|
# X evdev driver + calibration matrix (below) map correctly. This mirrors
|
||||||
|
# the known-good internal-SATA install.
|
||||||
|
#
|
||||||
|
# The RUN command modprobes usbtouchscreen ITSELF before unbinding usbhid
|
||||||
|
# and handing over via new_id. usbtouchscreen is also in boot.kernelModules
|
||||||
|
# (systemd-modules-load), but on a USB boot systemd-udev-trigger fires this
|
||||||
|
# rule (~2s) BEFORE modules-load gets usbtouchscreen in (~12s) — so the
|
||||||
|
# new_id write hit a not-yet-loaded driver and the panel was left bound to
|
||||||
|
# nothing. Loading it inline here makes the handoff independent of that
|
||||||
|
# boot-ordering race (on internal-SATA boot the order happened to work).
|
||||||
services.udev.extraRules = lib.mkAfter ''
|
services.udev.extraRules = lib.mkAfter ''
|
||||||
KERNEL=="ttyS[0-9]*", MODE="0666"
|
KERNEL=="ttyS[0-9]*", MODE="0666"
|
||||||
KERNEL=="ttyUSB[0-9]*", MODE="0666"
|
KERNEL=="ttyUSB[0-9]*", MODE="0666"
|
||||||
|
|
@ -116,7 +152,25 @@
|
||||||
SUBSYSTEM=="tty", ATTRS{serial}=="DDDLb103Y23", SYMLINK+="ttyF56", MODE="0666"
|
SUBSYSTEM=="tty", ATTRS{serial}=="DDDLb103Y23", SYMLINK+="ttyF56", MODE="0666"
|
||||||
SUBSYSTEM=="tty", ATTRS{serial}=="A9YW78OC", SYMLINK+="ttyMEI", MODE="0666"
|
SUBSYSTEM=="tty", ATTRS{serial}=="A9YW78OC", SYMLINK+="ttyMEI", MODE="0666"
|
||||||
SUBSYSTEM=="tty", ATTRS{serial}=="A9ZF8ELY", SYMLINK+="ttyNFC", MODE="0666"
|
SUBSYSTEM=="tty", ATTRS{serial}=="A9ZF8ELY", SYMLINK+="ttyNFC", MODE="0666"
|
||||||
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0eef", ATTRS{idProduct}=="0001", RUN+="${pkgs.bash}/bin/bash -c 'echo ''$kernel:1.0 > /sys/bus/usb/drivers/usbhid/unbind 2>/dev/null; echo 0eef 0001 > /sys/bus/usb/drivers/usbtouchscreen/new_id 2>/dev/null'"
|
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0eef", ATTRS{idProduct}=="0001", RUN+="${pkgs.bash}/bin/bash -c '${pkgs.kmod}/bin/modprobe usbtouchscreen 2>/dev/null; echo ''$kernel:1.0 > /sys/bus/usb/drivers/usbhid/unbind 2>/dev/null; echo 0eef 0001 > /sys/bus/usb/drivers/usbtouchscreen/new_id 2>/dev/null'"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Force the X evdev driver on the eGalax (not libinput). The usbtouchscreen
|
||||||
|
# node is a plain single-touch absolute device; evdev + the transformation
|
||||||
|
# matrix in egalax-calibrate below give correct orientation. Mirrors the
|
||||||
|
# working internal-SATA install's /etc/X11/xorg.conf.d/99-egalax.conf.
|
||||||
|
environment.etc."X11/xorg.conf.d/99-egalax.conf".text = ''
|
||||||
|
Section "InputClass"
|
||||||
|
Identifier "eGalax Touchscreen"
|
||||||
|
MatchVendor "0eef"
|
||||||
|
MatchProduct "0001"
|
||||||
|
MatchDevicePath "/dev/input/event*"
|
||||||
|
Driver "evdev"
|
||||||
|
Option "InvertY" "false"
|
||||||
|
Option "InvertX" "false"
|
||||||
|
Option "SwapAxes" "false"
|
||||||
|
Option "Calibration" ""
|
||||||
|
EndSection
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Apply touchscreen calibration after X11 starts
|
# Apply touchscreen calibration after X11 starts
|
||||||
|
|
@ -130,9 +184,26 @@
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
User = "bitspire";
|
User = "bitspire";
|
||||||
Environment = "DISPLAY=:0";
|
# DISPLAY *and* XAUTHORITY — without the auth cookie xinput dies with
|
||||||
ExecStartPre = "${pkgs.coreutils}/bin/sleep 3";
|
# "Invalid MIT-MAGIC-COOKIE-1 key / Unable to connect to X server" and
|
||||||
ExecStart = "${pkgs.xorg.xinput}/bin/xinput set-prop 'eGalax Inc. USB TouchController' 'Coordinate Transformation Matrix' 0 -1.268 1.147 -1.224 0 1.118 0 0 1";
|
# the matrix is never applied, so touches register but land in the wrong
|
||||||
|
# place (the panel then feels dead). This was the actual boot-time bug.
|
||||||
|
Environment = [ "DISPLAY=:0" "XAUTHORITY=/home/bitspire/.Xauthority" ];
|
||||||
|
# Wait for the eGalax X device to appear (usbtouchscreen binds a little
|
||||||
|
# after display-manager on a USB boot) and retry, instead of a fixed
|
||||||
|
# sleep — more robust to boot timing. Matrix: swap X/Y + invert + scale
|
||||||
|
# to the active panel area (matches the known-good internal install).
|
||||||
|
ExecStart = pkgs.writeShellScript "egalax-calibrate" ''
|
||||||
|
for i in $(${pkgs.coreutils}/bin/seq 1 30); do
|
||||||
|
if ${pkgs.xorg.xinput}/bin/xinput list --name-only 2>/dev/null | ${pkgs.gnugrep}/bin/grep -qx 'eGalax Inc. USB TouchController'; then
|
||||||
|
exec ${pkgs.xorg.xinput}/bin/xinput set-prop 'eGalax Inc. USB TouchController' \
|
||||||
|
'Coordinate Transformation Matrix' 0 -1.268 1.147 -1.224 0 1.118 0 0 1
|
||||||
|
fi
|
||||||
|
${pkgs.coreutils}/bin/sleep 1
|
||||||
|
done
|
||||||
|
echo "egalax-calibrate: eGalax device not found after 30s" >&2
|
||||||
|
exit 1
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
77
flake.nix
77
flake.nix
|
|
@ -424,6 +424,83 @@
|
||||||
printf 'verify ESP label: '; mlabel -i "$out/nixos.img@@$espStart" -s :: || true
|
printf 'verify ESP label: '; mlabel -i "$out/nixos.img@@$espStart" -s :: || true
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# USB-bootable BATM3 TEST image with DISTINCT partition labels
|
||||||
|
# (nixos-usb / ESP-USB). The plain disk-image-batm3 reuses the generic
|
||||||
|
# nixos/ESP labels, so a USB stick carrying it, booted on a batm3 whose
|
||||||
|
# internal SATA drive ALREADY holds a nixos/ESP-labelled install, makes
|
||||||
|
# stage-1's by-label/nixos resolve to the internal drive (larger fs,
|
||||||
|
# journal recovers) instead of the stick — the stage-2 init path baked
|
||||||
|
# into the USB's boot entry isn't on that root, so stage 1 aborts.
|
||||||
|
# Distinct labels make stage-1 pick the stick unambiguously WITHOUT
|
||||||
|
# touching the internal drive. Unlike disk-image-sintra-usb this keeps
|
||||||
|
# systemd-boot: the batm3 firmware UEFI-USB-boots fine via the ESP's
|
||||||
|
# /EFI/BOOT/BOOTX64.EFI removable fallback, so no GRUB/hybrid-table
|
||||||
|
# change is needed — only the label disambiguation here plus the
|
||||||
|
# usb_storage/uas initrd modules (in batm3.nix). Does NOT grow to fill
|
||||||
|
# the stick (see the growPartition note below — sfdisk on first boot
|
||||||
|
# wedges flaky USB bridges); auto-upgrade off (test image, not a managed
|
||||||
|
# fleet member — also stops scheduled bootloader writes landing on the
|
||||||
|
# internal drive's ESP).
|
||||||
|
disk-image-batm3-usb =
|
||||||
|
let
|
||||||
|
cfg = self.nixosConfigurations.batm3-installed.extendModules {
|
||||||
|
modules = [
|
||||||
|
({ lib, ... }: {
|
||||||
|
fileSystems."/".device = lib.mkForce "/dev/disk/by-label/nixos-usb";
|
||||||
|
fileSystems."/boot".device = lib.mkForce "/dev/disk/by-label/ESP-USB";
|
||||||
|
# /boot must NOT be a hard boot dependency on the USB test
|
||||||
|
# image. The firmware already loaded the bootloader from the
|
||||||
|
# ESP before Linux started; /boot is only remounted so the OS
|
||||||
|
# can *update* the bootloader — which this image never does
|
||||||
|
# (autoUpgrade off, no nixos-rebuild on the stick). Without
|
||||||
|
# nofail, a slow/late ESP-USB enumeration (BOT is slower than
|
||||||
|
# UAS) blows past systemd's 90s device-timeout and drops to
|
||||||
|
# emergency mode — with root locked, an unrecoverable dead end.
|
||||||
|
# nofail + a short timeout lets the (already-mounted) root carry
|
||||||
|
# the boot to completion; /boot mounts if/when the ESP shows up.
|
||||||
|
fileSystems."/boot".options = [ "nofail" "x-systemd.device-timeout=10s" ];
|
||||||
|
# DELIBERATELY NO growPartition/autoResize on the USB image.
|
||||||
|
# growPartition runs sfdisk to rewrite the stick's partition
|
||||||
|
# table on first boot — the single most bus-stressing write of
|
||||||
|
# the boot. Flaky USB bridges drop off the bus mid-rewrite
|
||||||
|
# (sfdisk hangs forever as an uninterruptible D-state task) and,
|
||||||
|
# worse, partition 1 (ESP-USB) vanishes with the device, so
|
||||||
|
# /boot times out too. The kiosk's persistent state (state.db,
|
||||||
|
# .env, wifi.conf, logs) is a few MB and the built image already
|
||||||
|
# carries ~2GB free inside root — growing to fill the stick buys
|
||||||
|
# nothing and costs reliability. The internal-SATA target
|
||||||
|
# (disk-image-batm3) keeps growPartition: a real AHCI SSD won't
|
||||||
|
# drop the bus and there filling the disk is worth it.
|
||||||
|
system.autoUpgrade.enable = lib.mkForce false;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
baseImage = import (nixpkgs + "/nixos/lib/make-disk-image.nix") {
|
||||||
|
inherit pkgs lib;
|
||||||
|
config = cfg.config;
|
||||||
|
format = "raw";
|
||||||
|
partitionTableType = "efi";
|
||||||
|
diskSize = "auto";
|
||||||
|
label = "nixos-usb"; # ext4 root label (make-disk-image -L)
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.runCommand "nixos-disk-image-batm3-usb"
|
||||||
|
{ nativeBuildInputs = [ pkgs.parted pkgs.mtools ]; }
|
||||||
|
''
|
||||||
|
mkdir -p $out
|
||||||
|
cp --sparse=always ${baseImage}/nixos.img $out/nixos.img
|
||||||
|
chmod +w $out/nixos.img
|
||||||
|
# make-disk-image hardcodes the ESP FAT label to "ESP"; relabel the
|
||||||
|
# volume to ESP-USB so /boot (by-label/ESP-USB) can't resolve to an
|
||||||
|
# internal drive's ESP. Volume label only — bootloader files are
|
||||||
|
# untouched, and UEFI loads /EFI/BOOT/BOOTX64.EFI regardless.
|
||||||
|
espStart=$(parted -sm "$out/nixos.img" unit B print | awk -F: '$1==1 {gsub("B","",$2); print $2}')
|
||||||
|
echo "ESP partition starts at byte $espStart — relabelling to ESP-USB"
|
||||||
|
export MTOOLS_SKIP_CHECK=1
|
||||||
|
mlabel -i "$out/nixos.img@@$espStart" ::ESP-USB
|
||||||
|
printf 'verify ESP label: '; mlabel -i "$out/nixos.img@@$espStart" -s :: || true
|
||||||
|
'';
|
||||||
|
|
||||||
# Backwards compat
|
# Backwards compat
|
||||||
iso = self.nixosConfigurations.douro.config.system.build.isoImage;
|
iso = self.nixosConfigurations.douro.config.system.build.isoImage;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue