Message ID | 20240514113914.21048-3-andrei.istodorescu@nxp.com |
---|---|
State | New |
Headers | show |
Series | Create transports for matching BISes | expand |
Hi Andrei, On Tue, May 14, 2024 at 7:39 AM Andrei Istodorescu <andrei.istodorescu@nxp.com> wrote: > > Rename bt_bap_add_bis to bt_bap_check_bis. This function is now an > utilitary for checking if the information from a BIS is matching the > capabilities in bt_bap. This utilitary is used when parsing the BASE > data so that we can decide if we create a new setup/stream/transport for > it, or advance to the next BIS. > When checking in local PACS for a BIS match, return also the local PAC, > along with the status of the operation. This information is required > later when we create the stream. > > Use the BIS index received in the BASE to synchronize to the BIG. > Allow bt_bap_endpoint to be NULL. > Remove the Broadcast Sink code from set_configuration. > Update BASE parsing so that it creates streams and transports, without a > remote PAC and endpoint. > Update bap_find_setup_by_stream to find the setup in case the stream does > not contain an endpoint. > --- > profiles/audio/bap.c | 323 ++++++++++++++++++++++++++++++------------- > src/shared/bap.c | 227 ++++++++++++++++-------------- > src/shared/bap.h | 6 +- > 3 files changed, 357 insertions(+), 199 deletions(-) > > diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c > index f09f2ea44109..eb321f33386a 100644 > --- a/profiles/audio/bap.c > +++ b/profiles/audio/bap.c > @@ -115,6 +115,7 @@ struct bap_data { > struct queue *srcs; > struct queue *snks; > struct queue *bcast; > + struct queue *bcast_snks; > struct queue *streams; > GIOChannel *listen_io; > int selecting; > @@ -131,7 +132,10 @@ struct bap_bcast_pa_req { > bool in_progress; > union { > struct btd_service *service; > - struct bap_setup *setup; > + struct bap_accept { > + struct bap_setup *setup; > + struct bap_data *bap_data; > + } accept_data; Don't really like the idea of using nested structs like this, make it confusing imo, besides can't you actually access the bap_data via btd_device_get_user_data? > } data; > }; > > @@ -202,6 +206,8 @@ static void ep_unregister(void *data) > MEDIA_ENDPOINT_INTERFACE); > } > > +static void setup_free(void *data); > + > static void bap_data_free(struct bap_data *data) > { > if (data->listen_io) { > @@ -218,6 +224,7 @@ static void bap_data_free(struct bap_data *data) > queue_destroy(data->srcs, ep_unregister); > queue_destroy(data->bcast, ep_unregister); > queue_destroy(data->streams, NULL); > + queue_destroy(data->bcast_snks, setup_free); > bt_bap_ready_unregister(data->bap, data->ready_id); > bt_bap_state_unregister(data->bap, data->state_id); > bt_bap_pac_unregister(data->bap, data->pac_id); > @@ -855,7 +862,11 @@ static struct bap_setup *setup_new(struct bap_ep *ep) > setup = new0(struct bap_setup, 1); > setup->ep = ep; > > - if (queue_find(ep->data->bcast, NULL, ep)) { > + /* Broadcast Source has endpoints in bcast list, Broadcast Sink > + * does not have endpoints > + */ > + if (((ep != NULL) && queue_find(ep->data->bcast, NULL, ep)) || > + (ep == NULL)) { > /* Mark BIG and BIS to be auto assigned */ > setup->qos.bcast.big = BT_ISO_QOS_BIG_UNSET; > setup->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET; > @@ -871,12 +882,14 @@ static struct bap_setup *setup_new(struct bap_ep *ep) > setup->qos_parser = setup_parse_ucast_qos; > } > > - if (!ep->setups) > - ep->setups = queue_new(); > + if (ep) { > + if (!ep->setups) > + ep->setups = queue_new(); > > - queue_push_tail(ep->setups, setup); > + queue_push_tail(ep->setups, setup); > > - DBG("ep %p setup %p", ep, setup); > + DBG("ep %p setup %p", ep, setup); > + } > > return setup; > } > @@ -947,17 +960,6 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, > return btd_error_invalid_args(msg); > } > > - /* For BAP Broadcast Sink, the capabilities and metadata are coming > - * from the source's BIS, which are present in the remote PAC > - */ > - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) { > - util_iov_free(setup->caps, 1); > - setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1); > - util_iov_free(setup->metadata, 1); > - setup->metadata = util_iov_dup( > - bt_bap_pac_get_metadata(ep->rpac), 1); > - } > - > setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac, > &setup->qos, setup->caps); > bt_bap_stream_set_user_data(setup->stream, ep->path); > @@ -993,20 +995,27 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, > static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data) > { > struct bap_bcast_pa_req *req = user_data; > - struct bap_setup *setup = req->data.setup; > + struct bap_setup *setup = req->data.accept_data.setup; > int fd; > > DBG("BIG Sync completed"); > > - queue_remove(setup->ep->data->adapter->bcast_pa_requests, req); > + g_io_channel_unref(setup->io); > + g_io_channel_shutdown(setup->io, TRUE, NULL); > + setup->io = NULL; > > /* This device is no longer needed */ > - btd_service_connecting_complete(setup->ep->data->service, 0); > + btd_service_connecting_complete(req->data.accept_data.bap_data->service, > + 0); > > fd = g_io_channel_unix_get_fd(io); > > + queue_remove(req->data.accept_data.bap_data->adapter->bcast_pa_requests, > + req); > + free(req); > + > if (bt_bap_stream_set_io(setup->stream, fd)) { > - bt_bap_stream_enable(setup->stream, true, NULL, NULL, NULL); > + bt_bap_stream_start(setup->stream, NULL, NULL); > g_io_channel_set_close_on_unref(io, FALSE); > return; > } > @@ -1019,8 +1028,54 @@ static void print_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, > util_hexdump(' ', v, l, user_data, NULL); > } > > -static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, > - util_debug_func_t func) > +static void create_stream_for_bis(struct bap_data *bap_data, > + struct bt_bap_pac *lpac, struct bt_iso_qos *qos, > + struct iovec *caps, struct iovec *meta, char *path) > +{ > + struct bap_setup *setup; > + > + setup = setup_new(NULL); > + > + /* Create BAP QoS structure */ > + setup->qos.bcast.big = qos->bcast.big; > + setup->qos.bcast.bis = qos->bcast.bis; > + setup->qos.bcast.sync_factor = qos->bcast.sync_factor; > + setup->qos.bcast.packing = qos->bcast.packing; > + setup->qos.bcast.framing = qos->bcast.framing; > + setup->qos.bcast.encryption = qos->bcast.encryption; > + if (setup->qos.bcast.encryption) > + util_iov_append(setup->qos.bcast.bcode, > + qos->bcast.bcode, > + sizeof(qos->bcast.bcode)); > + setup->qos.bcast.options = qos->bcast.options; > + setup->qos.bcast.skip = qos->bcast.skip; > + setup->qos.bcast.sync_timeout = qos->bcast.sync_timeout; > + setup->qos.bcast.sync_cte_type = > + qos->bcast.sync_cte_type; > + setup->qos.bcast.mse = qos->bcast.mse; > + setup->qos.bcast.timeout = qos->bcast.timeout; > + setup->qos.bcast.io_qos.interval = > + qos->bcast.in.interval; > + setup->qos.bcast.io_qos.latency = qos->bcast.in.latency; > + setup->qos.bcast.io_qos.phy = qos->bcast.in.phy; > + setup->qos.bcast.io_qos.rtn = qos->bcast.in.rtn; > + setup->qos.bcast.io_qos.sdu = qos->bcast.in.sdu; > + > + queue_push_tail(bap_data->bcast_snks, setup); > + > + /* Create and configure stream */ > + setup->stream = bt_bap_stream_new(bap_data->bap, > + lpac, NULL, &setup->qos, caps); > + > + bt_bap_stream_set_user_data(setup->stream, path); > + bt_bap_stream_config(setup->stream, &setup->qos, > + caps, NULL, NULL); > + bt_bap_stream_metadata(setup->stream, meta, > + NULL, NULL); > +} > + > +static bool parse_base(struct bap_data *bap_data, struct bt_iso_base *base, > + struct bt_iso_qos *qos, util_debug_func_t func) > { > struct iovec iov = { > .iov_base = base->base, > @@ -1099,6 +1154,10 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, > for (; num_bis; num_bis--) { > uint8_t bis_index; > struct iovec *l3_caps; > + struct iovec *merged_caps; > + struct bt_bap_pac *matched_lpac; > + char *path; > + int err; > > if (!util_iov_pull_u8(&iov, &bis_index)) { > ret = false; > @@ -1106,6 +1165,11 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, > } > > util_debug(func, NULL, "BIS #%d", bis_index); > + err = asprintf(&path, "%s/bis%d", > + device_get_path(bap_data->device), > + bis_index); > + if (err < 0) > + continue; > > /* Read Codec Specific Configuration */ > l3_caps = new0(struct iovec, 1); > @@ -1128,9 +1192,16 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, > l3_caps->iov_len, NULL, print_ltv, > func); > > - /* Try to create a PAC using this BIS information */ > - bt_bap_add_bis(bap, bis_index, &codec, l2_caps, l3_caps, > - meta); > + /* Check if this BIS matches any local PAC */ > + bt_bap_verify_bis(bap_data->bap, bis_index, &codec, > + l2_caps, l3_caps, &matched_lpac, > + &merged_caps); > + > + if (matched_lpac == NULL || merged_caps == NULL) > + continue; > + > + create_stream_for_bis(bap_data, matched_lpac, qos, > + merged_caps, meta, path); > } > > group_fail: > @@ -1175,12 +1246,16 @@ static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) > g_io_channel_unref(data->listen_io); > g_io_channel_shutdown(io, TRUE, NULL); > data->listen_io = NULL; > - queue_remove(data->adapter->bcast_pa_requests, req); > - free(req); > + > /* Analyze received BASE data and create remote media endpoints for each > * BIS matching our capabilities > */ > - parse_base(data->bap, &base, bap_debug); > + parse_base(data, &base, &qos, bap_debug); > + > + service_set_connecting(req->data.service); > + > + queue_remove(data->adapter->bcast_pa_requests, req); > + free(req); > } > > static bool match_data_bap_data(const void *data, const void *match_data) > @@ -1583,6 +1658,7 @@ static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, > struct bt_bap_stream *stream) > { > struct bap_ep *ep = NULL; > + struct queue *queue = NULL; > > switch (bt_bap_stream_get_type(stream)) { > case BT_BAP_STREAM_TYPE_UCAST: > @@ -1597,9 +1673,11 @@ static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, > } > > if (ep) > - return queue_find(ep->setups, match_setup_stream, stream); > + queue = ep->setups; > + else > + queue = data->bcast_snks; > > - return NULL; > + return queue_find(queue, match_setup_stream, stream); > } > > static void iso_connect_bcast_cb(GIOChannel *chan, GError *err, > @@ -2141,7 +2219,8 @@ static void setup_accept_io_broadcast(struct bap_data *data, > */ > req->type = BAP_PA_BIG_SYNC_REQ; > req->in_progress = FALSE; > - req->data.setup = setup; > + req->data.accept_data.setup = setup; > + req->data.accept_data.bap_data = data; > queue_push_tail(adapter->bcast_pa_requests, req); > } > > @@ -2201,7 +2280,7 @@ static void setup_create_bcast_io(struct bap_data *data, > memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos, > sizeof(struct bt_iso_io_qos)); > > - if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE) > + if (bt_bap_stream_get_dir(stream) == BT_BAP_BCAST_SINK) > setup_connect_io_broadcast(data, setup, stream, &iso_qos, > defer); > else > @@ -2413,7 +2492,7 @@ static uint8_t get_streams_nb_by_state(struct bap_setup *setup) > return stream_cnt; > } > > -static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, > +static void bap_state_bcast_src(struct bt_bap_stream *stream, uint8_t old_state, > uint8_t new_state, void *user_data) > { > struct bap_data *data = user_data; > @@ -2442,66 +2521,96 @@ static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, > case BT_BAP_STREAM_STATE_CONFIG: > 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); > - 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. > + /* 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. > */ > - 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; > + 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 (!create_io_bises(setup, nb_bises, data)) { > + 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); > + 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; > } > } > > +static void bap_state_bcast_sink(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; > + > + DBG("stream %p: %s(%u) -> %s(%u)", stream, > + bt_bap_stream_statestr(old_state), old_state, > + bt_bap_stream_statestr(new_state), new_state); > + > + if (new_state == old_state && new_state != BT_BAP_STREAM_STATE_CONFIG) > + return; > + > + setup = bap_find_setup_by_stream(data, stream); > + > + switch (new_state) { > + case BT_BAP_STREAM_STATE_IDLE: > + /* Release stream if idle */ > + if (setup) > + setup_free(setup); > + else > + queue_remove(data->streams, stream); > + break; > + case BT_BAP_STREAM_STATE_CONFIG: > + if (!setup) > + break; > + if (old_state == > + BT_BAP_STREAM_STATE_CONFIG) > + setup_create_io(data, setup, stream, defer); > + if (old_state == > + BT_BAP_STREAM_STATE_STREAMING) > + setup_io_close(setup, NULL); > + break; > + } > +} > + > static void pac_added(struct bt_bap_pac *pac, void *user_data) > { > struct btd_service *service = user_data; > @@ -2847,29 +2956,50 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) > { > GError *err = NULL; > struct bap_bcast_pa_req *req = user_data; > - struct bap_setup *setup = req->data.setup; > - struct bap_data *data = setup->ep->data; > + struct bap_setup *setup = req->data.accept_data.setup; > + struct bap_data *data = req->data.accept_data.bap_data; > struct sockaddr_iso_bc iso_bc_addr; > struct bt_iso_qos qos; > + char *path; > + int bis_index = 1; > + int s_err; > + const char *strbis = NULL; > > - DBG("PA Sync done, do BIG Sync"); > + DBG("PA Sync done"); > g_io_channel_unref(setup->io); > - setup->io = NULL; > - > + g_io_channel_shutdown(setup->io, TRUE, NULL); > setup->io = io; > g_io_channel_ref(setup->io); > > /* TODO > * We can only synchronize with a single BIS to a BIG. > * In order to have multiple BISes targeting this BIG we need to have > - * all the BISes before doing this request. This request is triggered > - * by an endpoint "SetConfiguration" command. For multiple BISes > - * we need another way to specify which BISes user is requesting > + * all the BISes before doing bt_io_bcast_accept. > + * This request comes from a transport "Acquire" call. > + * For multiple BISes in the same BIG we need to either wait for all > + * transports in the same BIG to be acquired or tell when to do the > + * bt_io_bcast_accept by other means > */ > + path = bt_bap_stream_get_user_data(setup->stream); > + > + strbis = strstr(path, "/bis"); > + if (strbis == NULL) { > + DBG("bis index cannot be found"); > + return; > + } > + > + s_err = sscanf(strbis, "/bis%d", &bis_index); > + if (s_err == -1) { > + DBG("sscanf error"); > + return; > + } > + > + DBG("Do BIG Sync with BIS %d", bis_index); > + > iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(data->device); > memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(data->device), > sizeof(bdaddr_t)); > - iso_bc_addr.bc_bis[0] = 1; > + iso_bc_addr.bc_bis[0] = bis_index; > iso_bc_addr.bc_num_bis = 1; > > /* Set the user requested QOS */ > @@ -2913,8 +3043,8 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) > static void pa_and_big_sync(struct bap_bcast_pa_req *req) > { > GError *err = NULL; > - struct bap_setup *setup = req->data.setup; > - struct bap_data *data = setup->ep->data; > + struct bap_setup *setup = req->data.accept_data.setup; > + struct bap_data *data = req->data.accept_data.bap_data; > > req->in_progress = TRUE; > > @@ -2967,6 +3097,7 @@ static int bap_bcast_probe(struct btd_service *service) > free(data); > return -EINVAL; > } > + data->bcast_snks = queue_new(); > > if (!bt_bap_attach(data->bap, NULL)) { > error("BAP unable to attach"); > @@ -2977,7 +3108,7 @@ static int bap_bcast_probe(struct btd_service *service) > > data->ready_id = bt_bap_ready_register(data->bap, bap_ready, service, > NULL); > - data->state_id = bt_bap_state_register(data->bap, bap_state_bcast, > + data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_sink, > bap_connecting_bcast, data, NULL); > data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, > pac_removed_broadcast, data, NULL); > @@ -3160,7 +3291,7 @@ static int bap_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) > return -EINVAL; > } > > - data->state_id = bt_bap_state_register(data->bap, bap_state_bcast, > + data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_src, > bap_connecting_bcast, data, NULL); > data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, > pac_removed_broadcast, data, NULL); Let's split the changes to src/shared/, that always should be self-contained as much as possible since it has a different license and is subject to unit tests. > diff --git a/src/shared/bap.c b/src/shared/bap.c > index 919ecbb6a016..fe91cd67f491 100644 > --- a/src/shared/bap.c > +++ b/src/shared/bap.c > @@ -226,6 +226,7 @@ struct bt_bap_stream_io { > struct bt_bap_stream_ops { > uint8_t type; > void (*set_state)(struct bt_bap_stream *stream, uint8_t state); > + unsigned int (*get_state)(struct bt_bap_stream *stream); > unsigned int (*config)(struct bt_bap_stream *stream, > struct bt_bap_qos *qos, struct iovec *data, > bt_bap_stream_func_t func, void *user_data); > @@ -245,6 +246,8 @@ struct bt_bap_stream_ops { > unsigned int (*metadata)(struct bt_bap_stream *stream, > struct iovec *data, bt_bap_stream_func_t func, > void *user_data); > + unsigned int (*get_dir)(struct bt_bap_stream *stream); > + unsigned int (*get_loc)(struct bt_bap_stream *stream); > unsigned int (*release)(struct bt_bap_stream *stream, > bt_bap_stream_func_t func, void *user_data); > }; > @@ -1578,6 +1581,11 @@ done: > bap_stream_state_changed(stream); > } > > +static unsigned int bap_ucast_get_state(struct bt_bap_stream *stream) > +{ > + return stream->ep->state; > +} > + > static unsigned int bap_ucast_config(struct bt_bap_stream *stream, > struct bt_bap_qos *qos, > struct iovec *data, > @@ -1923,6 +1931,27 @@ static bool bap_stream_valid(struct bt_bap_stream *stream) > return queue_find(stream->bap->streams, NULL, stream); > } > > +static unsigned int bap_ucast_get_dir(struct bt_bap_stream *stream) > +{ > + return stream->ep->dir; > +} > + > +static unsigned int bap_ucast_get_location(struct bt_bap_stream *stream) > +{ > + struct bt_pacs *pacs; > + > + if (!stream) > + return 0x00000000; > + > + pacs = stream->client ? stream->bap->rdb->pacs : stream->bap->ldb->pacs; > + > + if (stream->ep->dir == BT_BAP_SOURCE) > + return pacs->source_loc_value; > + else if (stream->ep->dir == BT_BAP_SINK) > + return pacs->sink_loc_value; > + return 0x00000000; > +} > + > static unsigned int bap_ucast_release(struct bt_bap_stream *stream, > bt_bap_stream_func_t func, > void *user_data) > @@ -2000,6 +2029,11 @@ static void bap_bcast_set_state(struct bt_bap_stream *stream, uint8_t state) > } > } > > +static unsigned int bap_bcast_get_state(struct bt_bap_stream *stream) > +{ > + return stream->state; > +} > + > static unsigned int bap_bcast_enable(struct bt_bap_stream *stream, > bool enable_links, struct iovec *data, > bt_bap_stream_func_t func, > @@ -2019,6 +2053,17 @@ static unsigned int bap_bcast_start(struct bt_bap_stream *stream, > return 1; > } > > +static unsigned int bap_bcast_sink_disable(struct bt_bap_stream *stream, > + bool disable_links, > + bt_bap_stream_func_t func, > + void *user_data) > +{ > + bap_stream_io_detach(stream); > + stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); > + > + return 1; > +} > + > static unsigned int bap_bcast_disable(struct bt_bap_stream *stream, > bool disable_links, > bt_bap_stream_func_t func, > @@ -2040,6 +2085,43 @@ static unsigned int bap_bcast_metadata(struct bt_bap_stream *stream, > return 1; > } > > +static unsigned int bap_bcast_src_get_dir(struct bt_bap_stream *stream) > +{ > + return BT_BAP_BCAST_SINK; > +} > + > +static unsigned int bap_bcast_sink_get_dir(struct bt_bap_stream *stream) > +{ > + return BT_BAP_BCAST_SOURCE; > +} > + > +static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t, > + uint8_t *v, void *user_data) > +{ > + uint32_t location32; > + > + if (!v) > + return; > + > + memcpy(&location32, v, l); > + *((uint32_t *)user_data) = le32_to_cpu(location32); > +} > + > +static unsigned int bap_bcast_get_location(struct bt_bap_stream *stream) > +{ > + uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; > + uint32_t allocation = 0; > + struct iovec *caps; > + > + caps = bt_bap_stream_get_config(stream); > + > + /* Get stream allocation from capabilities */ > + util_ltv_foreach(caps->iov_base, caps->iov_len, &type, > + bap_sink_get_allocation, &allocation); > + > + return allocation; > +} > + > static unsigned int bap_bcast_release(struct bt_bap_stream *stream, > bt_bap_stream_func_t func, > void *user_data) > @@ -2049,11 +2131,12 @@ static unsigned int bap_bcast_release(struct bt_bap_stream *stream, > return 1; > } > > -#define STREAM_OPS(_type, _set_state, _config, _qos, _enable, _start, \ > - _disable, _stop, _metadata, _release) \ > +#define STREAM_OPS(_type, _set_state, _get_state, _config, _qos, _enable, \ > + _start, _disable, _stop, _metadata, _get_dir, _get_loc, _release) \ > { \ > .type = _type, \ > .set_state = _set_state, \ > + .get_state = _get_state, \ > .config = _config, \ > .qos = _qos, \ > .enable = _enable, \ > @@ -2061,26 +2144,40 @@ static unsigned int bap_bcast_release(struct bt_bap_stream *stream, > .disable = _disable, \ > .stop = _stop, \ > .metadata = _metadata, \ > + .get_dir = _get_dir,\ > + .get_loc = _get_loc, \ > .release = _release, \ > } > > static const struct bt_bap_stream_ops stream_ops[] = { > STREAM_OPS(BT_BAP_SINK, bap_ucast_set_state, > + bap_ucast_get_state, > bap_ucast_config, bap_ucast_qos, bap_ucast_enable, > bap_ucast_start, bap_ucast_disable, bap_ucast_stop, > - bap_ucast_metadata, bap_ucast_release), > + bap_ucast_metadata, bap_ucast_get_dir, > + bap_ucast_get_location, > + bap_ucast_release), > STREAM_OPS(BT_BAP_SOURCE, bap_ucast_set_state, > + bap_ucast_get_state, > bap_ucast_config, bap_ucast_qos, bap_ucast_enable, > bap_ucast_start, bap_ucast_disable, bap_ucast_stop, > - bap_ucast_metadata, bap_ucast_release), > + bap_ucast_metadata, bap_ucast_get_dir, > + bap_ucast_get_location, > + bap_ucast_release), > STREAM_OPS(BT_BAP_BCAST_SINK, bap_bcast_set_state, > + bap_bcast_get_state, > bap_bcast_config, NULL, bap_bcast_enable, > - bap_bcast_start, bap_bcast_disable, NULL, > - bap_bcast_metadata, bap_bcast_release), > + bap_bcast_start, bap_bcast_sink_disable, NULL, > + bap_bcast_metadata, bap_bcast_sink_get_dir, > + bap_bcast_get_location, > + bap_bcast_release), > STREAM_OPS(BT_BAP_BCAST_SOURCE, bap_bcast_set_state, > + bap_bcast_get_state, > bap_bcast_config, NULL, bap_bcast_enable, > bap_bcast_start, bap_bcast_disable, NULL, > - bap_bcast_metadata, bap_bcast_release), > + bap_bcast_metadata, bap_bcast_src_get_dir, > + bap_bcast_get_location, > + bap_bcast_release), > }; > > static const struct bt_bap_stream_ops * > @@ -5380,11 +5477,7 @@ uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream) > if (!stream) > return BT_BAP_STREAM_STATE_IDLE; > > - if (stream->lpac->type != BT_BAP_BCAST_SOURCE && > - stream->lpac->type != BT_BAP_BCAST_SINK) > - return stream->ep->state; > - else > - return stream->state; > + return stream->ops->get_state(stream); > } > > bool bt_bap_stream_set_user_data(struct bt_bap_stream *stream, void *user_data) > @@ -5568,53 +5661,15 @@ uint8_t bt_bap_stream_get_dir(struct bt_bap_stream *stream) > if (!stream) > return 0x00; > > - if (stream->ep) > - return stream->ep->dir; > - > - if (bt_bap_pac_get_type(stream->lpac) == BT_BAP_BCAST_SINK) > - return BT_BAP_BCAST_SOURCE; > - else > - return BT_BAP_BCAST_SINK; > -} > - > -static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t, > - uint8_t *v, void *user_data) > -{ > - uint32_t location32; > - > - if (!v) > - return; > - > - memcpy(&location32, v, l); > - *((uint32_t *)user_data) = le32_to_cpu(location32); > + return stream->ops->get_dir(stream); > } > > uint32_t bt_bap_stream_get_location(struct bt_bap_stream *stream) > { > - struct bt_pacs *pacs; > - uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; > - uint32_t allocation = 0; > - struct iovec *caps; > - > if (!stream) > return 0x00000000; > > - pacs = stream->client ? stream->bap->rdb->pacs : stream->bap->ldb->pacs; > - > - if (stream->ep) { > - if (stream->ep->dir == BT_BAP_SOURCE) > - return pacs->source_loc_value; > - else if (stream->ep->dir == BT_BAP_SINK) > - return pacs->sink_loc_value; > - } > - > - caps = bt_bap_stream_get_config(stream); > - > - /* Get stream allocation from capabilities */ > - util_ltv_foreach(caps->iov_base, caps->iov_len, &type, > - bap_sink_get_allocation, &allocation); > - > - return allocation; > + return stream->ops->get_loc(stream); > } > > struct iovec *bt_bap_stream_get_config(struct bt_bap_stream *stream) > @@ -6398,9 +6453,11 @@ static void check_local_pac(void *data, void *user_data) > > /* We have a match if all selected LTVs have a match */ > if ((bis_compare_data.data32 & > - CODEC_SPECIFIC_CONFIGURATION_MASK) == > - CODEC_SPECIFIC_CONFIGURATION_MASK) > + CODEC_SPECIFIC_CONFIGURATION_MASK) == > + CODEC_SPECIFIC_CONFIGURATION_MASK) { > compare_data->found = true; > + compare_data->data = data; > + } > } > } > > @@ -6425,7 +6482,8 @@ static void bap_sink_match_allocation(size_t i, uint8_t l, uint8_t t, > data->found = false; > } > > -static bool bap_check_bis(struct bt_bap_db *ldb, struct iovec *bis_data) > +static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, > + struct iovec *bis_data) > { > struct bt_ltv_match compare_data = {}; > > @@ -6451,21 +6509,18 @@ static bool bap_check_bis(struct bt_bap_db *ldb, struct iovec *bis_data) > &compare_data); > } > > - return compare_data.found; > + return compare_data; > } > > -void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, > +void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, > struct bt_bap_codec *codec, > struct iovec *l2_caps, > struct iovec *l3_caps, > - struct iovec *meta) > + struct bt_bap_pac **lpac, > + struct iovec **caps) > { > - struct bt_bap_pac *pac_source_bis; > - struct bt_bap_endpoint *ep; > - int err = 0; > - struct bt_bap_pac_qos bis_qos = {0}; > - uint8_t type = 0; > struct bt_ltv_extract merge_data = {0}; > + struct bt_ltv_match match_data; > > merge_data.src = l3_caps; > merge_data.result = new0(struct iovec, 1); > @@ -6481,43 +6536,15 @@ void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, > /* Check each BIS Codec Specific Configuration LTVs against our Codec > * Specific Capabilities and if the BIS matches create a PAC with it > */ > - if (bap_check_bis(bap->ldb, merge_data.result) == false) > - goto cleanup; > - > - DBG(bap, "Matching BIS %i", bis_index); > - > - /* Create a QoS structure based on the received BIS information to > - * specify the desired channel for this BIS/PAC > - */ > - type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; > - util_ltv_foreach(merge_data.result->iov_base, > - merge_data.result->iov_len, &type, > - bap_sink_get_allocation, &bis_qos.location); > - > - /* Create a remote PAC */ > - pac_source_bis = bap_pac_new(bap->rdb, NULL, > - BT_BAP_BCAST_SOURCE, codec, &bis_qos, > - merge_data.result, meta); > - > - err = asprintf(&pac_source_bis->name, "%d", bis_index); > - > - if (err < 0) { > - DBG(bap, "error in asprintf"); > - goto cleanup; > + match_data = bap_check_bis(bap->ldb, merge_data.result); > + if (match_data.found == true) { > + *caps = merge_data.result; > + *lpac = match_data.data; > + DBG(bap, "Matching BIS %i", bis_index); > + } else { > + util_iov_free(merge_data.result, 1); > + *caps = NULL; > + *lpac = NULL; > } > > - /* Add remote source endpoint */ > - if (!bap->rdb->broadcast_sources) > - bap->rdb->broadcast_sources = queue_new(); > - queue_push_tail(bap->rdb->broadcast_sources, pac_source_bis); > - > - queue_foreach(bap->pac_cbs, notify_pac_added, pac_source_bis); > - /* Push remote endpoint with direction sink */ > - ep = bap_endpoint_new_broadcast(bap->rdb, BT_BAP_BCAST_SINK); > - > - if (ep) > - queue_push_tail(bap->remote_eps, ep); > - > -cleanup: > - util_iov_free(merge_data.result, 1); > } > diff --git a/src/shared/bap.h b/src/shared/bap.h > index 62e2104850c7..35524df0b451 100644 > --- a/src/shared/bap.h > +++ b/src/shared/bap.h > @@ -251,9 +251,9 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac); > > struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream); > > -void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, > +void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, > struct bt_bap_codec *codec, > struct iovec *l2_caps, > struct iovec *l3_caps, > - struct iovec *meta); > - > + struct bt_bap_pac **lpac, > + struct iovec **caps); > -- > 2.40.1 >
diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index f09f2ea44109..eb321f33386a 100644 --- a/profiles/audio/bap.c +++ b/profiles/audio/bap.c @@ -115,6 +115,7 @@ struct bap_data { struct queue *srcs; struct queue *snks; struct queue *bcast; + struct queue *bcast_snks; struct queue *streams; GIOChannel *listen_io; int selecting; @@ -131,7 +132,10 @@ struct bap_bcast_pa_req { bool in_progress; union { struct btd_service *service; - struct bap_setup *setup; + struct bap_accept { + struct bap_setup *setup; + struct bap_data *bap_data; + } accept_data; } data; }; @@ -202,6 +206,8 @@ static void ep_unregister(void *data) MEDIA_ENDPOINT_INTERFACE); } +static void setup_free(void *data); + static void bap_data_free(struct bap_data *data) { if (data->listen_io) { @@ -218,6 +224,7 @@ static void bap_data_free(struct bap_data *data) queue_destroy(data->srcs, ep_unregister); queue_destroy(data->bcast, ep_unregister); queue_destroy(data->streams, NULL); + queue_destroy(data->bcast_snks, setup_free); bt_bap_ready_unregister(data->bap, data->ready_id); bt_bap_state_unregister(data->bap, data->state_id); bt_bap_pac_unregister(data->bap, data->pac_id); @@ -855,7 +862,11 @@ static struct bap_setup *setup_new(struct bap_ep *ep) setup = new0(struct bap_setup, 1); setup->ep = ep; - if (queue_find(ep->data->bcast, NULL, ep)) { + /* Broadcast Source has endpoints in bcast list, Broadcast Sink + * does not have endpoints + */ + if (((ep != NULL) && queue_find(ep->data->bcast, NULL, ep)) || + (ep == NULL)) { /* Mark BIG and BIS to be auto assigned */ setup->qos.bcast.big = BT_ISO_QOS_BIG_UNSET; setup->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET; @@ -871,12 +882,14 @@ static struct bap_setup *setup_new(struct bap_ep *ep) setup->qos_parser = setup_parse_ucast_qos; } - if (!ep->setups) - ep->setups = queue_new(); + if (ep) { + if (!ep->setups) + ep->setups = queue_new(); - queue_push_tail(ep->setups, setup); + queue_push_tail(ep->setups, setup); - DBG("ep %p setup %p", ep, setup); + DBG("ep %p setup %p", ep, setup); + } return setup; } @@ -947,17 +960,6 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, return btd_error_invalid_args(msg); } - /* For BAP Broadcast Sink, the capabilities and metadata are coming - * from the source's BIS, which are present in the remote PAC - */ - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) { - util_iov_free(setup->caps, 1); - setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1); - util_iov_free(setup->metadata, 1); - setup->metadata = util_iov_dup( - bt_bap_pac_get_metadata(ep->rpac), 1); - } - setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac, &setup->qos, setup->caps); bt_bap_stream_set_user_data(setup->stream, ep->path); @@ -993,20 +995,27 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data) { struct bap_bcast_pa_req *req = user_data; - struct bap_setup *setup = req->data.setup; + struct bap_setup *setup = req->data.accept_data.setup; int fd; DBG("BIG Sync completed"); - queue_remove(setup->ep->data->adapter->bcast_pa_requests, req); + g_io_channel_unref(setup->io); + g_io_channel_shutdown(setup->io, TRUE, NULL); + setup->io = NULL; /* This device is no longer needed */ - btd_service_connecting_complete(setup->ep->data->service, 0); + btd_service_connecting_complete(req->data.accept_data.bap_data->service, + 0); fd = g_io_channel_unix_get_fd(io); + queue_remove(req->data.accept_data.bap_data->adapter->bcast_pa_requests, + req); + free(req); + if (bt_bap_stream_set_io(setup->stream, fd)) { - bt_bap_stream_enable(setup->stream, true, NULL, NULL, NULL); + bt_bap_stream_start(setup->stream, NULL, NULL); g_io_channel_set_close_on_unref(io, FALSE); return; } @@ -1019,8 +1028,54 @@ static void print_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v, util_hexdump(' ', v, l, user_data, NULL); } -static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, - util_debug_func_t func) +static void create_stream_for_bis(struct bap_data *bap_data, + struct bt_bap_pac *lpac, struct bt_iso_qos *qos, + struct iovec *caps, struct iovec *meta, char *path) +{ + struct bap_setup *setup; + + setup = setup_new(NULL); + + /* Create BAP QoS structure */ + setup->qos.bcast.big = qos->bcast.big; + setup->qos.bcast.bis = qos->bcast.bis; + setup->qos.bcast.sync_factor = qos->bcast.sync_factor; + setup->qos.bcast.packing = qos->bcast.packing; + setup->qos.bcast.framing = qos->bcast.framing; + setup->qos.bcast.encryption = qos->bcast.encryption; + if (setup->qos.bcast.encryption) + util_iov_append(setup->qos.bcast.bcode, + qos->bcast.bcode, + sizeof(qos->bcast.bcode)); + setup->qos.bcast.options = qos->bcast.options; + setup->qos.bcast.skip = qos->bcast.skip; + setup->qos.bcast.sync_timeout = qos->bcast.sync_timeout; + setup->qos.bcast.sync_cte_type = + qos->bcast.sync_cte_type; + setup->qos.bcast.mse = qos->bcast.mse; + setup->qos.bcast.timeout = qos->bcast.timeout; + setup->qos.bcast.io_qos.interval = + qos->bcast.in.interval; + setup->qos.bcast.io_qos.latency = qos->bcast.in.latency; + setup->qos.bcast.io_qos.phy = qos->bcast.in.phy; + setup->qos.bcast.io_qos.rtn = qos->bcast.in.rtn; + setup->qos.bcast.io_qos.sdu = qos->bcast.in.sdu; + + queue_push_tail(bap_data->bcast_snks, setup); + + /* Create and configure stream */ + setup->stream = bt_bap_stream_new(bap_data->bap, + lpac, NULL, &setup->qos, caps); + + bt_bap_stream_set_user_data(setup->stream, path); + bt_bap_stream_config(setup->stream, &setup->qos, + caps, NULL, NULL); + bt_bap_stream_metadata(setup->stream, meta, + NULL, NULL); +} + +static bool parse_base(struct bap_data *bap_data, struct bt_iso_base *base, + struct bt_iso_qos *qos, util_debug_func_t func) { struct iovec iov = { .iov_base = base->base, @@ -1099,6 +1154,10 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, for (; num_bis; num_bis--) { uint8_t bis_index; struct iovec *l3_caps; + struct iovec *merged_caps; + struct bt_bap_pac *matched_lpac; + char *path; + int err; if (!util_iov_pull_u8(&iov, &bis_index)) { ret = false; @@ -1106,6 +1165,11 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, } util_debug(func, NULL, "BIS #%d", bis_index); + err = asprintf(&path, "%s/bis%d", + device_get_path(bap_data->device), + bis_index); + if (err < 0) + continue; /* Read Codec Specific Configuration */ l3_caps = new0(struct iovec, 1); @@ -1128,9 +1192,16 @@ static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base, l3_caps->iov_len, NULL, print_ltv, func); - /* Try to create a PAC using this BIS information */ - bt_bap_add_bis(bap, bis_index, &codec, l2_caps, l3_caps, - meta); + /* Check if this BIS matches any local PAC */ + bt_bap_verify_bis(bap_data->bap, bis_index, &codec, + l2_caps, l3_caps, &matched_lpac, + &merged_caps); + + if (matched_lpac == NULL || merged_caps == NULL) + continue; + + create_stream_for_bis(bap_data, matched_lpac, qos, + merged_caps, meta, path); } group_fail: @@ -1175,12 +1246,16 @@ static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data) g_io_channel_unref(data->listen_io); g_io_channel_shutdown(io, TRUE, NULL); data->listen_io = NULL; - queue_remove(data->adapter->bcast_pa_requests, req); - free(req); + /* Analyze received BASE data and create remote media endpoints for each * BIS matching our capabilities */ - parse_base(data->bap, &base, bap_debug); + parse_base(data, &base, &qos, bap_debug); + + service_set_connecting(req->data.service); + + queue_remove(data->adapter->bcast_pa_requests, req); + free(req); } static bool match_data_bap_data(const void *data, const void *match_data) @@ -1583,6 +1658,7 @@ static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, struct bt_bap_stream *stream) { struct bap_ep *ep = NULL; + struct queue *queue = NULL; switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: @@ -1597,9 +1673,11 @@ static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, } if (ep) - return queue_find(ep->setups, match_setup_stream, stream); + queue = ep->setups; + else + queue = data->bcast_snks; - return NULL; + return queue_find(queue, match_setup_stream, stream); } static void iso_connect_bcast_cb(GIOChannel *chan, GError *err, @@ -2141,7 +2219,8 @@ static void setup_accept_io_broadcast(struct bap_data *data, */ req->type = BAP_PA_BIG_SYNC_REQ; req->in_progress = FALSE; - req->data.setup = setup; + req->data.accept_data.setup = setup; + req->data.accept_data.bap_data = data; queue_push_tail(adapter->bcast_pa_requests, req); } @@ -2201,7 +2280,7 @@ static void setup_create_bcast_io(struct bap_data *data, memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos, sizeof(struct bt_iso_io_qos)); - if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE) + if (bt_bap_stream_get_dir(stream) == BT_BAP_BCAST_SINK) setup_connect_io_broadcast(data, setup, stream, &iso_qos, defer); else @@ -2413,7 +2492,7 @@ static uint8_t get_streams_nb_by_state(struct bap_setup *setup) return stream_cnt; } -static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, +static void bap_state_bcast_src(struct bt_bap_stream *stream, uint8_t old_state, uint8_t new_state, void *user_data) { struct bap_data *data = user_data; @@ -2442,66 +2521,96 @@ static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state, case BT_BAP_STREAM_STATE_CONFIG: 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); - 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. + /* 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. */ - 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; + 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 (!create_io_bises(setup, nb_bises, data)) { + 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); + 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; } } +static void bap_state_bcast_sink(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; + + DBG("stream %p: %s(%u) -> %s(%u)", stream, + bt_bap_stream_statestr(old_state), old_state, + bt_bap_stream_statestr(new_state), new_state); + + if (new_state == old_state && new_state != BT_BAP_STREAM_STATE_CONFIG) + return; + + setup = bap_find_setup_by_stream(data, stream); + + switch (new_state) { + case BT_BAP_STREAM_STATE_IDLE: + /* Release stream if idle */ + if (setup) + setup_free(setup); + else + queue_remove(data->streams, stream); + break; + case BT_BAP_STREAM_STATE_CONFIG: + if (!setup) + break; + if (old_state == + BT_BAP_STREAM_STATE_CONFIG) + setup_create_io(data, setup, stream, defer); + if (old_state == + BT_BAP_STREAM_STATE_STREAMING) + setup_io_close(setup, NULL); + break; + } +} + static void pac_added(struct bt_bap_pac *pac, void *user_data) { struct btd_service *service = user_data; @@ -2847,29 +2956,50 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) { GError *err = NULL; struct bap_bcast_pa_req *req = user_data; - struct bap_setup *setup = req->data.setup; - struct bap_data *data = setup->ep->data; + struct bap_setup *setup = req->data.accept_data.setup; + struct bap_data *data = req->data.accept_data.bap_data; struct sockaddr_iso_bc iso_bc_addr; struct bt_iso_qos qos; + char *path; + int bis_index = 1; + int s_err; + const char *strbis = NULL; - DBG("PA Sync done, do BIG Sync"); + DBG("PA Sync done"); g_io_channel_unref(setup->io); - setup->io = NULL; - + g_io_channel_shutdown(setup->io, TRUE, NULL); setup->io = io; g_io_channel_ref(setup->io); /* TODO * We can only synchronize with a single BIS to a BIG. * In order to have multiple BISes targeting this BIG we need to have - * all the BISes before doing this request. This request is triggered - * by an endpoint "SetConfiguration" command. For multiple BISes - * we need another way to specify which BISes user is requesting + * all the BISes before doing bt_io_bcast_accept. + * This request comes from a transport "Acquire" call. + * For multiple BISes in the same BIG we need to either wait for all + * transports in the same BIG to be acquired or tell when to do the + * bt_io_bcast_accept by other means */ + path = bt_bap_stream_get_user_data(setup->stream); + + strbis = strstr(path, "/bis"); + if (strbis == NULL) { + DBG("bis index cannot be found"); + return; + } + + s_err = sscanf(strbis, "/bis%d", &bis_index); + if (s_err == -1) { + DBG("sscanf error"); + return; + } + + DBG("Do BIG Sync with BIS %d", bis_index); + iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(data->device); memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(data->device), sizeof(bdaddr_t)); - iso_bc_addr.bc_bis[0] = 1; + iso_bc_addr.bc_bis[0] = bis_index; iso_bc_addr.bc_num_bis = 1; /* Set the user requested QOS */ @@ -2913,8 +3043,8 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) static void pa_and_big_sync(struct bap_bcast_pa_req *req) { GError *err = NULL; - struct bap_setup *setup = req->data.setup; - struct bap_data *data = setup->ep->data; + struct bap_setup *setup = req->data.accept_data.setup; + struct bap_data *data = req->data.accept_data.bap_data; req->in_progress = TRUE; @@ -2967,6 +3097,7 @@ static int bap_bcast_probe(struct btd_service *service) free(data); return -EINVAL; } + data->bcast_snks = queue_new(); if (!bt_bap_attach(data->bap, NULL)) { error("BAP unable to attach"); @@ -2977,7 +3108,7 @@ static int bap_bcast_probe(struct btd_service *service) data->ready_id = bt_bap_ready_register(data->bap, bap_ready, service, NULL); - data->state_id = bt_bap_state_register(data->bap, bap_state_bcast, + data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_sink, bap_connecting_bcast, data, NULL); data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, pac_removed_broadcast, data, NULL); @@ -3160,7 +3291,7 @@ static int bap_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) return -EINVAL; } - data->state_id = bt_bap_state_register(data->bap, bap_state_bcast, + data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_src, bap_connecting_bcast, data, NULL); data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast, pac_removed_broadcast, data, NULL); diff --git a/src/shared/bap.c b/src/shared/bap.c index 919ecbb6a016..fe91cd67f491 100644 --- a/src/shared/bap.c +++ b/src/shared/bap.c @@ -226,6 +226,7 @@ struct bt_bap_stream_io { struct bt_bap_stream_ops { uint8_t type; void (*set_state)(struct bt_bap_stream *stream, uint8_t state); + unsigned int (*get_state)(struct bt_bap_stream *stream); unsigned int (*config)(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *data, bt_bap_stream_func_t func, void *user_data); @@ -245,6 +246,8 @@ struct bt_bap_stream_ops { unsigned int (*metadata)(struct bt_bap_stream *stream, struct iovec *data, bt_bap_stream_func_t func, void *user_data); + unsigned int (*get_dir)(struct bt_bap_stream *stream); + unsigned int (*get_loc)(struct bt_bap_stream *stream); unsigned int (*release)(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data); }; @@ -1578,6 +1581,11 @@ done: bap_stream_state_changed(stream); } +static unsigned int bap_ucast_get_state(struct bt_bap_stream *stream) +{ + return stream->ep->state; +} + static unsigned int bap_ucast_config(struct bt_bap_stream *stream, struct bt_bap_qos *qos, struct iovec *data, @@ -1923,6 +1931,27 @@ static bool bap_stream_valid(struct bt_bap_stream *stream) return queue_find(stream->bap->streams, NULL, stream); } +static unsigned int bap_ucast_get_dir(struct bt_bap_stream *stream) +{ + return stream->ep->dir; +} + +static unsigned int bap_ucast_get_location(struct bt_bap_stream *stream) +{ + struct bt_pacs *pacs; + + if (!stream) + return 0x00000000; + + pacs = stream->client ? stream->bap->rdb->pacs : stream->bap->ldb->pacs; + + if (stream->ep->dir == BT_BAP_SOURCE) + return pacs->source_loc_value; + else if (stream->ep->dir == BT_BAP_SINK) + return pacs->sink_loc_value; + return 0x00000000; +} + static unsigned int bap_ucast_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) @@ -2000,6 +2029,11 @@ static void bap_bcast_set_state(struct bt_bap_stream *stream, uint8_t state) } } +static unsigned int bap_bcast_get_state(struct bt_bap_stream *stream) +{ + return stream->state; +} + static unsigned int bap_bcast_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *data, bt_bap_stream_func_t func, @@ -2019,6 +2053,17 @@ static unsigned int bap_bcast_start(struct bt_bap_stream *stream, return 1; } +static unsigned int bap_bcast_sink_disable(struct bt_bap_stream *stream, + bool disable_links, + bt_bap_stream_func_t func, + void *user_data) +{ + bap_stream_io_detach(stream); + stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG); + + return 1; +} + static unsigned int bap_bcast_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, @@ -2040,6 +2085,43 @@ static unsigned int bap_bcast_metadata(struct bt_bap_stream *stream, return 1; } +static unsigned int bap_bcast_src_get_dir(struct bt_bap_stream *stream) +{ + return BT_BAP_BCAST_SINK; +} + +static unsigned int bap_bcast_sink_get_dir(struct bt_bap_stream *stream) +{ + return BT_BAP_BCAST_SOURCE; +} + +static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t, + uint8_t *v, void *user_data) +{ + uint32_t location32; + + if (!v) + return; + + memcpy(&location32, v, l); + *((uint32_t *)user_data) = le32_to_cpu(location32); +} + +static unsigned int bap_bcast_get_location(struct bt_bap_stream *stream) +{ + uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; + uint32_t allocation = 0; + struct iovec *caps; + + caps = bt_bap_stream_get_config(stream); + + /* Get stream allocation from capabilities */ + util_ltv_foreach(caps->iov_base, caps->iov_len, &type, + bap_sink_get_allocation, &allocation); + + return allocation; +} + static unsigned int bap_bcast_release(struct bt_bap_stream *stream, bt_bap_stream_func_t func, void *user_data) @@ -2049,11 +2131,12 @@ static unsigned int bap_bcast_release(struct bt_bap_stream *stream, return 1; } -#define STREAM_OPS(_type, _set_state, _config, _qos, _enable, _start, \ - _disable, _stop, _metadata, _release) \ +#define STREAM_OPS(_type, _set_state, _get_state, _config, _qos, _enable, \ + _start, _disable, _stop, _metadata, _get_dir, _get_loc, _release) \ { \ .type = _type, \ .set_state = _set_state, \ + .get_state = _get_state, \ .config = _config, \ .qos = _qos, \ .enable = _enable, \ @@ -2061,26 +2144,40 @@ static unsigned int bap_bcast_release(struct bt_bap_stream *stream, .disable = _disable, \ .stop = _stop, \ .metadata = _metadata, \ + .get_dir = _get_dir,\ + .get_loc = _get_loc, \ .release = _release, \ } static const struct bt_bap_stream_ops stream_ops[] = { STREAM_OPS(BT_BAP_SINK, bap_ucast_set_state, + bap_ucast_get_state, bap_ucast_config, bap_ucast_qos, bap_ucast_enable, bap_ucast_start, bap_ucast_disable, bap_ucast_stop, - bap_ucast_metadata, bap_ucast_release), + bap_ucast_metadata, bap_ucast_get_dir, + bap_ucast_get_location, + bap_ucast_release), STREAM_OPS(BT_BAP_SOURCE, bap_ucast_set_state, + bap_ucast_get_state, bap_ucast_config, bap_ucast_qos, bap_ucast_enable, bap_ucast_start, bap_ucast_disable, bap_ucast_stop, - bap_ucast_metadata, bap_ucast_release), + bap_ucast_metadata, bap_ucast_get_dir, + bap_ucast_get_location, + bap_ucast_release), STREAM_OPS(BT_BAP_BCAST_SINK, bap_bcast_set_state, + bap_bcast_get_state, bap_bcast_config, NULL, bap_bcast_enable, - bap_bcast_start, bap_bcast_disable, NULL, - bap_bcast_metadata, bap_bcast_release), + bap_bcast_start, bap_bcast_sink_disable, NULL, + bap_bcast_metadata, bap_bcast_sink_get_dir, + bap_bcast_get_location, + bap_bcast_release), STREAM_OPS(BT_BAP_BCAST_SOURCE, bap_bcast_set_state, + bap_bcast_get_state, bap_bcast_config, NULL, bap_bcast_enable, bap_bcast_start, bap_bcast_disable, NULL, - bap_bcast_metadata, bap_bcast_release), + bap_bcast_metadata, bap_bcast_src_get_dir, + bap_bcast_get_location, + bap_bcast_release), }; static const struct bt_bap_stream_ops * @@ -5380,11 +5477,7 @@ uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream) if (!stream) return BT_BAP_STREAM_STATE_IDLE; - if (stream->lpac->type != BT_BAP_BCAST_SOURCE && - stream->lpac->type != BT_BAP_BCAST_SINK) - return stream->ep->state; - else - return stream->state; + return stream->ops->get_state(stream); } bool bt_bap_stream_set_user_data(struct bt_bap_stream *stream, void *user_data) @@ -5568,53 +5661,15 @@ uint8_t bt_bap_stream_get_dir(struct bt_bap_stream *stream) if (!stream) return 0x00; - if (stream->ep) - return stream->ep->dir; - - if (bt_bap_pac_get_type(stream->lpac) == BT_BAP_BCAST_SINK) - return BT_BAP_BCAST_SOURCE; - else - return BT_BAP_BCAST_SINK; -} - -static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t, - uint8_t *v, void *user_data) -{ - uint32_t location32; - - if (!v) - return; - - memcpy(&location32, v, l); - *((uint32_t *)user_data) = le32_to_cpu(location32); + return stream->ops->get_dir(stream); } uint32_t bt_bap_stream_get_location(struct bt_bap_stream *stream) { - struct bt_pacs *pacs; - uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; - uint32_t allocation = 0; - struct iovec *caps; - if (!stream) return 0x00000000; - pacs = stream->client ? stream->bap->rdb->pacs : stream->bap->ldb->pacs; - - if (stream->ep) { - if (stream->ep->dir == BT_BAP_SOURCE) - return pacs->source_loc_value; - else if (stream->ep->dir == BT_BAP_SINK) - return pacs->sink_loc_value; - } - - caps = bt_bap_stream_get_config(stream); - - /* Get stream allocation from capabilities */ - util_ltv_foreach(caps->iov_base, caps->iov_len, &type, - bap_sink_get_allocation, &allocation); - - return allocation; + return stream->ops->get_loc(stream); } struct iovec *bt_bap_stream_get_config(struct bt_bap_stream *stream) @@ -6398,9 +6453,11 @@ static void check_local_pac(void *data, void *user_data) /* We have a match if all selected LTVs have a match */ if ((bis_compare_data.data32 & - CODEC_SPECIFIC_CONFIGURATION_MASK) == - CODEC_SPECIFIC_CONFIGURATION_MASK) + CODEC_SPECIFIC_CONFIGURATION_MASK) == + CODEC_SPECIFIC_CONFIGURATION_MASK) { compare_data->found = true; + compare_data->data = data; + } } } @@ -6425,7 +6482,8 @@ static void bap_sink_match_allocation(size_t i, uint8_t l, uint8_t t, data->found = false; } -static bool bap_check_bis(struct bt_bap_db *ldb, struct iovec *bis_data) +static struct bt_ltv_match bap_check_bis(struct bt_bap_db *ldb, + struct iovec *bis_data) { struct bt_ltv_match compare_data = {}; @@ -6451,21 +6509,18 @@ static bool bap_check_bis(struct bt_bap_db *ldb, struct iovec *bis_data) &compare_data); } - return compare_data.found; + return compare_data; } -void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, +void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, struct bt_bap_codec *codec, struct iovec *l2_caps, struct iovec *l3_caps, - struct iovec *meta) + struct bt_bap_pac **lpac, + struct iovec **caps) { - struct bt_bap_pac *pac_source_bis; - struct bt_bap_endpoint *ep; - int err = 0; - struct bt_bap_pac_qos bis_qos = {0}; - uint8_t type = 0; struct bt_ltv_extract merge_data = {0}; + struct bt_ltv_match match_data; merge_data.src = l3_caps; merge_data.result = new0(struct iovec, 1); @@ -6481,43 +6536,15 @@ void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, /* Check each BIS Codec Specific Configuration LTVs against our Codec * Specific Capabilities and if the BIS matches create a PAC with it */ - if (bap_check_bis(bap->ldb, merge_data.result) == false) - goto cleanup; - - DBG(bap, "Matching BIS %i", bis_index); - - /* Create a QoS structure based on the received BIS information to - * specify the desired channel for this BIS/PAC - */ - type = BAP_CHANNEL_ALLOCATION_LTV_TYPE; - util_ltv_foreach(merge_data.result->iov_base, - merge_data.result->iov_len, &type, - bap_sink_get_allocation, &bis_qos.location); - - /* Create a remote PAC */ - pac_source_bis = bap_pac_new(bap->rdb, NULL, - BT_BAP_BCAST_SOURCE, codec, &bis_qos, - merge_data.result, meta); - - err = asprintf(&pac_source_bis->name, "%d", bis_index); - - if (err < 0) { - DBG(bap, "error in asprintf"); - goto cleanup; + match_data = bap_check_bis(bap->ldb, merge_data.result); + if (match_data.found == true) { + *caps = merge_data.result; + *lpac = match_data.data; + DBG(bap, "Matching BIS %i", bis_index); + } else { + util_iov_free(merge_data.result, 1); + *caps = NULL; + *lpac = NULL; } - /* Add remote source endpoint */ - if (!bap->rdb->broadcast_sources) - bap->rdb->broadcast_sources = queue_new(); - queue_push_tail(bap->rdb->broadcast_sources, pac_source_bis); - - queue_foreach(bap->pac_cbs, notify_pac_added, pac_source_bis); - /* Push remote endpoint with direction sink */ - ep = bap_endpoint_new_broadcast(bap->rdb, BT_BAP_BCAST_SINK); - - if (ep) - queue_push_tail(bap->remote_eps, ep); - -cleanup: - util_iov_free(merge_data.result, 1); } diff --git a/src/shared/bap.h b/src/shared/bap.h index 62e2104850c7..35524df0b451 100644 --- a/src/shared/bap.h +++ b/src/shared/bap.h @@ -251,9 +251,9 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac); struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream); -void bt_bap_add_bis(struct bt_bap *bap, uint8_t bis_index, +void bt_bap_verify_bis(struct bt_bap *bap, uint8_t bis_index, struct bt_bap_codec *codec, struct iovec *l2_caps, struct iovec *l3_caps, - struct iovec *meta); - + struct bt_bap_pac **lpac, + struct iovec **caps);