@@ -633,14 +633,18 @@ static struct bt_bap_endpoint *bap_endpoint_new(struct bt_bap_db *bdb,
return ep;
}
-static struct bt_bap_endpoint *bap_endpoint_new_broacast(struct bt_bap_db *bdb)
+static struct bt_bap_endpoint *bap_endpoint_new_broadcast(struct bt_bap_db *bdb,
+ uint8_t type)
{
struct bt_bap_endpoint *ep;
ep = new0(struct bt_bap_endpoint, 1);
ep->bdb = bdb;
ep->attr = NULL;
- ep->dir = BT_BAP_BCAST_SOURCE;
+ if (type == BT_BAP_BCAST_SINK)
+ ep->dir = BT_BAP_BCAST_SOURCE;
+ else
+ ep->dir = BT_BAP_BCAST_SINK;
return ep;
}
@@ -667,22 +671,27 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints,
return ep;
}
+static bool match_ep_type(const void *data, const void *match_data)
+{
+ const struct bt_bap_endpoint *ep = data;
+ const uint8_t type = PTR_TO_INT(match_data);
+
+ return (ep->dir == type);
+}
+
static struct bt_bap_endpoint *bap_get_endpoint_bcast(struct queue *endpoints,
- struct bt_bap_db *db)
+ struct bt_bap_db *db, uint8_t type)
{
struct bt_bap_endpoint *ep;
if (!db)
return NULL;
- /*
- * We have support for only one stream so we will have
- * only one endpoint.
- * TO DO add support for more then one stream
- */
- if (queue_length(endpoints) > 0)
- return queue_peek_head(endpoints);
- ep = bap_endpoint_new_broacast(db);
+ ep = queue_find(endpoints, match_ep_type, INT_TO_PTR(type));
+ if (ep)
+ return ep;
+
+ ep = bap_endpoint_new_broadcast(db, type);
if (!ep)
return NULL;
@@ -1317,6 +1326,8 @@ static void stream_set_state_broadcast(struct bt_bap_stream *stream,
struct bt_bap *bap = stream->bap;
const struct queue_entry *entry;
+ if (ep->old_state == state)
+ return;
ep->old_state = ep->state;
ep->state = state;
@@ -1348,6 +1359,9 @@ static void stream_set_state(struct bt_bap_stream *stream, uint8_t state)
ep->old_state = ep->state;
ep->state = state;
+ if (stream->lpac->type == BT_BAP_BCAST_SINK)
+ goto done;
+
if (stream->client)
goto done;
@@ -2379,6 +2393,10 @@ static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type,
return queue_find(bdb->sources, match_codec, codec);
case BT_BAP_SINK:
return queue_find(bdb->sinks, match_codec, codec);
+ case BT_BAP_BCAST_SOURCE:
+ return queue_find(bdb->broadcast_sources, match_codec, codec);
+ case BT_BAP_BCAST_SINK:
+ return queue_find(bdb->broadcast_sinks, match_codec, codec);
}
return NULL;
@@ -2428,10 +2446,12 @@ static struct bt_bap_pac *bap_pac_new(struct bt_bap_db *bdb, const char *name,
pac->bdb = bdb;
pac->name = name ? strdup(name) : NULL;
pac->type = type;
- pac->codec = *codec;
- pac->data = util_iov_dup(data, 1);
- pac->metadata = util_iov_dup(metadata, 1);
-
+ if (codec)
+ pac->codec = *codec;
+ if (data)
+ pac->data = util_iov_dup(data, 1);
+ if (metadata)
+ pac->metadata = util_iov_dup(metadata, 1);
if (qos)
pac->qos = *qos;
@@ -2518,7 +2538,7 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
struct iovec *metadata)
{
struct bt_bap_db *bdb;
- struct bt_bap_pac *pac, *pac_brodcast_sink;
+ struct bt_bap_pac *pac, *pac_broadcast_sink;
struct bt_bap_codec codec;
if (!db)
@@ -2545,11 +2565,19 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
bap_add_source(pac);
break;
case BT_BAP_BCAST_SOURCE:
- // For broadcast add local pac and remote pac
bap_add_broadcast_source(pac);
- pac_brodcast_sink = bap_pac_new(bdb, name, type, &codec, qos,
+ if (queue_isempty(bdb->broadcast_sinks)) {
+ /* When adding a local broadcast source, add also a
+ * local broadcast sink
+ */
+ pac_broadcast_sink = bap_pac_new(bdb, name,
+ BT_BAP_BCAST_SINK, &codec, qos,
data, metadata);
- bap_add_broadcast_sink(pac_brodcast_sink);
+ bap_add_broadcast_sink(pac_broadcast_sink);
+ }
+ break;
+ case BT_BAP_BCAST_SINK:
+ bap_add_broadcast_sink(pac);
break;
default:
bap_pac_free(pac);
@@ -2923,6 +2951,98 @@ bool bap_print_cc(void *data, size_t len, util_debug_func_t func,
return bap_print_ltv("CC", data, len, func, user_data);
}
+bool bap_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 type, capsLen, metaLen;
+ uint16_t uuid;
+ uint8_t *hexstream;
+
+ util_debug(func, NULL, "BASE Length %ld", len);
+ if (!util_iov_pull_u8(&iov, &type))
+ return false;
+ util_debug(func, NULL, "ServiceType 0x%2.2x", type);
+ if (!util_iov_pull_le16(&iov, &uuid))
+ return false;
+ util_debug(func, NULL, "ServiceUUID 0x%4.4x", uuid);
+ if (!util_iov_pull_le24(&iov, presDelay))
+ return false;
+ util_debug(func, NULL, "PresentationDelay %d", *presDelay);
+ if (!util_iov_pull_u8(&iov, numSubgroups))
+ return false;
+ util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
+ if (!util_iov_pull_u8(&iov, numBis))
+ return false;
+ util_debug(func, NULL, "NumBis %d", *numBis);
+ 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;
+ if (!caps)
+ return false;
+ if (!(*caps))
+ *caps = new0(struct iovec, 1);
+ (*caps)->iov_len = capsLen;
+ (*caps)->iov_base = iov.iov_base;
+
+ for (int i = 0; capsLen > 1; i++) {
+ struct bt_ltv *ltv = util_iov_pull_mem(&iov, 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(&iov, 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;
+ if (!meta)
+ return false;
+ if (!(*meta))
+ *meta = new0(struct iovec, 1);
+ (*meta)->iov_len = metaLen;
+ (*meta)->iov_base = iov.iov_base;
+
+ hexstream = util_iov_pull_mem(&iov, metaLen);
+ if (!hexstream)
+ return false;
+ util_hexdump(' ', hexstream, metaLen, func, NULL);
+
+ return true;
+}
+
static void bap_parse_pacs(struct bt_bap *bap, uint8_t type,
struct queue *queue,
const uint8_t *value,
@@ -4008,7 +4128,8 @@ bool bt_bap_attach_broadcast(struct bt_bap *bap)
queue_push_tail(sessions, bap);
- ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb);
+ ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb,
+ BT_BAP_BCAST_SOURCE);
if (ep)
ep->bap = bap;
@@ -4198,7 +4319,11 @@ static void bap_foreach_pac(struct queue *l, struct queue *r,
for (er = queue_get_entries(r); er; er = er->next) {
struct bt_bap_pac *rpac = er->data;
- if (!bap_codec_equal(&lpac->codec, &rpac->codec))
+ /* Skip checking codec for bcast source,
+ * it will be checked when BASE info are received
+ */
+ if ((rpac->type != BT_BAP_BCAST_SOURCE) &&
+ (!bap_codec_equal(&lpac->codec, &rpac->codec)))
continue;
if (!func(lpac, rpac, user_data))
@@ -4221,9 +4346,19 @@ void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type,
return bap_foreach_pac(bap->ldb->sinks, bap->rdb->sources,
func, user_data);
case BT_BAP_BCAST_SOURCE:
- return bap_foreach_pac(bap->ldb->broadcast_sources,
+ if (queue_isempty(bap->rdb->broadcast_sources)
+ && queue_isempty(bap->rdb->broadcast_sinks))
+ return bap_foreach_pac(bap->ldb->broadcast_sources,
bap->ldb->broadcast_sinks,
func, user_data);
+
+ return bap_foreach_pac(bap->ldb->broadcast_sinks,
+ bap->rdb->broadcast_sources,
+ func, user_data);
+ case BT_BAP_BCAST_SINK:
+ return bap_foreach_pac(bap->ldb->broadcast_sinks,
+ bap->rdb->broadcast_sources,
+ func, user_data);
}
}
@@ -4243,10 +4378,10 @@ int bt_bap_pac_get_vendor_codec(struct bt_bap_pac *pac, uint8_t *id,
if (vid)
*vid = pac->codec.cid;
- if (data)
+ if (data && pac->data)
*data = pac->data;
- if (metadata)
+ if (metadata && pac->metadata)
*metadata = pac->metadata;
return 0;
@@ -4382,6 +4517,11 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
return req->id;
case BT_BAP_STREAM_TYPE_BCAST:
stream->qos = *qos;
+ if (stream->lpac->type == BT_BAP_BCAST_SINK) {
+ if (data)
+ stream_config(stream, data, NULL);
+ stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG);
+ }
return 1;
}
@@ -4434,7 +4574,8 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
return NULL;
if (lpac && rpac) {
- if (!bap_codec_equal(&lpac->codec, &rpac->codec))
+ if ((rpac->type != BT_BAP_BCAST_SOURCE)
+ && (!bap_codec_equal(&lpac->codec, &rpac->codec)))
return NULL;
} else {
uint8_t type;
@@ -4446,13 +4587,19 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
if (rpac)
type = rpac->type;
else if (lpac) {
- switch(lpac->type) {
+ switch (lpac->type) {
case BT_BAP_SINK:
type = BT_BAP_SOURCE;
break;
case BT_BAP_SOURCE:
type = BT_BAP_SINK;
break;
+ case BT_BAP_BCAST_SOURCE:
+ type = BT_BAP_BCAST_SINK;
+ break;
+ case BT_BAP_BCAST_SINK:
+ type = BT_BAP_BCAST_SOURCE;
+ break;
default:
return NULL;
}
@@ -4913,6 +5060,13 @@ struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream)
return io->io;
}
+bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data)
+{
+ const struct bt_bap_stream *stream = data;
+
+ return stream->lpac->type == BT_BAP_BCAST_SINK;
+}
+
static bool stream_io_disconnected(struct io *io, void *user_data)
{
struct bt_bap_stream *stream = user_data;
@@ -4944,6 +5098,14 @@ static bool match_req_id(const void *data, const void *match_data)
return (req->id == id);
}
+static bool match_name(const void *data, const void *match_data)
+{
+ const struct bt_bap_pac *pac = data;
+ const char *name = match_data;
+
+ return (!strcmp(pac->name, name));
+}
+
int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id)
{
struct bt_bap_req *req;
@@ -5132,3 +5294,50 @@ bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd)
return io->connecting;
}
+
+bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name)
+{
+ struct bt_bap_endpoint *ep;
+ struct bt_bap_pac *pac_broadcast_source;
+
+ /* Add the remote source only if a local sink endpoint was registered */
+ if (queue_isempty(bap->ldb->broadcast_sinks))
+ return false;
+
+ /* Add the remote source only if a local sink endpoint was registered */
+ if (queue_isempty(bap->ldb->broadcast_sinks))
+ return false;
+
+ /* Add remote source endpoint */
+ if (!bap->rdb->broadcast_sources)
+ bap->rdb->broadcast_sources = queue_new();
+
+ if (queue_find(bap->rdb->broadcast_sources, match_name, name))
+ return true;
+
+ pac_broadcast_source = bap_pac_new(bap->rdb, name, BT_BAP_BCAST_SOURCE,
+ NULL, NULL, NULL, NULL);
+ queue_push_tail(bap->rdb->broadcast_sources, pac_broadcast_source);
+
+ if (!pac_broadcast_source)
+ return false;
+
+ queue_foreach(bap->pac_cbs, notify_pac_added, pac_broadcast_source);
+
+ /* 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);
+
+ return true;
+}
+
+void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
+ struct bt_bap_codec *codec,
+ struct iovec *data,
+ struct iovec *metadata)
+{
+ bap_pac_merge(pac, data, metadata);
+ pac->codec = *codec;
+}
@@ -194,7 +194,10 @@ bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t cb,
bool bap_print_cc(void *data, size_t len, util_debug_func_t func,
void *user_data);
-
+bool bap_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);
unsigned int bt_bap_pac_register(struct bt_bap *bap, bt_bap_pac_func_t added,
bt_bap_pac_func_t removed, void *user_data,
bt_bap_destroy_func_t destroy);
@@ -289,7 +292,7 @@ struct bt_bap_qos *bt_bap_stream_get_qos(struct bt_bap_stream *stream);
struct iovec *bt_bap_stream_get_metadata(struct bt_bap_stream *stream);
struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream);
-
+bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data);
bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd);
int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id);
@@ -305,3 +308,9 @@ uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream);
int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd);
bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);
+
+bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name);
+void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
+ struct bt_bap_codec *codec,
+ struct iovec *data,
+ struct iovec *metadata);