dualsense: possibly fix weird vibrations
All checks were successful
Build and Deploy / mreow (push) Successful in 1m1s
Build and Deploy / yarn (push) Successful in 1m6s
Build and Deploy / muffin (push) Successful in 1m25s

This commit is contained in:
2026-05-07 01:27:00 -04:00
parent 03c3d01c66
commit e159f4d90f
2 changed files with 99 additions and 0 deletions

View File

@@ -16,6 +16,7 @@
./lact.nix
./vr.nix
./forza-trigger
./dualsense-attenuation.nix
inputs.impermanence.nixosModules.impermanence
];
@@ -173,4 +174,9 @@
# PS5 DualSense adaptive triggers in Forza Horizon 4 / 5.
services.forzaTrigger.enable = true;
# PS5 DualSense LRA whine fix: cap firmware-level haptic amplitude so the
# voice-coil actuators never hit their audible-buzz regime under heavy
# rumble. Independent of the forza-trigger adaptive trigger path.
services.dualsenseAttenuation.enable = true;
}

View File

@@ -0,0 +1,93 @@
# DualSense rumble/trigger firmware-level attenuation.
#
# The PS5 controller's haptic actuators are linear resonant actuators (LRAs) —
# voice coils, not ERM rotors. When driven near max amplitude they emit an
# audible high-pitched whine in addition to the intended rumble. This is a
# hardware property of the actuator, not a kernel or Steam Input bug, and
# can't be fixed in any rumble path that talks to the LRAs at full strength.
#
# Sony exposes a firmware knob ("rumble emulation amplitude modifier", in
# DS_OUTPUT report 0x05/0x31, gated by valid_flag2 bit RUMBLE_EMULATION_SELECT)
# that clamps haptic / trigger amplitude before the actuator is driven. This
# module sends that report once on plug-in via dualsensectl. The setting
# lives in controller RAM until the next power-cycle (USB unplug or BT
# disconnect-with-power-off), so re-applying on every udev `add` covers
# every reconnect.
#
# Coexists with Steam Input's rumble path: Steam writes rumble in the same
# output report, but only sets COMPATIBLE_VIBRATION / HAPTICS_SELECT in
# valid_flag0 — not RUMBLE_EMULATION_SELECT — so its writes don't reset the
# attenuation. Verified live with Steam (gamepadui) and forza-trigger both
# holding hidraw concurrently.
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.dualsenseAttenuation;
applyScript = pkgs.writeShellScript "dualsense-apply-attenuation" ''
set -eu
# Retry briefly: the hidraw node exists by udev `add`, but the device
# may still be settling its initial HID descriptor exchange. dualsensectl
# needs to read the firmware report before it can write attenuation.
for _ in 1 2 3 4 5 6 7 8 9 10; do
if ${pkgs.dualsensectl}/bin/dualsensectl -l 2>/dev/null | grep -q ":"; then
exec ${pkgs.dualsensectl}/bin/dualsensectl attenuation \
${toString cfg.rumble} ${toString cfg.trigger}
fi
sleep 0.2
done
echo "no DualSense detected; nothing to attenuate" >&2
exit 0
'';
in
{
options.services.dualsenseAttenuation = {
enable = lib.mkEnableOption "Apply DualSense haptic attenuation on plug-in";
rumble = lib.mkOption {
type = lib.types.ints.between 0 7;
default = 3;
description = ''
Rumble (LRA) attenuation, 0-7. Higher = weaker. 3 keeps the LRAs
below their audible-whine threshold while leaving rumble clearly
perceptible. 0 disables attenuation (full strength).
'';
};
trigger = lib.mkOption {
type = lib.types.ints.between 0 7;
default = 0;
description = ''
Adaptive trigger attenuation, 0-7. Higher = weaker. 0 leaves
trigger feedback untouched forza-trigger drives the triggers
with bounded amplitudes already, so the trigger LRAs don't
normally hit the audible regime.
'';
};
};
config = lib.mkIf cfg.enable {
services.udev.extraRules = ''
# DualSense (USB)
ACTION=="add", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ce6", TAG+="systemd", ENV{SYSTEMD_WANTS}+="dualsense-attenuation.service"
# DualSense Edge (USB)
ACTION=="add", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0df2", TAG+="systemd", ENV{SYSTEMD_WANTS}+="dualsense-attenuation.service"
# DualSense (Bluetooth)
ACTION=="add", SUBSYSTEM=="hidraw", KERNELS=="*054C:0CE6*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="dualsense-attenuation.service"
# DualSense Edge (Bluetooth)
ACTION=="add", SUBSYSTEM=="hidraw", KERNELS=="*054C:0DF2*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="dualsense-attenuation.service"
'';
systemd.services.dualsense-attenuation = {
description = "Apply DualSense rumble/trigger attenuation in controller firmware";
serviceConfig = {
Type = "oneshot";
ExecStart = applyScript;
};
};
};
}