Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: btusb: Wait for setup to finish before system sleep
@ 2026-11-11 22:18 Jens Willmer
  2026-11-12  0:29 ` bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Jens Willmer @ 2026-11-11 22:18 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: marcel, luiz.dentz

A Realtek controller loses its firmware in S3, so btusb sets
reset_resume, is probed again on resume and downloads the firmware
from hci_power_on(). On an RTL8852CU (0bda:0852) this takes about two
seconds.

If the system goes to sleep again during that time, for example when
systemd's suspend-then-hibernate fails to hibernate and suspends again
right away, nothing waits for the download. hci_suspend_dev() returns
early because the device is not powered yet, and btusb_suspend() kills
the pending command:

  PM: suspend exit
  PM: suspend entry (deep)
  Bluetooth: hci0: RTL: download fw command failed (-2)

The controller keeps the partial download and a USB reset does not
clear it. After the next resume every command times out, and the device
is reset and probed in a loop until it is power cycled:

  Bluetooth: hci0: command 0xfc20 tx timeout
  Bluetooth: hci0: Resetting usb device.
  Bluetooth: hci1: command 0xfc61 tx timeout
  Bluetooth: hci1: RTL: Failed to generate devcoredump

Register a PM notifier per device and flush hdev->power_on before
suspend and hibernation, so that a setup in flight completes first.
The notifier runs before tasks are frozen. Without a pending setup the
flush returns at once.

Fixes: 04b8c8143d46 ("Bluetooth: btusb: fix Realtek suspend/resume")
Cc: stable@vger.kernel.org
Signed-off-by: Jens Willmer <info@jwillmer.de>
---

Notes:
    Tested on kernel 7.2.15 (Pop!_OS build) with the same change applied to
    btusb.c, RTL8852CU 0bda:0852, firmware rtl_bt/rtl8852cu_fw_v2.bin.
    Reproducer: two suspends back to back, each woken by the RTC, so the
    second one starts while the firmware is downloading:
    
      A=/sys/class/rtc/rtc0/wakealarm
      echo 0 > $A; echo +20 > $A; echo mem > /sys/power/state
      echo 0 > $A; echo +25 > $A; echo mem > /sys/power/state
    
    Without the patch the second suspend kills the download and the
    controller is stuck after the wake (logs as in the commit message).
    With the patch the second suspend waits 1.2 s:
    
      [11733.476390] Bluetooth: hci0: RTL: loading rtl_bt/rtl8852cu_fw_v2.bin
      [11734.070703] PM: suspend exit
      [11734.071285] PM: suspend entry (deep)
      [11735.275401] Bluetooth: hci0: RTL: fw version 0x040d7225
      [11735.482027] Freezing remaining freezable tasks
    
    Not tested: this exact patch on bluetooth-next at run time (only built,
    i386 defconfig with BT_HCIBTUSB=m, W=1, no warnings), hibernation
    entered during the download, and controllers other than the RTL8852CU.
    
    The same wait could live in hci_suspend_notifier() in hci_core.c and
    would then cover every transport. I kept it in btusb because that is
    what I could test. I am happy to move it if that is preferred.
    
    Tool use: the analysis of the logs, the patch and this text were
    written with the AI assistant Claude (Anthropic), working from my
    problem report and logs on my machine. I reviewed the change and ran
    the tests above on my hardware.

 drivers/bluetooth/btusb_main.c | 35 +++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btusb_main.c b/drivers/bluetooth/btusb_main.c
index 95398e2ca..c8af3669e 100644
--- a/drivers/bluetooth/btusb_main.c
+++ b/drivers/bluetooth/btusb_main.c
@@ -1090,6 +1090,8 @@ struct btusb_data {
 	int oob_wake_irq;   /* irq for out-of-band wake-on-bt */
 
 	struct dentry *force_poll_sync_dentry;
+
+	struct notifier_block pm_notifier;
 };
 
 static void btusb_reset(struct hci_dev *hdev)
@@ -4382,6 +4384,29 @@ static int btusb_send_vendor_frame_qcom(struct hci_dev *hdev, struct sk_buff *sk
  * ============================================================================
  */
 
+/* The vendor setup (firmware download) runs in hdev->power_on before the
+ * device is powered, so hci_suspend_dev() does not wait for it and
+ * btusb_suspend() kills its URBs. A Realtek controller interrupted in the
+ * download stays half-patched until it is power cycled. Let the setup finish
+ * before the system goes to sleep.
+ */
+static int btusb_pm_notifier(struct notifier_block *nb, unsigned long action,
+			     void *unused)
+{
+	struct btusb_data *data = container_of(nb, struct btusb_data,
+					       pm_notifier);
+
+	switch (action) {
+	case PM_SUSPEND_PREPARE:
+	case PM_HIBERNATION_PREPARE:
+	case PM_RESTORE_PREPARE:
+		flush_work(&data->hdev->power_on);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
 static int btusb_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
 {
@@ -4816,10 +4841,17 @@ static int btusb_probe(struct usb_interface *intf,
 	if (data->proto == BTUSB_PROTO_LEGACY)
 		data->poll_sync = enable_poll_sync;
 
-	err = hci_register_dev(hdev);
+	data->pm_notifier.notifier_call = btusb_pm_notifier;
+	err = register_pm_notifier(&data->pm_notifier);
 	if (err < 0)
 		goto err_release_siblings;
 
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		unregister_pm_notifier(&data->pm_notifier);
+		goto err_release_siblings;
+	}
+
 	usb_set_intfdata(intf, data);
 
 	data->force_poll_sync_dentry =
@@ -4880,6 +4912,7 @@ static void btusb_disconnect(struct usb_interface *intf)
 	if (data->disconnect)
 		data->disconnect(hdev);
 
+	unregister_pm_notifier(&data->pm_notifier);
 	hci_unregister_dev(hdev);
 
 	if (data->oob_wake_irq) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-12  0:29 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 22:18 [PATCH] Bluetooth: btusb: Wait for setup to finish before system sleep Jens Willmer
2026-11-12  0:29 ` bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox