* [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace
@ 2026-09-20 9:44 Roman Stingler
2026-09-20 18:31 ` Lovekesh Solanki
0 siblings, 1 reply; 3+ messages in thread
From: Roman Stingler @ 2026-09-20 9:44 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Roman Stingler, Erik Hakansson, Filipe Lains, Bastien Nocera,
linux-input, linux-kernel, regressions
Hi,
Since Bolt receiver support was added in 7.3-rc1, the user-selected
"Scroll Wheel Resolution" (HID++ feature 0x2121 HiRes Wheel) setting on my
MX Master 4 is reset by the kernel every time the device reconnects, most
visibly across every suspend/resume cycle. I disable hi-res scrolling in
Solaar, suspend the laptop, and on resume the mouse is back in hi-res mode
and I have to toggle it manually again.
This worked correctly on 7.2 and is broken on 7.3-rc1 through 7.3-rc3.
#regzbot introduced: 022eb347ff3a48281e7e69c3addcb11bf24afa53
Hardware
========
Logi Bolt Receiver 046d:c548
Logitech MX Master 4 (WPID B042), HID++ 4.5, paired to the Bolt receiver
HIRES WHEEL {2121} V1, multiplier 15
Host: HP OmniBook Ultra Laptop 14-fd0xxx,
AMD Ryzen AI 9 HX 375 (family 0x1a model 0x24)
Distro kernel: 7.3.0-rc3-1-cachyos-rc
Solaar 1.1.20
Reproduction
============
1. Pair an MX Master 4 (or other 0x2121-capable mouse) to a Bolt receiver.
2. In Solaar, set "Scroll Wheel Resolution" to off
(solaar config 2 hires-smooth-resolution false).
3. Suspend and resume (s2idle here, but any reconnect does it -- turning
the mouse off and on again is enough).
4. The setting is back on.
Solaar shows the divergence between what the user asked for and what the
device is actually in:
18: HIRES WHEEL {2121} V1
Multiplier: 15
Has invert: Normal wheel motion
Has ratchet switch: Normal wheel mode
High resolution mode
HID notification
Scroll Wheel Direction (saved): False
Scroll Wheel Direction : False
Scroll Wheel Resolution (saved): False
Scroll Wheel Resolution : True
Scroll Wheel Diversion (saved): False
Scroll Wheel Diversion : False
"(saved)" is what I configured; the live value has been overwritten.
Analysis
========
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.
Note this is not a suspend/resume bug as such -- hidpp_driver has no .resume
or .reset_resume callback. The reconnect is driven purely by the receiver
re-announcing the device after USB resume, which queues hidpp_connect_event
via hidpp_report_is_connect_event().
The same call hard-codes invert = false, so "Scroll Wheel Direction" is reset
by the same path for anyone who inverts their wheel.
I realise hid-logitech-hidpp has always enabled hi-res scrolling at connect
for devices it drives, and that Unifying users have lived with this for
years. What changed in 7.3 is the set of devices this applies to: Bolt
devices were previously outside the driver's reach and are now inside it, so
for those users this is a user-visible behavioural regression.
Commit f0866517be934 ("HID: logitech-hidpp: sync wheel multiplier on wheel
mode changes") in 7.2 already added hidpp20_hires_wheel_raw_event() to notice
an external SetWheelMode and re-sync the cached multiplier. The driver
therefore sees userspace changing the mode; it just does not remember the
choice across a reconnect. Persisting the last observed mode and restoring
that in hi_res_scroll_enable(), rather than unconditionally forcing hi-res,
would fix this. I am happy to test any patch.
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.
Thanks,
Roman
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace 2026-09-20 9:44 [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace Roman Stingler @ 2026-09-20 18:31 ` Lovekesh Solanki 2026-09-20 20:50 ` Roman Stingler 0 siblings, 1 reply; 3+ messages in thread From: Lovekesh Solanki @ 2026-09-20 18:31 UTC (permalink / raw) To: Roman Stingler Cc: Jiri Kosina, Benjamin Tissoires, Erik Hakansson, Filipe Lains, Bastien Nocera, linux-input, linux-kernel, regressions 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; ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace 2026-09-20 18:31 ` Lovekesh Solanki @ 2026-09-20 20:50 ` Roman Stingler 0 siblings, 0 replies; 3+ messages in thread From: Roman Stingler @ 2026-09-20 20:50 UTC (permalink / raw) To: Lovekesh Solanki Cc: Roman Stingler, Jiri Kosina, Benjamin Tissoires, Erik Hakansson, Filipe Lains, Bastien Nocera, linux-input, linux-kernel, regressions On Mon, Sep 21, 2026 at 12:01:03AM +0530, Lovekesh Solanki wrote: > 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? Thanks. Your patch does fix the case I reported: Tested-by: Roman Stingler <roman.stingler@gmail.com> MX Master 4 (WPID B042) on a Bolt receiver, 7.3.0-rc3, patched driver installed via DKMS so it is the module actually loaded at boot. Set the wheel to low resolution, suspend (s2idle), resume: still low resolution. Before the patch that always came back hi-res. It also fixes more than suspend -- on the unpatched kernel even "solaar config <serial> hires-smooth-resolution false" read back True within a second, because Solaar's own HID++ ping provokes a connect event. (The patch as posted also adds #include "linux/stddef.h" above the pr_fmt definition, which looks like an editor artifact. I dropped it; the rest I applied verbatim.) But while testing I found something that I think makes this the wrong layer to fix it at. The wheel mode is persistent state in the device ================================================ I unloaded hid-logitech-hidpp completely, with an install override so udev could not bring it back, set the wheel to low resolution from userspace, switched the mouse off at the power switch for ten seconds, and switched it back on. It came back in low resolution mode. So 0x2121 wheel mode is not volatile on this hardware. The mouse holds it by itself, with no driver and no daemon in the picture. That is precisely why this worked on 7.2: nothing in the kernel ever wrote the setting, so a user could configure the mouse once and keep that configuration across reboots -- even with the configuration tool uninstalled afterwards. What 7.3 changed is that the kernel now overwrites that persistent device state on every probe. Remembering the mode in hidpp_device cannot close the gap ========================================================= struct hidpp_device is allocated at probe and freed at unbind, so hires_wheel_mode_seen is false again on every probe. I hit that three ways with your patch installed: - cold boot - unplugging and replugging the Bolt receiver - a plain module reload In each case the driver forces hi-res before userspace has said anything, and the persistent setting is gone. Gating on connected_once instead -- which I had considered suggesting -- has the same flaw, since "first connect" also resets per probe. The problem is not which connect the driver writes on. It is that it writes at all. Userspace does not reliably repair it either. Solaar skips its apply here: for devices exposing WIRELESS_DEVICE_STATUS it goes through a ConfigChange cookie check, the cookie still matches what it stored (the driver's SetWheelMode does not bump it), so it concludes nothing needs applying. I will report that to Solaar separately. But it should not have to be load-bearing -- the device already holds the setting. Proposal: read the mode instead of writing it ============================================= 0x2121 exposes getWheelMode (function 1) next to setWheelMode; the driver currently uses only getWheelCapability and setWheelMode. If hi_res_scroll_enable() reads the current mode and scales the wheel multiplier to match, the driver gets what it needs for REL_WHEEL_HI_RES without touching state it does not own -- and there is no mode to remember across probes. diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 1504de32b1c8..ffc7cef49b59 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -2044,6 +2044,7 @@ static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp, #define HIDPP_PAGE_HIRES_WHEEL 0x2121 #define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00 +#define CMD_HIRES_WHEEL_GET_WHEEL_MODE 0x10 #define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20 static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, @@ -2072,12 +2073,10 @@ static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, return ret; } -static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, - bool high_resolution, bool use_hidpp) +static int hidpp_hrw_get_wheel_mode(struct hidpp_device *hidpp, u8 *mode) { u8 feature_index; int ret; - u8 params[1]; struct hidpp_report response; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, @@ -2085,13 +2084,14 @@ static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, if (ret) return ret; - params[0] = (invert ? BIT(2) : 0) | - (high_resolution ? BIT(1) : 0) | - (use_hidpp ? BIT(0) : 0); + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_HIRES_WHEEL_GET_WHEEL_MODE, + NULL, 0, &response); + if (ret) + return ret; - return hidpp_send_fap_command_sync(hidpp, feature_index, - CMD_HIRES_WHEEL_SET_WHEEL_MODE, - params, sizeof(params), &response); + *mode = response.fap.params[0]; + return 0; } /* -------------------------------------------------------------------------- */ @@ -3910,8 +3910,16 @@ 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) + u8 mode; + + /* + * The wheel mode is persistent state in the device, so read it + * rather than overwriting it, and scale to match. A device + * left in hi-res still gets the multiplier it needs; one the + * user configured for low resolution is left alone. + */ + ret = hidpp_hrw_get_wheel_mode(hidpp, &mode); + if (ret == 0 && (mode & BIT(1))) ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier); } else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL) { ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true, Tested on top of 7.3-rc3, installed via DKMS so it is the module loaded at boot. Results against the same device: stock your patch this suspend/resume no yes yes solaar write sticks no yes yes module reload no no yes cold boot no no yes The hi-res direction still works: with the mouse left in hi-res, a full module reload leaves it in hi-res and the driver fetches the multiplier through the same getWheelCapability call as before. I checked the mode is honoured; I did not instrument events-per-detent, though that path is unchanged from the current code. The deliberate behaviour change is that a device sitting at its factory default in low resolution will no longer be switched into hi-res by the kernel. If that is unacceptable, an opt-out, or writing hi-res only on the very first enumeration of a device the driver has never seen, would both keep today's default while leaving a configured device alone. I did not want to guess which of those you would prefer, so the diff above is the simple form. For the immediate regression I still think your patch should go in -- it is a clear improvement and it fixes the reported case. I just think the setting ultimately belongs to the device and the user, not to the driver. Happy to respin the above as a proper patch with a commit message if it looks like the right direction, and happy to test anything else. Thanks, Roman ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-20 20:53 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-20 9:44 [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace Roman Stingler 2026-09-20 18:31 ` Lovekesh Solanki 2026-09-20 20:50 ` Roman Stingler
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®