This adds a handler for the Modify Source opcode. A Broadcast Assistant might write the Modify Source opcode to request the Scan Delegator to terminate or establish sync with some BISes. If required to terminate sync with specific BISes, the Scan Delegator must unlink and release the streams, leaving the others active. If required to sync to a new BIS, the Scan Delegator must redo BIG sync with a new array of BIS indeces. All active streams must be unlinked and disabled, and the new stream should be configured. Then, the user can select and acquire the streams, which will recreate the ios. --- profiles/audio/bass.c | 146 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c index 0904748a2..3a37d6711 100644 --- a/profiles/audio/bass.c +++ b/profiles/audio/bass.c @@ -335,6 +335,8 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, GError *gerr = NULL; struct bt_bap_qos *bap_qos = bt_bap_stream_get_qos(stream); struct bt_iso_qos qos; + struct bass_setup *setup = queue_find(dg->setups, + match_setup_stream, stream); if (dg->bap != bap) return; @@ -392,6 +394,10 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, */ bt_bass_clear_bis_sync(dg->src, bis); break; + case BT_BAP_STREAM_STATE_IDLE: + bt_bass_clear_bis_sync(dg->src, bis); + setup->stream = NULL; + break; } } @@ -417,6 +423,44 @@ static void setup_configure_stream(struct bass_setup *setup) NULL, NULL); } +static void stream_unlink(void *data, void *user_data) +{ + struct bt_bap_stream *link = data; + struct bt_bap_stream *stream = user_data; + + bt_bap_stream_io_unlink(link, stream); +} + +static void bass_remove_bis(struct bass_setup *setup) +{ + struct queue *links = bt_bap_stream_io_get_links(setup->stream); + + queue_foreach(links, stream_unlink, setup->stream); + bt_bap_stream_release(setup->stream, NULL, NULL); +} + +static void setup_disable_streaming(void *data, void *user_data) +{ + struct bass_setup *setup = data; + struct queue *links = bt_bap_stream_io_get_links(setup->stream); + + if (!setup->stream) + return; + + if (bt_bap_stream_get_state(setup->stream) != + BT_BAP_STREAM_STATE_STREAMING) + return; + + queue_foreach(links, stream_unlink, setup->stream); + bt_bap_stream_disable(setup->stream, false, NULL, NULL); +} + +static void bass_add_bis(struct bass_setup *setup) +{ + queue_foreach(setup->dg->setups, setup_disable_streaming, NULL); + setup_configure_stream(setup); +} + static void bis_handler(uint8_t bis, uint8_t sgrp, struct iovec *caps, struct iovec *meta, struct bt_bap_qos *qos, void *user_data) { @@ -566,8 +610,13 @@ static void setup_free(void *data) util_iov_free(setup->meta, 1); util_iov_free(setup->config, 1); - bt_bass_clear_bis_sync(setup->dg->src, + if (setup->stream) { + uint8_t state = bt_bap_stream_get_state(setup->stream); + + if (state == BT_BAP_STREAM_STATE_STREAMING) + bt_bass_clear_bis_sync(setup->dg->src, stream_get_bis(setup->stream)); + } } bool bass_bcast_remove(struct btd_device *device) @@ -1231,6 +1280,98 @@ static int handle_set_bcode_req(struct bt_bcast_src *bcast_src, return 0; } +static bool setup_match_bis(const void *data, const void *match_data) +{ + const struct bass_setup *setup = data; + const int bis = PTR_TO_INT(match_data); + + return setup->bis == bis; +} + +static void bass_update_bis_sync(struct bass_delegator *dg, + struct bt_bcast_src *bcast_src) +{ + for (int bis = 1; bis < ISO_MAX_NUM_BIS; bis++) { + struct bass_setup *setup = queue_find(dg->setups, + setup_match_bis, INT_TO_PTR(bis)); + uint8_t state; + + if (!setup) + continue; + + state = bt_bap_stream_get_state(setup->stream); + + if (!setup->stream && bt_bass_check_bis(bcast_src, bis)) + bass_add_bis(setup); + else if (setup->stream && + state == BT_BAP_STREAM_STATE_STREAMING && + !bt_bass_check_bis(bcast_src, bis)) + bass_remove_bis(setup); + } +} + +static int handle_mod_src_req(struct bt_bcast_src *bcast_src, + struct bt_bass_mod_src_params *params, + struct bass_data *data) +{ + struct bass_delegator *dg; + uint8_t sync_state; + int err = 0; + + DBG(""); + + dg = queue_find(delegators, delegator_match_src, bcast_src); + if (!dg) + return -EINVAL; + + err = bt_bass_get_pa_sync(bcast_src, &sync_state); + if (err) + return err; + + switch (sync_state) { + case BT_BASS_SYNCHRONIZED_TO_PA: + if (params->pa_sync == PA_SYNC_NO_SYNC) { + g_io_channel_shutdown(dg->io, TRUE, NULL); + g_io_channel_unref(dg->io); + dg->io = NULL; + + bt_bass_set_pa_sync(dg->src, + BT_BASS_NOT_SYNCHRONIZED_TO_PA); + } else { + bass_update_bis_sync(dg, bcast_src); + } + break; + case BT_BASS_NOT_SYNCHRONIZED_TO_PA: + if (params->pa_sync == PA_SYNC_NO_PAST) { + struct btd_adapter *adapter = + device_get_adapter(dg->device); + GError *err = NULL; + + dg->io = bt_io_listen(NULL, confirm_cb, dg, + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(adapter), + BT_IO_OPT_SOURCE_TYPE, + btd_adapter_get_address_type(adapter), + BT_IO_OPT_DEST_BDADDR, + device_get_address(dg->device), + BT_IO_OPT_DEST_TYPE, + btd_device_get_bdaddr_type(dg->device), + BT_IO_OPT_MODE, BT_IO_MODE_ISO, + BT_IO_OPT_QOS, &bap_sink_pa_qos, + BT_IO_OPT_INVALID); + if (!dg->io) { + error("%s", err->message); + g_error_free(err); + } + } + + break; + } + + return 0; +} + static int cp_handler(struct bt_bcast_src *bcast_src, uint8_t op, void *params, void *user_data) { @@ -1244,6 +1385,9 @@ static int cp_handler(struct bt_bcast_src *bcast_src, uint8_t op, void *params, case BT_BASS_SET_BCAST_CODE: err = handle_set_bcode_req(bcast_src, params, data); break; + case BT_BASS_MOD_SRC: + err = handle_mod_src_req(bcast_src, params, data); + break; } return err; -- 2.43.0