[PATCH] HID: multitouch: Fix Yoga Book 9 14IAH10 touchscreen misclassification
From: Dave Carey
Date: Thu Apr 02 2026 - 14:34:20 EST
The Lenovo Yoga Book 9 14IAH10 (83KJ) uses a composite USB HID device
(17EF:6161) where three descriptor quirks combine to cause hid-multitouch
to incorrectly set INPUT_PROP_BUTTONPAD on both touchscreen nodes, making
libinput treat them as indirect clickpads rather than direct touchscreens.
Quirk 1: The HID_DG_TOUCHSCREEN application collection contains
HID_UP_BUTTON usages (stylus barrel buttons). The generic heuristic in
mt_touch_input_mapping() treats any touchscreen-with-buttons as a
touchpad, setting INPUT_MT_POINTER.
Quirk 2: A HID_DG_TOUCHPAD collection ("Emulated Touchpad") sets
INPUT_MT_POINTER unconditionally in mt_allocate_application().
Quirk 3: The HID_DG_BUTTONTYPE feature report (0x51) returns
MT_BUTTONTYPE_CLICKPAD, directly setting td->is_buttonpad = true.
These combine to produce INPUT_PROP_BUTTONPAD on the touchscreen input
nodes. libinput treats the devices as indirect clickpads and suppresses
direct touch events, leaving the touchscreens non-functional under
KDE/Wayland.
Additionally, the firmware resets if any USB control request is received
during the CDC ACM initialization window. The existing GET_REPORT call
in mt_check_input_mode() during probe triggers this reset.
Fix by extending MT_QUIRK_YOGABOOK9I (already defined for the earlier
Yoga Book 9i) to guard all three BUTTONPAD heuristics and skip the
HID_DG_BUTTONTYPE GET_REPORT during probe for this device.
Signed-off-by: Dave Carey <carvsdriver@xxxxxxxxx>
Tested-by: Dave Carey <carvsdriver@xxxxxxxxx>
---
drivers/hid/hid-multitouch.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index e82a3c4e5..1bef32b1d 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -549,7 +549,14 @@ static void mt_feature_mapping(struct hid_device *hdev,
switch (usage->hid) {
case HID_DG_CONTACTMAX:
- mt_get_feature(hdev, field->report);
+ /*
+ * Yoga Book 9: skip GET_REPORT during probe; the firmware
+ * resets if it receives any control request before the init
+ * Output report is sent (within ~1.18s of USB enumeration).
+ * Logical maximum from the descriptor is used as the fallback.
+ */
+ if (!(td->mtclass.quirks & MT_QUIRK_YOGABOOK9I))
+ mt_get_feature(hdev, field->report);
td->maxcontacts = field->value[0];
if (!td->maxcontacts &&
@@ -566,6 +573,10 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
}
+ /* Yoga Book 9 reports Clickpad but is a direct touchscreen */
+ if (td->mtclass.quirks & MT_QUIRK_YOGABOOK9I)
+ break;
+
mt_get_feature(hdev, field->report);
switch (field->value[usage->usage_index]) {
case MT_BUTTONTYPE_CLICKPAD:
@@ -579,7 +590,9 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
case 0xff0000c5:
/* Retrieve the Win8 blob once to enable some devices */
- if (usage->usage_index == 0)
+ /* Yoga Book 9: skip; firmware resets before init if queried */
+ if (usage->usage_index == 0 &&
+ !(td->mtclass.quirks & MT_QUIRK_YOGABOOK9I))
mt_get_feature(hdev, field->report);
break;
}
@@ -644,8 +657,11 @@ static struct mt_application *mt_allocate_application(struct mt_device *td,
/*
* Model touchscreens providing buttons as touchpads.
+ * Yoga Book 9 has an emulated touchpad but its touch surfaces
+ * are direct screens, not indirect pointers.
*/
- if (application == HID_DG_TOUCHPAD) {
+ if (application == HID_DG_TOUCHPAD &&
+ !(td->mtclass.quirks & MT_QUIRK_YOGABOOK9I)) {
mt_application->mt_flags |= INPUT_MT_POINTER;
td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
}
@@ -802,11 +818,15 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
/*
* Model touchscreens providing buttons as touchpads.
+ * Skip for Yoga Book 9 which has stylus buttons inside
+ * touchscreen collections, not physical touchpad buttons.
*/
if (field->application == HID_DG_TOUCHSCREEN &&
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
- app->mt_flags |= INPUT_MT_POINTER;
- td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ if (!(app->quirks & MT_QUIRK_YOGABOOK9I)) {
+ app->mt_flags |= INPUT_MT_POINTER;
+ td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ }
}
/* count the buttons on touchpads */
@@ -1420,7 +1440,6 @@ static int mt_touch_input_configured(struct hid_device *hdev,
*/
if (cls->quirks & MT_QUIRK_APPLE_TOUCHBAR)
app->mt_flags |= INPUT_MT_DIRECT;
-
if (cls->is_indirect)
app->mt_flags |= INPUT_MT_POINTER;
@@ -1432,7 +1451,8 @@ static int mt_touch_input_configured(struct hid_device *hdev,
/* check for clickpads */
if ((app->mt_flags & INPUT_MT_POINTER) &&
- (app->buttons_count == 1))
+ (app->buttons_count == 1) &&
+ !(app->quirks & MT_QUIRK_YOGABOOK9I))
td->is_buttonpad = true;
if (td->is_buttonpad)
--
2.53.0