From 92f3ed1879042b43033241049b334404229df88d Mon Sep 17 00:00:00 2001 From: Silviu Florian Barbulescu Date: Fri, 23 Feb 2024 18:48:13 +0200 Subject: [PATCH] bap: Set generated BASE on all setups from the same BIG Set the BASE generated in BlueZ on all setups from the same BIG, and use defer to inform the kernel when to create the BIG. --- profiles/audio/bap.c | 212 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 196 insertions(+), 16 deletions(-) diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index 2c7daa6d4..38577bcf3 100644 --- a/profiles/audio/bap.c +++ b/profiles/audio/bap.c @@ -950,8 +950,12 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); - /* Disconnect IOs if connecting since QoS is going to be reconfigured */ - ep_close(ep); + /* Broadcast source supports multiple setups, each setup will be BIS + * and will be configured with the set_configuration command + * TO DO reconfiguration of a BIS. + */ + if (bt_bap_pac_get_type(ep->lpac) != BT_BAP_BCAST_SOURCE) + ep_close(ep); setup = setup_new(ep); @@ -987,7 +991,6 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) setup->msg = dbus_message_ref(msg); else { - setup->base = bt_bap_stream_get_base(setup->stream); setup->id = 0; } @@ -1853,7 +1856,7 @@ static void setup_connect_io(struct bap_data *data, struct bap_setup *setup, static void setup_connect_io_broadcast(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, - struct bt_iso_qos *qos) + struct bt_iso_qos *qos, int defer) { struct btd_adapter *adapter = data->user_data; GIOChannel *io = NULL; @@ -1890,7 +1893,7 @@ static void setup_connect_io_broadcast(struct bap_data *data, BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, qos, BT_IO_OPT_BASE, &base, - BT_IO_OPT_DEFER_TIMEOUT, false, + BT_IO_OPT_DEFER_TIMEOUT, defer, BT_IO_OPT_INVALID); if (!io) { @@ -2019,9 +2022,6 @@ static void setup_create_bcast_io(struct bap_data *data, memset(&iso_qos, 0, sizeof(iso_qos)); - if (!defer) - goto done; - iso_qos.bcast.big = setup->qos.bcast.big; iso_qos.bcast.bis = setup->qos.bcast.bis; iso_qos.bcast.sync_factor = setup->qos.bcast.sync_factor; @@ -2038,9 +2038,10 @@ static void setup_create_bcast_io(struct bap_data *data, iso_qos.bcast.timeout = setup->qos.bcast.timeout; memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos, sizeof(struct bt_iso_io_qos)); -done: + if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE) - setup_connect_io_broadcast(data, setup, stream, &iso_qos); + setup_connect_io_broadcast(data, setup, stream, &iso_qos, + defer); else setup_listen_io_broadcast(data, setup, stream, &iso_qos); } @@ -2128,11 +2129,135 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state, } } +/* This function will call setup_create_io on all BISes from a BIG. + * The defer parameter will be set on true on all but the last one. + * This is done to inform the kernel when to when to start the BIG. + */ +static bool create_io_bises(struct bap_setup *setup, + uint8_t nb_bises, struct bap_data *data) +{ + const struct queue_entry *entry; + struct bap_setup *ent_setup; + bool defer = true; + uint8_t active_bis_cnt = 1; + + for (entry = queue_get_entries(setup->ep->setups); + entry; entry = entry->next) { + ent_setup = entry->data; + + if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != + bt_bap_stream_get_qos(setup->stream)->bcast.big) + continue; + + if (active_bis_cnt == nb_bises) + defer = false; + + setup_create_io(data, ent_setup, ent_setup->stream, defer); + if (!ent_setup->io) { + error("Unable to create io"); + goto fail; + } + + active_bis_cnt++; + } + + return true; + +fail: + /* Clear the io of the created sockets if one + * socket creation fails. + */ + for (entry = queue_get_entries(setup->ep->setups); + entry; entry = entry->next) { + ent_setup = entry->data; + + if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != + bt_bap_stream_get_qos(setup->stream)->bcast.big) + continue; + + if (setup->io) + g_io_channel_unref(setup->io); + } + return false; +} + +static void iterate_setup_update_base(void *data, void *user_data) +{ + struct bap_setup *setup = data; + struct bap_setup *data_setup = user_data; + + if ((setup->stream != data_setup->stream) && + (setup->qos.bcast.big == data_setup->qos.bcast.big)) { + + if (setup->base) + util_iov_free(setup->base, 1); + + setup->base = util_iov_dup(data_setup->base, 1); + } +} + +/* Function checks the state of all streams in the same BIG + * as the parameter stream, so it can decide if any sockets need + * to be created. Returns he number of streams that need a socket + * from that BIG. + */ +static uint8_t get_streams_nb_by_state(struct bap_setup *setup) +{ + const struct queue_entry *entry; + struct bap_setup *ent_setup; + uint8_t stream_cnt = 0; + + if (setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET) + /* If BIG ID is unset this is a single BIS BIG. + * return 1 as create one socket only for this BIS + */ + return 1; + + for (entry = queue_get_entries(setup->ep->setups); + entry; entry = entry->next) { + ent_setup = entry->data; + + /* Skip the curent stream form testing */ + if (ent_setup == setup) { + stream_cnt++; + continue; + } + + /* Test only BISes for the same BIG */ + if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big != + bt_bap_stream_get_qos(setup->stream)->bcast.big) + continue; + + if (bt_bap_stream_get_state(ent_setup->stream) == + BT_BAP_STREAM_STATE_STREAMING) + /* If one stream in a multiple BIS BIG is in + * streaming state this means that just the current + * stream must have is socket created so return 1. + */ + return 1; + else if (bt_bap_stream_get_state(ent_setup->stream) != + BT_BAP_STREAM_STATE_CONFIG) + /* Not all streams form a BIG have received transport + * acquire, so wait for the other streams to. + */ + return 0; + + stream_cnt++; + } + + /* Return the number of streams for the BIG + * as all are ready to create sockets + */ + return stream_cnt; +} + static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bap_data *data = user_data; struct bap_setup *setup; + bool defer = false; + uint8_t nb_bises = 0; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, @@ -2153,14 +2278,69 @@ static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, queue_remove(data->streams, stream); break; case BT_BAP_STREAM_STATE_CONFIG: - if (setup && !setup->id) { - setup_create_io(data, setup, stream, true); + if (!setup || setup->id) + break; + if (bt_bap_stream_io_dir(stream) == + BT_BAP_BCAST_SOURCE) { + /* If the stream is attached to a + * broadcast sink endpoint. + */ + setup_create_io(data, setup, stream, defer); if (!setup->io) { error("Unable to create io"); - if (old_state != BT_BAP_STREAM_STATE_RELEASING) - bt_bap_stream_release(stream, NULL, - NULL); - return; + if (old_state != + BT_BAP_STREAM_STATE_RELEASING) + bt_bap_stream_release(stream, + NULL, NULL); + } + } else { + /* If the stream attached to a broadcast + * source endpoint generate the base. + */ + if (setup->base == NULL) { + setup->base = bt_bap_stream_get_base( + setup->stream); + /* Set the generated BASE on all setups + * from the same BIG. + */ + queue_foreach(setup->ep->setups, + iterate_setup_update_base, setup); + } + /* The kernel has 2 requirements when handling + * multiple BIS connections for the same BIG: + * 1 - setup_create_io for all but the last BIS + * must be with defer true so we can inform the + * kernel when to start the BIG. + * 2 - The order in which the setup_create_io + * are called must be in the order of BIS + * indexes in BASE from first to last. + * To address this requirement we will call + * setup_create_io on all BISes only when all + * transport acquire have been received and will + * send it in the order of the BIS index + * from BASE. + */ + nb_bises = get_streams_nb_by_state(setup); + + if (nb_bises == 1) { + setup_create_io(data, setup, + stream, defer); + if (!setup->io) { + error("Unable to create io"); + if (old_state != + BT_BAP_STREAM_STATE_RELEASING) + bt_bap_stream_release(stream, + NULL, NULL); + } + break; + } else if (nb_bises == 0) + break; + + if (!create_io_bises(setup, nb_bises, data)) { + if (old_state != + BT_BAP_STREAM_STATE_RELEASING) + bt_bap_stream_release(stream, + NULL, NULL); } } break; -- 2.47.3