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@xxxxxxxxxx> --- drivers/hid/hid-logitech-hidpp.c | 86 ++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) 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) }, -- 2.21.0