diff --git a/crates/punktfunk-host/src/inject/linux/steam_controller.rs b/crates/punktfunk-host/src/inject/linux/steam_controller.rs index 79373dad..b0f837eb 100644 --- a/crates/punktfunk-host/src/inject/linux/steam_controller.rs +++ b/crates/punktfunk-host/src/inject/linux/steam_controller.rs @@ -339,11 +339,18 @@ fn open_transport(idx: u8) -> Result { } } } - // 3. UHID — universal fallback (works everywhere; Steam Input won't promote it). + // 3. UHID — universal fallback (works everywhere; Steam Input won't promote it). This is a + // DEGRADED outcome, not a normal one: a UHID device has no USB interface number (Interface: -1), + // so Steam Input ignores it and the controller never appears in Game Mode / can't navigate. + // Reaching here almost always means `vhci_hcd` isn't loaded (the host runs unprivileged and + // can't modprobe it) — load it at boot (packaging ships modules-load.d/punktfunk.conf + + // 60-punktfunk.rules; on a systemd-sysext host `punktfunk-sysext` mirrors both into /etc). let p = SteamDeckPad::open(idx)?; - tracing::info!( + tracing::warn!( index = idx, - "virtual Steam Deck created (UHID hid-steam — not Steam-Input-promoted)" + "virtual Steam Deck created as UHID hid-steam — Steam Input WON'T promote it (no USB \ + interface), so it won't appear in Game Mode. Load vhci_hcd (usbip) so the pad arrives as a \ + real USB device: `sudo modprobe vhci_hcd`, and ensure it loads at boot." ); Ok(DeckTransport::Uhid(p)) } diff --git a/packaging/bazzite/punktfunk-sysext.sh b/packaging/bazzite/punktfunk-sysext.sh index 07a209e5..44df0fbe 100644 --- a/packaging/bazzite/punktfunk-sysext.sh +++ b/packaging/bazzite/punktfunk-sysext.sh @@ -77,10 +77,26 @@ post_merge() { for f in /usr/lib/sysctl.d/99-punktfunk-net.conf /usr/lib/sysctl.d/99-punktfunk-client-net.conf; do [ -f "$f" ] && sysctl -q -p "$f" 2>/dev/null || : done - # vhci-hcd now, no reboot (modules-load.d/punktfunk.conf covers boot): the usbip transport - # that makes the virtual Steam Deck pad a real USB device Steam Input adopts. The udev add - # event fires the 60-punktfunk.rules vhci rule, opening the attach files to the input group. - [ -f /usr/lib/modules-load.d/punktfunk.conf ] && modprobe vhci-hcd 2>/dev/null || : + # vhci-hcd: the usbip transport that makes the virtual Steam Deck pad a real USB device Steam + # Input adopts. Without it the pad falls back to plain UHID hid-steam, which Steam Input won't + # promote (Interface: -1) — so on a host in Game Mode the controller never appears and you can't + # navigate. Two things must be true at boot: the module loaded, and the vhci `attach`/`detach` + # sysfs files opened to the `input` group (the host runs unprivileged and can't modprobe/chown). + # + # A sysext CANNOT rely on its own /usr/lib/modules-load.d + /usr/lib/udev files for this: the + # image merges (systemd-sysext.service) AFTER systemd-modules-load and early udev have already + # run, so at a plain reboot vhci-hcd is never loaded and its rule never applied. Mirror BOTH into + # real /etc (read at the normal early-boot time, and shadowing the /usr copies by filename) so the + # module loads early and udev's coldplug trigger grants the group access. Refreshed every merge so + # a rule/module change in a new image propagates (neither is user-editable config). Then load + + # (re)apply now, no reboot, for this session. + install -Dm0644 /usr/lib/modules-load.d/punktfunk.conf /etc/modules-load.d/punktfunk.conf 2>/dev/null || : + install -Dm0644 /usr/lib/udev/rules.d/60-punktfunk.rules /etc/udev/rules.d/60-punktfunk.rules 2>/dev/null || : + udevadm control --reload 2>/dev/null || : + modprobe vhci-hcd 2>/dev/null || : + # Re-fire the vhci rule against the (possibly already-present) controller so attach/detach pick up + # the input-group ownership even when the module's original add event predated the reloaded rule. + udevadm trigger --subsystem-match=platform --sysname-match='vhci_hcd.*' 2>/dev/null || : # The /etc payload a sysext can't carry. The gamescope-session drop-in is %config(noreplace): # only seed it, never clobber a local edit. The tray autostart entry is not user config. if [ -f "$ETC_SRC/gamescope-session-plus/sessions.d/steam" ] \