Re: [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace

From: Lovekesh Solanki

Date: Sun Sep 20 2026 - 14:37:02 EST


Hi thanks for the report,

On Sun, Sep 20, 2026 at 11:44:39AM +0200, Roman Stingler wrote:
> I have not bisected this, but I believe the cause is clear from inspection.
>
> Before 022eb347ff3a4 ("HID: logitech: add Bolt receiver support for Logitech
> HID++ devices"), USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER was not present in
> logi_dj_receivers[] -- it appeared only in hid-quirks.c and hid-multitouch.c.
> So a Bolt-connected mouse never became a HID_GROUP_LOGITECH_DJ_DEVICE child,
> hid-logitech-hidpp never bound to it, and the kernel never touched HID++
> feature 0x2121. The device was driven by hid-generic and userspace was the
> only writer of the wheel mode, so the setting stuck.
>
> With Bolt support in place the mouse is now a hid-logitech-hidpp device:
>
> logitech-djreceiver 0003:046D:C548.0007: device of type Bolt (0x10) connected on slot 2
> input: Logitech Wireless Mouse PID:b042 Mouse as /devices/.../0003:046D:C548.0007/0003:046D:B042.0009/input/input22
> logitech-hidpp-device 0003:046D:B042.0009: input,hidraw7: USB HID v1.11 Mouse [Logitech Wireless Mouse PID:b042] on usb-0000:c5:00.4-1.3.2.4/input2:2
> logitech-hidpp-device 0003:046D:B042.0009: HID++ 4.5 device connected.
>
> and every reconnect now runs hidpp_connect_event(), which unconditionally
> does:
>
> if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
> hi_res_scroll_enable(hidpp);
>
> and hi_res_scroll_enable() in turn does:
>
> ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
> /* invert ^ ^ high_resolution */
>
> with high_resolution hard-coded to true. There is no record of a user
> preference and nothing consults the device's current mode, so any userspace
> choice is discarded at connect time.
I think this does look like the issue.

We could store this info in hidpp_device struct and use in hi_res_scroll_enable().
I'm pasting a patch below, could you give it a go?

>
> Possibly related
> ================
>
> On this same Bolt topology the wheel also scrolls far too far per detent,
> apparently because hid-logitech-dj does not forward the hi-res wheel reports
> to hid-logitech-hidpp, so the multiplier-15 steps reach userspace unscaled.
> There is an out-of-tree DKMS workaround for exactly this WPID:
>
> https://github.com/Magnetar-OS/logitech-bolt-hidpp-dkms
>
> I am reporting only the mode-reset problem here, but the two look like
> neighbouring consequences of the same commit and may be worth considering
> together.
They do, but I think this should be treated as a seperate bug. The author of
those patches hasn't sent them upstream.. hopefully they do.

Thanks,
Lovekesh

---------patch here---------

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 1504de32b1c8..6e3d717827ea 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -8,6 +8,7 @@
*/


+#include "linux/stddef.h"
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/device.h>
@@ -213,6 +214,8 @@ struct hidpp_device {

int hires_wheel_multiplier;
u8 hires_wheel_feature_index;
+ u8 hires_wheel_mode;
+ bool hires_wheel_mode_seen;

bool connected_once;
};
@@ -3910,9 +3913,24 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp)
u8 multiplier = 1;

if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL) {
- ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
- if (ret == 0)
- ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
+ bool invert = false;
+ bool high_resolution = true;
+
+ if(hidpp->hires_wheel_mode_seen){
+ invert = hidpp->hires_wheel_mode & BIT(2);
+ high_resolution = hidpp->hires_wheel_mode & BIT(1);
+ }
+
+ ret = hidpp_hrw_set_wheel_mode(hidpp, invert, high_resolution, false);
+ if (ret == 0) {
+ if(high_resolution){
+ ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
+ }
+ else {
+ multiplier = 1;
+ }
+ }
+
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL) {
ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
&multiplier);
@@ -3992,6 +4010,8 @@ static int hidpp20_hires_wheel_raw_event(struct hidpp_device *hidpp,
if ((data[3] & 0xf0) == CMD_HIRES_WHEEL_SET_WHEEL_MODE) {
u8 mode = data[4];
bool hires = (mode & 0x02) != 0;
+ hidpp->hires_wheel_mode = mode;
+ hidpp->hires_wheel_mode_seen = true;
int new_multiplier = (hires && hidpp->hires_wheel_multiplier > 0)
? hidpp->hires_wheel_multiplier : 1;
hidpp->vertical_wheel_counter.wheel_multiplier = new_multiplier;