diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 1120c7b914cf..3c08f28ea4da 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -69,6 +69,9 @@ static void _on_sync(void) { xTaskNotifyGive(cp_task); } +// All examples have this. It'd make sense in a header. +void ble_store_config_init(void); + void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { const bool is_enabled = common_hal_bleio_adapter_get_enabled(self); @@ -93,6 +96,20 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_hs_cfg.sync_cb = _on_sync; // ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; + ble_hs_cfg.sm_bonding = 1; + /* Enable the appropriate bit masks to make sure the keys + * that are needed are exchanged + */ + ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC; + ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC; + + ble_hs_cfg.sm_mitm = 1; + ble_hs_cfg.sm_sc = 1; + /* Stores the IRK */ + ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; + ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; + ble_svc_gap_init(); ble_svc_gatt_init(); ble_svc_ans_init(); @@ -115,6 +132,8 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable connection->conn_handle = BLEIO_HANDLE_INVALID; } + ble_store_config_init(); + cp_task = xTaskGetCurrentTaskHandle(); nimble_port_freertos_init(nimble_host_task); @@ -277,10 +296,13 @@ static int _mtu_reply(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t mtu, void *arg) { bleio_connection_internal_t *connection = (bleio_connection_internal_t *)arg; - if (conn_handle != connection->conn_handle || error->status != 0) { + if (conn_handle != connection->conn_handle) { return 0; } - connection->mtu = mtu; + if (error->status == 0) { + connection->mtu = mtu; + } + xTaskNotify(cp_task, conn_handle, eSetValueWithOverwrite); return 0; } @@ -324,11 +346,11 @@ static int _connect_event(struct ble_gap_event *event, void *self_in) { switch (event->type) { case BLE_GAP_EVENT_CONNECT: if (event->connect.status == 0) { + // This triggers an MTU exchange. Its reply will unblock CP. _new_connection(event->connect.conn_handle); // Set connections objs back to NULL since we have a new // connection and need a new tuple. self->connection_objs = NULL; - xTaskNotify(cp_task, event->connect.conn_handle, eSetValueWithOverwrite); } else { xTaskNotify(cp_task, -event->connect.status, eSetValueWithOverwrite); } @@ -663,7 +685,7 @@ bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; - if (connection->conn_handle != BLEIO_HANDLE_INVALID) { + if (connection->conn_handle != BLEIO_HANDLE_INVALID && connection->mtu != 0) { return true; } } @@ -678,7 +700,7 @@ mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT]; for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; - if (connection->conn_handle != BLEIO_HANDLE_INVALID) { + if (connection->conn_handle != BLEIO_HANDLE_INVALID && connection->mtu != 0) { if (connection->connection_obj == mp_const_none) { connection->connection_obj = bleio_connection_new_from_internal(connection); } @@ -691,14 +713,13 @@ mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { } void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); - // bonding_erase_storage(); + ble_store_clear(); } bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); - // return bonding_peripheral_bond_count() > 0; - return false; + int count; + ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count); + return count > 0; } void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { diff --git a/ports/espressif/common-hal/_bleio/Connection.c b/ports/espressif/common-hal/_bleio/Connection.c index be416b2fe02f..ea30b1865f59 100644 --- a/ports/espressif/common-hal/_bleio/Connection.c +++ b/ports/espressif/common-hal/_bleio/Connection.c @@ -55,22 +55,31 @@ int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in) } case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: { - #if CIRCUITPY_VERBOSE_BLE - mp_printf(&mp_plat_print, "TODO connection event: PHY update complete\n"); - #endif + // Nothing to do here. CircuitPython doesn't tell the user what PHY + // we're on. break; } case BLE_GAP_EVENT_CONN_UPDATE: { - #if CIRCUITPY_VERBOSE_BLE - mp_printf(&mp_plat_print, "TODO connection event: connection update\n"); - #endif + struct ble_gap_conn_desc desc; + int rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + connection->conn_params_updating = false; break; } - case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { - #if CIRCUITPY_VERBOSE_BLE - mp_printf(&mp_plat_print, "TODO connection event: l2cap update request\n"); - #endif + case BLE_GAP_EVENT_ENC_CHANGE: { + struct ble_gap_conn_desc desc; + ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if (desc.sec_state.encrypted) { + connection->pair_status = PAIR_PAIRED; + } + break; + } + case BLE_GAP_EVENT_MTU: { + if (event->mtu.conn_handle != connection->conn_handle) { + return 0; + } + connection->mtu = event->mtu.value; break; } @@ -113,15 +122,34 @@ void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { } void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { - // TODO: Implement this. + // We may already be trying to pair if we just reconnected to a peer we're + // bonded with. + while (self->pair_status == PAIR_WAITING && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + if (self->pair_status == PAIR_PAIRED) { + return; + } + self->pair_status = PAIR_WAITING; + CHECK_NIMBLE_ERROR(ble_gap_security_initiate(self->conn_handle)); + while (self->pair_status == PAIR_WAITING && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + if (mp_hal_is_interrupted()) { + return; + } } mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self) { - // TODO: Implement this. while (self->conn_params_updating && !mp_hal_is_interrupted()) { RUN_BACKGROUND_TASKS; } - return 0; + if (mp_hal_is_interrupted()) { + return 0; + } + struct ble_gap_conn_desc desc; + CHECK_NIMBLE_ERROR(ble_gap_conn_find(self->conn_handle, &desc)); + return 1.25f * desc.conn_itvl; } // Return the current negotiated MTU length, minus overhead. @@ -131,7 +159,16 @@ mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_inte void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval) { self->conn_params_updating = true; - // TODO: Implement this. + struct ble_gap_conn_desc desc; + CHECK_NIMBLE_ERROR(ble_gap_conn_find(self->conn_handle, &desc)); + uint16_t interval = new_interval / 1.25f; + struct ble_gap_upd_params updated = { + .itvl_min = interval, + .itvl_max = interval, + .latency = desc.conn_latency, + .supervision_timeout = desc.supervision_timeout + }; + CHECK_NIMBLE_ERROR(ble_gap_update_params(self->conn_handle, &updated)); } static volatile int _last_discovery_status; diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c index 361120c84867..0d99a97c23c4 100644 --- a/ports/espressif/common-hal/_bleio/__init__.c +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -97,6 +97,12 @@ void check_ble_error(int error_code, const char *file, size_t line) { return; } switch (error_code) { + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): + mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Insufficient authentication")); + return; + case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): + mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Insufficient encryption")); + return; default: #if CIRCUITPY_VERBOSE_BLE if (file) {