diff mbox series

[BlueZ,08/10] bass: Register MediaAssistant objects

Message ID 20240716142207.4298-9-iulia.tanasescu@nxp.com
State New
Headers show
Series Initial implementation of BAP Broadcast Assistant | expand

Commit Message

Iulia Tanasescu July 16, 2024, 2:22 p.m. UTC
This adds an initial implementation of the BAP Broadcast Assistant role
in the BASS plugin, by introducing the MediaAssistant DBus object.

The BAP plugin implements the callback to probe Broadcast Sources and
parse the BASE. This commit adds 2 BASS APIs, that will be called by the
BAP plugin to notify BISes discovered in the BASE of a broadcaster to
BASS, or to inform the BASS plugin that a broadcaster has been removed.

For each BASS client session, the BASS plugin checks BIS caps against
the peer caps, and registers a MediaAssistant object for each match.
---
 Makefile.plugins      |   2 +-
 profiles/audio/bass.c | 257 ++++++++++++++++++++++++++++++++++++++++++
 profiles/audio/bass.h |  13 +++
 3 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 profiles/audio/bass.h
diff mbox series

Patch

diff --git a/Makefile.plugins b/Makefile.plugins
index 9dd8134b4..9da29a3ce 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 7952105c5..083988358 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 000000000..5bef92946
--- /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);