Merge branch 'for-7.2/multitouch' into for-linus

- UX improvement fixes for Yoga Book 9 (Dave Carey)
This commit is contained in:
Jiri Kosina 2026-06-16 21:52:21 +02:00
commit df1d6781b2

View File

@ -443,11 +443,14 @@ static const struct mt_class mt_classes[] = {
MT_QUIRK_CONTACT_CNT_ACCURATE,
},
{ .name = MT_CLS_YOGABOOK9I,
.quirks = MT_QUIRK_ALWAYS_VALID |
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_FORCE_MULTI_INPUT |
MT_QUIRK_SEPARATE_APP_REPORT |
MT_QUIRK_HOVERING |
MT_QUIRK_YOGABOOK9I,
.maxcontacts = 10,
.export_all_inputs = true
},
{ .name = MT_CLS_EGALAX_P80H84,
@ -1566,6 +1569,144 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
return 0;
}
/*
* Yoga Book 9 14IAH10 descriptor fixup.
*
* The device includes a HID_DG_TOUCHPAD application collection designed for
* the Windows inbox HID driver's Win8 PTP touchpad mode. On Linux we want
* only the HID_DG_TOUCHSCREEN collections. The touchpad collection (and the
* HID_DG_BUTTONTYPE and Win8 compliance blob features it contains) must be
* removed so hid-multitouch does not misclassify the touchscreen nodes as
* indirect buttonpads.
*
* The firmware also resets if any USB control request is received while the
* CDC-ACM interface is initialising (~1.18 s after enumeration). Dropping
* the Win8 blob and Contact Count Max feature reports prevents the
* GET_REPORT calls that hid-multitouch issues at probe.
*/
static void mt_yogabook9_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *size)
{
/* Usage Page (Digitizer), Usage (Touch Pad), Collection (Application) */
static const __u8 tp_app_hdr[] = { 0x05, 0x0d, 0x09, 0x05, 0xa1, 0x01 };
/* Vendor Usage Page 0xff00 (Win8 compliance blob header) */
static const __u8 win8_page[] = { 0x06, 0x00, 0xff };
/* Usage (Contact Count Max = 0x55) */
static const __u8 ccmax_usage[] = { 0x09, 0x55 };
unsigned int i;
/*
* Step 1: find and remove the Touch Pad application collection.
* Walk HID short items from the collection header to its matching
* End Collection, then close the gap with memmove.
*/
for (i = 0; i + sizeof(tp_app_hdr) <= *size; i++) {
if (memcmp(rdesc + i, tp_app_hdr, sizeof(tp_app_hdr)) == 0) {
__u8 *start = rdesc + i;
__u8 *coll_end = NULL;
__u8 *p = start;
unsigned int drop;
int depth = 0;
while (p < rdesc + *size) {
__u8 b = *p;
int ds = b & 3;
int item_len;
if (b == 0xfe) { /* long item */
if (p + 2 >= rdesc + *size)
break;
item_len = p[1] + 3;
} else {
item_len = (ds == 3) ? 5 : ds + 1;
}
if (p + item_len > rdesc + *size)
break;
if ((b & 0xfc) == 0xa0)
depth++; /* Collection */
else if (b == 0xc0) {
depth--; /* End Collection */
if (depth == 0) {
coll_end = p;
break;
}
}
p += item_len;
}
if (!coll_end) {
hid_err(hdev,
"Yoga Book 9: Touch Pad End Collection not found\n");
break;
}
drop = coll_end - start + 1;
memmove(start, coll_end + 1, rdesc + *size - coll_end - 1);
*size -= drop;
hid_dbg(hdev,
"Yoga Book 9: dropped Touch Pad collection (%u bytes)\n",
drop);
break;
}
}
/*
* Step 2: neutralize Win8 compliance blob feature reports remaining
* in the touchscreen collections. Change Usage Page 0xff00 to 0x0f00
* so the case 0xff0000c5 branch in mt_feature_mapping() is not reached
* and no GET_REPORT is issued.
*/
for (i = 0; i + sizeof(win8_page) <= *size; i++) {
if (memcmp(rdesc + i, win8_page, sizeof(win8_page)) == 0) {
rdesc[i + 2] = 0x0f; /* 0xff00 -> 0x0f00 */
hid_dbg(hdev,
"Yoga Book 9: neutralized Win8 blob at offset %u\n",
i);
}
}
/*
* Step 3: neutralize Contact Count Max feature reports. Change usage
* 0x55 (HID_DG_CONTACTMAX) to 0x00 so mt_feature_mapping() does not
* issue GET_REPORT. The class maxcontacts field provides the value.
*/
for (i = 0; i + sizeof(ccmax_usage) <= *size; i++) {
if (memcmp(rdesc + i, ccmax_usage, sizeof(ccmax_usage)) == 0) {
rdesc[i + 1] = 0x00;
hid_dbg(hdev,
"Yoga Book 9: neutralized ContactMax at offset %u\n",
i);
}
}
/*
* Step 4: neutralize Surface Switch (0x57) and Button Switch (0x58)
* feature report usages in the Device Configuration collection.
* mt_set_modes() issues HID_REQ_SET_REPORT for these on every
* input-device open/close; those repeated control requests hit the
* firmware's CDC-ACM init window and trigger resets.
*
* Input Mode (0x52) is intentionally left intact. mt_set_modes()
* sends it once at probe to set the device into touchscreen mode,
* which flushes the firmware's contact buffer and clears a persistent
* ghost contact (cid 2, fixed coordinates) that otherwise appears on
* every enumeration. By probe time cdc_acm has already satisfied the
* CDC-ACM init watchdog (~130 ms), so the single SET_REPORT for Input
* Mode arrives safely after the reset window has closed.
*/
for (i = 0; i + 2 <= *size; i++) {
if (rdesc[i] == 0x09 &&
(rdesc[i + 1] == 0x57 ||
rdesc[i + 1] == 0x58)) {
hid_dbg(hdev,
"Yoga Book 9: neutralized set-modes usage 0x%02x at offset %u\n",
rdesc[i + 1], i);
rdesc[i + 1] = 0x00;
}
}
}
static const __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *size)
{
@ -1595,6 +1736,10 @@ got: %x\n",
}
}
if (hdev->vendor == USB_VENDOR_ID_LENOVO &&
hdev->product == USB_DEVICE_ID_LENOVO_YOGABOOK9I)
mt_yogabook9_fixup(hdev, rdesc, size);
return rdesc;
}