diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 9956671863..4645ae6c98 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