Skip to content

Latest commit

 

History

History
660 lines (510 loc) · 23.4 KB

File metadata and controls

660 lines (510 loc) · 23.4 KB

8BitDo Ultimate Controller HID Protocol

Reverse-engineered from the official Windows (8BitDoAdvance.dll) and Android (libadvance-lib.so) applications, and validated with USB traffic captures.

Device Identification

Vendor ID: 0x2DC8 (11720)

Controller PID PID (hex) Notes
Ultimate 2.4G 12306 0x3012 Via 2.4G USB adapter
Ultimate 2.4G RR 12307 0x3013 Recovery mode
Ultimate 2.4G Wired 12550 0x3106 Direct USB-C, Xbox mode
Ultimate BT 24583 0x6007 Bluetooth / config mode after switch
Ultimate PC 12305 0x3011 Wired USB
Pro2 24579 0x6003
Pro2 CY 24582 0x6006
Pro2 Wired 12304 0x3010
USB Ultimate 12544 0x3100 Adapter
USB Ultimate2 12549 0x3105 Adapter v2
SN30Plus 24578 0x6002
ArcadeStick 36890 0x901A
Xcloud 8448 0x2100
Xcloud2 8449 0x2101
IDLE 12553 0x3109 Adapter standby / HID config mode
Start 36900 0x9024
StartRR 36899 0x9023 Recovery

USB Connection Modes

When connected via USB-C, the Ultimate controller presents as an Xbox 360 controller (Vendor Specific Class, subclass 0x5D, protocol 0x01) with PID 0x3106. This is not HID class — the Linux xpad driver claims it.

The controller has two modes:

Xbox Gamepad Mode (PID 0x3106)

  • Interface class: 0xFF (Vendor Specific) / subclass 0x5D (Xbox)
  • Endpoints: EP 0x81 IN (32 bytes, interrupt), EP 0x02 OUT (32 bytes, interrupt)
  • Sends 20-byte Xbox 360 gamepad reports continuously
  • Claimed by xpad driver on Linux

HID Config Mode (PID 0x6007)

  • Interface class: 0x03 (HID)
  • Creates /dev/hidrawN via hid-generic driver
  • Accepts 64-byte HID reports for configuration read/write
  • Supports the full config protocol documented below

Mode Switch Protocol

To configure the controller, it must be switched from Xbox mode to HID config mode. This was captured from the official Windows application.

Prerequisites

  • On Linux, the xpad kernel driver must be detached from the device before sending the switch commands. The tool handles this automatically via detach_kernel_driver. The mode switch sequence may need multiple attempts (up to 5).

Switch Sequence

Send the following commands on EP 0x02 OUT (interrupt transfer) while in Xbox mode (PID 0x3106):

Step  Bytes                              Description
1     01 03 02                           Xbox LED init
2     02 08 03                           Xbox controller init
3     01 03 02                           Xbox LED init (repeat)
4     01 03 06                           Config mode trigger (LED cmd 0x06)
5     00 08 00 01 00 00 00 00            Rumble left motor
6     00 08 00 00 01 00 00 00            Rumble right motor
7     00 08 00 01 01 00 00 00            Rumble both motors
8     00 08 00 05 01 00 00 00            Rumble strong
9     00 08 00 00 00 00 00 00            Rumble off

Steps 5-9 (rumble sequence) appear to be required for the mode switch to complete. After the sequence, the device will:

  1. Disconnect from USB (~1 second)
  2. Re-enumerate as PID 0x6007 (Ultimate BT) with HID class
  3. Appear as /dev/hidrawN via hid-generic driver
  4. Be ready for config protocol commands

The re-enumeration takes approximately 1-3 seconds.

Xbox Command Format Reference

LED commands:     01 03 XX     (XX = LED pattern, 0x06 = config mode)
Rumble commands:  00 08 00 LL RR 00 00 00  (LL = left motor, RR = right motor)
Init command:     02 08 03

Returning to Gamepad Mode

The controller returns to Xbox mode (0x3106) when physically unplugged and replugged.

HID Config Protocol

All config communication uses 64-byte HID reports with report ID 0x81.

Initialization

Before reading or writing config, send a report state command:

Request:  81 11 04 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...
Response: 02 04 04 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ...

Reading Firmware Version

Request:  81 05 00 21 01 00 ...
Response: 02 22 VL VH ...

Version = uint16_le(VL, VH) / 100. E.g., 6D 00 = 109 = version 1.09.

Config Transfer Packet Format

Corrected from USB traffic capture analysis. Fields marked with (pcap-verified).

Request (Host -> Device), 64 bytes

Offset  Size   Field                  Notes
0       1      Report ID              Always 0x81
1       1      Payload length         Inner payload size (including 0x04 byte)
2       1      Transfer type          Always 0x04
3       2      Command (uint16 LE)    1=write, 2=read (pcap-verified)
5       2      Sub-param (uint16 LE)  Usually 0
7       2      Chunk length (uint16)  Bytes in this packet, max 45 (pcap-verified)
9       2      CRC-16                 CRC of payload data
11      4      Total size (uint32 LE) 0x674 = 1652 (pcap-verified)
15      4      Offset (uint32 LE)     Current transfer position (pcap-verified)
19      45     Data payload            Config data (write) or padding (read)

Response (Device -> Host), 64 bytes

Offset  Size   Field                  Notes
0       1      Status                 0x02 = success (pcap-verified)
1       1      Transfer type          0x04 (pcap-verified)
2       2      Command (uint16 LE)    4=read_response, 1=write_ack (pcap-verified)
4       2      Sub-param (uint16 LE)  Echoed from request (2 for read)
6       2      Chunk length (uint16)  Bytes of data in this response (pcap-verified)
8       2      CRC-16                 CRC of payload data
10      4      Total size (uint32 LE) 1652 (pcap-verified)
14      4      Offset (uint32 LE)     Matches request offset (pcap-verified)
18      45     Data payload            Config data (read response)

Simple Command Packet (no data payload)

Offset  Size   Field
0       1      Report ID (0x81)
1       1      Payload length (0x11 = 17)
2       1      Transfer type (0x04)
3       2      Command (uint16 LE)
5       2      Sub-command (uint16 LE)
7       57     Zero padding

Known simple commands:

  • Report state (enter config mode): cmd=7, sub=0
  • Save config: cmd=6, sub=0x15

Direct Command Packet (non-0x04 format)

Offset  Size   Field
0       1      Report ID (0x81)
1       1      Payload length
2       1      Command high byte
3       1      Command low byte
4+      N      Parameters

Known direct commands:

  • Version request: 81 05 00 21 01 00 ...

Read Protocol (pcap-verified)

  1. Send report state command (cmd=7, sub=0)
  2. Loop (45 bytes per chunk, 37 iterations for 1652 bytes): a. Build read request: cmd=2, sub=0, chunk_len=45, total=1652, offset=N b. Send on HID write c. Read 64-byte response d. Verify: status=0x02, type=0x04, cmd=4, sub=2 e. Copy 45 bytes from response offset 18 to config buffer f. Advance offset by chunk_len (45) g. Repeat until offset >= 1652

Write Protocol

  1. Send report state command (cmd=7, sub=0)
  2. Compute CRC-16 over config struct, store at offset 0x0C
  3. Loop (45 bytes per chunk): a. Build write packet: cmd=1, sub=0, chunk_len=45, total=1652, offset=N, data=chunk b. Send on HID write c. Read response, verify: status=0x02, type=0x04, cmd=1 d. Advance offset by response chunk_len e. Repeat until offset >= 1652
  4. Send save command: cmd=6, sub=0x15

CRC-16

Algorithm: CRC-16/KERMIT (reflected)

  • Polynomial: 0x8408 (bit-reversed 0x1021)
  • Initial value: 0xFFFF
  • No final XOR

Two CRC-16 uses:

  1. Config struct CRC: Computed over the entire 1652-byte config, stored at offset 0x0C (crc_value field). This uses the ANSI_CRC_16_sh function from the native library.
  2. Packet CRC: Computed over the data payload of each transfer packet, stored in the packet header at offset 9-10.
fn crc16_kermit(data: &[u8]) -> u16 {
    let mut crc: u16 = 0xFFFF;
    for &byte in data {
        crc ^= byte as u16;
        for _ in 0..8 {
            if crc & 1 != 0 {
                crc = (crc >> 1) ^ 0x8408;
            } else {
                crc >>= 1;
            }
        }
    }
    crc
}

Config Struct Layout

Total size: 1652 bytes (0x674), verified by USB capture.

All Ultimate variants (2.4G, BT, PC) share the same struct. 3 config slots.

Offsets (verified by Ghidra decompilation of native libraries)

Offset  Size    Type                          Field
0x000   12      uint32[3]                     flag
0x00C   4       uint32                        crc_value
0x010   2       uint16                        gamepad_mode (enum GAMEPAD_MODE)
0x012   2       uint16                        cur_slot (0-2)
0x014   96      config_file_name_t[3]         file_name (32 bytes each)
0x074   36      rumble_params_record_t[3]     rumble (12 bytes each)
0x098   24      joy_params_record_t[3]        joystick (8 bytes each)
0x0B0   24      trigger_params_record_t[3]    trigger (8 bytes each)
0x0C8   24      joy_trigger_special_feature_t[3] special_feature (8 bytes each)
0x0E0   252     mode_map_key_record_t_Pro2[3] map_key (84 bytes each)
0x1DC   1176    macro_fun_record_t_Pro2[3]    macro_fun (392 bytes each)
                                              Total: 1652 bytes

Sub-struct Definitions

config_file_name_t (32 bytes):
  name: byte[32]           -- UTF-8 profile name, null-padded

rumble_params_record_t (12 bytes):
  flag:     uint32          -- enable/disable flags
  zoom_out: float32[2]     -- [left_intensity, right_intensity] (0.0-1.0)

action_range_t (2 bytes):
  start_value: uint8
  end_value:   uint8

joy_params_record_t (8 bytes):
  flag:  uint32             -- enable/disable flags
  range: action_range_t[2]  -- [left_stick, right_stick]

trigger_params_record_t (8 bytes):
  flag:  uint32
  range: action_range_t[2]  -- [left_trigger, right_trigger]

joy_trigger_special_feature_t (8 bytes):
  flag:    uint32
  featrue: uint32           -- bitmask of special feature flags (see below)

map_key_t_Pro2 (80 bytes):
  key: uint32[20]           -- button mapping values (indexed by MAP_KEY_E)

mode_map_key_record_t_Pro2 (84 bytes):
  flag: uint32
  mode: map_key_t_Pro2      -- 20 key mappings (single set, shared across gamepad modes)

macro_key_record_t_Pro2 (96 bytes, including 1 byte padding):
  key_map:            uint32       -- trigger button bitmask
  interval_time_index: uint16[18]  -- timing between key presses
  key_index:          uint16[18]   -- key sequence
  digtal_joy:         uint8[18]    -- digital/analog indicators
  max_index:          uint8        -- number of steps in macro
  _padding:           uint8        -- alignment to 96 bytes

macro_fun_record_t_Pro2 (392 bytes):
  flag:      uint32                    -- enable/disable
  total_cnt: uint8                     -- number of active macros
  _padding:  uint8[3]                  -- alignment to 4 bytes
  record:    macro_key_record_t_Pro2[4] -- 4 macro slots

Enums

GAMEPAD_MODE (uint16):
  0 = SWITCH_PRO_M      (Nintendo Switch Pro Controller)
  1 = GAMEPAD_J_M       (DirectInput)
  2 = MAC_M             (macOS)
  3 = XINPUT_M          (XInput / Xbox)
  4 = EXTEND_M          (Extended)
  5 = GAMEPAD_NONE

MAP_KEY_E (button indices, 0-19):
  0=A, 1=B, 2=X, 3=Y, 4=L1, 5=R1, 6=L2, 7=R2,
  8=L3, 9=R3, 10=SELECT, 11=START, 12=STAR/SHARE,
  13=HOME, 14=UP, 15=DOWN, 16=LEFT, 17=RIGHT,
  18=P1 (back paddle), 19=P2 (back paddle)

Button Bitmask Values (for key mapping)

KEY_START_MAP      = 0x00000001
KEY_L3_MAP         = 0x00000002
KEY_R3_MAP         = 0x00000004
KEY_SELECT_MAP     = 0x00000008
KEY_X_MAP          = 0x00000010
KEY_Y_MAP          = 0x00000020
DIR_RIGHT_MAP      = 0x00000040
DIR_LEFT_MAP       = 0x00000080
DIR_DOWN_MAP       = 0x00000100
DIR_UP_MAP         = 0x00000200
KEY_L1_MAP         = 0x00000400
KEY_R1_MAP         = 0x00000800
KEY_B_MAP          = 0x00001000
KEY_A_MAP          = 0x00002000
KEY_L2_MAP         = 0x00004000
KEY_R2_MAP         = 0x00008000
KEY_MENU_MAP       = 0x00010000
KEY_HOME_MAP       = 0x00020000
KEY_BT_CON_MAP     = 0x00040000
KEY_SCREENSHOT_MAP = 0x00400000
KEY_TURBO_SINGLE   = 0x00800000
KEY_TURBO_AUTO     = 0x01000000
KEY_P1_MAP         = 0x02000000
KEY_P2_MAP         = 0x04000000
KEY_NULL_MAP       = 0x00000000

Special Feature Flags

The special_feature.featrue field is a bitmask (RE'd from ConfidentialData.cs):

0x0001 — Invert Left Stick X
0x0002 — Invert Left Stick Y
0x0004 — Invert Right Stick X
0x0008 — Invert Right Stick Y
0x0010 — Swap Left/Right Sticks
0x0080 — Swap Left/Right Triggers
0x1000 — Enable Deadzone
0x2000 — 4-Way Mode (no diagonals)

Mapping: Single Set Per Profile

The controller firmware stores one mapping array per profile slot (20 keys × 4 bytes = 80 bytes). This mapping is shared across all gamepad modes (XInput, Switch Pro, etc.).

The Windows app maintains two sets of mappings in local storage (mode[0] for XInput, mode[1] for Switch Pro) and swaps them when the user changes gamepad mode. This is a client-side feature — the controller itself only has one active mapping.

The Windows app's in-memory struct map_key_record_t defines map_key_t[2] mode (two 18-key arrays = 148 bytes), but the on-wire format is map_key_t_Pro2 (one 20-key array = 84 bytes including flag). The app translates between these formats during read/write. Verified: the 84-byte struct produces the correct 1652-byte config total.

P1/P2 (indices 18-19) can be remapped FROM but should not be remapped TO — the controller firmware does not support mapping other buttons to produce paddle output signals.

Special Bitmask Values

These are standalone function values used for the Star button's alternate mapping, NOT modifiers that can be OR'd with button bitmasks:

  • KEY_TURBO_SINGLE = 0x00800000 — turbo function (Star button alternate)
  • KEY_TURBO_AUTO = 0x01000000 — auto-turbo / swap function
  • KEY_SCREENSHOT_MAP = 0x00400000 — screenshot/capture function

Vibration Test Command

Captured from official Windows app USB traffic (pcap-verified).

Command Format

Uses the simple command format (0x04 wrapper) with cmd=0x08:

Request:  81 11 04 08 00 E8 03 LL RR 00 00 00 00 00 00 00 00 00 ...
Response: 02 04 08 00 ...
Byte Value Description
0 0x81 Report ID
1 0x11 Simple command payload length (17)
2 0x04 Transfer type
3-4 0x08 0x00 Command: vibration test (uint16 LE)
5-6 0xE8 0x03 Duration: 1000ms (uint16 LE)
7 LL Left motor intensity (0-255)
8 RR Right motor intensity (0-255)
9+ 0x00... Zero padding

The controller acknowledges with a standard response and activates the motors for the specified duration.

Probing Notes

Other vibration-related commands discovered during probing:

  • cmd=0x0B sub=0: reads current vibration parameters (4 bytes)
  • cmd=0x0C sub=0 + 4B: writes vibration parameters (acknowledged, no motor activation)
  • cmd=0x0C + 6B payload: causes controller disconnect — payload size sensitive
  • cmd=0x0B sub=1: causes controller disconnect
  • cmd=0x0D: controller reset/disconnect command

Parameter Packing (from Android app)

The Android app packs motor values into a single int:

int packed = (right << 8) | left | (l2_trigger << 16) | (r2_trigger << 24);
HIDChannel.setUltimateWiredVibration(packed);

Stick Calibration Commands

RE'd from Calibration.cs in the Windows app. Calibration uses the direct command format (non-0x04).

Start Calibration

The user must center both sticks before sending this command.

Request:  81 03 00 36 ...
Response: 02 37 01 ...
Field Value Description
Command high 0x00
Command low 0x36 Start calibration
Response[0] 0x02 Success status
Response[1] 0x37 Calibration response ID
Response[2] 0x01 1 = success, other = failure

Save Calibration

Two variants depending on the controller model:

Ultimate PC (PID 0x3011):

Request:  81 04 00 36 01 ...
Response: 02 37 01 ...

Other devices (Ultimate 2.4G, BT, etc.):

Request:  81 04 00 38 01 ...
Response: 02 39 ...
Field Value Description
Command high 0x00
Command low 0x36 (PC) / 0x38 (others) Save calibration
Payload[0] 0x01 Save flag
Response[1] 0x37 (PC) / 0x39 (others) Save response ID

Calibration Flow

1. User centers both sticks
2. Send start calibration (0x00, 0x36)
3. Wait for response [0x02, 0x37, 0x01]
4. Send save calibration (0x00, 0x38 or 0x36 for PC) with payload [0x01]
5. Wait for response [0x02, 0x39] or [0x02, 0x37, 0x01] for PC

Firmware Update

Firmware Server API

The 8BitDo Firmware Updater (Windows, v252) downloads firmware from http://dl.8bitdo.com:8080.

List firmware versions:

POST http://dl.8bitdo.com:8080/firmware/select
Content-Type: application/x-www-form-urlencoded
Body: type={device_type}&beta=1

Response (JSON):

{
  "msgState": 1,
  "list": [
    {
      "fileName": "Ultimate 2.4g",
      "filePathName": "/firmwareFile/upload/{uuid}.dat",
      "fileSize": 356380,
      "version": 2.04,
      "md5": "...",
      "type": 43,
      "readme_en": "Fixed the abnormal joysticks issue when holding down the L3 / R3 button.",
      "id": 1872,
      "date": "2024-02-21"
    }
  ]
}

Download firmware:

GET http://dl.8bitdo.com:8080{filePathName}

Device Type IDs

Type Controller
41 Ultimate BT
42 Ultimate BT Receiver
43 Ultimate 2.4G
44 Ultimate 2.4G Receiver
45 Ultimate Wired (PC)
48 Ultimate C 2.4G
50 Ultimate C Wired

See scripts/download_firmware.py for the complete list of all device types.

Firmware File Format (UFW)

The firmware files use a Jieli SoC encrypted format (UFW). The file has a 204-byte header followed by encrypted payload.

Offset  Size  Description
0x000   4     Header size (0xCC = 204)
0x004   4     Load address (e.g. 0x3000)
0x008   4     Firmware data size
0x00C   4     PID (e.g. 0x3012 = Ultimate 2.4G)
0x01C   4     CRC/checksum
0x020   4     VID string "2DC8"
0x024   12    Device identifier bytes
0x030   4     Version/build info
0x0CC   ...   Encrypted firmware payload

The payload is encrypted with Jieli's proprietary encryption and cannot be statically analyzed. Flashing requires the proprietary firmwareUpgradeTools.dll (Jieli SDK).

Update Flow

For Jieli-based devices (Ultimate 2.4G, Ultimate BT, etc.):

  1. Download firmware .dat file from server
  2. Send "jump to boot" HID command to controller
  3. Controller re-enumerates as USB mass storage device (removable drive)
  4. Jieli SDK decrypts UFW, erases flash, writes firmware via USB mass storage protocol
  5. Controller reboots with new firmware

Note: Firmware updates are not implemented in this tool due to the proprietary Jieli encryption. Use the official 8BitDo Firmware Updater for updates.

BLE Protocol

The Android app communicates with the controller exclusively via Bluetooth Low Energy (BLE), never USB. When the user "connects via USB-C" on Android, the USB cable provides power only — configuration still happens over BLE.

BLE Service & Characteristic UUIDs

Two UUID families are used depending on the controller model:

Family 1 — Pro2, Ultimate BT, Pro3, Micro:

Device BLE Name Service UUID Characteristic UUID
Pro 2 8ap 0000FF10-0000-1000-8000-00805F9B34FB 0000FF11-0000-1000-8000-00805F9B34FB
Pro 2 (boot) 8bt 0000FF20-0000-1000-8000-00805F9B34FB 0000FF21-0000-1000-8000-00805F9B34FB
Pro 2 CY - 0000FF10-0000-1000-8000-00805F9B34FB 0000FF13-0000-1000-8000-00805F9B34FB
Ultimate BT 80NA 0000FF10-0000-1000-8000-00805F9B34FB 0000FF11-0000-1000-8000-00805F9B34FB, 0000FF13-0000-1000-8000-00805F9B34FB
Pro 3 80GQ 0000FF10-0000-1000-8000-00805F9B34FB 0000FF11-0000-1000-8000-00805F9B34FB
Micro 80EL 0000FF10-0000-1000-8000-00805F9B34FB 0000FF11-0000-1000-8000-00805F9B34FB

Family 2 — Ultimate Wired, Xbox Wired, Three/Gamesir:

Device BLE Name Service UUID Characteristic UUID
Ultimate Wired 82CE 00010203-0405-0607-0809-0A0B0C0D1910 00010203-0405-0607-0809-0A0B0C0D2B11
Xbox Wired 80GG 00010203-0405-0607-0809-0A0B0C0D1910 00010203-0405-0607-0809-0A0B0C0D2B11
Three/Gamesir 81HB-X/D/W 00010203-0405-0607-0809-0A0B0C0D1910 00010203-0405-0607-0809-0A0B0C0D2B11

BLE Connection Flow

1. Scan for BLE devices by name filter
   Names: "8ap", "8bt", "80GG", "82CE", "80NA", "80NA_S", "80EL", "80GQ", "81HB-*"
   RSSI threshold: -80 dBm
   Scan timeout: 3000ms

2. Connect to GATT server (high priority)

3. Negotiate MTU
   - Xbox/Ultimate Wired/Three (80GG, 82CE, 81HB-*): MTU = 67
   - Others (Pro2, Ultimate BT, Pro3, Micro): MTU = 100

4. Discover services & characteristics
   Match against known service/characteristic UUID pairs

5. Subscribe to notifications
   Enable CCCD descriptor (UUID 00002902-0000-1000-8000-00805f9b34fb)

6. Ready for config read/write

BLE vs USB Packet Format (verified by testing)

The same config protocol commands are used over BLE, but the packet framing differs:

Request packets:

  • USB HID: [0x81, length, 0x04, cmd, ...] — report ID + length + transfer payload (64 bytes fixed)
  • BLE: [0x04, cmd, ...] — transfer payload only, no report ID, no length byte

BLE writes skip bytes 0 and 1 of the USB packet (report ID + length prefix), sending only the protocol payload starting at the transfer type byte (0x04). Packet length is determined by the payload content, not padded to 64 bytes.

Response packets:

  • USB HID: [status(02), type(04), cmd_lo, cmd_hi, sub_lo, sub_hi, chunk_lo, chunk_hi, crc, crc, total(4), offset(4), data...]
  • BLE: [type(04), 0x00, cmd_lo, cmd_hi, chunk_lo, chunk_hi, crc, crc, total(4), offset(4), data...]

BLE responses omit the status byte and have an extra 0x00 after the type byte. The cmd field echoes the request command (e.g. 0x02 for READ) rather than using CMD_READ_RESPONSE (0x04). The response is typically 61 bytes.

BLE-specific behavior:

  • CMD_REPORT_STATE (enter config mode) is not needed over BLE — go straight to read/write
  • BLE characteristic requires WriteType::WithResponse (WRITE property, not WRITE_WITHOUT_RESPONSE)
  • Config read/write/vibration all work over BLE with the adjusted packet format

BLE-Supported Controllers

Model BLE Name Scan Default
Pro 2 8ap/8apS Yes
Pro 3 80GQ Yes
Ultimate BT 80NA/80NA_S Yes
Ultimate Wired 82CE Yes
Xbox Wired 80GG Yes
Micro 80EL Yes
Three/Gamesir 81HB-X/D/W Yes
USB 2.4G Adapter - No (USB only)

Linux-Specific Notes

xpad Driver Conflict

On Linux, the xpad kernel driver automatically claims the controller in Xbox mode (PID 0x3106). xpad sends a "magic message" init sequence which fails (unable to receive magic message: -121), causing repeated USB resets.

The tool handles xpad automatically via per-device kernel driver detach (detach_kernel_driver). There is no need to run sudo rmmod xpad — only the specific device's driver attachment is released, leaving other controllers unaffected. The mode switch sequence may need multiple attempts (up to 5) before the device successfully re-enumerates as a HID device.

udev Rules

Install udev/99-8bitdo.rules for non-root hidraw access:

sudo cp udev/99-8bitdo.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules

This grants world read/write to 8BitDo USB and hidraw devices.