extmod/modbluetooth: Add timeout to deinit.

If the BLE radio stops responding before deinit is called the function can
get stuck waiting for an event that is never received, particularly if the
radio is external or on a separate core.

This commit adds a timeout, similar to the timeout already used in the init
function.  Updated for nimble, btstack, esp32 and zephyr bindings.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
This commit is contained in:
Andrew Leech
2025-05-07 09:11:25 +10:00
committed by Damien George
parent 17898f8607
commit d5f2fc239a
8 changed files with 34 additions and 16 deletions

View File

@@ -705,12 +705,12 @@ int mp_bluetooth_init(void) {
return 0;
}
void mp_bluetooth_deinit(void) {
int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit\n");
// Nothing to do if not initialised.
if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) {
return;
return 0;
}
mp_bluetooth_gap_advertise_stop();
@@ -737,6 +737,9 @@ void mp_bluetooth_deinit(void) {
deinit_stack();
DEBUG_printf("mp_bluetooth_deinit: complete\n");
bool timeout = mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT;
return timeout ? MP_ETIMEDOUT : 0;
}
bool mp_bluetooth_is_active(void) {

View File

@@ -290,12 +290,13 @@ static mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
static mp_obj_t bluetooth_ble_active(size_t n_args, const mp_obj_t *args) {
if (n_args == 2) {
// Boolean enable/disable argument supplied, set current state.
int err;
if (mp_obj_is_true(args[1])) {
int err = mp_bluetooth_init();
bluetooth_handle_errno(err);
err = mp_bluetooth_init();
} else {
mp_bluetooth_deinit();
err = mp_bluetooth_deinit();
}
bluetooth_handle_errno(err);
}
// Return current state.
return mp_obj_new_bool(mp_bluetooth_is_active());

View File

@@ -295,7 +295,7 @@ extern const mp_obj_type_t mp_type_bluetooth_uuid;
int mp_bluetooth_init(void);
// Disables the Bluetooth stack. Is a no-op when not enabled.
void mp_bluetooth_deinit(void);
int mp_bluetooth_deinit(void);
// Returns true when the Bluetooth stack is active.
bool mp_bluetooth_is_active(void);

View File

@@ -60,6 +60,7 @@
static uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM;
#define NIMBLE_STARTUP_TIMEOUT 2000
#define NIMBLE_SHUTDOWN_TIMEOUT 500
// Any BLE_HS_xxx code not in this table will default to MP_EIO.
static int8_t ble_hs_err_to_errno_table[] = {
@@ -554,7 +555,7 @@ static void ble_hs_shutdown_stop_cb(int status, void *arg) {
static struct ble_hs_stop_listener ble_hs_shutdown_stop_listener;
void mp_bluetooth_nimble_port_shutdown(void) {
int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n");
// By default, just call ble_hs_stop directly and wait for the stack to stop.
@@ -562,9 +563,17 @@ void mp_bluetooth_nimble_port_shutdown(void) {
ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL);
mp_uint_t timeout_start_ticks_ms = mp_hal_ticks_ms();
while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
mp_event_wait_indefinite();
mp_uint_t elapsed = mp_hal_ticks_ms() - timeout_start_ticks_ms;
if (elapsed > NIMBLE_SHUTDOWN_TIMEOUT) {
// Stack had not responded (via ble_hs_shutdown_stop_cb)
return MP_ETIMEDOUT;
}
mp_event_wait_ms(NIMBLE_SHUTDOWN_TIMEOUT - elapsed);
}
return 0;
}
#endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
@@ -659,10 +668,11 @@ int mp_bluetooth_init(void) {
return 0;
}
void mp_bluetooth_deinit(void) {
int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_nimble_ble_state);
int ret = 0;
if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
return;
return 0;
}
// Must call ble_hs_stop() in a port-specific way to stop the background
@@ -675,7 +685,7 @@ void mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n");
mp_bluetooth_nimble_port_shutdown();
ret = mp_bluetooth_nimble_port_shutdown();
assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF);
} else {
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
@@ -692,6 +702,7 @@ void mp_bluetooth_deinit(void) {
#endif
DEBUG_printf("mp_bluetooth_deinit: shut down\n");
return ret;
}
bool mp_bluetooth_is_active(void) {

View File

@@ -84,7 +84,7 @@ void mp_bluetooth_nimble_port_hci_deinit(void);
void mp_bluetooth_nimble_port_start(void);
// Tell the port to stop its background task.
void mp_bluetooth_nimble_port_shutdown(void);
int mp_bluetooth_nimble_port_shutdown(void);
// --- Called by the HCI UART layer to let us know when packets have been sent.
void mp_bluetooth_nimble_sent_hci_packet(void);

View File

@@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) {
nimble_port_freertos_init(ble_host_task);
}
void mp_bluetooth_nimble_port_shutdown(void) {
int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
@@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) {
// Mark stack as shutdown.
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
return 0;
}
#endif

View File

@@ -775,7 +775,7 @@ MP_NOINLINE int main_(int argc, char **argv) {
#endif
#if MICROPY_PY_BLUETOOTH
void mp_bluetooth_deinit(void);
int mp_bluetooth_deinit(void);
mp_bluetooth_deinit();
#endif

View File

@@ -305,11 +305,11 @@ int mp_bluetooth_init(void) {
return 0;
}
void mp_bluetooth_deinit(void) {
int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state);
if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF
|| mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) {
return;
return 0;
}
mp_bluetooth_gap_advertise_stop();
@@ -332,6 +332,7 @@ void mp_bluetooth_deinit(void) {
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL;
mp_bt_zephyr_next_conn = NULL;
return 0;
}
bool mp_bluetooth_is_active(void) {