Getting rumble on the 8BitDo SN30 Pro+ to work properly with Linux

TLDR: After having owned the controller for a while I noticed that every time you install a new kernel update the fixes are undone again, and have to be re-applied. I also discovered that the controller UID is the same for all SN30 Pro+ controllers. So I’ve made the following script that fixes everything automatically instead. Save it somewhere, check that the config file paths are correct, install xpadneo using the github instructions, then run the script as root.

#!/bin/bash

if [[ $EUID -ne 0 ]]; then
    echo "This script must be run as root"
    exit 1
fi

# Config file paths
MP_PATH="/etc/modprobe.d/99-xpadneo-bluetooth.conf"
UDEV_PATH="/etc/udev/rules.d/99-xpadneo.rules"
ENV_PATH="/etc/environment"

# Look for already applied fixes
MPFIXED=$(grep -c "disable_ff=2" $MP_PATH)
UDEVFIXED=$(grep -c "RUN+=\"/bin/sh -c 'echo xpadneo udev: \$kernel > /dev/kmsg && { echo \$kernel > /sys/bus/hid/drivers/hid-generic/unbind; echo \$kernel > /sys/bus/hid/drivers/microsoft/unbind; echo \$kernel > /sys/bus/hid/drivers/xpadneo/bind; }; '\"" $UDEV_PATH)
ENVFIXED=$(grep -c "8BitDo SN30 Pro+" $ENV_PATH)

if [[ $MPFIXED -eq 0 ]]; then
  # Turn off trigger rumble
  echo "options hid_xpadneo debug_level=0 disable_ff=2 trigger_rumble_damping=4 fake_dev_version=4400 combined_z_axis=n" >> $MP_PATH
  echo "Applied fix to $MP_PATH."
else
  echo "Fix already applied to $MP_PATH. Skipping."
fi

if [[ $UDEVFIXED -eq 0 ]]; then
  # Apply dirty fix to udev rules
  sed -i 's/RUN/#RUN/g' $UDEV_PATH
  echo "RUN+=\"/bin/sh -c 'echo xpadneo udev: \$kernel > /dev/kmsg && { echo \$kernel > /sys/bus/hid/drivers/hid-generic/unbind; echo \$kernel > /sys/bus/hid/drivers/microsoft/unbind; echo \$kernel > /sys/bus/hid/drivers/xpadneo/bind; }; '\"" >> $UDEV_PATH
  echo "Applied fix to $UDEV_PATH"
else
  echo "Fix already applied to $UDEV_PATH. Skipping."
fi

if [[ $ENVFIXED -eq 0 ]]; then
  # Add SDL_CONTROLLERCONFIG environment variable
  echo "SDL_GAMECONTROLLERCONFIG=\"050000005e040000e002000030110000,8BitDo SN30 Pro+,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,\"" >> $ENV_PATH
  echo "Added 'SDL_CONTROLLERCONFIG' variable to $ENV_PATH."
else
  echo "'SDL_CONTROLLERCONFIG' variable already present in $ENV_PATH. Skipping."
fi

Original post follows:

I recently bought an 8BitDo SN30 Pro+ since my old gamepad was an ancient Logitech Dual Action. I wanted a gamepad that was wireless as well as having support for XInput. When I heard about the SN30 Pro+ I couldn’t contain myself, so I went and bought it the same day after reading a bit about it and how it works on Linux. It’s not officially supported, and there wasn’t really much definitive information regarding Linux compatibility, other than that the older SN30 Pro worked fine and it works on the raspberry pi. So I just took a gamble and bought it.

And it does work pretty well with a standard Linux kernel (currently I’m running 5.3.11 on Arch Linux) out of the box. Holding X + Start on the controller will boot it up in XInput mode. B + Start is the old DInput mode. And Y + Start will make it behave like a Switch Pro controller. All of these different modes paired just fine with my Bluetooth adapter. (I have not tested the Apple mode A + Start). It’s just overall a really nice controller.

But there was one thing that was missing when using it on Linux. Rumble! Rumble was only working when plugging the controller in through USB. When doing that, the kernel recognizes it as an Xbox 360 controller and uses the xpad kernel module. But connecting it with Bluetooth the xpad module is not used for some reason.

After some experimenting it seemed like it was a limitation with evdev or the kernel modules or something like that, and I was ready to just give up on getting rumble to work for now. But then I read about xpadneo, which is a kernel module specifically for the Xbox One Wireless Controller.

The SN30 Pro+ is obviously not an Xbox One gamepad, but it identifies as one when you connect it wirelessly using the XInput mode. And as it turns out, xpadneo will make the SN30 Pro+ work with rumble over Bluetooth! There’s some setup required to make it work properly however.

Installation and configuration

Here’s what we need to do:

  • Install xpadneo
  • Disable the Trigger Force Feedback feature of xpadneo
  • Might need to get Steam to recognize the controller when using xpadneo depending on distro.

Being comfortable with the terminal, editing config files, and knowing how to compile a C source file is helpful for this guide.

Installing xpadneo was very simple on Arch Linux; just install xpadneo-dkms-git from AUR. After that the kernel will automatically use the hid_xpadneo module when you connect your SN30 Pro+ over Bluetooth after you have rebooted. I’m not sure there are packages for other platforms, but the GitHub page has a very simple manual installation guide for the most common distros, including Arch.

After installing it and rebooting, when you connect the controller wirelessly it will begin to rumble constantly. While it’s nice that the rumble is now working, this is obviously not what we want. This is caused by the trigger rumble feature that xpadneo has, which needs to be turned off for this controller. This can be done by adding some options to /etc/modprobe.d/99-xpadneo-bluetooth.conf manually, or with the configure.sh script which will do it for you.

So even if you do install the AUR package on Arch Linux you probably still want to clone the xpadneo GitHub repo to get the configure.sh script, since this makes changing the settings a lot easier. So go ahead and clone it if you haven’t already:

git clone https://github.com/atar-axis/xpadneo.git

Once that is done, enter the xpadneo directory and run the configure.sh script. It will show you which flags you can use to configure the module with. For the SN30 Pro+ I just had to turn off the Trigger Force Feedback feature like this:

sudo ./configure.sh -f 2

This will update the 99-xpadneo-bluetooth.conf file so it looks like this:

options bluetooth disable_ertm=y
options hid_xpadneo debug_level=0 disable_ff=2 trigger_rumble_damping=4 fake_dev_version=4400 combined_z_axis=n

If you don’t want to use the configure script you can just paste the above into your 99-xpadneo-bluetooth.conf file.

Disconnect the controller if it’s connected, then unload the module with sudo modprobe -r hid_xpadneo. The next time you connect the controller it won’t be vibrating constantly, and everything will work as expected! Except for Steam… in some cases. While things like native games and RetroArch are now working with rumble, using this module sometimes makes Steam not detect any controllers for some reason. Luckily there are work-arounds for that.

First lets try the easier option as per this post. Open your /etc/udev/rules.d/99-xpadneo.rules file and comment out the RUN line, and replace it with the line from that post. It will look like this:

# XPADNEO UDEV BINDING
# by atar-axis (dollinger.florian@gmx.de)

# in general those files with a higher leading number are processed
# and executed _later_, that means assignment in those files can overwrite
# assignments in other rules which are parsed earlier

# every block is a rule. rules are collected from the .rules files
# according to their name. on every kernel uevent, all rules are
# compared to that event - one by one.

# kernel versions        manual binding (bind/unbind)   bind uevent             hid_generic greedy      hid_microsoft support   gamepad     method
# ---------------        ----------------------------   --------------          ------------------      ---------------------   -------     ------
# [    ... - 2.6.13 [    no                             no                      yes                     no                      *           rmmod (not supported in xpadneo)
# [ 2.6.13 - 4.14   [    yes                            no                      yes                     no                      *           1: add, hid_generic
# [   4.14 - 4.16   [    yes                            yes                     yes                     no                      *           2: bind, hid_generc
# [   4.16 - 4.20   [    yes                            yes                     no                      no                      *           automatic
# [   4.20 - ...    ]    yes                            yes                     no                      yes                     02FD        3: bind, microsoft
#                                                                                                                               02E0        automatic

ACTION=="add", \
KERNEL=="0005:045E:02FD.*|0005:045E:02E0.*", \
SUBSYSTEM=="hid", \
#RUN:="/bin/sh -c 'echo xpadneo udev: $kernel > /dev/kmsg; modprobe hid_xpadneo; echo $kernel > /sys/bus/hid/drivers/hid-generic/unbind; echo $kernel > /sys/bus/hid/drivers/microsoft/unbind; echo $kernel > /sys/bus/hid/drivers/xpadneo/bind; echo xpadneo udev: ok > /dev/km>
RUN+="/bin/sh -c 'echo xpadneo udev: $kernel > /dev/kmsg && { echo $kernel > /sys/bus/hid/drivers/hid-generic/unbind; echo $kernel > /sys/bus/hid/drivers/microsoft/unbind; echo $kernel > /sys/bus/hid/drivers/xpadneo/bind; }; '"

Note that at least when using the AUR xpadneo package this file is overwritten on kernel upgrades.

Now save it and reboot, then try launching Steam and see if the controller is detected. If not we can try to create an FNA/SDL environment variable for the controller instead.

Undo the changes in 99-xpadneo.rules again before continuing (or just keep them, it works with both applied), then follow the instructions here on the Arch wiki and compile the controllermap.c file from the SDL sources (you’re going to need GCC or another compiler for this. Look up how to install it in your distro if you don’t have it), and you will get a guid for your controller when you run it that you can add to an environment variable named SDL_GAMECONTROLLERCONFIG.

Where to add the environment variables varies a bit depending on your distro. You should add them in a place where they are applied even for graphical environments, so ~/.bash_profile and such is probably not enough. For distros with systemd (Or at least on Arch Linux. Look up where to add your environment variables in your distro) you can generally add simple ones like this to /etc/environment, which is where I ended up putting it. This is applied globally for all users however, so keep that in mind. It should look something like this:

#
# This file is parsed by pam_env module
#
# Syntax: simple "KEY=VAL" pairs on separate lines
#
SDL_GAMECONTROLLERCONFIG="050000005e040000e002000030110000,8BitDo SN30 Pro+,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,"

And that’s it! Now once you reboot one more time, Steam should recognize your controller as an Xbox One Wireless Controller and have working rumble as well.

So there you have it. A bit messy to set up but once done you don’t have to worry about it. You might have to re-install the xpadneo module after kernel upgrades depending on your distro though, but that’s as simple as running the install.sh script again. And maybe the xpadneo devs will make the SN30 Pro+ work more seamlessly in the future as well so it can automatically disable trigger force feedback etc.

Update: After some more testing I noticed that games through Wine didn’t detect the controller at all. This is because Wine’s Xinput implementation is using SDL2, which means you’re going to have to do the above SDL environment variable fix to get it to work in Wine. After doing that it works perfectly. I’m now using both the fixes at the same time and they don’t appear to conflict in any way.

Before doing that I also disabled the event and js type joysticks in Wine’s controller settings (run wine control in a terminal to access them), as well as added library overrides for xinput1_{1-4}.dll and xinput9_1_0.dll in winecfg and set them to builtin, although I’m not sure that was necessary. Those changes didn’t do anything to fix it by themselves; only after adding the SDL environment variable did it start to work.


Links

2 thoughts on “Getting rumble on the 8BitDo SN30 Pro+ to work properly with Linux”

  1. Hey!
    I created the GitHub issue regarding to Steam not recognizing the controller and eventually I found out this blog post and I bookmarked it since the controller kept stopping to work and came here from time to time when I forgot what to do (and now you made that script that is really helpful, specially because I use this controller in two different computers with Fedora) and just now I noticed you thanked me! But now it is my time to thank you for this really helpful script!
    By the way, did you posted this to the 8bitdo subreddit? I noticed there are a few SN30 Pro+ Linux users there. If you don’t have an account I can do it for you, just let me know.

    1. Oh hey!
      I’m really glad to hear that my script was helpful to someone, and even to the person who inspired this post in the first place no less! I mostly wrote it for myself to remember actually haha. I have not posted it anywhere else. Feel free to share it if you think it could be helpful to others as well, I’d be honored. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.