From e05d0a6335be6a45f29f5d987091b858ad378cfa Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Mar 2021 12:34:49 +1100 Subject: [PATCH 1/2] extmod/modbluetooth: Add support for running sync irq on system thread. If the Bluetooth stack runs on another OS thread then synchronous BLE irq callbacks, which block the Bluetooth stack until the callback to Python is complete, must coordinate with the main thread and configure the MicroPython thread-local-state. This commit adds MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK which can be enabled if the system has these requirements. Signed-off-by: Damien George --- extmod/modbluetooth.c | 73 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 9956671863109..4645ae6c98691 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -34,6 +34,7 @@ #include "py/objarray.h" #include "py/qstr.h" #include "py/runtime.h" +#include "py/stackctrl.h" #include "extmod/modbluetooth.h" #include @@ -1135,7 +1136,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_inv #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS -STATIC mp_obj_t invoke_irq_handler(uint16_t event, +STATIC mp_obj_t invoke_irq_handler_run(uint16_t event, const mp_int_t *numeric, size_t n_unsigned, size_t n_signed, const uint8_t *addr, const mp_obj_bluetooth_uuid_t *uuid, @@ -1185,6 +1186,76 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event, return result; } +#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK + +// On some systems the BLE event callbacks may occur on a system thread which is not +// a MicroPython thread. In such cases the callback must set up relevant MicroPython +// state and obtain the GIL, to synchronised with the rest of the runtime. + +#if MICROPY_ENABLE_PYSTACK +#error not supported +#endif + +STATIC mp_obj_t invoke_irq_handler(uint16_t event, + const mp_int_t *numeric, size_t n_unsigned, size_t n_signed, + const uint8_t *addr, + const mp_obj_bluetooth_uuid_t *uuid, + const uint8_t **data, size_t *data_len, size_t n_data) { + + // This code may run on an existing MicroPython thread, or a non-MicroPython thread + // that's not using the mp_thread_get_state() value. In the former case the state + // must be restored once this callback finishes. + mp_state_thread_t *ts_orig = mp_thread_get_state(); + + mp_state_thread_t ts; + if (ts_orig == NULL) { + mp_thread_set_state(&ts); + mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan + mp_stack_set_limit(MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE - 1024); + ts.gc_lock_depth = 0; + ts.mp_pending_exception = MP_OBJ_NULL; + mp_locals_set(mp_state_ctx.thread.dict_locals); // set from the outer context + mp_globals_set(mp_state_ctx.thread.dict_globals); // set from the outer context + MP_THREAD_GIL_ENTER(); + } + + mp_obj_t result = mp_const_none; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_sched_lock(); + result = invoke_irq_handler_run(event, numeric, n_unsigned, n_signed, addr, uuid, data, data_len, n_data); + mp_sched_unlock(); + nlr_pop(); + } else { + // Uncaught exception, print it out. + mp_sched_unlock(); + mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in IRQ callback handler\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + + if (ts_orig == NULL) { + MP_THREAD_GIL_EXIT(); + mp_thread_set_state(ts_orig); + } + + return result; +} + +#else + +// BLE event callbacks are called directly from the MicroPython runtime, so additional +// synchronisation is not needed, and BLE event handlers can be called directly. + +STATIC mp_obj_t invoke_irq_handler(uint16_t event, + const mp_int_t *numeric, size_t n_unsigned, size_t n_signed, + const uint8_t *addr, + const mp_obj_bluetooth_uuid_t *uuid, + const uint8_t **data, size_t *data_len, size_t n_data) { + return invoke_irq_handler_run(event, numeric, n_unsigned, n_signed, addr, uuid, data, data_len, n_data); +} + +#endif + #define NULL_NUMERIC NULL #define NULL_ADDR NULL #define NULL_UUID NULL From 5dbb822ca4a809ac5cb4513afb0411b4eb8dc3cf Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Mar 2021 12:35:59 +1100 Subject: [PATCH 2/2] esp32/mpconfigport: Enable BLE synchronous events and pairing/bonding. Signed-off-by: Damien George --- ports/esp32/main/CMakeLists.txt | 6 +++++- ports/esp32/mpconfigport.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index 9ac6537f86be1..fccfd6b7c7a9f 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -198,11 +198,15 @@ target_compile_options(${MICROPY_TARGET} PUBLIC -Wno-missing-field-initializers ) +# Additional include directories needed for private NimBLE headers. +target_include_directories(${MICROPY_TARGET} PUBLIC + ${IDF_PATH}/components/bt/host/nimble/nimble +) + # Add additional extmod and usermod components. target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) target_link_libraries(${MICROPY_TARGET} usermod) - # Collect all of the include directories and compile definitions for the IDF components. foreach(comp ${IDF_COMPONENTS}) micropy_gather_target_properties(__idf_${comp}) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 8dc3537e59d4a..dfa577d2bc693 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -70,7 +70,11 @@ // extended modules #ifndef MICROPY_PY_BLUETOOTH #define MICROPY_PY_BLUETOOTH (1) +#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1) +#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK (1) +#define MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE (CONFIG_BT_NIMBLE_TASK_STACK_SIZE) #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1) #define MICROPY_BLUETOOTH_NIMBLE (1) #define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1) #endif