Skip to content

Commit

Permalink
HID: sony: Workaround for DS4 dongle hotplug kernel crash.
Browse files Browse the repository at this point in the history
The hid-sony driver has custom DS4 connect/disconnect logic for the
DS4 dongle, which is a USB dongle acting as a proxy to Bluetooth
connected DS4.

The connect/disconnect logic works fine generally, however not in
conjunction with Steam. Steam implements its own DS4 driver using
hidraw. Both hid-sony and Steam are issuing their own HID requests
and are racing each other during DS4 dongle connect/disconnect
resulting in a kernel crash in hid-sony.

The problem is that upon a DS4 connect to the dongle, hid-sony kicks
of 'ds4_get_calibration_data' from within its dongle hotplug code.
The calibration code issues raw HID feature report for reportID 0x02.
When Steam is running, it issues a feature report for reportID 0x12
typically just prior to hid-sony requesting feature reportID 0x02.
The result is that 'ds4_get_calibration_data' receives the data Steam
requested as that's the HID report returing first. Currently this
results in it processing invalid data, which ultimately results in a
divide by zero upon a future 'dualshock4_parse_report'.

The solution for now is to check within 'ds4_get_calibration_data' to
check if we received data for the feature report we issued and if not
retry. This fixes bug 206785.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
Roderick Colenbrander authored and Jiri Kosina committed Nov 25, 2020
1 parent b4c00e7 commit f5dc93b
Showing 1 changed file with 28 additions and 6 deletions.
34 changes: 28 additions & 6 deletions drivers/hid/hid-sony.c
Expand Up @@ -1597,16 +1597,38 @@ static int dualshock4_get_calibration_data(struct sony_sc *sc)
* of the controller, so that it sends input reports of type 0x11.
*/
if (sc->quirks & (DUALSHOCK4_CONTROLLER_USB | DUALSHOCK4_DONGLE)) {
int retries;

buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;

ret = hid_hw_raw_request(sc->hdev, 0x02, buf,
DS4_FEATURE_REPORT_0x02_SIZE,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0)
goto err_stop;
/* We should normally receive the feature report data we asked
* for, but hidraw applications such as Steam can issue feature
* reports as well. In particular for Dongle reconnects, Steam
* and this function are competing resulting in often receiving
* data for a different HID report, so retry a few times.
*/
for (retries = 0; retries < 3; retries++) {
ret = hid_hw_raw_request(sc->hdev, 0x02, buf,
DS4_FEATURE_REPORT_0x02_SIZE,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0)
goto err_stop;

if (buf[0] != 0x02) {
if (retries < 2) {
hid_warn(sc->hdev, "Retrying DualShock 4 get calibration report (0x02) request\n");
continue;
} else {
ret = -EILSEQ;
goto err_stop;
}
} else {
break;
}
}
} else {
u8 bthdr = 0xA3;
u32 crc;
Expand Down

0 comments on commit f5dc93b

Please sign in to comment.