Diff between 88bf423eb525655e15890bdca84d9acb5afab122 and 77e4c0976c0d342e45f0ad1b485efd7e60863e30

Changed Files

File Additions Deletions Status
Makefile.plugins +1 -1 modified
profiles/audio/bass.c +257 -0 modified
profiles/audio/bass.h +13 -0 added

Full Patch

diff --git a/Makefile.plugins b/Makefile.plugins
index 9dd8134..9da29a3 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -120,7 +120,7 @@ endif
 
 if BASS
 builtin_modules += bass
-builtin_sources += profiles/audio/bass.c
+builtin_sources += profiles/audio/bass.h profiles/audio/bass.c
 endif
 
 if MCP
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index 7952105..0839883 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -39,6 +39,7 @@
 #include "src/shared/gatt-server.h"
 #include "src/adapter.h"
 #include "src/shared/bass.h"
+#include "src/shared/bap.h"
 
 #include "src/plugin.h"
 #include "src/gatt-database.h"
@@ -48,21 +49,265 @@
 #include "src/log.h"
 #include "src/error.h"
 
+#include "bass.h"
+#include "bap.h"
+
 #define BASS_UUID_STR "0000184f-0000-1000-8000-00805f9b34fb"
 
+#define MEDIA_ASSISTANT_INTERFACE "org.bluez.MediaAssistant1"
+
+enum assistant_state {
+	ASSISTANT_STATE_IDLE,		/* Assistant object was created for
+					 * the stream
+					 */
+	ASSISTANT_STATE_PENDING,	/* Assistant object was pushed */
+	ASSISTANT_STATE_REQUESTING,	/* Remote device requires
+					 * Broadcast_Code
+					 */
+	ASSISTANT_STATE_ACTIVE,		/* Remote device started receiving
+					 * stream
+					 */
+};
+
 struct bass_data {
 	struct btd_device *device;
 	struct btd_service *service;
 	struct bt_bass *bass;
 };
 
+struct bass_assistant {
+	struct btd_device *device;	/* Broadcast source device */
+	struct bass_data *data;		/* BASS session with peer device */
+	uint8_t sgrp;
+	uint8_t bis;
+	struct bt_iso_qos qos;
+	struct iovec *meta;
+	struct iovec *caps;
+	enum assistant_state state;
+	char *path;
+};
+
 static struct queue *sessions;
+static struct queue *assistants;
 
 static void bass_debug(const char *str, void *user_data)
 {
 	DBG_IDX(0xffff, "%s", str);
 }
 
+static DBusMessage *push(DBusConnection *conn, DBusMessage *msg,
+							  void *user_data)
+{
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable assistant_methods[] = {
+	{GDBUS_EXPERIMENTAL_ASYNC_METHOD("Push",
+					GDBUS_ARGS({ "Props", "a{sv}" }),
+					NULL, push)},
+	{},
+};
+
+static const char *state2str(enum assistant_state state)
+{
+	switch (state) {
+	case ASSISTANT_STATE_IDLE:
+		return "idle";
+	case ASSISTANT_STATE_PENDING:
+		return "pending";
+	case ASSISTANT_STATE_REQUESTING:
+		return "requesting";
+	case ASSISTANT_STATE_ACTIVE:
+		return "active";
+	}
+
+	return NULL;
+}
+
+static gboolean get_state(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct bass_assistant *assistant = data;
+	const char *state = state2str(assistant->state);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state);
+
+	return TRUE;
+}
+
+static gboolean get_metadata(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct bass_assistant *assistant = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+
+	if (assistant->meta)
+		dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&assistant->meta->iov_base,
+						assistant->meta->iov_len);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean get_qos(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct bass_assistant *assistant = data;
+	DBusMessageIter dict;
+	uint8_t *bcode = assistant->qos.bcast.bcode;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE,
+				&assistant->qos.bcast.encryption);
+	dict_append_array(&dict, "BCode", DBUS_TYPE_BYTE,
+				&bcode, BT_BASS_BCAST_CODE_SIZE);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable assistant_properties[] = {
+	{ "State", "s", get_state },
+	{ "Metadata", "ay", get_metadata, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "QoS", "a{sv}", get_qos, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static void assistant_free(void *data)
+{
+	struct bass_assistant *assistant = data;
+
+	g_free(assistant->path);
+	util_iov_free(assistant->meta, 1);
+	util_iov_free(assistant->caps, 1);
+
+	free(assistant);
+}
+
+static struct bass_assistant *assistant_new(struct btd_adapter *adapter,
+		struct btd_device *device, struct bass_data *data,
+		uint8_t sgrp, uint8_t bis, struct bt_iso_qos *qos,
+		struct iovec *meta, struct iovec *caps)
+{
+	struct bass_assistant *assistant;
+	char src_addr[18];
+	char dev_addr[18];
+
+	assistant = new0(struct bass_assistant, 1);
+	if (!assistant)
+		return NULL;
+
+	DBG("assistant %p", assistant);
+
+	assistant->device = device;
+	assistant->data = data;
+	assistant->sgrp = sgrp;
+	assistant->bis = bis;
+	assistant->qos = *qos;
+	assistant->meta = util_iov_dup(meta, 1);
+	assistant->caps = util_iov_dup(caps, 1);
+
+	ba2str(device_get_address(device), src_addr);
+	ba2str(device_get_address(data->device), dev_addr);
+
+	assistant->path = g_strdup_printf("%s/src_%s/dev_%s/bis%d",
+		adapter_get_path(adapter), src_addr, dev_addr, bis);
+
+	g_strdelimit(assistant->path, ":", '_');
+
+	if (!assistants)
+		assistants = queue_new();
+
+	queue_push_tail(assistants, assistant);
+
+	return assistant;
+}
+
+void bass_add_stream(struct btd_device *device, struct iovec *meta,
+			struct iovec *caps, struct bt_iso_qos *qos,
+			uint8_t sgrp, uint8_t bis)
+{
+	const struct queue_entry *entry;
+	struct bt_bap *bap;
+	struct bt_bap_pac *pac;
+	struct bass_assistant *assistant;
+	char addr[18];
+
+	for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
+		struct bass_data *data = entry->data;
+		struct btd_adapter *adapter = device_get_adapter(data->device);
+
+		if (!bt_bass_get_client(data->bass))
+			/* Only client sessions must be handled */
+			continue;
+
+		bap = bap_get_session(data->device);
+		if (!bap)
+			continue;
+
+		/* Check stream capabilities against peer caps. */
+		bt_bap_verify_bis(bap, bis, caps, &pac);
+
+		if (!pac)
+			/* Capabilities did not match. */
+			continue;
+
+		ba2str(device_get_address(device), addr);
+
+		DBG("%s data %p BIS %d", addr, data, bis);
+
+		assistant = assistant_new(adapter, device, data, sgrp,
+							bis, qos, meta, caps);
+
+		if (g_dbus_register_interface(btd_get_dbus_connection(),
+						assistant->path,
+						MEDIA_ASSISTANT_INTERFACE,
+						assistant_methods, NULL,
+						assistant_properties,
+						assistant,
+						assistant_free) == FALSE)
+			DBG("Could not register path %s", assistant->path);
+	}
+}
+
+static bool assistant_match_device(const void *data, const void *match_data)
+{
+	const struct bass_assistant *assistant = data;
+	const struct btd_device *device = match_data;
+
+	return (assistant->device == device);
+}
+
+static void unregister_assistant(void *data)
+{
+	struct bass_assistant *assistant = data;
+
+	DBG("%p", assistant);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				assistant->path, MEDIA_ASSISTANT_INTERFACE);
+}
+
+void bass_remove_stream(struct btd_device *device)
+{
+	queue_remove_all(assistants, assistant_match_device,
+		device, unregister_assistant);
+}
+
 static struct bass_data *bass_data_new(struct btd_device *device)
 {
 	struct bass_data *data;
@@ -101,6 +346,14 @@ static bool match_data(const void *data, const void *match_data)
 	return bdata->bass == bass;
 }
 
+static bool assistant_match_data(const void *data, const void *match_data)
+{
+	const struct bass_assistant *assistant = data;
+	const struct bass_data *bdata = match_data;
+
+	return (assistant->data == bdata);
+}
+
 static void bass_data_free(struct bass_data *data)
 {
 	if (data->service) {
@@ -109,6 +362,10 @@ static void bass_data_free(struct bass_data *data)
 	}
 
 	bt_bass_unref(data->bass);
+
+	queue_remove_all(assistants, assistant_match_data,
+		data, unregister_assistant);
+
 	free(data);
 }
 
diff --git a/profiles/audio/bass.h b/profiles/audio/bass.h
new file mode 100644
index 0000000..5bef929
--- /dev/null
+++ b/profiles/audio/bass.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright 2024 NXP
+ *
+ */
+
+void bass_add_stream(struct btd_device *device, struct iovec *meta,
+			struct iovec *caps, struct bt_iso_qos *qos,
+			uint8_t sgrp, uint8_t bis);
+void bass_remove_stream(struct btd_device *device);