diff mbox series

[28/28] HID: logitech-hidpp: add support for HID++ 1.0 mice hwheel and extra buttons

Message ID 20190330112418.15042-29-hdegoede@redhat.com (mailing list archive)
State Superseded
Delegated to: Jiri Kosina
Headers show
Series HID: logitech: Handling of non DJ receivers in hid-logitech-dj | expand

Commit Message

Hans de Goede March 30, 2019, 11:24 a.m. UTC
Some HID++ 1.0 (27 MHz) mice have a wheel which clicks left / right
for horizontal scrolling and some extra buttons which are not reported
through the regular mouse input report.

This commits adds support for using the special HID++ input reports for
the hwheel and extra buttons, so that these will work under Linux.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/hid/hid-logitech-hidpp.c | 86 ++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)
diff mbox series

Patch

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index f02a665dfebe..08040ef095ef 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -53,6 +53,9 @@  MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_REPORT_LONG_LENGTH		20
 #define HIDPP_REPORT_VERY_LONG_LENGTH		64
 
+#define HIDPP_SUB_ID_ROLLER			0x05
+#define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS		0x06
+
 #define HIDPP_QUIRK_CLASS_WTP			BIT(0)
 #define HIDPP_QUIRK_CLASS_M560			BIT(1)
 #define HIDPP_QUIRK_CLASS_K400			BIT(2)
@@ -68,6 +71,7 @@  MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_HI_RES_SCROLL_1P0		BIT(26)
 #define HIDPP_QUIRK_HI_RES_SCROLL_X2120		BIT(27)
 #define HIDPP_QUIRK_HI_RES_SCROLL_X2121		BIT(28)
+#define HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS	BIT(29)
 
 /* Convenience constant to check for any high-res support. */
 #define HIDPP_QUIRK_HI_RES_SCROLL	(HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
@@ -2774,6 +2778,73 @@  static int g920_get_config(struct hidpp_device *hidpp)
 	return 0;
 }
 
+/* -------------------------------------------------------------------------- */
+/* HID++1.0 mice which use HID++ report for hwheel and extra buttons          */
+/* -------------------------------------------------------------------------- */
+
+static int hidpp10_hwheel_connect(struct hid_device *hdev, bool connected)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	int ret;
+
+	/* Bit 5 enables HID++ 1.0 hwheel reporting */
+	ret = hidpp10_set_register_bit(hidpp, HIDPP_REG_GENERAL, 0, 5);
+	if (ret)
+		return ret;
+
+	/* Bit 3 enables HID++ 1.0 extra buttons reporting */
+	return hidpp10_set_register_bit(hidpp, HIDPP_REG_GENERAL, 0, 3);
+}
+
+static int hidpp10_hwheel_raw_event(struct hidpp_device *hidpp,
+				    u8 *data, int size)
+{
+	if (!hidpp->input)
+		return -EINVAL;
+
+	if (size < 7)
+		return 0;
+
+	if (data[0] == REPORT_ID_HIDPP_SHORT &&
+	    data[2] == HIDPP_SUB_ID_ROLLER) {
+		s8 value = data[4];
+
+		input_report_rel(hidpp->input, REL_HWHEEL, value);
+		input_report_rel(hidpp->input, REL_HWHEEL_HI_RES, value * 120);
+		input_sync(hidpp->input);
+	}
+
+	if (data[0] == REPORT_ID_HIDPP_SHORT &&
+	    data[2] == HIDPP_SUB_ID_MOUSE_EXTRA_BTNS) {
+		int i;
+
+		for (i = 0; i < 8; i++)
+			input_report_key(hidpp->input, BTN_0 + i,
+					 (data[4] & (1 << i)));
+		input_sync(hidpp->input);
+	}
+
+	return 1;
+}
+
+static void hidpp10_hwheel_populate_input(struct hidpp_device *hidpp,
+		struct input_dev *input_dev, bool origin_is_hid_core)
+{
+	__set_bit(EV_REL, input_dev->evbit);
+	__set_bit(REL_HWHEEL, input_dev->relbit);
+	__set_bit(REL_HWHEEL_HI_RES, input_dev->relbit);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_0, input_dev->keybit);
+	__set_bit(BTN_1, input_dev->keybit);
+	__set_bit(BTN_2, input_dev->keybit);
+	__set_bit(BTN_3, input_dev->keybit);
+	__set_bit(BTN_4, input_dev->keybit);
+	__set_bit(BTN_5, input_dev->keybit);
+	__set_bit(BTN_6, input_dev->keybit);
+	__set_bit(BTN_7, input_dev->keybit);
+}
+
 /* -------------------------------------------------------------------------- */
 /* High-resolution scroll wheels                                              */
 /* -------------------------------------------------------------------------- */
@@ -2858,6 +2929,8 @@  static void hidpp_populate_input(struct hidpp_device *hidpp,
 		wtp_populate_input(hidpp, input, origin_is_hid_core);
 	else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
 		m560_populate_input(hidpp, input, origin_is_hid_core);
+	else if (hidpp->quirks & HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS)
+		hidpp10_hwheel_populate_input(hidpp, input, origin_is_hid_core);
 }
 
 static int hidpp_input_configured(struct hid_device *hdev,
@@ -2929,6 +3002,9 @@  static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
 			return ret;
 	}
 
+	if (hidpp->quirks & HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS)
+		hidpp10_hwheel_raw_event(hidpp, data, size);
+
 	return 0;
 }
 
@@ -3174,6 +3250,10 @@  static void hidpp_connect_event(struct hidpp_device *hidpp)
 		ret = k400_connect(hdev, connected);
 		if (ret)
 			return;
+	} else if (hidpp->quirks & HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS) {
+		ret = hidpp10_hwheel_connect(hdev, connected);
+		if (ret)
+			return;
 	}
 
 	/* the device is already connected, we can ask for its name and
@@ -3493,6 +3573,12 @@  static const struct hid_device_id hidpp_devices[] = {
 	{ /* Solar Keyboard Logitech K750 */
 	  LDJ_DEVICE(0x4002),
 	  .driver_data = HIDPP_QUIRK_CLASS_K750 },
+	{ /* Mouse from Logitech "Cordless Rechargeable Desktop" (M-RAK89D) */
+	  LDJ_DEVICE(0x0029),
+	  .driver_data = HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS },
+	{ /* Mouse Logitech MX3200 (M-RAZ105) */
+	  LDJ_DEVICE(0x003a),
+	  .driver_data = HIDPP_QUIRK_HIDPP_HWHEEL_AND_EXTRA_BTNS },
 
 	{ LDJ_DEVICE(HID_ANY_ID) },