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 <rubin.gerritsen@nordicsemi.no>
This commit is contained in:
Rubin Gerritsen 2021-05-05 08:29:12 +02:00 committed by Johan Hedberg
commit 98321c61fb
4 changed files with 70 additions and 3 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);