Though additional research, a ton of testing/tinkering, and a bit of generative AI to help with implementation, I have a solution that seems to be working for now, though it’s a bit heavy-handed. (On the point of AI implementation, I read through the code to make sure it was reasonable, but I can’t say I 100% understand every detail, use at your own risk.) Here’s my whole process in case it’s helpful for others that have controllers Steam doesn’t support out-of-the-box.
First, my config didn’t work in Steam. I fixed this by editing /lib/udev/rules.d/60-steam-input.rules, copying the line of a similar config and modifying it by adding my idVendor and idProduct. In my case I have idVendor “20d6” and idProduct “a718”, so I added the line KERNEL==“hidraw*”, ATTRS{idVendor}==“20d6”, ATTRS{idProduct}==“a718”, MODE=“0660”, TAG+=“uaccess”. NOTE: It probably would have been more appropriate to add this new line in a new file at /etc/udev/rules.d/; I would recommend doing that if you’re following this procedure.
Now my config does work in Steam, but it only works in Steam games half the time. If I run sudo dmesg | grep -i 20d6, I get the output:
[ 1010.510402] usb 1-3: New USB device found, idVendor=20d6, idProduct=a718, bcdDevice= 1.14
[ 1010.527272] input: PowerA NSW Nano Wired controller as /devices/pci0000:00/0000:00:01.2/0000:01:00.0/usb1/1-3/1-3:1.0/0003:20D6:A718.0008/input/input24
[ 1010.527458] hid-generic 0003:20D6:A718.0008: input,hidraw7: USB HID v1.11 Gamepad [PowerA NSW Nano Wired controller] on usb-0000:01:00.0-3/input0
That line “hid-generic 0003:20D6:A718.0008: input,hidraw7”; I think the input and the hidraw7 are fighting each other for control. (this is also one way to get the idVendor and idProduct, that “20D6:A718” output.) I don’t know why Steam can’t disable the “input” in a reasonable way like it seems to for controllers in its list, but we can disable it ourselves. In my case, my controller is a PowerA NSW Nano Wired controller. I have files /dev/input/by-id/usb-PowerA_NSW_Nano_Wired_controller-event-joystick and /dev/input/by-id/usb-PowerA_NSW_Nano_Wired_controller-joystick that symlink to /dev/input/event[arbitrary number] and /dev/input/js0. If I sudo rm [files] all 4 of those files, then everything works perfectly (seemingly until I reboot). Important note: This is a heavy-handed approach that seems to make the controller not work at all unless you’re using Steam. This is fine for my use case. Now let’s just make it automatic. This is where AI wrote most of the code.
sudo nano /usr/local/bin/steam-powera-cleanup.sh
Populate the file as follows:
#!/usr/bin/env bash
# steam-powera-cleanup.sh — disable kernel input devices for PowerA NSW Nano Wired controller
set -euo pipefail
VENDOR="20d6"
PRODUCT="a718"
# Give the kernel a short moment to populate input nodes fully
sleep 1
found=0
# Remove any by-id symlinks referencing the controller
for idfile in /dev/input/by-id/*PowerA_NSW_Nano_Wired_controller*; do
[ -e "$idfile" ] && rm -f "$idfile" && found=1
done
# Remove /dev/input/event* or js* that match this vendor/product
for node in /dev/input/event* /dev/input/js*; do
if udevadm info -q property -n "$node" 2>/dev/null | grep -qi "${VENDOR}/${PRODUCT}"; then
rm -f "$node"
found=1
fi
done
if [[ $found -eq 1 ]]; then
logger -t "steam-powera-cleanup" "Removed PowerA input nodes (vid:pid ${VENDOR}:${PRODUCT})"
else
logger -t "steam-powera-cleanup" "No PowerA controller nodes found"
fi
exit 0
Make it executable
sudo chmod +x /usr/local/bin/steam-powera-cleanup.sh
Create a service to handle executing the script on controller plugged in.
sudo nano /etc/systemd/system/steam-powera-cleanup.service
Populate with
[Unit]
Description=Remove kernel input devices for PowerA NSW Nano Wired controller
After=local-fs.target
ConditionPathExistsGlob=/dev/input/by-id/*PowerA_NSW_Nano_Wired_controller*
[Service]
Type=oneshot
ExecStart=/usr/local/bin/steam-powera-cleanup.sh
Now create the udev rule to call the service:
sudo nano /etc/udev/rules.d/99-steam-powera.rules
Populate it with
# When PowerA NSW Nano Wired controller is connected, run cleanup service
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="20d6", ATTR{idProduct}=="a718", \
TAG+="systemd", ENV{SYSTEMD_WANTS}="steam-powera-cleanup.service"
Create a service to handle executing the script on boot
sudo nano /etc/systemd/system/steam-powera-cleanup-boot.service
Populate with
[Unit]
Description=Run PowerA controller cleanup on boot if needed
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/steam-powera-cleanup.sh
[Install]
WantedBy=multi-user.target
Then enable it all with sudo udevadm control --reload-rules, sudo systemctl daemon-reload, sudo systemctl start steam-powera-cleanup.service, and sudo systemctl enable steam-powera-cleanup-boot.service.
If things aren’t working at that point, try unplugging/replugging the controller and/or restarting the PC. You could check the /dev/input/ and /dev/input/by-id/ to see if things are being deleted as expected. Now the hidraw that SteamInput is using is still available, but the Inputs that are fighting for control are disabled. If you wish to use the controller without Steam again, you’d need to remove the udev rule and remove the services. In my case /dev/input/js0 doesn’t seem to have been deleted (probably due to imperfect code from the AI), but everything is working perfectly for me playing the Steam game I’ve been testing on. I’ll test on additional Steam games in the coming days to ensure all is good.
Thanks again to everyone for the help! I’ve learned a ton about how Linux handles controllers and such.