diff --git a/ports/zephyr-cp/background.c b/ports/zephyr-cp/background.c index 56e9e98f1f2..4b8fd06cd37 100644 --- a/ports/zephyr-cp/background.c +++ b/ports/zephyr-cp/background.c @@ -9,6 +9,10 @@ #include "py/runtime.h" #include "supervisor/port.h" +#if CIRCUITPY_BLEIO +#include "common-hal/_bleio/__init__.h" +#endif + #include void port_start_background_tick(void) { @@ -26,4 +30,7 @@ void port_background_task(void) { #if defined(CONFIG_ARCH_POSIX) k_busy_wait(100); #endif + #if CIRCUITPY_BLEIO + bleio_background(); + #endif } diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index 7819e68676e..ec25cb43ae3 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -51,6 +51,9 @@ void bleio_request_bluetooth_background(void) { static bool scan_callbacks_registered = false; static bleio_scanresults_obj_t *active_scan_results = NULL; static struct bt_le_scan_cb scan_callbacks; +// supervisor_ticks_ms64() value at which the running scan must stop, or 0 for +// no timeout. See bleio_background() for why the host side enforces this. +static uint64_t scan_deadline_ms; static bool ble_advertising = false; // True when advertising was started by the BLE workflow (supervisor) rather // than user code. Lets the workflow restart its own adverts without disturbing @@ -286,6 +289,7 @@ static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf } static void scan_timeout_cb(void) { + scan_deadline_ms = 0; if (active_scan_results == NULL) { return; } @@ -729,10 +733,21 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t raise_zephyr_error(err); } + // Zephyr hands scan_params.timeout to the controller only on the extended + // scanning path (LE Set Extended Scan Enable carries a duration and the + // controller reports LE Scan Timeout). start_le_scan_legacy() never reads + // it, and the legacy path is what CONFIG_BT_EXT_ADV=n selects -- which a + // controller without extended advertising, like the CYW43439, forces. So + // on those builds the scan would run until stop_scan() and the ScanResults + // iterator would never finish. Keep the deadline here and enforce it from + // bleio_background(), on the main thread, where stopping is safe. + scan_deadline_ms = timeout > 0 ? supervisor_ticks_ms64() + (uint64_t)(timeout * 1000.0f) : 0; + return MP_OBJ_FROM_PTR(self->scan_results); } void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { + scan_deadline_ms = 0; if (self->scan_results == NULL) { return; } @@ -742,6 +757,22 @@ void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { self->scan_results = NULL; } +// Called from port_background_task(), i.e. from RUN_BACKGROUND_TASKS on the +// main thread. This is where the scan timeout is enforced when the controller +// cannot do it (see common_hal_bleio_adapter_start_scan). Stopping from here +// rather than from a k_timer keeps the blocking HCI round-trip in +// bt_le_scan_stop() off the system work queue, which the USB CDC console also +// runs on. +void bleio_background(void) { + if (active_scan_results == NULL || scan_deadline_ms == 0) { + return; + } + if (supervisor_ticks_ms64() < scan_deadline_ms) { + return; + } + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); +} + bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { if (!ble_adapter_enabled) { return false; diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.h b/ports/zephyr-cp/common-hal/_bleio/__init__.h index 72dc249d142..0982f835032 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.h +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.h @@ -45,3 +45,7 @@ void bleio_gattc_write_sync(struct bt_conn *conn, uint16_t handle, // callback so discover_remote_services() fails cleanly instead of hanging // or NULL-dereferencing the cleared connection. void bleio_connection_discovery_abort(void); + +// Main-thread housekeeping, run from port_background_task(): enforces the scan +// timeout on controllers whose legacy scan path cannot. +void bleio_background(void);