@@ -62,6 +62,13 @@
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
#define MEDIA_INTERFACE "org.bluez.Media1"
+/* Periodic advertisments are performed by an idle timer, which,
+ * at every tick, checks a queue for pending PA requests.
+ * When there is no pending requests, an item is popped from the
+ * queue, marked as pending and then it gets processed.
+ */
+#define PA_IDLE_TIMEOUT 2
+
struct bap_setup {
struct bap_ep *ep;
struct bt_bap_stream *stream;
@@ -110,7 +117,62 @@ struct bap_data {
void *user_data;
};
+enum {
+ BAP_PA_SHORT_REQ = 0, /* Request for short PA sync */
+ BAP_PA_BIG_SYNC_REQ, /* Request for PA Sync and BIG Sync */
+};
+
+struct bap_bcast_pa_req {
+ uint8_t type;
+ bool in_progress;
+ union {
+ struct btd_service *service;
+ struct bap_setup *setup;
+ } data;
+};
+
static struct queue *sessions;
+static struct queue *bcast_pa_requests;
+static unsigned int pa_timer_id;
+
+/* Structure holding the parameters for Periodic Advertisement create sync.
+ * The full QOS is populated at the time the user selects and endpoint and
+ * configures it using SetConfiguration.
+ */
+static struct bt_iso_qos bap_sink_pa_qos = {
+ .bcast = {
+ .options = 0x00,
+ .skip = 0x0000,
+ .sync_timeout = 0x4000,
+ .sync_cte_type = 0x00,
+ /* TODO: The following parameters are not needed for PA Sync.
+ * They will be removed when the kernel checks will be removed.
+ */
+ .big = BT_ISO_QOS_BIG_UNSET,
+ .bis = BT_ISO_QOS_BIS_UNSET,
+ .encryption = 0x00,
+ .bcode = {0x00},
+ .mse = 0x00,
+ .timeout = 0x4000,
+ .sync_factor = 0x07,
+ .packing = 0x00,
+ .framing = 0x00,
+ .in = {
+ .interval = 10000,
+ .latency = 10,
+ .sdu = 40,
+ .phy = 0x02,
+ .rtn = 2,
+ },
+ .out = {
+ .interval = 10000,
+ .latency = 10,
+ .sdu = 40,
+ .phy = 0x02,
+ .rtn = 2,
+ }
+ }
+};
static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
{
@@ -427,113 +489,6 @@ static int parse_array(DBusMessageIter *iter, struct iovec *iov)
return 0;
}
-static bool parse_base(void *data, size_t len, util_debug_func_t func,
- uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
- struct bt_bap_codec *codec, struct iovec **caps,
- struct iovec **meta)
-{
- struct iovec iov = {
- .iov_base = data,
- .iov_len = len,
- };
-
- uint8_t capsLen, metaLen;
- struct iovec cc;
- struct iovec metadata;
-
- if (presDelay) {
- if (!util_iov_pull_le24(&iov, presDelay))
- return false;
- util_debug(func, NULL, "PresentationDelay %d", *presDelay);
- }
-
- if (numSubgroups) {
- if (!util_iov_pull_u8(&iov, numSubgroups))
- return false;
- util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
- }
-
- if (numBis) {
- if (!util_iov_pull_u8(&iov, numBis))
- return false;
- util_debug(func, NULL, "NumBis %d", *numBis);
- }
-
- if (codec) {
- codec = util_iov_pull_mem(&iov, sizeof(*codec));
- if (!codec)
- return false;
- util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
- "Codec", codec->id, codec->cid, codec->vid);
- }
-
- if (!util_iov_pull_u8(&iov, &capsLen))
- return false;
- util_debug(func, NULL, "CC Len %d", capsLen);
-
- if (!capsLen)
- return false;
-
- cc.iov_len = capsLen;
- cc.iov_base = util_iov_pull_mem(&iov, capsLen);
- if (!cc.iov_base)
- return false;
-
- if (caps) {
- if (*caps)
- util_iov_free(*caps, 1);
-
- *caps = util_iov_dup(&cc, 1);
- }
-
- for (int i = 0; capsLen > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
- uint8_t *caps;
-
- if (!ltv) {
- util_debug(func, NULL, "Unable to parse %s",
- "Capabilities");
- return false;
- }
-
- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
-
- caps = util_iov_pull_mem(&cc, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL, "Unable to parse %s",
- "CC");
- return false;
- }
- util_hexdump(' ', caps, ltv->len - 1, func, NULL);
-
- capsLen -= (ltv->len + 1);
- }
-
- if (!util_iov_pull_u8(&iov, &metaLen))
- return false;
- util_debug(func, NULL, "Metadata Len %d", metaLen);
-
- if (!metaLen)
- return false;
-
- metadata.iov_len = metaLen;
- metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
- if (!metadata.iov_base)
- return false;
-
- if (meta) {
- if (*meta)
- util_iov_free(*meta, 1);
-
- *meta = util_iov_dup(&metadata, 1);
- }
-
- util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
-
- return true;
-}
-
static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
struct bt_bap_io_qos *qos)
{
@@ -965,9 +920,20 @@ 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 = setup->stream_new(ep->data->bap, ep->lpac, ep->rpac,
&setup->qos, setup->caps);
-
+ bt_bap_stream_set_user_data(setup->stream, ep->path);
setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
setup->caps, config_cb, setup);
if (!setup->id) {
@@ -976,8 +942,6 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return btd_error_invalid_args(msg);
}
- bt_bap_stream_set_user_data(setup->stream, ep->path);
-
if (setup->metadata && setup->metadata->iov_len)
bt_bap_stream_metadata(setup->stream, setup->metadata, NULL,
NULL);
@@ -988,11 +952,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
break;
case BT_BAP_STREAM_TYPE_BCAST:
/* No message sent over the air for broadcast */
- if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
- setup->msg = dbus_message_ref(msg);
- else {
- setup->id = 0;
- }
+ setup->id = 0;
if (ep->data->service)
service_set_connecting(ep->data->service);
@@ -1003,79 +963,18 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
-static void update_bcast_qos(struct bt_iso_qos *qos,
- struct bt_bap_qos *bap_qos)
-{
- bap_qos->bcast.big = qos->bcast.big;
- bap_qos->bcast.bis = qos->bcast.bis;
- bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
- bap_qos->bcast.packing = qos->bcast.packing;
- bap_qos->bcast.framing = qos->bcast.framing;
- bap_qos->bcast.encryption = qos->bcast.encryption;
- bap_qos->bcast.options = qos->bcast.options;
- bap_qos->bcast.skip = qos->bcast.skip;
- bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
- bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
- bap_qos->bcast.mse = qos->bcast.mse;
- bap_qos->bcast.timeout = qos->bcast.timeout;
- bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
- bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
- bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
- bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
- bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
- if (!bap_qos->bcast.bcode)
- bap_qos->bcast.bcode = new0(struct iovec, 1);
- util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
- sizeof(qos->bcast.bcode));
-}
-
static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
{
- struct bap_setup *setup = user_data;
- struct bap_ep *ep = setup->ep;
- struct bap_data *data = ep->data;
- struct bt_iso_qos qos;
- struct bt_iso_base base;
- char address[18];
+ struct bap_bcast_pa_req *req = user_data;
+ struct bap_setup *setup = req->data.setup;
int fd;
- struct iovec *base_io;
- uint32_t presDelay;
- uint8_t numSubgroups;
- uint8_t numBis;
- struct bt_bap_codec codec;
-
- bt_io_get(io, &err,
- BT_IO_OPT_DEST, address,
- BT_IO_OPT_QOS, &qos,
- BT_IO_OPT_BASE, &base,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- goto drop;
- }
-
- g_io_channel_ref(io);
- btd_service_connecting_complete(data->service, 0);
- DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
- address, qos.bcast.big, qos.bcast.bis);
- update_bcast_qos(&qos, &setup->qos);
+ DBG("BIG Sync completed");
- base_io = new0(struct iovec, 1);
- util_iov_memcpy(base_io, base.base, base.base_len);
+ queue_remove(bcast_pa_requests, req);
- parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
- &presDelay, &numSubgroups, &numBis,
- &codec, &setup->caps, &setup->metadata);
-
- /* Update pac with BASE information */
- bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
- setup->metadata);
- setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
- setup->caps, NULL, NULL);
-
- bt_bap_stream_set_user_data(setup->stream, ep->path);
+ /* This device is no longer needed */
+ btd_service_connecting_complete(setup->ep->data->service, 0);
fd = g_io_channel_unix_get_fd(io);
@@ -1084,26 +983,157 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
g_io_channel_set_close_on_unref(io, FALSE);
return;
}
+}
+static void print_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
+ void *user_data)
+{
+ util_debug(user_data, NULL, "CC #%zu: l:%u t:%u", i, l, t);
+ util_hexdump(' ', v, l, user_data, NULL);
+}
- return;
+static bool parse_base(struct bt_bap *bap, struct bt_iso_base *base,
+ util_debug_func_t func)
+{
+ struct iovec iov = {
+ .iov_base = base->base,
+ .iov_len = base->base_len,
+ };
+ uint32_t pres_delay;
+ uint8_t num_subgroups;
-drop:
- g_io_channel_shutdown(io, TRUE, NULL);
+ util_debug(func, NULL, "BASE len: %ld", iov.iov_len);
+ if (!util_iov_pull_le24(&iov, &pres_delay))
+ return false;
+ util_debug(func, NULL, "PresentationDelay: %d", pres_delay);
+
+ if (!util_iov_pull_u8(&iov, &num_subgroups))
+ return false;
+ util_debug(func, NULL, "Number of Subgroups: %d", num_subgroups);
+
+ /* Loop subgroups */
+ for (int idx = 0; idx < num_subgroups; idx++) {
+ uint8_t num_bis;
+ struct bt_bap_codec codec;
+ struct iovec *l2_caps;
+ struct iovec *meta;
+
+ util_debug(func, NULL, "Subgroup #%d", idx);
+
+ if (!util_iov_pull_u8(&iov, &num_bis))
+ goto fail;
+ util_debug(func, NULL, "Number of BISes: %d", num_bis);
+
+ memcpy(&codec,
+ util_iov_pull_mem(&iov,
+ sizeof(struct bt_bap_codec)),
+ sizeof(struct bt_bap_codec));
+ util_debug(func, NULL, "Codec: ID %d CID 0x%2.2x VID 0x%2.2x",
+ codec.id, codec.cid, codec.vid);
+
+ /* Level 2 */
+ /* Read Codec Specific Configuration */
+ l2_caps = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov, (void *)&l2_caps->iov_len))
+ goto fail;
+
+ util_iov_memcpy(l2_caps, util_iov_pull_mem(&iov,
+ l2_caps->iov_len),
+ l2_caps->iov_len);
+
+ /* Print Codec Specific Configuration */
+ util_debug(func, NULL, "CC len: %ld", l2_caps->iov_len);
+ util_ltv_foreach(l2_caps->iov_base, l2_caps->iov_len, NULL,
+ print_ltv, func);
+
+ /* Read Metadata */
+ meta = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov, (void *)&meta->iov_len))
+ goto fail;
+
+ util_iov_memcpy(meta,
+ util_iov_pull_mem(&iov, meta->iov_len),
+ meta->iov_len);
+
+ /* Print Metadata */
+ util_debug(func, NULL, "Metadata len: %i",
+ (uint8_t)meta->iov_len);
+ util_hexdump(' ', meta->iov_base, meta->iov_len, func, NULL);
+
+ /* Level 3 */
+ for (; num_bis; num_bis--) {
+ uint8_t bis_index;
+ struct iovec *l3_caps;
+
+ if (!util_iov_pull_u8(&iov, &bis_index))
+ goto fail;
+
+ util_debug(func, NULL, "BIS #%d", bis_index);
+
+ /* Read Codec Specific Configuration */
+ l3_caps = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov, (void *)&l3_caps->iov_len))
+ goto fail;
+
+ util_iov_memcpy(l3_caps,
+ util_iov_pull_mem(&iov,
+ l3_caps->iov_len),
+ l3_caps->iov_len);
+
+ /* Print Codec Specific Configuration */
+ util_debug(func, NULL, "CC Len: %d",
+ (uint8_t)l3_caps->iov_len);
+ util_ltv_foreach(l3_caps->iov_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);
+ }
+
+ }
+ return true;
+
+fail:
+ util_debug(func, NULL, "Unable to parse Base");
+
+ return false;
}
static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
{
GError *err = NULL;
+ struct bap_bcast_pa_req *pa_req = user_data;
+ struct bap_data *data = btd_service_get_user_data(pa_req->data.service);
+ struct bt_iso_base base;
+ struct bt_iso_qos qos;
- if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
- user_data, NULL, &err, BT_IO_OPT_INVALID)) {
- error("bt_io_bcast_accept: %s", err->message);
+ DBG("PA Sync done");
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_BASE, &base,
+ BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
g_error_free(err);
g_io_channel_shutdown(io, TRUE, NULL);
+ return;
}
+ /* Close the io and remove the queue request for another PA Sync */
+ 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;
+ queue_remove(bcast_pa_requests, pa_req);
+
+ /* Analyze received BASE data and create remote media endpoints for each
+ * BIS matching our capabilities
+ */
+ parse_base(data->bap, &base, bap_debug);
}
static bool match_data_bap_data(const void *data, const void *match_data)
@@ -1942,47 +1972,58 @@ static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
data->listen_io = io;
}
-static void setup_listen_io_broadcast(struct bap_data *data,
- struct bap_setup *setup,
- struct bt_bap_stream *stream,
- struct bt_iso_qos *qos)
+static void check_pa_req_in_progress(void *data, void *user_data)
{
- GIOChannel *io;
- GError *err = NULL;
- struct sockaddr_iso_bc iso_bc_addr;
+ struct bap_bcast_pa_req *req = data;
- 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_num_bis = 1;
+ if (req->in_progress == TRUE)
+ *((bool *)user_data) = TRUE;
+}
- DBG("stream %p", stream);
+static int short_lived_pa_sync(struct bap_bcast_pa_req *req);
+static void pa_and_big_sync(struct bap_bcast_pa_req *req);
- /* If IO already set skip creating it again */
- if (bt_bap_stream_get_io(stream) || data->listen_io)
- return;
+static gboolean pa_idle_timer(gpointer user_data)
+{
+ struct bap_bcast_pa_req *req = user_data;
+ bool in_progress = FALSE;
- io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR,
- btd_adapter_get_address(data->adapter),
- BT_IO_OPT_DEST_BDADDR,
- device_get_address(data->device),
- BT_IO_OPT_DEST_TYPE,
- btd_device_get_bdaddr_type(data->device),
- BT_IO_OPT_MODE, BT_IO_MODE_ISO,
- BT_IO_OPT_QOS, &qos->bcast,
- BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
- BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
+ /* Handle timer if no request is in progress */
+ queue_foreach(bcast_pa_requests, check_pa_req_in_progress,
+ &in_progress);
+ if (in_progress == FALSE) {
+ req = queue_peek_head(bcast_pa_requests);
+ if (req != NULL)
+ switch (req->type) {
+ case BAP_PA_SHORT_REQ:
+ DBG("do short lived PA Sync");
+ short_lived_pa_sync(req);
+ break;
+ case BAP_PA_BIG_SYNC_REQ:
+ DBG("do PA Sync and BIG Sync");
+ pa_and_big_sync(req);
+ break;
+ }
}
- setup->io = io;
- data->listen_io = io;
+ return TRUE;
}
+
+static void setup_accept_io_broadcast(struct bap_data *data,
+ struct bap_setup *setup)
+{
+ struct bap_bcast_pa_req *pa_req = new0(struct bap_bcast_pa_req, 1);
+
+ /* Add this request to the PA queue.
+ * We don't need to check the queue here and the timer, as we cannot
+ * have BAP_PA_BIG_SYNC_REQ before a short PA (BAP_PA_SHORT_REQ)
+ */
+ pa_req->type = BAP_PA_BIG_SYNC_REQ;
+ pa_req->in_progress = FALSE;
+ pa_req->data.setup = setup;
+ queue_push_tail(bcast_pa_requests, pa_req);
+}
+
static void setup_create_ucast_io(struct bap_data *data,
struct bap_setup *setup,
struct bt_bap_stream *stream,
@@ -2043,7 +2084,7 @@ static void setup_create_bcast_io(struct bap_data *data,
setup_connect_io_broadcast(data, setup, stream, &iso_qos,
defer);
else
- setup_listen_io_broadcast(data, setup, stream, &iso_qos);
+ setup_accept_io_broadcast(data, setup);
}
static void setup_create_io(struct bap_data *data, struct bap_setup *setup,
@@ -2281,19 +2322,12 @@ static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state,
if (!setup || setup->id)
break;
if (bt_bap_stream_io_dir(stream) ==
- BT_BAP_BCAST_SOURCE) {
+ 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);
- }
- } else {
+ else {
/* If the stream attached to a broadcast
* source endpoint generate the base.
*/
@@ -2657,32 +2691,23 @@ static void bap_detached(struct bt_bap *bap, void *user_data)
bap_data_remove(data);
}
-static int bap_bcast_probe(struct btd_service *service)
+static int short_lived_pa_sync(struct bap_bcast_pa_req *req)
{
+ struct btd_service *service = req->data.service;
struct btd_device *device = btd_service_get_device(service);
struct btd_adapter *adapter = device_get_adapter(device);
struct btd_gatt_database *database = btd_adapter_get_database(adapter);
struct bap_data *data = btd_service_get_user_data(service);
- char addr[18];
-
- ba2str(device_get_address(device), addr);
-
- if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) {
- error("BAP requires ISO Socket which is not enabled");
- return -ENOTSUP;
- }
+ GError *err = NULL;
- /* Ignore, if we were probed for this device already */
if (data) {
- error("Profile probed twice for the same device!");
- return -EINVAL;
+ DBG("Already probed");
+ return -1;
}
-
data = bap_data_new(device);
data->service = service;
data->adapter = adapter;
data->device = device;
-
data->bap = bt_bap_new(btd_gatt_database_get_db(database),
btd_gatt_database_get_db(database));
if (!data->bap) {
@@ -2707,7 +2732,146 @@ static int bap_bcast_probe(struct btd_service *service)
bt_bap_set_user_data(data->bap, service);
- bt_bap_new_bcast_source(data->bap, device_get_path(device));
+ DBG("Create PA sync with this source");
+ req->in_progress = TRUE;
+ data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, req,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(data->adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(data->device),
+ BT_IO_OPT_DEST_TYPE,
+ btd_device_get_bdaddr_type(data->device),
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &bap_sink_pa_qos,
+ BT_IO_OPT_INVALID);
+ if (!data->listen_io) {
+ error("%s", err->message);
+ g_error_free(err);
+ }
+
+ return 0;
+}
+
+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 sockaddr_iso_bc iso_bc_addr;
+ struct bt_iso_qos qos;
+
+ DBG("PA Sync done, do BIG Sync");
+ g_io_channel_unref(setup->io);
+ setup->io = 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
+ */
+ 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_num_bis = 1;
+
+ /* Set the user requested QOS */
+ qos.bcast.big = setup->qos.bcast.big;
+ qos.bcast.bis = setup->qos.bcast.bis;
+ qos.bcast.sync_factor = setup->qos.bcast.sync_factor;
+ qos.bcast.packing = setup->qos.bcast.packing;
+ qos.bcast.framing = setup->qos.bcast.framing;
+ qos.bcast.encryption = setup->qos.bcast.encryption;
+ if (setup->qos.bcast.bcode && setup->qos.bcast.bcode->iov_base)
+ memcpy(qos.bcast.bcode, setup->qos.bcast.bcode->iov_base,
+ setup->qos.bcast.bcode->iov_len);
+ qos.bcast.options = setup->qos.bcast.options;
+ qos.bcast.skip = setup->qos.bcast.skip;
+ qos.bcast.sync_timeout = setup->qos.bcast.sync_timeout;
+ qos.bcast.sync_cte_type = setup->qos.bcast.sync_cte_type;
+ qos.bcast.mse = setup->qos.bcast.mse;
+ qos.bcast.timeout = setup->qos.bcast.timeout;
+ memcpy(&qos.bcast.out, &setup->qos.bcast.io_qos,
+ sizeof(struct bt_iso_io_qos));
+
+ if (!bt_io_set(setup->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,
+ iso_bcast_confirm_cb,
+ req, NULL, &err,
+ BT_IO_OPT_ISO_BC_NUM_BIS,
+ iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
+ iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
+ error("bt_io_bcast_accept: %s", err->message);
+ g_error_free(err);
+ }
+}
+
+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;
+
+ req->in_progress = TRUE;
+
+ DBG("Create PA sync with this source");
+ setup->io = bt_io_listen(NULL, iso_do_big_sync, req,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(data->adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(data->device),
+ BT_IO_OPT_DEST_TYPE,
+ btd_device_get_bdaddr_type(data->device),
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &bap_sink_pa_qos,
+ BT_IO_OPT_INVALID);
+ if (!setup->io) {
+ error("%s", err->message);
+ g_error_free(err);
+ }
+}
+
+static int bap_bcast_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct bap_bcast_pa_req *pa_req =
+ new0(struct bap_bcast_pa_req, 1);
+
+ if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) {
+ error("BAP requires ISO Socket which is not enabled");
+ return -ENOTSUP;
+ }
+
+ /* First time initialize the queue and start the idle timer */
+ if (bcast_pa_requests == NULL) {
+ bcast_pa_requests = queue_new();
+ pa_timer_id = g_timeout_add_seconds(PA_IDLE_TIMEOUT,
+ pa_idle_timer, NULL);
+ }
+
+ /* Enqueue this device advertisement so that we can do short-lived
+ */
+ DBG("enqueue service: %p", service);
+ pa_req->type = BAP_PA_SHORT_REQ;
+ pa_req->in_progress = FALSE;
+ pa_req->data.service = service;
+ queue_push_tail(bcast_pa_requests, pa_req);
+
return 0;
}