Skip to content

Commit 0c38b9f

Browse files
Set DS4 BT HID-present flag and align tests
DualShock 4 Bluetooth input reports now set the HID-present header bit (0x80) while keeping existing BT framing and CRC behavior. This enables HIDAPI/SDL consumers to accept live input after hotplug on Linux. Updated report and Linux consumer tests to assert the new behavior, and refreshed platform-support docs to describe the BT report contract and unchanged transport boundaries.
1 parent 91365cf commit 0c38b9f

5 files changed

Lines changed: 19 additions & 8 deletions

File tree

docs/platform-support.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,11 @@ provides it separately on the UHID event.
141141
The default DualShock 4 and DualSense profiles use Bluetooth framing, avoiding
142142
the parent-USB checks that can make virtual USB devices appear late in Steam.
143143
Explicit USB and Bluetooth factories remain available for consumers that
144-
require a particular transport. DualSense motion packing preserves the public
145-
meters-per-second-squared and degrees-per-second units while applying the same
144+
require a particular transport. DualShock 4 Bluetooth input reports set the
145+
HID-present header flag required by HIDAPI consumers and include the transport
146+
CRC, so a running consumer can accept live input after hotplug. DualSense motion
147+
packing preserves the public meters-per-second-squared and degrees-per-second
148+
units while applying the same
146149
raw sensor calibration used by Inputtino. Periodic PlayStation reports are
147150
repacked at 100 Hz so their sequence number and sensor timestamp continue to
148151
advance even when controller state is unchanged. Periodic and application
@@ -158,9 +161,8 @@ a controller before its kernel HID device has started.
158161

159162
On Linux, DualShock 4 and DualSense emit Sony's native `Wireless Controller`
160163
product name for Steam HID discovery. The requested USB or Bluetooth bus,
161-
descriptor, and report framing remain unchanged; in particular, the default
162-
DualShock 4 profile stays on its USB report contract. This transport-only name
163-
is confined to the Linux backend; public profile names, Windows names, and VHF
164+
descriptor, and report framing remain unchanged. This transport-only name is
165+
confined to the Linux backend; public profile names, Windows names, and VHF
164166
behavior are unchanged.
165167

166168
Switch Pro keeps its Nintendo identity on the Linux uinput path. This follows

src/core/report.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace lvh::reports {
3333

3434
constexpr auto dualshock4_bt_input_report_id = std::byte {0x11};
3535

36+
constexpr auto dualshock4_bt_input_hid_present = std::byte {0x80};
37+
3638
constexpr auto dualshock4_bt_output_report_id = std::byte {0x11};
3739

3840
constexpr auto dualshock4_output_hwctl_crc32 = std::byte {0x40};
@@ -646,6 +648,9 @@ namespace lvh::reports {
646648

647649
ByteReport report(profile.input_report_size, zero_byte);
648650
report[0] = is_bluetooth ? dualshock4_bt_input_report_id : to_byte(profile.report_id);
651+
if (is_bluetooth) {
652+
report[1] = dualshock4_bt_input_hid_present;
653+
}
649654

650655
report[payload_offset + 0U] = to_byte(normalize_u8_axis(normalized.left_stick.x));
651656
report[payload_offset + 1U] = to_byte(normalize_u8_axis(-normalized.left_stick.y));

tests/fixtures/linux_backend_test_hooks.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1868,7 +1868,11 @@ namespace lvh::detail::test {
18681868

18691869
if (read_uhid_event_type(descriptors[1], UHID_INPUT2, event)) {
18701870
const auto report_size = static_cast<std::size_t>(event.u.input2.size);
1871-
if (report_size == options.profile.input_report_size && event.u.input2.data[0] == 0x11) {
1871+
if (
1872+
report_size == options.profile.input_report_size &&
1873+
event.u.input2.data[0] == 0x11 &&
1874+
(event.u.input2.data[1] & 0x80U) != 0U
1875+
) {
18721876
const auto crc_offset = report_size - 4U;
18731877
const auto expected_crc = crc32(std::span<const std::uint8_t> {event.u.input2.data, crc_offset}, playstation_crc_seed(0xA1));
18741878
const auto actual_crc = read_u32_le(event.u.input2.data + crc_offset);

tests/unit/test_linux_consumers.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ TEST_F(LinuxConsumerTest, SdlSeesDualShock4UsbControllerBehavior) {
886886
});
887887
}
888888

889-
TEST_F(LinuxConsumerTest, SdlSeesDualShock4BluetoothControllerDiscovery) {
889+
TEST_F(LinuxConsumerTest, SdlSeesDualShock4BluetoothControllerBehavior) {
890890
ASSERT_TRUE(HasReadableWritableDeviceNode("/dev/uhid"));
891891

892892
run_sdl_playstation_controller_test({
@@ -895,7 +895,6 @@ TEST_F(LinuxConsumerTest, SdlSeesDualShock4BluetoothControllerDiscovery) {
895895
.stable_id = "02:00:00:00:00:04",
896896
.minimum_buttons = 10,
897897
.minimum_axes = 4,
898-
.expect_live_input = false,
899898
});
900899
}
901900

tests/unit/test_report.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ TEST(ReportTest, PacksDualShock4BluetoothReportWithCrc) {
350350

351351
ASSERT_EQ(report.size(), profile.input_report_size);
352352
EXPECT_EQ(report[0], 0x11);
353+
EXPECT_EQ(report[1], 0x80);
353354
EXPECT_EQ(report[3], 128);
354355
EXPECT_EQ(report[4], 128);
355356
EXPECT_EQ(report[9], 0x02);

0 commit comments

Comments
 (0)