From 98321c61fbba0709a7029eade7190dca9cfb7d4d Mon Sep 17 00:00:00 2001 From: Rubin Gerritsen Date: Wed, 5 May 2021 08:29:12 +0200 Subject: [PATCH] bluetooth: host: Allow concurrent advertising with multiple ids The HCI specification creates additional complexity to allow this configuration: - When a connection gets established, we need to know which identity the HCI_LE_Connection_Complete event corresponds to. - The identity is a property of the advertising set. Therefore we need the advertising handle. - The advertising handle is part of the HCI_LE_Advertising_Set_Terminated event and is not part of the HCI_LE_Connection_Complete event. Therefore the information of both events needs to be combined. By spec the LE_Connection_Complete comes first. Therefore we cache this event until the identity is available. The event is only cached when a connection gets established as that is the only case where we need to resolve the identity. As the caching requires more resources, it is only enabled if the application requires multiple advertising sets and multiple identities. The host maps the HCI_LE_Advertising_Set_Terminated event with the HCI_LE_Connection_Complete event by comparing the connection handles. Signed-off-by: Rubin Gerritsen --- include/bluetooth/bluetooth.h | 3 --- subsys/bluetooth/host/adv.c | 20 ++++++++++++++++++++ subsys/bluetooth/host/hci_core.c | 31 +++++++++++++++++++++++++++++++ subsys/bluetooth/host/hci_core.h | 19 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/include/bluetooth/bluetooth.h b/include/bluetooth/bluetooth.h index 2fe96fe1dc8..a460d98e2a5 100644 --- a/include/bluetooth/bluetooth.h +++ b/include/bluetooth/bluetooth.h @@ -540,9 +540,6 @@ struct bt_le_adv_param { * enabled or not supported by the controller it is not possible * to scan and advertise simultaneously using two different * random addresses. - * - * @note It is not possible to have multiple connectable advertising - * sets advertising simultaneously using different identities. */ uint8_t id; diff --git a/subsys/bluetooth/host/adv.c b/subsys/bluetooth/host/adv.c index d64e12c9b62..7d242149f32 100644 --- a/subsys/bluetooth/host/adv.c +++ b/subsys/bluetooth/host/adv.c @@ -1553,6 +1553,26 @@ void bt_hci_le_adv_set_terminated(struct net_buf *buf) adv = bt_adv_lookup_handle(evt->adv_handle); conn_handle = sys_le16_to_cpu(evt->conn_handle); +#if (CONFIG_BT_ID_MAX > 1) && (CONFIG_BT_EXT_ADV_MAX_ADV_SET > 1) + bt_dev.adv_conn_id = adv->id; + for (int i = 0; i < ARRAY_SIZE(bt_dev.cached_conn_complete); i++) { + if (bt_dev.cached_conn_complete[i].valid && + bt_dev.cached_conn_complete[i].evt.handle == evt->conn_handle) { + if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) { + /* Process the cached connection complete event + * now that the corresponding advertising set is known. + * + * If the advertiser has been stopped before the connection + * complete event has been raised to the application, we + * discard the event. + */ + bt_hci_le_enh_conn_complete(&bt_dev.cached_conn_complete[i].evt); + } + bt_dev.cached_conn_complete[i].valid = false; + } + } +#endif + BT_DBG("status 0x%02x adv_handle %u conn_handle 0x%02x num %u", evt->status, evt->adv_handle, conn_handle, evt->num_completed_ext_adv_evts); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 538e5ea58e6..4590a207157 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1043,6 +1043,37 @@ static void le_conn_complete_adv_timeout(void) } static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) +{ +#if (CONFIG_BT_ID_MAX > 1) && (CONFIG_BT_EXT_ADV_MAX_ADV_SET > 1) + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + evt->role == BT_HCI_ROLE_SLAVE && + evt->status == BT_HCI_ERR_SUCCESS && + (IS_ENABLED(CONFIG_BT_EXT_ADV) && + BT_FEAT_LE_EXT_ADV(bt_dev.le.features))) { + + /* Cache the connection complete event. Process it later. + * See bt_dev.cached_conn_complete. + */ + for (int i = 0; i < ARRAY_SIZE(bt_dev.cached_conn_complete); i++) { + if (!bt_dev.cached_conn_complete[i].valid) { + (void)memcpy(&bt_dev.cached_conn_complete[i].evt, + evt, + sizeof(struct bt_hci_evt_le_enh_conn_complete)); + bt_dev.cached_conn_complete[i].valid = true; + return; + } + } + + __ASSERT(false, "No more cache entries available." + "This should not happen by design"); + + return; + } +#endif + bt_hci_le_enh_conn_complete(evt); +} + +void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) { uint16_t handle = sys_le16_to_cpu(evt->handle); bt_addr_le_t peer_addr, id_addr; diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index b2b0be809ef..b14a737c499 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -252,6 +252,22 @@ struct bt_dev { #else /* Pointer to reserved advertising set */ struct bt_le_ext_adv *adv; +#if (CONFIG_BT_ID_MAX > 1) && (CONFIG_BT_EXT_ADV_MAX_ADV_SET > 1) + /* When supporting multiple concurrent connectable advertising sets + * with multiple identities, we need to know the identity of + * the terminating advertising set to identify the connection object. + * The identity of the advertising set is determined by its + * advertising handle, which is part of the + * LE Set Advertising Set Terminated event which is always sent + * _after_ the LE Enhanced Connection complete event. + * Therefore we need cache this event until its identity is known. + */ + struct { + bool valid; + struct bt_hci_evt_le_enh_conn_complete evt; + } cached_conn_complete[MIN(CONFIG_BT_MAX_CONN, + CONFIG_BT_EXT_ADV_MAX_ADV_SET)]; +#endif #endif /* Current local Random Address */ bt_addr_le_t random_addr; @@ -394,6 +410,9 @@ void bt_hci_auth_complete(struct net_buf *buf); void bt_hci_evt_le_pkey_complete(struct net_buf *buf); void bt_hci_evt_le_dhkey_complete(struct net_buf *buf); +/* Common HCI event handlers */ +void bt_hci_le_enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt); + /* Scan HCI event handlers */ void bt_hci_le_adv_report(struct net_buf *buf); void bt_hci_le_scan_timeout(struct net_buf *buf);