hid: lenovo: Resend all settings on reset_resume for compact keyboards
authorJamie Lentin <jm@lentin.co.uk>
Mon, 2 Oct 2023 15:09:14 +0000 (15:09 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Nov 2023 17:20:04 +0000 (17:20 +0000)
commit 2f2bd7cbd1d1548137b351040dc4e037d18cdfdc upstream.

The USB Compact Keyboard variant requires a reset_resume function to
restore keyboard configuration after a suspend in some situations. Move
configuration normally done on probe to lenovo_features_set_cptkbd(), then
recycle this for use on reset_resume.

Without, the keyboard and driver would end up in an inconsistent state,
breaking middle-button scrolling amongst other problems, and twiddling
sysfs values wouldn't help as the middle-button mode won't be set until
the driver is reloaded.

Tested on a USB and Bluetooth Thinkpad Compact Keyboard.

CC: stable@vger.kernel.org
Fixes: 94eefa271323 ("HID: lenovo: Use native middle-button mode for compact keyboards")
Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
Signed-off-by: Martin Kepplinger <martink@posteo.de>
Link: https://lore.kernel.org/r/20231002150914.22101-1-martink@posteo.de
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/hid/hid-lenovo.c

index 9c11813..7c1b33b 100644 (file)
@@ -526,6 +526,19 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
        int ret;
        struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
 
+       /*
+        * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
+        * regular keys
+        */
+       ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
+       if (ret)
+               hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
+
+       /* Switch middle button to native mode */
+       ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
+       if (ret)
+               hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
+
        ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
        if (ret)
                hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
@@ -1148,22 +1161,6 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
        }
        hid_set_drvdata(hdev, cptkbd_data);
 
-       /*
-        * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
-        * regular keys (Compact only)
-        */
-       if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD ||
-           hdev->product == USB_DEVICE_ID_LENOVO_CBTKBD) {
-               ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
-               if (ret)
-                       hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
-       }
-
-       /* Switch middle button to native mode */
-       ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
-       if (ret)
-               hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
-
        /* Set keyboard settings to known state */
        cptkbd_data->middlebutton_state = 0;
        cptkbd_data->fn_lock = true;
@@ -1286,6 +1283,24 @@ err:
        return ret;
 }
 
+#ifdef CONFIG_PM
+static int lenovo_reset_resume(struct hid_device *hdev)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_CUSBKBD:
+       case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
+               if (hdev->type == HID_TYPE_USBMOUSE)
+                       lenovo_features_set_cptkbd(hdev);
+
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+#endif
+
 static void lenovo_remove_tpkbd(struct hid_device *hdev)
 {
        struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
@@ -1402,6 +1417,9 @@ static struct hid_driver lenovo_driver = {
        .raw_event = lenovo_raw_event,
        .event = lenovo_event,
        .report_fixup = lenovo_report_fixup,
+#ifdef CONFIG_PM
+       .reset_resume = lenovo_reset_resume,
+#endif
 };
 module_hid_driver(lenovo_driver);