Bluetooth samples: Add multi subgroup support
Add multi subgroup support for the broadcast sink sample Signed-off-by: Jens Rehhoff Thomsen <jthm@demant.com>
This commit is contained in:
parent
5a728825b7
commit
7d83419b6f
1 changed files with 133 additions and 162 deletions
|
|
@ -538,6 +538,7 @@ static void stream_started_cb(struct bt_bap_stream *stream)
|
|||
k_sem_give(&sem_stream_started);
|
||||
if (k_sem_count_get(&sem_stream_started) == stream_count) {
|
||||
big_synced = true;
|
||||
printk("BIG synced\n");
|
||||
k_sem_give(&sem_big_synced);
|
||||
}
|
||||
}
|
||||
|
|
@ -554,6 +555,7 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
|||
}
|
||||
|
||||
if (k_sem_count_get(&sem_stream_started) != stream_count) {
|
||||
printk("BIG sync terminated\n");
|
||||
big_synced = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -605,8 +607,9 @@ static struct bt_bap_stream_ops stream_ops = {
|
|||
};
|
||||
|
||||
#if defined(CONFIG_TARGET_BROADCAST_CHANNEL)
|
||||
struct find_valid_bis_data {
|
||||
struct bis_channel_allocation_data {
|
||||
struct {
|
||||
bool chan_allocation_available;
|
||||
uint8_t index;
|
||||
enum bt_audio_location chan_allocation;
|
||||
} bis[BT_ISO_BIS_INDEX_MAX];
|
||||
|
|
@ -617,106 +620,39 @@ struct find_valid_bis_data {
|
|||
/**
|
||||
* This is called for each BIS in a subgroup
|
||||
*
|
||||
* It returns `false` if the current BIS contains all of the channels we are looking for,
|
||||
* or if it does not contain any and we are looking for BT_AUDIO_LOCATION_MONO_AUDIO. This stops
|
||||
* the iteration of the remaining BIS in the subgroup.
|
||||
*
|
||||
* It returns `true` if the BIS either contains none or some of the channels we are looking for.
|
||||
* If it contains some, then that is being stored in the user_data, so that the calling function
|
||||
* can check if a combination of the BIS satisfy the channel allocations we want.
|
||||
* Gets BIS channel allocation (if exists).
|
||||
* Always returns `true` to continue to next BIS
|
||||
*/
|
||||
static bool find_valid_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
|
||||
void *user_data)
|
||||
static bool bis_get_channel_allocation_cb(const struct bt_bap_base_subgroup_bis *bis,
|
||||
void *user_data)
|
||||
{
|
||||
struct find_valid_bis_data *data = user_data;
|
||||
struct bt_audio_codec_cfg codec_cfg = {0};
|
||||
enum bt_audio_location chan_allocation;
|
||||
int err;
|
||||
struct bis_channel_allocation_data *data = user_data;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
int err, idx;
|
||||
|
||||
idx = data->cnt++;
|
||||
data->bis[idx].index = bis->index;
|
||||
data->bis[idx].chan_allocation_available = false;
|
||||
|
||||
err = bt_bap_base_subgroup_bis_codec_to_codec_cfg(bis, &codec_cfg);
|
||||
if (err != 0) {
|
||||
printk("Could not get codec configuration for BIS: %d\n", err);
|
||||
return true;
|
||||
|
||||
return true; /* continue to next BIS */
|
||||
}
|
||||
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &chan_allocation, true);
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &data->bis[idx].chan_allocation,
|
||||
false);
|
||||
if (err != 0) {
|
||||
printk("Could not find channel allocation for BIS: %d\n", err);
|
||||
|
||||
if (err == -ENODATA && strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) {
|
||||
/* Accept no channel allocation data available
|
||||
* if TARGET_BROADCAST_NAME defined. Use current index.
|
||||
*/
|
||||
data->bis[0].index = bis->index;
|
||||
data->bis[0].chan_allocation = chan_allocation;
|
||||
data->cnt = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
/* Found single BIS with all channels we want - keep as only and stop
|
||||
* parsing
|
||||
*/
|
||||
data->bis[0].index = bis->index;
|
||||
data->bis[0].chan_allocation = chan_allocation;
|
||||
data->cnt = 1;
|
||||
|
||||
return false;
|
||||
} else if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) != 0) {
|
||||
/* BIS contains part of what we are looking for - Store and see if there are
|
||||
* other BIS that may fill the gaps
|
||||
*/
|
||||
data->bis[data->cnt].index = bis->index;
|
||||
data->bis[data->cnt].chan_allocation = chan_allocation;
|
||||
data->cnt++;
|
||||
}
|
||||
return true; /* continue to next BIS */
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Channel allocation data available for this bis */
|
||||
data->bis[idx].chan_allocation_available = true;
|
||||
|
||||
/**
|
||||
* This function searches all the BIS in a subgroup for a set of BIS indexes that satisfy
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL
|
||||
*
|
||||
* Returns `true` if the right channels were found, otherwise `false`.
|
||||
*/
|
||||
static bool find_valid_bis_in_subgroup_bis(const struct bt_bap_base_subgroup *subgroup,
|
||||
uint32_t *bis_indexes)
|
||||
{
|
||||
struct find_valid_bis_data data = {0};
|
||||
int err;
|
||||
|
||||
err = bt_bap_base_subgroup_foreach_bis(subgroup, find_valid_bis_cb, &data);
|
||||
if (err == -ECANCELED) {
|
||||
/* We found what we are looking for in a single BIS */
|
||||
|
||||
*bis_indexes = BIT(data.bis[0].index);
|
||||
|
||||
return true;
|
||||
} else if (err == 0) {
|
||||
/* We are finished parsing all BIS - Try to find a combination that satisfy our
|
||||
* channel allocation. For simplicity this is using a greedy approach, rather than
|
||||
* an optimal one.
|
||||
*/
|
||||
enum bt_audio_location chan_allocation = BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
*bis_indexes = 0;
|
||||
|
||||
for (uint8_t i = 0U; i < data.cnt; i++) {
|
||||
chan_allocation |= data.bis[i].chan_allocation;
|
||||
*bis_indexes |= BIT(data.bis[i].index);
|
||||
|
||||
if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Some error occurred or we did not find expected channel allocation */
|
||||
return false;
|
||||
return true; /* continue to next BIS */
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -731,89 +667,91 @@ static bool find_valid_bis_in_subgroup_bis(const struct bt_bap_base_subgroup *su
|
|||
* configuration overwrites the subgroup values
|
||||
*
|
||||
* This function returns `true` if the subgroup does not support the channels in
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL which makes it iterate over the next subgroup, and returns
|
||||
* `false` if this subgroup satisfies our CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL which makes it iterate over the next subgroup, and
|
||||
* returns `false` if this subgroup satisfies our CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
static bool find_valid_bis_in_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
|
||||
void *user_data)
|
||||
static bool subgroup_get_valid_bis_indexes_cb(const struct bt_bap_base_subgroup *subgroup,
|
||||
void *user_data)
|
||||
{
|
||||
enum bt_audio_location chan_allocation;
|
||||
enum bt_audio_location subgroup_chan_allocation;
|
||||
enum bt_audio_location chan_allocation = BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
bool subgroup_chan_allocation_available = false;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
uint32_t *bis_indexes = user_data;
|
||||
struct bis_channel_allocation_data data = {
|
||||
.cnt = 0,
|
||||
};
|
||||
uint32_t bis_indexes = 0;
|
||||
int err;
|
||||
|
||||
/* We only want indexes from a single subgroup, so reset between each of them*/
|
||||
*bis_indexes = 0U;
|
||||
|
||||
err = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, &codec_cfg);
|
||||
if (err != 0) {
|
||||
printk("Could not get codec configuration: %d\n", err);
|
||||
|
||||
return true;
|
||||
return true; /* continue to next subgroup */
|
||||
}
|
||||
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &chan_allocation, false);
|
||||
if (err != 0) {
|
||||
printk("Could not find subgroup channel allocation: %d - Looking in the BISes\n",
|
||||
err);
|
||||
if (codec_cfg.id != BT_HCI_CODING_FORMAT_LC3) {
|
||||
/* Only LC3 codec supported */
|
||||
return false; /* abort */
|
||||
}
|
||||
|
||||
/* Find chan alloc in BIS */
|
||||
if (find_valid_bis_in_subgroup_bis(subgroup, bis_indexes)) {
|
||||
/* Found BISes with correct channel allocation */
|
||||
return false;
|
||||
}
|
||||
/* Get channel allocation at subgroup level */
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &subgroup_chan_allocation, true);
|
||||
if (err == 0) {
|
||||
printk("Channel allocation (subgroup level) 0x%x\n", subgroup_chan_allocation);
|
||||
subgroup_chan_allocation_available = true;
|
||||
} else {
|
||||
/* If the subgroup contains a single channel, then we just grab the first BIS index
|
||||
*/
|
||||
if (bt_audio_get_chan_count(chan_allocation) == 1 &&
|
||||
chan_allocation == CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
uint32_t subgroup_bis_indexes;
|
||||
/* subgroup error */
|
||||
return false; /* abort */
|
||||
}
|
||||
|
||||
/* Set bis_indexes to the first bit set */
|
||||
err = bt_bap_base_subgroup_get_bis_indexes(subgroup, &subgroup_bis_indexes);
|
||||
if (err != 0) {
|
||||
/* Should never happen as that would indicate an invalid
|
||||
* subgroup If it does, we just parse the next subgroup
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
/* Get channel allocation at BIS level */
|
||||
err = bt_bap_base_subgroup_foreach_bis(subgroup, bis_get_channel_allocation_cb, &data);
|
||||
if (err != 0) {
|
||||
printk("Get channel allocation error %d\n", err);
|
||||
|
||||
/* We found the BIS index we want, stop parsing*/
|
||||
*bis_indexes = BIT(find_lsb_set(subgroup_bis_indexes) - 1);
|
||||
return true; /* continue to next subgroup */
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
/* The subgroup contains all channels we are looking for/
|
||||
* We continue searching each BIS to get the minimal amount of BIS that
|
||||
* satisfy CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
|
||||
if (find_valid_bis_in_subgroup_bis(subgroup, bis_indexes)) {
|
||||
/* Found BISes with correct channel allocation */
|
||||
return false;
|
||||
}
|
||||
/* If no BIS channel allocation available use subgroup channel allocation instead if
|
||||
* exists (otherwise mono assumed)
|
||||
*/
|
||||
for (uint8_t i = 0U; i < data.cnt; i++) {
|
||||
if (!data.bis[i].chan_allocation_available) {
|
||||
data.bis[i].chan_allocation = subgroup_chan_allocation_available
|
||||
? subgroup_chan_allocation
|
||||
: BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Get the BIS indexes */
|
||||
for (uint8_t i = 0U; i < data.cnt; i++) {
|
||||
if ((data.bis[i].chan_allocation == CONFIG_TARGET_BROADCAST_CHANNEL) ||
|
||||
((data.bis[i].chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL)) {
|
||||
/* Exact match */
|
||||
bis_indexes = BT_ISO_BIS_INDEX_BIT(data.bis[i].index);
|
||||
|
||||
/**
|
||||
* This function gets a 32-bit bitfield of BIS indexes that cover the channel allocation values in
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
static int base_get_valid_bis_indexes(const struct bt_bap_base *base, uint32_t *bis_indexes)
|
||||
{
|
||||
int err;
|
||||
printk("Channel allocation match. BIS index bitfield 0x%x\n", bis_indexes);
|
||||
*(uint32_t *)user_data = bis_indexes;
|
||||
|
||||
err = bt_bap_base_foreach_subgroup(base, find_valid_bis_in_subgroup_cb, bis_indexes);
|
||||
if (err != -ECANCELED) {
|
||||
printk("Failed to parse subgroups: %d\n", err);
|
||||
return err != 0 ? err : -ENOENT;
|
||||
return false; /* bis index found */
|
||||
} else if ((data.bis[i].chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) != 0) {
|
||||
/* Partial match */
|
||||
chan_allocation |= data.bis[i].chan_allocation;
|
||||
bis_indexes |= BT_ISO_BIS_INDEX_BIT(data.bis[i].index);
|
||||
|
||||
if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
printk("Channel allocation match. BIS index bitfield 0x%x\n",
|
||||
bis_indexes);
|
||||
*(uint32_t *)user_data = bis_indexes;
|
||||
|
||||
return false; /* bis indexes found */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true; /* continue to next subgroup */
|
||||
}
|
||||
#endif /* CONFIG_TARGET_BROADCAST_CHANNEL */
|
||||
|
||||
|
|
@ -831,17 +769,27 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap
|
|||
bt_bap_base_get_subgroup_count(base), sink);
|
||||
|
||||
#if defined(CONFIG_TARGET_BROADCAST_CHANNEL)
|
||||
err = base_get_valid_bis_indexes(base, &base_bis_index_bitfield);
|
||||
if (err != 0) {
|
||||
printk("Failed to find a valid BIS\n");
|
||||
/**
|
||||
* Get a 32-bit bitfield of BIS indexes that cover the channel allocation values in
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
printk("Target channel location: 0x%x\n", CONFIG_TARGET_BROADCAST_CHANNEL);
|
||||
err = bt_bap_base_foreach_subgroup(base, subgroup_get_valid_bis_indexes_cb,
|
||||
&base_bis_index_bitfield);
|
||||
if ((err != 0 && err != -ECANCELED) ||
|
||||
(err == -ECANCELED && base_bis_index_bitfield == 0)) {
|
||||
printk("Failed to get valid BIS indexes: %d\n", err);
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
err = bt_bap_base_get_bis_indexes(base, &base_bis_index_bitfield);
|
||||
if (err != 0) {
|
||||
printk("Failed to BIS indexes: %d\n", err);
|
||||
printk("Failed to get BIS indexes: %d\n", err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TARGET_BROADCAST_CHANNEL */
|
||||
|
||||
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
|
||||
|
|
@ -939,7 +887,8 @@ static int pa_sync_past(struct bt_conn *conn, uint16_t pa_interval)
|
|||
static void recv_state_updated_cb(struct bt_conn *conn,
|
||||
const struct bt_bap_scan_delegator_recv_state *recv_state)
|
||||
{
|
||||
printk("Receive state updated, pa sync state: %u\n", recv_state->pa_sync_state);
|
||||
printk("Receive state updated, pa sync state: %u, encrypt_state %u\n",
|
||||
recv_state->pa_sync_state, recv_state->encrypt_state);
|
||||
|
||||
for (uint8_t i = 0; i < recv_state->num_subgroups; i++) {
|
||||
printk("subgroup %d bis_sync: 0x%08x\n", i, recv_state->subgroups[i].bis_sync);
|
||||
|
|
@ -1032,15 +981,38 @@ static int bis_sync_req_cb(struct bt_conn *conn,
|
|||
const struct bt_bap_scan_delegator_recv_state *recv_state,
|
||||
const uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS])
|
||||
{
|
||||
printk("BIS sync request received for %p: 0x%08x->0x%08x, broadcast id: 0x%06x, (%s)\n",
|
||||
recv_state, requested_bis_sync, bis_sync_req[0], recv_state->broadcast_id,
|
||||
/* Bit field indicating from which subgroup(s) BIS sync is requested */
|
||||
uint32_t requested_subgroup_sync = 0; /* currently only used for printout */
|
||||
|
||||
requested_bis_sync = 0;
|
||||
|
||||
for (uint8_t subgroup = 0; subgroup < recv_state->num_subgroups; subgroup++) {
|
||||
if (bis_sync_req[subgroup] != 0) {
|
||||
if (requested_bis_sync == 0) {
|
||||
requested_bis_sync = bis_sync_req[subgroup];
|
||||
} else {
|
||||
if (requested_bis_sync != BT_BAP_BIS_SYNC_NO_PREF &&
|
||||
bis_sync_req[subgroup] != BT_BAP_BIS_SYNC_NO_PREF) {
|
||||
/* Spec a little bit unclear. Here we choose to say that
|
||||
* BIS sync request from more than 1 subgroup is not
|
||||
* possible unless sync value is 0 or
|
||||
* BT_BAP_BIS_SYNC_NO_PREF
|
||||
*/
|
||||
printk("Unsupported BIS sync request from more than 1 "
|
||||
"subgroup\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
requested_subgroup_sync |= BIT(subgroup);
|
||||
}
|
||||
}
|
||||
|
||||
printk("BIS sync req for %p: BIS indexes 0x%08x (subgroup indexes 0x%08x), "
|
||||
"broadcast id: 0x%06x, (%s)\n",
|
||||
recv_state, requested_bis_sync, requested_subgroup_sync, recv_state->broadcast_id,
|
||||
big_synced ? "BIG synced" : "BIG not synced");
|
||||
|
||||
/* We only care about a single subgroup in this sample */
|
||||
if (big_synced && requested_bis_sync != bis_sync_req[0]) {
|
||||
/* If the BIS sync request is received while we are already
|
||||
* synced, it means that the requested BIS sync has changed.
|
||||
*/
|
||||
if (big_synced && requested_bis_sync == 0) {
|
||||
int err;
|
||||
|
||||
/* The stream stopped callback will be called as part of this,
|
||||
|
|
@ -1058,9 +1030,8 @@ static int bis_sync_req_cb(struct bt_conn *conn,
|
|||
k_sem_give(&sem_broadcast_sink_stopped);
|
||||
}
|
||||
|
||||
requested_bis_sync = bis_sync_req[0];
|
||||
broadcaster_broadcast_id = recv_state->broadcast_id;
|
||||
if (bis_sync_req[0] != 0) {
|
||||
if (requested_bis_sync != 0) {
|
||||
k_sem_give(&sem_bis_sync_requested);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue