]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
HID: nintendo: avoid bluetooth suspend/resume stalls
authorDaniel J. Ogorchock <djogorchock@gmail.com>
Tue, 13 May 2025 07:47:00 +0000 (03:47 -0400)
committerJiri Kosina <jkosina@suse.com>
Tue, 10 Jun 2025 19:14:11 +0000 (21:14 +0200)
Ensure we don't stall or panic the kernel when using bluetooth-connected
controllers. This was reported as an issue on android devices using
kernel 6.6 due to the resume hook which had been added for usb joycons.

First, set a new state value to JOYCON_CTLR_STATE_SUSPENDED in a
newly-added nintendo_hid_suspend. This makes sure we will not stall out
the kernel waiting for input reports during led classdev suspend. The
stalls could happen if connectivity is unreliable or lost to the
controller prior to suspend.

Second, since we lose connectivity during suspend, do not try
joycon_init() for bluetooth controllers in the nintendo_hid_resume path.

Tested via multiple suspend/resume flows when using the controller both
in USB and bluetooth modes.

Signed-off-by: Daniel J. Ogorchock <djogorchock@gmail.com>
Reviewed-by: Silvan Jegen <s.jegen@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/hid-nintendo.c

index 839d5bcd72b1edb23b5881a0bba0187673308989..fb4985988615b3b9950a4fa07d807b0475df0eb8 100644 (file)
@@ -308,6 +308,7 @@ enum joycon_ctlr_state {
        JOYCON_CTLR_STATE_INIT,
        JOYCON_CTLR_STATE_READ,
        JOYCON_CTLR_STATE_REMOVED,
+       JOYCON_CTLR_STATE_SUSPENDED,
 };
 
 /* Controller type received as part of device info */
@@ -2750,14 +2751,46 @@ static void nintendo_hid_remove(struct hid_device *hdev)
 
 static int nintendo_hid_resume(struct hid_device *hdev)
 {
-       int ret = joycon_init(hdev);
+       struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
+       int ret;
+
+       hid_dbg(hdev, "resume\n");
+       if (!joycon_using_usb(ctlr)) {
+               hid_dbg(hdev, "no-op resume for bt ctlr\n");
+               ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
+               return 0;
+       }
 
+       ret = joycon_init(hdev);
        if (ret)
-               hid_err(hdev, "Failed to restore controller after resume");
+               hid_err(hdev,
+                       "Failed to restore controller after resume: %d\n",
+                       ret);
+       else
+               ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
 
        return ret;
 }
 
+static int nintendo_hid_suspend(struct hid_device *hdev, pm_message_t message)
+{
+       struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
+
+       hid_dbg(hdev, "suspend: %d\n", message.event);
+       /*
+        * Avoid any blocking loops in suspend/resume transitions.
+        *
+        * joycon_enforce_subcmd_rate() can result in repeated retries if for
+        * whatever reason the controller stops providing input reports.
+        *
+        * This has been observed with bluetooth controllers which lose
+        * connectivity prior to suspend (but not long enough to result in
+        * complete disconnection).
+        */
+       ctlr->ctlr_state = JOYCON_CTLR_STATE_SUSPENDED;
+       return 0;
+}
+
 #endif
 
 static const struct hid_device_id nintendo_hid_devices[] = {
@@ -2796,6 +2829,7 @@ static struct hid_driver nintendo_hid_driver = {
 
 #ifdef CONFIG_PM
        .resume         = nintendo_hid_resume,
+       .suspend        = nintendo_hid_suspend,
 #endif
 };
 static int __init nintendo_init(void)