Skip to content

Commit 8b576b3

Browse files
committed
pbio/drv/bluetooth: Add way to forcefully exit.
Such as mechanism had not yet been implemented after overhauling the Bluetooth drivers. Before, we used to try to disconnect and stop observing/scanning in the MicroPython deinit, and stop waiting in case of a shutdown request. No we do it as part of the pbio cleanup, and enforce shutdown if it fails since this is not a recoverable state.
1 parent a67ce01 commit 8b576b3

File tree

9 files changed

+112
-33
lines changed

9 files changed

+112
-33
lines changed

lib/pbio/drv/bluetooth/bluetooth.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,52 @@ pbio_error_t pbdrv_bluetooth_process_thread(pbio_os_state_t *state, void *contex
559559
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
560560
}
561561

562+
pbio_error_t pbdrv_bluetooth_close_user_tasks(pbio_os_state_t *state, pbio_os_timer_t *timer) {
563+
564+
static pbio_os_state_t sub;
565+
566+
if (pbio_os_timer_is_expired(timer)) {
567+
return PBIO_ERROR_TIMEDOUT;
568+
}
569+
570+
PBIO_OS_ASYNC_BEGIN(state);
571+
572+
pbdrv_bluetooth_cancel_operation_request();
573+
574+
// Let ongoing user tasks finish first.
575+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_await_peripheral_command(&sub, NULL));
576+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_await_advertise_or_scan_command(&sub, NULL));
577+
578+
// Disconnect peripheral.
579+
pbdrv_bluetooth_peripheral_disconnect();
580+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_await_peripheral_command(&sub, NULL));
581+
582+
// Stop scanning.
583+
pbdrv_bluetooth_start_observing(NULL);
584+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_await_advertise_or_scan_command(&sub, NULL));
585+
586+
// Stop advertising.
587+
pbdrv_bluetooth_start_broadcasting(NULL, 0);
588+
PBIO_OS_AWAIT(state, &sub, pbdrv_bluetooth_await_advertise_or_scan_command(&sub, NULL));
589+
590+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
591+
}
592+
562593
void pbdrv_bluetooth_deinit(void) {
594+
595+
// Under normal operation ::pbdrv_bluetooth_close_user_tasks completes
596+
// normally and there should be no user activity at this point. If there
597+
// is, a task got stuck, so exit forcefully.
598+
pbio_os_state_t unused;
599+
if (pbdrv_bluetooth_await_advertise_or_scan_command(&unused, NULL) != PBIO_SUCCESS ||
600+
pbdrv_bluetooth_await_peripheral_command(&unused, NULL) != PBIO_SUCCESS) {
601+
602+
// Hard reset without waitng on completion of any process.
603+
pbdrv_bluetooth_controller_reset_hard();
604+
return;
605+
}
606+
607+
// Gracefully disconnect from host and power down.
563608
pbio_busy_count_up();
564609
pbdrv_bluetooth_cancel_operation_request();
565610
shutting_down = true;

lib/pbio/drv/bluetooth/bluetooth.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#define PBDRV_BLUETOOTH_MAX_ADV_SIZE 31
2525

2626
void pbdrv_bluetooth_init_hci(void);
27+
void pbdrv_bluetooth_controller_reset_hard(void);
2728
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer);
2829
pbio_error_t pbdrv_bluetooth_controller_initialize(pbio_os_state_t *state, pbio_os_timer_t *timer);
2930

lib/pbio/drv/bluetooth/bluetooth_btstack.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ const char *pbdrv_bluetooth_get_fw_version(void) {
848848
return "v1.4";
849849
}
850850

851+
void pbdrv_bluetooth_controller_reset_hard(void) {
852+
hci_power_control(HCI_POWER_OFF);
853+
}
854+
851855
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer) {
852856

853857
// The event handler also pushes the bluetooth process along, but shouldn't
@@ -864,7 +868,7 @@ pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_ti
864868
PBIO_OS_AWAIT_UNTIL(state, le_con_handle == HCI_CON_HANDLE_INVALID);
865869
}
866870

867-
hci_power_control(HCI_POWER_OFF);
871+
pbdrv_bluetooth_controller_reset_hard();
868872
PBIO_OS_AWAIT_UNTIL(state, hci_get_state() == HCI_STATE_OFF);
869873

870874
PBIO_OS_ASYNC_END(PBIO_SUCCESS);

lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,13 @@ static pbio_error_t init_uart_service(pbio_os_state_t *state, void *context) {
12371237
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
12381238
}
12391239

1240+
void pbdrv_bluetooth_controller_reset_hard(void) {
1241+
pybricks_notify_en = uart_tx_notify_en = false;
1242+
conn_handle = peripheral_singleton.con_handle = 0;
1243+
spi_disable_cs();
1244+
bluetooth_reset(true);
1245+
}
1246+
12401247
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer) {
12411248
PBIO_OS_ASYNC_BEGIN(state);
12421249

@@ -1249,11 +1256,8 @@ pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_ti
12491256
PBIO_OS_AWAIT_UNTIL(state, conn_handle == 0);
12501257
}
12511258

1252-
pybricks_notify_en = uart_tx_notify_en = false;
1253-
conn_handle = peripheral_singleton.con_handle = 0;
1259+
pbdrv_bluetooth_controller_reset_hard();
12541260

1255-
spi_disable_cs();
1256-
bluetooth_reset(true);
12571261
PBIO_OS_AWAIT_MS(state, timer, 50);
12581262

12591263
PBIO_OS_ASYNC_END(PBIO_SUCCESS);

lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,14 @@ static pbio_error_t init_uart_service(pbio_os_state_t *state, void *context) {
18371837
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
18381838
}
18391839

1840+
void pbdrv_bluetooth_controller_reset_hard(void) {
1841+
pybricks_notify_en = uart_tx_notify_en = false;
1842+
conn_handle = peripheral_singleton.con_handle = NO_CONNECTION;
1843+
1844+
spi_set_mrdy(false);
1845+
bluetooth_reset(RESET_STATE_OUT_LOW);
1846+
}
1847+
18401848
pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_timer_t *timer) {
18411849
PBIO_OS_ASYNC_BEGIN(state);
18421850

@@ -1847,11 +1855,8 @@ pbio_error_t pbdrv_bluetooth_controller_reset(pbio_os_state_t *state, pbio_os_ti
18471855
PBIO_OS_AWAIT_UNTIL(state, conn_handle == NO_CONNECTION);
18481856
}
18491857

1850-
pybricks_notify_en = uart_tx_notify_en = false;
1851-
conn_handle = peripheral_singleton.con_handle = NO_CONNECTION;
1858+
pbdrv_bluetooth_controller_reset_hard();
18521859

1853-
spi_set_mrdy(false);
1854-
bluetooth_reset(RESET_STATE_OUT_LOW);
18551860
PBIO_OS_AWAIT_MS(state, timer, 150);
18561861

18571862
PBIO_OS_ASYNC_END(PBIO_SUCCESS);

lib/pbio/include/pbdrv/bluetooth.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,19 @@ void pbdrv_bluetooth_restart_observing_request(void);
503503
*/
504504
pbio_error_t pbdrv_bluetooth_await_advertise_or_scan_command(pbio_os_state_t *state, void *context);
505505

506+
/**
507+
* Awaits user activity to complete, usually called during cleanup after running
508+
* a user program. This will disconnect from the peripheral and stop scanning
509+
* and advertising.
510+
*
511+
* @param [in] state Protothread state.
512+
* @param [in] timer Timer used to give up if this takes too long.
513+
* @return ::PBIO_SUCCESS on completion.
514+
* ::PBIO_ERROR_AGAIN while awaiting.
515+
* ::PBIO_ERROR_TIMEDOUT if the timer expired.
516+
*/
517+
pbio_error_t pbdrv_bluetooth_close_user_tasks(pbio_os_state_t *state, pbio_os_timer_t *timer);
518+
506519
#else // PBDRV_CONFIG_BLUETOOTH
507520

508521
static inline void pbdrv_bluetooth_init(void) {
@@ -602,6 +615,11 @@ static inline pbio_error_t pbdrv_bluetooth_await_advertise_or_scan_command(pbio_
602615
return PBIO_ERROR_NOT_SUPPORTED;
603616
}
604617

618+
static inline pbio_error_t pbdrv_bluetooth_close_user_tasks(pbio_os_state_t *state, pbio_os_timer_t *timer) {
619+
return PBIO_ERROR_NOT_SUPPORTED;
620+
}
621+
622+
605623
#endif // PBDRV_CONFIG_BLUETOOTH
606624

607625
#endif // _PBDRV_BLUETOOTH_H_

lib/pbio/include/pbio/main.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
#include "pbio/config.h"
1010

11+
#include <pbio/error.h>
12+
1113
void pbio_init(bool start_processes);
1214
void pbio_deinit(void);
13-
void pbio_main_stop_application_resources();
15+
pbio_error_t pbio_main_stop_application_resources(void);
1416
void pbio_main_soft_stop(void);
1517

1618
#endif // _PBIO_MAIN_H_

lib/pbio/src/main.c

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,37 +69,28 @@ void pbio_main_soft_stop(void) {
6969
pbdrv_bluetooth_cancel_operation_request();
7070
}
7171

72-
static void wait_for_bluetooth(void) {
73-
pbio_os_state_t unused;
74-
while (pbdrv_bluetooth_await_advertise_or_scan_command(&unused, NULL) == PBIO_ERROR_AGAIN ||
75-
pbdrv_bluetooth_await_peripheral_command(&unused, NULL) == PBIO_ERROR_AGAIN) {
76-
77-
// Run event loop until Bluetooth is idle.
78-
pbio_os_run_processes_and_wait_for_event();
79-
}
80-
}
81-
8272
/**
8373
* Stops all application-level background processes. Called when the user
8474
* application completes to get these modules back into their default state.
8575
* Drivers and OS-level processes continue running.
76+
*
77+
* @return ::PBIO_SUCCESS when completed
78+
* ::PBIO_ERROR_TIMEDOUT if it could not stop processes in a reasonable
79+
* amount of time.
8680
*/
87-
void pbio_main_stop_application_resources() {
81+
pbio_error_t pbio_main_stop_application_resources(void) {
8882

8983
pbio_main_soft_stop();
9084

91-
// Let ongoing task finish first.
92-
wait_for_bluetooth();
85+
pbio_error_t err;
86+
pbio_os_state_t state = 0;
87+
pbio_os_timer_t timer;
88+
pbio_os_timer_set(&timer, 5000);
9389

94-
// Stop broadcasting, observing and disconnect peripheral.
95-
pbdrv_bluetooth_start_broadcasting(NULL, 0);
96-
wait_for_bluetooth();
97-
98-
pbdrv_bluetooth_start_observing(NULL);
99-
wait_for_bluetooth();
100-
101-
pbdrv_bluetooth_peripheral_disconnect();
102-
wait_for_bluetooth();
90+
// Run event loop until Bluetooth is idle or times out.
91+
while ((err = pbdrv_bluetooth_close_user_tasks(&state, &timer)) == PBIO_ERROR_AGAIN) {
92+
pbio_os_run_processes_and_wait_for_event();
93+
}
10394

10495
#if PBIO_CONFIG_LIGHT
10596
pbio_light_animation_stop_all();
@@ -109,6 +100,10 @@ void pbio_main_stop_application_resources() {
109100
pbio_image_fill(pbdrv_display_get_image(), 0);
110101
pbdrv_display_update();
111102
#endif
103+
104+
pbio_os_run_processes_and_wait_for_event();
105+
106+
return err;
112107
}
113108

114109
/** @} */

lib/pbio/sys/main.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,12 @@ int main(int argc, char **argv) {
114114
pbsys_main_run_program(&program);
115115

116116
// Stop motors, user animations, user bluetooth activity, etc.
117-
pbio_main_stop_application_resources();
117+
err = pbio_main_stop_application_resources();
118+
if (err != PBIO_SUCCESS) {
119+
// If we couldn't get the system back in a normal state, proceed
120+
// towards shutdown.
121+
break;
122+
}
118123

119124
// Get system back in idle state.
120125
pbsys_status_clear(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING);

0 commit comments

Comments
 (0)