2018-08-30 21:59:06

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 0/5] Add support for high-resolution scrolling on Logitech mice

Hi everyone,

This is v2 of the patch set adding support for high-resolution scroll
wheels on Logitech mice. See the linux-input "Reporting high-resolution
scroll events" thread [0] for previous discussion of the evdev changes.
I would love to hear your feedback.

Thanks,

Harry Cutts
Chrome OS Touch/Input team

[0]: https://www.spinics.net/lists/linux-input/msg57380.html


Changes in v2:
* Changed the REL_WHEEL_HI_RES code to use micrometres (a.k.a. microns)
as its units, instead of 256ths of a millimetre.
* Removed support for mice connecting over Bluetooth, due to a bug where
the mouse's high-res mode and the driver's settings get out of sync
when the mouse is power cycled.
* Moved the creation of the HID++ 1.0 function and the refactor to use
the LDJ_DEVICE macro into separate patches.
* Added a couple of explanatory comments to the Logitech driver.

Harry Cutts (5):
Add the `REL_WHEEL_HI_RES` event code
Create a utility class for counting scroll events
Add function to enable HID++ 1.0 "scrolling acceleration"
Enable high-resolution scrolling on Logitech mice
Use LDJ_DEVICE macro for existing Logitech mice

Documentation/input/event-codes.rst | 11 +-
drivers/hid/hid-input.c | 45 ++++
drivers/hid/hid-logitech-hidpp.c | 311 ++++++++++++++++++++++---
include/linux/hid.h | 28 +++
include/uapi/linux/input-event-codes.h | 1 +
5 files changed, 368 insertions(+), 28 deletions(-)

--
2.19.0.rc0.228.g281dcd1b4d0-goog



2018-08-30 21:58:29

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 5/5] Use LDJ_DEVICE macro for existing Logitech mice

Signed-off-by: Harry Cutts <[email protected]>
---

Changes in v2:
* Changed the REL_WHEEL_HI_RES code to use micrometres (a.k.a. microns)
as its units, instead of 256ths of a millimetre.
* Removed support for mice connecting over Bluetooth, due to a bug where
the mouse's high-res mode and the driver's settings get out of sync
when the mouse is power cycled.
* Moved the creation of the HID++ 1.0 function and the refactor to use
the LDJ_DEVICE macro into separate patches.
* Added a couple of explanatory comments to the Logitech driver.

drivers/hid/hid-logitech-hidpp.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index fd6a8c325fa0..5f0c080059c6 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -3314,13 +3314,11 @@ static void hidpp_remove(struct hid_device *hdev)

static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4011),
+ LDJ_DEVICE(0x4011),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
{ /* wireless touchpad T650 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4101),
+ LDJ_DEVICE(0x4101),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
{ /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
@@ -3360,16 +3358,13 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Mouse Logitech Performance MX */
LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4024),
+ LDJ_DEVICE(0x4024),
.driver_data = HIDPP_QUIRK_CLASS_K400 },
{ /* Solar Keyboard Logitech K750 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4002),
+ LDJ_DEVICE(0x4002),
.driver_data = HIDPP_QUIRK_CLASS_K750 },

- { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
+ { LDJ_DEVICE(HID_ANY_ID) },

{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
--
2.19.0.rc0.228.g281dcd1b4d0-goog


2018-08-30 21:58:39

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 3/5] Add function to enable HID++ 1.0 "scrolling acceleration"

"Scrolling acceleration" is a bit of a misnomer: it doesn't deal with
acceleration at all. However, that's the name used in Logitech's spec,
so I used it here.

Signed-off-by: Harry Cutts <[email protected]>
---

Changes in v2: None

drivers/hid/hid-logitech-hidpp.c | 47 +++++++++++++++++++++++---------
1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 19cc980eebce..7f8218f6ff56 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -400,32 +400,53 @@ static void hidpp_prefix_name(char **name, int name_length)
#define HIDPP_SET_LONG_REGISTER 0x82
#define HIDPP_GET_LONG_REGISTER 0x83

-#define HIDPP_REG_GENERAL 0x00
-
-static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+/**
+ * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
+ * @hidpp_dev: the device to set the register on.
+ * @register_address: the address of the register to modify.
+ * @byte: the byte of the register to modify. Should be less than 3.
+ * Return: 0 if successful, otherwise a negative error code.
+ */
+static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
+ u8 register_address, u8 byte, u8 bit)
{
struct hidpp_report response;
int ret;
u8 params[3] = { 0 };

ret = hidpp_send_rap_command_sync(hidpp_dev,
- REPORT_ID_HIDPP_SHORT,
- HIDPP_GET_REGISTER,
- HIDPP_REG_GENERAL,
- NULL, 0, &response);
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_REGISTER,
+ register_address,
+ NULL, 0, &response);
if (ret)
return ret;

memcpy(params, response.rap.params, 3);

- /* Set the battery bit */
- params[0] |= BIT(4);
+ params[byte] |= BIT(bit);

return hidpp_send_rap_command_sync(hidpp_dev,
- REPORT_ID_HIDPP_SHORT,
- HIDPP_SET_REGISTER,
- HIDPP_REG_GENERAL,
- params, 3, &response);
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_SET_REGISTER,
+ register_address,
+ params, 3, &response);
+}
+
+
+#define HIDPP_REG_GENERAL 0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+{
+ return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
+}
+
+#define HIDPP_REG_FEATURES 0x01
+
+/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
+static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
+{
+ return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
}

#define HIDPP_REG_BATTERY_STATUS 0x07
--
2.19.0.rc0.228.g281dcd1b4d0-goog


2018-08-30 21:58:41

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 2/5] Create a utility class for counting scroll events

To avoid code duplication, this class counts high-resolution scroll
movements and emits the legacy low-resolution events when appropriate.
Drivers should be able to create one instance for each scroll wheel that
they need to handle.

Signed-off-by: Harry Cutts <[email protected]>
---

Changes in v2: None

drivers/hid/hid-input.c | 45 +++++++++++++++++++++++++++++++++++++++++
include/linux/hid.h | 28 +++++++++++++++++++++++++
2 files changed, 73 insertions(+)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 4e94ea3e280a..6e84e7b9afcb 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1826,3 +1826,48 @@ void hidinput_disconnect(struct hid_device *hid)
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);

+/**
+ * hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
+ * events given a high-resolution wheel
+ * movement.
+ * @counter: a hid_scroll_counter struct describing the wheel.
+ * @hi_res_value: the movement of the wheel, in the mouse's high-resolution
+ * units.
+ *
+ * Given a high-resolution movement, this function converts the movement into
+ * microns and emits high-resolution scroll events for the input device. It also
+ * uses the multiplier from &struct hid_scroll_counter to emit low-resolution
+ * scroll events when appropriate for backwards-compatibility with userspace
+ * input libraries.
+ */
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+ int hi_res_value)
+{
+ int low_res_scroll_amount;
+ /* Some wheels will rest 7/8ths of a notch from the previous notch
+ * after slow movement, so we want the threshold for low-res events to
+ * be in the middle of the notches (e.g. after 4/8ths) as opposed to on
+ * the notches themselves (8/8ths).
+ */
+ int threshold = counter->resolution_multiplier / 2;
+
+ input_report_rel(counter->dev, REL_WHEEL_HI_RES,
+ hi_res_value * counter->microns_per_hi_res_unit);
+
+ counter->remainder += hi_res_value;
+ if (abs(counter->remainder) >= threshold) {
+ /* Add (or subtract) 1 because we want to trigger when the wheel
+ * is half-way to the next notch (i.e. scroll 1 notch after a
+ * 1/2 notch movement, 2 notches after a 1 1/2 notch movement,
+ * etc.).
+ */
+ low_res_scroll_amount =
+ counter->remainder / counter->resolution_multiplier
+ + (hi_res_value > 0 ? 1 : -1);
+ input_report_rel(counter->dev, REL_WHEEL,
+ low_res_scroll_amount);
+ counter->remainder -=
+ low_res_scroll_amount * counter->resolution_multiplier;
+ }
+}
+EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 834e6461a690..037e37b0b0e6 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1138,6 +1138,34 @@ static inline u32 hid_report_len(struct hid_report *report)
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int interrupt);

+
+/**
+ * struct hid_scroll_counter - Utility class for processing high-resolution
+ * scroll events.
+ * @dev: the input device for which events should be reported.
+ * @microns_per_hi_res_unit: the amount moved by the user's finger for each
+ * high-resolution unit reported by the mouse, in
+ * microns.
+ * @resolution_multiplier: the wheel's resolution in high-resolution mode as a
+ * multiple of its lower resolution. For example, if
+ * moving the wheel by one "notch" would result in a
+ * value of 1 in low-resolution mode but 8 in
+ * high-resolution, the multiplier is 8.
+ * @remainder: counts the number of high-resolution units moved since the last
+ * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
+ * only be used by class methods.
+ */
+struct hid_scroll_counter {
+ struct input_dev *dev;
+ int microns_per_hi_res_unit;
+ int resolution_multiplier;
+
+ int remainder;
+};
+
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+ int hi_res_value);
+
/* HID quirks API */
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
int hid_quirks_init(char **quirks_param, __u16 bus, int count);
--
2.19.0.rc0.228.g281dcd1b4d0-goog


2018-08-30 21:58:54

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 1/5] Add the `REL_WHEEL_HI_RES` event code

This event code represents scroll reports from high-resolution wheels,
and will be used by future patches in this series. See the linux-input
"Reporting high-resolution scroll events" thread [0] for more details.

[0]: https://www.spinics.net/lists/linux-input/msg57380.html

Signed-off-by: Harry Cutts <[email protected]>
---

Changes in v2: None

Documentation/input/event-codes.rst | 11 ++++++++++-
include/uapi/linux/input-event-codes.h | 1 +
2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
index a8c0873beb95..cef220c176a4 100644
--- a/Documentation/input/event-codes.rst
+++ b/Documentation/input/event-codes.rst
@@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
* REL_WHEEL, REL_HWHEEL:

- These codes are used for vertical and horizontal scroll wheels,
- respectively.
+ respectively. The value is the number of "notches" moved on the wheel, the
+ physical size of which varies by device. For high-resolution wheels (which
+ report multiple events for each notch of movement, or do not have notches)
+ this may be an approximation based on the high-resolution scroll events.
+
+* REL_WHEEL_HI_RES:
+
+ - If a vertical scroll wheel supports high-resolution scrolling, this code
+ will be emitted in addition to REL_WHEEL. The value is the (approximate)
+ distance travelled by the user's finger, in microns.

EV_ABS
------
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 53fbae27b280..dad8d3890a3a 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -708,6 +708,7 @@
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
+#define REL_WHEEL_HI_RES 0x0a
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)

--
2.19.0.rc0.228.g281dcd1b4d0-goog


2018-08-30 21:59:27

by Harry Cutts

[permalink] [raw]
Subject: [PATCH v2 4/5] Enable high-resolution scrolling on Logitech mice

There are three features used by various Logitech mice for
high-resolution scrolling: the scrolling acceleration bit in HID++ 1.0,
and the x2120 and x2121 features in HID++ 2.0 and above. This patch
supports all three, and uses the multiplier reported by the mouse for
the HID++ 2.0+ features.

The full list of product IDs of mice which support high-resolution
scrolling was provided by Logitech, but the patch was tested using the
following mice (using the Unifying receiver):

* HID++ 1.0: Anywhere MX, Performance MX
* x2120: M560
* x2121: MX Anywhere 2, MX Master 2S

Signed-off-by: Harry Cutts <[email protected]>
---

Changes in v2: None

drivers/hid/hid-logitech-hidpp.c | 249 ++++++++++++++++++++++++++++++-
1 file changed, 245 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 7f8218f6ff56..fd6a8c325fa0 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -64,6 +64,14 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25)
+#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)
+
+/* Convenience constant to check for any high-res support. */
+#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
+ HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
+ HIDPP_QUIRK_HI_RES_SCROLL_X2121)

#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT

@@ -149,6 +157,7 @@ struct hidpp_device {
unsigned long capabilities;

struct hidpp_battery battery;
+ struct hid_scroll_counter vertical_wheel_counter;
};

/* HID++ 1.0 error codes */
@@ -1157,6 +1166,101 @@ static int hidpp_battery_get_property(struct power_supply *psy,
return ret;
}

+/* -------------------------------------------------------------------------- */
+/* 0x2120: Hi-resolution scrolling */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120
+
+#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
+
+static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
+ bool enabled, u8 *multiplier)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ u8 params[1];
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp,
+ HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
+ &feature_index,
+ &feature_type);
+ if (ret)
+ return ret;
+
+ params[0] = enabled ? BIT(0) : 0;
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
+ params, sizeof(params), &response);
+ if (ret)
+ return ret;
+ *multiplier = response.fap.params[1];
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x2121: HiRes Wheel */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HIRES_WHEEL 0x2121
+
+#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00
+#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20
+
+static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
+ u8 *multiplier)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+ &feature_index, &feature_type);
+ if (ret)
+ goto return_default;
+
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
+ NULL, 0, &response);
+ if (ret)
+ goto return_default;
+
+ *multiplier = response.fap.params[0];
+ return 0;
+return_default:
+ *multiplier = 8;
+ hid_warn(hidpp->hid_dev,
+ "Couldn't get wheel multiplier (error %d), assuming %d.\n",
+ ret, *multiplier);
+ return ret;
+}
+
+static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
+ bool high_resolution, bool use_hidpp)
+{
+ u8 feature_index;
+ u8 feature_type;
+ int ret;
+ u8 params[1];
+ struct hidpp_report response;
+
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+ &feature_index, &feature_type);
+ if (ret)
+ return ret;
+
+ params[0] = (invert ? BIT(2) : 0) |
+ (high_resolution ? BIT(1) : 0) |
+ (use_hidpp ? BIT(0) : 0);
+
+ return hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_HIRES_WHEEL_SET_WHEEL_MODE,
+ params, sizeof(params), &response);
+}
+
/* -------------------------------------------------------------------------- */
/* 0x4301: Solar Keyboard */
/* -------------------------------------------------------------------------- */
@@ -2420,7 +2524,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
input_report_rel(mydata->input, REL_Y, v);

v = hid_snto32(data[6], 8);
- input_report_rel(mydata->input, REL_WHEEL, v);
+ hid_scroll_counter_handle_scroll(
+ &hidpp->vertical_wheel_counter, v);

input_sync(mydata->input);
}
@@ -2548,6 +2653,73 @@ static int g920_get_config(struct hidpp_device *hidpp)
return 0;
}

+/* -------------------------------------------------------------------------- */
+/* High-resolution scroll wheels */
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel.
+ * @product_id: the HID product ID of the device being described.
+ * @microns_per_hi_res_unit: the distance moved by the user's finger for each
+ * high-resolution unit reported by the device, in
+ * 256ths of a millimetre.
+ */
+struct hi_res_scroll_info {
+ __u32 product_id;
+ int microns_per_hi_res_unit;
+};
+
+static struct hi_res_scroll_info hi_res_scroll_devices[] = {
+ { /* Anywhere MX */
+ .product_id = 0x1017, .microns_per_hi_res_unit = 445 },
+ { /* Performance MX */
+ .product_id = 0x101a, .microns_per_hi_res_unit = 406 },
+ { /* M560 */
+ .product_id = 0x402d, .microns_per_hi_res_unit = 435 },
+ { /* MX Master 2S */
+ .product_id = 0x4069, .microns_per_hi_res_unit = 406 },
+};
+
+static int hi_res_scroll_look_up_microns(__u32 product_id)
+{
+ int i;
+ int num_devices = sizeof(hi_res_scroll_devices)
+ / sizeof(hi_res_scroll_devices[0]);
+ for (i = 0; i < num_devices; i++) {
+ if (hi_res_scroll_devices[i].product_id == product_id)
+ return hi_res_scroll_devices[i].microns_per_hi_res_unit;
+ }
+ /* We don't have a value for this device, so use a sensible default. */
+ return 406;
+}
+
+static int hi_res_scroll_enable(struct hidpp_device *hidpp)
+{
+ int ret;
+ u8 multiplier;
+
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
+ ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
+ hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
+ } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
+ ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
+ &multiplier);
+ } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ {
+ ret = hidpp10_enable_scrolling_acceleration(hidpp);
+ multiplier = 8;
+ }
+ if (ret)
+ return ret;
+
+ hidpp->vertical_wheel_counter.resolution_multiplier = multiplier;
+ hidpp->vertical_wheel_counter.microns_per_hi_res_unit =
+ hi_res_scroll_look_up_microns(hidpp->hid_dev->product);
+ hid_info(hidpp->hid_dev, "multiplier = %d, microns = %d\n",
+ multiplier,
+ hidpp->vertical_wheel_counter.microns_per_hi_res_unit);
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
@@ -2593,6 +2765,11 @@ 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);
+
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) {
+ input_set_capability(input, EV_REL, REL_WHEEL_HI_RES);
+ hidpp->vertical_wheel_counter.dev = input;
+ }
}

static int hidpp_input_configured(struct hid_device *hdev,
@@ -2711,6 +2888,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}

+static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ /* This function will only be called for scroll events, due to the
+ * restriction imposed in hidpp_usages.
+ */
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter;
+ /* A scroll event may occur before the multiplier has been retrieved or
+ * the input device set, or high-res scroll enabling may fail. In such
+ * cases we must return early (falling back to default behaviour) to
+ * avoid a crash in hid_scroll_counter_handle_scroll.
+ */
+ if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
+ || counter->dev == NULL || counter->resolution_multiplier == 0)
+ return 0;
+
+ hid_scroll_counter_handle_scroll(counter, value);
+ return 1;
+}
+
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
{
static atomic_t battery_no = ATOMIC_INIT(0);
@@ -2922,6 +3120,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);

+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
+ hi_res_scroll_enable(hidpp);
+
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
/* if the input nodes are already created, we can stop now */
return;
@@ -3107,6 +3308,10 @@ static void hidpp_remove(struct hid_device *hdev)
mutex_destroy(&hidpp->send_mutex);
}

+#define LDJ_DEVICE(product) \
+ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
+ USB_VENDOR_ID_LOGITECH, (product))
+
static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
@@ -3121,10 +3326,39 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
+ { /* Mouse Logitech Anywhere MX */
+ LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+ { /* Mouse Logitech Cube */
+ LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+ { /* Mouse Logitech M335 */
+ LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech M515 */
+ LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
{ /* Mouse logitech M560 */
- HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x402d),
- .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
+ LDJ_DEVICE(0x402d),
+ .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
+ | HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+ { /* Mouse Logitech M705 (firmware RQM17) */
+ LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+ { /* Mouse Logitech M705 (firmware RQM67) */
+ LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech M720 */
+ LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Anywhere 2 */
+ LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Anywhere 2S */
+ LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Master */
+ LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech MX Master 2S */
+ LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+ { /* Mouse Logitech Performance MX */
+ LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4024),
@@ -3144,12 +3378,19 @@ static const struct hid_device_id hidpp_devices[] = {

MODULE_DEVICE_TABLE(hid, hidpp_devices);

+static const struct hid_usage_id hidpp_usages[] = {
+ { HID_GD_WHEEL, EV_REL, REL_WHEEL },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
static struct hid_driver hidpp_driver = {
.name = "logitech-hidpp-device",
.id_table = hidpp_devices,
.probe = hidpp_probe,
.remove = hidpp_remove,
.raw_event = hidpp_raw_event,
+ .usage_table = hidpp_usages,
+ .event = hidpp_event,
.input_configured = hidpp_input_configured,
.input_mapping = hidpp_input_mapping,
.input_mapped = hidpp_input_mapped,
--
2.19.0.rc0.228.g281dcd1b4d0-goog


2018-09-03 08:16:40

by Benjamin Tissoires

[permalink] [raw]
Subject: Re: [PATCH v2 0/5] Add support for high-resolution scrolling on Logitech mice

Hi Harry,

On Thu, Aug 30, 2018 at 11:56 PM Harry Cutts <[email protected]> wrote:
>
> Hi everyone,
>
> This is v2 of the patch set adding support for high-resolution scroll
> wheels on Logitech mice. See the linux-input "Reporting high-resolution
> scroll events" thread [0] for previous discussion of the evdev changes.
> I would love to hear your feedback.

Series looks good. I just tested it on a regular Logitech mouse and a
MX Master with high-res scrolling and everything works as expected.

Reviewed-by: Benjamin Tissoires <[email protected]>

Cheers,
Benjamin

>
> Thanks,
>
> Harry Cutts
> Chrome OS Touch/Input team
>
> [0]: https://www.spinics.net/lists/linux-input/msg57380.html
>
>
> Changes in v2:
> * Changed the REL_WHEEL_HI_RES code to use micrometres (a.k.a. microns)
> as its units, instead of 256ths of a millimetre.
> * Removed support for mice connecting over Bluetooth, due to a bug where
> the mouse's high-res mode and the driver's settings get out of sync
> when the mouse is power cycled.
> * Moved the creation of the HID++ 1.0 function and the refactor to use
> the LDJ_DEVICE macro into separate patches.
> * Added a couple of explanatory comments to the Logitech driver.
>
> Harry Cutts (5):
> Add the `REL_WHEEL_HI_RES` event code
> Create a utility class for counting scroll events
> Add function to enable HID++ 1.0 "scrolling acceleration"
> Enable high-resolution scrolling on Logitech mice
> Use LDJ_DEVICE macro for existing Logitech mice
>
> Documentation/input/event-codes.rst | 11 +-
> drivers/hid/hid-input.c | 45 ++++
> drivers/hid/hid-logitech-hidpp.c | 311 ++++++++++++++++++++++---
> include/linux/hid.h | 28 +++
> include/uapi/linux/input-event-codes.h | 1 +
> 5 files changed, 368 insertions(+), 28 deletions(-)
>
> --
> 2.19.0.rc0.228.g281dcd1b4d0-goog
>

2018-09-03 13:44:35

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] Add the `REL_WHEEL_HI_RES` event code

On Thu, 30 Aug 2018, Harry Cutts wrote:

> This event code represents scroll reports from high-resolution wheels,
> and will be used by future patches in this series. See the linux-input
> "Reporting high-resolution scroll events" thread [0] for more details.
>
> [0]: https://www.spinics.net/lists/linux-input/msg57380.html
>
> Signed-off-by: Harry Cutts <[email protected]>
> ---
>
> Changes in v2: None
>
> Documentation/input/event-codes.rst | 11 ++++++++++-
> include/uapi/linux/input-event-codes.h | 1 +
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
> index a8c0873beb95..cef220c176a4 100644
> --- a/Documentation/input/event-codes.rst
> +++ b/Documentation/input/event-codes.rst
> @@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
> * REL_WHEEL, REL_HWHEEL:
>
> - These codes are used for vertical and horizontal scroll wheels,
> - respectively.
> + respectively. The value is the number of "notches" moved on the wheel, the
> + physical size of which varies by device. For high-resolution wheels (which
> + report multiple events for each notch of movement, or do not have notches)
> + this may be an approximation based on the high-resolution scroll events.
> +
> +* REL_WHEEL_HI_RES:
> +
> + - If a vertical scroll wheel supports high-resolution scrolling, this code
> + will be emitted in addition to REL_WHEEL. The value is the (approximate)
> + distance travelled by the user's finger, in microns.
>
> EV_ABS
> ------
> diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
> index 53fbae27b280..dad8d3890a3a 100644
> --- a/include/uapi/linux/input-event-codes.h
> +++ b/include/uapi/linux/input-event-codes.h
> @@ -708,6 +708,7 @@
> #define REL_DIAL 0x07
> #define REL_WHEEL 0x08
> #define REL_MISC 0x09
> +#define REL_WHEEL_HI_RES 0x0a
> #define REL_MAX 0x0f
> #define REL_CNT (REL_MAX+1)

Dmitry, is this OK with you?

Thanks,

--
Jiri Kosina
SUSE Labs


2018-09-04 06:35:35

by Peter Hutterer

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] Add the `REL_WHEEL_HI_RES` event code

On Thu, Aug 30, 2018 at 02:56:18PM -0700, Harry Cutts wrote:
> This event code represents scroll reports from high-resolution wheels,
> and will be used by future patches in this series. See the linux-input
> "Reporting high-resolution scroll events" thread [0] for more details.
>
> [0]: https://www.spinics.net/lists/linux-input/msg57380.html
>
> Signed-off-by: Harry Cutts <[email protected]>

looks good to me, thanks. For the archives, the libinput issue filed for
this is here: https://gitlab.freedesktop.org/libinput/libinput/issues/130

Cheers,
Peter

> ---
>
> Changes in v2: None
>
> Documentation/input/event-codes.rst | 11 ++++++++++-
> include/uapi/linux/input-event-codes.h | 1 +
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
> index a8c0873beb95..cef220c176a4 100644
> --- a/Documentation/input/event-codes.rst
> +++ b/Documentation/input/event-codes.rst
> @@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
> * REL_WHEEL, REL_HWHEEL:
>
> - These codes are used for vertical and horizontal scroll wheels,
> - respectively.
> + respectively. The value is the number of "notches" moved on the wheel, the
> + physical size of which varies by device. For high-resolution wheels (which
> + report multiple events for each notch of movement, or do not have notches)
> + this may be an approximation based on the high-resolution scroll events.
> +
> +* REL_WHEEL_HI_RES:
> +
> + - If a vertical scroll wheel supports high-resolution scrolling, this code
> + will be emitted in addition to REL_WHEEL. The value is the (approximate)
> + distance travelled by the user's finger, in microns.
>
> EV_ABS
> ------
> diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
> index 53fbae27b280..dad8d3890a3a 100644
> --- a/include/uapi/linux/input-event-codes.h
> +++ b/include/uapi/linux/input-event-codes.h
> @@ -708,6 +708,7 @@
> #define REL_DIAL 0x07
> #define REL_WHEEL 0x08
> #define REL_MISC 0x09
> +#define REL_WHEEL_HI_RES 0x0a
> #define REL_MAX 0x0f
> #define REL_CNT (REL_MAX+1)
>
> --
> 2.19.0.rc0.228.g281dcd1b4d0-goog
>

2018-09-05 00:26:34

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] Add the `REL_WHEEL_HI_RES` event code

On Mon, Sep 03, 2018 at 03:43:13PM +0200, Jiri Kosina wrote:
> On Thu, 30 Aug 2018, Harry Cutts wrote:
>
> > This event code represents scroll reports from high-resolution wheels,
> > and will be used by future patches in this series. See the linux-input
> > "Reporting high-resolution scroll events" thread [0] for more details.
> >
> > [0]: https://www.spinics.net/lists/linux-input/msg57380.html
> >
> > Signed-off-by: Harry Cutts <[email protected]>
> > ---
> >
> > Changes in v2: None
> >
> > Documentation/input/event-codes.rst | 11 ++++++++++-
> > include/uapi/linux/input-event-codes.h | 1 +
> > 2 files changed, 11 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
> > index a8c0873beb95..cef220c176a4 100644
> > --- a/Documentation/input/event-codes.rst
> > +++ b/Documentation/input/event-codes.rst
> > @@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
> > * REL_WHEEL, REL_HWHEEL:
> >
> > - These codes are used for vertical and horizontal scroll wheels,
> > - respectively.
> > + respectively. The value is the number of "notches" moved on the wheel, the
> > + physical size of which varies by device. For high-resolution wheels (which
> > + report multiple events for each notch of movement, or do not have notches)
> > + this may be an approximation based on the high-resolution scroll events.
> > +
> > +* REL_WHEEL_HI_RES:
> > +
> > + - If a vertical scroll wheel supports high-resolution scrolling, this code
> > + will be emitted in addition to REL_WHEEL. The value is the (approximate)
> > + distance travelled by the user's finger, in microns.
> >
> > EV_ABS
> > ------
> > diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
> > index 53fbae27b280..dad8d3890a3a 100644
> > --- a/include/uapi/linux/input-event-codes.h
> > +++ b/include/uapi/linux/input-event-codes.h
> > @@ -708,6 +708,7 @@
> > #define REL_DIAL 0x07
> > #define REL_WHEEL 0x08
> > #define REL_MISC 0x09
> > +#define REL_WHEEL_HI_RES 0x0a
> > #define REL_MAX 0x0f
> > #define REL_CNT (REL_MAX+1)
>
> Dmitry, is this OK with you?

Yep, please feel free to merge through your tree.

Acked-by: Dmitry Torokhov <[email protected]>

Thanks.

--
Dmitry

2018-09-05 08:14:37

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH v2 0/5] Add support for high-resolution scrolling on Logitech mice

On Thu, 30 Aug 2018, Harry Cutts wrote:

> This is v2 of the patch set adding support for high-resolution scroll
> wheels on Logitech mice. See the linux-input "Reporting high-resolution
> scroll events" thread [0] for previous discussion of the evdev changes.
> I would love to hear your feedback.

Queued in for-4.20/logitech-highres. Thanks,

--
Jiri Kosina
SUSE Labs