linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] hid-logitech-hidpp: holdable_thumb_buttons parameter for Logitech MX Anywhere 3
@ 2022-10-03 10:29 Tim Schumacher
  0 siblings, 0 replies; only message in thread
From: Tim Schumacher @ 2022-10-03 10:29 UTC (permalink / raw)
  To: linux-input; +Cc: Filipe Laíns

On the Logitech MX Anywhere 3 the thumb buttons only activate on release.
This is because the mouse also uses those buttons as modifiers to enable
horizontal scrolling with the mouse wheel. This patch adds the
holdable_thumb_buttons module parameter. Users who don't care about
horizontal scrolling and want properly functioning thumb buttons can set
this parameter. If it's set we use the feature 0x1b04 (special keys and
mouse buttons) to divert thumb button events and handle them in software.

Signed-off-by: Tim Schumacher <tim@tschumacher.net>
---

Thanks for your feedback Bastien! I made the following changes:

* Gate setting by feature 0x1b04 instead of specific device ID
* Style fixes and no magic numbers

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 81de88ab2..25af6aaca 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -41,6 +41,11 @@ module_param(disable_tap_to_click, bool, 0644);
  MODULE_PARM_DESC(disable_tap_to_click,
  	"Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently).");
  
+static bool holdable_thumb_buttons;
+module_param(holdable_thumb_buttons, bool, 0644);
+MODULE_PARM_DESC(holdable_thumb_buttons,
+	"Make it possible to hold down the thumb buttons (tested only on the MX Anywhere 3).");
+
  #define REPORT_ID_HIDPP_SHORT			0x10
  #define REPORT_ID_HIDPP_LONG			0x11
  #define REPORT_ID_HIDPP_VERY_LONG		0x12
@@ -204,6 +209,7 @@ struct hidpp_device {
  	struct hidpp_scroll_counter vertical_wheel_counter;
  
  	u8 wireless_feature_index;
+	u8 special_km_btns_feature_index;
  };
  
  /* HID++ 1.0 error codes */
@@ -1726,6 +1732,25 @@ static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
  	return ret;
  }
  
+/* -------------------------------------------------------------------------- */
+/* 0x1b04: Special keys and mouse buttons                                     */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_SPECIAL_KM_BTNS				0x1b04
+
+static int hidpp_set_special_km_btns_feature_index(struct hidpp_device *hidpp)
+{
+	u8 feature_type;
+	int ret;
+
+	ret = hidpp_root_get_feature(hidpp,
+				     HIDPP_PAGE_SPECIAL_KM_BTNS,
+				     &hidpp->special_km_btns_feature_index,
+				     &feature_type);
+
+	return ret;
+}
+
  /* -------------------------------------------------------------------------- */
  /* 0x2120: Hi-resolution scrolling                                            */
  /* -------------------------------------------------------------------------- */
@@ -3122,6 +3147,79 @@ static int k400_connect(struct hid_device *hdev, bool connected)
  	return k400_disable_tap_to_click(hidpp);
  }
  
+/* ------------------------------------------------------------------------- */
+/* Holdable thumb buttons                                                    */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * On the Logitech MX Anywhere 3 the thumb buttons only active on release.
+ * This is because the mouse also uses those buttons as modifiers to enable
+ * horizontal scrolling with the mouse wheel.
+ *
+ * Users who don't care about horizontal scrolling and want properly
+ * functioning thumb buttons can set the holdable_thumb_buttons parameter.
+ * If it's set we use the feature 0x1b04 (special keys and mouse buttons)
+ * to divert thumb button events and handle them in software.
+ */
+
+#define CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING		0x31
+#define EVENT_SPECIAL_KM_BTNS_DIVERTED_BTNS_EVENT	0x00
+
+#define CID_BTN_SIDE	0x53
+#define CID_BTN_EXTRA	0x56
+
+static int holdable_thumb_buttons_connect(struct hidpp_device *hidpp)
+{
+	u8 feature_type;
+	int ret;
+	u8 params[5] = { 0 };
+	struct hidpp_report response;
+
+	params[1] = CID_BTN_SIDE;
+	params[2] = BIT(1) | BIT(0); /* dvalid=1 divert=1 */
+	ret = hidpp_send_fap_command_sync(hidpp,
+					  hidpp->special_km_btns_feature_index,
+					  CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING,
+					  params, sizeof(params), &response);
+
+	if (ret)
+		return ret;
+
+	params[1] = CID_BTN_EXTRA;
+	return hidpp_send_fap_command_sync(hidpp,
+					   hidpp->special_km_btns_feature_index,
+					   CMD_SPECIAL_KM_BTNS_SET_CID_REPORTING,
+					   params, sizeof(params), &response);
+}
+
+static int holdable_thumb_buttons_event(struct hidpp_device *hidpp, u8 *data, int size)
+{
+	struct hidpp_report *report = (struct hidpp_report *)data;
+	bool btn_side = 0, btn_extra = 0;
+	int i;
+
+	if (report->fap.feature_index != hidpp->special_km_btns_feature_index ||
+	    report->fap.funcindex_clientid != EVENT_SPECIAL_KM_BTNS_DIVERTED_BTNS_EVENT)
+		return 0;
+
+	for (i = 0; i < 8; i++) {
+		switch (report->fap.params[i]) {
+		case CID_BTN_SIDE:
+			btn_side = 1;
+			break;
+		case CID_BTN_EXTRA:
+			btn_extra = 1;
+			break;
+		}
+	}
+
+	input_report_key(hidpp->input, BTN_SIDE, btn_side);
+	input_report_key(hidpp->input, BTN_EXTRA, btn_extra);
+
+	input_sync(hidpp->input);
+	return 1;
+}
+
  /* ------------------------------------------------------------------------- */
  /* Logitech G920 Driving Force Racing Wheel for Xbox One                     */
  /* ------------------------------------------------------------------------- */
@@ -3620,6 +3718,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
  			return ret;
  	}
  
+	if (holdable_thumb_buttons && hidpp->special_km_btns_feature_index) {
+		ret = holdable_thumb_buttons_event(hidpp, data, size);
+		if (ret != 0)
+			return ret;
+	}
+
  	return 0;
  }
  
@@ -3900,6 +4004,12 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
  			return;
  	}
  
+	if (holdable_thumb_buttons && hidpp->special_km_btns_feature_index) {
+		ret = holdable_thumb_buttons_connect(hidpp);
+		if (ret)
+			return;
+	}
+
  	/* the device is already connected, we can ask for its name and
  	 * protocol */
  	if (!hidpp->protocol_major) {
@@ -4157,6 +4267,14 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
  			goto hid_hw_init_fail;
  	}
  
+	if (connected && hidpp->protocol_major >= 2) {
+		ret = hidpp_set_special_km_btns_feature_index(hidpp);
+		if (ret == -ENOENT)
+			hidpp->special_km_btns_feature_index = 0;
+		else if (ret)
+			goto hid_hw_init_fail;
+	}
+
  	if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
  		ret = wtp_get_config(hidpp);
  		if (ret)

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-03 10:29 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-03 10:29 [PATCH v2] hid-logitech-hidpp: holdable_thumb_buttons parameter for Logitech MX Anywhere 3 Tim Schumacher

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).