HID: logitech-hidpp: add support for HID++ 1.0 extra mouse buttons reports
authorHans de Goede <hdegoede@redhat.com>
Sat, 20 Apr 2019 11:22:16 +0000 (13:22 +0200)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Tue, 23 Apr 2019 16:03:05 +0000 (18:03 +0200)
Some mice have extra buttons which are only reported through HID++ 1.0
extra mouse buttons reports, this commit adds support for this and
automatically enables this support for all 27 MHz mice.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
drivers/hid/hid-logitech-hidpp.c

index 7f6beba..215c3a2 100644 (file)
@@ -72,6 +72,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_HI_RES_SCROLL_X2120                BIT(27)
 #define HIDPP_QUIRK_HI_RES_SCROLL_X2121                BIT(28)
 #define HIDPP_QUIRK_HIDPP_WHEELS               BIT(29)
+#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS     BIT(30)
 
 /* These are just aliases for now */
 #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -2793,6 +2794,64 @@ static void hidpp10_wheel_populate_input(struct hidpp_device *hidpp,
 }
 
 /* -------------------------------------------------------------------------- */
+/* HID++1.0 mice which use HID++ reports for extra mouse buttons              */
+/* -------------------------------------------------------------------------- */
+static int hidpp10_extra_mouse_buttons_connect(struct hidpp_device *hidpp)
+{
+       return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
+                                   HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT,
+                                   HIDPP_ENABLE_MOUSE_EXTRA_BTN_REPORT);
+}
+
+static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
+                                   u8 *data, int size)
+{
+       int i;
+
+       if (!hidpp->input)
+               return -EINVAL;
+
+       if (size < 7)
+               return 0;
+
+       if (data[0] != REPORT_ID_HIDPP_SHORT ||
+           data[2] != HIDPP_SUB_ID_MOUSE_EXTRA_BTNS)
+               return 0;
+
+       /*
+        * Buttons are either delivered through the regular mouse report *or*
+        * through the extra buttons report. At least for button 6 how it is
+        * delivered differs per receiver firmware version. Even receivers with
+        * the same usb-id show different behavior, so we handle both cases.
+        */
+       for (i = 0; i < 8; i++)
+               input_report_key(hidpp->input, BTN_MOUSE + i,
+                                (data[3] & (1 << i)));
+
+       /* Some mice report events on button 9+, use BTN_MISC */
+       for (i = 0; i < 8; i++)
+               input_report_key(hidpp->input, BTN_MISC + i,
+                                (data[4] & (1 << i)));
+
+       input_sync(hidpp->input);
+       return 1;
+}
+
+static void hidpp10_extra_mouse_buttons_populate_input(
+                       struct hidpp_device *hidpp, struct input_dev *input_dev)
+{
+       /* BTN_MOUSE - BTN_MOUSE+7 are set already by the descriptor */
+       __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                                              */
 /* -------------------------------------------------------------------------- */
 
@@ -2879,6 +2938,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
 
        if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS)
                hidpp10_wheel_populate_input(hidpp, input);
+
+       if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS)
+               hidpp10_extra_mouse_buttons_populate_input(hidpp, input);
 }
 
 static int hidpp_input_configured(struct hid_device *hdev,
@@ -2956,6 +3018,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
                        return ret;
        }
 
+       if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
+               ret = hidpp10_extra_mouse_buttons_raw_event(hidpp, data, size);
+               if (ret != 0)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -3209,6 +3277,12 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
                        return;
        }
 
+       if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) {
+               ret = hidpp10_extra_mouse_buttons_connect(hidpp);
+               if (ret)
+                       return;
+       }
+
        /* the device is already connected, we can ask for its name and
         * protocol */
        if (!hidpp->protocol_major) {
@@ -3374,7 +3448,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
        if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
            hidpp_application_equals(hdev, HID_GD_MOUSE))
-               hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS;
+               hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
+                                HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS;
 
        if (disable_raw_mode) {
                hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;