Files
punktfunk/scripts/60-punktfunk.rules
T
enricobuehler 165a42fcc9
ci / web (pull_request) Successful in 2m16s
ci / rust-arm64 (pull_request) Successful in 3m25s
ci / bun-nix (pull_request) Successful in 34s
ci / docs-site (pull_request) Successful in 2m20s
ci / rust (pull_request) Successful in 7m28s
fix(host/udev): the virtual Steam Controller 2's hidraw node was root-only, so Steam never saw it
Field report: the Android client captures a wired SC2 and says so ("captured — streams as-is"),
the host attaches the virtual pad over usbip cleanly, and Steam's Settings → Controller →
Connected Controllers is empty. Nothing works in game mode.

The host's own log names the culprit by what is missing from it. Every kernel control transfer is
there — device/config/string descriptors, SET_CONFIGURATION, SET_IDLE, and a GET of the 372-byte
report descriptor — and there is not one SET_REPORT and not one `answering feature GET`. The
kernel enumerated the controller; Steam never opened it.

60-punktfunk.rules grants hidraw access per product id, and it lists what the host used to mint:
the Sony pads, the Switch Pro, the Deck (28DE:1205) and the classic Steam Controller (28DE:1102).
The SC2 identities `steam_backend_product` mints — wired 28DE:1302 and Puck 28DE:1304 — were never
added, so their nodes stayed root-only while the host runs as a user service.

For any other pad that would be a degradation. For this one it is total: no kernel driver claims
the PID (mainline hid-steam stops at the Deck) and the state reports ride a vendor collection, so
there is no evdev node either. Steam is the only consumer there is, and a hidraw node it cannot
open is not a degraded controller, it is no controller. Leaning on the distro's steam-devices
rules doesn't save it — those lists are per-PID and the SC2 shipped in 2026, so a host whose copy
predates it grants nothing.

Add both identities in the same two forms the Deck uses (KERNELS for the UHID shape, ATTRS for the
usbip/gadget one), and document the symptom in troubleshooting with the hand-rollable rule for
hosts on an older package, the one-line-plus-one-absence log signature, and a note that the
trackpads needing Steam to act as a mouse is lizard mode being off on purpose, not a fault.

Single source of truth: every distro installs this file (the NixOS module takes it from the
package via services.udev.packages), so no per-packaging change is needed.
2026-08-15 20:10:53 +02:00

64 lines
5.1 KiB
Plaintext

# udev rules for the punktfunk streaming host (mirrors Sunshine's 60-sunshine.rules).
#
# Grants the `input` group access to /dev/uinput (virtual X-Box-360 gamepads) and /dev/uhid
# (virtual DualSense via the kernel hid-playstation driver — LED, adaptive triggers, touchpad,
# gyro). `static_node` makes the nodes exist before their modules load.
#
# Install:
# sudo cp scripts/60-punktfunk.rules /etc/udev/rules.d/
# sudo usermod -aG input $USER # then re-login (or reboot)
# sudo udevadm control --reload-rules && sudo udevadm trigger
KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="uhid", SUBSYSTEM=="misc", OPTIONS+="static_node=uhid", GROUP="input", MODE="0660", TAG+="uaccess"
# usbip vhci attach/detach for the virtual Steam Deck controller. Steam Input only
# adopts the virtual Deck when it arrives as a USB device (usbip/vhci or raw_gadget);
# the UHID fallback has no USB interface and Steam ignores it. The sysfs attach files
# are root-only by default while the host runs as a user service — grant the dedicated
# `punktfunk` group write when vhci_hcd appears (module autoload: modules-load.d/punktfunk.conf).
#
# ⚠ This is deliberately NOT the `input` group (2026-08-05 review M-4). Writing `attach` hands the
# kernel a caller-supplied socket fd and materialises an arbitrary, fully userspace-emulated USB
# device — a root-only kernel primitive. Every packaging scriptlet tells the user to
# `usermod -aG input $USER` as step 1, so putting it on `input` handed that primitive to a group
# people are routinely told to join: a member could present a HID keyboard and inject keystrokes
# into a root TTY or the lock screen, or drive any of hundreds of in-tree USB drivers from
# userspace, all without CAP_SYS_ADMIN. The uinput/uhid grants above are already systemwide input
# injection, but neither reaches kernel USB enumeration — this one does, so it gets its own group
# that nothing else asks users to join.
ACTION=="add", SUBSYSTEM=="platform", KERNEL=="vhci_hcd.*", RUN+="/bin/sh -c 'chgrp punktfunk /sys%p/attach /sys%p/detach && chmod 0660 /sys%p/attach /sys%p/detach'"
# hidraw access for the VIRTUAL pads this host creates. Steam/SDL drive a DualSense's rich
# feedback (adaptive triggers, lightbar, player LEDs) exclusively over hidraw — the kernel has no
# evdev API for any of it — and Steam without hidraw demotes a PlayStation pad to a generic evdev
# device, losing its rumble handling too. hidraw nodes are root-only by default; the distro's
# steam-devices rules + logind's uaccess ACL cover only the active seat session, so a game
# launched outside it (a headless/dedicated streaming session) is silently feedback-dead.
# GROUP="input" makes access follow the same group the host itself already requires.
# KERNELS matches the HID device name (works for UHID devices, which have no USB parent);
# the ATTRS pair covers the usbip/gadget Deck, which IS a (virtual) USB device.
# DualSense (054C:0CE6) / DualSense Edge (054C:0DF2) / DualShock 4 (054C:09CC)
KERNEL=="hidraw*", KERNELS=="*054C:0CE6*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", KERNELS=="*054C:0DF2*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", KERNELS=="*054C:09CC*", GROUP="input", MODE="0660", TAG+="uaccess"
# Switch Pro Controller (057E:2009) — SDL's hidapi driver wants hidraw for rumble/LEDs too
KERNEL=="hidraw*", KERNELS=="*057E:2009*", GROUP="input", MODE="0660", TAG+="uaccess"
# Steam Deck (28DE:1205) / classic Steam Controller (28DE:1102), UHID and usbip/gadget forms
KERNEL=="hidraw*", KERNELS=="*28DE:1205*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", KERNELS=="*28DE:1102*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="1205", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="1102", GROUP="input", MODE="0660", TAG+="uaccess"
# Steam Controller 2 (Triton): wired 28DE:1302 and the Puck dongle 28DE:1304 — the two identities
# `steam_backend_product` mints. Steam is the ONLY consumer of this pad: no kernel driver claims
# the PID (mainline hid-steam stops at the Deck), so its state reports ride a vendor collection
# that yields no evdev node at all. A hidraw node Steam cannot open is therefore not a degraded
# controller, it is no controller — the pad enumerates, the kernel reads its report descriptor,
# and Steam's own device list stays empty (field report 2026-08-15: usbip attached cleanly, the
# log showed every kernel control transfer and not one SET_REPORT from Steam).
# Relying on the distro's steam-devices rules is not enough for a pad that shipped in 2026: those
# lists are per-PID and a host whose copy predates the SC2 leaves the node root-only.
KERNEL=="hidraw*", KERNELS=="*28DE:1302*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", KERNELS=="*28DE:1304*", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="1302", GROUP="input", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="1304", GROUP="input", MODE="0660", TAG+="uaccess"