Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off
@ 2026-10-10 11:35 Birk Skyum
  2026-10-14 16:20 ` Vladyslav Tymoshchuk
  0 siblings, 1 reply; 4+ messages in thread
From: Birk Skyum @ 2026-10-10 11:35 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-arm-msm, linux-pm, linux-kernel

qca_power_off() cuts the power of the controller but leaves the host
side of the in-band sleep (IBS) protocol as it was. tx_ibs_state and
rx_ibs_state are initialised in qca_open() only, so a controller that
qca_setup() powers on again meets whatever state the last session ended
in.

If that state is HCI_IBS_TX_WAKING, the host is waiting for a wake
acknowledgment, and qca_hci_shutdown() has deleted the timer that
sends the wake indication again. The setup still works, because frames
bypass IBS while QCA_IBS_DISABLED is set. Once the flag is cleared,
qca_enqueue() puts every frame on tx_wait_q and nothing takes it off:

  Bluetooth: hci0: QCA setup on UART is completed
  Bluetooth: hci0: Opcode 0x0c03 failed: -110

A restore from hibernation gets there on a Lenovo Yoga Slim 7x
(WCN7850). The controller has been through a power cycle, while the
restored kernel holds the state from before the image was taken. Its
wake indications go unanswered, the commands behind them time out, and
the core closes the device and sets it up again. From then on
tx_ibs_state reads 1 in debugfs and Bluetooth stays dead until hci_uart
is reloaded.

When the controller loses power, put both sides back to asleep, which
is where qca_open() starts, and stop the two IBS timers.

Signed-off-by: Birk Skyum <birk.skyum@pm.me>
---
Tested on a Lenovo Yoga Slim 7x with Arch Linux ARM's v7.2 kernel and
hci_uart rebuilt with this change. qca_open(), qca_enqueue() and
qca_hci_shutdown() are the same there as in bluetooth-next, which this
is based on and where it builds without warnings (W=1).

Each run is a fresh boot, a hibernation with a real power-off and a
resume, with Bluetooth powered before it:

- Without the patch Bluetooth is dead afterwards in four runs out of
  four, with the log above. In the one run where I read it, debugfs
  showed tx_ibs_state=1, ibs_sent_wakes=109 and ibs_recv_wake_acks=8.
- With the patch it is powered again by itself in two runs out of two,
  about 20 seconds after the resume. The log shows a failed version
  read and "Retry BT power ON:0" before a setup that works. In the run
  where I read it, debugfs showed tx_ibs_state=0 afterwards.
- Powering the controller off and on with bluetoothctl still works.

The 20 seconds are the commands that time out before the core gives up
on the old session. This patch does not change that part.

The kernel also carried the hibernation changes in [1], [2] and [3],
without which this machine does not get this far, and a GENI serial
change that is not ready for the list.

[1] https://lore.kernel.org/r/179157746908.73485.2343672865542690797@pm.me
[2] https://lore.kernel.org/r/179158127177.70185.5691100900886332545@pm.me
[3] https://lore.kernel.org/r/179163141354.18006.80461347454353634@pm.me

 drivers/bluetooth/hci_qca.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 7089e9b63..19a75faba 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -2235,6 +2235,11 @@ static void qca_power_off(struct hci_uart *hu)
 	spin_lock_irqsave(&qca->hci_ibs_lock, flags);
 	set_bit(QCA_IBS_DISABLED, &qca->flags);
 	qca_flush(hu);
+	/* A controller that loses power is asleep when it comes back. */
+	timer_delete(&qca->wake_retrans_timer);
+	timer_delete(&qca->tx_idle_timer);
+	qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
+	qca->rx_ibs_state = HCI_IBS_RX_ASLEEP;
 	spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
 
 	/* Non-serdev device usually is powered by external power
-- 
2.56.0



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

* Re: [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off
  2026-10-10 11:35 [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off Birk Skyum
@ 2026-10-14 16:20 ` Vladyslav Tymoshchuk
  2026-10-14 17:36   ` Birk Skyum
  0 siblings, 1 reply; 4+ messages in thread
From: Vladyslav Tymoshchuk @ 2026-10-14 16:20 UTC (permalink / raw)
  To: Birk Skyum
  Cc: Vladyslav Tymoshchuk, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, linux-bluetooth, linux-arm-msm, linux-pm,
	linux-kernel

Hi Birk,

On Sat, 10 Oct 2026 11:35:26 +0000, Birk Skyum wrote:
> qca_power_off() cuts the power of the controller but leaves the host
> side of the in-band sleep (IBS) protocol as it was. tx_ibs_state and
> rx_ibs_state are initialised in qca_open() only, so a controller that
> qca_setup() powers on again meets whatever state the last session ended
> in.

I ran into the same bug from another side, on a OnePlus Pad 3 (SM8750,
WCN7880 on UART at 8 Mbaud with IBS), and came to the same fix in the
same place, so here is a second trigger and some test numbers.

The second trigger is HCI_IBS_TX_AWAKE rather than WAKING:
qca_hci_shutdown() deletes the IBS timers and then sends the
pre-shutdown command, which goes through qca_enqueue() and arms
tx_idle_timer again for 2 s. If the device is opened again within those
2 s, the host still believes the controller is awake and sends the
frames after the new setup without an HCI_IBS_WAKE_IND, while the
freshly booted controller starts asleep. The commands right after the
setup get through, the first one after the controller has gone to sleep
breaks the link:

  Bluetooth: hci0: Frame reassembly failed (-84)
  Bluetooth: hci0: Opcode 0x0c56 failed: -110
  Bluetooth: hci0: crash the soc to collect controller dump

It shows up with "btmgmt power off" followed by "power on" within 2 s,
and on every boot of a board without local-bd-address when user space
sets the address with MGMT Set Public Address right after registration
(the unconfigured controller is powered down after its setup and up
again well within the 2 s). The stale idle timer can also queue an
HCI_IBS_SLEEP_IND in the middle of the next setup, which is what
df1e5c51492f was about.

> +	/* A controller that loses power is asleep when it comes back. */
> +	timer_delete(&qca->wake_retrans_timer);
> +	timer_delete(&qca->tx_idle_timer);
> +	qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
> +	qca->rx_ibs_state = HCI_IBS_RX_ASLEEP;
>  	spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);

I agree with what the Sashiko review pointed out [1]: without _sync a
timer callback that is already waiting for hci_ibs_lock still runs
after the unlock, and ws_awake_device/ws_awake_rx can queue a wake
indication after the reset. As both timer callbacks take hci_ibs_lock,
the _sync variants have to be called outside of it. This is what I
have been testing (on bluetooth-next):

	/* From this point we go into power off state. But serial port may
	 * still be open, stop queueing the IBS data.
	 */
	spin_lock_irqsave(&qca->hci_ibs_lock, flags);
	set_bit(QCA_IBS_DISABLED, &qca->flags);
	spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);

	/* The controller loses the IBS state with its power and comes back
	 * with both sides asleep, as qca_open() assumes. Stop the IBS work and
	 * timers, drop what they queued and start over from there.
	 */
	cancel_work_sync(&qca->ws_awake_device);
	cancel_work_sync(&qca->ws_awake_rx);
	timer_delete_sync(&qca->wake_retrans_timer);
	timer_delete_sync(&qca->tx_idle_timer);

	spin_lock_irqsave(&qca->hci_ibs_lock, flags);
	qca_flush(hu);
	qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
	qca->rx_ibs_state = HCI_IBS_RX_ASLEEP;
	spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);

All callers of qca_power_off() can sleep, and the work items do not
call it, so there is no wait cycle. The flush moves after the waits,
so it also drops what the work and the timers queued in the meantime.

Sashiko also suggested Fixes: and Cc: stable, with df1e5c51492f. I
would point Fixes: further back:

Fixes: 3e4be65eb82c ("Bluetooth: hci_qca: Add poweroff support during hci down for wcn3990")
Cc: stable@vger.kernel.org

3e4be65eb82c is where the IBS state started to outlive the power of the
controller; the AWAKE case exists since then, and df1e5c51492f made the
WAKING case permanent. Mentioning the AWAKE trigger in the commit
message would help too.

Test numbers, five quick power cycles each (power on within 2 s of the
power off, then a scan): 4 of 5 fail without the change, with commands
timing out ("Opcode 0x2042 failed: -110"), and 0 of 5 with the code
above. With it, user space also sets the address at boot without having
to wait for the 2 s first, and it is now in the kernel I use on this
tablet every day. I have not tried your v1 as posted; I expect it to
cover my case as well most of the time, but I have only tested the form
above.

Would you fold this into a v2? I am happy to test it on this board and
give my Tested-by there. If you would rather not, I can send it as a
separate patch with your Reported-by, whichever you and the
maintainers prefer.

Thanks,
Vladyslav

[1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/179163212446.46972.2306254853726299632%40pm.me

(I used an AI assistant, Claude, for the analysis and to draft this
mail; the tests ran on my device.)

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

* Re: [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off
  2026-10-14 16:20 ` Vladyslav Tymoshchuk
@ 2026-10-14 17:36   ` Birk Skyum
  2026-10-14 17:50     ` Vladyslav Tymoshchuk
  0 siblings, 1 reply; 4+ messages in thread
From: Birk Skyum @ 2026-10-14 17:36 UTC (permalink / raw)
  To: Vladyslav Tymoshchuk
  Cc: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	linux-bluetooth, linux-arm-msm, linux-pm, linux-kernel

Hi Vladyslav,

On Wed, 14 Oct 2026 19:20:24 +0300, Vladyslav Tymoshchuk wrote:
> Would you fold this into a v2? I am happy to test it on this board and
> give my Tested-by there.

Yes, and thanks for the second trigger and the numbers.

I ran your quick power cycle on the Yoga Slim 7x (WCN7850) with
v7.3-rc7, ten cycles per module, power on 1 s after the power off and a
scan after each. All of them pass here, with the stock module too, so
this board does not show the failure. It does show the stale state.
Right after the power off, debugfs reads tx_ibs_state=2 and
rx_ibs_state=1 with the stock module, and 0 and 0 with v1 and with your
version. Your version also builds without warnings (W=1) on today's
bluetooth-next.

v2 takes your code, describes the AWAKE trigger next to the WAKING one,
and carries Fixes: 3e4be65eb82c and Cc: stable. The code is yours, so I
will add you as Co-developed-by, which needs your Signed-off-by. Reply
with it and it goes in above mine.

Birk


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

* Re: [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off
  2026-10-14 17:36   ` Birk Skyum
@ 2026-10-14 17:50     ` Vladyslav Tymoshchuk
  0 siblings, 0 replies; 4+ messages in thread
From: Vladyslav Tymoshchuk @ 2026-10-14 17:50 UTC (permalink / raw)
  To: Birk Skyum
  Cc: Vladyslav Tymoshchuk, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, linux-bluetooth, linux-arm-msm, linux-pm,
	linux-kernel

Hi Birk,

On Wed, 14 Oct 2026 17:36:59 +0000, Birk Skyum wrote:
> v2 takes your code, describes the AWAKE trigger next to the WAKING one,
> and carries Fixes: 3e4be65eb82c and Cc: stable. The code is yours, so I
> will add you as Co-developed-by, which needs your Signed-off-by. Reply
> with it and it goes in above mine.

Thanks for running the power cycle on the Yoga. It is good to know the
stale state shows up there as well.

Co-developed-by: Vladyslav Tymoshchuk <tvlad744@gmail.com>
Signed-off-by: Vladyslav Tymoshchuk <tvlad744@gmail.com>

I wrote my version with the help of an AI assistant (Claude), so please
also carry an Assisted-by line for it, as described in
Documentation/process/coding-assistants.rst:

Assisted-by: Claude:claude-opus-5-5

When v2 is out I will run it on the Pad 3 (power on within 2 s of the
power off, and boots without local-bd-address) and report back in the
v2 thread.

Thanks,
Vladyslav

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

end of thread, other threads:[~2026-10-14 17:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-10 11:35 [PATCH] Bluetooth: hci_qca: Reset the in-band sleep state on power off Birk Skyum
2026-10-14 16:20 ` Vladyslav Tymoshchuk
2026-10-14 17:36   ` Birk Skyum
2026-10-14 17:50     ` Vladyslav Tymoshchuk

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