From f22a5db8a7d38152556f230d6d68e59dbc27971b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 17:01:50 -0700 Subject: [PATCH 1/2] HID: logitech-hidpp: remove excess kernel-doc member in hidpp_scroll_counter The @dev member described in the kernel-doc does not exist in the struct. Remove the stale entry. Fixes: 0610430e3dea ("HID: logitech-hidpp: add input_device ptr to struct hidpp_device") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-logitech-hidpp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index ccbf28869a96..1990ba5b26ea 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -164,7 +164,6 @@ struct hidpp_battery { /** * struct hidpp_scroll_counter - Utility class for processing high-resolution * scroll events. - * @dev: the input device for which events should be reported. * @wheel_multiplier: the scalar multiplier to be applied to each wheel event * @remainder: counts the number of high-resolution units moved since the last * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should From f0866517be9345d8245d32b722574b8aecccb348 Mon Sep 17 00:00:00 2001 From: Lauri Saurus Date: Mon, 18 May 2026 19:28:50 +0000 Subject: [PATCH 2/2] HID: logitech-hidpp: sync wheel multiplier on wheel mode changes The hid-logitech-hidpp driver enables high resolution scrolling on device connect for capable HID++ 2.0 devices. Driver also reads the wheel capability and caches the returned high resolution wheel scroll multiplier, that is used for scroll scaling when handling wheel scroll events. Wheel mode can also be set externally through HID++ requests, which can leave the cached multiplier stale and cause incorrect scroll scaling. If external SetWheelMode HID++ request sets the mode to low resolution, the cached multiplier is not updated accordingly. This causes extremely slow scrolling since driver expects multiple wheel scroll events per detent but is only getting one. The fix listens for HID++ SetWheelMode request responses and updates the wheel scroll multiplier based on the set high resolution scroll mode. The fix has been tested with Logitech G502X lightspeed mouse. Signed-off-by: Lauri Saurus Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 1990ba5b26ea..70ba1a5e40d8 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -206,6 +206,9 @@ struct hidpp_device { u8 wireless_feature_index; + int hires_wheel_multiplier; + u8 hires_wheel_feature_index; + bool connected_once; }; @@ -3709,6 +3712,7 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp) multiplier = 1; } + hidpp->hires_wheel_multiplier = multiplier; hidpp->vertical_wheel_counter.wheel_multiplier = multiplier; hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier); return 0; @@ -3719,6 +3723,7 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) int ret; unsigned long capabilities; + hidpp->hires_wheel_feature_index = 0xff; capabilities = hidpp->capabilities; if (hidpp->protocol_major >= 2) { @@ -3728,6 +3733,7 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) &feature_index); if (!ret) { hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL; + hidpp->hires_wheel_feature_index = feature_index; hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scroll wheel\n"); return 0; } @@ -3750,6 +3756,31 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) return 0; } +static int hidpp20_hires_wheel_raw_event(struct hidpp_device *hidpp, + u8 *data, int size) +{ + if (hidpp->hires_wheel_feature_index == 0xff) + return 0; + + if (size < 5) + return 0; + + if (data[0] != REPORT_ID_HIDPP_LONG || + data[2] != hidpp->hires_wheel_feature_index) + return 0; + + if ((data[3] & 0xf0) == CMD_HIRES_WHEEL_SET_WHEEL_MODE) { + u8 mode = data[4]; + bool hires = (mode & 0x02) != 0; + int new_multiplier = (hires && hidpp->hires_wheel_multiplier > 0) + ? hidpp->hires_wheel_multiplier : 1; + hidpp->vertical_wheel_counter.wheel_multiplier = new_multiplier; + return 1; + } + + return 0; +} + /* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -3946,6 +3977,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return ret; } + if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL) { + ret = hidpp20_hires_wheel_raw_event(hidpp, data, size); + if (ret != 0) + return ret; + } + return 0; }