This adds BAP support for long-lived PA sync. A BAP Broadcast Sink might probe Broadcasters autonomoulsy (by performing short-lived PA sync to parse the BASE and discover streams), or it might act as a Scan Delegator, probing Broadcasters added by a Broadcast Assistant. If the Assistant requested the Delegator to establish PA sync with a Broadcaster, the PA sync should be long-lived (it should remain active until requested otherwise by the Assistant). --- profiles/audio/bap.c | 64 +++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index a2c5a546d..76171eae7 100644 --- a/profiles/audio/bap.c +++ b/profiles/audio/bap.c @@ -127,6 +127,7 @@ struct bap_data { enum { BAP_PA_SHORT_REQ = 0, /* Request for short PA sync */ + BAP_PA_LONG_REQ, /* Request for long PA sync */ BAP_PA_BIG_SYNC_REQ, /* Request for PA Sync and BIG Sync */ }; @@ -1004,9 +1005,11 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data) DBG("BIG Sync completed"); - g_io_channel_unref(setup->io); - g_io_channel_shutdown(setup->io, TRUE, NULL); - setup->io = NULL; + if (setup->io) { + 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(bap_data->service, 0); @@ -1255,12 +1258,24 @@ static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond, return FALSE; } - /* Close the io and remove the queue request for another PA Sync */ + /* Close the listen io */ g_io_channel_shutdown(data->listen_io, TRUE, NULL); g_io_channel_unref(data->listen_io); - g_io_channel_shutdown(io, TRUE, NULL); data->listen_io = NULL; + if (req->type == BAP_PA_LONG_REQ) { + /* If long-lived PA sync was requested, keep a reference + * to the PA sync io to keep the sync active. + */ + data->listen_io = io; + g_io_channel_ref(io); + } else { + /* For short-lived PA, the sync is no longer needed at + * this point, so the io can be closed. + */ + g_io_channel_shutdown(io, TRUE, NULL); + } + /* Analyze received BASE data and create remote media endpoints for each * BIS matching our capabilities */ @@ -2192,7 +2207,7 @@ static void check_pa_req_in_progress(void *data, void *user_data) *((bool *)user_data) = TRUE; } -static int short_lived_pa_sync(struct bap_bcast_pa_req *req); +static int pa_sync(struct bap_bcast_pa_req *req); static void pa_and_big_sync(struct bap_bcast_pa_req *req); static gboolean pa_idle_timer(gpointer user_data) @@ -2210,7 +2225,11 @@ static gboolean pa_idle_timer(gpointer user_data) switch (req->type) { case BAP_PA_SHORT_REQ: DBG("do short lived PA Sync"); - short_lived_pa_sync(req); + pa_sync(req); + break; + case BAP_PA_LONG_REQ: + DBG("do long lived PA Sync"); + pa_sync(req); break; case BAP_PA_BIG_SYNC_REQ: DBG("do PA Sync and BIG Sync"); @@ -2236,8 +2255,8 @@ static void setup_accept_io_broadcast(struct bap_data *data, struct bap_bcast_pa_req *req = new0(struct bap_bcast_pa_req, 1); struct bap_adapter *adapter = data->adapter; - /* Timer could be stopped if all the short lived requests were treated. - * Check the state of the timer and turn it on so that this requests + /* Timer could be stopped if all other requests were treated. + * Check the state of the timer and turn it on so that this request * can also be treated. */ if (adapter->pa_timer_id == 0) @@ -2980,7 +2999,7 @@ static void bap_detached(struct bt_bap *bap, void *user_data) bap_data_remove(data); } -static int short_lived_pa_sync(struct bap_bcast_pa_req *req) +static int pa_sync(struct bap_bcast_pa_req *req) { struct btd_service *service = req->data.service; struct bap_data *data = btd_service_get_user_data(service); @@ -3030,10 +3049,13 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) const char *strbis = NULL; DBG("PA Sync done"); - g_io_channel_unref(setup->io); - g_io_channel_shutdown(setup->io, TRUE, NULL); - setup->io = io; - g_io_channel_ref(setup->io); + + if (setup->io) { + g_io_channel_unref(setup->io); + 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. @@ -3086,14 +3108,14 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data) memcpy(&qos.bcast.out, &setup->qos.bcast.io_qos, sizeof(struct bt_iso_io_qos)); - if (!bt_io_set(setup->io, &err, + if (!bt_io_set(io, &err, BT_IO_OPT_QOS, &qos, BT_IO_OPT_INVALID)) { error("bt_io_set: %s", err->message); g_error_free(err); } - if (!bt_io_bcast_accept(setup->io, + if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb, req, NULL, &err, BT_IO_OPT_ISO_BC_NUM_BIS, @@ -3114,6 +3136,16 @@ static void pa_and_big_sync(struct bap_bcast_pa_req *req) req->in_progress = TRUE; + if (bap_data->listen_io) { + /* If there is an active listen io for the BAP session + * with the Broadcast Source, it means that PA sync is + * already established. Go straight to establishing BIG + * sync. + */ + iso_do_big_sync(bap_data->listen_io, req); + return; + } + DBG("Create PA sync with this source"); setup->io = bt_io_listen(NULL, iso_do_big_sync, req, NULL, &err, -- 2.39.2