From: "Erik Håkansson" <erikhakan@gmail.com>
To: "Filipe Laíns" <lains@riseup.net>,
"Jiri Kosina" <jikos@kernel.org>,
"Benjamin Tissoires" <bentiss@kernel.org>,
"Bastien Nocera" <hadess@hadess.net>
Cc: "Rafael Passos" <rafael@rcpassos.me>,
"Grégoire Stein" <greyxor@protonmail.com>,
"Alexey Zagorodnikov" <xglooom@gmail.com>,
"Oleksandr Natalenko" <oleksandr@natalenko.name>,
"Roman Stingler" <roman.stingler@gmail.com>,
"Lovekesh Solanki" <lovekeshsolanki00@gmail.com>,
"Kateřina Medvědová" <k8ie@mcld.eu>,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
"Erik Håkansson" <erikhakan@gmail.com>
Subject: [PATCH RFC 4/4] HID: logitech-hidpp: support ratchet wheel
Date: Fri, 02 Oct 2026 00:23:38 +0200 [thread overview]
Message-ID: <20261002-bolt-input-rfc-v1-4-333e1f350586@gmail.com> (raw)
In-Reply-To: <20261002-bolt-input-rfc-v1-0-333e1f350586@gmail.com>
Detect the 0x2130 Ratchet wheel feature and handle its HID++ scroll events.
Enable HID++ wheel diversion by default for Bolt-connected devices, but
leave other devices unchanged.
Signed-off-by: Erik Håkansson <erikhakan@gmail.com>
---
drivers/hid/hid-logitech-hidpp.c | 101 +++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 9217ad4a97f4..6195080505cb 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -101,6 +101,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL BIT(9)
#define HIDPP_CAPABILITY_ADC_MEASUREMENT BIT(10)
#define HIDPP_CAPABILITY_HIDPP20_THUMBWHEEL BIT(11)
+#define HIDPP_CAPABILITY_HIDPP20_RATCHET_WHEEL BIT(12)
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
@@ -225,6 +226,9 @@ struct hidpp_device {
u8 hires_wheel_feature_index;
bool hires_wheel_has_invert;
bool hires_wheel_invert;
+
+ u8 ratchet_wheel_feature_index;
+
u8 thumbwheel_feature_index;
bool thumbwheel_positive_right;
@@ -2131,6 +2135,27 @@ static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
params, sizeof(params), &response);
}
+/* -------------------------------------------------------------------------- */
+/* 0x2130: Ratchet Wheel */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_RATCHET_WHEEL 0x2130
+#define CMD_RATCHET_WHEEL_SET_WHEEL_MODE 0x10
+#define EVENT_RATCHET_WHEEL_MOVEMENT 0x00
+#define RATCHET_WHEEL_MODE_USE_HIDPP BIT(0)
+
+static int hidpp_ratchet_wheel_set_mode(struct hidpp_device *hidpp,
+ bool use_hidpp)
+{
+ struct hidpp_report response;
+ u8 mode = use_hidpp ? RATCHET_WHEEL_MODE_USE_HIDPP : 0;
+
+ return hidpp_send_fap_command_sync(hidpp,
+ hidpp->ratchet_wheel_feature_index,
+ CMD_RATCHET_WHEEL_SET_WHEEL_MODE,
+ &mode, sizeof(mode), &response);
+}
+
/* -------------------------------------------------------------------------- */
/* 0x2150: Thumbwheel */
/* -------------------------------------------------------------------------- */
@@ -4105,6 +4130,35 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp)
return 0;
}
+static void ratchet_wheel_scroll_enable(struct hidpp_device *hidpp)
+{
+ int ret;
+
+ if (!hidpp_is_bolt_child(hidpp->hid_dev))
+ return;
+
+ ret = hidpp_ratchet_wheel_set_mode(hidpp, true);
+ if (ret)
+ hid_dbg(hidpp->hid_dev,
+ "Could not divert ratchet wheel reports: %d\n", ret);
+}
+
+static void hidpp_initialize_ratchet_wheel(struct hidpp_device *hidpp)
+{
+ int ret;
+
+ hidpp->capabilities &= ~HIDPP_CAPABILITY_HIDPP20_RATCHET_WHEEL;
+ hidpp->ratchet_wheel_feature_index = 0xff;
+ if (hidpp->protocol_major < 2)
+ return;
+
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_RATCHET_WHEEL,
+ &hidpp->ratchet_wheel_feature_index);
+ if (ret)
+ return;
+ hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_RATCHET_WHEEL;
+}
+
static void thumbwheel_scroll_enable(struct hidpp_device *hidpp)
{
bool divert;
@@ -4213,6 +4267,44 @@ static int hidpp20_hires_wheel_raw_event(struct hidpp_device *hidpp,
return 0;
}
+static int hidpp20_ratchet_wheel_raw_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ int delta_v, delta_h;
+
+ if (hidpp->ratchet_wheel_feature_index == 0xff)
+ return 0;
+
+ if (size < 6)
+ return 0;
+
+ if (data[0] != REPORT_ID_HIDPP_LONG ||
+ data[2] != hidpp->ratchet_wheel_feature_index)
+ return 0;
+
+ if (data[3] != EVENT_RATCHET_WHEEL_MOVEMENT)
+ return 0;
+
+ if (!hidpp->input)
+ return 1;
+
+ delta_v = (s8)data[4];
+ delta_h = (s8)data[5];
+ if (delta_v) {
+ input_report_rel(hidpp->input, REL_WHEEL_HI_RES, delta_v * 120);
+ input_report_rel(hidpp->input, REL_WHEEL, delta_v);
+ }
+ if (delta_h) {
+ input_report_rel(hidpp->input, REL_HWHEEL_HI_RES,
+ delta_h * 120);
+ input_report_rel(hidpp->input, REL_HWHEEL, delta_h);
+ }
+ if (delta_v || delta_h)
+ input_sync(hidpp->input);
+
+ return 1;
+}
+
static int hidpp20_thumbwheel_raw_event(struct hidpp_device *hidpp,
u8 *data, int size)
{
@@ -4480,6 +4572,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
return ret;
}
+ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_RATCHET_WHEEL) {
+ ret = hidpp20_ratchet_wheel_raw_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_THUMBWHEEL) {
ret = hidpp20_thumbwheel_raw_event(hidpp, data, size);
if (ret != 0)
@@ -4828,6 +4926,7 @@ static void hidpp_connect_event(struct work_struct *work)
hidpp_initialize_battery(hidpp);
if (!hid_is_usb(hidpp->hid_dev))
hidpp_initialize_hires_scroll(hidpp);
+ hidpp_initialize_ratchet_wheel(hidpp);
hidpp_initialize_thumbwheel_scroll(hidpp);
/* forward current battery state */
@@ -4852,6 +4951,8 @@ static void hidpp_connect_event(struct work_struct *work)
if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
hi_res_scroll_enable(hidpp);
+ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_RATCHET_WHEEL)
+ ratchet_wheel_scroll_enable(hidpp);
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_THUMBWHEEL)
thumbwheel_scroll_enable(hidpp);
--
2.55.0
prev parent reply other threads:[~2026-10-01 22:23 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 22:23 [PATCH RFC 0/4] HID: logitech: add Bolt support with HID++ input handling Erik Håkansson
2026-10-01 22:23 ` [PATCH RFC 1/4] HID: logitech: add Bolt receiver support Erik Håkansson
2026-10-01 22:23 ` [PATCH RFC 2/4] HID: logitech: handle HID++ thumbwheel reports Erik Håkansson
2026-10-01 22:23 ` [PATCH RFC 3/4] HID: logitech: divert M650 side buttons over Bolt Erik Håkansson
2026-10-01 22:23 ` Erik Håkansson [this message]
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=20261002-bolt-input-rfc-v1-4-333e1f350586@gmail.com \
--to=erikhakan@gmail.com \
--cc=bentiss@kernel.org \
--cc=greyxor@protonmail.com \
--cc=hadess@hadess.net \
--cc=jikos@kernel.org \
--cc=k8ie@mcld.eu \
--cc=lains@riseup.net \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lovekeshsolanki00@gmail.com \
--cc=oleksandr@natalenko.name \
--cc=rafael@rcpassos.me \
--cc=roman.stingler@gmail.com \
--cc=xglooom@gmail.com \
/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®