Skip to content

Commit 990592f

Browse files
committed
pbio/drv/bluetooth_btstack: Safely handle reentrancy on power change.
This properly protects the power handler from running multiple times instead of relying on the contents of the event packet.
1 parent a71d6bc commit 990592f

File tree

1 file changed

+33
-14
lines changed

1 file changed

+33
-14
lines changed

lib/pbio/drv/bluetooth/bluetooth_btstack.c

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -858,14 +858,37 @@ void pbdrv_bluetooth_controller_reset_hard(void) {
858858
hci_power_control(HCI_POWER_OFF);
859859
}
860860

861-
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer) {
861+
/**
862+
* btstack's hci_power_control() synchronously emits an event that would cause
863+
* it to re-enter the event loop. This would not be safe to call from within
864+
* the event loop. This wrapper ensures it is called at most once.
865+
*/
866+
static pbio_error_t bluetooth_btstack_handle_power_control(pbio_os_state_t *state, HCI_POWER_MODE power_mode, HCI_STATE end_state) {
867+
868+
bool do_it_this_time = false;
862869

863-
// The event handler also pushes the bluetooth process along, but shouldn't
864-
// be needing to touch the power state.
865-
if (event_packet) {
866-
return PBIO_ERROR_AGAIN;
870+
PBIO_OS_ASYNC_BEGIN(state);
871+
872+
do_it_this_time = true;
873+
PBIO_OS_ASYNC_SET_CHECKPOINT(state);
874+
875+
// The first time we get here, do_it_this_time = true, so we call
876+
// hci_power_control. When it re-enters at the checkpoint above, it will
877+
// be false, so move on.
878+
if (do_it_this_time) {
879+
hci_power_control(power_mode);
867880
}
868881

882+
// Wait for the power state to take effect.
883+
PBIO_OS_AWAIT_UNTIL(state, hci_get_state() == end_state);
884+
885+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
886+
}
887+
888+
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer) {
889+
890+
static pbio_os_state_t sub;
891+
869892
PBIO_OS_ASYNC_BEGIN(state);
870893

871894
// Disconnect gracefully if connected to host.
@@ -874,24 +897,20 @@ pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_ti
874897
PBIO_OS_AWAIT_UNTIL(state, le_con_handle == HCI_CON_HANDLE_INVALID);
875898
}
876899

877-
pbdrv_bluetooth_controller_reset_hard();
878-
PBIO_OS_AWAIT_UNTIL(state, hci_get_state() == HCI_STATE_OFF);
900+
// Wait for power off.
901+
PBIO_OS_AWAIT(state, &sub, bluetooth_btstack_handle_power_control(&sub, HCI_POWER_OFF, HCI_STATE_OFF));
879902

880903
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
881904
}
882905

883906
pbio_error_t pbdrv_bluetooth_controller_initialize(pbio_os_state_t *state, pbio_os_timer_t *timer) {
884907

885-
// The event handler also pushes the bluetooth process along, but shouldn't
886-
// be needing to touch the power state.
887-
if (event_packet) {
888-
return PBIO_ERROR_AGAIN;
889-
}
908+
static pbio_os_state_t sub;
890909

891910
PBIO_OS_ASYNC_BEGIN(state);
892911

893-
hci_power_control(HCI_POWER_ON);
894-
PBIO_OS_AWAIT_UNTIL(state, hci_get_state() == HCI_STATE_WORKING);
912+
// Wait for power on.
913+
PBIO_OS_AWAIT(state, &sub, bluetooth_btstack_handle_power_control(&sub, HCI_POWER_ON, HCI_STATE_WORKING));
895914

896915
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
897916
}

0 commit comments

Comments
 (0)