The shipped WirePlumber policy sets `priority.driver = 1` on a DS5's ALSA sink and says it "keeps the pad from ever driving the graph". Read against PipeWire's own recalc, it does not: `priority_driver` is unsigned and `pw_context_recalc_graph` skips a driver only when it is `<= 0`. At 1 the pad is merely LAST in the ordering — and last is still elected whenever nothing above it qualifies, which on a punktfunk host is the ordinary in-session state, because claiming our own sink as the default output leaves the box's real card idle. With `node.always-process` on the same node it is also permanently runnable, i.e. permanently eligible. Zero is the value that means excluded. The pad keeps driving the streams actually linked to it — a driver always drives its own group, priority orders the election and nothing else — so GE-Proton's haptics are unaffected. The second rule covers the capture side of the same cards. That node is what clocked a reporter's desktop audio for a whole session: in the Pro Audio profile it carries `priority.driver = 2600`, never suspends, and had nothing linked to it at all — its only function on that machine was to clock other people's graphs. The `alsa_output` matches never touched it. Only the priority is set there; holding a device open is about the playback node GE opens raw, and an always-processing microphone is not something this host should ask for. Both of these are belt to the braces of the host-side fix — a capture group that carries its own driver cannot be handed one — but they are worth having on their own: they are what stops a pad from clocking anything else on the box, including a build that predates it.
63 lines
3.3 KiB
Plaintext
63 lines
3.3 KiB
Plaintext
# WirePlumber policy for DualSense sound cards on a punktfunk host (virtual usbip pads AND
|
|
# physically plugged pads — the failure modes are identical).
|
|
#
|
|
# 1. `node.always-process` + no suspend: PipeWire must HOLD the pad's ALSA device open at all
|
|
# times. GE-Proton's DS5 haptic router opens the sink's backing `hw:` device RAW whenever it
|
|
# is free — and then its own path re-probe EBUSYs against its own handle, invalidates the
|
|
# stream, and spins a refresh loop at 100 Hz (haptics dead, speaker dead). On SteamOS, where
|
|
# that code was developed, PipeWire always holds the device, so GE lands on its well-tested
|
|
# Pulse-routing fallback immediately. This rule reproduces that environment. Field-diagnosed
|
|
# 2026-08-18 (Spider-Man Remastered, GE-Proton 11-5).
|
|
#
|
|
# 2. `priority.driver = 0`: an always-processing node is a permanent graph-driver candidate, and
|
|
# a USB pad's audio clock (virtual or real) must never clock somebody else's graph — the day
|
|
# this was diagnosed, the virtual pad's clock drove the desktop capture to 50 % delivery, and
|
|
# a reporter's pad forwarded over VirtualHere did the same to a whole 15-minute session.
|
|
#
|
|
# ⚠ It must be ZERO, not 1. PipeWire's `pw_context_recalc_graph` skips a driver only when its
|
|
# `priority.driver` is `<= 0` (`priority_driver` is unsigned); at 1 the node is merely LAST in
|
|
# the ordering, and last is still elected whenever nothing above it qualifies — which is the
|
|
# normal in-session state on a punktfunk host, because claiming our own stream sink as the
|
|
# default output leaves the box's real card idle. Zero keeps the pad driving its own linked
|
|
# streams (a driver always drives its own group) while removing it from the election for
|
|
# everyone else's.
|
|
#
|
|
# The `alsa_input` rule below is the same key on the pad's capture side. That node is what
|
|
# clocked the reporter's desktop audio for a whole session: in the Pro Audio profile it
|
|
# carries `priority.driver = 2600` and never suspends, and nothing was linked to it at all —
|
|
# its only function on that machine was to clock other people's graphs.
|
|
#
|
|
# Install: /usr/share/wireplumber/wireplumber.conf.d/ (the user instance reads the shared dirs).
|
|
monitor.alsa.rules = [
|
|
{
|
|
matches = [
|
|
# Both product-string spellings a DS5 family pad ships with: newer firmware / Edge say
|
|
# "DualSense[ Edge] Wireless Controller", earlier firmware says just "Wireless Controller".
|
|
{ node.name = "~alsa_output.usb-Sony_Interactive_Entertainment_DualSense.*" }
|
|
{ node.name = "~alsa_output.usb-Sony_Interactive_Entertainment_Wireless_Controller.*" }
|
|
]
|
|
actions = {
|
|
update-props = {
|
|
session.suspend-timeout-seconds = 0
|
|
node.pause-on-idle = false
|
|
node.always-process = true
|
|
priority.driver = 0
|
|
}
|
|
}
|
|
}
|
|
{
|
|
matches = [
|
|
# The capture side of the same cards. Only the driver priority: holding the device open is
|
|
# about the playback node GE-Proton opens raw, and an always-processing microphone is not
|
|
# something this host has any business asking for.
|
|
{ node.name = "~alsa_input.usb-Sony_Interactive_Entertainment_DualSense.*" }
|
|
{ node.name = "~alsa_input.usb-Sony_Interactive_Entertainment_Wireless_Controller.*" }
|
|
]
|
|
actions = {
|
|
update-props = {
|
|
priority.driver = 0
|
|
}
|
|
}
|
|
}
|
|
]
|