mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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; 4+ 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] 4+ 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; 4+ 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] 4+ 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
  2026-09-20 21:24     ` Erik Håkansson
  0 siblings, 1 reply; 4+ 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] 4+ 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 20:50   ` Roman Stingler
@ 2026-09-20 21:24     ` Erik Håkansson
  0 siblings, 0 replies; 4+ messages in thread
From: Erik Håkansson @ 2026-09-20 21:24 UTC (permalink / raw)
  To: Roman Stingler, Lovekesh Solanki
  Cc: Jiri Kosina, Benjamin Tissoires, Filipe Lains, Bastien Nocera,
	linux-input, linux-kernel, regressions

Hi!

> 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.

There are two different patches that aim to solve this, but it looks like the
dkms fix is a bit more robust.
https://lore.kernel.org/linux-input/20260901-bolt-scroll-fix-v1-1-58bca7ae487f@protonmail.com/
https://lore.kernel.org/linux-input/20260904034843.1340846-1-rafael@rcpassos.me/

I think the best approach would probably be to apply Rafael's patch and see
if there's anything from the dkms patches that's worth including too.

Regards,
Erik


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-20 21:24 UTC | newest]

Thread overview: 4+ 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
2026-09-20 21:24     ` Erik Håkansson

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®