If you want to support me or accelerate the development of a special feature, consider a small donation :heart: Just leave a message if your donation is for a specific use (like a new hardware or a specific function).

Build Status Codacy Badge Average time to resolve an issue Packaging status Discord

Advanced Linux Driver for Xbox One Wireless Gamepad

xpadneo Logo

Quote from @atar-axis (Florian Dollinger), creator of the initial driver:

This is the first driver for the Xbox One Wireless Gamepad (which is shipped with the Xbox One S). I wrote it for a student project at fortiss GmbH and it is fully functional but does only support the connection via Bluetooth as yet - more will follow.

Many thanks to Kai Krakow who sponsored me a Xbox One Wireless Controller :video_game: (including Wireless Adapter) and a pack of mouthwatering guarana cacao :coffee:

Other Projects

These other projects may not support some of the advanced features of xpadneo.

Breaking Changes

Kernel 4.18 or newer required

As of xpadneo v0.10, we require kernel 4.18 or later to utilize HID_QUIRK_INPUT_PER_APP which splits the gamepad into multiple sub-devices to fix problems and incompatibilities at several layers.

SDL 2.0.12 Breakage

As of SDL 2.0.12, SDL introduced a new HIDAPI which can read HID devices in raw mode, bypassing the drivers. Due to the way SDL works, and because xpadneo exposes hidraw devices as user-readable, SDL may see wrong button mappings because it may make wrong assumptions about the protocol mode of Xbox and compatible controllers. If you see wrong button mappings / missing buttons in SDL applications, or rumble does not work, you may need to turn off this behavior by setting an environment variable in your profile: SDL_JOYSTICK_HIDAPI=0

Observed problems:

Advantages of this driver

Xbox One S Wireless controller

This is the initial controller supported from the first version of xpadneo. All features are fully supported. This controller uses emulated profile switching support (see below).

Xbox Elite Series 2 Wireless controller

Basic support for the Xbox Elite Series 2 Wireless controller is present, covering all the features of the driver. The following features are missing:

This controller uses native profile switching support (see below).

Xbox Series X|S Wireless controller

Full support for the Xbox Series X|S controller is present including the share button. This is currently mapped to keyboard event KEY_RECORD and may not work at all for any purpose. Thus, this implementation details may change during one of the next updates. This controller uses emulated profile switching support (see below).

This controller uses BLE (Bluetooth low energy) and can only be supported if your Bluetooth dongle also supports BLE.

8BitDo controllers

This driver supports the Nintendo layout of those controllers to exposes them correctly as button A, B, X, and Y as labelled on the device. This is swapped compared to the original Xbox controller layout. However, this feature is not enabled by default. If you want to use this feature, you have to add a quirk flag to the module options:

# /etc/modprobe.conf
options hid_xpadneo quirks=E4:17:D8:xx:xx:xx:32

where you replace xx:xx:xx with the values from your controller MAC (as shown in dmesg). The value 32 enables Nintendo layout. If you’ll want to add other quirk flags, simply add the values, e.g. 32 + 7 (default quirks for 8BitDo) = 39. After changing this, reload the driver or reboot.

This controller uses emulated profile switching support (see below).

Breaking change: Users of previous versions of the driver may want to remove their custom SDL mappings. Full support has been added for these controllers and broken mapping of previously versions no longer needs to be applied. See also: SDL.

Profile switching

The driver supports switching between different profiles, either through emulation or by using the hardware switch that comes with some models. This switching can be done at any time even while in a game. The API for customizing each profile does not exist yet.

Native profile switching support

The driver support native profile switching for the Xbox Elite Series 2 controller. However, the feature is not finalized yet:

Emulated profile switching support

The driver emulates profile switching for controllers without a hardware profile switch by pressing buttons A, B, X, or Y while holding down the Xbox logo button. However, the following caveats apply:

Getting started


Make sure you have installed dkms, linux headers and a bluetooth implementation (e.g. bluez) and their dependencies.

Please feel free to add other Distributions as well!



You know that everything works fine when you feel the gamepad rumble ;)



In order to update xpadneo, do the following


Further information

For further information please visit the GitHub Page https://atar-axis.github.io/xpadneo/ which is generated automatically from the content of the /docs folder.

You will find there e.g. the following sections

BT Dongles

Please report your Dongles and how they work here

Bluetooth Low Energy

Some newer controller may work in Bluetooth low energy mode (BLE). One of those controllers is the XBOX Series X|S controller.

If your distribution supports the command, run btmgmt info and look for le in supported and current settings, example:

# btmgmt info
Index list with 1 item
hci0:   Primary controller
        addr 00:1A:7D:XX:XX:XX version 6 manufacturer 10 class 0x100104
        supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr hs le advertising secure-conn debug-keys privacy static-addr phy-configuration
        current settings: powered ssp br/edr le secure-conn
        name jupiter
        short name

Cambridge Silicon Radio





Alternatively to using the config-script, you can also do it by hand:

The driver can be reconfigured at runtime by accessing the following sysfs files in /sys/module/hid_xpadneo/parameters:

Some settings may need to be changed at loading time of the module, take a look at the following example to see how that works:


To disable trigger rumbling temporarily, run echo 2 | sudo tee /sys/module/hid_xpadneo/parameters/trigger_rumble_mode

To make the setting permanent and applied at loading time, try echo "options hid_xpadneo trigger_rumble_mode=2" | sudo tee /etc/modprobe.d/99-xpadneo-bluetooth.conf


Gamepad does not connect properly

Enhanced ReTransmission Mode is enabled

If your Gamepad is stuck in a Connected / Disconnected loop, then it may be caused by ERTM. Usually the driver does disable this incompatible mode automatically, but sometimes things go wrong.

Also, there seems to be a bug in the L2CAP handling of the kernel and you may need to force-disable this setting:

# cat /sys/module/bluetooth/parameters/disable_ertm

If it says N, write Y to the file and try again. You may need to remove your partially paired controller from your Bluetooth settings completely before pairing again.

Incompatible Bluetooth Chipset

Some chipsets, e.g. the CSR 85xx, do have problems when you try to reconnect the Gamepad.

Some chipsets may need additional driver firmware to work correctly. Try installing linux-firmware from your distribution.

Secure Boot

Secure Boot may be enabled on your computer. On most Linux distribution, running mokutil --sb-state will tell you if it is the case. When Secure Boot is enabled, unsigned kernel module cannot be loaded. Two options are available:

  1. Disable Secure Boot.
  2. Sign the module yourself.

Instructions for both of these options are available here. Secure Boot is not enabled and pairing still fails? See Debugging.

Gamepad is connected but did not rumble

If the gamepad does connect but it doesn’t rumble, then mosty probably the wrong driver is loaded, or the gamepad is quirky and doesn’t fully support the protocol.

Check the output of the dmesg command to see whether xpadneo was loaded and logged your gamepad.

Gamepad has quirks (i.e., wrong rumble behavior)

You may want to try serveral combinations of quirk flags added to the module paramters. See Configuration and modinfo hid-xpadneo for more information. You may also want to use the hidraw testing utility which bypasses the driver and let’s you try different combination of parameters. The utility is located at misc/examples/c_hidraw.

Gamepad does not connect at all, runs a reconnect loop, or immediately disconnects

Check whether ERTM was disabled (see above). Also, some newer models use a different Bluetooth protocol “Bluetooth low energe” (BLE) which you may accidentally have disabled. Check the following settings in /etc/bluetooth/main.conf:

ControllerMode = dual
Privacy = device

Gamepad axes are swapped, seemingly unresponsive or strange behavior

If you observe this problem with jstest, systemsettings joystick (KDE) or jstest-gtk, there’s usually nothing to do as these test programs use the old joydev API while most (modern) games use the evdev API. Some emulators, tho, use the joydev API and do not respect the axes naming from evdev. In this case, please run the following command to correct the axes mapping for the js interface:

jscal -u 8,0,1,3,4,2,5,16,17,10,304,305,307,308,310,311,314,315,317,318 /dev/input/js0

Explanation: -u set the mapping for 8 axes to axes code 0,1,3,4,... and for 10 buttons to button codes 304,305,307,308,.... This only remaps the joydev API, not the evdev API. Making this change may have unexpected consequences for applications using both APIs.

IMPORTANT: Now test all your axes to collect calibration data. You can then use the following command to store the settings permanently:

sudo jscal-store /dev/input/js0

If the gamepad does not restore the mapping after disconnecting and reconnecting it, i.e., your distribution doesn’t ship a proper udev rule for that, you may want to add the this udev rule, then reboot (see also /misc/examples/udev-rules/99-xpadneo-joydev.rules):

# /etc/udev/rules.d/99-xpadneo-joydev.rules
KERNEL=="js*", ACTION=="add", DRIVERS=="xpadneo", RUN+="/usr/bin/jscal-restore %E{DEVNAME}"

From now on, connecting the gamepad should restore the values from /var/lib/joystick/joystick.state. If you messed up, simply remove your gamepad from the file and start over.

Please check, if jscal-restore is really in /usr/bin, otherwise use the correct path, found by running:

type -p jscal-restore

IMPORTANT NOTE: The Chrome gamepad API (used for Stadia and other browser games) is a hybrid user of both the joydev and the evdev API bit it uses a hard-coded axes mapping for each controller model. Thus, when you run the above commands, the API will be confused and now shows the problem you initially tried to fix. To use the Chrome gamepad API, you’d need to revert that settings. There is currently no known work-around.


If you are asked to send debug info or want to fix bugs, follow the guide displayed when opening a new bug report. This has all the hints to get you started with debugging. You may also want to increase the kernel debug level if your distribution sets it very low. Otherwise, the driver reports most incidents, quirks, and fixes to dmesg.


Useful information can now be aquired with the commands:

xxd -c20 -g1 /sys/module/hid_xpadneo/drivers/hid:xpadneo/0005:045E:*/report_descriptor | tee >(cksum)

Generated Events

If you are asked to supply the events generated by xpadneo, please run the following command:

perl -0777 -l -ne 'print "/dev/input/$1\n" if /Name="Xbox Wireless Controller".*Handlers.*(event[0-9]+)/s' /proc/bus/input/devices | xargs evtest

Do whatever you think does not behave correctly (e.g. move the sticks from left to right if you think the range is wrong) and upload the output.

HID device descriptor (including checksum)

If we ask you to supply the device descriptor, please post the output of the following command:

xxd -c20 -g1 /sys/module/hid_xpadneo/drivers/hid:xpadneo/0005:045E:*/report_descriptor | tee >(cksum)

Bluetooth Connection

Some debugging needs a deeper low level look. You can do this by running btmon:

sudo btmon | tee xpadneo-btmon.txt

Then reproduce the problem you are observing.

We probably also need some information about the dongle:

Working with Secure Boot

Secure Boot is a verification mechanism used when your computer loads your operating system. The boot process of a Linux distribution usually goes like this: UEFI -> UEFI Shim loader -> your distribution. Now back to our two options: disabling Secure Boot or signing xpadneo with your own key.

Disabling Secure Boot

In order for Secure Boot to be active, it must be enabled both at the UEFI level and at the shim level. Which means you should be able to disable Secure Boot:

After choosing either of these two options, Secure Boot should be disabled. You may therefore try to connect your Xbox One gamepad.

Signing xpadneo with your own keys

If you do not wish to disable Secure Boot, you will need to get the Shim to trust xpadneo. The process goes this way:

Xpadneo will need to be re-signed every time it is updated, and every time the kernel is updated. To simplify this process, we provide scripts which will help you generate your keys and automatically sign xpadneo when needed.

cd xpadneo
# Xpadneo will be signed each time it is installed, you should therefore uninstall it now if needed!
sudo ./uninstall.sh

# Copy a directory containing three helper scripts in the /root directory
sudo cp -r misc/module-signing /root

# Generate the keys and ask the shim to trust them.
# They will be saved as /root/module-signing/MOK.der (public key) and /root/module-signing/MOK.priv (private key)
# You will be asked to choose a password. This password will only be needed one time, just after the reboot.
sudo /root/module-signing/one-time-setup

# Just after rebooting, you will be prompted to press a key to enter the Shim. Press any key, then select the
# `Enroll key` option. You will get a chance to review the key that you are about to trust. To accept the key, select
# Yes then enter the password you chose previously.
# /!\ WARNING: The keyboard layout used to type the password will be QWERTY, no matter what keyboard you use.

# Choose `Continue` to boot into your operating system.
# Your public key is now trusted by the shim.

# Ask DKMS to sign Xpadneo when needed.
echo "POST_BUILD=../../../../../../root/module-signing/dkms-sign-module" | sudo tee "/etc/dkms/hid_xpadneo"

# Go back to the xpadneo folder and install the module. It will be signed automatically.
cd xpadneo
sudo ./install.sh


Third party Bugs

While developing this driver we recognized some bugs in KDE and linux itself, some of which are fixed now - others are not:

SDL Mapping

We fixed the following problem by pretending we are in Windows wireless mode by faking the input device PID to 0x02E0. The original PID 0x02FD triggeres several unwanted fixups at multiple layers, i.e. SDL or the HTML5 game controller API. The following paragraphs document the originally wrong behaviour observed and we clearly don’t want our fixed mappings to be “fixed” again by layers detected a seemingly wrong button mapping:

Since after libSDL2 2.0.8, SDL contains a fix for the wrong mapping introduced by the generic hid driver. Thus, you may experience wrong button mappings again.

Also, Wine since version 3.3 supports using SDL for xinput*.dll, and with version 3.4 it includes a patch to detect the Xbox One S controller. Games running in Wine and using xinput may thus also see wrong mappings.

The Steam client includes a correction for SDL based games since some version, not depending on the SDL version. It provides a custom SDL mapping the same way we are describing here.

To fix this and have both SDL-based software and software using the legacy joystick interface using correct button mapping, you need to export an environment variable which then overrides default behavior:

  050000005e040000fd02000003090000,Xbox One Wireless Controller,\

You need to set this before starting the software. To apply it globally, put this line into your logon scripts.

The id 050000005e040000fd02000003090000 is crafted from your device id as four 32-bit words. It is, in LSB order, the bus number 5, the vendor id 045e, the device id 02fd, and the interface version or serial 0903 which is not a running number but firmware dependent. This version number is not the same as shown in dmesg as the fourth component.

You can find the values by looking at dmesg when xpadneo detects your device. In dmesg, find the device path, then change to the device path below /sys and look for the files in the id directory.

The name value after the id is purely cosmetical, you can name it whatever you like. It may show up in games as a visual identifier.

If running Wine games, to properly support xpadneo, ensure you have removed any previous xinput hacks (which includes redirecting xinput*.dll to native and placing a hacked xinput dll in the game directory. Also ensure your Wine built comes with SDL support compiled in.

If you do not want to apply this setting globally, you can instead put the SDL mapping inside Steam config.vdf. You can find this file in $STEAM_BASE/config/config.vdf. Find the line containing "SDL_GamepadBind" and adjust or add your own controller (see above). Ensure correct quoting, and Steam is not running while editing the file. This may not work for Steam in Wine because the Wine SDL layer comes first, you still need to export the variable before running Wine. An example with multiple controllers looks like this:

        "SDL_GamepadBind"               "030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,"

An alternative store location of user-defined mappings can be found here: