From 8f39af37eb18b03cbd5ea3b01c8eea26280b3b3f Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 8 Feb 2024 01:36:43 +0900 Subject: HID: playstation: DS4: Fix LED blinking There was no way to disable blinking once enabled. Disable it on brightness = 0, as per the Linux LED spec. The driver reports back the values it sends to the controller, but they need to be scaled back to milliseconds. Setting the LED blinking via sysfs works as expected now. Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 8ac8f7b8e317..7f50e13601f0 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -2037,8 +2037,9 @@ static int dualshock4_led_set_blink(struct led_classdev *led, unsigned long *del dualshock4_schedule_work(ds4); - *delay_on = ds4->lightbar_blink_on; - *delay_off = ds4->lightbar_blink_off; + /* Report scaled values back to LED subsystem */ + *delay_on = ds4->lightbar_blink_on * 10; + *delay_off = ds4->lightbar_blink_off * 10; return 0; } @@ -2065,6 +2066,13 @@ static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brig break; case 3: ds4->lightbar_enabled = !!value; + + /* brightness = 0 also cancels blinking in Linux. */ + if (!ds4->lightbar_enabled) { + ds4->lightbar_blink_off = 0; + ds4->lightbar_blink_on = 0; + ds4->update_lightbar_blink = true; + } } ds4->update_lightbar = true; -- cgit v1.2.3 From 46089080a8e1c9a16c5967063986f31031bd218f Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 8 Feb 2024 01:36:44 +0900 Subject: HID: playstation: DS4: Don't fail on FW/HW version request Some third-party controllers can't report firmware/hardware version. Unlike for the DualSense, the driver does not use these values for anything in the DualShock 4 case, but merely exposes them via sysfs. They will simply be 0x0. Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 7f50e13601f0..df50ca4dab90 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -2558,8 +2558,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ret = dualshock4_get_firmware_info(ds4); if (ret) { - hid_err(hdev, "Failed to get firmware info from DualShock4\n"); - return ERR_PTR(ret); + hid_warn(hdev, "Failed to get firmware info from DualShock4\n"); + hid_warn(hdev, "HW/FW version data in sysfs will be invalid.\n"); } ret = ps_devices_list_add(ps_dev); -- cgit v1.2.3 From a48a7cd85f5518d21525642391602ec734cb100f Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 8 Feb 2024 01:36:45 +0900 Subject: HID: playstation: DS4: Don't fail on calibration data request Some third-party controllers can't report calibration data for the gyro/accelerometer. We can still use the gamepad as-is, so let's do that. Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index df50ca4dab90..53bfc2828a61 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -1778,8 +1778,10 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) int retries; buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + ret = -ENOMEM; + goto no_buffer_tail_check; + } /* We should normally receive the feature report data we asked * for, but hidraw applications such as Steam can issue feature @@ -1796,26 +1798,27 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) continue; } - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); ret = -EILSEQ; - goto err_free; } else { break; } } } else { /* Bluetooth */ buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + ret = -ENOMEM; + goto no_buffer_tail_check; + } ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf, DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true); - if (ret) { - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); - goto err_free; - } + + if (ret) + hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); } + /* Parse buffer. If the transfer failed, this safely copies zeroes. */ gyro_pitch_bias = get_unaligned_le16(&buf[1]); gyro_yaw_bias = get_unaligned_le16(&buf[3]); gyro_roll_bias = get_unaligned_le16(&buf[5]); @@ -1867,6 +1870,11 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) ds4->gyro_calib_data[2].sens_denom = abs(gyro_roll_plus - gyro_roll_bias) + abs(gyro_roll_minus - gyro_roll_bias); + /* Done parsing the buffer, so let's free it. */ + kfree(buf); + +no_buffer_tail_check: + /* * Sanity check gyro calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing @@ -1919,8 +1927,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) } } -err_free: - kfree(buf); return ret; } @@ -2568,8 +2574,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ret = dualshock4_get_calibration_data(ds4); if (ret) { - hid_err(hdev, "Failed to get calibration data from DualShock4\n"); - goto err; + hid_warn(hdev, "Failed to get calibration data from DualShock4\n"); + hid_warn(hdev, "Gyroscope and accelerometer will be inaccurate.\n"); } ds4->gamepad = ps_gamepad_create(hdev, dualshock4_play_effect); -- cgit v1.2.3 From c7593026522a92a4fa4264a2f33b57a0d5addb6c Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 8 Feb 2024 01:36:46 +0900 Subject: HID: playstation: DS4: Parse minimal report 0x01 Some third-party controllers never switch to the full 0x11 report. They keep sending the short 0x01 report, so let's parse that instead. Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 53bfc2828a61..6b0f25688657 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -287,6 +287,8 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_INPUT_REPORT_BT_MINIMAL 0x01 +#define DS4_INPUT_REPORT_BT_MINIMAL_SIZE 10 #define DS4_INPUT_REPORT_BT 0x11 #define DS4_INPUT_REPORT_BT_SIZE 78 #define DS4_OUTPUT_REPORT_USB 0x05 @@ -2196,6 +2198,7 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * int battery_status, i, j; uint16_t sensor_timestamp; unsigned long flags; + bool is_minimal = false; /* * DualShock4 in USB uses the full HID report for reportID 1, but @@ -2223,6 +2226,18 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * ds4_report = &bt->common; num_touch_reports = bt->num_touch_reports; touch_reports = bt->touch_reports; + } else if (hdev->bus == BUS_BLUETOOTH && + report->id == DS4_INPUT_REPORT_BT_MINIMAL && + size == DS4_INPUT_REPORT_BT_MINIMAL_SIZE) { + /* Some third-party pads never switch to the full 0x11 report. + * The short 0x01 report is 10 bytes long: + * u8 report_id == 0x01 + * u8 first_bytes_of_full_report[9] + * So let's reuse the full report parser, and stop it after + * parsing the buttons. + */ + ds4_report = (struct dualshock4_input_report_common *)&data[1]; + is_minimal = true; } else { hid_err(hdev, "Unhandled reportID=%d\n", report->id); return -1; @@ -2256,6 +2271,9 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); input_sync(ds4->gamepad); + if (is_minimal) + return 0; + /* Parse and calibrate gyroscope data. */ for (i = 0; i < ARRAY_SIZE(ds4_report->gyro); i++) { int raw_data = (short)le16_to_cpu(ds4_report->gyro[i]); -- cgit v1.2.3 From 8f607e007e8127627680e2e5e2cd70da76fb45b1 Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 8 Feb 2024 01:36:47 +0900 Subject: HID: playstation: Simplify device type ID Distinguish PS4/PS5 type controllers using .driver_data in MODULE_DEVICE_TABLE rather than by VID/PID. This allows adding compatible controllers with different VID/PID. Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 6b0f25688657..edc46fc02e9a 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -27,6 +27,11 @@ static DEFINE_IDA(ps_player_id_allocator); #define HID_PLAYSTATION_VERSION_PATCH 0x8000 +enum PS_TYPE { + PS_TYPE_PS4_DUALSHOCK4, + PS_TYPE_PS5_DUALSENSE, +}; + /* Base class for playstation devices. */ struct ps_device { struct list_head list; @@ -2687,17 +2692,14 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } - if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER || - hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 || - hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) { + if (id->driver_data == PS_TYPE_PS4_DUALSHOCK4) { dev = dualshock4_create(hdev); if (IS_ERR(dev)) { hid_err(hdev, "Failed to create dualshock4.\n"); ret = PTR_ERR(dev); goto err_close; } - } else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER || - hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) { + } else if (id->driver_data == PS_TYPE_PS5_DUALSENSE) { dev = dualsense_create(hdev); if (IS_ERR(dev)) { hid_err(hdev, "Failed to create dualsense.\n"); @@ -2731,16 +2733,26 @@ static void ps_remove(struct hid_device *hdev) static const struct hid_device_id ps_devices[] = { /* Sony DualShock 4 controllers for PS4 */ - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + /* Sony DualSense controllers for PS5 */ - { 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_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2), + .driver_data = PS_TYPE_PS5_DUALSENSE }, { } }; MODULE_DEVICE_TABLE(hid, ps_devices); -- cgit v1.2.3 From 947992c7fa9e0e7adf2451e7b7a88ce550248794 Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Sun, 5 May 2024 01:55:32 +0900 Subject: HID: playstation: DS4: Fix calibration workaround for clone devices The logic in dualshock4_get_calibration_data() used uninitialised data in case of a failed kzalloc() for the transfer buffer. The solution is to group all business logic and all sanity checks together, and jump only to the latter in case of an error. While we're at it, factor out the axes' labelling, since it must happen either way for input_report_abs() to succeed later on. Thanks to Dan Carpenter for the Smatch static checker warning. Fixes: a48a7cd85f55 ("HID: playstation: DS4: Don't fail on calibration data request") Signed-off-by: Max Staudt Signed-off-by: Jiri Kosina --- drivers/hid/hid-playstation.c | 52 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index edc46fc02e9a..e7c309cfe3a0 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -1787,7 +1787,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); if (!buf) { ret = -ENOMEM; - goto no_buffer_tail_check; + goto transfer_failed; } /* We should normally receive the feature report data we asked @@ -1807,6 +1807,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); ret = -EILSEQ; + goto transfer_failed; } else { break; } @@ -1815,17 +1816,19 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); if (!buf) { ret = -ENOMEM; - goto no_buffer_tail_check; + goto transfer_failed; } ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf, DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true); - if (ret) + if (ret) { hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto transfer_failed; + } } - /* Parse buffer. If the transfer failed, this safely copies zeroes. */ + /* Transfer succeeded - parse the calibration data received. */ gyro_pitch_bias = get_unaligned_le16(&buf[1]); gyro_yaw_bias = get_unaligned_le16(&buf[3]); gyro_roll_bias = get_unaligned_le16(&buf[5]); @@ -1854,6 +1857,9 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) acc_z_plus = get_unaligned_le16(&buf[31]); acc_z_minus = get_unaligned_le16(&buf[33]); + /* Done parsing the buffer, so let's free it. */ + kfree(buf); + /* * Set gyroscope calibration and normalization parameters. * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s. @@ -1877,26 +1883,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) ds4->gyro_calib_data[2].sens_denom = abs(gyro_roll_plus - gyro_roll_bias) + abs(gyro_roll_minus - gyro_roll_bias); - /* Done parsing the buffer, so let's free it. */ - kfree(buf); - -no_buffer_tail_check: - - /* - * Sanity check gyro calibration data. This is needed to prevent crashes - * during report handling of virtual, clone or broken devices not implementing - * calibration data properly. - */ - for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { - if (ds4->gyro_calib_data[i].sens_denom == 0) { - hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", - ds4->gyro_calib_data[i].abs_code); - ds4->gyro_calib_data[i].bias = 0; - ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE; - ds4->gyro_calib_data[i].sens_denom = S16_MAX; - } - } - /* * Set accelerometer calibration and normalization parameters. * Data values will be normalized to 1/DS4_ACC_RES_PER_G g. @@ -1919,6 +1905,23 @@ no_buffer_tail_check: ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G; ds4->accel_calib_data[2].sens_denom = range_2g; +transfer_failed: + /* + * Sanity check gyro calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing + * calibration data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { + if (ds4->gyro_calib_data[i].sens_denom == 0) { + ds4->gyro_calib_data[i].abs_code = ABS_RX + i; + hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", + ds4->gyro_calib_data[i].abs_code); + ds4->gyro_calib_data[i].bias = 0; + ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE; + ds4->gyro_calib_data[i].sens_denom = S16_MAX; + } + } + /* * Sanity check accelerometer calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing calibration @@ -1926,6 +1929,7 @@ no_buffer_tail_check: */ for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) { if (ds4->accel_calib_data[i].sens_denom == 0) { + ds4->accel_calib_data[i].abs_code = ABS_X + i; hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.", ds4->accel_calib_data[i].abs_code); ds4->accel_calib_data[i].bias = 0; -- cgit v1.2.3