Commit 46a0a2c9 authored by Mikhail Khvainitski's avatar Mikhail Khvainitski Committed by Jiri Kosina
Browse files

HID: lenovo: Detect quirk-free fw on cptkbd and stop applying workaround

Built-in firmware of cptkbd handles scrolling by itself (when middle
button is pressed) but with issues: it does not support horizontal and
hi-res scrolling and upon middle button release it sends middle button
click even if there was a scrolling event. Commit 3cb5ff02 ("HID:
lenovo: Hide middle-button press until release") workarounds last
issue but it's impossible to workaround scrolling-related issues
without firmware modification.

Likely, Dennis Schneider has reverse engineered the firmware and
provided an instruction on how to patch it [1]. However,
aforementioned workaround prevents userspace (libinput) from knowing
exact moment when middle button has been pressed down and performing
"On-Button scrolling". This commit detects correctly-behaving patched
firmware if cursor movement events has been received during middle
button being pressed and stops applying workaround for this device.

Link: https://hohlerde.org/rauch/en/elektronik/projekte/tpkbd-fix/

 [1]

Signed-off-by: default avatarMikhail Khvainitski <me@khvoinitsky.org>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 29aa98d0
Loading
Loading
Loading
Loading
+45 −23
Original line number Diff line number Diff line
@@ -51,7 +51,12 @@ struct lenovo_drvdata {
	int select_right;
	int sensitivity;
	int press_speed;
	u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
	/* 0: Up
	 * 1: Down (undecided)
	 * 2: Scrolling
	 * 3: Patched firmware, disable workaround
	 */
	u8 middlebutton_state;
	bool fn_lock;
};

@@ -668,6 +673,22 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
{
	struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);

	if (cptkbd_data->middlebutton_state != 3) {
		/* REL_X and REL_Y events during middle button pressed
		 * are only possible on patched, bug-free firmware
		 * so set middlebutton_state to 3
		 * to never apply workaround anymore
		 */
		if (cptkbd_data->middlebutton_state == 1 &&
				usage->type == EV_REL &&
				(usage->code == REL_X || usage->code == REL_Y)) {
			cptkbd_data->middlebutton_state = 3;
			/* send middle button press which was hold before */
			input_event(field->hidinput->input,
				EV_KEY, BTN_MIDDLE, 1);
			input_sync(field->hidinput->input);
		}

		/* "wheel" scroll events */
		if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
				usage->code == REL_HWHEEL)) {
@@ -694,6 +715,7 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
			}
			return 1;
		}
	}

	if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
		/*