From e3bee803b466e3f91c0fd9c58279946a185a8251 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Mar 2021 12:34:49 +1100 Subject: [PATCH 1/3] extmod/modbluetooth: Support BT callbacks running on another OS thread. If NimBLE runs on another OS thread then synchronous BLE irq callbacks, which block the NimBLE stack until the callback to Python is complete, must coordinate with the main MicroPython OS thread. Signed-off-by: Damien George --- extmod/modbluetooth.c | 39 +++++++++++++++++++++++++++++++-------- extmod/modbluetooth.h | 8 ++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index e379a8c6a304c..2d22c17dc900b 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -66,9 +66,12 @@ typedef struct { mp_obj_base_t base; mp_obj_t irq_handler; + mp_obj_t irq_data_tuple; + #if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD + mp_obj_t irq_event; + #endif #if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS bool irq_scheduled; - mp_obj_t irq_data_tuple; uint8_t irq_data_addr_bytes[6]; uint16_t irq_data_data_alloc; mp_obj_array_t irq_data_addr; @@ -260,10 +263,10 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, o->irq_handler = mp_const_none; - #if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // Pre-allocate the event data tuple to prevent needing to allocate in the IRQ handler. o->irq_data_tuple = mp_obj_new_tuple(MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN, NULL); + #if !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // Pre-allocated buffers for address, payload and uuid. mp_obj_memoryview_init(&o->irq_data_addr, 'B', 0, 0, o->irq_data_addr_bytes); o->irq_data_data_alloc = MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN(MICROPY_PY_BLUETOOTH_RINGBUF_SIZE); @@ -274,6 +277,10 @@ STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, ringbuf_alloc(&o->ringbuf, MICROPY_PY_BLUETOOTH_RINGBUF_SIZE); #endif + #if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD + mp_bluetooth_port_sync_init(); + #endif + MP_STATE_VM(bluetooth) = MP_OBJ_FROM_PTR(o); } return MP_STATE_VM(bluetooth); @@ -1111,6 +1118,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_inv #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS +#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD +STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t bluetooth_in) { + mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(bluetooth_in); + o->irq_event = mp_call_function_2(o->irq_handler, o->irq_event, o->irq_data_tuple); + mp_bluetooth_port_sync_signal_done(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_invoke_irq); +#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, @@ -1125,8 +1142,7 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event, mp_obj_array_t mv_data[2]; assert(n_data <= 2); - mp_obj_tuple_t *data_tuple = mp_local_alloc(sizeof(mp_obj_tuple_t) + sizeof(mp_obj_t) * MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN); - data_tuple->base.type = &mp_type_tuple; + mp_obj_tuple_t *data_tuple = MP_OBJ_TO_PTR(o->irq_data_tuple); data_tuple->len = 0; for (size_t i = 0; i < n_unsigned; ++i) { @@ -1154,11 +1170,18 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event, } assert(data_tuple->len <= MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN); - mp_obj_t result = mp_call_function_2(o->irq_handler, MP_OBJ_NEW_SMALL_INT(event), MP_OBJ_FROM_PTR(data_tuple)); - - mp_local_free(data_tuple); + #if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD + if (!mp_bluetooth_port_sync_is_main_thread()) { + o->irq_event = MP_OBJ_NEW_SMALL_INT(event); + while (!mp_sched_schedule(MP_OBJ_FROM_PTR(&bluetooth_ble_invoke_irq_obj), MP_OBJ_FROM_PTR(o))) { + mp_bluetooth_port_sync_yield(); + } + mp_bluetooth_port_sync_wait_for_signal(); + return o->irq_event; + } + #endif - return result; + return mp_call_function_2(o->irq_handler, MP_OBJ_NEW_SMALL_INT(event), MP_OBJ_FROM_PTR(data_tuple)); } #define NULL_NUMERIC NULL diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index d126ad6c11344..9c5fb6725d09c 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -405,6 +405,14 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf int mp_bluetooth_hci_cmd(uint16_t ogf, uint16_t ocf, const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_len, uint8_t *status); #endif // MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD +#if MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD +void mp_bluetooth_port_sync_init(void); +bool mp_bluetooth_port_sync_is_main_thread(void); +void mp_bluetooth_port_sync_yield(void); +void mp_bluetooth_port_sync_wait_for_signal(void); +void mp_bluetooth_port_sync_signal_done(void); +#endif + ///////////////////////////////////////////////////////////////////////////// // API implemented by modbluetooth (called by port-specific implementations): From 6f02de0b64d62ad3fe3deb487195a441e583a1b5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Mar 2021 12:35:17 +1100 Subject: [PATCH 2/3] extmod/nimble/modbluetooth_nimble: Disable assertions on addr type. ESP32 generates addresses of the form 0:0:0:0:0:0. Signed-off-by: Damien George --- extmod/nimble/modbluetooth_nimble.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index e3a2f872e5ecb..f653b95d3e699 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1791,9 +1791,9 @@ STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, unio case BLE_STORE_OBJ_TYPE_OUR_SEC: { // // Find our secret for this remote device, matching this ediv/rand key. - assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address. + // TODO: Support cases where key->sec.peer_addr is BLE_ADDR_ANY, and + // key->sec.ediv_rand_present is false. assert(key->sec.idx == 0); - assert(key->sec.ediv_rand_present); key_data = (const uint8_t *)&key->sec.peer_addr; key_data_len = sizeof(ble_addr_t); break; From 886b24bc26f5a217125dd930674e71f35efa89c9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 17 Mar 2021 12:35:59 +1100 Subject: [PATCH 3/3] esp32: Enable BLE synchronous events and BLE pairing/bonding. Signed-off-by: Damien George --- ports/esp32/main/CMakeLists.txt | 6 +++++- ports/esp32/mpconfigport.h | 3 +++ ports/esp32/mphalport.c | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index 656045da90a21..3c3c723138de4 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -172,11 +172,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 8788963cb3209..b6eb9adf3f33c 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -124,7 +124,10 @@ // extended modules #ifndef MICROPY_PY_BLUETOOTH #define MICROPY_PY_BLUETOOTH (1) +#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1) +#define MICROPY_PY_BLUETOOTH_IRQ_CAN_BE_ON_SEPARATE_THREAD (1) #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 diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index cf668216df9e3..8d077c5293508 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -213,3 +213,28 @@ void mp_hal_wake_main_task_from_isr(void) { portYIELD_FROM_ISR(); } } + +/******************************************************************************/ +// Bluetooth event synchronisation helpers. + +SemaphoreHandle_t mp_bluetooth_port_sync_sem; + +void mp_bluetooth_port_sync_init(void) { + mp_bluetooth_port_sync_sem = xSemaphoreCreateBinary(); +} + +bool mp_bluetooth_port_sync_is_main_thread(void) { + return xTaskGetCurrentTaskHandle() == mp_main_task_handle; +} + +void mp_bluetooth_port_sync_yield(void) { + vPortYield(); +} + +void mp_bluetooth_port_sync_wait_for_signal(void) { + xSemaphoreTake(mp_bluetooth_port_sync_sem, portMAX_DELAY); +} + +void mp_bluetooth_port_sync_signal_done(void) { + xSemaphoreGive(mp_bluetooth_port_sync_sem); +}