Subject: [PATCH v4 0/4] Add initial support for BAP broadcast source

This patch adds initial support for BAP broadcast source.

The current implementation allows BAP source endpoint registration,
media transport creation, transport acquiring and sending broadcast ISO data.

Currently, one BIG containing one BIS is supported.

To test the current implementation use bluetoothctl with the commands:
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
endpoint.config <created endpoint> <local endpoint> 16_2_1
transport.acquire <created transport>
transport.send <created transport> <file.wav>

The curent implementation checks that ISO_BROADCASTER is suported on the board so
"Check for ISO support in controller" patch is required
(https://patchwork.kernel.org/project/bluetooth/patch/[email protected]/)

Silviu Florian Barbulescu (4):
Split bt_bap_qos structure into unicast and broadcast structures
Update bluetoothctl with support for broadcast source
This adds the initial code for BAP broadcast source
Update BAP plugin with broadcast source support

client/player.c | 226 +++++++++++++--
profiles/audio/bap.c | 551 +++++++++++++++++++++++++++++++++----
profiles/audio/media.c | 115 +++++---
profiles/audio/media.h | 2 +
profiles/audio/transport.c | 84 ++++--
src/shared/bap.c | 329 ++++++++++++++++------
src/shared/bap.h | 51 +++-
unit/test-bap.c | 75 ++---
8 files changed, 1171 insertions(+), 262 deletions(-)


base-commit: 52477e80fc892bdd2ac250e932ceacda59f90b1c
--
2.34.1



Subject: [PATCH v4 4/4] Update BAP plugin with broadcast source support

This updates BAP plugin with broadcast source support.

---
profiles/audio/bap.c | 515 +++++++++++++++++++++++++++++++++----
profiles/audio/media.c | 82 ++++--
profiles/audio/media.h | 2 +
profiles/audio/transport.c | 59 +++--
4 files changed, 581 insertions(+), 77 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index cbaf705c0..cbe3c4b97 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -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,
};
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 515263af3..8280101af 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -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;
+}
diff --git a/profiles/audio/media.h b/profiles/audio/media.h
index 96bea9db4..1de84a8ff 100644
--- a/profiles/audio/media.h
+++ b/profiles/audio/media.h
@@ -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);
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 82f5fa6fe..773be8411 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -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;
--
2.34.1


Subject: [PATCH v4 2/4] Update bluetoothctl with support for broadcast source

This adds bluetoothctl support for broadcast source.
To test the current implementation use bluetoothctl with the commands:
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
endpoint.config <created endpoint> <local endpoint> 16_2_1
transport.acquire <created transport>
transport.send <created transport> <file.wav>

---
client/player.c | 226 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 201 insertions(+), 25 deletions(-)

diff --git a/client/player.c b/client/player.c
index a9f56fb94..389fc1d07 100644
--- a/client/player.c
+++ b/client/player.c
@@ -74,11 +74,13 @@ struct endpoint {
bool auto_accept;
bool acquiring;
uint8_t max_transports;
- uint8_t cig;
- uint8_t cis;
+ uint8_t iso_group;
+ uint8_t iso_stream;
char *transport;
DBusMessage *msg;
struct preset *preset;
+ bool broadcast;
+ struct iovec *bcode;
};

static DBusConnection *dbus_conn;
@@ -104,6 +106,22 @@ struct transport {
struct io *timer_io;
};

+static const uint8_t base_lc3_16_2_1[] = {
+ 0x28, 0x00, 0x00, /* Presentation Delay */
+ 0x01, /* Number of Subgroups */
+ 0x01, /* Number of BIS */
+ 0x06, 0x00, 0x00, 0x00, 0x00, /* Code ID = LC3 (0x06) */
+ 0x11, /* Codec Specific Configuration */
+ 0x02, 0x01, 0x03, /* 16 KHZ */
+ 0x02, 0x02, 0x01, /* 10 ms */
+ 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Front Left */
+ 0x03, 0x04, 0x28, 0x00, /* Frame Length 40 bytes */
+ 0x04, /* Metadata */
+ 0x03, 0x02, 0x02, 0x00, /* Audio Context: Convertional */
+ 0x01, /* BIS */
+ 0x00, /* Codec Specific Configuration */
+};
+
static void endpoint_unregister(void *data)
{
struct endpoint *ep = data;
@@ -1154,6 +1172,16 @@ static const struct capabilities {
CODEC_CAPABILITIES(PAC_SOURCE_UUID, LC3_ID,
LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
3u, 30, 240)),
+ /* Broadcast LC3 Source:
+ *
+ * Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
+ * Duration: 7.5 ms 10 ms
+ * Channel count: 3
+ * Frame length: 30-240
+ */
+ CODEC_CAPABILITIES(BAA_SERVICE_UUID, LC3_ID,
+ LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
+ 3u, 30, 240)),
};

struct codec_qos {
@@ -1435,6 +1463,7 @@ static struct preset {
PRESET(A2DP_SINK_UUID, A2DP_CODEC_SBC, sbc_presets, 6),
PRESET(PAC_SINK_UUID, LC3_ID, lc3_presets, 3),
PRESET(PAC_SOURCE_UUID, LC3_ID, lc3_presets, 3),
+ PRESET(BAA_SERVICE_UUID, LC3_ID, lc3_presets, 3),
};

static void parse_vendor_codec(const char *codec, uint16_t *vid, uint16_t *cid)
@@ -1707,6 +1736,27 @@ struct endpoint_config {
const struct codec_qos *qos;
};

+#define BCODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \
+ 0xa2, 0x65, 0xbb, 0xaf, 0xc6, 0xea, 0x03, 0xb8}
+
+static struct bt_iso_qos bcast_qos = {
+ .bcast = {
+ .big = BT_ISO_QOS_BIG_UNSET,
+ .bis = BT_ISO_QOS_BIS_UNSET,
+ .sync_interval = 0x07,
+ .packing = 0x00,
+ .framing = 0x00,
+ .encryption = 0x00,
+ .bcode = BCODE,
+ .options = 0x00,
+ .skip = 0x0000,
+ .sync_timeout = 0x4000,
+ .sync_cte_type = 0x00,
+ .mse = 0x00,
+ .timeout = 0x4000,
+ }
+ };
+
static void append_properties(DBusMessageIter *iter,
struct endpoint_config *cfg)
{
@@ -1714,6 +1764,7 @@ static void append_properties(DBusMessageIter *iter,
struct codec_qos *qos = (void *)cfg->qos;
const char *key = "Capabilities";
const char *meta = "Metadata";
+ const char *keyBCode = "BroadcastCode";

dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);

@@ -1742,16 +1793,27 @@ static void append_properties(DBusMessageIter *iter,
DBUS_TYPE_BYTE, &cfg->target_latency);
}

- if (cfg->ep->cig != BT_ISO_QOS_CIG_UNSET) {
- bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->cig);
+ if ((!cfg->ep->broadcast) &&
+ (cfg->ep->iso_group != BT_ISO_QOS_GROUP_UNSET)) {
+ bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->iso_group);
g_dbus_dict_append_entry(&dict, "CIG", DBUS_TYPE_BYTE,
- &cfg->ep->cig);
+ &cfg->ep->iso_group);
+ } else {
+ bt_shell_printf("BIG 0x%2.2x\n", bcast_qos.bcast.big);
+ g_dbus_dict_append_entry(&dict, "BIG", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.big);
}

- if (cfg->ep->cis != BT_ISO_QOS_CIS_UNSET) {
- bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->cis);
+ if ((!cfg->ep->broadcast) &&
+ (cfg->ep->iso_stream != BT_ISO_QOS_STREAM_UNSET)) {
+ bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->iso_stream);
g_dbus_dict_append_entry(&dict, "CIS", DBUS_TYPE_BYTE,
- &cfg->ep->cis);
+ &cfg->ep->iso_stream);
+
+ } else {
+ bt_shell_printf("BIS 0x%2.2x\n", bcast_qos.bcast.bis);
+ g_dbus_dict_append_entry(&dict, "BIS", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.bis);
}

bt_shell_printf("Interval %u\n", qos->interval);
@@ -1759,10 +1821,19 @@ static void append_properties(DBusMessageIter *iter,
g_dbus_dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT32,
&qos->interval);

- bt_shell_printf("Framing %s\n", qos->framing ? "true" : "false");
+ if (!cfg->ep->broadcast) {
+ bt_shell_printf("Framing %s\n",
+ qos->framing ? "true" : "false");

- g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
- &qos->framing);
+ g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
+ &qos->framing);
+ } else {
+ bt_shell_printf("Framing %s\n",
+ bcast_qos.bcast.framing ? "true" : "false");
+
+ g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
+ &bcast_qos.bcast.framing);
+ }

bt_shell_printf("PHY %s\n", qos->phy);

@@ -1787,6 +1858,57 @@ static void append_properties(DBusMessageIter *iter,
g_dbus_dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT32,
&qos->delay);

+ if (!cfg->ep->broadcast)
+ goto done;
+
+ bt_shell_printf("SyncInterval %u\n", bcast_qos.bcast.sync_interval);
+
+ g_dbus_dict_append_entry(&dict, "SyncInterval", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.sync_interval);
+
+ bt_shell_printf("Encryption %u\n", bcast_qos.bcast.encryption);
+
+ g_dbus_dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.encryption);
+
+ bt_shell_printf("Options %u\n", bcast_qos.bcast.options);
+
+ g_dbus_dict_append_entry(&dict, "Options", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.options);
+
+ bt_shell_printf("Skip %u\n", bcast_qos.bcast.skip);
+
+ g_dbus_dict_append_entry(&dict, "Skip", DBUS_TYPE_UINT16,
+ &bcast_qos.bcast.skip);
+
+ bt_shell_printf("SyncTimeout %u\n", bcast_qos.bcast.sync_timeout);
+
+ g_dbus_dict_append_entry(&dict, "SyncTimeout", DBUS_TYPE_UINT16,
+ &bcast_qos.bcast.sync_timeout);
+
+ bt_shell_printf("SyncCteType %u\n", bcast_qos.bcast.sync_cte_type);
+
+ g_dbus_dict_append_entry(&dict, "SyncCteType", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.sync_cte_type);
+
+ bt_shell_printf("MSE %u\n", bcast_qos.bcast.mse);
+
+ g_dbus_dict_append_entry(&dict, "MSE", DBUS_TYPE_BYTE,
+ &bcast_qos.bcast.mse);
+
+ bt_shell_printf("Timeout %u\n", bcast_qos.bcast.timeout);
+
+ g_dbus_dict_append_entry(&dict, "Timeout", DBUS_TYPE_UINT16,
+ &bcast_qos.bcast.timeout);
+
+ bt_shell_printf("BroadcastCode:\n");
+ bt_shell_hexdump(cfg->ep->bcode->iov_base, cfg->ep->bcode->iov_len);
+
+ g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &keyBCode,
+ DBUS_TYPE_BYTE,
+ &cfg->ep->bcode->iov_base,
+ cfg->ep->bcode->iov_len);
+
done:
dbus_message_iter_close_container(iter, &dict);
}
@@ -2239,14 +2361,14 @@ fail:

}

-static void endpoint_cis(const char *input, void *user_data)
+static void endpoint_iso_stream(const char *input, void *user_data)
{
struct endpoint *ep = user_data;
char *endptr = NULL;
int value;

if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
- ep->cis = BT_ISO_QOS_CIS_UNSET;
+ ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
} else {
value = strtol(input, &endptr, 0);

@@ -2255,20 +2377,20 @@ static void endpoint_cis(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

- ep->cis = value;
+ ep->iso_stream = value;
}

endpoint_register(ep);
}

-static void endpoint_cig(const char *input, void *user_data)
+static void endpoint_iso_group(const char *input, void *user_data)
{
struct endpoint *ep = user_data;
char *endptr = NULL;
int value;

if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
- ep->cig = BT_ISO_QOS_CIG_UNSET;
+ ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
} else {
value = strtol(input, &endptr, 0);

@@ -2277,10 +2399,15 @@ static void endpoint_cig(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

- ep->cig = value;
+ ep->iso_group = value;
}

- bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_cis, ep);
+ if (!ep->broadcast)
+ bt_shell_prompt_input(ep->path, "CIS (auto/value):",
+ endpoint_iso_stream, ep);
+ else
+ bt_shell_prompt_input(ep->path, "BIS (auto/value):",
+ endpoint_iso_stream, ep);
}

static void endpoint_max_transports(const char *input, void *user_data)
@@ -2302,13 +2429,24 @@ static void endpoint_max_transports(const char *input, void *user_data)
ep->max_transports = value;
}

- bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
+ if (ep->broadcast)
+ bt_shell_prompt_input(ep->path, "BIG (auto/value):",
+ endpoint_iso_group, ep);
+ else
+ bt_shell_prompt_input(ep->path, "CIG (auto/value):",
+ endpoint_iso_group, ep);
}

static void endpoint_auto_accept(const char *input, void *user_data)
{
struct endpoint *ep = user_data;

+ if (!strcmp(ep->uuid, BAA_SERVICE_UUID)) {
+ ep->broadcast = true;
+ } else {
+ ep->broadcast = false;
+ }
+
if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) {
ep->auto_accept = true;
bt_shell_prompt_input(ep->path, "Max Transports (auto/value):",
@@ -2321,7 +2459,12 @@ static void endpoint_auto_accept(const char *input, void *user_data)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

- bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
+ if (ep->broadcast)
+ bt_shell_prompt_input(ep->path, "BIG (auto/value):",
+ endpoint_iso_group, ep);
+ else
+ bt_shell_prompt_input(ep->path, "CIG (auto/value):",
+ endpoint_iso_group, ep);
}

static void endpoint_set_metadata(const char *input, void *user_data)
@@ -2598,9 +2741,17 @@ static void cmd_config_endpoint(int argc, char *argv[])
goto fail;
}

- /* Copy capabilities */
- iov_append(&cfg->caps, preset->data.iov_base,
- preset->data.iov_len);
+ if (cfg->ep->broadcast) {
+ iov_append(&cfg->ep->bcode, bcast_qos.bcast.bcode,
+ sizeof(bcast_qos.bcast.bcode));
+ /* Copy capabilities for broadcast*/
+ iov_append(&cfg->caps, base_lc3_16_2_1,
+ sizeof(base_lc3_16_2_1));
+ } else {
+ /* Copy capabilities */
+ iov_append(&cfg->caps, preset->data.iov_base,
+ preset->data.iov_len);
+ }

/* Set QoS parameters */
cfg->qos = &preset->qos;
@@ -3050,8 +3201,8 @@ static void register_endpoints(GDBusProxy *proxy)
ep->cid);
ep->max_transports = UINT8_MAX;
ep->auto_accept = true;
- ep->cig = BT_ISO_QOS_CIG_UNSET;
- ep->cis = BT_ISO_QOS_CIS_UNSET;
+ ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
+ ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
endpoint_register(ep);
}
}
@@ -3595,6 +3746,7 @@ static void cmd_acquire_transport(int argc, char *argv[])
{
GDBusProxy *proxy;
int i;
+ struct endpoint *ep, *link;

for (i = 1; i < argc; i++) {
proxy = g_dbus_proxy_lookup(transports, NULL, argv[i],
@@ -3610,6 +3762,30 @@ static void cmd_acquire_transport(int argc, char *argv[])
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

+ ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy));
+ if (!ep || ep->acquiring) {
+ bt_shell_printf(
+ "Transport %s already in acquiring process\n",
+ argv[i]);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ ep->acquiring = true;
+
+ link = find_link_by_proxy(proxy);
+ if (link) {
+ bt_shell_printf("Link %s found\n", link->transport);
+ /* If link already acquiring wait it to be complete */
+ if (link->acquiring) {
+ bt_shell_printf(
+ "Link %s is in acquiring process\n",
+ argv[i]);
+ return bt_shell_noninteractive_quit(
+ EXIT_FAILURE);
+ }
+ link->acquiring = true;
+ }
+
if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
acquire_reply, proxy, NULL)) {
bt_shell_printf("Failed acquire transport\n");
--
2.34.1


Subject: [PATCH v4 1/4] Split bt_bap_qos structure into unicast and broadcast structures

This splits bt_bap_qos structure into unicast and broadcast structures.

---
profiles/audio/bap.c | 58 +++++++++++++-----------
profiles/audio/media.c | 33 ++++++++------
profiles/audio/transport.c | 25 ++++++-----
src/shared/bap.c | 92 ++++++++++++++++++++------------------
src/shared/bap.h | 42 ++++++++++++++---
unit/test-bap.c | 75 ++++++++++++++++---------------
6 files changed, 188 insertions(+), 137 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 1a543a9ce..cbaf705c0 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -253,6 +253,7 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
struct iovec **metadata, struct bt_bap_qos *qos)
{
const char *key;
+ struct bt_bap_io_qos io_qos;

while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter value, entry;
@@ -282,17 +283,17 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->cig_id);
+ dbus_message_iter_get_basic(&value, &qos->ucast.cig_id);
} else if (!strcasecmp(key, "CIS")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->cis_id);
+ dbus_message_iter_get_basic(&value, &qos->ucast.cis_id);
} else if (!strcasecmp(key, "Interval")) {
if (var != DBUS_TYPE_UINT32)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->interval);
+ dbus_message_iter_get_basic(&value, &io_qos.interval);
} else if (!strcasecmp(key, "Framing")) {
dbus_bool_t val;

@@ -301,7 +302,7 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,

dbus_message_iter_get_basic(&value, &val);

- qos->framing = val;
+ qos->ucast.framing = val;
} else if (!strcasecmp(key, "PHY")) {
const char *str;

@@ -311,42 +312,43 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
dbus_message_iter_get_basic(&value, &str);

if (!strcasecmp(str, "1M"))
- qos->phy = 0x01;
+ io_qos.phy = 0x01;
else if (!strcasecmp(str, "2M"))
- qos->phy = 0x02;
+ io_qos.phy = 0x02;
else
goto fail;
} else if (!strcasecmp(key, "SDU")) {
if (var != DBUS_TYPE_UINT16)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->sdu);
+ dbus_message_iter_get_basic(&value, &io_qos.sdu);
} else if (!strcasecmp(key, "Retransmissions")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->rtn);
+ dbus_message_iter_get_basic(&value, &io_qos.rtn);
} else if (!strcasecmp(key, "Latency")) {
if (var != DBUS_TYPE_UINT16)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->latency);
+ dbus_message_iter_get_basic(&value, &io_qos.latency);
} else if (!strcasecmp(key, "Delay")) {
if (var != DBUS_TYPE_UINT32)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->delay);
+ dbus_message_iter_get_basic(&value, &qos->ucast.delay);
} else if (!strcasecmp(key, "TargetLatency")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

dbus_message_iter_get_basic(&value,
- &qos->target_latency);
+ &qos->ucast.target_latency);
}

dbus_message_iter_next(props);
}

+ memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
return 0;

fail:
@@ -456,8 +458,8 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
}

/* Mark CIG and CIS to be auto assigned */
- ep->qos.cig_id = BT_ISO_QOS_CIG_UNSET;
- ep->qos.cis_id = BT_ISO_QOS_CIS_UNSET;
+ 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");
@@ -734,11 +736,11 @@ static void bap_iso_qos(struct bt_bap_qos *qos, struct bt_iso_io_qos *io)
if (!qos)
return;

- io->interval = qos->interval;
- io->latency = qos->latency;
- io->sdu = qos->sdu;
- io->phy = qos->phy;
- io->rtn = qos->rtn;
+ io->interval = qos->ucast.io_qos.interval;
+ io->latency = qos->ucast.io_qos.latency;
+ io->sdu = qos->ucast.io_qos.sdu;
+ io->phy = qos->ucast.io_qos.phy;
+ io->rtn = qos->ucast.io_qos.rtn;
}

static bool match_stream_qos(const void *data, const void *user_data)
@@ -749,10 +751,10 @@ static bool match_stream_qos(const void *data, const void *user_data)

qos = bt_bap_stream_get_qos((void *)stream);

- if (iso_qos->ucast.cig != qos->cig_id)
+ if (iso_qos->ucast.cig != qos->ucast.cig_id)
return false;

- return iso_qos->ucast.cis == qos->cis_id;
+ return iso_qos->ucast.cis == qos->ucast.cis_id;
}

static void iso_confirm_cb(GIOChannel *io, void *user_data)
@@ -993,8 +995,10 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
}

memset(&iso_qos, 0, sizeof(iso_qos));
- iso_qos.ucast.cig = qos[0] ? qos[0]->cig_id : qos[1]->cig_id;
- iso_qos.ucast.cis = qos[0] ? qos[0]->cis_id : qos[1]->cis_id;
+ 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 :
+ qos[1]->ucast.cis_id;

bap_iso_qos(qos[0], &iso_qos.ucast.in);
bap_iso_qos(qos[1], &iso_qos.ucast.out);
@@ -1179,8 +1183,8 @@ 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.cig_id == BT_ISO_QOS_CIG_UNSET ||
- ep->qos.cis_id == BT_ISO_QOS_CIS_UNSET) {
+ 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;

@@ -1192,12 +1196,12 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
return;
}

- ep->qos.cig_id = qos.ucast.cig;
- ep->qos.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,
- ep->qos.cig_id, ep->qos.cis_id);
+ ep->qos.ucast.cig_id, ep->qos.ucast.cis_id);
}

static void bap_attached(struct bt_bap *bap, void *user_data)
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 6ce668e31..515263af3 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -6,7 +6,7 @@
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2004-2009 Marcel Holtmann <[email protected]>
* Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
- *
+ * Copyright 2023 NXP
*
*/

@@ -748,7 +748,10 @@ static int parse_select_properties(DBusMessageIter *props, struct iovec *caps,
struct bt_bap_qos *qos)
{
const char *key;
+ struct bt_bap_io_qos io_qos;
+ uint8_t framing = 0;

+ memset(&io_qos, 0, sizeof(io_qos));
while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter value, entry;
int var;
@@ -777,17 +780,17 @@ static int parse_select_properties(DBusMessageIter *props, struct iovec *caps,
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->cig_id);
+ dbus_message_iter_get_basic(&value, &qos->ucast.cig_id);
} else if (!strcasecmp(key, "CIS")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->cis_id);
+ dbus_message_iter_get_basic(&value, &qos->ucast.cis_id);
} else if (!strcasecmp(key, "Interval")) {
if (var != DBUS_TYPE_UINT32)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->interval);
+ dbus_message_iter_get_basic(&value, &io_qos.interval);
} else if (!strcasecmp(key, "Framing")) {
dbus_bool_t val;

@@ -796,7 +799,7 @@ static int parse_select_properties(DBusMessageIter *props, struct iovec *caps,

dbus_message_iter_get_basic(&value, &val);

- qos->framing = val;
+ framing = val;
} else if (!strcasecmp(key, "PHY")) {
const char *str;

@@ -806,42 +809,44 @@ static int parse_select_properties(DBusMessageIter *props, struct iovec *caps,
dbus_message_iter_get_basic(&value, &str);

if (!strcasecmp(str, "1M"))
- qos->phy = 0x01;
+ io_qos.phy = 0x01;
else if (!strcasecmp(str, "2M"))
- qos->phy = 0x02;
+ io_qos.phy = 0x02;
else
goto fail;
} else if (!strcasecmp(key, "SDU")) {
if (var != DBUS_TYPE_UINT16)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->sdu);
+ dbus_message_iter_get_basic(&value, &io_qos.sdu);
} else if (!strcasecmp(key, "Retransmissions")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->rtn);
+ dbus_message_iter_get_basic(&value, &io_qos.rtn);
} else if (!strcasecmp(key, "Latency")) {
if (var != DBUS_TYPE_UINT16)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->latency);
+ dbus_message_iter_get_basic(&value, &io_qos.latency);
} else if (!strcasecmp(key, "Delay")) {
if (var != DBUS_TYPE_UINT32)
goto fail;

- dbus_message_iter_get_basic(&value, &qos->delay);
+ dbus_message_iter_get_basic(&value, &qos->ucast.delay);
} else if (!strcasecmp(key, "TargetLatency")) {
if (var != DBUS_TYPE_BYTE)
goto fail;

dbus_message_iter_get_basic(&value,
- &qos->target_latency);
+ &qos->ucast.target_latency);
}

dbus_message_iter_next(props);
}

+ memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
+ qos->ucast.framing = framing;
return 0;

fail:
@@ -875,8 +880,8 @@ static void pac_select_cb(struct media_endpoint *endpoint, void *ret, int size,
memset(&qos, 0, sizeof(qos));

/* Mark CIG and CIS to be auto assigned */
- qos.cig_id = BT_ISO_QOS_CIG_UNSET;
- qos.cis_id = BT_ISO_QOS_CIS_UNSET;
+ qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET;
+ qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET;

memset(&caps, 0, sizeof(caps));
memset(&meta, 0, sizeof(meta));
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 9172d167e..82f5fa6fe 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2004-2009 Marcel Holtmann <[email protected]>
+ * Copyright 2023 NXP
*
*
*/
@@ -828,7 +829,7 @@ static gboolean qos_exists(const GDBusPropertyTable *property, void *data)
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

- return bap->qos.phy != 0x00;
+ return bap->qos.ucast.io_qos.phy != 0x00;
}

static gboolean get_cig(const GDBusPropertyTable *property,
@@ -838,7 +839,7 @@ static gboolean get_cig(const GDBusPropertyTable *property,
struct bap_transport *bap = transport->data;

dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
- &bap->qos.cig_id);
+ &bap->qos.ucast.cig_id);

return TRUE;
}
@@ -850,7 +851,7 @@ static gboolean get_cis(const GDBusPropertyTable *property,
struct bap_transport *bap = transport->data;

dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
- &bap->qos.cis_id);
+ &bap->qos.ucast.cis_id);

return TRUE;
}
@@ -862,7 +863,7 @@ static gboolean get_interval(const GDBusPropertyTable *property,
struct bap_transport *bap = transport->data;

dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
- &bap->qos.interval);
+ &bap->qos.ucast.io_qos.interval);

return TRUE;
}
@@ -872,7 +873,7 @@ static gboolean get_framing(const GDBusPropertyTable *property,
{
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;
- dbus_bool_t val = bap->qos.framing;
+ dbus_bool_t val = bap->qos.ucast.framing;

dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);

@@ -885,7 +886,8 @@ static gboolean get_phy(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

- dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &bap->qos.phy);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.ucast.io_qos.phy);

return TRUE;
}
@@ -896,7 +898,8 @@ static gboolean get_sdu(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &bap->qos.sdu);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &bap->qos.ucast.io_qos.sdu);

return TRUE;
}
@@ -907,7 +910,8 @@ static gboolean get_retransmissions(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

- dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &bap->qos.rtn);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.ucast.io_qos.rtn);

return TRUE;
}
@@ -919,7 +923,7 @@ static gboolean get_latency(const GDBusPropertyTable *property,
struct bap_transport *bap = transport->data;

dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
- &bap->qos.latency);
+ &bap->qos.ucast.io_qos.latency);

return TRUE;
}
@@ -930,7 +934,8 @@ static gboolean get_delay(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &bap->qos.delay);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+ &bap->qos.ucast.delay);

return TRUE;
}
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 7e597c662..05d814cb0 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -4,6 +4,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2022 Intel Corporation. All rights reserved.
+ * Copyright 2023 NXP
*
*/

@@ -861,15 +862,15 @@ static void stream_notify_qos(struct bt_bap_stream *stream)
status->state = ep->state;

qos = (void *)status->params;
- qos->cis_id = stream->qos.cis_id;
- qos->cig_id = stream->qos.cig_id;
- put_le24(stream->qos.interval, qos->interval);
- qos->framing = stream->qos.framing;
- qos->phy = stream->qos.phy;
- qos->sdu = cpu_to_le16(stream->qos.sdu);
- qos->rtn = stream->qos.rtn;
- qos->latency = cpu_to_le16(stream->qos.latency);
- put_le24(stream->qos.delay, qos->pd);
+ qos->cis_id = stream->qos.ucast.cis_id;
+ qos->cig_id = stream->qos.ucast.cig_id;
+ put_le24(stream->qos.ucast.io_qos.interval, qos->interval);
+ qos->framing = stream->qos.ucast.framing;
+ qos->phy = stream->qos.ucast.io_qos.phy;
+ qos->sdu = cpu_to_le16(stream->qos.ucast.io_qos.sdu);
+ qos->rtn = stream->qos.ucast.io_qos.rtn;
+ qos->latency = cpu_to_le16(stream->qos.ucast.io_qos.latency);
+ put_le24(stream->qos.ucast.delay, qos->pd);

gatt_db_attribute_notify(ep->attr, (void *) status, len,
bt_bap_get_att(stream->bap));
@@ -898,8 +899,8 @@ static void stream_notify_metadata(struct bt_bap_stream *stream)
status->state = ep->state;

meta = (void *)status->params;
- meta->cis_id = stream->qos.cis_id;
- meta->cig_id = stream->qos.cig_id;
+ meta->cis_id = stream->qos.ucast.cis_id;
+ meta->cig_id = stream->qos.ucast.cig_id;

if (stream->meta) {
meta->len = stream->meta->iov_len;
@@ -1575,20 +1576,22 @@ static uint8_t ascs_qos(struct bt_ascs *ascs, struct bt_bap *bap,

memset(&qos, 0, sizeof(qos));

- qos.cig_id = req->cig;
- qos.cis_id = req->cis;
- qos.interval = get_le24(req->interval);
- qos.framing = req->framing;
- qos.phy = req->phy;
- qos.sdu = le16_to_cpu(req->sdu);
- qos.rtn = req->rtn;
- qos.latency = le16_to_cpu(req->latency);
- qos.delay = get_le24(req->pd);
+ qos.ucast.cig_id = req->cig;
+ qos.ucast.cis_id = req->cis;
+ qos.ucast.io_qos.interval = get_le24(req->interval);
+ qos.ucast.framing = req->framing;
+ qos.ucast.io_qos.phy = req->phy;
+ qos.ucast.io_qos.sdu = le16_to_cpu(req->sdu);
+ qos.ucast.io_qos.rtn = req->rtn;
+ qos.ucast.io_qos.latency = le16_to_cpu(req->latency);
+ qos.ucast.delay = get_le24(req->pd);

DBG(bap, "CIG 0x%02x CIS 0x%02x interval %u framing 0x%02x "
"phy 0x%02x SDU %u rtn %u latency %u pd %u",
- req->cig, req->cis, qos.interval, qos.framing, qos.phy,
- qos.sdu, qos.rtn, qos.latency, qos.delay);
+ req->cig, req->cis, qos.ucast.io_qos.interval,
+ qos.ucast.framing, qos.ucast.io_qos.phy,
+ qos.ucast.io_qos.sdu, qos.ucast.io_qos.rtn,
+ qos.ucast.io_qos.latency, qos.ucast.delay);

ep = bap_get_local_endpoint_id(bap, req->ase);
if (!ep) {
@@ -3283,13 +3286,13 @@ static void ep_status_qos(struct bt_bap *bap, struct bt_bap_endpoint *ep,
if (!ep->stream)
return;

- ep->stream->qos.interval = interval;
- ep->stream->qos.framing = qos->framing;
- ep->stream->qos.phy = qos->phy;
- ep->stream->qos.sdu = sdu;
- ep->stream->qos.rtn = qos->rtn;
- ep->stream->qos.latency = latency;
- ep->stream->qos.delay = pd;
+ ep->stream->qos.ucast.io_qos.interval = interval;
+ ep->stream->qos.ucast.framing = qos->framing;
+ ep->stream->qos.ucast.io_qos.phy = qos->phy;
+ ep->stream->qos.ucast.io_qos.sdu = sdu;
+ ep->stream->qos.ucast.io_qos.rtn = qos->rtn;
+ ep->stream->qos.ucast.io_qos.latency = latency;
+ ep->stream->qos.ucast.delay = pd;

if (ep->old_state == BT_ASCS_ASE_STATE_CONFIG)
bap_stream_config_cfm(ep->stream);
@@ -4189,8 +4192,8 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
memset(&config, 0, sizeof(config));

config.ase = stream->ep->id;
- config.latency = qos->target_latency;
- config.phy = qos->phy;
+ config.latency = qos->ucast.target_latency;
+ config.phy = qos->ucast.io_qos.phy;
config.codec = stream->rpac->codec;

iov[0].iov_base = &config;
@@ -4372,15 +4375,15 @@ unsigned int bt_bap_stream_qos(struct bt_bap_stream *stream,

/* TODO: Figure out how to pass these values around */
qos.ase = stream->ep->id;
- qos.cig = data->cig_id;
- qos.cis = data->cis_id;
- put_le24(data->interval, qos.interval);
- qos.framing = data->framing;
- qos.phy = data->phy;
- qos.sdu = cpu_to_le16(data->sdu);
- qos.rtn = data->rtn;
- qos.latency = cpu_to_le16(data->latency);
- put_le24(data->delay, qos.pd);
+ qos.cig = data->ucast.cig_id;
+ qos.cis = data->ucast.cis_id;
+ put_le24(data->ucast.io_qos.interval, qos.interval);
+ qos.framing = data->ucast.framing;
+ qos.phy = data->ucast.io_qos.phy;
+ qos.sdu = cpu_to_le16(data->ucast.io_qos.sdu);
+ qos.rtn = data->ucast.io_qos.rtn;
+ qos.latency = cpu_to_le16(data->ucast.io_qos.latency);
+ put_le24(data->ucast.delay, qos.pd);

iov.iov_base = &qos;
iov.iov_len = sizeof(qos);
@@ -4784,8 +4787,8 @@ int bt_bap_stream_io_link(struct bt_bap_stream *stream,
return -EALREADY;

if (stream->client != link->client ||
- stream->qos.cig_id != link->qos.cig_id ||
- stream->qos.cis_id != link->qos.cis_id)
+ stream->qos.ucast.cig_id != link->qos.ucast.cig_id ||
+ stream->qos.ucast.cis_id != link->qos.ucast.cis_id)
return -EINVAL;

if (!stream->links)
@@ -4822,7 +4825,7 @@ static void bap_stream_get_in_qos(void *data, void *user_data)
struct bt_bap_qos **qos = user_data;

if (!qos || *qos || stream->ep->dir != BT_BAP_SOURCE ||
- !stream->qos.sdu)
+ !stream->qos.ucast.io_qos.sdu)
return;

*qos = &stream->qos;
@@ -4833,7 +4836,8 @@ static void bap_stream_get_out_qos(void *data, void *user_data)
struct bt_bap_stream *stream = data;
struct bt_bap_qos **qos = user_data;

- if (!qos || *qos || stream->ep->dir != BT_BAP_SINK || !stream->qos.sdu)
+ if (!qos || *qos || stream->ep->dir != BT_BAP_SINK ||
+ !stream->qos.ucast.io_qos.sdu)
return;

*qos = &stream->qos;
diff --git a/src/shared/bap.h b/src/shared/bap.h
index e9f769d0e..0c42e730f 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -4,6 +4,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2022 Intel Corporation. All rights reserved.
+ * Copyright 2023 NXP
*
*/

@@ -49,17 +50,46 @@ struct bt_ltv {
uint8_t value[0];
} __packed;

-struct bt_bap_qos {
+struct bt_bap_io_qos {
+ uint32_t interval; /* Frame interval */
+ uint16_t latency; /* Transport Latency */
+ uint16_t sdu; /* Maximum SDU Size */
+ uint8_t phy; /* PHY */
+ uint8_t rtn; /* Retransmission Effort */
+};
+
+struct bt_bap_ucast_qos {
uint8_t cig_id;
uint8_t cis_id;
- uint32_t interval; /* Frame interval */
uint8_t framing; /* Frame framing */
- uint8_t phy; /* PHY */
- uint16_t sdu; /* Maximum SDU Size */
- uint8_t rtn; /* Retransmission Effort */
- uint16_t latency; /* Transport Latency */
uint32_t delay; /* Presentation Delay */
uint8_t target_latency; /* Target Latency */
+ struct bt_bap_io_qos io_qos;
+};
+
+struct bt_bap_bcast_qos {
+ uint8_t big;
+ uint8_t bis;
+ uint8_t sync_interval;
+ uint8_t packing;
+ uint8_t framing;
+ uint8_t encryption;
+ struct iovec bcode;
+ uint8_t options;
+ uint16_t skip;
+ uint16_t sync_timeout;
+ uint8_t sync_cte_type;
+ uint8_t mse;
+ uint16_t timeout;
+ uint8_t pa_sync;
+ struct bt_bap_io_qos io_qos;
+};
+
+struct bt_bap_qos {
+ union {
+ struct bt_bap_ucast_qos ucast;
+ struct bt_bap_bcast_qos bcast;
+ };
};

typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data);
diff --git a/unit/test-bap.c b/unit/test-bap.c
index bf525742d..adade07db 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
@@ -712,12 +712,15 @@ static void test_disc(void)
#define QOS_BALANCED_2M \
{ \
.target_latency = BT_BAP_CONFIG_LATENCY_BALANCED, \
- .phy = BT_BAP_CONFIG_PHY_2M, \
+ .io_qos.phy = BT_BAP_CONFIG_PHY_2M, \
}
-
+#define QOS_UCAST \
+{\
+ .ucast = QOS_BALANCED_2M, \
+}
static struct test_config cfg_snk_8_1 = {
.cc = LC3_CONFIG_8_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -727,7 +730,7 @@ static struct test_config cfg_snk_8_1 = {

static struct test_config cfg_snk_8_2 = {
.cc = LC3_CONFIG_8_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -737,7 +740,7 @@ static struct test_config cfg_snk_8_2 = {

static struct test_config cfg_snk_16_1 = {
.cc = LC3_CONFIG_16_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -747,7 +750,7 @@ static struct test_config cfg_snk_16_1 = {

static struct test_config cfg_snk_16_2 = {
.cc = LC3_CONFIG_16_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -757,7 +760,7 @@ static struct test_config cfg_snk_16_2 = {

static struct test_config cfg_snk_24_1 = {
.cc = LC3_CONFIG_24_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -767,7 +770,7 @@ static struct test_config cfg_snk_24_1 = {

static struct test_config cfg_snk_24_2 = {
.cc = LC3_CONFIG_24_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -777,7 +780,7 @@ static struct test_config cfg_snk_24_2 = {

static struct test_config cfg_snk_32_1 = {
.cc = LC3_CONFIG_32_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -787,7 +790,7 @@ static struct test_config cfg_snk_32_1 = {

static struct test_config cfg_snk_32_2 = {
.cc = LC3_CONFIG_32_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -797,7 +800,7 @@ static struct test_config cfg_snk_32_2 = {

static struct test_config cfg_snk_44_1 = {
.cc = LC3_CONFIG_44_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -807,7 +810,7 @@ static struct test_config cfg_snk_44_1 = {

static struct test_config cfg_snk_44_2 = {
.cc = LC3_CONFIG_44_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -817,7 +820,7 @@ static struct test_config cfg_snk_44_2 = {

static struct test_config cfg_snk_48_1 = {
.cc = LC3_CONFIG_48_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -827,7 +830,7 @@ static struct test_config cfg_snk_48_1 = {

static struct test_config cfg_snk_48_2 = {
.cc = LC3_CONFIG_48_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -837,7 +840,7 @@ static struct test_config cfg_snk_48_2 = {

static struct test_config cfg_snk_48_3 = {
.cc = LC3_CONFIG_48_3,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -847,7 +850,7 @@ static struct test_config cfg_snk_48_3 = {

static struct test_config cfg_snk_48_4 = {
.cc = LC3_CONFIG_48_4,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -857,7 +860,7 @@ static struct test_config cfg_snk_48_4 = {

static struct test_config cfg_snk_48_5 = {
.cc = LC3_CONFIG_48_5,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -867,7 +870,7 @@ static struct test_config cfg_snk_48_5 = {

static struct test_config cfg_snk_48_6 = {
.cc = LC3_CONFIG_48_6,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
};

@@ -899,7 +902,7 @@ static struct test_config cfg_snk_48_6 = {

static struct test_config cfg_src_8_1 = {
.cc = LC3_CONFIG_8_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -909,7 +912,7 @@ static struct test_config cfg_src_8_1 = {

static struct test_config cfg_src_8_2 = {
.cc = LC3_CONFIG_8_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -919,7 +922,7 @@ static struct test_config cfg_src_8_2 = {

static struct test_config cfg_src_16_1 = {
.cc = LC3_CONFIG_16_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -929,7 +932,7 @@ static struct test_config cfg_src_16_1 = {

static struct test_config cfg_src_16_2 = {
.cc = LC3_CONFIG_16_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -939,7 +942,7 @@ static struct test_config cfg_src_16_2 = {

static struct test_config cfg_src_24_1 = {
.cc = LC3_CONFIG_24_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -949,7 +952,7 @@ static struct test_config cfg_src_24_1 = {

static struct test_config cfg_src_24_2 = {
.cc = LC3_CONFIG_24_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -959,7 +962,7 @@ static struct test_config cfg_src_24_2 = {

static struct test_config cfg_src_32_1 = {
.cc = LC3_CONFIG_32_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -969,7 +972,7 @@ static struct test_config cfg_src_32_1 = {

static struct test_config cfg_src_32_2 = {
.cc = LC3_CONFIG_32_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -979,7 +982,7 @@ static struct test_config cfg_src_32_2 = {

static struct test_config cfg_src_44_1 = {
.cc = LC3_CONFIG_44_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -989,7 +992,7 @@ static struct test_config cfg_src_44_1 = {

static struct test_config cfg_src_44_2 = {
.cc = LC3_CONFIG_44_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -999,7 +1002,7 @@ static struct test_config cfg_src_44_2 = {

static struct test_config cfg_src_48_1 = {
.cc = LC3_CONFIG_48_1,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1009,7 +1012,7 @@ static struct test_config cfg_src_48_1 = {

static struct test_config cfg_src_48_2 = {
.cc = LC3_CONFIG_48_2,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1019,7 +1022,7 @@ static struct test_config cfg_src_48_2 = {

static struct test_config cfg_src_48_3 = {
.cc = LC3_CONFIG_48_3,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1029,7 +1032,7 @@ static struct test_config cfg_src_48_3 = {

static struct test_config cfg_src_48_4 = {
.cc = LC3_CONFIG_48_4,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1039,7 +1042,7 @@ static struct test_config cfg_src_48_4 = {

static struct test_config cfg_src_48_5 = {
.cc = LC3_CONFIG_48_5,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1049,7 +1052,7 @@ static struct test_config cfg_src_48_5 = {

static struct test_config cfg_src_48_6 = {
.cc = LC3_CONFIG_48_6,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
};

@@ -1141,7 +1144,7 @@ static void test_scc_cc_lc3(void)

static struct test_config cfg_snk_vs = {
.cc = IOV_NULL,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.snk = true,
.vs = true,
};
@@ -1155,7 +1158,7 @@ static struct test_config cfg_snk_vs = {

static struct test_config cfg_src_vs = {
.cc = IOV_NULL,
- .qos = QOS_BALANCED_2M,
+ .qos = QOS_UCAST,
.src = true,
.vs = true,
};
--
2.34.1


Subject: [PATCH v4 3/4] This adds the initial code for BAP broadcast source

This adds initial support for BAP broadcast source.

---
src/shared/bap.c | 241 ++++++++++++++++++++++++++++++++++++++++-------
src/shared/bap.h | 9 ++
2 files changed, 214 insertions(+), 36 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 05d814cb0..9c80d21d1 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -121,6 +121,8 @@ struct bt_bap_db {
struct bt_ascs *ascs;
struct queue *sinks;
struct queue *sources;
+ struct queue *broadcast_sources;
+ struct queue *broadcast_sinks;
};

struct bt_bap_req {
@@ -623,6 +625,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)
+{
+ struct bt_bap_endpoint *ep;
+
+ ep = new0(struct bt_bap_endpoint, 1);
+ ep->bdb = bdb;
+ ep->attr = NULL;
+ ep->dir = BT_BAP_BCAST_SOURCE;
+
+ return ep;
+}
+
static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints,
struct bt_bap_db *db,
struct gatt_db_attribute *attr)
@@ -645,6 +659,30 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints,
return ep;
}

+static struct bt_bap_endpoint *bap_get_endpoint_bcast(struct queue *endpoints,
+ struct bt_bap_db *db)
+{
+ 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);
+ if (!ep)
+ return NULL;
+
+ queue_push_tail(endpoints, ep);
+
+ return ep;
+}
+
static bool bap_endpoint_match_id(const void *data, const void *match_data)
{
const struct bt_bap_endpoint *ep = data;
@@ -1261,6 +1299,37 @@ static void bap_stream_state_changed(struct bt_bap_stream *stream)
bt_bap_unref(bap);
}

+static void stream_set_state_broadcast(struct bt_bap_stream *stream,
+ uint8_t state)
+{
+ struct bt_bap_endpoint *ep = stream->ep;
+ struct bt_bap *bap = stream->bap;
+ const struct queue_entry *entry;
+
+ ep->old_state = ep->state;
+ ep->state = state;
+
+ bt_bap_ref(bap);
+
+ for (entry = queue_get_entries(bap->state_cbs); entry;
+ entry = entry->next) {
+ struct bt_bap_state *state = entry->data;
+
+ if (state->func)
+ state->func(stream, stream->ep->old_state,
+ stream->ep->state, state->data);
+ }
+
+ /* Post notification updates */
+ switch (stream->ep->state) {
+ case BT_ASCS_ASE_STATE_IDLE:
+ bap_stream_detach(stream);
+ break;
+ }
+
+ bt_bap_unref(bap);
+}
+
static void stream_set_state(struct bt_bap_stream *stream, uint8_t state)
{
struct bt_bap_endpoint *ep = stream->ep;
@@ -1397,6 +1466,11 @@ static void ep_config_cb(struct bt_bap_stream *stream, int err)
if (err)
return;

+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BROADCAST) {
+ stream_set_state_broadcast(stream, BT_BAP_STREAM_STATE_CONFIG);
+ return;
+ }
+
stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG);
}

@@ -2222,6 +2296,8 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
bdb->db = gatt_db_ref(db);
bdb->sinks = queue_new();
bdb->sources = queue_new();
+ bdb->broadcast_sources = queue_new();
+ bdb->broadcast_sinks = queue_new();

if (!bap_db)
bap_db = queue_new();
@@ -2397,6 +2473,16 @@ static void bap_add_source(struct bt_bap_pac *pac)
iov.iov_len, NULL);
}

+static void bap_add_broadcast_source(struct bt_bap_pac *pac)
+{
+ queue_push_tail(pac->bdb->broadcast_sources, pac);
+}
+
+static void bap_add_broadcast_sink(struct bt_bap_pac *pac)
+{
+ queue_push_tail(pac->bdb->broadcast_sinks, pac);
+}
+
static void notify_pac_added(void *data, void *user_data)
{
struct bt_bap_pac_changed *changed = data;
@@ -2421,7 +2507,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;
+ struct bt_bap_pac *pac, *pac_brodcast_sink;
struct bt_bap_codec codec;

if (!db)
@@ -2447,6 +2533,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
case BT_BAP_SOURCE:
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,
+ data, metadata);
+ bap_add_broadcast_sink(pac_brodcast_sink);
+ break;
default:
bap_pac_free(pac);
return NULL;
@@ -2489,6 +2582,21 @@ uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac)
}
}

+uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream)
+{
+ if (!stream)
+ return BT_BAP_STREAM_TYPE_UNKNOWN;
+
+ if ((bt_bap_pac_get_type(stream->lpac) == BT_BAP_SINK) ||
+ (bt_bap_pac_get_type(stream->lpac) == BT_BAP_SOURCE))
+ return BT_BAP_STREAM_TYPE_UNICAST;
+ else if ((bt_bap_pac_get_type(stream->lpac) == BT_BAP_BCAST_SOURCE) ||
+ (bt_bap_pac_get_type(stream->lpac) == BT_BAP_BCAST_SINK))
+ return BT_BAP_STREAM_TYPE_BROADCAST;
+ else
+ return BT_BAP_STREAM_TYPE_UNKNOWN;
+}
+
static void notify_pac_removed(void *data, void *user_data)
{
struct bt_bap_pac_changed *changed = data;
@@ -2547,6 +2655,9 @@ bool bt_bap_remove_pac(struct bt_bap_pac *pac)
if (queue_remove_if(pac->bdb->sources, NULL, pac))
goto found;

+ if (queue_remove_if(pac->bdb->broadcast_sources, NULL, pac))
+ goto found;
+
return false;

found:
@@ -3867,6 +3978,25 @@ clone:
return true;
}

+bool bt_bap_attach_broadcast(struct bt_bap *bap)
+{
+ struct bt_bap_endpoint *ep;
+
+ if (queue_find(sessions, NULL, bap))
+ return true;
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, bap);
+
+ ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb);
+ if (ep)
+ ep->bap = bap;
+
+ return true;
+}
+
static void stream_foreach_detach(void *data, void *user_data)
{
struct bt_bap_stream *stream = data;
@@ -4071,7 +4201,11 @@ void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type,
func, user_data);
case BT_BAP_SOURCE:
return bap_foreach_pac(bap->ldb->sinks, bap->rdb->sources,
- func, user_data);
+ func, user_data);
+ case BT_BAP_BCAST_SOURCE:
+ return bap_foreach_pac(bap->ldb->broadcast_sources,
+ bap->ldb->broadcast_sinks,
+ func, user_data);
}
}

@@ -4184,42 +4318,51 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
if (!bap_stream_valid(stream))
return 0;

- if (!stream->client) {
- stream_config(stream, data, NULL);
- return 0;
- }
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UNICAST) {
+ if (!stream->client) {
+ stream_config(stream, data, NULL);
+ return 0;
+ }

- memset(&config, 0, sizeof(config));
+ memset(&config, 0, sizeof(config));

- config.ase = stream->ep->id;
- config.latency = qos->ucast.target_latency;
- config.phy = qos->ucast.io_qos.phy;
- config.codec = stream->rpac->codec;
+ config.ase = stream->ep->id;
+ config.latency = qos->ucast.target_latency;
+ config.phy = qos->ucast.io_qos.phy;
+ config.codec = stream->rpac->codec;

- iov[0].iov_base = &config;
- iov[0].iov_len = sizeof(config);
+ iov[0].iov_base = &config;
+ iov[0].iov_len = sizeof(config);

- if (data) {
- if (!bap_print_cc(data->iov_base, data->iov_len,
- stream->bap->debug_func,
- stream->bap->debug_data))
- return 0;
+ if (data) {
+ if (!bap_print_cc(data->iov_base, data->iov_len,
+ stream->bap->debug_func,
+ stream->bap->debug_data))
+ return 0;

- config.cc_len = data->iov_len;
- iov[1] = *data;
- iovlen++;
- }
+ config.cc_len = data->iov_len;
+ iov[1] = *data;
+ iovlen++;
+ }

- req = bap_req_new(stream, BT_ASCS_CONFIG, iov, iovlen, func, user_data);
+ req = bap_req_new(stream, BT_ASCS_CONFIG, iov, iovlen,
+ func, user_data);

- if (!bap_queue_req(stream->bap, req)) {
- bap_req_free(req);
- return 0;
- }
+ if (!bap_queue_req(stream->bap, req)) {
+ bap_req_free(req);
+ return 0;
+ }

- stream->qos = *qos;
+ stream->qos = *qos;

- return req->id;
+ return req->id;
+ } else if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ stream->qos = *qos;
+ return 1;
+ } else {
+ return 0;
+ }
}

static bool match_pac(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
@@ -4345,6 +4488,10 @@ bool bt_bap_stream_set_user_data(struct bt_bap_stream *stream, void *user_data)

stream->user_data = user_data;

+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BROADCAST)
+ stream->lpac->ops->config(stream, stream->cc, &stream->qos,
+ ep_config_cb, stream->lpac->user_data);
+
return true;
}

@@ -4454,7 +4601,7 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
bt_bap_stream_func_t func,
void *user_data)
{
- int ret;
+ int ret = 0;

/* Table 3.2: ASE state machine transition
* Initiating device - client Only
@@ -4462,12 +4609,19 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
if (!bap_stream_valid(stream) || !stream->client)
return 0;

- ret = bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, func,
- user_data);
- if (!ret || !enable_links)
- return ret;
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UNICAST) {
+ ret = bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata,
+ func, user_data);
+ if (!ret || !enable_links)
+ return ret;

- queue_foreach(stream->links, bap_stream_enable_link, metadata);
+ queue_foreach(stream->links, bap_stream_enable_link, metadata);
+ } else if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BROADCAST) {
+ stream_set_state_broadcast(stream,
+ BT_BAP_STREAM_STATE_STREAMING);
+ return 1;
+ }

return ret;
}
@@ -4646,6 +4800,16 @@ unsigned int bt_bap_stream_release(struct bt_bap_stream *stream,

bap = stream->bap;

+ /* If stream is broadcast, no BT_ASCS_RELEASE is required */
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BROADCAST) {
+ if (!bap_stream_valid(stream)) {
+ stream_set_state_broadcast(stream,
+ BT_BAP_STREAM_STATE_IDLE);
+ stream = NULL;
+ }
+ return 0;
+ }
+
/* If stream does not belong to a client session, clean it up now */
if (!bap_stream_valid(stream)) {
stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE);
@@ -4681,8 +4845,13 @@ uint32_t bt_bap_stream_get_location(struct bt_bap_stream *stream)

if (stream->ep->dir == BT_BAP_SOURCE)
return pacs->source_loc_value;
- else
+ else if (stream->ep->dir == BT_BAP_SINK)
return pacs->sink_loc_value;
+ else
+ /* TO DO get the location values from metadata
+ * for brodcast source and sink
+ */
+ return stream->bap->ldb->pacs->source_loc_value;
}

struct iovec *bt_bap_stream_get_config(struct bt_bap_stream *stream)
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 0c42e730f..891b4b5d8 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -17,6 +17,12 @@

#define BT_BAP_SINK 0x01
#define BT_BAP_SOURCE 0x02
+#define BT_BAP_BCAST_SOURCE 0x03
+#define BT_BAP_BCAST_SINK 0x04
+
+#define BT_BAP_STREAM_TYPE_UNICAST 0x01
+#define BT_BAP_STREAM_TYPE_BROADCAST 0x02
+#define BT_BAP_STREAM_TYPE_UNKNOWN 0x03

#define BT_BAP_STREAM_STATE_IDLE 0x00
#define BT_BAP_STREAM_STATE_CONFIG 0x01
@@ -160,6 +166,8 @@ uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac);

uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac);

+uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream);
+
struct bt_bap_stream *bt_bap_pac_get_stream(struct bt_bap_pac *pac);

/* Session related function */
@@ -179,6 +187,7 @@ struct bt_bap *bt_bap_ref(struct bt_bap *bap);
void bt_bap_unref(struct bt_bap *bap);

bool bt_bap_attach(struct bt_bap *bap, struct bt_gatt_client *client);
+bool bt_bap_attach_broadcast(struct bt_bap *bap);
void bt_bap_detach(struct bt_bap *bap);

bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t cb,
--
2.34.1


2023-06-07 10:11:04

by bluez.test.bot

[permalink] [raw]
Subject: RE: Add initial support for BAP broadcast source

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=754766

---Test result---

Test Summary:
CheckPatch PASS 3.28 seconds
GitLint PASS 1.37 seconds
BuildEll PASS 26.70 seconds
BluezMake PASS 782.50 seconds
MakeCheck PASS 11.91 seconds
MakeDistcheck PASS 153.78 seconds
CheckValgrind PASS 248.53 seconds
CheckSmatch PASS 334.32 seconds
bluezmakeextell PASS 101.15 seconds
IncrementalBuild PASS 2619.13 seconds
ScanBuild PASS 1009.30 seconds



---
Regards,
Linux Bluetooth

2023-06-08 00:31:23

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH v4 0/4] Add initial support for BAP broadcast source

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:

On Wed, 7 Jun 2023 11:22:56 +0300 you wrote:
> This patch adds initial support for BAP broadcast source.
>
> The current implementation allows BAP source endpoint registration,
> media transport creation, transport acquiring and sending broadcast ISO data.
>
> Currently, one BIG containing one BIS is supported.
>
> [...]

Here is the summary with links:
- [v4,1/4] Split bt_bap_qos structure into unicast and broadcast structures
(no matching commit)
- [v4,2/4] Update bluetoothctl with support for broadcast source
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=eb821743f9bf
- [v4,3/4] This adds the initial code for BAP broadcast source
(no matching commit)
- [v4,4/4] Update BAP plugin with broadcast source support
(no matching commit)

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html