summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-steam.c
diff options
context:
space:
mode:
authorVicki Pfau <vi@endrift.com>2026-07-29 21:12:34 -0700
committerJiri Kosina <jkosina@suse.com>2026-08-03 21:00:27 +0200
commitfcfa7db89f7a00c220146695668842de34210d63 (patch)
treeef7728e26e8f7941323b5b5f429c0897188b0bde /drivers/hid/hid-steam.c
parent33ff7b49c38b39b1f3d27db508ac0720fb25c08a (diff)
downloadlinux-next-fcfa7db89f7a00c220146695668842de34210d63.tar.gz
linux-next-fcfa7db89f7a00c220146695668842de34210d63.zip
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>
Diffstat (limited to 'drivers/hid/hid-steam.c')
-rw-r--r--drivers/hid/hid-steam.c54
1 files changed, 41 insertions, 13 deletions
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 3b4a588c20ad..6199f67f3c4c 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -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));
- if (ret < 0)
- return ret;
- ret = steam_recv_report(steam, reply, sizeof(reply));
+ ret = steam_exchange_report(steam, cmd, sizeof(cmd), 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;