diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index e5ce389a7..17ff4eae0 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -103,6 +103,21 @@ static const struct ble_gap_conn_params ble_gap_conn_params_dflt = { }; #endif +#if NIMBLE_BLE_CONNECT && CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT +struct ble_gap_connect_reattempt_ctxt { + uint8_t own_addr_type; + ble_addr_t peer_addr; + int32_t duration_ms; + struct ble_gap_conn_params conn_params; + ble_gap_event_fn *cb; + void *cb_arg; +}; + +static struct ble_gap_connect_reattempt_ctxt ble_conn_reattempt[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +static uint16_t reattempt_idx; +static bool conn_cookie_enabled; +#endif + /** * The state of the in-progress master connection. If no master connection is * currently in progress, then the op field is set to BLE_GAP_OP_NULL. @@ -864,6 +879,93 @@ ble_gap_master_connect_cancelled(void) state.cb(&event, state.cb_arg); } } + +#if CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT +static void +ble_gap_update_notify(uint16_t conn_handle, int status); + +static int +ble_gap_find_retry_conn_param(const struct ble_gap_conn_desc *conn_desc) +{ + int i; + + for(i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) { + if (memcmp(&ble_conn_reattempt[i].peer_addr, &conn_desc->peer_ota_addr, sizeof(ble_addr_t)) == 0) { + return i; + } + } + /* No matching entry found. Return invalid index */ + return MYNEWT_VAL(BLE_MAX_CONNECTIONS); +} + +int +ble_gap_master_connect_reattempt(uint16_t conn_handle) +{ + struct ble_gap_snapshot snap; + struct ble_gap_conn_desc conn; + struct ble_gap_update_entry *entry; + int idx; + int rc = BLE_HS_EUNKNOWN; + + snap.desc = &conn; + rc = ble_gap_find_snapshot(conn_handle, &snap); + if (rc != 0) { + return rc; + } + + if (conn.role == BLE_GAP_ROLE_MASTER) { + idx = ble_gap_find_retry_conn_param(&conn); + if (idx >= MYNEWT_VAL(BLE_MAX_CONNECTIONS)) { + return BLE_HS_EINVAL; + } + + /* XXX Connection state in host needs to be removed and cleaned + * up to validate the connection when re-attempting. */ + + /* If there was a connection update in progress, indicate to the + * application that it did not complete. + */ + ble_hs_lock(); + entry = ble_gap_update_entry_remove(conn_handle); + ble_hs_unlock(); + + if (entry != NULL) { + ble_gap_update_notify(conn_handle, BLE_ERR_CONN_ESTABLISHMENT); + ble_gap_update_entry_free(entry); + } + + ble_l2cap_sig_conn_broken(conn_handle, BLE_ERR_CONN_ESTABLISHMENT); + ble_sm_connection_broken(conn_handle); + ble_gatts_connection_broken(conn_handle); + ble_gattc_connection_broken(conn_handle); + ble_hs_flow_connection_broken(conn_handle);; + + rc = ble_hs_atomic_conn_delete(conn_handle); + if (rc != 0) { + return rc; + } + + /* Utilize cookie to get the index updated correctly for re-attempt */ + ble_conn_reattempt[idx].cb_arg = &conn; + conn_cookie_enabled = true; + + rc = ble_gap_connect(ble_conn_reattempt[idx].own_addr_type, + &ble_conn_reattempt[idx].peer_addr, + ble_conn_reattempt[idx].duration_ms, + &ble_conn_reattempt[idx].conn_params, + ble_conn_reattempt[idx].cb, + ble_conn_reattempt[idx].cb_arg); + if (rc != 0) { + return rc; + } + } else { + rc = BLE_HS_EUNKNOWN; + } + + return rc; +} +#endif + #endif #if NIMBLE_BLE_SCAN @@ -5008,6 +5110,31 @@ ble_gap_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr, ble_gap_master.op = BLE_GAP_OP_M_CONN; +#if CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT + /* ble_gap_connect_reattempt save the connection parameters */ + if ((cb_arg != NULL) && conn_cookie_enabled) { + struct ble_gap_conn_desc *conn_desc = cb_arg; + /* reattempt_idx should follow conn handle corresponding to MASTER role */ + reattempt_idx = conn_desc->conn_handle; + /* Reset cookie_enabled flag, it will be set again by reattempt call */ + conn_cookie_enabled = false; + } + + ble_conn_reattempt[reattempt_idx].own_addr_type = own_addr_type; + memcpy(&ble_conn_reattempt[reattempt_idx].peer_addr, peer_addr, + sizeof(ble_addr_t)); + ble_conn_reattempt[reattempt_idx].duration_ms = duration_ms; + memcpy(&ble_conn_reattempt[reattempt_idx].conn_params, + conn_params, + sizeof(struct ble_gap_conn_params)); + ble_conn_reattempt[reattempt_idx].cb = cb; + ble_conn_reattempt[reattempt_idx].cb_arg = cb_arg; + /* reattempt_idx need to be within limits. This may end up being unnecessary + * operation. However, it is better to be sure as it can get tricky with + * multiple connections and client + server roles XXX*/ + reattempt_idx = (reattempt_idx + 1) % MYNEWT_VAL(BLE_MAX_CONNECTIONS); +#endif + rc = ble_gap_conn_create_tx(own_addr_type, peer_addr, conn_params); if (rc != 0) { diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index ba046c5f9..ace9198fe 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -28,6 +28,17 @@ #include "ble_hs_priv.h" #include "ble_hs_resolv_priv.h" +#if CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT +static uint16_t reattempt_conn[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +extern int ble_gap_master_connect_reattempt(uint16_t conn_handle); + +#ifdef CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT +#define MAX_REATTEMPT_ALLOWED CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT +#else +#define MAX_REATTEMPT_ALLOWED 0 +#endif +#endif + _Static_assert(sizeof (struct hci_data_hdr) == BLE_HCI_DATA_HDR_SZ, "struct hci_data_hdr must be 4 bytes"); @@ -156,6 +167,32 @@ ble_hs_hci_evt_disconn_complete(uint8_t event_code, const void *data, } ble_hs_unlock(); +#if CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT + int rc; + + if (ev->reason == BLE_ERR_CONN_ESTABLISHMENT && (conn != NULL)) { + BLE_HS_LOG(DEBUG, "Reattempt connection; reason = 0x%x, status = %d," + " reattempt count = %d ", ev->reason, ev->status, + reattempt_conn[ev->conn_handle]); + if (conn->bhc_flags & BLE_HS_CONN_F_MASTER) { + if (reattempt_conn[ev->conn_handle] < MAX_REATTEMPT_ALLOWED) { + reattempt_conn[ev->conn_handle] += 1; + rc = ble_gap_master_connect_reattempt(ev->conn_handle); + if (rc != 0) { + BLE_HS_LOG(DEBUG, "Master reconnect attempt failed; rc = %d", rc); + } + } else { + reattempt_conn[ev->conn_handle] = 0; + } + } + } else { + /* Disconnect completed with some other reason than + * BLE_ERR_CONN_ESTABLISHMENT, reset the corresponding reattempt count + * */ + reattempt_conn[ev->conn_handle] = 0; + } +#endif + ble_gap_rx_disconn_complete(ev); /* The connection termination may have freed up some capacity in the