* [PATCH v2 00/10] HID: steam: General cleanup and improvements
@ 2026-07-07 23:32 Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 01/10] HID: steam: Update documentation Vicki Pfau
` (9 more replies)
0 siblings, 10 replies; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
This is the first half of a patch series improving the hid-steam driver.
This series contains a few notable changes:
- As I've been maintaining it downstream in SteamOS as well as writing
general improvements, I'm marking myself as the maintainer of the driver.
- Renames some stuff, notably marking the original Steam Controller as
Steam Controller (2015) in preparation of adding support for the Steam
Controller (2026), which will be in the second half of this series once
it's ready for submission.
- Adds IMU support for the Steam Controller (2015).
- Some reliability improvements.
- Fixes a short read issue reported by syzkaller.
Hopefully the second half of the series will be ready soon, but I am
waiting on more testing by SteamOS users before I submit it.
This is a minor interation on v1 that fixes the short read check as well as
some miscellaneous issues found by sashiko.
Vicki Pfau (10):
HID: steam: Update documentation
HID: steam: Refactor and clean up report parsing
HID: steam: Rename some constants that got renamed upstream
HID: steam: Add support for sensor events on the Steam Controller
(2015)
HID: steam: Coalesce rumble packets
HID: steam: Fully unregister controller when hidraw is opened
HID: steam: Rearrange deinitialization sequence
HID: steam: Improve logging and other cleanup
HID: steam: Reject short reads
HID: steam: Retry send/recv reports if stale
MAINTAINERS | 6 +
drivers/hid/hid-steam.c | 669 +++++++++++++++++++++++++++-------------
2 files changed, 463 insertions(+), 212 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 01/10] HID: steam: Update documentation
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 02/10] HID: steam: Refactor and clean up report parsing Vicki Pfau
` (8 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
Mark myself as the maintainer, as well as adding myself as an author.
It also makes some minor updates to comments, such as correcly calling the
left menu key view and retroactively renaming the original Steam Controller
as Steam Controller (2015), in preparation for support for the 2026 model.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
MAINTAINERS | 6 ++++++
drivers/hid/hid-steam.c | 13 +++++++------
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4ecd282f8f52..2ed29e970023 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11500,6 +11500,12 @@ F: drivers/hid/hid-sensor-*
F: drivers/iio/*/hid-*
F: include/linux/hid-sensor-*
+HID STEAM CONTROLLER
+M: Vicki Pfau <vi@endrift.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-steam.c
+
HID VRC-2 CAR CONTROLLER DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
L: linux-input@vger.kernel.org
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 197126d6e081..a854d6360a0e 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -48,6 +48,7 @@
MODULE_DESCRIPTION("HID driver for Valve Steam Controller");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>");
+MODULE_AUTHOR("Vicki Pfau <vi@endrift.com>");
static bool lizard_mode = true;
@@ -1413,9 +1414,9 @@ static inline s16 steam_le16(u8 *data)
* 9.1 | BTN_DPAD_RIGHT | left-pad right
* 9.2 | BTN_DPAD_LEFT | left-pad left
* 9.3 | BTN_DPAD_DOWN | left-pad down
- * 9.4 | BTN_SELECT | menu left
+ * 9.4 | BTN_SELECT | view
* 9.5 | BTN_MODE | steam logo
- * 9.6 | BTN_START | menu right
+ * 9.6 | BTN_START | menu
* 9.7 | BTN_GRIPL | left back lever
* 10.0 | BTN_GRIPR | right back lever
* 10.1 | -- | left-pad clicked
@@ -1541,9 +1542,9 @@ static void steam_do_input_event(struct steam_device *steam,
* 9.1 | BTN_DPAD_RIGHT | left-pad right
* 9.2 | BTN_DPAD_LEFT | left-pad left
* 9.3 | BTN_DPAD_DOWN | left-pad down
- * 9.4 | BTN_SELECT | menu left
+ * 9.4 | BTN_SELECT | view
* 9.5 | BTN_MODE | steam logo
- * 9.6 | BTN_START | menu right
+ * 9.6 | BTN_START | menu
* 9.7 | BTN_GRIPL2 | left bottom grip button
* 10.0 | BTN_GRIPR2 | right bottom grip button
* 10.1 | BTN_THUMB | left pad pressed
@@ -1850,11 +1851,11 @@ MODULE_PARM_DESC(lizard_mode,
"Enable mouse and keyboard emulation (lizard mode) when the gamepad is not in use");
static const struct hid_device_id steam_controllers[] = {
- { /* Wired Steam Controller */
+ { /* Wired Steam Controller (2015) */
HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
USB_DEVICE_ID_STEAM_CONTROLLER)
},
- { /* Wireless Steam Controller */
+ { /* Wireless Steam Controller (2015) */
HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS),
.driver_data = STEAM_QUIRK_WIRELESS
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 02/10] HID: steam: Refactor and clean up report parsing
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 01/10] HID: steam: Update documentation Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 03/10] HID: steam: Rename some constants that got renamed upstream Vicki Pfau
` (7 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
This switches from a parsing style where each button or axis is parsed
individually out of a report using !!(byte & BIT(x)) style. This commit
switches it to a mostly unified approach of defining a list of individual
mappings in an array and passing it to a function that handles all of the
extraction. Theoretically this is more lines, but in practice it results in
(subjectively) cleaner code. Some exceptions still need to be made for
things like handling the lizard mode toggle key, but in general there's a
lot less manual code.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 211 ++++++++++++++++++++++++----------------
1 file changed, 128 insertions(+), 83 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index a854d6360a0e..1b0367c98eac 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -43,6 +43,7 @@
#include <linux/rcupdate.h>
#include <linux/delay.h>
#include <linux/power_supply.h>
+#include <linux/unaligned.h>
#include "hid-ids.h"
MODULE_DESCRIPTION("HID driver for Valve Steam Controller");
@@ -1355,13 +1356,45 @@ static void steam_do_connect_event(struct steam_device *steam, bool connected)
* Clamp the values to 32767..-32767 so that the range is
* symmetrical and can be negated safely.
*/
-static inline s16 steam_le16(u8 *data)
+static inline s16 steam_le16(const u8 *data)
{
- s16 x = (s16) le16_to_cpup((__le16 *)data);
+ s16 x = (s16) get_unaligned_le16((__le16 *)data);
return x == -32768 ? -32767 : x;
}
+struct steam_button_mapping {
+ int code;
+ u8 byte;
+ u8 bit;
+};
+
+struct steam_axis_mapping {
+ int code;
+ s8 sign;
+ u8 byte;
+};
+
+static void steam_map_buttons(struct input_dev *input,
+ const struct steam_button_mapping *mappings, const u8 *data)
+{
+ const struct steam_button_mapping *mapping;
+
+ for (mapping = mappings; mapping->code; mapping++)
+ input_report_key(input, mapping->code,
+ data[mapping->byte] & BIT(mapping->bit));
+}
+
+static void steam_map_axes(struct input_dev *input,
+ const struct steam_axis_mapping *mappings, const u8 *data)
+{
+ const struct steam_axis_mapping *mapping;
+
+ for (mapping = mappings; mapping->sign; mapping++)
+ input_report_abs(input, mapping->code,
+ mapping->sign * steam_le16(&data[mapping->byte]));
+}
+
/*
* The size for this message payload is 60.
* The known values are:
@@ -1428,18 +1461,42 @@ static inline s16 steam_le16(u8 *data)
* 10.7 | -- | lpad_and_joy
*/
+static const struct steam_button_mapping steam_controller_button_mappings[] = {
+ { BTN_TR2, 8, 0 },
+ { BTN_TL2, 8, 1 },
+ { BTN_TR, 8, 2 },
+ { BTN_TL, 8, 3 },
+ { BTN_Y, 8, 4 },
+ { BTN_B, 8, 5 },
+ { BTN_X, 8, 6 },
+ { BTN_A, 8, 7 },
+ { BTN_SELECT, 9, 4 },
+ { BTN_MODE, 9, 5 },
+ { BTN_START, 9, 6 },
+ { BTN_GRIPL, 9, 7 },
+ { BTN_GRIPR, 10, 0 },
+ { BTN_THUMBR, 10, 2 },
+ { BTN_THUMBL, 10, 6 },
+ { BTN_THUMB2, 10, 4 },
+ { BTN_DPAD_UP, 9, 0 },
+ { BTN_DPAD_RIGHT, 9, 1 },
+ { BTN_DPAD_LEFT, 9, 2 },
+ { BTN_DPAD_DOWN, 9, 3 },
+ { /* sentinel */ },
+};
+
+static const struct steam_axis_mapping steam_controller_axis_mappings[] = {
+ { ABS_RX, 1, 20 },
+ { ABS_RY, -1, 22 },
+ { /* sentinel */ },
+};
+
static void steam_do_input_event(struct steam_device *steam,
struct input_dev *input, u8 *data)
{
- /* 24 bits of buttons */
- u8 b8, b9, b10;
s16 x, y;
bool lpad_touched, lpad_and_joy;
- b8 = data[8];
- b9 = data[9];
- b10 = data[10];
-
input_report_abs(input, ABS_HAT2Y, data[11]);
input_report_abs(input, ABS_HAT2X, data[12]);
@@ -1451,8 +1508,8 @@ static void steam_do_input_event(struct steam_device *steam,
* joystick values.
* (lpad_touched || lpad_and_joy) tells if the lpad is really touched.
*/
- lpad_touched = b10 & BIT(3);
- lpad_and_joy = b10 & BIT(7);
+ lpad_touched = data[10] & BIT(3);
+ lpad_and_joy = data[10] & BIT(7);
x = steam_le16(data + 16);
y = -steam_le16(data + 18);
@@ -1468,31 +1525,10 @@ static void steam_do_input_event(struct steam_device *steam,
input_report_abs(input, ABS_HAT0X, 0);
input_report_abs(input, ABS_HAT0Y, 0);
}
+ input_report_key(input, BTN_THUMB, lpad_touched || lpad_and_joy);
- input_report_abs(input, ABS_RX, steam_le16(data + 20));
- input_report_abs(input, ABS_RY, -steam_le16(data + 22));
-
- input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
- input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
- input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
- input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
- input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
- input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
- input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
- input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
- input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
- input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
- input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
- input_event(input, EV_KEY, BTN_GRIPL, !!(b9 & BIT(7)));
- input_event(input, EV_KEY, BTN_GRIPR, !!(b10 & BIT(0)));
- input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2)));
- input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
- input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy);
- input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(4)));
- input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
- input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
- input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
- input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
+ steam_map_buttons(input, steam_controller_button_mappings, data);
+ steam_map_axes(input, steam_controller_axis_mappings, data);
input_sync(input);
}
@@ -1595,23 +1631,67 @@ static void steam_do_input_event(struct steam_device *steam,
* 15.6 | -- | unknown
* 15.7 | -- | unknown
*/
+
+static const struct steam_button_mapping steam_deck_button_mappings[] = {
+ { BTN_TR2, 8, 0 },
+ { BTN_TL2, 8, 1 },
+ { BTN_TR, 8, 2 },
+ { BTN_TL, 8, 3 },
+ { BTN_Y, 8, 4 },
+ { BTN_B, 8, 5 },
+ { BTN_X, 8, 6 },
+ { BTN_A, 8, 7 },
+ { BTN_SELECT, 9, 4 },
+ { BTN_MODE, 9, 5 },
+ { BTN_START, 9, 6 },
+ { BTN_GRIPL2, 9, 7 },
+ { BTN_GRIPR2, 10, 0 },
+ { BTN_THUMBL, 10, 6 },
+ { BTN_THUMBR, 11, 2 },
+ { BTN_DPAD_UP, 9, 0 },
+ { BTN_DPAD_RIGHT, 9, 1 },
+ { BTN_DPAD_LEFT, 9, 2 },
+ { BTN_DPAD_DOWN, 9, 3 },
+ { BTN_THUMB, 10, 1 },
+ { BTN_THUMB2, 10, 2 },
+ { BTN_GRIPL, 13, 1 },
+ { BTN_GRIPR, 13, 2 },
+ { BTN_BASE, 14, 2 },
+ { /* sentinel */ },
+};
+
+static const struct steam_axis_mapping steam_deck_axis_mappings[] = {
+ { ABS_X, 1, 48 },
+ { ABS_Y, -1, 50 },
+ { ABS_RX, 1, 52 },
+ { ABS_RY, -1, 54 },
+ { ABS_HAT2Y, 1, 44 },
+ { ABS_HAT2X, 1, 46 },
+ { /* sentinel */ },
+};
+
+static const struct steam_axis_mapping steam_deck_imu_mappings[] = {
+ { ABS_X, 1, 24 },
+ { ABS_Z, -1, 26 },
+ { ABS_Y, 1, 28 },
+ { ABS_RX, 1, 30 },
+ { ABS_RZ, -1, 32 },
+ { ABS_RY, 1, 34 },
+ { /* sentinel */ },
+};
+
static void steam_do_deck_input_event(struct steam_device *steam,
struct input_dev *input, u8 *data)
{
- u8 b8, b9, b10, b11, b13, b14;
+ bool start_pressed;
bool lpad_touched, rpad_touched;
- b8 = data[8];
- b9 = data[9];
- b10 = data[10];
- b11 = data[11];
- b13 = data[13];
- b14 = data[14];
+ start_pressed = data[9] & BIT(6);
- if (!(b9 & BIT(6)) && steam->did_mode_switch) {
+ if (!start_pressed && steam->did_mode_switch) {
steam->did_mode_switch = false;
cancel_delayed_work(&steam->mode_switch);
- } else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) {
+ } else if (!steam->client_opened && start_pressed && !steam->did_mode_switch) {
steam->did_mode_switch = true;
schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
}
@@ -1619,8 +1699,8 @@ static void steam_do_deck_input_event(struct steam_device *steam,
if (!steam->gamepad_mode && lizard_mode)
return;
- lpad_touched = b10 & BIT(3);
- rpad_touched = b10 & BIT(4);
+ lpad_touched = data[10] & BIT(3);
+ rpad_touched = data[10] & BIT(4);
if (lpad_touched) {
input_report_abs(input, ABS_HAT0X, steam_le16(data + 16));
@@ -1638,38 +1718,8 @@ static void steam_do_deck_input_event(struct steam_device *steam,
input_report_abs(input, ABS_HAT1Y, 0);
}
- input_report_abs(input, ABS_X, steam_le16(data + 48));
- input_report_abs(input, ABS_Y, -steam_le16(data + 50));
- input_report_abs(input, ABS_RX, steam_le16(data + 52));
- input_report_abs(input, ABS_RY, -steam_le16(data + 54));
-
- input_report_abs(input, ABS_HAT2Y, steam_le16(data + 44));
- input_report_abs(input, ABS_HAT2X, steam_le16(data + 46));
-
- input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
- input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
- input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
- input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
- input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
- input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
- input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
- input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
- input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
- input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
- input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
- input_event(input, EV_KEY, BTN_GRIPL2, !!(b9 & BIT(7)));
- input_event(input, EV_KEY, BTN_GRIPR2, !!(b10 & BIT(0)));
- input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
- input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2)));
- input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
- input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
- input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
- input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
- input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1)));
- input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2)));
- input_event(input, EV_KEY, BTN_GRIPL, !!(b13 & BIT(1)));
- input_event(input, EV_KEY, BTN_GRIPR, !!(b13 & BIT(2)));
- input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2)));
+ steam_map_buttons(input, steam_deck_button_mappings, data);
+ steam_map_axes(input, steam_deck_axis_mappings, data);
input_sync(input);
}
@@ -1690,12 +1740,7 @@ static void steam_do_deck_sensors_event(struct steam_device *steam,
return;
input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us);
- input_report_abs(sensors, ABS_X, steam_le16(data + 24));
- input_report_abs(sensors, ABS_Z, -steam_le16(data + 26));
- input_report_abs(sensors, ABS_Y, steam_le16(data + 28));
- input_report_abs(sensors, ABS_RX, steam_le16(data + 30));
- input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32));
- input_report_abs(sensors, ABS_RY, steam_le16(data + 34));
+ steam_map_axes(sensors, steam_deck_imu_mappings, data);
input_sync(sensors);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 03/10] HID: steam: Rename some constants that got renamed upstream
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 01/10] HID: steam: Update documentation Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 02/10] HID: steam: Refactor and clean up report parsing Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015) Vicki Pfau
` (6 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
SETTING_MOUSE_POINTER_ENABLED was renamed to SETTING_LIZARD_MODE upstream.
SETTING_GYRO_MODE was renamed to SETTING_IMU_MODE in an older commit, but
the associated enum was overlooked.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 1b0367c98eac..add9f4f27fff 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -151,7 +151,7 @@ enum {
SETTING_USB_DEBUG_MODE,
SETTING_LEFT_TRACKPAD_MODE,
SETTING_RIGHT_TRACKPAD_MODE,
- SETTING_MOUSE_POINTER_ENABLED,
+ SETTING_LIZARD_MODE,
/* 10 */
SETTING_DPAD_DEADZONE,
@@ -261,14 +261,14 @@ enum {
ATTRIB_STR_UNIT_SERIAL,
};
-/* Values for GYRO_MODE (bitmask) */
+/* Values for IMU_MODE (bitmask) */
enum {
- SETTING_GYRO_MODE_OFF = 0,
- SETTING_GYRO_MODE_STEERING = BIT(0),
- SETTING_GYRO_MODE_TILT = BIT(1),
- SETTING_GYRO_MODE_SEND_ORIENTATION = BIT(2),
- SETTING_GYRO_MODE_SEND_RAW_ACCEL = BIT(3),
- SETTING_GYRO_MODE_SEND_RAW_GYRO = BIT(4),
+ SETTING_IMU_MODE_OFF = 0,
+ SETTING_IMU_MODE_STEERING = BIT(0),
+ SETTING_IMU_MODE_TILT = BIT(1),
+ SETTING_IMU_MODE_SEND_ORIENTATION = BIT(2),
+ SETTING_IMU_MODE_SEND_RAW_ACCEL = BIT(3),
+ SETTING_IMU_MODE_SEND_RAW_GYRO = BIT(4),
};
/* Trackpad modes */
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015)
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (2 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 03/10] HID: steam: Rename some constants that got renamed upstream Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:50 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 05/10] HID: steam: Coalesce rumble packets Vicki Pfau
` (5 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
Sensor support was added for the Steam Deck previously, but Steam
Controller sensor events were never added. This adds that missing support,
bringing Steam Controller support much closer to feature parity with things
like SDL and Steam itself.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 214 ++++++++++++++++++++++++++++++++--------
1 file changed, 175 insertions(+), 39 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index add9f4f27fff..6029c055a6cf 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -70,13 +70,14 @@ static LIST_HEAD(steam_devices);
/* Joystick runs are about 5 mm and 32768 units */
#define STEAM_DECK_JOYSTICK_RESOLUTION 6553
/* Accelerometer has 16 bit resolution and a range of +/- 2g */
-#define STEAM_DECK_ACCEL_RES_PER_G 16384
-#define STEAM_DECK_ACCEL_RANGE 32768
+#define STEAM_ACCEL_RES_PER_G 16384
+#define STEAM_ACCEL_RANGE 32768
+#define STEAM_ACCEL_FUZZ 128
#define STEAM_DECK_ACCEL_FUZZ 32
/* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */
-#define STEAM_DECK_GYRO_RES_PER_DPS 16
-#define STEAM_DECK_GYRO_RANGE 32768
-#define STEAM_DECK_GYRO_FUZZ 1
+#define STEAM_GYRO_RES_PER_DPS 16
+#define STEAM_GYRO_RANGE 32768
+#define STEAM_GYRO_FUZZ 0
#define STEAM_PAD_FUZZ 256
@@ -255,6 +256,31 @@ enum
ID_CONTROLLER_DECK_STATE = 9
};
+/* Read-only attributes */
+enum {
+ ATTRIB_UNIQUE_ID, // deprecated
+ ATTRIB_PRODUCT_ID,
+ ATTRIB_PRODUCT_REVISON, // deprecated
+ ATTRIB_CAPABILITIES = ATTRIB_PRODUCT_REVISON, // intentional aliasing
+ ATTRIB_FIRMWARE_VERSION, // deprecated
+ ATTRIB_FIRMWARE_BUILD_TIME,
+ ATTRIB_RADIO_FIRMWARE_BUILD_TIME,
+ ATTRIB_RADIO_DEVICE_ID0,
+ ATTRIB_RADIO_DEVICE_ID1,
+ ATTRIB_DONGLE_FIRMWARE_BUILD_TIME,
+ ATTRIB_HW_ID, // AKA BOARD_REVISION,
+ ATTRIB_BOOTLOADER_BUILD_TIME,
+ ATTRIB_CONNECTION_INTERVAL_IN_US,
+ ATTRIB_SECONDARY_FIRMWARE_BUILD_TIME,
+ ATTRIB_SECONDARY_BOOTLOADER_BUILD_TIME,
+ ATTRIB_SECONDARY_HW_ID, // AKA BOARD_REVISION,
+ ATTRIB_STREAMING,
+ ATTRIB_TRACKPAD_ID,
+ ATTRIB_SECONDARY_TRACKPAD_ID,
+
+ ATTRIB_COUNT
+};
+
/* String attribute identifiers */
enum {
ATTRIB_STR_BOARD_SERIAL,
@@ -284,6 +310,11 @@ enum {
TRACKPAD_GESTURE_KEYBOARD,
};
+struct steam_controller_attribute {
+ unsigned char tag;
+ __le32 value;
+} __packed;
+
/* Pad identifiers for the deck */
#define STEAM_PAD_LEFT 0
#define STEAM_PAD_RIGHT 1
@@ -315,6 +346,7 @@ struct steam_device {
u16 rumble_left;
u16 rumble_right;
unsigned int sensor_timestamp_us;
+ unsigned int sensor_update_rate_us;
struct work_struct unregister_work;
};
@@ -468,6 +500,38 @@ static int steam_get_serial(struct steam_device *steam)
return ret;
}
+static int steam_get_attributes(struct steam_device *steam)
+{
+ int ret = 0;
+ u8 cmd[] = {ID_GET_ATTRIBUTES_VALUES, 0};
+ u8 reply[64] = {};
+ u8 size;
+ 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));
+ if (ret < 0)
+ return ret;
+ if (reply[0] != ID_GET_ATTRIBUTES_VALUES || reply[1] < 2)
+ return -EIO;
+
+ size = min(reply[1], sizeof(reply) - 2);
+ for (i = 0; i + sizeof(*attr) <= size; i += sizeof(*attr)) {
+ attr = (struct steam_controller_attribute *)&reply[i + 2];
+ if (attr->tag == ATTRIB_CONNECTION_INTERVAL_IN_US) {
+ steam->sensor_update_rate_us = get_unaligned_le32(&attr->value);
+ hid_dbg(steam->hdev, "Sensor update rate: %uus\n",
+ steam->sensor_update_rate_us);
+ }
+ }
+
+ return 0;
+}
+
/*
* This command requests the wireless adaptor to post an event
* with the connection status. Useful if this driver is loaded when
@@ -626,6 +690,42 @@ static void steam_input_close(struct input_dev *dev)
}
}
+static int steam_sensor_open(struct input_dev *dev)
+{
+ struct steam_device *steam = input_get_drvdata(dev);
+ unsigned long flags;
+ bool client_opened;
+
+ spin_lock_irqsave(&steam->lock, flags);
+ client_opened = steam->client_opened;
+ spin_unlock_irqrestore(&steam->lock, flags);
+ if (client_opened)
+ return 0;
+
+ guard(mutex)(&steam->report_mutex);
+ steam_write_settings(steam, SETTING_IMU_MODE,
+ SETTING_IMU_MODE_SEND_RAW_ACCEL | SETTING_IMU_MODE_SEND_RAW_GYRO,
+ 0);
+
+ return 0;
+}
+
+static void steam_sensor_close(struct input_dev *dev)
+{
+ struct steam_device *steam = input_get_drvdata(dev);
+ unsigned long flags;
+ bool client_opened;
+
+ spin_lock_irqsave(&steam->lock, flags);
+ client_opened = steam->client_opened;
+ spin_unlock_irqrestore(&steam->lock, flags);
+ if (client_opened)
+ return;
+
+ guard(mutex)(&steam->report_mutex);
+ steam_write_settings(steam, SETTING_IMU_MODE, 0, 0);
+}
+
static enum power_supply_property steam_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_SCOPE,
@@ -839,9 +939,6 @@ static int steam_sensors_register(struct steam_device *steam)
struct input_dev *sensors;
int ret;
- if (!(steam->quirks & STEAM_QUIRK_DECK))
- return 0;
-
rcu_read_lock();
sensors = rcu_dereference(steam->sensors);
rcu_read_unlock();
@@ -856,8 +953,14 @@ static int steam_sensors_register(struct steam_device *steam)
input_set_drvdata(sensors, steam);
sensors->dev.parent = &hdev->dev;
+ if (!(steam->quirks & STEAM_QUIRK_DECK)) {
+ sensors->open = steam_sensor_open;
+ sensors->close = steam_sensor_close;
+ }
- sensors->name = "Steam Deck Motion Sensors";
+ sensors->name = steam->quirks & STEAM_QUIRK_DECK ?
+ "Steam Deck Motion Sensors" :
+ "Steam Controller Motion Sensors";
sensors->phys = hdev->phys;
sensors->uniq = steam->serial_no;
sensors->id.bustype = hdev->bus;
@@ -869,25 +972,34 @@ static int steam_sensors_register(struct steam_device *steam)
__set_bit(EV_MSC, sensors->evbit);
__set_bit(MSC_TIMESTAMP, sensors->mscbit);
- input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE,
- STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
- input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE,
- STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
- input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE,
- STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
- input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G);
- input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G);
- input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G);
-
- input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE,
- STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
- input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE,
- STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
- input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE,
- STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
- input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS);
- input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS);
- input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS);
+ if (steam->quirks & STEAM_QUIRK_DECK) {
+ input_set_abs_params(sensors, ABS_X, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Y, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Z, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
+ } else {
+ input_set_abs_params(sensors, ABS_X, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Y, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_ACCEL_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_Z, -STEAM_ACCEL_RANGE,
+ STEAM_ACCEL_RANGE, STEAM_ACCEL_FUZZ, 0);
+ }
+ input_abs_set_res(sensors, ABS_X, STEAM_ACCEL_RES_PER_G);
+ input_abs_set_res(sensors, ABS_Y, STEAM_ACCEL_RES_PER_G);
+ input_abs_set_res(sensors, ABS_Z, STEAM_ACCEL_RES_PER_G);
+
+ input_set_abs_params(sensors, ABS_RX, -STEAM_GYRO_RANGE,
+ STEAM_GYRO_RANGE, STEAM_GYRO_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_RY, -STEAM_GYRO_RANGE,
+ STEAM_GYRO_RANGE, STEAM_GYRO_FUZZ, 0);
+ input_set_abs_params(sensors, ABS_RZ, -STEAM_GYRO_RANGE,
+ STEAM_GYRO_RANGE, STEAM_GYRO_FUZZ, 0);
+ input_abs_set_res(sensors, ABS_RX, STEAM_GYRO_RES_PER_DPS);
+ input_abs_set_res(sensors, ABS_RY, STEAM_GYRO_RES_PER_DPS);
+ input_abs_set_res(sensors, ABS_RZ, STEAM_GYRO_RES_PER_DPS);
ret = input_register_device(sensors);
if (ret)
@@ -918,9 +1030,6 @@ static void steam_sensors_unregister(struct steam_device *steam)
{
struct input_dev *sensors;
- if (!(steam->quirks & STEAM_QUIRK_DECK))
- return;
-
rcu_read_lock();
sensors = rcu_dereference(steam->sensors);
rcu_read_unlock();
@@ -968,6 +1077,12 @@ static int steam_register(struct steam_device *steam)
strscpy(steam->serial_no, "XXXXXXXXXX",
sizeof(steam->serial_no));
+ ret = steam_get_attributes(steam);
+ if (ret < 0)
+ hid_err(steam->hdev,
+ "%s:steam_get_attributes failed with error %d\n",
+ __func__, ret);
+
hid_info(steam->hdev, "Steam Controller '%s' connected",
steam->serial_no);
@@ -1246,6 +1361,10 @@ static int steam_probe(struct hid_device *hdev,
INIT_LIST_HEAD(&steam->list);
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
steam->sensor_timestamp_us = 0;
+ if (steam->quirks & STEAM_QUIRK_DECK)
+ steam->sensor_update_rate_us = 4000;
+ else
+ steam->sensor_update_rate_us = 9000;
INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
/*
@@ -1491,6 +1610,16 @@ static const struct steam_axis_mapping steam_controller_axis_mappings[] = {
{ /* sentinel */ },
};
+static const struct steam_axis_mapping steam_controller_imu_mappings[] = {
+ { ABS_X, 1, 28 },
+ { ABS_Z, -1, 30 },
+ { ABS_Y, 1, 32 },
+ { ABS_RX, 1, 34 },
+ { ABS_RZ, 1, 36 },
+ { ABS_RY, 1, 38 },
+ { /* sentinel */ },
+};
+
static void steam_do_input_event(struct steam_device *steam,
struct input_dev *input, u8 *data)
{
@@ -1533,6 +1662,17 @@ static void steam_do_input_event(struct steam_device *steam,
input_sync(input);
}
+static void steam_do_sensors_event(struct steam_device *steam,
+ struct input_dev *sensors, u8 *data)
+{
+ steam->sensor_timestamp_us += steam->sensor_update_rate_us;
+
+ input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us);
+ steam_map_axes(sensors, steam_controller_imu_mappings, data);
+
+ input_sync(sensors);
+}
+
/*
* The size for this message payload is 56.
* The known values are:
@@ -1727,14 +1867,7 @@ static void steam_do_deck_input_event(struct steam_device *steam,
static void steam_do_deck_sensors_event(struct steam_device *steam,
struct input_dev *sensors, u8 *data)
{
- /*
- * The deck input report is received every 4 ms on average,
- * with a jitter of +/- 4 ms even though the USB descriptor claims
- * that it uses 1 kHz.
- * Since the HID report does not include a sensor timestamp,
- * use a fixed increment here.
- */
- steam->sensor_timestamp_us += 4000;
+ steam->sensor_timestamp_us += steam->sensor_update_rate_us;
if (!steam->gamepad_mode && lizard_mode)
return;
@@ -1819,6 +1952,9 @@ static int steam_raw_event(struct hid_device *hdev,
input = rcu_dereference(steam->input);
if (likely(input))
steam_do_input_event(steam, input, data);
+ sensors = rcu_dereference(steam->sensors);
+ if (likely(sensors))
+ steam_do_sensors_event(steam, sensors, data);
rcu_read_unlock();
break;
case ID_CONTROLLER_DECK_STATE:
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 05/10] HID: steam: Coalesce rumble packets
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (3 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015) Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:46 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened Vicki Pfau
` (4 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
The Steam Deck resets the haptic pattern every time it receives a rumble
packet, leading to weird discontinuities or sometimes cutting out entirely.
Instead of overloading the interface, Steam interally rate-limits sending
these packets, so we should too.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 6029c055a6cf..553748813901 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -343,6 +343,7 @@ struct steam_device {
bool did_mode_switch;
bool gamepad_mode;
struct work_struct rumble_work;
+ struct delayed_work coalesce_rumble_work;
u16 rumble_left;
u16 rumble_right;
unsigned int sensor_timestamp_us;
@@ -603,10 +604,24 @@ static void steam_haptic_rumble_cb(struct work_struct *work)
{
struct steam_device *steam = container_of(work, struct steam_device,
rumble_work);
+
steam_haptic_rumble(steam, 0, steam->rumble_left,
steam->rumble_right, 2, 0);
}
+static void steam_coalesce_rumble_cb(struct work_struct *work)
+{
+ struct steam_device *steam = container_of(to_delayed_work(work),
+ struct steam_device,
+ coalesce_rumble_work);
+
+ steam_haptic_rumble(steam, 0, steam->rumble_left,
+ steam->rumble_right, 2, 0);
+
+ if (steam->rumble_left || steam->rumble_right)
+ schedule_delayed_work(&steam->coalesce_rumble_work, HZ / 20);
+}
+
#ifdef CONFIG_STEAM_FF
static int steam_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
@@ -616,6 +631,14 @@ static int steam_play_effect(struct input_dev *dev, void *data,
steam->rumble_left = effect->u.rumble.strong_magnitude;
steam->rumble_right = effect->u.rumble.weak_magnitude;
+ /*
+ * The interface gets somewhat overloaded when too many rumble
+ * packets are sent in a row, so Steam throttles it to 20 Hz
+ */
+ if (delayed_work_pending(&steam->coalesce_rumble_work))
+ return 0;
+
+ schedule_delayed_work(&steam->coalesce_rumble_work, HZ / 20);
return schedule_work(&steam->rumble_work);
}
#endif
@@ -1360,6 +1383,7 @@ static int steam_probe(struct hid_device *hdev,
INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
INIT_LIST_HEAD(&steam->list);
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
+ INIT_DELAYED_WORK(&steam->coalesce_rumble_work, steam_coalesce_rumble_cb);
steam->sensor_timestamp_us = 0;
if (steam->quirks & STEAM_QUIRK_DECK)
steam->sensor_update_rate_us = 4000;
@@ -1426,6 +1450,7 @@ static int steam_probe(struct hid_device *hdev,
cancel_work_sync(&steam->work_connect);
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->rumble_work);
+ cancel_delayed_work_sync(&steam->coalesce_rumble_work);
cancel_work_sync(&steam->unregister_work);
return ret;
@@ -1444,6 +1469,7 @@ static void steam_remove(struct hid_device *hdev)
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->work_connect);
cancel_work_sync(&steam->rumble_work);
+ cancel_delayed_work_sync(&steam->coalesce_rumble_work);
cancel_work_sync(&steam->unregister_work);
steam->client_hdev = NULL;
steam->client_opened = 0;
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (4 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 05/10] HID: steam: Coalesce rumble packets Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:52 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 07/10] HID: steam: Rearrange deinitialization sequence Vicki Pfau
` (3 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
To avoid conflicts between anything touching the hidraw and the driver we
had previously detached the evdev nodes when the hidraw is opened. However,
this isn't sufficient to avoid FEATURE reports from conflicting, so we
change to fully unregistering the controller internally, leaving only the
hidraw active until it's closed.
This also unifies the unregister and connect callbacks, as now the logic
between these two callbacks is identical.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 59 ++++++++++++-----------------------------
1 file changed, 17 insertions(+), 42 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 553748813901..3f1dbdcad23c 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -348,7 +348,6 @@ struct steam_device {
u16 rumble_right;
unsigned int sensor_timestamp_us;
unsigned int sensor_update_rate_us;
- struct work_struct unregister_work;
};
static int steam_recv_report(struct steam_device *steam,
@@ -1077,6 +1076,7 @@ static void steam_battery_unregister(struct steam_device *steam)
RCU_INIT_POINTER(steam->battery, NULL);
synchronize_rcu();
power_supply_unregister(battery);
+ devm_kfree(&steam->hdev->dev, steam->battery_desc.name);
}
static int steam_register(struct steam_device *steam)
@@ -1142,38 +1142,41 @@ static int steam_register(struct steam_device *steam)
static void steam_unregister(struct steam_device *steam)
{
+ if (!steam->serial_no[0])
+ return;
+
+ hid_info(steam->hdev, "Steam Controller '%s' disconnected",
+ steam->serial_no);
steam_battery_unregister(steam);
steam_sensors_unregister(steam);
steam_input_unregister(steam);
- if (steam->serial_no[0]) {
- hid_info(steam->hdev, "Steam Controller '%s' disconnected",
- steam->serial_no);
- mutex_lock(&steam_devices_lock);
- list_del_init(&steam->list);
- mutex_unlock(&steam_devices_lock);
- steam->serial_no[0] = 0;
- }
+ mutex_lock(&steam_devices_lock);
+ list_del_init(&steam->list);
+ mutex_unlock(&steam_devices_lock);
+ steam->serial_no[0] = 0;
}
static void steam_work_connect_cb(struct work_struct *work)
{
struct steam_device *steam = container_of(work, struct steam_device,
work_connect);
+
unsigned long flags;
bool connected;
+ bool opened;
int ret;
spin_lock_irqsave(&steam->lock, flags);
+ opened = steam->client_opened;
connected = steam->connected;
spin_unlock_irqrestore(&steam->lock, flags);
- if (connected) {
+ if (connected && !opened) {
ret = steam_register(steam);
- if (ret) {
+ if (ret)
hid_err(steam->hdev,
"%s:steam_register failed with error %d\n",
__func__, ret);
- }
} else {
steam_unregister(steam);
}
@@ -1207,31 +1210,6 @@ static void steam_mode_switch_cb(struct work_struct *work)
}
}
-static void steam_work_unregister_cb(struct work_struct *work)
-{
- struct steam_device *steam = container_of(work, struct steam_device,
- unregister_work);
- unsigned long flags;
- bool connected;
- bool opened;
-
- spin_lock_irqsave(&steam->lock, flags);
- opened = steam->client_opened;
- connected = steam->connected;
- spin_unlock_irqrestore(&steam->lock, flags);
-
- if (connected) {
- if (opened) {
- steam_sensors_unregister(steam);
- steam_input_unregister(steam);
- } else {
- steam_set_lizard_mode(steam, lizard_mode);
- steam_input_register(steam);
- steam_sensors_register(steam);
- }
- }
-}
-
static bool steam_is_valve_interface(struct hid_device *hdev)
{
struct hid_report_enum *rep_enum;
@@ -1277,7 +1255,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
steam->client_opened++;
spin_unlock_irqrestore(&steam->lock, flags);
- schedule_work(&steam->unregister_work);
+ schedule_work(&steam->work_connect);
return 0;
}
@@ -1292,7 +1270,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
steam->client_opened--;
spin_unlock_irqrestore(&steam->lock, flags);
- schedule_work(&steam->unregister_work);
+ schedule_work(&steam->work_connect);
}
static int steam_client_ll_raw_request(struct hid_device *hdev,
@@ -1389,7 +1367,6 @@ static int steam_probe(struct hid_device *hdev,
steam->sensor_update_rate_us = 4000;
else
steam->sensor_update_rate_us = 9000;
- INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
/*
* With the real steam controller interface, do not connect hidraw.
@@ -1451,7 +1428,6 @@ static int steam_probe(struct hid_device *hdev,
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->rumble_work);
cancel_delayed_work_sync(&steam->coalesce_rumble_work);
- cancel_work_sync(&steam->unregister_work);
return ret;
}
@@ -1470,7 +1446,6 @@ static void steam_remove(struct hid_device *hdev)
cancel_work_sync(&steam->work_connect);
cancel_work_sync(&steam->rumble_work);
cancel_delayed_work_sync(&steam->coalesce_rumble_work);
- cancel_work_sync(&steam->unregister_work);
steam->client_hdev = NULL;
steam->client_opened = 0;
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 07/10] HID: steam: Rearrange deinitialization sequence
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (5 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 08/10] HID: steam: Improve logging and other cleanup Vicki Pfau
` (2 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
This fixes a narrow window during the deinitialization where callbacks
could still be scheduled during cleanup that would then have a dangling
pointer to the now-freed steam struct.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 3f1dbdcad23c..296ced2989a4 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -1327,6 +1327,7 @@ static int steam_probe(struct hid_device *hdev,
{
struct steam_device *steam;
int ret;
+ unsigned long flags;
ret = hid_parse(hdev);
if (ret) {
@@ -1417,6 +1418,10 @@ static int steam_probe(struct hid_device *hdev,
err_destroy:
hid_destroy_device(steam->client_hdev);
err_steam_unregister:
+ spin_lock_irqsave(&steam->lock, flags);
+ steam->client_opened = 0;
+ spin_unlock_irqrestore(&steam->lock, flags);
+ cancel_work_sync(&steam->work_connect);
if (steam->connected)
steam_unregister(steam);
err_hw_close:
@@ -1424,7 +1429,6 @@ static int steam_probe(struct hid_device *hdev,
err_hw_stop:
hid_hw_stop(hdev);
err_cancel_work:
- cancel_work_sync(&steam->work_connect);
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->rumble_work);
cancel_delayed_work_sync(&steam->coalesce_rumble_work);
@@ -1435,6 +1439,7 @@ static int steam_probe(struct hid_device *hdev,
static void steam_remove(struct hid_device *hdev)
{
struct steam_device *steam = hid_get_drvdata(hdev);
+ unsigned long flags;
if (!steam || hdev->group == HID_GROUP_STEAM) {
hid_hw_stop(hdev);
@@ -1442,18 +1447,20 @@ static void steam_remove(struct hid_device *hdev)
}
hid_destroy_device(steam->client_hdev);
- cancel_delayed_work_sync(&steam->mode_switch);
- cancel_work_sync(&steam->work_connect);
- cancel_work_sync(&steam->rumble_work);
- cancel_delayed_work_sync(&steam->coalesce_rumble_work);
steam->client_hdev = NULL;
+ hid_hw_close(hdev);
+ spin_lock_irqsave(&steam->lock, flags);
steam->client_opened = 0;
+ spin_unlock_irqrestore(&steam->lock, flags);
+ cancel_work_sync(&steam->work_connect);
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver disconnected");
}
- hid_hw_close(hdev);
- hid_hw_stop(hdev);
steam_unregister(steam);
+ cancel_work_sync(&steam->rumble_work);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_delayed_work_sync(&steam->coalesce_rumble_work);
+ hid_hw_stop(hdev);
}
static void steam_do_connect_event(struct steam_device *steam, bool connected)
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 08/10] HID: steam: Improve logging and other cleanup
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (6 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 07/10] HID: steam: Rearrange deinitialization sequence Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:47 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 09/10] HID: steam: Reject short reads Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale Vicki Pfau
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
Adds more logging as appropriate, reindents an enum to match surrounding
style, as well as cleaning up some places where we can use guard() instead
of doing locking and unlocking manually.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 56 ++++++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 23 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 296ced2989a4..1f2cf15d4083 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -246,14 +246,14 @@ enum {
/* Input report identifiers */
enum
{
- ID_CONTROLLER_STATE = 1,
- ID_CONTROLLER_DEBUG = 2,
- ID_CONTROLLER_WIRELESS = 3,
- ID_CONTROLLER_STATUS = 4,
- ID_CONTROLLER_DEBUG2 = 5,
- ID_CONTROLLER_SECONDARY_STATE = 6,
- ID_CONTROLLER_BLE_STATE = 7,
- ID_CONTROLLER_DECK_STATE = 9
+ ID_CONTROLLER_STATE = 1,
+ ID_CONTROLLER_DEBUG = 2,
+ ID_CONTROLLER_WIRELESS = 3,
+ ID_CONTROLLER_STATUS = 4,
+ ID_CONTROLLER_DEBUG2 = 5,
+ ID_CONTROLLER_SECONDARY_STATE = 6,
+ ID_CONTROLLER_BLE_STATE = 7,
+ ID_CONTROLLER_DECK_STATE = 9,
};
/* Read-only attributes */
@@ -379,9 +379,16 @@ static int steam_recv_report(struct steam_device *steam,
ret = hid_hw_raw_request(steam->hdev, 0x00,
buf, hid_report_len(r) + 1,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
- if (ret > 0)
- memcpy(data, buf + 1, min(size, ret - 1));
+ if (ret > 0) {
+ ret = min(size, ret - 1);
+ memcpy(data, buf + 1, ret);
+ }
kfree(buf);
+
+ if (ret < 0)
+ hid_err(steam->hdev, "%s: error %d\n", __func__, ret);
+ else
+ hid_dbg(steam->hdev, "Received report %*ph\n", ret, data);
return ret;
}
@@ -409,6 +416,8 @@ static int steam_send_report(struct steam_device *steam,
/* The report ID is always 0 */
memcpy(buf + 1, cmd, size);
+ hid_dbg(steam->hdev, "Sending report %*ph\n", size, cmd);
+
/*
* Sometimes the wireless controller fails with EPIPE
* when sending a feature report.
@@ -481,22 +490,21 @@ 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];
- mutex_lock(&steam->report_mutex);
+ guard(mutex)(&steam->report_mutex);
ret = steam_send_report(steam, cmd, sizeof(cmd));
if (ret < 0)
- goto out;
+ return ret;
ret = steam_recv_report(steam, reply, sizeof(reply));
if (ret < 0)
- goto out;
+ return ret;
if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] < 1 ||
reply[1] > sizeof(steam->serial_no) || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
- ret = -EIO;
- goto out;
+ hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
+ (int)sizeof(reply), reply);
+ return -EIO;
}
reply[3 + STEAM_SERIAL_LEN] = 0;
strscpy(steam->serial_no, reply + 3, reply[1]);
-out:
- mutex_unlock(&steam->report_mutex);
return ret;
}
@@ -516,8 +524,11 @@ static int steam_get_attributes(struct steam_device *steam)
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[0] != ID_GET_ATTRIBUTES_VALUES || reply[1] < 2) {
+ hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
+ (int)sizeof(reply), reply);
return -EIO;
+ }
size = min(reply[1], sizeof(reply) - 2);
for (i = 0; i + sizeof(*attr) <= size; i += sizeof(*attr)) {
@@ -539,11 +550,8 @@ static int steam_get_attributes(struct steam_device *steam)
*/
static inline int steam_request_conn_status(struct steam_device *steam)
{
- int ret;
- mutex_lock(&steam->report_mutex);
- ret = steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE);
- mutex_unlock(&steam->report_mutex);
- return ret;
+ guard(mutex)(&steam->report_mutex);
+ return steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE);
}
/*
@@ -1192,6 +1200,7 @@ static void steam_mode_switch_cb(struct work_struct *work)
return;
steam->gamepad_mode = !steam->gamepad_mode;
+ hid_dbg(steam->hdev, "%s: switching gamepad mode to %i\n", __func__, steam->gamepad_mode);
if (steam->gamepad_mode)
steam_set_lizard_mode(steam, false);
else {
@@ -1840,6 +1849,7 @@ static void steam_do_deck_input_event(struct steam_device *steam,
steam->did_mode_switch = false;
cancel_delayed_work(&steam->mode_switch);
} else if (!steam->client_opened && start_pressed && !steam->did_mode_switch) {
+ hid_dbg(steam->hdev, "%s: doing mode switch\n", __func__);
steam->did_mode_switch = true;
schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 09/10] HID: steam: Reject short reads
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (7 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 08/10] HID: steam: Improve logging and other cleanup Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:47 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale Vicki Pfau
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input
Cc: Vicki Pfau, Yousef Alhouseen, syzbot+75f3f9bff8c510602d36
Steam Controller FEATURE reports encode the size of the message in the
message itself. Previously we were trusting that the size reported matched
the size we actually read, leading to a potential issue with short reads.
Instead, we should actually verify the length of the read.
Fixes: c164d6abf384 ("HID: add driver for Valve Steam Controller")
Reported-by: syzbot+75f3f9bff8c510602d36@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=75f3f9bff8c510602d36
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/hid/hid-steam.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 1f2cf15d4083..49f6733d8935 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -357,6 +357,13 @@ static int steam_recv_report(struct steam_device *steam,
u8 *buf;
int ret;
+ /*
+ * All reports start with a two byte header.
+ * We must read at least two bytes to get a sensible output.
+ */
+ if (size < 2)
+ return -EINVAL;
+
r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0];
if (!r) {
hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted - nothing to read\n");
@@ -380,8 +387,10 @@ static int steam_recv_report(struct steam_device *steam,
buf, hid_report_len(r) + 1,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret > 0) {
- ret = min(size, ret - 1);
- memcpy(data, buf + 1, ret);
+ /* Remove the report ID from the return buffer */
+ ret--;
+ size = min(size, ret);
+ memcpy(data, buf + 1, size);
}
kfree(buf);
@@ -389,7 +398,15 @@ static int steam_recv_report(struct steam_device *steam,
hid_err(steam->hdev, "%s: error %d\n", __func__, ret);
else
hid_dbg(steam->hdev, "Received report %*ph\n", ret, data);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ if (ret < 2 || ret < data[1] + 2) {
+ hid_err(steam->hdev, "%s: expected %u bytes, read %i\n",
+ __func__, data[1] + 2, ret);
+ return -EPROTO;
+ }
+ return size;
}
static int steam_send_report(struct steam_device *steam,
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
` (8 preceding siblings ...)
2026-07-07 23:32 ` [PATCH v2 09/10] HID: steam: Reject short reads Vicki Pfau
@ 2026-07-07 23:32 ` Vicki Pfau
2026-07-07 23:48 ` sashiko-bot
9 siblings, 1 reply; 17+ messages in thread
From: Vicki Pfau @ 2026-07-07 23:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, linux-input; +Cc: Vicki Pfau, Yousef Alhouseen
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>
---
drivers/hid/hid-steam.c | 54 +++++++++++++++++++++++++++++++----------
1 file changed, 41 insertions(+), 13 deletions(-)
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 49f6733d8935..ba2dd40a7976 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -497,6 +497,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)
{
/*
@@ -507,15 +544,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];
- 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;
@@ -534,14 +566,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;
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v2 05/10] HID: steam: Coalesce rumble packets
2026-07-07 23:32 ` [PATCH v2 05/10] HID: steam: Coalesce rumble packets Vicki Pfau
@ 2026-07-07 23:46 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:46 UTC (permalink / raw)
To: Vicki Pfau; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The rate-limiting logic inadvertently creates a continuous polling loop that actively induces the haptic discontinuities it was intended to fix.
--
--- Patch [5]: [PATCH v2 05/10] HID: steam: Coalesce rumble packets ---
commit 57ea23e2a38271a41e31886938cdef03c430a55f
Author: Vicki Pfau <vi@endrift.com>
HID: steam: Coalesce rumble packets
This commit adds rate limiting to Steam Deck haptic rumble packets to
avoid resetting the haptic pattern too frequently when userspace sends
many rapid updates. It introduces a delayed work queue to throttle updates
to 20Hz.
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 6029c055a6cf3..5537488139011 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -603,10 +604,24 @@ static void steam_haptic_rumble_cb(struct work_struct *work)
> {
> struct steam_device *steam = container_of(work, struct steam_device,
> rumble_work);
> +
> steam_haptic_rumble(steam, 0, steam->rumble_left,
> steam->rumble_right, 2, 0);
> }
>
> +static void steam_coalesce_rumble_cb(struct work_struct *work)
> +{
> + struct steam_device *steam = container_of(to_delayed_work(work),
> + struct steam_device,
> + coalesce_rumble_work);
> +
> + steam_haptic_rumble(steam, 0, steam->rumble_left,
> + steam->rumble_right, 2, 0);
> +
> + if (steam->rumble_left || steam->rumble_right)
> + schedule_delayed_work(&steam->coalesce_rumble_work, HZ / 20);
> +}
[Severity: High]
Does this logic inadvertently create a continuous loop that actively induces
the haptic discontinuities it was intended to fix?
Since the callback unconditionally reschedules itself every 50ms as long as
the stored rumble values are non-zero, it will continuously transmit identical
rumble packets to the device at 20Hz, even when userspace has stopped
updating the effect.
Because the commit message mentions the Steam Deck resets its haptic pattern
on every packet, won't this perpetual loop guarantee the pattern resets every
50ms and unnecessarily spam the USB bus?
> +
> #ifdef CONFIG_STEAM_FF
> static int steam_play_effect(struct input_dev *dev, void *data,
> struct ff_effect *effect)
> @@ -616,6 +631,14 @@ static int steam_play_effect(struct input_dev *dev, void *data,
> steam->rumble_left = effect->u.rumble.strong_magnitude;
> steam->rumble_right = effect->u.rumble.weak_magnitude;
>
> + /*
> + * The interface gets somewhat overloaded when too many rumble
> + * packets are sent in a row, so Steam throttles it to 20 Hz
> + */
> + if (delayed_work_pending(&steam->coalesce_rumble_work))
> + return 0;
[Severity: High]
Could this condition unnecessarily stall valid userspace updates?
Because the loop in steam_coalesce_rumble_cb() keeps the delayed work pending
continuously while the effect is active, valid userspace updates may be
stalled by up to 50ms instead of executing immediately when the system is
otherwise idle.
> +
> + schedule_delayed_work(&steam->coalesce_rumble_work, HZ / 20);
> return schedule_work(&steam->rumble_work);
> }
> #endif
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=5
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 09/10] HID: steam: Reject short reads
2026-07-07 23:32 ` [PATCH v2 09/10] HID: steam: Reject short reads Vicki Pfau
@ 2026-07-07 23:47 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:47 UTC (permalink / raw)
To: Vicki Pfau; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Out-of-bounds stack memory read in `steam_recv_report` when logging debug information.
- [Medium] Uninitialized kernel stack memory read on the error path for short reports.
--
--- Patch [9]: [PATCH v2 09/10] HID: steam: Reject short reads ---
commit ea3743d28300ad972f70af0939882012d7dbf763
Author: Vicki Pfau <vi@endrift.com>
HID: steam: Reject short reads
Steam Controller FEATURE reports encode the size of the message in the
message itself. Previously we were trusting that the size reported matched
the size we actually read, leading to a potential issue with short reads.
Instead, we should actually verify the length of the read.
Fixes: c164d6abf384 ("HID: add driver for Valve Steam Controller")
Reported-by: syzbot+75f3f9bff8c510602d36@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=75f3f9bff8c510602d36
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 1f2cf15d40831..49f6733d89359 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -380,8 +387,10 @@ static int steam_recv_report(struct steam_device *steam,
> buf, hid_report_len(r) + 1,
> HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> if (ret > 0) {
> - ret = min(size, ret - 1);
> - memcpy(data, buf + 1, ret);
> + /* Remove the report ID from the return buffer */
> + ret--;
> + size = min(size, ret);
> + memcpy(data, buf + 1, size);
> }
> kfree(buf);
>
> @@ -389,7 +398,15 @@ static int steam_recv_report(struct steam_device *steam,
> hid_err(steam->hdev, "%s: error %d\n", __func__, ret);
> else
> hid_dbg(steam->hdev, "Received report %*ph\n", ret, data);
[Severity: Medium]
Since the clamping of ret to size was removed above, ret can now be larger
than the caller-provided buffer size.
If a caller provides a small stack buffer (e.g. 25 bytes in
steam_get_serial()) and the device returns a larger report, will hid_dbg()
read out-of-bounds stack memory from the data buffer when dynamic debug is
enabled?
> - return ret;
> + if (ret < 0)
> + return ret;
> +
> + if (ret < 2 || ret < data[1] + 2) {
> + hid_err(steam->hdev, "%s: expected %u bytes, read %i\n",
> + __func__, data[1] + 2, ret);
[Severity: Medium]
If the device returns a short read of 0 or 1 bytes, memcpy() copies less
than 2 bytes to data, leaving data[1] uninitialized.
Since hid_err() arguments are unconditionally evaluated before the function
is called, could this read uninitialized stack memory from data[1] if
ret < 2 evaluates to true?
> + return -EPROTO;
> + }
> + return size;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=9
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 08/10] HID: steam: Improve logging and other cleanup
2026-07-07 23:32 ` [PATCH v2 08/10] HID: steam: Improve logging and other cleanup Vicki Pfau
@ 2026-07-07 23:47 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:47 UTC (permalink / raw)
To: Vicki Pfau; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Uninitialized kernel stack memory leaked to dmesg via short reports in steam_get_serial()
- [High] Use-After-Free due to bypassed workqueue cancellation in steam_probe() error paths
Pre-existing issues:
- [High] Use-After-Free via global steam_devices list due to bypassed unregister in probe error paths
--
--- Patch [8]: [PATCH v2 08/10] HID: steam: Improve logging and other cleanup ---
commit 5aa379e88890315b198ed7d8da2c352e3a8ce6a8
Author: Vicki Pfau <vi@endrift.com>
HID: steam: Improve logging and other cleanup
This commit adds more logging where appropriate and reindents an enum to
match the surrounding coding style. It also cleans up some places where
guard() can be used for lock management instead of manually calling locking
and unlocking functions.
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 296ced2989a42..1f2cf15d40831 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -481,22 +490,21 @@ 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];
>
> - mutex_lock(&steam->report_mutex);
> + guard(mutex)(&steam->report_mutex);
> ret = steam_send_report(steam, cmd, sizeof(cmd));
> if (ret < 0)
> - goto out;
> + return ret;
> ret = steam_recv_report(steam, reply, sizeof(reply));
> if (ret < 0)
> - goto out;
> + return ret;
> if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] < 1 ||
> reply[1] > sizeof(steam->serial_no) || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
> - ret = -EIO;
> - goto out;
> + hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__,
> + (int)sizeof(reply), reply);
[Severity: High]
Does this code risk exposing uninitialized kernel stack memory to the kernel
log?
When steam_recv_report() reads a short report, it might copy fewer bytes than
sizeof(reply) into the uninitialized reply buffer. The new hid_err() call
unconditionally dumps the entire buffer capacity, which could leak stack
memory if the device responds with an invalid or short report.
> + return -EIO;
> }
> reply[3 + STEAM_SERIAL_LEN] = 0;
> strscpy(steam->serial_no, reply + 3, reply[1]);
> -out:
> - mutex_unlock(&steam->report_mutex);
> return ret;
> }
[ ... ]
While reviewing this series, I also noticed a couple of issues in the
error handling paths of steam_probe() from earlier patches or existing code:
[Severity: High]
This is a pre-existing issue, but does this code leave a dangling pointer in
the global steam_devices list on registration failure?
In steam_probe():
} else {
/* A wired connection is always present */
steam->connected = true;
ret = steam_register(steam);
if (ret) {
hid_err(hdev,
"%s:steam_register failed with error %d\n",
__func__, ret);
goto err_hw_close;
}
}
If steam_register() fails, the device is already added to the global
steam_devices list, but the error path jumps to err_hw_close. This bypasses
err_steam_unregister where it would normally be removed.
Once probe fails, devm frees the steam structure, which can lead to a
use-after-free if the lizard_mode sysfs parameter is subsequently accessed.
[Severity: High]
Does the following sequence result in a use-after-free if probe fails late?
In a previous patch in this series, the cancellation of work_connect was
moved from err_cancel_work to err_steam_unregister:
err_steam_unregister:
spin_lock_irqsave(&steam->lock, flags);
steam->client_opened = 0;
spin_unlock_irqrestore(&steam->lock, flags);
cancel_work_sync(&steam->work_connect);
if (steam->connected)
steam_unregister(steam);
err_hw_close:
hid_hw_close(hdev);
err_hw_stop:
hid_hw_stop(hdev);
err_cancel_work:
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->rumble_work);
cancel_delayed_work_sync(&steam->coalesce_rumble_work);
If an ID_CONTROLLER_WIRELESS event is received early asynchronously,
work_connect could be queued. If hid_hw_open() then fails and jumps to
err_hw_stop, err_steam_unregister is bypassed.
The devm subsystem will free the steam structure, and the still-queued work
item could crash when executed.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=8
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale
2026-07-07 23:32 ` [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale Vicki Pfau
@ 2026-07-07 23:48 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:48 UTC (permalink / raw)
To: Vicki Pfau; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Log spam in expected retry path due to unconditional `hid_err` in `steam_recv_report`.
- [Medium] Out-of-bounds stack read in `hid_dbg` when the device report exceeds the caller's buffer.
- [Medium] `steam_recv_report` reads uninitialized stack memory on short hardware reads.
--
--- Patch [10]: [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale ---
commit 181ac85173536f44d1a324b3bc0042d43fa8238e
Author: Vicki Pfau <vi@endrift.com>
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.
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 49f6733d89359..ba2dd40a79763 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
> @@ -497,6 +497,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);
[Severity: Medium]
Does this call cause an out-of-bounds stack read in steam_recv_report()?
When a device sends a padded report that is larger than the caller's buffer
(like the 36-byte reply buffer in steam_get_serial()), steam_recv_report()
copies the correct size but logs using the unbounded ret length:
steam_recv_report() {
...
size = min(size, ret);
memcpy(data, buf + 1, size);
...
hid_dbg(steam->hdev, "Received report %*ph\n", ret, data);
...
}
Since ret can be larger than size, won't hid_dbg() read past the end of the
reply buffer on the stack?
[Severity: Medium]
Can this also result in reading uninitialized stack memory on short reads?
If the hardware sends a short report where hid_hw_raw_request() returns
0 or 1, memcpy() copies 0 bytes, leaving the caller's reply buffer
uninitialized.
steam_recv_report() {
...
if (ret < 2 || ret < data[1] + 2) {
hid_err(steam->hdev, "%s: expected %u bytes, read %i\n",
__func__, data[1] + 2, ret);
return -EPROTO;
}
}
When ret < 2, data[1] has not been populated. Won't this read and log an
uninitialized byte from the stack?
> + /*
> + * 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)
[Severity: Medium]
Will this retry logic cause log spam during normal operation?
Because steam_recv_report() unconditionally calls hid_err() before returning
-EPROTO:
steam_recv_report() {
...
if (ret < 2 || ret < data[1] + 2) {
hid_err(steam->hdev, "%s: expected %u bytes, read %i\n",
__func__, data[1] + 2, ret);
return -EPROTO;
}
}
Won't every expected retry here flood the kernel log with error messages?
> + 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;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=10
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015)
2026-07-07 23:32 ` [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015) Vicki Pfau
@ 2026-07-07 23:50 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:50 UTC (permalink / raw)
To: Vicki Pfau; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] If `steam_register()` fails during probe for a wired device, the `steam` struct is left on the global `steam_devices` list but its memory is freed, leading to a Use-After-Free.
--
--- Patch [4]: [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015) ---
commit d6e009f462fb9aec26d7157d6389455fb438d8e1
Author: Vicki Pfau <vi@endrift.com>
HID: steam: Add support for sensor events on the Steam Controller (2015)
Sensor support was added for the Steam Deck previously, but Steam Controller
sensor events were never added. This adds that missing support, bringing Steam
Controller support much closer to feature parity with things like SDL and Steam
itself.
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index add9f4f27fff9..6029c055a6cf3 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -968,6 +1077,12 @@ static int steam_register(struct steam_device *steam)
> strscpy(steam->serial_no, "XXXXXXXXXX",
> sizeof(steam->serial_no));
>
> + ret = steam_get_attributes(steam);
> + if (ret < 0)
> + hid_err(steam->hdev,
> + "%s:steam_get_attributes failed with error %d\n",
> + __func__, ret);
> +
> hid_info(steam->hdev, "Steam Controller '%s' connected",
> steam->serial_no);
>
[Severity: High]
This is a pre-existing issue, but can the error path in steam_probe() lead to
a use-after-free if steam_register() fails?
In steam_register(), the device is added to the global steam_devices list:
mutex_lock(&steam_devices_lock);
if (list_empty(&steam->list))
list_add(&steam->list, &steam_devices);
mutex_unlock(&steam_devices_lock);
If a subsequent registration step (like steam_input_register() or
steam_sensors_register()) fails, steam_register() returns an error without
removing the device from the list.
Then, back in steam_probe(), if steam_register() fails, it jumps to
err_hw_close, bypassing the unregister cleanup:
err_steam_unregister:
if (steam->connected)
steam_unregister(steam);
err_hw_close:
hid_hw_close(hdev);
Because the steam device was allocated with devm_kzalloc(), devres will
automatically free the structure upon probe failure. Does this leave a dangling
pointer in the global steam_devices list, which could cause memory corruption
when the list is later iterated (e.g., when setting the lizard_mode module
parameter)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened
2026-07-07 23:32 ` [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened Vicki Pfau
@ 2026-07-07 23:52 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-07 23:52 UTC (permalink / raw)
To: Vicki Pfau; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Medium] Memory leak of devm-allocated battery name if power_supply_register() fails.
Pre-existing issues:
- [High] Race condition in steam_remove leading to Use-After-Free of steam_device.
- [High] List corruption and Memory Leak when steam_register fails during probe.
- [High] Race condition in hidraw open/close callbacks leading to Use-After-Free.
- [High] TOCTOU race in steam_raw_event accessing steam->client_hdev during device removal.
--
--- Patch [6]: [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened ---
commit d361e686d46a1745e3d9d699aff5bb87aea155b9
Author: Vicki Pfau <vi@endrift.com>
HID: steam: Fully unregister controller when hidraw is opened
This patch changes the driver to fully unregister the controller internally
when hidraw is opened, rather than just detaching the evdev nodes. This
also unifies the unregister and connect callbacks.
> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index 5537488139011..3f1dbdcad23c8 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -1077,6 +1076,7 @@ static void steam_battery_unregister(struct steam_device *steam)
> RCU_INIT_POINTER(steam->battery, NULL);
> synchronize_rcu();
> power_supply_unregister(battery);
> + devm_kfree(&steam->hdev->dev, steam->battery_desc.name);
> }
[Severity: Medium]
If power_supply_register() fails in steam_battery_register(), steam->battery
is not set. When this unregister function is called, an early return on
!battery could skip this newly added devm_kfree():
steam_battery_unregister()
{
...
if (!battery)
return;
...
devm_kfree(&steam->hdev->dev, steam->battery_desc.name);
}
Since this unregister function is now dynamically triggered every time the
hidraw node is opened, could an unprivileged user repeatedly open and close
the hidraw node to continuously leak the devm-allocated string?
[ ... ]
> @@ -1277,7 +1255,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
> steam->client_opened++;
> spin_unlock_irqrestore(&steam->lock, flags);
>
> - schedule_work(&steam->unregister_work);
> + schedule_work(&steam->work_connect);
>
> return 0;
> }
[Severity: High]
This isn't a bug introduced by this patch, but releasing the lock here before
scheduling work appears to open a race window:
steam_client_ll_open()
{
...
spin_unlock_irqrestore(&steam->lock, flags);
schedule_work(&steam->work_connect);
...
}
If the thread is preempted immediately after releasing the lock, and the
physical device is removed concurrently, steam_remove() could execute, free
the steam structure, and leave the resumed schedule_work() call to access
freed memory. Is there a way to safely synchronize this against device
removal?
[ ... ]
> @@ -1389,7 +1367,6 @@ static int steam_probe(struct hid_device *hdev,
> steam->sensor_update_rate_us = 4000;
> else
> steam->sensor_update_rate_us = 9000;
> - INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
[Severity: High]
This is a pre-existing issue, but I wanted to ask about the error handling
path in steam_probe() since it is closely related to driver registration:
steam_probe()
{
...
ret = steam_register(steam);
if (ret) {
hid_err(hdev, ...);
goto err_hw_close;
}
...
}
If steam_register() adds the device to the steam_devices list, but a
subsequent step like steam_input_register() fails, steam_register() returns
an error but leaves the device on the global list.
Since the err_hw_close path bypasses unregistering, could this lead to a
use-after-free when the lizard_mode module parameter later traverses the
steam_devices list?
[ ... ]
> @@ -1470,7 +1446,6 @@ static void steam_remove(struct hid_device *hdev)
> cancel_work_sync(&steam->work_connect);
> cancel_work_sync(&steam->rumble_work);
> cancel_delayed_work_sync(&steam->coalesce_rumble_work);
> - cancel_work_sync(&steam->unregister_work);
> steam->client_hdev = NULL;
> steam->client_opened = 0;
[Severity: High]
This is a pre-existing issue, but does calling cancel_work_sync() here before
stopping the hardware leave a race window?
steam_remove()
{
...
cancel_work_sync(&steam->work_connect);
...
} // hid_hw_stop() is called later
Because the HID hardware is still active, could an incoming connection event
interrupt trigger steam_raw_event() and re-schedule work_connect after it was
canceled?
Once steam_remove() completes and frees the steam object, this might cause the
workqueue to execute the asynchronous callback using the freed steam structure.
[Severity: High]
This is also a pre-existing issue in the raw event handler, but could this
unlocked check lead to a time-of-check to time-of-use race?
steam_raw_event()
{
...
if (steam->client_opened)
hid_input_report(steam->client_hdev, ...);
...
}
If the thread is preempted after reading a non-zero client_opened value, and
the physical device is concurrently unplugged, steam_remove() could run and
destroy client_hdev.
When steam_raw_event() resumes, will it attempt to call hid_input_report()
with the freed steam->client_hdev pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707233241.2644971-1-vi@endrift.com?part=6
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-07-07 23:52 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07 23:32 [PATCH v2 00/10] HID: steam: General cleanup and improvements Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 01/10] HID: steam: Update documentation Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 02/10] HID: steam: Refactor and clean up report parsing Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 03/10] HID: steam: Rename some constants that got renamed upstream Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 04/10] HID: steam: Add support for sensor events on the Steam Controller (2015) Vicki Pfau
2026-07-07 23:50 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 05/10] HID: steam: Coalesce rumble packets Vicki Pfau
2026-07-07 23:46 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 06/10] HID: steam: Fully unregister controller when hidraw is opened Vicki Pfau
2026-07-07 23:52 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 07/10] HID: steam: Rearrange deinitialization sequence Vicki Pfau
2026-07-07 23:32 ` [PATCH v2 08/10] HID: steam: Improve logging and other cleanup Vicki Pfau
2026-07-07 23:47 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 09/10] HID: steam: Reject short reads Vicki Pfau
2026-07-07 23:47 ` sashiko-bot
2026-07-07 23:32 ` [PATCH v2 10/10] HID: steam: Retry send/recv reports if stale Vicki Pfau
2026-07-07 23:48 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox