Story / scenario opener
You plug in your Xbox controller. The lights flash. Steam shows the controller icon grayed out. You open a game, and the menu tells you to press a button, but nothing happens. You've checked the cable. You've rebooted. The hardware works on Windows. Fedora is ignoring it. This is usually a driver gap, a permission wall, or a udev rule that hasn't fired. The kernel might see the USB device but refuse to create the input node. Or the node exists, but your user account lacks the group membership to read it. SELinux can also block access if the context is wrong. Start by checking where the chain breaks.
What's actually happening
The kernel handles the USB connection. It loads a driver module. That module creates a device file in /dev/input/. Your game reads that file. If the module isn't loaded, the file doesn't exist. If the file exists but your user account isn't in the right group, the kernel blocks the read. Think of it like a secure door. The kernel is the building manager. The driver is the keycard reader. The device file is the door. Your user account needs the right keycard to walk through.
The udev daemon watches the kernel for hardware changes. When the kernel reports a new device, udev runs rules to create the device node and set permissions. Fedora ships with robust default rules. You rarely need to write custom rules for controllers. If you do, put them in /etc/udev/rules.d/. Never edit files in /usr/lib/udev/rules.d/. Those ship with packages and get overwritten on updates. Edit /etc/ for your changes.
The fix
Run lsusb to see if the USB subsystem sees the hardware at all. If it's not here, the cable is dead or the port is broken.
lsusb
# Lists all USB devices currently attached to the system.
# Look for a line mentioning "Microsoft", "Sony", or "Controller".
# If the device is missing here, the kernel never saw the connection.
If lsusb shows the device, check /dev/input/. The kernel driver must have created an event file for the controller to be usable.
ls -l /dev/input/event*
# Lists event devices created by input drivers.
# Each controller usually creates an eventX file.
# Check the timestamps to see if a new file appeared after plugging in.
If no new file appeared, the driver module might not be loaded. Fedora loads most common drivers automatically, but some controllers require manual intervention. Load the module for your controller type.
sudo modprobe xpad
# Loads the Xbox controller driver module into the kernel.
# Try hid-generic if xpad fails or for non-Xbox controllers.
# modprobe handles dependencies automatically.
Check the device list again. If the file exists now, the driver is working. If your game still fails, the issue is permissions. Fedora restricts access to input devices to the input group. Add your user to this group.
sudo usermod -aG input $USER
# Adds your current user to the input group.
# The -a flag appends; without it, you lose other groups.
# The -G flag specifies supplementary groups.
Group changes do not apply to running sessions. Log out and log back in to apply the change to GUI applications like Steam or Lutris. Running newgrp input in a terminal only affects that terminal session. GUI apps inherit permissions at login time. If you skip the re-login, the game will still fail with a permission error.
If you are using a Bluetooth controller, the bluetooth package and bluez stack must be running. Pairing happens via bluetoothctl.
bluetoothctl
# Opens the interactive Bluetooth control tool.
# Type 'scan on' to find devices.
# Type 'pair <MAC>' to initiate pairing.
# Type 'trust <MAC>' to remember the device across reboots.
If the controller works in a test tool but not in a game, check for SELinux denials. Fedora's default policy allows games to access input devices, but custom setups can trigger blocks.
journalctl -t setroubleshoot --no-pager | tail -n 20
# Shows SELinux denial summaries from the setroubleshoot service.
# Look for lines mentioning "input" or "event" devices.
# This is faster than parsing raw audit logs manually.
Verify it worked
Verify the fix with a dedicated test tool. This isolates the kernel and driver from the game engine.
sudo dnf install jstest-gtk
# Installs a graphical joystick tester from the Fedora repos.
# dnf handles dependencies and signature verification automatically.
jstest-gtk
# Launches the tester GUI.
# Select your controller from the dropdown.
# Press buttons to verify the kernel is passing events correctly.
If jstest-gtk detects the controller and buttons register correctly, the hardware and kernel are working. The issue lies within the specific game's configuration or compatibility layer. If jstest-gtk sees nothing, re-check the kernel logs for driver errors.
journalctl -k --no-pager | tail -n 20
# Shows recent kernel log messages.
# Look for errors from xpad, hid-generic, or usbhid.
# journalctl -k is the modern replacement for dmesg.
Common pitfalls
If you see Permission denied when running a test tool, you are likely not in the input group, or you haven't re-logged in since adding the group. GUI applications inherit permissions at login time. Running newgrp input in a terminal only affects that terminal session. Log out and log back in to apply group changes to Steam or Lutris.
If the controller works in jstest-gtk but not in a game, the game might be looking for a joystick device (/dev/input/js0) instead of an event device. Modern Linux prefers event devices. Some older games or emulators still expect joystick nodes. You can enable joystick nodes via a udev rule or a kernel parameter, but most modern games handle event devices fine.
Bluetooth controllers often fail because the bluetooth package isn't installed or the service isn't running. Fedora Workstation installs bluetooth by default, but minimal spins might not. Check the service status.
systemctl status bluetooth
# Checks if the Bluetooth daemon is active and running.
# If inactive, start it with systemctl start bluetooth.
# The daemon handles pairing and device discovery.
If the module loads manually but disappears after reboot, configure persistent loading. Put the module name in /etc/modules-load.d/.
echo "xpad" | sudo tee /etc/modules-load.d/xpad.conf
# Creates a config file to load the xpad module at boot.
# systemd-modules-load.service reads this directory automatically.
# This ensures the driver is available before user space starts.
Reboot before you debug. Half the time the symptom is gone.
When to use this vs alternatives
Use xpad when you have an Xbox controller or a compatible clone. Use hid-generic when you have a generic USB controller or a PlayStation controller on older kernels. Use hid-playstation when you need advanced PlayStation 4/5 features like touchpad or gyro on newer kernels. Use Steam Input when the game doesn't support the controller natively but you want to map buttons anyway. Use jstest-gtk when you need to verify the kernel is receiving input before blaming the game.