From: Roman Stingler <roman.stingler@gmail.com>
To: Lovekesh Solanki <lovekeshsolanki00@gmail.com>
Cc: Roman Stingler <roman.stingler@gmail.com>,
Jiri Kosina <jikos@kernel.org>,
Benjamin Tissoires <bentiss@kernel.org>,
Erik Hakansson <erikhakan@gmail.com>,
Filipe Lains <lains@riseup.net>,
Bastien Nocera <hadess@hadess.net>,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
regressions@lists.linux.dev
Subject: Re: [REGRESSION 7.3-rc1] HID: logitech-hidpp: hi-res scroll mode forcibly re-enabled on every reconnect for Bolt devices, overriding userspace
Date: Sun, 20 Sep 2026 22:50:53 +0200 [thread overview]
Message-ID: <20260920205058.11958-1-roman.stingler@gmail.com> (raw)
In-Reply-To: <arAe2iRkYh9p98Rf@eggarch>
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
next prev parent reply other threads:[~2026-09-20 20:53 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-20 9:44 Roman Stingler
2026-09-20 18:31 ` Lovekesh Solanki
2026-09-20 20:50 ` Roman Stingler [this message]
2026-09-20 21:24 ` Erik Håkansson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260920205058.11958-1-roman.stingler@gmail.com \
--to=roman.stingler@gmail.com \
--cc=bentiss@kernel.org \
--cc=erikhakan@gmail.com \
--cc=hadess@hadess.net \
--cc=jikos@kernel.org \
--cc=lains@riseup.net \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lovekeshsolanki00@gmail.com \
--cc=regressions@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®