From: Lauri Tirkkonen lauri@hacktheplanet.fi
[ Upstream commit a45f15808fb753a14c6041fd1e5bef5d552bd2e3 ]
The keyboard of this device has the following in its report description for Usage (Keyboard) in Collection (Application):
# 0x15, 0x00, // Logical Minimum (0) 52 # 0x25, 0x65, // Logical Maximum (101) 54 # 0x05, 0x07, // Usage Page (Keyboard) 56 # 0x19, 0x00, // Usage Minimum (0) 58 # 0x29, 0xdd, // Usage Maximum (221) 60 # 0x81, 0x00, // Input (Data,Arr,Abs) 62
Since the Usage Min/Max range exceeds the Logical Min/Max range, keypresses outside the Logical range are not recognized. This includes, for example, the Japanese language keyboard variant's keys for |, _ and .
Fixup the report description to make the Logical range match the Usage range, fixing the interpretation of keypresses above 101 on this device.
Signed-off-by: Lauri Tirkkonen lauri@hacktheplanet.fi Signed-off-by: Jiri Kosina jkosina@suse.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
HID: lenovo: fixup Lenovo Yoga Slim 7x Keyboard rdesc
**1. COMMIT MESSAGE ANALYSIS** - **Problem:** The commit addresses a firmware bug in the Lenovo Yoga Slim 7x keyboard HID descriptor. The "Logical Maximum" (101) is defined incorrectly as smaller than the "Usage Maximum" (221). - **User Impact:** This causes the OS to ignore keypresses with usage codes above 101. This specifically breaks functionality for keys like `|`, `_`, and `` on Japanese keyboard layouts, rendering the device partially unusable for those users. - **Fix:** The patch modifies the report descriptor at runtime to align the Logical Maximum with the Usage Maximum. - **Stable Context:** This is a classic "quirk" or "workaround" for broken hardware, which is a standard exception for stable backports.
**2. DEEP CODE RESEARCH** - **Mechanism:** The commit implements a fixup that intercepts the report descriptor before the HID core parses it. - **Specific Changes:** 1. Adds a new device ID `I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD` (0x8987). 2. Defines the specific byte sequence of the broken descriptor to identify it uniquely: ```151:157:drivers/hid/hid-lenovo.c static const __u8 lenovo_yoga7x_kbd_need_fixup_collection[] = { 0x15, 0x00, // Logical Minimum (0) 0x25, 0x65, // Logical Maximum (101) 0x05, 0x07, // Usage Page (Keyboard) ``` 3. Updates the `lenovo_report_fixup` function to apply the patch only when the device ID matches *and* the descriptor content matches: ```189:192:drivers/hid/hid-lenovo.c case I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD: if (*rsize == 176 && memcmp(&rdesc[52], lenovo_yoga7x_kbd_need_fixup_collection, sizeof(lenovo_yoga7x_kbd_need_fixup_collection)) == 0) { ``` - **Safety:** The fix is defensive. It requires an exact match of the device product ID and the descriptor bytes. It uses established patterns found elsewhere in the driver (e.g., for the ThinkPad Pro Dock).
**3. STABLE KERNEL RULES ASSESSMENT** - **Obviously correct and tested:** The fix uses standard HID subsystem mechanisms for correcting buggy hardware descriptors. - **Fixes real bug:** Without this patch, specific keys do not function on this hardware. - **Small and contained:** The changes are surgical, affecting only `drivers/hid/hid-lenovo.c` and `drivers/hid/hid-ids.h`. - **Exceptions Applied:** - **NEW DEVICE IDs:** Adds support for the Yoga Slim 7x (allowed). - **QUIRKS/WORKAROUNDS:** Fixes the broken report descriptor (allowed).
**4. RISK VS BENEFIT** - **Benefit:** High for affected users. It makes the keyboard fully functional. - **Risk:** Very Low. The change is additive and guarded by a specific device ID check. It cannot cause regressions for other Lenovo devices because the code path is unreachable for them.
**5. CONCLUSION** This commit is a textbook candidate for stable backporting. It supports new hardware (Device ID exception) and fixes a firmware bug that breaks functionality (Quirk exception). It is low-risk, contained, and provides a necessary fix for users of this specific laptop model.
**YES**
drivers/hid/hid-ids.h | 1 + drivers/hid/hid-lenovo.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5721b8414bbdf..4b1946eb4e7fc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -715,6 +715,7 @@ #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 +#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987 #define USB_DEVICE_ID_ITE8595 0x8595 #define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 654879814f97a..9cc3e029e9f61 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -148,6 +148,14 @@ static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = { 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ };
+static const __u8 lenovo_yoga7x_kbd_need_fixup_collection[] = { + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x05, 0x07, // Usage Page (Keyboard) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xDD, // Usage Maximum (221) +}; + static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -177,6 +185,13 @@ static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[260] = 0x01; /* report count (2) = 0x01 */ } break; + case I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD: + if (*rsize == 176 && + memcmp(&rdesc[52], lenovo_yoga7x_kbd_need_fixup_collection, + sizeof(lenovo_yoga7x_kbd_need_fixup_collection)) == 0) { + rdesc[55] = rdesc[61]; // logical maximum = usage maximum + } + break; } return rdesc; } @@ -1538,6 +1553,8 @@ static const struct hid_device_id lenovo_devices[] = { USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) }, + { HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC, + USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD) }, { } };