mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


      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®