Commit 4a038108 authored by Daniel J. Ogorchock's avatar Daniel J. Ogorchock Committed by Jiri Kosina
Browse files

HID: nintendo: avoid bluetooth suspend/resume stalls



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: default avatarDaniel J. Ogorchock <djogorchock@gmail.com>
Reviewed-by: default avatarSilvan Jegen <s.jegen@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.com>
parent 85a720f4
Loading
Loading
Loading
Loading
+36 −2
Original line number Diff line number Diff line
@@ -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)