Commit 6a42e9bf authored by Iulia Tanasescu's avatar Iulia Tanasescu Committed by Luiz Augusto von Dentz

Bluetooth: ISO: Support multiple BIGs

This adds support for creating multiple BIGs. According to
spec, each BIG shall have an unique handle, and each BIG should
be associated with a different advertising handle. Otherwise,
the LE Create BIG command will fail, with error code
Command Disallowed (for reusing a BIG handle), or
Unknown Advertising Identifier (for reusing an advertising
handle).

The btmon snippet below shows an exercise for creating two BIGs
for the same controller, by opening two isotest instances with
the following command:
    tools/isotest -i hci0 -s 00:00:00:00:00:00

< HCI Command: LE Create Broadcast Isochronous Group (0x08|0x0068) plen 31
        Handle: 0x00
        Advertising Handle: 0x01
        Number of BIS: 1
        SDU Interval: 10000 us (0x002710)
        Maximum SDU size: 40
        Maximum Latency: 10 ms (0x000a)
        RTN: 0x02
        PHY: LE 2M (0x02)
        Packing: Sequential (0x00)
        Framing: Unframed (0x00)
        Encryption: 0x00
        Broadcast Code: 00000000000000000000000000000000

> HCI Event: Command Status (0x0f) plen 4
      LE Create Broadcast Isochronous Group (0x08|0x0068) ncmd 1
        Status: Success (0x00)

> HCI Event: LE Meta Event (0x3e) plen 21
      LE Broadcast Isochronous Group Complete (0x1b)
        Status: Success (0x00)
        Handle: 0x00
        BIG Synchronization Delay: 912 us (0x000390)
        Transport Latency: 912 us (0x000390)
        PHY: LE 2M (0x02)
        NSE: 3
        BN: 1
        PTO: 1
        IRC: 3
        Maximum PDU: 40
        ISO Interval: 10.00 msec (0x0008)
        Connection Handle #0: 10

< HCI Command: LE Create Broadcast Isochronous Group (0x08|0x0068)
        Handle: 0x01
        Advertising Handle: 0x02
        Number of BIS: 1
        SDU Interval: 10000 us (0x002710)
        Maximum SDU size: 40
        Maximum Latency: 10 ms (0x000a)
        RTN: 0x02
        PHY: LE 2M (0x02)
        Packing: Sequential (0x00)
        Framing: Unframed (0x00)
        Encryption: 0x00
        Broadcast Code: 00000000000000000000000000000000

> HCI Event: Command Status (0x0f) plen 4
      LE Create Broadcast Isochronous Group (0x08|0x0068) ncmd 1
        Status: Success (0x00)
Signed-off-by: default avatarIulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 69ae5065
...@@ -1095,8 +1095,7 @@ static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle) ...@@ -1095,8 +1095,7 @@ static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
} }
static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev, static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
bdaddr_t *ba, bdaddr_t *ba, __u8 bis)
__u8 big, __u8 bis)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c; struct hci_conn *c;
...@@ -1107,7 +1106,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev, ...@@ -1107,7 +1106,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
if (bacmp(&c->dst, ba) || c->type != ISO_LINK) if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
continue; continue;
if (c->iso_qos.bcast.big == big && c->iso_qos.bcast.bis == bis) { if (c->iso_qos.bcast.bis == bis) {
rcu_read_unlock(); rcu_read_unlock();
return c; return c;
} }
......
...@@ -927,9 +927,7 @@ static void bis_cleanup(struct hci_conn *conn) ...@@ -927,9 +927,7 @@ static void bis_cleanup(struct hci_conn *conn)
/* Check if ISO connection is a BIS and terminate advertising /* Check if ISO connection is a BIS and terminate advertising
* set and BIG if there are no other connections using it. * set and BIG if there are no other connections using it.
*/ */
bis = hci_conn_hash_lookup_bis(hdev, BDADDR_ANY, bis = hci_conn_hash_lookup_big(hdev, conn->iso_qos.bcast.big);
conn->iso_qos.bcast.big,
conn->iso_qos.bcast.bis);
if (bis) if (bis)
return; return;
...@@ -1449,25 +1447,23 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev, ...@@ -1449,25 +1447,23 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
{ {
struct iso_list_data data; struct hci_conn *conn;
u8 big;
/* Allocate a BIG if not set */ /* Allocate a BIG if not set */
if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) { if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) {
for (data.big = 0x00; data.big < 0xef; data.big++) { for (big = 0x00; big < 0xef; big++) {
data.count = 0;
data.bis = 0xff;
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, conn = hci_conn_hash_lookup_big(hdev, big);
BT_BOUND, &data); if (!conn)
if (!data.count)
break; break;
} }
if (data.big == 0xef) if (big == 0xef)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
/* Update BIG */ /* Update BIG */
qos->bcast.big = data.big; qos->bcast.big = big;
} }
return 0; return 0;
...@@ -1475,28 +1471,27 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos) ...@@ -1475,28 +1471,27 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos) static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
{ {
struct iso_list_data data; struct hci_conn *conn;
u8 bis;
/* Allocate BIS if not set */ /* Allocate BIS if not set */
if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) { if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) {
/* Find an unused adv set to advertise BIS, skip instance 0x00 /* Find an unused adv set to advertise BIS, skip instance 0x00
* since it is reserved as general purpose set. * since it is reserved as general purpose set.
*/ */
for (data.bis = 0x01; data.bis < hdev->le_num_of_adv_sets; for (bis = 0x01; bis < hdev->le_num_of_adv_sets;
data.bis++) { bis++) {
data.count = 0;
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, conn = hci_conn_hash_lookup_bis(hdev, BDADDR_ANY, bis);
BT_BOUND, &data); if (!conn)
if (!data.count)
break; break;
} }
if (data.bis == hdev->le_num_of_adv_sets) if (bis == hdev->le_num_of_adv_sets)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
/* Update BIS */ /* Update BIS */
qos->bcast.bis = data.bis; qos->bcast.bis = bis;
} }
return 0; return 0;
...@@ -1534,8 +1529,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -1534,8 +1529,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
/* Check BIS settings against other bound BISes, since all /* Check BIS settings against other bound BISes, since all
* BISes in a BIG must have the same value for all parameters * BISes in a BIG must have the same value for all parameters
*/ */
conn = hci_conn_hash_lookup_bis(hdev, dst, qos->bcast.big, conn = hci_conn_hash_lookup_big(hdev, qos->bcast.big);
qos->bcast.bis);
if (conn && (memcmp(qos, &conn->iso_qos, sizeof(*qos)) || if (conn && (memcmp(qos, &conn->iso_qos, sizeof(*qos)) ||
base_len != conn->le_per_adv_data_len || base_len != conn->le_per_adv_data_len ||
......
...@@ -1639,7 +1639,7 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data, ...@@ -1639,7 +1639,7 @@ static u8 hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, void *data,
hci_dev_set_flag(hdev, HCI_LE_ADV); hci_dev_set_flag(hdev, HCI_LE_ADV);
if (adv) if (adv && !adv->periodic)
adv->enabled = true; adv->enabled = true;
conn = hci_lookup_le_connect(hdev); conn = hci_lookup_le_connect(hdev);
...@@ -3941,24 +3941,47 @@ static u8 hci_cc_le_set_per_adv_enable(struct hci_dev *hdev, void *data, ...@@ -3941,24 +3941,47 @@ static u8 hci_cc_le_set_per_adv_enable(struct hci_dev *hdev, void *data,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct hci_ev_status *rp = data; struct hci_ev_status *rp = data;
__u8 *sent; struct hci_cp_le_set_per_adv_enable *cp;
struct adv_info *adv = NULL, *n;
u8 per_adv_cnt = 0;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
if (rp->status) if (rp->status)
return rp->status; return rp->status;
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE); cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE);
if (!sent) if (!cp)
return rp->status; return rp->status;
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (*sent) adv = hci_find_adv_instance(hdev, cp->handle);
if (cp->enable) {
hci_dev_set_flag(hdev, HCI_LE_PER_ADV); hci_dev_set_flag(hdev, HCI_LE_PER_ADV);
else
if (adv)
adv->enabled = true;
} else {
/* If just one instance was disabled check if there are
* any other instance enabled before clearing HCI_LE_PER_ADV.
* The current periodic adv instance will be marked as
* disabled once extended advertising is also disabled.
*/
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
list) {
if (adv->periodic && adv->enabled)
per_adv_cnt++;
}
if (per_adv_cnt > 1)
goto unlock;
hci_dev_clear_flag(hdev, HCI_LE_PER_ADV); hci_dev_clear_flag(hdev, HCI_LE_PER_ADV);
}
unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
return rp->status; return rp->status;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux * BlueZ - Bluetooth protocol stack for Linux
* *
* Copyright (C) 2021 Intel Corporation * Copyright (C) 2021 Intel Corporation
* Copyright 2023 NXP
*/ */
#include <linux/property.h> #include <linux/property.h>
...@@ -1319,9 +1320,11 @@ int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance) ...@@ -1319,9 +1320,11 @@ int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance)
static int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance) static int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
{ {
struct hci_cp_le_set_per_adv_enable cp; struct hci_cp_le_set_per_adv_enable cp;
struct adv_info *adv = NULL;
/* If periodic advertising already disabled there is nothing to do. */ /* If periodic advertising already disabled there is nothing to do. */
if (!hci_dev_test_flag(hdev, HCI_LE_PER_ADV)) adv = hci_find_adv_instance(hdev, instance);
if (!adv || !adv->periodic || !adv->enabled)
return 0; return 0;
memset(&cp, 0, sizeof(cp)); memset(&cp, 0, sizeof(cp));
...@@ -1386,9 +1389,11 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) ...@@ -1386,9 +1389,11 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance) static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
{ {
struct hci_cp_le_set_per_adv_enable cp; struct hci_cp_le_set_per_adv_enable cp;
struct adv_info *adv = NULL;
/* If periodic advertising already enabled there is nothing to do. */ /* If periodic advertising already enabled there is nothing to do. */
if (hci_dev_test_flag(hdev, HCI_LE_PER_ADV)) adv = hci_find_adv_instance(hdev, instance);
if (adv && adv->periodic && adv->enabled)
return 0; return 0;
memset(&cp, 0, sizeof(cp)); memset(&cp, 0, sizeof(cp));
...@@ -1458,22 +1463,19 @@ int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len, ...@@ -1458,22 +1463,19 @@ int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len,
sync_interval); sync_interval);
if (IS_ERR(adv)) if (IS_ERR(adv))
return PTR_ERR(adv); return PTR_ERR(adv);
adv->pending = false;
added = true; added = true;
} }
} }
/* Only start advertising if instance 0 or if a dedicated instance has /* Start advertising */
* been added. err = hci_start_ext_adv_sync(hdev, instance);
*/ if (err < 0)
if (!adv || added) { goto fail;
err = hci_start_ext_adv_sync(hdev, instance);
if (err < 0)
goto fail;
err = hci_adv_bcast_annoucement(hdev, adv); err = hci_adv_bcast_annoucement(hdev, adv);
if (err < 0) if (err < 0)
goto fail; goto fail;
}
err = hci_set_per_adv_params_sync(hdev, instance, min_interval, err = hci_set_per_adv_params_sync(hdev, instance, min_interval,
max_interval); max_interval);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment