* [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