--- v3
+++ v2
@@ -1,330 +1,103 @@
From: Roderick Colenbrander <roderick.colenbrander@sony.com>
-The DualSense features a haptics system based on voicecoil motors,
-which requires PCM data (or special HID packets using Bluetooth). There
-is no appropriate API yet in the Linux kernel to expose these. The
-controller also provides a classic rumble feature for backwards
-compatibility. Expose this classic rumble feature using the FF framework.
+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.
Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
---
- drivers/hid/Kconfig | 8 ++
- drivers/hid/hid-playstation.c | 208 +++++++++++++++++++++++++++++++++-
- 2 files changed, 214 insertions(+), 2 deletions(-)
+ drivers/hid/hid-playstation.c | 35 +++++++++++++++++++++++++++++++++++
+ drivers/hid/hid-quirks.c | 1 +
+ 2 files changed, 36 insertions(+)
-diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index b3ec01c7a0b7..54b4eee222f9 100644
---- a/drivers/hid/Kconfig
-+++ b/drivers/hid/Kconfig
-@@ -863,6 +863,14 @@ config HID_PLAYSTATION
- its special functionalities e.g. touchpad, lights and motion
- sensors.
-
-+config PLAYSTATION_FF
-+ bool "PlayStation force feedback support"
-+ depends on HID_PLAYSTATION
-+ select INPUT_FF_MEMLESS
-+ help
-+ Say Y here if you would like to enable force feedback support for
-+ PlayStation game controllers.
-+
- config HID_PRIMAX
- tristate "Primax non-fully HID-compliant devices"
- depends on HID
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
-index bcf93836beb2..49205bb323a7 100644
+index 91f3ed005fce..552a52a50b78 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
-@@ -48,12 +48,17 @@ struct ps_calibration_data {
-
- /* Seed values for DualShock4 / DualSense CRC32 for different report types. */
- #define PS_INPUT_CRC32_SEED 0xA1
-+#define PS_OUTPUT_CRC32_SEED 0xA2
- #define PS_FEATURE_CRC32_SEED 0xA3
+@@ -47,6 +47,7 @@ struct ps_calibration_data {
+ };
#define DS_INPUT_REPORT_USB 0x01
- #define DS_INPUT_REPORT_USB_SIZE 64
- #define DS_INPUT_REPORT_BT 0x31
- #define DS_INPUT_REPORT_BT_SIZE 78
-+#define DS_OUTPUT_REPORT_USB 0x02
-+#define DS_OUTPUT_REPORT_USB_SIZE 63
-+#define DS_OUTPUT_REPORT_BT 0x31
-+#define DS_OUTPUT_REPORT_BT_SIZE 78
++#define DS_INPUT_REPORT_BT 0x31
- #define DS_FEATURE_REPORT_CALIBRATION 0x05
+ #define DS_FEATURE_REPORT_CALIBRATION 5
#define DS_FEATURE_REPORT_CALIBRATION_SIZE 41
-@@ -89,6 +94,12 @@ struct ps_calibration_data {
- */
- #define DS_TOUCH_POINT_INACTIVE BIT(7)
-
-+ /* Magic value required in tag field of Bluetooth output report. */
-+#define DS_OUTPUT_TAG 0x10
-+/* Flags for DualSense output report. */
-+#define DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION BIT(0)
-+#define DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT BIT(1)
-+
- /* DualSense hardware limits */
- #define DS_ACC_RES_PER_G 8192
- #define DS_ACC_RANGE (4*DS_ACC_RES_PER_G)
-@@ -111,6 +122,15 @@ struct dualsense {
- bool sensor_timestamp_initialized;
- uint32_t prev_sensor_timestamp;
- uint32_t sensor_timestamp_us;
-+
-+ /* Compatible rumble state */
-+ bool update_rumble;
-+ uint8_t motor_left;
-+ uint8_t motor_right;
-+
-+ struct work_struct output_worker;
-+ void *output_report_dmabuf;
-+ uint8_t output_seq; /* Sequence number for output report. */
- };
-
- struct dualsense_touch_point {
-@@ -146,6 +166,68 @@ struct dualsense_input_report {
- /* Common input report size shared equals the size of the USB report minus 1 byte for ReportID. */
- static_assert(sizeof(struct dualsense_input_report) == DS_INPUT_REPORT_USB_SIZE - 1);
-
-+/* Common data between DualSense BT/USB main output report. */
-+struct dualsense_output_report_common {
-+ uint8_t valid_flag0;
-+ uint8_t valid_flag1;
-+
-+ /* For DualShock 4 compatibility mode. */
-+ uint8_t motor_right;
-+ uint8_t motor_left;
-+
-+ /* Audio controls */
-+ uint8_t reserved[4];
-+ uint8_t mute_button_led;
-+
-+ uint8_t power_save_control;
-+ uint8_t reserved2[28];
-+
-+ /* LEDs and lightbar */
-+ uint8_t valid_flag2;
-+ uint8_t reserved3[2];
-+ uint8_t lightbar_setup;
-+ uint8_t led_brightness;
-+ uint8_t player_leds;
-+ uint8_t lightbar_red;
-+ uint8_t lightbar_green;
-+ uint8_t lightbar_blue;
-+} __packed;
-+static_assert(sizeof(struct dualsense_output_report_common) == 47);
-+
-+struct dualsense_output_report_bt {
-+ uint8_t report_id; /* 0x31 */
-+ uint8_t seq_tag;
-+ uint8_t tag;
-+ struct dualsense_output_report_common common;
-+ uint8_t reserved[24];
-+ __le32 crc32;
-+} __packed;
-+static_assert(sizeof(struct dualsense_output_report_bt) == DS_OUTPUT_REPORT_BT_SIZE);
-+
-+struct dualsense_output_report_usb {
-+ uint8_t report_id; /* 0x02 */
-+ struct dualsense_output_report_common common;
-+ uint8_t reserved[15];
-+} __packed;
-+static_assert(sizeof(struct dualsense_output_report_usb) == DS_OUTPUT_REPORT_USB_SIZE);
-+
-+/*
-+ * The DualSense has a main output report used to control most features. It is
-+ * largely the same between Bluetooth and USB except for different headers and CRC.
-+ * This structure hide the differences between the two to simplify sending output reports.
-+ */
-+struct dualsense_output_report {
-+ uint8_t *data; /* Start of data */
-+ uint8_t len; /* Size of output report */
-+
-+ /* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */
-+ struct dualsense_output_report_bt *bt;
-+ /* Points to USB data payload in case for a USB report else NULL. */
-+ struct dualsense_output_report_usb *usb;
-+ /* Points to common section of report, so past any headers. */
-+ struct dualsense_output_report_common *common;
-+};
-+
- /*
- * Common gamepad buttons across DualShock 3 / 4 and DualSense.
- * Note: for device with a touchpad, touchpad button is not included
-@@ -310,7 +392,8 @@ static bool ps_check_crc32(uint8_t seed, uint8_t *data, size_t len, uint32_t rep
- 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,
-+ int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
- {
- struct input_dev *gamepad;
- unsigned int i;
-@@ -333,6 +416,13 @@ static struct input_dev *ps_gamepad_create(struct hid_device *hdev)
- for (i = 0; i < ARRAY_SIZE(ps_gamepad_buttons); i++)
- input_set_capability(gamepad, EV_KEY, ps_gamepad_buttons[i]);
-
-+#if IS_ENABLED(CONFIG_PLAYSTATION_FF)
-+ if (play_effect) {
-+ input_set_capability(gamepad, EV_FF, FF_RUMBLE);
-+ input_ff_create_memless(gamepad, NULL, play_effect);
-+ }
-+#endif
-+
- ret = input_register_device(gamepad);
- if (ret)
- return ERR_PTR(ret);
-@@ -552,6 +642,94 @@ static int dualsense_get_mac_address(struct dualsense *ds)
- return ret;
- }
-
-+static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp,
-+ void *buf)
-+{
-+ struct hid_device *hdev = ds->base.hdev;
-+
-+ if (hdev->bus == BUS_BLUETOOTH) {
-+ struct dualsense_output_report_bt *bt = buf;
-+
-+ memset(bt, 0, sizeof(*bt));
-+ bt->report_id = DS_OUTPUT_REPORT_BT;
-+ bt->tag = DS_OUTPUT_TAG; /* Tag must be set. Exact meaning is unclear. */
-+
-+ /*
-+ * Highest 4-bit is a sequence number, which needs to be increased
-+ * every report. Lowest 4-bit is tag and can be zero for now.
-+ */
-+ bt->seq_tag = (ds->output_seq << 4) | 0x0;
-+ if (++ds->output_seq == 16)
-+ ds->output_seq = 0;
-+
-+ rp->data = buf;
-+ rp->len = sizeof(*bt);
-+ rp->bt = bt;
-+ rp->usb = NULL;
-+ rp->common = &bt->common;
-+ } else { /* USB */
-+ struct dualsense_output_report_usb *usb = buf;
-+
-+ memset(usb, 0, sizeof(*usb));
-+ usb->report_id = DS_OUTPUT_REPORT_USB;
-+
-+ rp->data = buf;
-+ rp->len = sizeof(*usb);
-+ rp->bt = NULL;
-+ rp->usb = usb;
-+ rp->common = &usb->common;
-+ }
-+}
-+
-+/*
-+ * Helper function to send DualSense output reports. Applies a CRC at the end of a report
-+ * for Bluetooth reports.
-+ */
-+static void dualsense_send_output_report(struct dualsense *ds,
-+ struct dualsense_output_report *report)
-+{
-+ struct hid_device *hdev = ds->base.hdev;
-+
-+ /* Bluetooth packets need to be signed with a CRC in the last 4 bytes. */
-+ if (report->bt) {
-+ uint32_t crc;
-+ uint8_t seed = PS_OUTPUT_CRC32_SEED;
-+
-+ crc = crc32_le(0xFFFFFFFF, &seed, 1);
-+ crc = ~crc32_le(crc, report->data, report->len - 4);
-+
-+ report->bt->crc32 = cpu_to_le32(crc);
-+ }
-+
-+ hid_hw_output_report(hdev, report->data, report->len);
-+}
-+
-+static void dualsense_output_worker(struct work_struct *work)
-+{
-+ struct dualsense *ds = container_of(work, struct dualsense, output_worker);
-+ struct dualsense_output_report report;
-+ struct dualsense_output_report_common *common;
-+ unsigned long flags;
-+
-+ dualsense_init_output_report(ds, &report, ds->output_report_dmabuf);
-+ common = report.common;
-+
-+ spin_lock_irqsave(&ds->base.lock, flags);
-+
-+ if (ds->update_rumble) {
-+ /* Select classic rumble style haptics and enable it. */
-+ common->valid_flag0 |= DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT;
-+ common->valid_flag0 |= DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION;
-+ common->motor_left = ds->motor_left;
-+ common->motor_right = ds->motor_right;
-+ ds->update_rumble = false;
-+ }
-+
-+ spin_unlock_irqrestore(&ds->base.lock, flags);
-+
-+ dualsense_send_output_report(ds, &report);
-+}
-+
- static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *report,
- u8 *data, int size)
- {
-@@ -712,10 +890,30 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r
+@@ -285,6 +286,17 @@ static int ps_device_register_battery(struct ps_device *dev)
return 0;
}
-+static int dualsense_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
++/* 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)
+{
-+ struct hid_device *hdev = input_get_drvdata(dev);
-+ struct dualsense *ds = hid_get_drvdata(hdev);
-+ unsigned long flags;
++ uint32_t crc;
+
-+ if (effect->type != FF_RUMBLE)
-+ return 0;
++ crc = crc32_le(0xFFFFFFFF, &seed, 1);
++ crc = ~crc32_le(crc, data, len);
+
-+ spin_lock_irqsave(&ds->base.lock, flags);
-+ ds->update_rumble = true;
-+ ds->motor_left = effect->u.rumble.strong_magnitude / 256;
-+ ds->motor_right = effect->u.rumble.weak_magnitude / 256;
-+ spin_unlock_irqrestore(&ds->base.lock, flags);
-+
-+ schedule_work(&ds->output_worker);
-+ return 0;
++ return crc == report_crc;
+}
+
- static struct ps_device *dualsense_create(struct hid_device *hdev)
+ static struct input_dev *ps_gamepad_create(struct hid_device *hdev)
{
- struct dualsense *ds;
- struct ps_device *ps_dev;
-+ uint8_t max_output_report_size;
- int ret;
-
- ds = devm_kzalloc(&hdev->dev, sizeof(*ds), GFP_KERNEL);
-@@ -734,8 +932,14 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
- ps_dev->battery_capacity = 100; /* initial value until parse_report. */
- ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
- ps_dev->parse_report = dualsense_parse_report;
-+ INIT_WORK(&ds->output_worker, dualsense_output_worker);
- hid_set_drvdata(hdev, ds);
-
-+ max_output_report_size = sizeof(struct dualsense_output_report_bt);
-+ ds->output_report_dmabuf = devm_kzalloc(&hdev->dev, max_output_report_size, GFP_KERNEL);
-+ if (!ds->output_report_dmabuf)
-+ return ERR_PTR(-ENOMEM);
-+
- ret = dualsense_get_mac_address(ds);
- if (ret) {
- hid_err(hdev, "Failed to get MAC address from DualSense\n");
-@@ -753,7 +957,7 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
- goto err;
+ struct input_dev *gamepad;
+@@ -406,6 +418,18 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
+ goto err_free;
}
-- ds->gamepad = ps_gamepad_create(hdev);
-+ ds->gamepad = ps_gamepad_create(hdev, dualsense_play_effect);
- if (IS_ERR(ds->gamepad)) {
- ret = PTR_ERR(ds->gamepad);
- goto err;
++ if (ds->base.hdev->bus == BUS_BLUETOOTH) {
++ /* Last 4 bytes contains crc32 */
++ uint8_t crc_offset = DS_FEATURE_REPORT_CALIBRATION_SIZE - 4;
++ uint32_t report_crc = get_unaligned_le32(&buf[crc_offset]);
++
++ if (!ps_check_crc32(0xa3, buf, crc_offset, report_crc)) {
++ hid_err(ds->base.hdev, "DualSense calibration report CRC's check failed\n");
++ ret = -EILSEQ;
++ goto err_free;
++ }
++ }
++
+ gyro_pitch_bias = get_unaligned_le16(&buf[1]);
+ gyro_yaw_bias = get_unaligned_le16(&buf[3]);
+ gyro_roll_bias = get_unaligned_le16(&buf[5]);
+@@ -515,6 +539,16 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r
+ */
+ if (report->id == DS_INPUT_REPORT_USB && hdev->bus == BUS_USB) {
+ ds_report = (struct dualsense_input_report *)&data[1];
++ } else if (report->id == DS_INPUT_REPORT_BT && hdev->bus == BUS_BLUETOOTH) {
++ /* Last 4 bytes of input report contain crc32 */
++ uint32_t report_crc = get_unaligned_le32(&data[size - 4]);
++
++ if (!ps_check_crc32(0xa1, data, size - 4, report_crc)) {
++ hid_err(hdev, "DualSense input CRC's check failed, size=%d\n", size);
++ return -EILSEQ;
++ }
++
++ ds_report = (struct dualsense_input_report *)&data[2];
+ } else {
+ hid_err(hdev, "Unhandled reportID=%d\n", report->id);
+ return -1;
+@@ -774,6 +808,7 @@ static void ps_remove(struct hid_device *hdev)
+ }
+
+ 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) },
+ { }
+ };
+diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
+index 1ca46cb445be..541c8837debd 100644
+--- a/drivers/hid/hid-quirks.c
++++ b/drivers/hid/hid-quirks.c
+@@ -567,6 +567,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
+ #endif
+ #if IS_ENABLED(CONFIG_HID_PLAYSTATION)
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
+++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
+ #endif
+ #if IS_ENABLED(CONFIG_HID_PRIMAX)
+ { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
--
2.26.2