@@ -75,6 +75,7 @@ struct bap_ep {
struct bap_data {
struct btd_device *device;
+ struct btd_adapter *adapter;
struct btd_service *service;
struct bt_bap *bap;
unsigned int ready_id;
@@ -82,13 +83,25 @@ struct bap_data {
unsigned int pac_id;
struct queue *srcs;
struct queue *snks;
+ struct queue *bcast;
struct queue *streams;
GIOChannel *listen_io;
int selecting;
+ void *user_data;
};
static struct queue *sessions;
+static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
+{
+ if (!data)
+ return false;
+
+ data->user_data = user_data;
+
+ return true;
+}
+
static void bap_debug(const char *str, void *user_data)
{
DBG_IDX(0xffff, "%s", str);
@@ -167,8 +180,10 @@ static gboolean get_uuid(const GDBusPropertyTable *property,
if (queue_find(ep->data->snks, NULL, ep))
uuid = PAC_SINK_UUID;
- else
+ if (queue_find(ep->data->srcs, NULL, ep))
uuid = PAC_SOURCE_UUID;
+ else
+ uuid = BAA_SERVICE_UUID;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
@@ -214,7 +229,10 @@ static gboolean get_device(const GDBusPropertyTable *property,
struct bap_ep *ep = data;
const char *path;
- path = device_get_path(ep->data->device);
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
+ path = adapter_get_path(ep->data->adapter);
+ else
+ path = device_get_path(ep->data->device);
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
@@ -254,7 +272,10 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
{
const char *key;
struct bt_bap_io_qos io_qos;
+ uint8_t framing = 0;
+ bool broadcast = false;
+ memset(&io_qos, 0, sizeof(io_qos));
while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter value, entry;
int var;
@@ -284,11 +305,21 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
goto fail;
dbus_message_iter_get_basic(&value, &qos->ucast.cig_id);
+ } else if (!strcasecmp(key, "BIG")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &qos->bcast.big);
} else if (!strcasecmp(key, "CIS")) {
if (var != DBUS_TYPE_BYTE)
goto fail;
dbus_message_iter_get_basic(&value, &qos->ucast.cis_id);
+ } else if (!strcasecmp(key, "BIS")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &qos->bcast.bis);
} else if (!strcasecmp(key, "Interval")) {
if (var != DBUS_TYPE_UINT32)
goto fail;
@@ -302,7 +333,7 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
dbus_message_iter_get_basic(&value, &val);
- qos->ucast.framing = val;
+ framing = val;
} else if (!strcasecmp(key, "PHY")) {
const char *str;
@@ -342,13 +373,78 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
goto fail;
dbus_message_iter_get_basic(&value,
- &qos->ucast.target_latency);
+ &qos->ucast.target_latency);
+ } else if (!strcasecmp(key, "Encryption")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.encryption);
+ broadcast = true;
+ } else if (!strcasecmp(key, "Options")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.options);
+ } else if (!strcasecmp(key, "Skip")) {
+ if (var != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.skip);
+ } else if (!strcasecmp(key, "SyncTimeout")) {
+ if (var != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.sync_timeout);
+ } else if (!strcasecmp(key, "SyncCteType")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.sync_cte_type);
+
+ } else if (!strcasecmp(key, "SyncInterval")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.sync_interval);
+ } else if (!strcasecmp(key, "MSE")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.mse);
+ } else if (!strcasecmp(key, "Timeout")) {
+ if (var != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &qos->bcast.timeout);
+ } else if (!strcasecmp(key, "BroadcastCode")) {
+ struct iovec *iov;
+
+ if (var != DBUS_TYPE_ARRAY)
+ goto fail;
+ iov = &qos->bcast.bcode;
+ parse_array(&value, &iov);
}
dbus_message_iter_next(props);
}
- memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
+ if (broadcast) {
+ memcpy(&qos->bcast.io_qos, &io_qos, sizeof(io_qos));
+ qos->bcast.framing = framing;
+
+ } else {
+ memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
+ qos->ucast.framing = framing;
+ }
+
return 0;
fail:
@@ -457,9 +553,15 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
bt_bap_stream_io_connecting(ep->stream, -1);
}
- /* Mark CIG and CIS to be auto assigned */
- ep->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET;
- ep->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET;
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
+ /* Mark CIG and CIS to be auto assigned */
+ ep->qos.bcast.big = BT_ISO_QOS_BIG_UNSET;
+ ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
+ } else {
+ /* Mark CIG and CIS to be auto assigned */
+ ep->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET;
+ ep->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET;
+ }
if (parse_properties(&props, &ep->caps, &ep->metadata, &ep->qos) < 0) {
DBG("Unable to parse properties");
@@ -482,6 +584,10 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return btd_error_invalid_args(msg);
}
+ if (bt_bap_stream_get_type(ep->stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST)
+ ep->id = 0; // No message sent over the air for broadcast
+
bt_bap_stream_set_user_data(ep->stream, ep->path);
ep->msg = dbus_message_ref(msg);
@@ -510,6 +616,8 @@ static void ep_free(void *data)
util_iov_free(ep->caps, 1);
util_iov_free(ep->metadata, 1);
+ if (bt_bap_stream_get_type(ep->stream) == BT_BAP_STREAM_TYPE_BROADCAST)
+ util_iov_free(&ep->qos.bcast.bcode, 1);
free(ep->path);
free(ep);
}
@@ -530,6 +638,63 @@ static bool match_ep(const void *data, const void *user_data)
return ep->rpac == match->rpac;
}
+static struct bap_ep *ep_register_bcast(struct bap_data *data,
+ struct bt_bap_pac *lpac,
+ struct bt_bap_pac *rpac)
+{
+ struct btd_adapter *adapter = data->user_data;
+ struct bap_ep *ep;
+ struct queue *queue;
+ int i, err;
+ const char *suffix;
+ struct match_ep match = { lpac, rpac };
+
+ switch (bt_bap_pac_get_type(rpac)) {
+ case BT_BAP_BCAST_SOURCE:
+ queue = data->bcast;
+ i = queue_length(data->bcast);
+ suffix = "bcast";
+ break;
+ default:
+ return NULL;
+ }
+
+ ep = queue_find(queue, match_ep, &match);
+ if (ep)
+ return ep;
+
+ ep = new0(struct bap_ep, 1);
+ ep->data = data;
+ ep->lpac = lpac;
+ ep->rpac = rpac;
+
+ err = asprintf(&ep->path, "%s/pac_%s%d", adapter_get_path(adapter),
+ suffix, i);
+ if (err < 0) {
+ error("Could not allocate path for remote pac %s/pac%d",
+ adapter_get_path(adapter), i);
+ free(ep);
+ return NULL;
+ }
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ ep->path, MEDIA_ENDPOINT_INTERFACE,
+ ep_methods, NULL, ep_properties,
+ ep, ep_free) == FALSE) {
+ error("Could not register remote ep %s", ep->path);
+ ep_free(ep);
+ return NULL;
+ }
+
+ bt_bap_pac_set_user_data(rpac, ep->path);
+
+ DBG("ep %p lpac %p rpac %p path %s", ep, ep->lpac, ep->rpac, ep->path);
+
+ queue_push_tail(queue, ep);
+
+ return ep;
+}
+
static struct bap_ep *ep_register(struct btd_service *service,
struct bt_bap_pac *lpac,
struct bt_bap_pac *rpac)
@@ -677,6 +842,23 @@ static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
return true;
}
+static bool pac_found_bcast(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
+ void *user_data)
+{
+ struct bap_ep *ep;
+
+ DBG("lpac %p rpac %p", lpac, rpac);
+
+ ep = ep_register_bcast(user_data, lpac, rpac);
+ if (!ep) {
+ error("Unable to register endpoint for pac %p", rpac);
+ return true;
+ }
+
+ return true;
+}
+
+
static void bap_ready(struct bt_bap *bap, void *user_data)
{
struct btd_service *service = user_data;
@@ -700,11 +882,18 @@ static struct bap_ep *bap_find_ep_by_stream(struct bap_data *data,
{
struct bap_ep *ep;
- ep = queue_find(data->snks, match_ep_by_stream, stream);
- if (ep)
- return ep;
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UNICAST) {
+ ep = queue_find(data->snks, match_ep_by_stream, stream);
+ if (ep)
+ return ep;
- return queue_find(data->srcs, match_ep_by_stream, stream);
+ return queue_find(data->srcs, match_ep_by_stream, stream);
+ } else if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+
+ return queue_find(data->bcast, match_ep_by_stream, stream);
+ } else
+ return NULL;
}
static void iso_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
@@ -943,6 +1132,62 @@ static void bap_connect_io(struct bap_data *data, struct bap_ep *ep,
bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io));
}
+static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep,
+ struct bt_bap_stream *stream,
+ struct bt_iso_qos *qos)
+{
+ struct btd_adapter *adapter = data->user_data;
+ GIOChannel *io = NULL;
+ GError *err = NULL;
+ bdaddr_t dst_addr = {0};
+ char addr[18];
+ struct bt_iso_base base;
+
+ /* If IO already set and we are in the creation step,
+ * skip creating it again
+ */
+ if (bt_bap_stream_get_io(stream))
+ return;
+
+ if (ep->io_id) {
+ g_source_remove(ep->io_id);
+ ep->io_id = 0;
+ }
+ base.base_len = ep->caps->iov_len;
+
+ memset(base.base, 0, 248);
+ memcpy(base.base, ep->caps->iov_base, base.base_len);
+ DBG("ep %p stream %p ", ep, stream);
+ ba2str(btd_adapter_get_address(adapter), addr);
+
+ io = bt_io_connect(bap_connect_io_cb, ep, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ &dst_addr,
+ BT_IO_OPT_DEST_TYPE,
+ BDADDR_LE_PUBLIC,
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, qos,
+ BT_IO_OPT_BASE, &base,
+ BT_IO_OPT_DEFER_TIMEOUT, false,
+ BT_IO_OPT_INVALID);
+
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ ep->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ bap_io_disconnected, ep);
+
+ ep->io = io;
+
+ bt_bap_stream_io_connecting(stream,
+ g_io_channel_unix_get_fd(io));
+}
+
static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
struct bt_iso_qos *qos)
{
@@ -989,22 +1234,53 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
if (!queue_find(data->streams, NULL, stream))
queue_push_tail(data->streams, stream);
- if (!bt_bap_stream_io_get_qos(stream, &qos[0], &qos[1])) {
- error("bt_bap_stream_get_qos_links: failed");
- return;
- }
-
memset(&iso_qos, 0, sizeof(iso_qos));
- iso_qos.ucast.cig = qos[0] ? qos[0]->ucast.cig_id :
+
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UNICAST) {
+ if (!bt_bap_stream_io_get_qos(stream, &qos[0], &qos[1])) {
+ error("bt_bap_stream_get_qos_links: failed");
+ return;
+ }
+
+ iso_qos.ucast.cig = qos[0] ? qos[0]->ucast.cig_id :
qos[1]->ucast.cig_id;
- iso_qos.ucast.cis = qos[0] ? qos[0]->ucast.cis_id :
+ iso_qos.ucast.cis = qos[0] ? qos[0]->ucast.cis_id :
qos[1]->ucast.cis_id;
- bap_iso_qos(qos[0], &iso_qos.ucast.in);
- bap_iso_qos(qos[1], &iso_qos.ucast.out);
+ bap_iso_qos(qos[0], &iso_qos.ucast.in);
+ bap_iso_qos(qos[1], &iso_qos.ucast.out);
+ } else if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ if (defer == true) {
+ iso_qos.bcast.big = ep->qos.bcast.big;
+ iso_qos.bcast.bis = ep->qos.bcast.bis;
+ iso_qos.bcast.sync_interval =
+ ep->qos.bcast.sync_interval;
+ iso_qos.bcast.packing = ep->qos.bcast.packing;
+ iso_qos.bcast.framing = ep->qos.bcast.framing;
+ iso_qos.bcast.encryption = ep->qos.bcast.encryption;
+ memcpy(iso_qos.bcast.bcode,
+ ep->qos.bcast.bcode.iov_base, 16);
+ iso_qos.bcast.options = ep->qos.bcast.options;
+ iso_qos.bcast.skip = ep->qos.bcast.skip;
+ iso_qos.bcast.sync_timeout = ep->qos.bcast.sync_timeout;
+ iso_qos.bcast.sync_cte_type =
+ ep->qos.bcast.sync_cte_type;
+ iso_qos.bcast.mse = ep->qos.bcast.mse;
+ iso_qos.bcast.timeout = ep->qos.bcast.timeout;
+ memcpy(&iso_qos.bcast.out, &ep->qos.bcast.io_qos,
+ sizeof(struct bt_iso_io_qos));
+ }
+ } else
+ return;
if (ep)
- bap_connect_io(data, ep, stream, &iso_qos, defer);
+ if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST)
+ bap_connect_io_broadcast(data, ep, stream,
+ &iso_qos);
+ else
+ bap_connect_io(data, ep, stream, &iso_qos, defer);
else
bap_listen_io(data, stream, &iso_qos);
}
@@ -1043,12 +1319,16 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
}
- /* Wait QoS response to respond */
- ep->id = bt_bap_stream_qos(stream, &ep->qos, qos_cb,
- ep);
- if (!ep->id) {
- error("Failed to Configure QoS");
- bt_bap_stream_release(stream, NULL, NULL);
+ if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_UNICAST) {
+ /* Wait QoS response to respond */
+ ep->id = bt_bap_stream_qos(stream, &ep->qos,
+ qos_cb, ep);
+ if (!ep->id) {
+ error("Failed to Configure QoS");
+ bt_bap_stream_release(stream,
+ NULL, NULL);
+ }
}
}
break;
@@ -1059,6 +1339,13 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
if (ep)
bap_create_io(data, ep, stream, false);
break;
+ case BT_BAP_STREAM_STATE_STREAMING:
+ if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ if (ep)
+ bap_create_io(data, ep, stream, false);
+ }
+ break;
}
}
@@ -1078,6 +1365,18 @@ static void pac_added(struct bt_bap_pac *pac, void *user_data)
bt_bap_foreach_pac(data->bap, BT_BAP_SINK, pac_found, service);
}
+static void pac_added_broadcast(struct bt_bap_pac *pac, void *user_data)
+{
+ struct bap_data *data = user_data;
+
+ if (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SOURCE) {
+ DBG("pac %p", pac);
+
+ bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SOURCE,
+ pac_found_bcast, data);
+ }
+}
+
static bool ep_match_pac(const void *data, const void *match_data)
{
const struct bap_ep *ep = data;
@@ -1118,6 +1417,38 @@ static void pac_removed(struct bt_bap_pac *pac, void *user_data)
ep_unregister(ep);
}
+static void pac_removed_broadcast(struct bt_bap_pac *pac, void *user_data)
+{
+ struct btd_service *service = user_data;
+ struct bap_data *data;
+ struct queue *queue;
+ struct bap_ep *ep;
+
+ DBG("pac %p", pac);
+
+ data = btd_service_get_user_data(service);
+
+ switch (bt_bap_pac_get_type(pac)) {
+ case BT_BAP_SINK:
+ queue = data->srcs;
+ break;
+ case BT_BAP_SOURCE:
+ queue = data->snks;
+ break;
+ case BT_BAP_BCAST_SOURCE:
+ queue = data->bcast;
+ break;
+ default:
+ return;
+ }
+
+ ep = queue_remove_if(queue, ep_match_pac, pac);
+ if (!ep)
+ return;
+
+ ep_unregister(ep);
+}
+
static struct bap_data *bap_data_new(struct btd_device *device)
{
struct bap_data *data;
@@ -1126,6 +1457,7 @@ static struct bap_data *bap_data_new(struct btd_device *device)
data->device = device;
data->srcs = queue_new();
data->snks = queue_new();
+ data->bcast = queue_new();
return data;
}
@@ -1158,6 +1490,14 @@ static bool match_data(const void *data, const void *match_data)
return bdata->bap == bap;
}
+static bool match_data_bap_data(const void *data, const void *match_data)
+{
+ const struct bap_data *bdata = data;
+ const struct btd_adapter *adapter = match_data;
+
+ return bdata->user_data == adapter;
+}
+
static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
void *user_data)
{
@@ -1182,26 +1522,50 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
g_io_channel_set_close_on_unref(io, FALSE);
- /* Attempt to get CIG/CIS if they have not been set */
- if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET ||
+ if (bt_bap_stream_get_type(ep->stream) == BT_BAP_STREAM_TYPE_UNICAST) {
+ /* Attempt to get CIG/CIS if they have not been set */
+ if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET ||
ep->qos.ucast.cis_id == BT_ISO_QOS_CIS_UNSET) {
- struct bt_iso_qos qos;
- GError *err = NULL;
-
- if (!bt_io_get(io, &err, BT_IO_OPT_QOS, &qos,
- BT_IO_OPT_INVALID)) {
- error("%s", err->message);
- g_error_free(err);
- g_io_channel_unref(io);
- return;
- }
+ struct bt_iso_qos qos;
+ GError *err = NULL;
+
+ if (!bt_io_get(io, &err, BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_INVALID)) {
+ error("%s", err->message);
+ g_error_free(err);
+ g_io_channel_unref(io);
+ return;
+ }
- ep->qos.ucast.cig_id = qos.ucast.cig;
- ep->qos.ucast.cis_id = qos.ucast.cis;
- }
+ ep->qos.ucast.cig_id = qos.ucast.cig;
+ ep->qos.ucast.cis_id = qos.ucast.cis;
+ }
- DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd,
+ DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd,
ep->qos.ucast.cig_id, ep->qos.ucast.cis_id);
+ } else if (bt_bap_stream_get_type(ep->stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ /* Attempt to get BIG/BIS if they have not been set */
+ if (ep->qos.bcast.big == BT_ISO_QOS_BIG_UNSET ||
+ ep->qos.bcast.bis == BT_ISO_QOS_BIS_UNSET) {
+ struct bt_iso_qos qos;
+ GError *err = NULL;
+
+ if (!bt_io_get(io, &err, BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_INVALID)) {
+ error("%s", err->message);
+ g_error_free(err);
+ g_io_channel_unref(io);
+ return;
+ }
+
+ ep->qos.bcast.big = qos.bcast.big;
+ ep->qos.bcast.bis = qos.bcast.bis;
+ }
+
+ DBG("stream %p fd %d: BIG 0x%02x BIS 0x%02x", stream, fd,
+ ep->qos.bcast.big, ep->qos.bcast.bis);
+ }
}
static void bap_attached(struct bt_bap *bap, void *user_data)
@@ -1349,6 +1713,67 @@ static int bap_disconnect(struct btd_service *service)
return 0;
}
+static int bap_adapter_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+ struct bap_data *data;
+ char addr[18];
+
+ ba2str(btd_adapter_get_address(adapter), addr);
+ DBG("%s", addr);
+
+ if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) {
+ error("BAP requires ISO Socket which is not enabled");
+ return -ENOTSUP;
+ }
+
+ data = bap_data_new(NULL);
+ data->adapter = adapter;
+
+ data->bap = bt_bap_new(btd_gatt_database_get_db(database),
+ btd_gatt_database_get_db(database));
+ if (!data->bap) {
+ error("Unable to create BAP instance");
+ free(data);
+ return -EINVAL;
+ }
+
+ bap_data_add(data);
+
+ if (!bt_bap_attach_broadcast(data->bap)) {
+ error("BAP unable to attach");
+ return -EINVAL;
+ }
+
+ data->state_id = bt_bap_state_register(data->bap, bap_state,
+ bap_connecting, data, NULL);
+ data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast,
+ pac_removed_broadcast, data, NULL);
+
+ bt_bap_set_user_data(data->bap, adapter);
+ bap_data_set_user_data(data, adapter);
+ return 0;
+}
+
+static void bap_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct bap_data *data = queue_find(sessions, match_data_bap_data,
+ adapter);
+ char addr[18];
+
+ ba2str(btd_adapter_get_address(adapter), addr);
+ DBG("%s", addr);
+
+ if (!data) {
+ error("BAP service not handled by profile");
+ return;
+ }
+
+ bap_data_remove(data);
+}
+
static struct btd_profile bap_profile = {
.name = "bap",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
@@ -1357,6 +1782,8 @@ static struct btd_profile bap_profile = {
.device_remove = bap_remove,
.accept = bap_accept,
.disconnect = bap_disconnect,
+ .adapter_probe = bap_adapter_probe,
+ .adapter_remove = bap_adapter_remove,
.auto_connect = true,
.experimental = true,
};
@@ -1041,28 +1041,50 @@ static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,
transport = find_transport(endpoint, stream);
if (!transport) {
struct bt_bap *bap = bt_bap_stream_get_session(stream);
- struct btd_service *service = bt_bap_get_user_data(bap);
- struct btd_device *device;
+ if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_UNICAST) {
+ struct btd_service *service = bt_bap_get_user_data(bap);
+ struct btd_device *device;
+
+ if (service)
+ device = btd_service_get_device(service);
+ else {
+ struct bt_att *att = bt_bap_get_att(bap);
+ int fd = bt_att_get_fd(att);
+
+ device = btd_adapter_find_device_by_fd(fd);
+ }
+
+ if (!device) {
+ error("Unable to find device");
+ return -EINVAL;
+ }
- if (service)
- device = btd_service_get_device(service);
- else {
- struct bt_att *att = bt_bap_get_att(bap);
- int fd = bt_att_get_fd(att);
+ path = bt_bap_stream_get_user_data(stream);
- device = btd_adapter_find_device_by_fd(fd);
- }
+ transport = media_transport_create(device, path,
+ cfg->iov_base,
+ cfg->iov_len,
+ endpoint,
+ stream);
+ } else if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ struct btd_adapter *adapter = bt_bap_get_user_data(bap);
- if (!device) {
- error("Unable to find device");
- return -EINVAL;
- }
+ if (!adapter) {
+ error("Unable to find adapter");
+ return -EINVAL;
+ }
+
+ path = bt_bap_stream_get_user_data(stream);
- path = bt_bap_stream_get_user_data(stream);
+ transport = media_transport_create(NULL, path,
+ cfg->iov_base,
+ cfg->iov_len,
+ endpoint,
+ stream);
+ }
- transport = media_transport_create(device, path, cfg->iov_base,
- cfg->iov_len, endpoint,
- stream);
if (!transport)
return -EINVAL;
@@ -1198,6 +1220,12 @@ static bool endpoint_init_pac_source(struct media_endpoint *endpoint, int *err)
return endpoint_init_pac(endpoint, BT_BAP_SOURCE, err);
}
+static bool endpoint_init_broadcast_source(struct media_endpoint *endpoint,
+ int *err)
+{
+ return endpoint_init_pac(endpoint, BT_BAP_BCAST_SOURCE, err);
+}
+
static bool endpoint_properties_exists(const char *uuid,
struct btd_device *dev,
void *user_data)
@@ -1300,6 +1328,18 @@ static bool experimental_endpoint_supported(struct btd_adapter *adapter)
return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
}
+static bool experimental_broadcaster_ep_supported(struct btd_adapter *adapter)
+{
+
+ if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET))
+ return false;
+
+ if (!btd_adapter_has_settings(adapter, MGMT_SETTING_ISO_BROADCASTER))
+ return false;
+
+ return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
+}
+
static struct media_endpoint_init {
const char *uuid;
bool (*func)(struct media_endpoint *endpoint, int *err);
@@ -1313,6 +1353,8 @@ static struct media_endpoint_init {
experimental_endpoint_supported },
{ PAC_SOURCE_UUID, endpoint_init_pac_source,
experimental_endpoint_supported },
+ { BAA_SERVICE_UUID, endpoint_init_broadcast_source,
+ experimental_broadcaster_ep_supported },
};
static struct media_endpoint *
@@ -3185,3 +3227,9 @@ uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
{
return endpoint->codec;
}
+
+struct btd_adapter *media_endpoint_get_btd_adapter(
+ struct media_endpoint *endpoint)
+{
+ return endpoint->adapter->btd_adapter;
+}
@@ -20,5 +20,7 @@ void media_unregister(struct btd_adapter *btd_adapter);
struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct btd_adapter *media_endpoint_get_btd_adapter(
+ struct media_endpoint *endpoint);
int8_t media_player_get_device_volume(struct btd_device *device);
@@ -91,6 +91,7 @@ struct bap_transport {
struct media_transport {
char *path; /* Transport object path */
struct btd_device *device; /* Transport device */
+ struct btd_adapter *adapter; /* Transport adapter bcast*/
const char *remote_endpoint; /* Transport remote SEP */
struct media_endpoint *endpoint; /* Transport endpoint */
struct media_owner *owner; /* Transport owner */
@@ -526,6 +527,13 @@ static void media_owner_add(struct media_owner *owner,
owner->pending = req;
}
+static void *get_stream_bap(struct media_transport *transport)
+{
+ struct bap_transport *bap = transport->data;
+
+ return bap->stream;
+}
+
static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -541,15 +549,24 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
return btd_error_not_authorized(msg);
owner = media_owner_create(msg);
+ if (bt_bap_stream_get_type(get_stream_bap(transport)) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ req = media_request_create(msg, 0x00);
+ media_owner_add(owner, req);
+ media_transport_set_owner(transport, owner);
+ }
id = transport->resume(transport, owner);
if (id == 0) {
media_owner_free(owner);
return btd_error_not_authorized(msg);
}
- req = media_request_create(msg, id);
- media_owner_add(owner, req);
- media_transport_set_owner(transport, owner);
+ if (bt_bap_stream_get_type(get_stream_bap(transport)) ==
+ BT_BAP_STREAM_TYPE_UNICAST) {
+ req = media_request_create(msg, id);
+ media_owner_add(owner, req);
+ media_transport_set_owner(transport, owner);
+ }
return NULL;
}
@@ -628,7 +645,12 @@ static gboolean get_device(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct media_transport *transport = data;
- const char *path = device_get_path(transport->device);
+ const char *path;
+
+ if (transport->device)
+ path = device_get_path(transport->device);
+ else
+ path = adapter_get_path(transport->adapter);
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
@@ -1483,13 +1505,6 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
bap_update_links(transport);
}
-static void *get_stream_bap(struct media_transport *transport)
-{
- struct bap_transport *bap = transport->data;
-
- return bap->stream;
-}
-
static void free_bap(void *data)
{
struct bap_transport *bap = data;
@@ -1539,15 +1554,26 @@ struct media_transport *media_transport_create(struct btd_device *device,
const GDBusPropertyTable *properties;
transport = g_new0(struct media_transport, 1);
- transport->device = device;
+ if (device)
+ transport->device = device;
+ else
+ transport->adapter = media_endpoint_get_btd_adapter(endpoint);
transport->endpoint = endpoint;
transport->configuration = g_new(uint8_t, size);
memcpy(transport->configuration, configuration, size);
transport->size = size;
transport->remote_endpoint = remote_endpoint;
- transport->path = g_strdup_printf("%s/fd%d",
- remote_endpoint ? remote_endpoint :
- device_get_path(device), fd++);
+ if (device)
+ transport->path = g_strdup_printf("%s/fd%d",
+ remote_endpoint ? remote_endpoint :
+ device_get_path(device), fd++);
+ else
+ transport->path = g_strdup_printf("%s/fd%d",
+ remote_endpoint ? remote_endpoint :
+ adapter_get_path(
+ media_endpoint_get_btd_adapter(
+ endpoint)),
+ fd++);
transport->fd = -1;
uuid = media_endpoint_get_uuid(endpoint);
@@ -1560,7 +1586,8 @@ struct media_transport *media_transport_create(struct btd_device *device,
goto fail;
properties = a2dp_properties;
} else if (!strcasecmp(uuid, PAC_SINK_UUID) ||
- !strcasecmp(uuid, PAC_SOURCE_UUID)) {
+ !strcasecmp(uuid, PAC_SOURCE_UUID) ||
+ !strcasecmp(uuid, BAA_SERVICE_UUID)) {
if (media_transport_init_bap(transport, stream) < 0)
goto fail;
properties = bap_properties;