shared/bap: Add support for BAP broadcast sink
authorClaudia Draghicescu <claudia.rosu@nxp.com>
Tue, 22 Aug 2023 14:29:30 +0000 (17:29 +0300)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 13:34:03 +0000 (19:04 +0530)
This adds support for BAP broadcast sink, creates a remote endpoint when a
broadcast source is discovered and synchronizes with the source upon
endpoint configuration.
This feature was tested using bluetoothctl with the following commands:

[bluetooth]# endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
[bluetooth]# scan on
[NEW] Endpoint /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/pac_bcast0
[bluetooth]# endpoint.config
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/pac_bcast0
/local/endpoint/ep0 16_2_1

src/shared/bap.c
src/shared/bap.h

index 72ce67c086382e651ccb6d6f542553339a3f3a26..1c43680c245772f4560c30ff00aa6fa63ffada4f 100644 (file)
@@ -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);
@@ -4008,7 +4036,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 +4227,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 +4254,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 +4286,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 +4425,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 +4482,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 +4495,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 +4968,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 +5006,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 +5202,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;
+}
index 50b567663c32f4ad3dda030c8e59005316f6861f..edb5c1bed27e509ae1bce7bdb638eca5aa54d7c8 100644 (file)
@@ -289,7 +289,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 +305,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);