2023-07-12 09:06:34

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 0/6] Add support for BAP broadcast sink

This series of patches adds support for BAP broadcast sink.
It consists in registering a sink endpoint using the Sink PAC UUID,
discovering of broadcast advertisers that announce the
Broadcast Audio Announcement service, synchronizes to the Periodic
advertisements of the source and synchronizes to the BIG advertised
in the PA train.
To retrieve the BASE info advertised in the PA train, the patch
Bluetooth: ISO: Add support for periodic adv reports processing
was used.

This feature was tested using bluetoothctl with the following commands:

[bluetooth]# endpoint.register 00002bc9-0000-1000-8000-00805f9b34fb 0x06
[/local/endpoint/ep0] Auto Accept (yes/no): y
[/local/endpoint/ep0] Max Transports (auto/value): a
[/local/endpoint/ep0] unicast/broadcast (u/b): b
[/local/endpoint/ep0] BIG (auto/value): a
[/local/endpoint/ep0] BIS (auto/value): a

[bluetooth]# scan on

[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0
/local/endpoint/ep0 16_2_1 <source_address>

Claudia Draghicescu (6):
client/player: Add broadcast sink endpoint registration and
configuration.
client/main: Add broadcast source discovery
media: Add support for a broadcast sink media endpoint
transport: Update transport properties for a broadcast stream
btio: Add support for getsockopt(BT_ISO_BASE)
bap: Add gdbus interface for BAP broadcast sink, create
synchronization with source and create BAP broadcast sink stream

btio/btio.c | 13 +-
client/main.c | 57 +++++-
client/player.c | 187 +++++++++++++++++++-
client/player.h | 3 +
profiles/audio/bap.c | 347 ++++++++++++++++++++++++++++++++++---
profiles/audio/media.c | 35 +++-
profiles/audio/media.h | 2 +-
profiles/audio/transport.c | 244 +++++++++++++++++++++++++-
src/shared/bap.c | 50 +++++-
src/shared/bap.h | 4 +-
10 files changed, 888 insertions(+), 54 deletions(-)


base-commit: 838e1578072900d1f98dfb31cc538940d2fad876
--
2.34.1



2023-07-12 09:06:36

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 2/6] client/main: Add broadcast source discovery

This checks if the scanned device advertises the
Broadcast Audio Announcement service.
If it does, the device will be added to a list of broadcast sources
to which the adapter can synchronize.

To test this feature use the following commands:

[bluetooth]# endpoint.register 00002bc9-0000-1000-8000-00805f9b34fb 0x06
[/local/endpoint/ep0] Auto Accept (yes/no): y
[/local/endpoint/ep0] Max Transports (auto/value): a
[/local/endpoint/ep0] unicast/broadcast (u/b): b
[/local/endpoint/ep0] BIG (auto/value): a
[/local/endpoint/ep0] BIS (auto/value): a

[bluetooth]# scan on

[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0
/local/endpoint/ep0 16_2_1 <source_address>
---
client/main.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/client/main.c b/client/main.c
index b433a2200..8fe537f34 100644
--- a/client/main.c
+++ b/client/main.c
@@ -198,7 +198,7 @@ static void print_device(GDBusProxy *proxy, const char *description)
if (filter.discoverable)
return;

- bt_shell_printf("%s%s%s" COLOR_BOLDGRAY "Device %s %s"
+ bt_shell_printf("%s%s%s" COLOR_GREEN "Device %s %s"
COLOR_OFF "\n",
description ? "[" : "",
description ? : "",
@@ -304,6 +304,57 @@ static gboolean proxy_is_child(GDBusProxy *device, GDBusProxy *parent)
return FALSE;
}

+static bool parse_service_data(GDBusProxy *proxy)
+{
+ DBusMessageIter iter, entries;
+
+ if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &entries);
+
+ while (dbus_message_iter_get_arg_type(&entries)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry, array;
+ const char *uuid_str;
+ uint8_t *service_data;
+ int len;
+
+ dbus_message_iter_recurse(&entries, &entry);
+ dbus_message_iter_get_basic(&entry, &uuid_str);
+
+ dbus_message_iter_next(&entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+ goto fail;
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&value, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&array, &service_data, &len);
+
+ if (!strcmp(uuid_str, BAA_SERVICE_UUID)) {
+ player_add_bcast_source(proxy, service_data, len);
+ return true;
+ }
+
+ dbus_message_iter_next(&entries);
+ }
+
+fail:
+ return false;
+}
+
static gboolean service_is_child(GDBusProxy *service)
{
DBusMessageIter iter;
@@ -399,6 +450,8 @@ static void device_added(GDBusProxy *proxy)
if (connected)
set_default_device(proxy, NULL);
}
+
+ parse_service_data(proxy);
}

static struct adapter *find_ctrl(GList *source, const char *path);
@@ -531,6 +584,8 @@ static void device_removed(GDBusProxy *proxy)
/* TODO: Error */
return;
}
+ /* Check if it was a broadcast source and remove it */
+ player_remove_bcast_source(proxy);

adapter->devices = g_list_remove(adapter->devices, proxy);

--
2.34.1


2023-07-12 09:06:35

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 1/6] client/player: Add broadcast sink endpoint registration and configuration.

Added support for broadcast sink registration using the UUID for PAC sink.
Added support for broadcast source discovery.
Added in the endpoint configuration command a new parameter for
source address that triggers source synchronization and
stream configuration.
To test this feature use the following commands:

[bluetooth]# endpoint.register 00002bc9-0000-1000-8000-00805f9b34fb 0x06
[/local/endpoint/ep0] Auto Accept (yes/no): y
[/local/endpoint/ep0] Max Transports (auto/value): a
[/local/endpoint/ep0] unicast/broadcast (u/b): b
[/local/endpoint/ep0] BIG (auto/value): a
[/local/endpoint/ep0] BIS (auto/value): a

[bluetooth]# scan on
NOTE! in the list of scanned devices, the broadcast source will be
printed in green.
[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0
/local/endpoint/ep0 16_2_1 <source_address>

---
client/player.c | 187 +++++++++++++++++++++++++++++++++++++++++++++---
client/player.h | 3 +
2 files changed, 181 insertions(+), 9 deletions(-)

diff --git a/client/player.c b/client/player.c
index e5084967a..fd2c89f0b 100644
--- a/client/player.c
+++ b/client/player.c
@@ -81,6 +81,7 @@ struct endpoint {
struct preset *preset;
bool broadcast;
struct iovec *bcode;
+ struct queue *bcast_sources;
};

static DBusConnection *dbus_conn;
@@ -94,6 +95,11 @@ static GList *local_endpoints = NULL;
static GList *transports = NULL;
static struct queue *ios = NULL;

+struct bcast_source {
+ GDBusProxy *proxy;
+ uint32_t bcast_id;
+};
+
struct transport {
GDBusProxy *proxy;
int sk;
@@ -2285,6 +2291,9 @@ static void register_endpoint_setup(DBusMessageIter *iter, void *user_data)
bt_shell_hexdump(ep->meta->iov_base, ep->meta->iov_len);
}

+ g_dbus_dict_append_entry(&dict, "Broadcast", DBUS_TYPE_BOOLEAN,
+ &ep->broadcast);
+
dbus_message_iter_close_container(iter, &dict);
}

@@ -2424,6 +2433,28 @@ static void endpoint_iso_group(const char *input, void *user_data)
endpoint_iso_stream, ep);
}

+static void endpoint_iso_mode(const char *input, void *user_data)
+{
+ struct endpoint *ep = user_data;
+
+ if (!strcasecmp(input, "u") || !strcasecmp(input, "unicast")) {
+ ep->broadcast = false;
+ } else if (!strcasecmp(input, "b") || !strcasecmp(input, "broadcast")) {
+ ep->broadcast = true;
+ ep->bcast_sources = queue_new();
+ } else {
+ bt_shell_printf("Invalid input for Auto Accept\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (!ep->broadcast)
+ bt_shell_prompt_input(ep->path, "CIG (auto/value):",
+ endpoint_iso_group, ep);
+ else
+ bt_shell_prompt_input(ep->path, "BIG (auto/value):",
+ endpoint_iso_group, ep);
+}
+
static void endpoint_max_transports(const char *input, void *user_data)
{
struct endpoint *ep = user_data;
@@ -2445,7 +2476,10 @@ static void endpoint_max_transports(const char *input, void *user_data)

if (ep->broadcast)
bt_shell_prompt_input(ep->path, "BIG (auto/value):",
- endpoint_iso_group, ep);
+ endpoint_iso_group, ep);
+ else if (!strcmp(ep->uuid, PAC_SINK_UUID))
+ bt_shell_prompt_input(ep->path, "unicast/broadcast (u/b):",
+ endpoint_iso_mode, ep);
else
bt_shell_prompt_input(ep->path, "CIG (auto/value):",
endpoint_iso_group, ep);
@@ -2472,13 +2506,6 @@ static void endpoint_auto_accept(const char *input, void *user_data)
bt_shell_printf("Invalid input for Auto Accept\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
-
- 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)
@@ -2714,6 +2741,103 @@ static void endpoint_set_config(struct endpoint_config *cfg)
}
}

+static void sink_create_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ bt_shell_printf("Failed to create broadcast sink: %s\n",
+ error.name);
+ dbus_error_free(&error);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+struct bcast_sink {
+ uint8_t bc_sid;
+ uint8_t bc_num_bis;
+} bcast_sink = {
+ .bc_sid = 1,
+ .bc_num_bis = 1,
+};
+
+static bool match_bcast_source_by_address(
+ const void *data, const void *match_data)
+{
+ const struct bcast_source *source = data;
+ const char *addr = match_data;
+ char *source_addr;
+ DBusMessageIter iter;
+
+ if (!g_dbus_proxy_get_property(source->proxy, "Address", &iter))
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &source_addr);
+
+ if (!strcasecmp(addr, source_addr))
+ return true;
+
+ return false;
+}
+static void sink_create_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct bcast_source *bcast_source = user_data;
+ DBusMessageIter dict, source_iter;
+ const char *source_type, *source_address;
+
+ g_dbus_proxy_get_property(bcast_source->proxy, "Address", &source_iter);
+
+ dbus_message_iter_get_basic(&source_iter, &source_address);
+
+ g_dbus_proxy_get_property(bcast_source->proxy, "AddressType",
+ &source_iter);
+
+ dbus_message_iter_get_basic(&source_iter, &source_type);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+ g_dbus_dict_append_entry(&dict, "SourceAddress", DBUS_TYPE_STRING,
+ &source_address);
+
+ g_dbus_dict_append_entry(&dict, "SourceAddressType", DBUS_TYPE_STRING,
+ &source_type);
+
+ g_dbus_dict_append_entry(&dict, "BIS", DBUS_TYPE_BYTE,
+ &bcast_sink.bc_sid);
+
+ g_dbus_dict_append_entry(&dict, "NumBis", DBUS_TYPE_BYTE,
+ &bcast_sink.bc_num_bis);
+
+ g_dbus_dict_append_entry(&dict, "BcastID", DBUS_TYPE_UINT32,
+ &bcast_source->bcast_id);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void endpoint_bcast_sink_sync(struct endpoint_config *cfg, char *source)
+{
+ struct bcast_source *bcast_source = NULL;
+
+ bcast_source = queue_find(cfg->ep->bcast_sources,
+ match_bcast_source_by_address, source);
+
+ if (!bcast_source) {
+ bt_shell_printf("Source not found\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+
+ if (g_dbus_proxy_method_call(cfg->proxy, "BcastSinkCreate",
+ sink_create_setup, sink_create_reply,
+ bcast_source, NULL) == FALSE) {
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
static void endpoint_config(const char *input, void *user_data)
{
struct endpoint_config *cfg = user_data;
@@ -2771,6 +2895,10 @@ static void cmd_config_endpoint(int argc, char *argv[])
cfg->qos = &preset->qos;

endpoint_set_config(cfg);
+
+ if (argv[4])
+ endpoint_bcast_sink_sync(cfg, argv[4]);
+
return;
}

@@ -3172,7 +3300,7 @@ static const struct bt_shell_menu endpoint_menu = {
{ "unregister", "<UUID/object>", cmd_unregister_endpoint,
"Register Endpoint",
local_endpoint_generator },
- { "config", "<endpoint> <local endpoint> [preset]",
+ { "config", "<endpoint> <local endpoint> [preset] [source]",
cmd_config_endpoint,
"Configure Endpoint",
endpoint_generator },
@@ -4238,3 +4366,44 @@ void player_remove_submenu(void)
g_dbus_client_unref(client);
queue_destroy(ios, transport_free);
}
+
+void player_add_bcast_source(GDBusProxy *proxy, uint8_t *service_data, int len)
+{
+ GList *l;
+
+ for (l = local_endpoints; l; l = g_list_next(l)) {
+ struct endpoint *ep = l->data;
+
+ if (ep->broadcast && ep->bcast_sources) {
+ struct bcast_source *bcast_source =
+ new0(struct bcast_source, 1);
+
+ bcast_source->proxy = proxy;
+ bcast_source->bcast_id = get_le24(service_data);
+ queue_push_tail(ep->bcast_sources, bcast_source);
+ }
+ }
+
+}
+static bool match_bcast_source_by_proxy(const void *data,
+ const void *user_data)
+{
+ const struct bcast_source *bcast_source = data;
+
+ if (bcast_source->proxy == user_data)
+ return true;
+
+ return false;
+}
+void player_remove_bcast_source(GDBusProxy *proxy)
+{
+ GList *l;
+
+ for (l = local_endpoints; l; l = g_list_next(l)) {
+ struct endpoint *ep = l->data;
+
+ if (ep->broadcast && ep->bcast_sources)
+ queue_remove_if(ep->bcast_sources,
+ match_bcast_source_by_proxy, proxy);
+ }
+}
diff --git a/client/player.h b/client/player.h
index e7778cb1e..21e73e8ed 100644
--- a/client/player.h
+++ b/client/player.h
@@ -10,3 +10,6 @@

void player_add_submenu(void);
void player_remove_submenu(void);
+void player_add_bcast_source(GDBusProxy *proxy,
+ uint8_t *service_data, int len);
+void player_remove_bcast_source(GDBusProxy *proxy);
--
2.34.1


2023-07-12 09:06:36

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 4/6] transport: Update transport properties for a broadcast stream

This patch gets the QOS broadcast stream parameters and passes them
to upper layers.

---
profiles/audio/transport.c | 244 ++++++++++++++++++++++++++++++++++++-
1 file changed, 241 insertions(+), 3 deletions(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index aa3a718b0..e7f21ff9c 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -551,8 +551,8 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,

owner = media_owner_create(msg);

- if (!strcmp(media_endpoint_get_uuid(transport->endpoint),
- BAA_SERVICE_UUID)) {
+ if (bt_bap_stream_get_type(get_stream_bap(transport)) ==
+ BT_BAP_STREAM_TYPE_BCAST) {
req = media_request_create(msg, 0x00);
media_owner_add(owner, req);
media_transport_set_owner(transport, owner);
@@ -853,6 +853,9 @@ static gboolean qos_exists(const GDBusPropertyTable *property, void *data)
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

+ if (media_endpoint_is_broadcast(transport->endpoint))
+ return bap->qos.bcast.io_qos.sdu != 0x00;
+
return bap->qos.ucast.io_qos.phy != 0x00;
}

@@ -868,6 +871,18 @@ static gboolean get_cig(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean get_big(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.big);
+
+ return TRUE;
+}
+
static gboolean get_cis(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -880,6 +895,18 @@ static gboolean get_cis(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean get_bis(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.bis);
+
+ return TRUE;
+}
+
static gboolean get_interval(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -899,6 +926,9 @@ static gboolean get_framing(const GDBusPropertyTable *property,
struct bap_transport *bap = transport->data;
dbus_bool_t val = bap->qos.ucast.framing;

+ if (media_endpoint_is_broadcast(transport->endpoint))
+ val = bap->qos.bcast.framing;
+
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);

return TRUE;
@@ -910,6 +940,12 @@ static gboolean get_phy(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

+ if (media_endpoint_is_broadcast(transport->endpoint)) {
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.io_qos.phy);
+ return TRUE;
+ }
+
dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
&bap->qos.ucast.io_qos.phy);

@@ -922,6 +958,12 @@ static gboolean get_sdu(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct bap_transport *bap = transport->data;

+ if (media_endpoint_is_broadcast(transport->endpoint)) {
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &bap->qos.bcast.io_qos.sdu);
+ return TRUE;
+ }
+
dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
&bap->qos.ucast.io_qos.sdu);

@@ -1040,6 +1082,121 @@ static gboolean get_links(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean get_sync_interval(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.sync_interval);
+
+ return TRUE;
+}
+
+static gboolean get_packing(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.packing);
+
+ return TRUE;
+}
+
+static gboolean get_bcode(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ if (bap->qos.bcast.bcode && bap->qos.bcast.bcode->iov_len)
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.bcode->iov_base,
+ bap->qos.bcast.bcode->iov_len);
+
+ dbus_message_iter_close_container(iter, &array);
+ return TRUE;
+}
+
+static gboolean get_options(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.options);
+
+ return TRUE;
+}
+
+static gboolean get_skip(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &bap->qos.bcast.skip);
+
+ return TRUE;
+}
+
+static gboolean get_sync_timeout(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &bap->qos.bcast.sync_timeout);
+
+ return TRUE;
+}
+
+static gboolean get_sync_cte_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.sync_cte_type);
+
+ return TRUE;
+}
+
+static gboolean get_mse(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &bap->qos.bcast.mse);
+
+ return TRUE;
+}
+
+static gboolean get_timeout(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct media_transport *transport = data;
+ struct bap_transport *bap = transport->data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &bap->qos.bcast.timeout);
+
+ return TRUE;
+}
+
static const GDBusPropertyTable bap_properties[] = {
{ "Device", "o", get_device },
{ "UUID", "s", get_uuid },
@@ -1059,6 +1216,17 @@ static const GDBusPropertyTable bap_properties[] = {
{ "Location", "u", get_location },
{ "Metadata", "ay", get_metadata },
{ "Links", "ao", get_links, NULL, links_exists },
+ { "BIG", "y", get_big, NULL, qos_exists },
+ { "BIS", "y", get_bis, NULL, qos_exists },
+ { "SyncInterval", "y", get_sync_interval, NULL, qos_exists },
+ { "Packing", "y", get_packing, NULL, qos_exists },
+ { "BCode", "ay", get_bcode, NULL, qos_exists },
+ { "Options", "y", get_options, NULL, qos_exists },
+ { "Skip", "q", get_skip, NULL, qos_exists },
+ { "SyncTimeout", "q", get_sync_timeout, NULL, qos_exists },
+ { "SyncCteType", "y", get_sync_cte_type, NULL, qos_exists },
+ { "MSE", "y", get_mse, NULL, qos_exists },
+ { "Timeout", "q", get_timeout, NULL, qos_exists },
{ }
};

@@ -1341,6 +1509,71 @@ static gboolean bap_resume_wait_cb(void *data)
return FALSE;
}

+static void bap_update_bcast_qos(const struct media_transport *transport)
+{
+ struct bap_transport *bap = transport->data;
+ struct bt_bap_qos *qos;
+
+ qos = bt_bap_stream_get_qos(bap->stream);
+
+ if (!memcmp(qos, &bap->qos, sizeof(struct bt_bap_qos)))
+ return;
+
+ bap->qos = *qos;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "BIG");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "BIS");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "SyncInterval");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Packing");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Framing");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "BCode");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Options");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Skip");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "SyncTimeout");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "SyncCteType");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "MSE");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Timeout");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Interval");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "Latency");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "PHY");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "SDU");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ transport->path, MEDIA_TRANSPORT_INTERFACE,
+ "RTN");
+}
+
static guint resume_bap(struct media_transport *transport,
struct media_owner *owner)
{
@@ -1493,7 +1726,10 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
if (owner && owner->pending)
return;
bap_update_links(transport);
- bap_update_qos(transport);
+ if (!media_endpoint_is_broadcast(transport->endpoint))
+ bap_update_qos(transport);
+ else if (bt_bap_stream_io_dir(stream) != BT_BAP_BCAST_SOURCE)
+ bap_update_bcast_qos(transport);
transport_update_playing(transport, FALSE);
return;
case BT_BAP_STREAM_STATE_DISABLING:
@@ -1503,6 +1739,8 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
return;
break;
case BT_BAP_STREAM_STATE_STREAMING:
+ if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
+ bap_update_bcast_qos(transport);
break;
}

--
2.34.1


2023-07-12 09:06:37

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 3/6] media: Add support for a broadcast sink media endpoint

This patch adds the possibility to register a broadcast sink
media endpoint if the controller has support for ISO Sync Receiver.

---
profiles/audio/media.c | 35 ++++++++++++++++++++++++++++-------
profiles/audio/media.h | 2 +-
2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index bcf4eae26..d529a4f60 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -105,6 +105,7 @@ struct media_endpoint {
GSList *requests;
struct media_adapter *adapter;
GSList *transports;
+ bool broadcast;
};

struct media_player {
@@ -1224,6 +1225,9 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,

static bool endpoint_init_pac_sink(struct media_endpoint *endpoint, int *err)
{
+ if (endpoint->broadcast)
+ return endpoint_init_pac(endpoint, BT_BAP_BCAST_SINK, err);
+
return endpoint_init_pac(endpoint, BT_BAP_SINK, err);
}

@@ -1333,8 +1337,10 @@ static bool experimental_endpoint_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_CIS_CENTRAL |
+ if ((!btd_adapter_has_settings(adapter, MGMT_SETTING_CIS_CENTRAL |
MGMT_SETTING_CIS_PERIPHERAL))
+ && (!btd_adapter_has_settings(adapter,
+ MGMT_SETTING_ISO_SYNC_RECEIVER)))
return false;

return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
@@ -1382,6 +1388,7 @@ media_endpoint_create(struct media_adapter *adapter,
int size,
uint8_t *metadata,
int metadata_size,
+ bool broadcast,
int *err)
{
struct media_endpoint *endpoint;
@@ -1397,6 +1404,7 @@ media_endpoint_create(struct media_adapter *adapter,
endpoint->cid = cid;
endpoint->vid = vid;
endpoint->delay_reporting = delay_reporting;
+ endpoint->broadcast = broadcast;

if (qos)
endpoint->qos = *qos;
@@ -1462,7 +1470,8 @@ static int parse_properties(DBusMessageIter *props, const char **uuid,
uint16_t *cid, uint16_t *vid,
struct bt_bap_pac_qos *qos,
uint8_t **capabilities, int *size,
- uint8_t **metadata, int *metadata_size)
+ uint8_t **metadata, int *metadata_size,
+ bool *broadcast)
{
gboolean has_uuid = FALSE;
gboolean has_codec = FALSE;
@@ -1546,6 +1555,10 @@ static int parse_properties(DBusMessageIter *props, const char **uuid,
if (var != DBUS_TYPE_UINT16)
return -EINVAL;
dbus_message_iter_get_basic(&value, &qos->ppd_max);
+ } else if (strcasecmp(key, "Broadcast") == 0) {
+ if (var != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, broadcast);
}

dbus_message_iter_next(props);
@@ -1569,6 +1582,7 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
uint8_t *metadata = NULL;
int size = 0;
int metadata_size = 0;
+ bool broadcast = false;
int err;

sender = dbus_message_get_sender(msg);
@@ -1587,13 +1601,13 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,

if (parse_properties(&props, &uuid, &delay_reporting, &codec, &cid,
&vid, &qos, &capabilities, &size, &metadata,
- &metadata_size) < 0)
+ &metadata_size, &broadcast) < 0)
return btd_error_invalid_args(msg);

if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
- codec, cid, vid, &qos, capabilities,
- size, metadata, metadata_size,
- &err) == NULL) {
+ codec, cid, vid, &qos, capabilities,
+ size, metadata, metadata_size, broadcast,
+ &err) == NULL) {
if (err == -EPROTONOSUPPORT)
return btd_error_not_supported(msg);
else
@@ -2627,6 +2641,7 @@ static void app_register_endpoint(void *data, void *user_data)
int metadata_size = 0;
DBusMessageIter iter, array;
struct media_endpoint *endpoint;
+ bool broadcast = false;

if (app->err)
return;
@@ -2741,7 +2756,7 @@ static void app_register_endpoint(void *data, void *user_data)
vendor.cid, vendor.vid, &qos,
capabilities, size,
metadata, metadata_size,
- &app->err);
+ broadcast, &app->err);
if (!endpoint) {
error("Unable to register endpoint %s:%s: %s", app->sender,
path, strerror(-app->err));
@@ -3245,3 +3260,9 @@ struct btd_adapter *media_endpoint_get_btd_adapter(
{
return endpoint->adapter->btd_adapter;
}
+
+bool media_endpoint_is_broadcast(
+ struct media_endpoint *endpoint)
+{
+ return endpoint->broadcast;
+}
diff --git a/profiles/audio/media.h b/profiles/audio/media.h
index 1de84a8ff..2b579877b 100644
--- a/profiles/audio/media.h
+++ b/profiles/audio/media.h
@@ -22,5 +22,5 @@ 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);
-
+bool media_endpoint_is_broadcast(struct media_endpoint *endpoint);
int8_t media_player_get_device_volume(struct btd_device *device);
--
2.34.1


2023-07-12 09:06:37

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 6/6] bap: Add support for BAP broadcast sink

This adds support for BAP broadcast sink, adds a new gdbus method
to synchronize to a given source and sets the QOS and capabilities
of the stream.
This feature was tested using bluetoothctl with the following commands:

[bluetooth]# endpoint.register 00002bc9-0000-1000-8000-00805f9b34fb 0x06
[/local/endpoint/ep0] Auto Accept (yes/no): y
[/local/endpoint/ep0] Max Transports (auto/value): a
[/local/endpoint/ep0] unicast/broadcast (u/b): b
[/local/endpoint/ep0] BIG (auto/value): a
[/local/endpoint/ep0] BIS (auto/value): a

[bluetooth]# scan on

[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0
/local/endpoint/ep0 16_2_1 <source_address>

---
profiles/audio/bap.c | 347 ++++++++++++++++++++++++++++++++++++++++---
src/shared/bap.c | 50 ++++++-
src/shared/bap.h | 4 +-
3 files changed, 370 insertions(+), 31 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index d7ce9e038..5cb109950 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -34,6 +34,7 @@
#include "lib/hci.h"
#include "lib/sdp.h"
#include "lib/uuid.h"
+#include "lib/iso.h"

#include "src/btd.h"
#include "src/dbus-common.h"
@@ -185,6 +186,9 @@ static gboolean get_uuid(const GDBusPropertyTable *property,
uuid = PAC_SINK_UUID;
else if (queue_find(ep->data->srcs, NULL, ep))
uuid = PAC_SOURCE_UUID;
+ else if ((queue_find(ep->data->bcast, NULL, ep)
+ && (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)))
+ uuid = PAC_SINK_UUID;
else
uuid = BAA_SERVICE_UUID;

@@ -232,7 +236,8 @@ static gboolean get_device(const GDBusPropertyTable *property,
struct bap_ep *ep = data;
const char *path;

- if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
+ if ((bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
+ || (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK))
path = adapter_get_path(ep->data->adapter);
else
path = device_get_path(ep->data->device);
@@ -555,7 +560,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
}

if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
- /* Mark CIG and CIS to be auto assigned */
+ /* Mark BIG and BIS to be auto assigned */
ep->qos.bcast.big = BT_ISO_QOS_BIG_UNSET;
ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
} else {
@@ -576,8 +581,12 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
ep->stream = bt_bap_stream_new(ep->data->bap, ep->lpac,
ep->rpac, &ep->qos, ep->caps);

- ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps,
- config_cb, ep);
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos, NULL,
+ config_cb, ep);
+ else
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps,
+ config_cb, ep);
if (!ep->id) {
DBG("Unable to config stream");
free(ep->caps);
@@ -603,14 +612,296 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return NULL;
}

+static void update_bcast_qos(struct bt_iso_qos *qos,
+ struct bt_bap_qos *bap_qos)
+{
+ bap_qos->bcast.big = qos->bcast.big;
+ bap_qos->bcast.bis = qos->bcast.bis;
+ bap_qos->bcast.sync_interval = qos->bcast.sync_interval;
+ bap_qos->bcast.packing = qos->bcast.packing;
+ bap_qos->bcast.framing = qos->bcast.framing;
+ bap_qos->bcast.encryption = qos->bcast.encryption;
+ bap_qos->bcast.options = qos->bcast.options;
+ bap_qos->bcast.skip = qos->bcast.skip;
+ bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
+ bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
+ bap_qos->bcast.mse = qos->bcast.mse;
+ bap_qos->bcast.timeout = qos->bcast.timeout;
+ bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
+ bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
+ bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
+ bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
+ bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
+
+ bap_qos->bcast.bcode = new0(struct iovec, 1);
+ util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
+ sizeof(qos->bcast.bcode));
+}
+
+static bool match_bcast_stream_qos(const void *data, const void *user_data)
+{
+
+ const struct bt_bap_stream *stream = data;
+ const struct bt_iso_qos *iso_qos = user_data;
+ struct bt_bap_qos *qos;
+
+ qos = bt_bap_stream_get_qos((void *)stream);
+
+ if (iso_qos->bcast.big != qos->bcast.big)
+ return false;
+
+ return iso_qos->bcast.bis == qos->bcast.bis;
+}
+
+static void bap_add_stream(struct bap_data *data,
+ struct bt_bap_stream *stream)
+{
+ DBG("stream pointer %p", stream);
+
+ if (!data->streams)
+ data->streams = queue_new();
+
+ if (!queue_find(data->streams, NULL, stream))
+ queue_push_tail(data->streams, stream);
+}
+
+static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
+{
+ struct bap_data *data = user_data;
+ struct bt_bap_stream *stream;
+ struct bt_iso_qos qos;
+ struct bt_iso_base base;
+ char address[18];
+ struct queue *queue;
+ struct bap_ep *ep;
+ int fd;
+ struct iovec *base_io;
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_BASE, &base,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ g_io_channel_ref(io);
+
+ DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
+ address, qos.bcast.big, qos.bcast.bis);
+
+ queue = data->bcast;
+ ep = queue_pop_head(queue);
+ if (!ep) {
+ DBG("ep not found");
+ return;
+ }
+
+ update_bcast_qos(&qos, &ep->qos);
+
+ base_io = new0(struct iovec, 1);
+ util_iov_memcpy(base_io, base.base, base.base_len);
+
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos,
+ base_io, NULL, NULL);
+ data->listen_io = io;
+
+ bt_bap_stream_set_user_data(ep->stream, ep->path);
+
+ stream = queue_remove_if(data->streams, match_bcast_stream_qos, &qos);
+ if (!stream) {
+ DBG("stream removed");
+ bap_add_stream(data, ep->stream);
+ stream = ep->stream;
+ fd = g_io_channel_unix_get_fd(io);
+
+ if (bt_bap_stream_set_io(stream, fd)) {
+ bt_bap_stream_enable(stream, true, NULL, NULL, NULL);
+ g_io_channel_set_close_on_unref(io, FALSE);
+ return;
+ }
+ } else
+ DBG("stream not removed");
+
+ return;
+
+drop:
+ g_io_channel_shutdown(io, TRUE, NULL);
+
+}
+
+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;
+}
+
+#define DEFAULT_IO_QOS \
+{ \
+ .interval = 10000, \
+ .latency = 10, \
+ .sdu = 40, \
+ .phy = 0x02, \
+ .rtn = 2, \
+}
+
+static DBusMessage *bcast_sink_create(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct bt_iso_qos qos_bcast;
+ struct sockaddr_iso_bc iso_bc_addr;
+ DBusMessageIter iter, args;
+ const char *key;
+ GIOChannel *io;
+ GError *err = NULL;
+ struct bap_ep *ep = user_data;
+ uint32_t bcastid;
+
+ memset(&qos_bcast, 0, sizeof(struct bt_iso_qos));
+ memset(&iso_bc_addr, 0, sizeof(struct sockaddr_iso_bc));
+
+ DBG("sender %s", dbus_message_get_sender(msg));
+
+ dbus_message_iter_init(msg, &args);
+
+ if ((dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) ||
+ (dbus_message_iter_get_element_type(&args) != DBUS_TYPE_DICT_ENTRY))
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&args, &iter);
+
+ while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&iter, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+
+ if (!strcasecmp(key, "SourceAddress")) {
+ const char *str;
+
+ if (var != DBUS_TYPE_STRING)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &str);
+ DBG("SourceAddress %s", str);
+ str2ba(str, &iso_bc_addr.bc_bdaddr);
+ } else if (!strcasecmp(key, "SourceAddressType")) {
+ const char *type;
+
+ if (var != DBUS_TYPE_STRING)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &type);
+
+ if (!strcasecmp(type, "public"))
+ iso_bc_addr.bc_bdaddr_type = BDADDR_LE_PUBLIC;
+ else
+ iso_bc_addr.bc_bdaddr_type = BDADDR_LE_RANDOM;
+
+ DBG("SourceAddressType %d", iso_bc_addr.bc_bdaddr_type);
+ } else if (!strcasecmp(key, "BIS")) {
+ uint8_t bis;
+
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &bis);
+ iso_bc_addr.bc_bis[0] = bis;
+ DBG("BIS %d", iso_bc_addr.bc_bis[0]);
+ } else if (!strcasecmp(key, "NumBis")) {
+ if (var != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value,
+ &iso_bc_addr.bc_num_bis);
+ DBG("NumBis %d", iso_bc_addr.bc_num_bis);
+ } else if (!strcasecmp(key, "BcastID")) {
+ if (var != DBUS_TYPE_UINT32)
+ goto fail;
+
+ dbus_message_iter_get_basic(&value, &bcastid);
+ DBG("BcastID 0x%4.4x", bcastid);
+ }
+
+ dbus_message_iter_next(&iter);
+ }
+
+ qos_bcast.bcast.big = ep->qos.bcast.big;
+ qos_bcast.bcast.bis = ep->qos.bcast.bis;
+ qos_bcast.bcast.encryption = ep->qos.bcast.encryption;
+ qos_bcast.bcast.framing = ep->qos.bcast.framing;
+ qos_bcast.bcast.mse = ep->qos.bcast.mse;
+ qos_bcast.bcast.options = ep->qos.bcast.options;
+ qos_bcast.bcast.packing = ep->qos.bcast.packing;
+ qos_bcast.bcast.skip = ep->qos.bcast.skip;
+ qos_bcast.bcast.sync_cte_type = ep->qos.bcast.sync_cte_type;
+ qos_bcast.bcast.sync_interval = ep->qos.bcast.sync_interval;
+ qos_bcast.bcast.sync_timeout = ep->qos.bcast.sync_timeout;
+ qos_bcast.bcast.timeout = ep->qos.bcast.timeout;
+ qos_bcast.bcast.in.interval = ep->qos.bcast.io_qos.interval;
+ qos_bcast.bcast.in.phy = ep->qos.bcast.io_qos.phy;
+ qos_bcast.bcast.in.sdu = ep->qos.bcast.io_qos.sdu;
+ qos_bcast.bcast.in.latency = ep->qos.bcast.io_qos.latency;
+ qos_bcast.bcast.in.rtn = ep->qos.bcast.io_qos.rtn;
+
+ io = bt_io_listen(iso_bcast_confirm_cb, NULL, ep->data, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(ep->data->adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ &iso_bc_addr.bc_bdaddr,
+ BT_IO_OPT_DEST_TYPE,
+ iso_bc_addr.bc_bdaddr_type,
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &qos_bcast,
+ BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
+ BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return dbus_message_new_method_return(msg);
+ }
+
+ ep->data->listen_io = io;
+ return dbus_message_new_method_return(msg);
+
+fail:
+ DBG("Failed parsing %s", key);
+ return dbus_message_new_method_return(msg);
+
+}
+
static const GDBusMethodTable ep_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
GDBUS_ARGS({ "endpoint", "o" },
- { "properties", "a{sv}" } ),
+ { "properties", "a{sv}" }),
NULL, set_configuration) },
{ },
};

+static const GDBusMethodTable bap_bcast_ep_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
+ GDBUS_ARGS({ "endpoint", "o" },
+ { "properties", "a{sv}" }),
+ NULL, set_configuration) },
+
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("BcastSinkCreate",
+ GDBUS_ARGS({ "properties", "a{sv}" }),
+ NULL, bcast_sink_create) },
+ { },
+};
+
static void ep_free(void *data)
{
struct bap_ep *ep = data;
@@ -657,6 +948,7 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,

switch (bt_bap_pac_get_type(rpac)) {
case BT_BAP_BCAST_SOURCE:
+ case BT_BAP_BCAST_SINK:
queue = data->bcast;
i = queue_length(data->bcast);
suffix = "bcast";
@@ -683,15 +975,24 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
return NULL;
}

- if (g_dbus_register_interface(btd_get_dbus_connection(),
+ if (bt_bap_pac_get_type(rpac) == BT_BAP_BCAST_SOURCE) {
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ ep->path, MEDIA_ENDPOINT_INTERFACE,
+ bap_bcast_ep_methods, NULL, NULL,
+ ep, ep_free) == FALSE) {
+ error("Could not register bap bcast interface %s",
+ ep->path);
+ }
+ } else {
+ 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;
+ 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);
@@ -724,6 +1025,12 @@ static struct bap_ep *ep_register(struct btd_service *service,
i = queue_length(data->srcs);
suffix = "source";
break;
+ case BT_BAP_BCAST_SOURCE:
+ case BT_BAP_BCAST_SINK:
+ queue = data->bcast;
+ i = queue_length(data->bcast);
+ suffix = "bcast";
+ break;
default:
return NULL;
}
@@ -823,6 +1130,7 @@ done:

queue_foreach(ep->data->srcs, bap_config, NULL);
queue_foreach(ep->data->snks, bap_config, NULL);
+ queue_foreach(ep->data->bcast, bap_config, NULL);
}

static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
@@ -1416,6 +1724,10 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
break;
case BT_BAP_STREAM_STATE_CONFIG:
if (ep && !ep->id) {
+ /* For BAP Sink create io when receiving source info */
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
+ return;
+
bap_create_io(data, ep, stream, true);
if (!ep->io) {
error("Unable to create io");
@@ -1423,7 +1735,6 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
return;
}

-
if (bt_bap_stream_get_type(stream) ==
BT_BAP_STREAM_TYPE_UCAST) {
/* Wait QoS response to respond */
@@ -1479,7 +1790,9 @@ static void pac_added_broadcast(struct bt_bap_pac *pac, void *user_data)

bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SOURCE,
pac_found_bcast, data);
- }
+ } else if (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SINK)
+ bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SINK,
+ pac_found_bcast, data);
}

static bool ep_match_pac(const void *data, const void *match_data)
@@ -1595,14 +1908,6 @@ 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 bool io_get_qos(GIOChannel *io, struct bt_iso_qos *qos)
{
GError *err = NULL;
@@ -1853,7 +2158,7 @@ static int bap_adapter_probe(struct btd_profile *p,

bap_data_add(data);

- if (!bt_bap_attach_broadcast(data->bap)) {
+ if (!bt_bap_attach_broadcast(data->bap, BT_BAP_BCAST_SOURCE)) {
error("BAP unable to attach");
return -EINVAL;
}
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 72ce67c08..1f6527b98 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -633,14 +633,15 @@ 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_broacast(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;
+ ep->dir = type;

return ep;
}
@@ -668,7 +669,7 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints,
}

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;

@@ -682,7 +683,7 @@ static struct bt_bap_endpoint *bap_get_endpoint_bcast(struct queue *endpoints,
if (queue_length(endpoints) > 0)
return queue_peek_head(endpoints);

- ep = bap_endpoint_new_broacast(db);
+ ep = bap_endpoint_new_broacast(db, type);
if (!ep)
return NULL;

@@ -1348,6 +1349,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;

@@ -2518,7 +2522,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_brodcast_sink, *pac_brodcast_source;
struct bt_bap_codec codec;

if (!db)
@@ -2551,6 +2555,13 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
data, metadata);
bap_add_broadcast_sink(pac_brodcast_sink);
break;
+ case BT_BAP_BCAST_SINK:
+ bap_add_broadcast_sink(pac);
+ pac_brodcast_source = bap_pac_new(bdb, name,
+ BT_BAP_BCAST_SOURCE,
+ &codec, qos, data, metadata);
+ bap_add_broadcast_source(pac_brodcast_source);
+ break;
default:
bap_pac_free(pac);
return NULL;
@@ -3996,7 +4007,7 @@ clone:
return true;
}

-bool bt_bap_attach_broadcast(struct bt_bap *bap)
+bool bt_bap_attach_broadcast(struct bt_bap *bap, uint8_t type)
{
struct bt_bap_endpoint *ep;

@@ -4008,7 +4019,7 @@ 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, type);
if (ep)
ep->bap = bap;

@@ -4224,6 +4235,10 @@ void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type,
return bap_foreach_pac(bap->ldb->broadcast_sources,
bap->ldb->broadcast_sinks,
func, user_data);
+ case BT_BAP_BCAST_SINK:
+ return bap_foreach_pac(bap->ldb->broadcast_sinks,
+ bap->ldb->broadcast_sources,
+ func, user_data);
}
}

@@ -4382,6 +4397,12 @@ 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;
}

@@ -4446,13 +4467,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 +4940,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;
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 50b567663..297ecf0e5 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -186,7 +186,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);
+bool bt_bap_attach_broadcast(struct bt_bap *bap, uint8_t type);
void bt_bap_detach(struct bt_bap *bap);

bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t cb,
@@ -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);
--
2.34.1


2023-07-12 09:06:39

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ 5/6] btio: Add support for getsockopt(BT_ISO_BASE)

This adds the posibility for a broadcast sink to retrieve the
BASE information received from a source afeter a PA synchronization,
using the getsockopt(BT_ISO_BASE) function.
This needs the patch from bluetooth-next:
Bluetooth: ISO: Add support for periodic adv reports processing

---
btio/btio.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index 179be6289..8178250d2 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -1638,6 +1638,7 @@ static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args)
BtIOOption opt = opt1;
struct sockaddr_iso src, dst;
struct bt_iso_qos qos;
+ struct bt_iso_base base;
socklen_t len;
uint32_t phy;

@@ -1648,6 +1649,11 @@ static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args)
return FALSE;
}

+ if (getsockopt(sock, SOL_BLUETOOTH, BT_ISO_BASE, &base, &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(BT_ISO_BASE)", errno);
+ return FALSE;
+ }
+
if (!get_src(sock, &src, sizeof(src), err))
return FALSE;

@@ -1694,6 +1700,8 @@ static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args)
*(va_arg(args, struct bt_iso_qos *)) = qos;
break;
case BT_IO_OPT_BASE:
+ *(va_arg(args, struct bt_iso_base *)) = base;
+ break;
case BT_IO_OPT_HANDLE:
case BT_IO_OPT_CLASS:
case BT_IO_OPT_DEFER_TIMEOUT:
@@ -1896,8 +1904,9 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts,
goto failed;
if (!iso_set_qos(sock, &opts->qos, err))
goto failed;
- if (!iso_set_base(sock, &opts->base, err))
- goto failed;
+ if (opts->base.base_len)
+ if (!iso_set_base(sock, &opts->base, err))
+ goto failed;
break;
case BT_IO_INVALID:
default:
--
2.34.1


2023-07-12 11:39:23

by bluez.test.bot

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

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=764738

---Test result---

Test Summary:
CheckPatch PASS 4.18 seconds
GitLint FAIL 2.34 seconds
BuildEll PASS 27.10 seconds
BluezMake PASS 993.03 seconds
MakeCheck PASS 12.77 seconds
MakeDistcheck PASS 158.86 seconds
CheckValgrind PASS 263.06 seconds
CheckSmatch PASS 349.14 seconds
bluezmakeextell PASS 106.30 seconds
IncrementalBuild PASS 4981.09 seconds
ScanBuild PASS 1040.99 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,1/6] client/player: Add broadcast sink endpoint registration and configuration.

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
1: T1 Title exceeds max length (86>80): "[BlueZ,1/6] client/player: Add broadcast sink endpoint registration and configuration."
1: T3 Title has trailing punctuation (.): "[BlueZ,1/6] client/player: Add broadcast sink endpoint registration and configuration."
20: B2 Line has trailing whitespace: "[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0 "
[BlueZ,2/6] client/main: Add broadcast source discovery

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
3: B2 Line has trailing whitespace: "This checks if the scanned device advertises the "
19: B2 Line has trailing whitespace: "[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0 "
[BlueZ,3/6] media: Add support for a broadcast sink media endpoint

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
3: B2 Line has trailing whitespace: "This patch adds the possibility to register a broadcast sink "
[BlueZ,6/6] bap: Add support for BAP broadcast sink

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
4: B2 Line has trailing whitespace: "to synchronize to a given source and sets the QOS and capabilities "
17: B2 Line has trailing whitespace: "[bluetooth]# endpoint.config /org/bluez/hci0/pac_bcast0 "


---
Regards,
Linux Bluetooth