Skip to content

Commit 2d77474

Browse files
Roderick ColenbranderJiri Kosina
authored andcommitted
HID: playstation: add DualShock4 bluetooth support.
Add support for DualShock4 in Bluetooth mode. In Bluetooth, the device is a bit strange in that after 'calibration' it switches sending all its input data from a basic report (only containing buttons/sticks) to an extended report, which also contains touchpad, motion sensors and other data. The overall design of this code is similar to the DualSense code. Signed-off-by: Roderick Colenbrander <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent a23b063 commit 2d77474

File tree

1 file changed

+144
-26
lines changed

1 file changed

+144
-26
lines changed

drivers/hid/hid-playstation.c

Lines changed: 144 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,17 @@ struct dualsense_output_report {
287287

288288
#define DS4_INPUT_REPORT_USB 0x01
289289
#define DS4_INPUT_REPORT_USB_SIZE 64
290+
#define DS4_INPUT_REPORT_BT 0x11
291+
#define DS4_INPUT_REPORT_BT_SIZE 78
290292
#define DS4_OUTPUT_REPORT_USB 0x05
291293
#define DS4_OUTPUT_REPORT_USB_SIZE 32
294+
#define DS4_OUTPUT_REPORT_BT 0x11
295+
#define DS4_OUTPUT_REPORT_BT_SIZE 78
292296

293297
#define DS4_FEATURE_REPORT_CALIBRATION 0x02
294298
#define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37
299+
#define DS4_FEATURE_REPORT_CALIBRATION_BT 0x05
300+
#define DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE 41
295301
#define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3
296302
#define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49
297303
#define DS4_FEATURE_REPORT_PAIRING_INFO 0x12
@@ -310,6 +316,9 @@ struct dualsense_output_report {
310316
/* Battery status within batery_status field. */
311317
#define DS4_BATTERY_STATUS_FULL 11
312318

319+
#define DS4_OUTPUT_HWCTL_CRC32 0x40
320+
#define DS4_OUTPUT_HWCTL_HID 0x80
321+
313322
/* Flags for DualShock4 output report. */
314323
#define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01
315324
#define DS4_OUTPUT_VALID_FLAG0_LED 0x02
@@ -401,6 +410,17 @@ struct dualshock4_input_report_usb {
401410
} __packed;
402411
static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE);
403412

413+
struct dualshock4_input_report_bt {
414+
uint8_t report_id; /* 0x11 */
415+
uint8_t reserved[2];
416+
struct dualshock4_input_report_common common;
417+
uint8_t num_touch_reports;
418+
struct dualshock4_touch_report touch_reports[4]; /* BT has 4 compared to 3 for USB */
419+
uint8_t reserved2[2];
420+
__le32 crc32;
421+
} __packed;
422+
static_assert(sizeof(struct dualshock4_input_report_bt) == DS4_INPUT_REPORT_BT_SIZE);
423+
404424
/* Common data between Bluetooth and USB DualShock4 output reports. */
405425
struct dualshock4_output_report_common {
406426
uint8_t valid_flag0;
@@ -425,6 +445,16 @@ struct dualshock4_output_report_usb {
425445
} __packed;
426446
static_assert(sizeof(struct dualshock4_output_report_usb) == DS4_OUTPUT_REPORT_USB_SIZE);
427447

448+
struct dualshock4_output_report_bt {
449+
uint8_t report_id; /* 0x11 */
450+
uint8_t hw_control;
451+
uint8_t audio_control;
452+
struct dualshock4_output_report_common common;
453+
uint8_t reserved[61];
454+
__le32 crc32;
455+
} __packed;
456+
static_assert(sizeof(struct dualshock4_output_report_bt) == DS4_OUTPUT_REPORT_BT_SIZE);
457+
428458
/*
429459
* The DualShock4 has a main output report used to control most features. It is
430460
* largely the same between Bluetooth and USB except for different headers and CRC.
@@ -434,6 +464,8 @@ struct dualshock4_output_report {
434464
uint8_t *data; /* Start of data */
435465
uint8_t len; /* Size of output report */
436466

467+
/* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */
468+
struct dualshock4_output_report_bt *bt;
437469
/* Points to USB data payload in case for a USB report else NULL. */
438470
struct dualshock4_output_report_usb *usb;
439471
/* Points to common section of report, so past any headers. */
@@ -1646,26 +1678,49 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
16461678
int ret = 0;
16471679
uint8_t *buf;
16481680

1649-
buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL);
1650-
if (!buf)
1651-
return -ENOMEM;
1681+
if (ds4->base.hdev->bus == BUS_USB) {
1682+
buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL);
1683+
if (!buf)
1684+
return -ENOMEM;
16521685

1653-
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf,
1654-
DS4_FEATURE_REPORT_CALIBRATION_SIZE, true);
1655-
if (ret) {
1656-
hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
1657-
goto err_free;
1686+
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf,
1687+
DS4_FEATURE_REPORT_CALIBRATION_SIZE, true);
1688+
if (ret) {
1689+
hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
1690+
goto err_free;
1691+
}
1692+
} else { /* Bluetooth */
1693+
buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL);
1694+
if (!buf)
1695+
return -ENOMEM;
1696+
1697+
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf,
1698+
DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true);
1699+
if (ret) {
1700+
hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
1701+
goto err_free;
1702+
}
16581703
}
16591704

16601705
gyro_pitch_bias = get_unaligned_le16(&buf[1]);
16611706
gyro_yaw_bias = get_unaligned_le16(&buf[3]);
16621707
gyro_roll_bias = get_unaligned_le16(&buf[5]);
1663-
gyro_pitch_plus = get_unaligned_le16(&buf[7]);
1664-
gyro_pitch_minus = get_unaligned_le16(&buf[9]);
1665-
gyro_yaw_plus = get_unaligned_le16(&buf[11]);
1666-
gyro_yaw_minus = get_unaligned_le16(&buf[13]);
1667-
gyro_roll_plus = get_unaligned_le16(&buf[15]);
1668-
gyro_roll_minus = get_unaligned_le16(&buf[17]);
1708+
if (ds4->base.hdev->bus == BUS_USB) {
1709+
gyro_pitch_plus = get_unaligned_le16(&buf[7]);
1710+
gyro_pitch_minus = get_unaligned_le16(&buf[9]);
1711+
gyro_yaw_plus = get_unaligned_le16(&buf[11]);
1712+
gyro_yaw_minus = get_unaligned_le16(&buf[13]);
1713+
gyro_roll_plus = get_unaligned_le16(&buf[15]);
1714+
gyro_roll_minus = get_unaligned_le16(&buf[17]);
1715+
} else {
1716+
/* BT + Dongle */
1717+
gyro_pitch_plus = get_unaligned_le16(&buf[7]);
1718+
gyro_yaw_plus = get_unaligned_le16(&buf[9]);
1719+
gyro_roll_plus = get_unaligned_le16(&buf[11]);
1720+
gyro_pitch_minus = get_unaligned_le16(&buf[13]);
1721+
gyro_yaw_minus = get_unaligned_le16(&buf[15]);
1722+
gyro_roll_minus = get_unaligned_le16(&buf[17]);
1723+
}
16691724
gyro_speed_plus = get_unaligned_le16(&buf[19]);
16701725
gyro_speed_minus = get_unaligned_le16(&buf[21]);
16711726
acc_x_plus = get_unaligned_le16(&buf[23]);
@@ -1731,8 +1786,11 @@ static int dualshock4_get_firmware_info(struct dualshock4 *ds4)
17311786
if (!buf)
17321787
return -ENOMEM;
17331788

1789+
/* Note USB and BT support the same feature report, but this report
1790+
* lacks CRC support, so must be disabled in ps_get_report.
1791+
*/
17341792
ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf,
1735-
DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true);
1793+
DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, false);
17361794
if (ret) {
17371795
hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret);
17381796
goto err_free;
@@ -1748,21 +1806,38 @@ static int dualshock4_get_firmware_info(struct dualshock4 *ds4)
17481806

17491807
static int dualshock4_get_mac_address(struct dualshock4 *ds4)
17501808
{
1809+
struct hid_device *hdev = ds4->base.hdev;
17511810
uint8_t *buf;
17521811
int ret = 0;
17531812

1754-
buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL);
1755-
if (!buf)
1756-
return -ENOMEM;
1813+
if (hdev->bus == BUS_USB) {
1814+
buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL);
1815+
if (!buf)
1816+
return -ENOMEM;
1817+
1818+
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf,
1819+
DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, false);
1820+
if (ret) {
1821+
hid_err(hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret);
1822+
goto err_free;
1823+
}
17571824

1758-
ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf,
1759-
DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, true);
1760-
if (ret) {
1761-
hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret);
1762-
goto err_free;
1763-
}
1825+
memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address));
1826+
} else {
1827+
/* Rely on HIDP for Bluetooth */
1828+
if (strlen(hdev->uniq) != 17)
1829+
return -EINVAL;
1830+
1831+
ret = sscanf(hdev->uniq, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1832+
&ds4->base.mac_address[5], &ds4->base.mac_address[4],
1833+
&ds4->base.mac_address[3], &ds4->base.mac_address[2],
1834+
&ds4->base.mac_address[1], &ds4->base.mac_address[0]);
17641835

1765-
memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address));
1836+
if (ret != sizeof(ds4->base.mac_address))
1837+
return -EINVAL;
1838+
1839+
ret = 0;
1840+
}
17661841

17671842
err_free:
17681843
kfree(buf);
@@ -1859,14 +1934,26 @@ static void dualshock4_init_output_report(struct dualshock4 *ds4,
18591934
{
18601935
struct hid_device *hdev = ds4->base.hdev;
18611936

1862-
if (hdev->bus == BUS_USB) {
1937+
if (hdev->bus == BUS_BLUETOOTH) {
1938+
struct dualshock4_output_report_bt *bt = buf;
1939+
1940+
memset(bt, 0, sizeof(*bt));
1941+
bt->report_id = DS4_OUTPUT_REPORT_BT;
1942+
1943+
rp->data = buf;
1944+
rp->len = sizeof(*bt);
1945+
rp->bt = bt;
1946+
rp->usb = NULL;
1947+
rp->common = &bt->common;
1948+
} else { /* USB */
18631949
struct dualshock4_output_report_usb *usb = buf;
18641950

18651951
memset(usb, 0, sizeof(*usb));
18661952
usb->report_id = DS4_OUTPUT_REPORT_USB;
18671953

18681954
rp->data = buf;
18691955
rp->len = sizeof(*usb);
1956+
rp->bt = NULL;
18701957
rp->usb = usb;
18711958
rp->common = &usb->common;
18721959
}
@@ -1913,6 +2000,22 @@ static void dualshock4_output_worker(struct work_struct *work)
19132000

19142001
spin_unlock_irqrestore(&ds4->base.lock, flags);
19152002

2003+
/* Bluetooth packets need additional flags as well as a CRC in the last 4 bytes. */
2004+
if (report.bt) {
2005+
uint32_t crc;
2006+
uint8_t seed = PS_OUTPUT_CRC32_SEED;
2007+
2008+
/* Hardware control flags need to set to let the device know
2009+
* there is HID data as well as CRC.
2010+
*/
2011+
report.bt->hw_control = DS4_OUTPUT_HWCTL_HID | DS4_OUTPUT_HWCTL_CRC32;
2012+
2013+
crc = crc32_le(0xFFFFFFFF, &seed, 1);
2014+
crc = ~crc32_le(crc, report.data, report.len - 4);
2015+
2016+
report.bt->crc32 = cpu_to_le32(crc);
2017+
}
2018+
19162019
hid_hw_output_report(ds4->base.hdev, report.data, report.len);
19172020
}
19182021

@@ -1940,6 +2043,19 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
19402043
ds4_report = &usb->common;
19412044
num_touch_reports = usb->num_touch_reports;
19422045
touch_reports = usb->touch_reports;
2046+
} else if (hdev->bus == BUS_BLUETOOTH && report->id == DS4_INPUT_REPORT_BT &&
2047+
size == DS4_INPUT_REPORT_BT_SIZE) {
2048+
struct dualshock4_input_report_bt *bt = (struct dualshock4_input_report_bt *)data;
2049+
2050+
/* Last 4 bytes of input report contains CRC. */
2051+
if (!ps_check_crc32(PS_INPUT_CRC32_SEED, data, size - 4, bt->crc32)) {
2052+
hid_err(hdev, "DualShock4 input CRC's check failed\n");
2053+
return -EILSEQ;
2054+
}
2055+
2056+
ds4_report = &bt->common;
2057+
num_touch_reports = bt->num_touch_reports;
2058+
touch_reports = bt->touch_reports;
19432059
} else {
19442060
hid_err(hdev, "Unhandled reportID=%d\n", report->id);
19452061
return -1;
@@ -2354,7 +2470,9 @@ static void ps_remove(struct hid_device *hdev)
23542470

23552471
static const struct hid_device_id ps_devices[] = {
23562472
/* Sony DualShock 4 controllers for PS4 */
2473+
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
23572474
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
2475+
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
23582476
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
23592477
/* Sony DualSense controllers for PS5 */
23602478
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },

0 commit comments

Comments
 (0)