8bitdo HIDAPI driver breaks Pro 2 controllers
If an application using SDL (e.g. current Steam, testcontroller in current SDL git) connects to an 8bitdo Pro 2 controller in Android / DirectInput mode, the controller doesn't report any input events until it is powered off and back on again.
Wiggling a thumbstick after every step while single-stepping through SDL shows that the controller stops generating events after ReadFeatureReport(device->dev, SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID, data, sizeof(data)) calls the HIDIOCGFEATURE ioctl.
What firmware version does your controller have?
3.06, assuming the firmware updater limping along in Wine is actually doing the job.
Also I should add that it's broken in Bluetooth mode, it seems to work flawlessly in Steam and testcontroller when wired.
Hmm, I wonder if you got a bad firmware update? I updated mine to 3.06 and tested on Windows and Linux and it works fine over Bluetooth with both of them. Can you try updating the firmware again on a Windows PC?
Updated the firmware on Windows, no change in behavior.
This is with:
- kernel 6.17.8-300.fc43.x86_64 and now 6.17.9-300.fc43.x86_64
- bluez-5.85-1.fc43.x86_64 (with both UserspaceHID=true and UserspaceHID=false)
- the 60-steam-input.rules from the upstream steam-devices git commit 4d7e6c1
Now that I think about it, I guess it makes sense that the controller stops producing normal output when the SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID feature report is retrieved -- I assume this is telling the controller to use a SDL-specific input report that the Linux HID stack can't understand, and that's why it vanishes from e.g. the KDE Game Controller System Settings applet.
The question is then why do I get input over USB but not over Bluetooth when this mode is enabled?
Looking at the Wireshark trace, the controller continuously sends Protocol Code 3 input reports until SDL sends the ENABLE_SDL_REPORTID feature request, and then it continuously sends Protocol Code 4 input reports (which I assume is SDL_8BITDO_REPORTID_SDL_REPORTID), so I assume these packets are getting discarded somewhere in the Bluetooth stack before they get sent to the hidraw device?
Oh, I see. I suspect what's happening is that the Bluetooth HID report descriptor defines the 0x03 report but not the 0x04 report, while the USB descriptor defines them both.
@8BitDo, can you confirm?
Decoding the report_descriptor in sysfs when connected via Bluetooth gives:
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x95, 0x01, // Report Count (1)
0x75, 0x04, // Report Size (4)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x95, 0x04, // Report Count (4)
0x75, 0x08, // Report Size (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x02, // Usage Page (Sim Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0xC4, // Usage (Accelerator)
0x09, 0xC5, // Usage (Brake)
0x95, 0x02, // Report Count (2)
0x75, 0x08, // Report Size (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x06, // Usage Page (Generic Dev Ctrls)
0x09, 0x20, // Usage (Battery Strength)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0F, // Usage Page (PID Page)
0x09, 0x70, // Usage (0x70)
0x85, 0x05, // Report ID (5)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0x09, 0x02, // Usage (0x02)
0x07, 0x35, 0x08, 0x35, 0x06, // Usage Page (0x06350835)
0x09, 0x04, // Usage (0x04)
0x00, // Unknown (bTag: 0x00, bType: 0x00)
i.e. only reports 3 and 5
But when connected via USB:
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x95, 0x01, // Report Count (1)
0x75, 0x04, // Report Size (4)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x95, 0x04, // Report Count (4)
0x75, 0x08, // Report Size (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x02, // Usage Page (Sim Ctrls)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0xC4, // Usage (Accelerator)
0x09, 0xC5, // Usage (Brake)
0x95, 0x02, // Report Count (2)
0x75, 0x08, // Report Size (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x85, 0x04, // Report ID (4)
0x09, 0x23, // Usage (0x23)
0x75, 0x08, // Report Size (8)
0x95, 0x1F, // Report Count (31)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x85, 0x06, // Report ID (6)
0x09, 0x23, // Usage (0x23)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x05, 0x06, // Usage Page (Generic Dev Ctrls)
0x09, 0x20, // Usage (Battery Strength)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0F, // Usage Page (PID Page)
0x09, 0x70, // Usage (0x70)
0x85, 0x05, // Report ID (5)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x02, // Report ID (2)
0x09, 0x02, // Usage (0x02)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x81, // Report ID (-127)
0x09, 0x03, // Usage (0x03)
0x75, 0x08, // Report Size (8)
0x95, 0x3F, // Report Count (63)
0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0xC0, // End Collection
i.e. reports 3, 4, 6, 5, 2, and -127
(Another oddity discovered while fiddling with the controller for this bug report: now when it is plugged in to USB, it generates type 4 reports by default instead of type 3 i.e. it doesn't work with the normal Linux input stack at all, only with SDL reading hidraw.)
And the tail end of the Bluetooth descriptor really looks like random garbage.
At this point this is clearly not an SDL issue, so I'll go ahead and mark it appropriately.
If you'd like to avoid SDL changing the report mode, you can set the environment variable SDL_JOYSTICK_HIDAPI=0
Out of curiosity, could you please attach the report_descriptor for your Pro 2 when it's connected via Bluetooth?
Out of curiosity, could you please attach the report_descriptor for your Pro 2 when it's connected via Bluetooth?
Sure, how do you dump the descriptor?
Once you've connected it, you should be able to run something similar to hexdump -X /sys/bus/hid/devices/0005:2DC8:6006.0039/report_descriptor and paste it as a comment. (The 0039 part of 0005:2DC8:6006.0039 will vary based on how many Bluetooth devices you've attached since boot.)
I'm curious if your Pro 2 also has a malformed HID report descriptor and works because your kernel/bluez/etc. is different from mine, or if my Pro 2 is has a uniquely corrupted firmware that somehow survives re-flashing.
Here you go:
0000000 0105 0509 01a1 0385 0105 0015 0725 3b46
0000010 9501 7501 6504 0914 8139 7542 9501 8104
0000020 1501 2600 00ff 3009 3109 3209 3509 0495
0000030 0875 0281 0205 0015 ff26 0900 09c4 95c5
0000040 7502 8108 0502 1909 2901 1510 2500 7501
0000050 9501 8110 0602 ff00 0485 2309 0875 1f95
0000060 0381 0006 85ff 0906 9523 b13f 0502 0906
0000070 1520 2500 7564 9508 8101 0502 090f 8570
0000080 1505 2500 7564 9508 9104 c002 0209 3507
0000090 3508 0906 0004
and this decodes to:
# device 0:0
# 0x05, 0x01, // Usage Page (Generic Desktop) 0
# 0x09, 0x05, // Usage (Game Pad) 2
# 0xa1, 0x01, // Collection (Application) 4
# 0x85, 0x03, // Report ID (3) 6
# 0x05, 0x01, // Usage Page (Generic Desktop) 8
# 0x15, 0x00, // Logical Minimum (0) 10
# 0x25, 0x07, // Logical Maximum (7) 12
# 0x46, 0x3b, 0x01, // Physical Maximum (315) 14
# 0x95, 0x01, // Report Count (1) 17
# 0x75, 0x04, // Report Size (4) 19
# 0x65, 0x14, // Unit (EnglishRotation: deg) 21
# 0x09, 0x39, // Usage (Hat switch) 23
# 0x81, 0x42, // Input (Data,Var,Abs,Null) 25
# 0x75, 0x01, // Report Size (1) 27
# 0x95, 0x04, // Report Count (4) 29
# 0x81, 0x01, // Input (Cnst,Arr,Abs) 31
# 0x15, 0x00, // Logical Minimum (0) 33
# 0x26, 0xff, 0x00, // Logical Maximum (255) 35
# 0x09, 0x30, // Usage (X) 38
# 0x09, 0x31, // Usage (Y) 40
# 0x09, 0x32, // Usage (Z) 42
# 0x09, 0x35, // Usage (Rz) 44
# 0x95, 0x04, // Report Count (4) 46
# 0x75, 0x08, // Report Size (8) 48
# 0x81, 0x02, // Input (Data,Var,Abs) 50
# 0x05, 0x02, // Usage Page (Simulation Controls) 52
# 0x15, 0x00, // Logical Minimum (0) 54
# 0x26, 0xff, 0x00, // Logical Maximum (255) 56
# 0x09, 0xc4, // Usage (Accelerator) 59
# 0x09, 0xc5, // Usage (Brake) 61
# 0x95, 0x02, // Report Count (2) 63
# 0x75, 0x08, // Report Size (8) 65
# 0x81, 0x02, // Input (Data,Var,Abs) 67
# 0x05, 0x09, // Usage Page (Button) 69
# 0x19, 0x01, // Usage Minimum (1) 71
# 0x29, 0x10, // Usage Maximum (16) 73
# 0x15, 0x00, // Logical Minimum (0) 75
# 0x25, 0x01, // Logical Maximum (1) 77
# 0x75, 0x01, // Report Size (1) 79
# 0x95, 0x10, // Report Count (16) 81
# 0x81, 0x02, // Input (Data,Var,Abs) 83
# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 85
# 0x85, 0x04, // Report ID (4) 88
# 0x09, 0x23, // Usage (Vendor Usage 0x23) 90
# 0x75, 0x08, // Report Size (8) 92
# 0x95, 0x1f, // Report Count (31) 94
# 0x81, 0x03, // Input (Cnst,Var,Abs) 96
# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 98
# 0x85, 0x06, // Report ID (6) 101
# 0x09, 0x23, // Usage (Vendor Usage 0x23) 103
# 0x95, 0x3f, // Report Count (63) 105
# 0xb1, 0x02, // Feature (Data,Var,Abs) 107
# 0x05, 0x06, // Usage Page (Generic Device Controls) 109
# 0x09, 0x20, // Usage (Battery Strength) 111
# 0x15, 0x00, // Logical Minimum (0) 113
# 0x25, 0x64, // Logical Maximum (100) 115
# 0x75, 0x08, // Report Size (8) 117
# 0x95, 0x01, // Report Count (1) 119
# 0x81, 0x02, // Input (Data,Var,Abs) 121
# 0x05, 0x0f, // Usage Page (Vendor Usage Page 0x0f) 123
# 0x09, 0x70, // Usage (Vendor Usage 0x70) 125
# 0x85, 0x05, // Report ID (5) 127
# 0x15, 0x00, // Logical Minimum (0) 129
# 0x25, 0x64, // Logical Maximum (100) 131
# 0x75, 0x08, // Report Size (8) 133
# 0x95, 0x04, // Report Count (4) 135
# 0x91, 0x02, // Output (Data,Var,Abs) 137
# 0xc0, // End Collection 139
# 0x09, 0x02, // Usage (Vendor Usage 0x02) 140
# 0x07, 0x35, 0x08, 0x35, 0x06, // Usage Page (Vendor Usage Page 0x6350835) 142
# 0x09, 0x04, // Usage (Vendor Usage 0x04) 147
#
@8BitDo, one thing we do in the Steam Controller that might work here, is there's a regular heartbeat from SDL that keeps the controller sending enhanced reports. If the controller firmware stops receiving this heartbeat, it will stop sending enhanced reports and start sending normal HID reports again.
For the steam controller we send the heartbeat every 3 seconds and I think the firmware may time out in 10 seconds.
Oh, yeah, my controller is definitely broken somehow. Thanks for your assistance.
You're welcome!
@8BitDo, the issue that the Linux kernel device doesn't get any input once SDL enables the new reports is still active. I'll leave this open for your perusal, and the heartbeat approach I outlined above might be a good solution for this.