UPSTREAM: HID: playstation: Add DualSense Bluetooth support

This patch adds support for the DualSense when operating in Bluetooth mode.
The device has the same behavior as the DualShock 4 in that by default it
sends a limited input report (0x1), but after requesting calibration data,
it switches to an extended input report (report 49), which adds data for
touchpad, motion sensors, battery and more.

Bug: 167947264
CRs-fixed: 2971837
Change-Id: I6ba3eb018f9938b71bb63cb70b26e4acdcfb788a
Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Reviewed-by: Barnabás Pőcze <pobrn@protonmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Farid Chahla <farid.chahla@sony.com>
Signed-off-by: Siarhei Vishniakou <svv@google.com>
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
Git-commit: 799b2b533a299ba5b64ddd22639836c2a5eaee31
Signed-off-by: Kishor Krishna Bhat <kishkris@codeaurora.org>
This commit is contained in:
Roderick Colenbrander
2021-07-13 16:53:15 +05:30
committed by Kishor Krishna Bhat
parent 1be8411693
commit 7dee1966e6
2 changed files with 42 additions and 0 deletions

View File

@@ -800,6 +800,7 @@ config HID_PLANTRONICS
config HID_PLAYSTATION config HID_PLAYSTATION
tristate "PlayStation HID Driver" tristate "PlayStation HID Driver"
depends on HID depends on HID
select CRC32
---help--- ---help---
Provides support for Sony PS5 controllers including support for Provides support for Sony PS5 controllers including support for
its special functionalities e.g. touchpad, lights and motion its special functionalities e.g. touchpad, lights and motion

View File

@@ -5,6 +5,7 @@
* Copyright (c) 2020 Sony Interactive Entertainment * Copyright (c) 2020 Sony Interactive Entertainment
*/ */
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/crc32.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
@@ -23,8 +24,14 @@ struct ps_device {
int (*parse_report)(struct ps_device *dev, struct hid_report *report, u8 *data, int size); int (*parse_report)(struct ps_device *dev, struct hid_report *report, u8 *data, int size);
}; };
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
#define PS_INPUT_CRC32_SEED 0xA1
#define PS_FEATURE_CRC32_SEED 0xA3
#define DS_INPUT_REPORT_USB 0x01 #define DS_INPUT_REPORT_USB 0x01
#define DS_INPUT_REPORT_USB_SIZE 64 #define DS_INPUT_REPORT_USB_SIZE 64
#define DS_INPUT_REPORT_BT 0x31
#define DS_INPUT_REPORT_BT_SIZE 78
#define DS_FEATURE_REPORT_PAIRING_INFO 0x09 #define DS_FEATURE_REPORT_PAIRING_INFO 0x09
#define DS_FEATURE_REPORT_PAIRING_INFO_SIZE 20 #define DS_FEATURE_REPORT_PAIRING_INFO_SIZE 20
@@ -135,6 +142,17 @@ static struct input_dev *ps_allocate_input_dev(struct hid_device *hdev, const ch
return input_dev; return input_dev;
} }
/* Compute crc32 of HID data and compare against expected CRC. */
static bool ps_check_crc32(uint8_t seed, uint8_t *data, size_t len, uint32_t report_crc)
{
uint32_t crc;
crc = crc32_le(0xFFFFFFFF, &seed, 1);
crc = ~crc32_le(crc, data, len);
return crc == report_crc;
}
static struct input_dev *ps_gamepad_create(struct hid_device *hdev) static struct input_dev *ps_gamepad_create(struct hid_device *hdev)
{ {
struct input_dev *gamepad; struct input_dev *gamepad;
@@ -186,6 +204,17 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
return -EINVAL; return -EINVAL;
} }
if (hdev->bus == BUS_BLUETOOTH) {
/* Last 4 bytes contains crc32. */
uint8_t crc_offset = size - 4;
uint32_t report_crc = get_unaligned_le32(&buf[crc_offset]);
if (!ps_check_crc32(PS_FEATURE_CRC32_SEED, buf, crc_offset, report_crc)) {
hid_err(hdev, "CRC check failed for reportID=%d\n", report_id);
return -EILSEQ;
}
}
return 0; return 0;
} }
@@ -228,6 +257,17 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r
if (hdev->bus == BUS_USB && report->id == DS_INPUT_REPORT_USB && if (hdev->bus == BUS_USB && report->id == DS_INPUT_REPORT_USB &&
size == DS_INPUT_REPORT_USB_SIZE) { size == DS_INPUT_REPORT_USB_SIZE) {
ds_report = (struct dualsense_input_report *)&data[1]; ds_report = (struct dualsense_input_report *)&data[1];
} else if (hdev->bus == BUS_BLUETOOTH && report->id == DS_INPUT_REPORT_BT &&
size == DS_INPUT_REPORT_BT_SIZE) {
/* Last 4 bytes of input report contain crc32 */
uint32_t report_crc = get_unaligned_le32(&data[size - 4]);
if (!ps_check_crc32(PS_INPUT_CRC32_SEED, data, size - 4, report_crc)) {
hid_err(hdev, "DualSense input CRC's check failed\n");
return -EILSEQ;
}
ds_report = (struct dualsense_input_report *)&data[2];
} else { } else {
hid_err(hdev, "Unhandled reportID=%d\n", report->id); hid_err(hdev, "Unhandled reportID=%d\n", report->id);
return -1; return -1;
@@ -361,6 +401,7 @@ static void ps_remove(struct hid_device *hdev)
} }
static const struct hid_device_id ps_devices[] = { static const struct hid_device_id ps_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
{ } { }
}; };