Skip to content

Instantly share code, notes, and snippets.

@al3rez
Created June 6, 2026 17:15
Show Gist options
  • Select an option

  • Save al3rez/82e49bde55d4b27cb1a4bd1682490c71 to your computer and use it in GitHub Desktop.

Select an option

Save al3rez/82e49bde55d4b27cb1a4bd1682490c71 to your computer and use it in GitHub Desktop.
Pairing a DualShock 3 over Bluetooth on modern Linux (BlueZ 5.85) when the sixaxis plugin is broken

Pairing a DualShock 3 over Bluetooth on modern Linux (BlueZ 5.85) when the sixaxis plugin is broken

On some modern distros (tested: Ubuntu-based, BlueZ 5.85, kernel 7.x) the built-in BlueZ sixaxis plugin silently does nothing — plug the controller in over USB and it never sets the master address, never registers the device, never writes an SDP record. The result is either no connection at all, or a controller that connects and instantly disconnects (js0 flaps), with this in journalctl -u bluetooth:

profiles/input/device.c:hidp_add_connection() Could not parse HID SDP record: No such file or directory (2)

This guide does the entire pairing manually — the five things the plugin was supposed to do. Everything here persists across reboots.

The DualShock 3 does not implement standard Bluetooth pairing. You must connect it by USB once to write the host adapter's address into the controller ("the master"), then it connects wirelessly.


0. Prerequisites & info you need

# Host Bluetooth adapter MAC
bluetoothctl show | grep Controller          # e.g. 30:89:4A:11:7E:38

# Plug the DS3 in via USB. Confirm the kernel sees it:
lsusb | grep 054c:0268                        # Sony Corp. PlayStation 3 Controller
ls /dev/input/js0                             # wired joystick node should exist

Note your two MACs — referred to below as:

  • HOST = your laptop's BT adapter (e.g. 30:89:4A:11:7E:38)
  • CTRL = the controller's own MAC (read in step 1)

1. Read the controller MAC and write the host address into it

This is what the sixpair tool does, implemented here in pure Python over hidraw (no libusb, no compiling). Controller must be plugged in via USB. Adjust /dev/hidrawN if needed (grep -l 'PS3 Controller' /sys/class/hidraw/*/device/uevent).

# ds3pair.py  —  run: sudo python3 ds3pair.py        (read only)
#                    sudo python3 ds3pair.py set      (write host MAC as master)
import fcntl, sys

DEV  = "/dev/hidraw3"
HOST = "30:89:4A:11:7E:38"          # <-- your adapter MAC

def _IOC(d,t,nr,size): return (d<<30)|(size<<16)|(ord(t)<<8)|nr
def GFEATURE(size): return _IOC(3,'H',0x07,size)
def SFEATURE(size): return _IOC(3,'H',0x06,size)

f = open(DEV, "rb+", buffering=0)

buf = bytearray(17); buf[0]=0xf2                     # report 0xf2 = controller identity
fcntl.ioctl(f, GFEATURE(len(buf)), buf, True)
print("CONTROLLER_MAC=" + ":".join("%02X"%b for b in buf[4:10]))

buf2 = bytearray(8); buf2[0]=0xf5                    # report 0xf5 = current master
fcntl.ioctl(f, GFEATURE(len(buf2)), buf2, True)
print("CURRENT_MASTER=" + ":".join("%02X"%b for b in buf2[2:8]))

if len(sys.argv)>1 and sys.argv[1]=="set":
    m = [int(x,16) for x in HOST.split(":")]
    out = bytearray([0xf5,0x01]+m)
    fcntl.ioctl(f, SFEATURE(len(out)), out, True)
    print("MASTER_SET_TO=" + HOST)
sudo python3 ds3pair.py          # note CONTROLLER_MAC
sudo python3 ds3pair.py set      # writes your adapter as the master

2. Register the controller in BlueZ as a trusted HID device

HOST=30:89:4A:11:7E:38
CTRL=A0:5A:5F:33:8D:F0           # from step 1

sudo mkdir -p /var/lib/bluetooth/$HOST/$CTRL
sudo tee /var/lib/bluetooth/$HOST/$CTRL/info >/dev/null <<EOF
[General]
Name=PLAYSTATION(R)3 Controller
Class=0x000508
SupportedTechnologies=BR/EDR;
Trusted=true
Blocked=false
Services=00001124-0000-1000-8000-00805f9b34fb;

[DeviceID]
Source=1
Vendor=1356
Product=616
Version=257
EOF

3. Write the HID SDP record into the cache (the part everyone misses)

Without this you get Could not parse HID SDP record and the controller flaps. The DS3 doesn't serve its own SDP record over Bluetooth, so BlueZ must have it cached up front. This is the standard DS3 HID record:

SDP="3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800"

sudo mkdir -p /var/lib/bluetooth/$HOST/cache
printf '[General]\nName=PLAYSTATION(R)3 Controller\n\n[ServiceRecords]\n0x00010000=%s\n' "$SDP" \
  | sudo tee /var/lib/bluetooth/$HOST/cache/$CTRL >/dev/null

(SDP record courtesy of https://github.com/debiangamer/ps3controller.)


4. Allow unbonded HID connections

The DS3 is trusted but never bonds (no link key). Modern BlueZ refuses HID from unbonded devices by default. Flip one switch:

sudo sed -i 's/^#*\s*ClassicBondedOnly.*/ClassicBondedOnly=false/I' /etc/bluetooth/input.conf
# if the line doesn't exist, add ClassicBondedOnly=false under [General]

5. Disable L2CAP ERTM (kills the connect/disconnect flapping)

DualShock 3 controllers choke on L2CAP Enhanced Re-Transmission Mode. This is the single most common cause of "it connects then drops instantly":

# runtime
echo 1 | sudo tee /sys/module/bluetooth/parameters/disable_ertm
# permanent
echo "options bluetooth disable_ertm=1" | sudo tee /etc/modprobe.d/bluetooth-ds3.conf

6. Finish

sudo systemctl restart bluetooth

Then unplug the USB cable and press the PS (center) button. The light settles to one solid LED, /dev/input/js0 appears and stays. It reconnects on the PS button from now on.

ls /dev/input/js0
bluetoothctl info $CTRL | grep -E 'Name|Connected|Trusted'

Summary of why it failed and what fixed it

Symptom Cause Fix
Plugging USB does nothing BlueZ sixaxis plugin inert on this build do it all manually (steps 1–3)
Could not parse HID SDP record no SDP record cached step 3
Connects then instantly drops requires bonding / ERTM incompatible steps 4 & 5

Tested on Ubuntu-based distro, BlueZ 5.85, kernel 7.x, ThinkPad (Intel AX211), genuine Sony DS3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment