HID: steam: Retry send/recv reports if stale

Sometimes recv report will reply with a stale result from a previous send
report. Instead of failing out, we should retry them, as they generally
reply correctly after three tries, give or take.

Signed-off-by: Vicki Pfau <vi@endrift.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
Vicki Pfau 2026-07-29 21:12:34 -07:00 committed by Jiri Kosina
parent 33ff7b49c3
commit fcfa7db89f

View File

@ -501,6 +501,43 @@ static int steam_write_settings(struct steam_device *steam,
return steam_recv_report(steam, cmd, 2 + cmd[1]);
}
static int steam_exchange_report(struct steam_device *steam, u8 *cmd, int csize,
u8 *reply, int rsize)
{
unsigned int retries = 5;
int ret;
guard(mutex)(&steam->report_mutex);
do {
ret = steam_send_report(steam, cmd, csize);
if (ret < 0)
return ret;
ret = steam_recv_report(steam, reply, rsize);
/*
* Sometimes this can fail on the first few tries on the Steam
* Controller (2015). It appears to be a firmware bug, and Steam
* itself just retries, so we should also retry a few times to
* see if we get it.
*/
if (ret == -EPROTO)
continue;
if (ret < 0) {
hid_err(steam->hdev, "%s: error reading reply (%*ph)\n",
__func__, csize, cmd);
return ret;
}
if (reply[0] == cmd[0] && reply[1] >= 1)
break;
if (retries > 0)
continue;
hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
rsize, reply);
return -EPROTO;
} while (retries--);
return ret;
}
static int steam_get_serial(struct steam_device *steam)
{
/*
@ -511,15 +548,10 @@ static int steam_get_serial(struct steam_device *steam)
u8 cmd[] = {ID_GET_STRING_ATTRIBUTE, sizeof(steam->serial_no), ATTRIB_STR_UNIT_SERIAL};
u8 reply[3 + STEAM_SERIAL_LEN + 1] = {0};
guard(mutex)(&steam->report_mutex);
ret = steam_send_report(steam, cmd, sizeof(cmd));
ret = steam_exchange_report(steam, cmd, sizeof(cmd), reply, sizeof(reply));
if (ret < 0)
return ret;
ret = steam_recv_report(steam, reply, sizeof(reply));
if (ret < 0)
return ret;
if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] < 1 ||
reply[1] > sizeof(steam->serial_no) || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
if (reply[1] > sizeof(steam->serial_no) || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
(int)sizeof(reply), reply);
return -EIO;
@ -537,14 +569,10 @@ static int steam_get_attributes(struct steam_device *steam)
int i;
struct steam_controller_attribute *attr;
guard(mutex)(&steam->report_mutex);
ret = steam_send_report(steam, cmd, sizeof(cmd));
ret = steam_exchange_report(steam, cmd, sizeof(cmd), reply, sizeof(reply));
if (ret < 0)
return ret;
ret = steam_recv_report(steam, reply, sizeof(reply));
if (ret < 0)
return ret;
if (reply[0] != ID_GET_ATTRIBUTES_VALUES || reply[1] < 2) {
if (reply[1] < 2) {
hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
(int)sizeof(reply), reply);
return -EIO;