2023-06-29 08:22:09

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ v3 0/2] shared/bass: Introduce Add Source opcode handler

This patch series adds the BASS control point handler
for the Add Source operation.

If instructed by a Client through the Add Source opcode,
the BASS Server attempts to synchronize to a Broadcast
Source, by opening a btio channel.

Some additional btio options have been added,
to allow binding a socket to a broadcast address.

The BASS adapter_probe callback has been implemented,
in order to automatically configure BASS into the adapter
database. The adapter bdaddr is also stored along with the
BASS database configuration, and this address will be used
as the source address when binding the btio channel.

This patch version fixes the compilation errors from
the previous version.

Iulia Tanasescu (2):
btio: Add option for binding iso broadcast address
shared/bass: Introduce Add Source opcode handler

Makefile.am | 2 +-
btio/btio.c | 76 +++++++-
btio/btio.h | 5 +-
profiles/audio/bass.c | 26 ++-
src/shared/bass.c | 427 ++++++++++++++++++++++++++++++++++++++++--
src/shared/bass.h | 14 +-
unit/test-bass.c | 2 +-
7 files changed, 523 insertions(+), 29 deletions(-)


base-commit: d6bfbd28420edf91382635b229b9f8b2f94dc060
--
2.34.1



2023-06-29 08:22:11

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ v3 1/2] btio: Add options for binding iso broadcast address

This adds additional btio options, to allow binding a socket
to a broadcaster address.
---
btio/btio.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++-------
btio/btio.h | 5 +++-
2 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index b68bfb14c..179be6289 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -16,6 +16,7 @@

#include <stdarg.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
@@ -71,6 +72,9 @@ struct set_opts {
uint16_t voice;
struct bt_iso_qos qos;
struct bt_iso_base base;
+ uint8_t bc_sid;
+ uint8_t bc_num_bis;
+ uint8_t bc_bis[ISO_MAX_NUM_BIS];
};

struct connect {
@@ -771,21 +775,47 @@ static int sco_bind(int sock, const bdaddr_t *src, GError **err)
}

static int iso_bind(int sock, const bdaddr_t *src, uint8_t src_type,
- GError **err)
+ const bdaddr_t *dst, uint8_t dst_type,
+ uint8_t bc_sid, uint8_t num_bis,
+ uint8_t *bis, GError **err)
{
- struct sockaddr_iso addr;
+ struct sockaddr_iso *addr = NULL;
+ size_t addr_len;
+ int ret = 0;

- memset(&addr, 0, sizeof(addr));
- addr.iso_family = AF_BLUETOOTH;
- bacpy(&addr.iso_bdaddr, src);
- addr.iso_bdaddr_type = src_type;
+ if (num_bis)
+ addr_len = sizeof(*addr) + sizeof(*addr->iso_bc);
+ else
+ addr_len = sizeof(*addr);
+
+ addr = malloc(addr_len);
+
+ if (!addr)
+ return -ENOMEM;
+
+ memset(addr, 0, addr_len);
+ addr->iso_family = AF_BLUETOOTH;
+ bacpy(&addr->iso_bdaddr, src);
+ addr->iso_bdaddr_type = src_type;

- if (!bind(sock, (struct sockaddr *) &addr, sizeof(addr)))
- return 0;
+ if (num_bis) {
+ bacpy(&addr->iso_bc->bc_bdaddr, dst);
+ addr->iso_bc->bc_bdaddr_type = dst_type;
+ addr->iso_bc->bc_sid = bc_sid;
+ addr->iso_bc->bc_num_bis = num_bis;
+ memcpy(addr->iso_bc->bc_bis, bis,
+ addr->iso_bc->bc_num_bis);
+ }
+
+ if (!bind(sock, (struct sockaddr *)addr, addr_len))
+ goto done;

+ ret = -errno;
ERROR_FAILED(err, "iso_bind", errno);

- return -errno;
+done:
+ free(addr);
+ return ret;
}

static int sco_connect(int sock, const bdaddr_t *dst)
@@ -980,6 +1010,16 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err,
case BT_IO_OPT_BASE:
opts->base = *va_arg(args, struct bt_iso_base *);
break;
+ case BT_IO_OPT_ISO_BC_SID:
+ opts->bc_sid = va_arg(args, int);
+ break;
+ case BT_IO_OPT_ISO_BC_NUM_BIS:
+ opts->bc_num_bis = va_arg(args, int);
+ break;
+ case BT_IO_OPT_ISO_BC_BIS:
+ memcpy(opts->bc_bis, va_arg(args, uint8_t *),
+ opts->bc_num_bis);
+ break;
case BT_IO_OPT_INVALID:
case BT_IO_OPT_KEY_SIZE:
case BT_IO_OPT_SOURCE_CHANNEL:
@@ -1305,6 +1345,9 @@ parse_opts:
case BT_IO_OPT_VOICE:
case BT_IO_OPT_QOS:
case BT_IO_OPT_BASE:
+ case BT_IO_OPT_ISO_BC_SID:
+ case BT_IO_OPT_ISO_BC_NUM_BIS:
+ case BT_IO_OPT_ISO_BC_BIS:
default:
g_set_error(err, BT_IO_ERROR, EINVAL,
"Unknown option %d", opt);
@@ -1460,6 +1503,9 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
case BT_IO_OPT_VOICE:
case BT_IO_OPT_QOS:
case BT_IO_OPT_BASE:
+ case BT_IO_OPT_ISO_BC_SID:
+ case BT_IO_OPT_ISO_BC_NUM_BIS:
+ case BT_IO_OPT_ISO_BC_BIS:
case BT_IO_OPT_INVALID:
default:
g_set_error(err, BT_IO_ERROR, EINVAL,
@@ -1571,6 +1617,9 @@ static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
case BT_IO_OPT_VOICE:
case BT_IO_OPT_QOS:
case BT_IO_OPT_BASE:
+ case BT_IO_OPT_ISO_BC_SID:
+ case BT_IO_OPT_ISO_BC_NUM_BIS:
+ case BT_IO_OPT_ISO_BC_BIS:
case BT_IO_OPT_INVALID:
default:
g_set_error(err, BT_IO_ERROR, EINVAL,
@@ -1660,6 +1709,9 @@ static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args)
case BT_IO_OPT_FLUSHABLE:
case BT_IO_OPT_PRIORITY:
case BT_IO_OPT_VOICE:
+ case BT_IO_OPT_ISO_BC_SID:
+ case BT_IO_OPT_ISO_BC_NUM_BIS:
+ case BT_IO_OPT_ISO_BC_BIS:
case BT_IO_OPT_INVALID:
default:
g_set_error(err, BT_IO_ERROR, EINVAL,
@@ -1836,7 +1888,11 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts,
ERROR_FAILED(err, "socket(SEQPACKET, ISO)", errno);
return NULL;
}
- if (iso_bind(sock, &opts->src, opts->src_type, err) < 0)
+
+ if (iso_bind(sock, &opts->src, opts->src_type,
+ &opts->dst, opts->dst_type,
+ opts->bc_sid, opts->bc_num_bis,
+ opts->bc_bis, err) < 0)
goto failed;
if (!iso_set_qos(sock, &opts->qos, err))
goto failed;
diff --git a/btio/btio.h b/btio/btio.h
index e9a8a01a3..642af2e22 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -46,7 +46,10 @@ typedef enum {
BT_IO_OPT_VOICE,
BT_IO_OPT_PHY,
BT_IO_OPT_QOS,
- BT_IO_OPT_BASE
+ BT_IO_OPT_BASE,
+ BT_IO_OPT_ISO_BC_SID,
+ BT_IO_OPT_ISO_BC_NUM_BIS,
+ BT_IO_OPT_ISO_BC_BIS,
} BtIOOption;

typedef enum {
--
2.34.1


2023-06-29 08:28:11

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ v3 2/2] shared/bass: Introduce Add Source opcode handler

This implements the Control Point handler for the Add Source operation.
---
Makefile.am | 2 +-
profiles/audio/bass.c | 26 ++-
src/shared/bass.c | 427 ++++++++++++++++++++++++++++++++++++++++--
src/shared/bass.h | 14 +-
unit/test-bass.c | 2 +-
5 files changed, 453 insertions(+), 18 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 2bd547564..98eb185fc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -580,7 +580,7 @@ unit_test_bap_LDADD = src/libshared-glib.la \

unit_tests += unit/test-bass

-unit_test_bass_SOURCES = unit/test-bass.c
+unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources)
unit_test_bass_LDADD = src/libshared-glib.la \
lib/libbluetooth-internal.la $(GLIB_LIBS)

diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index fae7fe004..7952105c5 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -37,10 +37,10 @@
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
#include "src/shared/gatt-server.h"
+#include "src/adapter.h"
#include "src/shared/bass.h"

#include "src/plugin.h"
-#include "src/adapter.h"
#include "src/gatt-database.h"
#include "src/device.h"
#include "src/profile.h"
@@ -197,7 +197,8 @@ static int bass_probe(struct btd_service *service)
data->service = service;

data->bass = bt_bass_new(btd_gatt_database_get_db(database),
- btd_device_get_gatt_db(device));
+ btd_device_get_gatt_db(device),
+ btd_adapter_get_address(adapter));
if (!data->bass) {
error("Unable to create BASS instance");
free(data);
@@ -268,6 +269,25 @@ static int bass_disconnect(struct btd_service *service)
return 0;
}

+static int bass_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+
+ DBG("BASS path %s", adapter_get_path(adapter));
+
+ bt_bass_add_db(btd_gatt_database_get_db(database),
+ btd_adapter_get_address(adapter));
+
+ return 0;
+}
+
+static void bass_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ DBG("BASS remove Adapter");
+}
+
static struct btd_profile bass_service = {
.name = "bass",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
@@ -276,6 +296,8 @@ static struct btd_profile bass_service = {
.device_remove = bass_remove,
.accept = bass_accept,
.disconnect = bass_disconnect,
+ .adapter_probe = bass_server_probe,
+ .adapter_remove = bass_server_remove,
.experimental = true,
};

diff --git a/src/shared/bass.c b/src/shared/bass.c
index 423ab5bf7..1bb40877f 100644
--- a/src/shared/bass.c
+++ b/src/shared/bass.c
@@ -14,9 +14,13 @@
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
+#include <poll.h>

#include "lib/bluetooth.h"
#include "lib/uuid.h"
+#include "lib/iso.h"
+
+#include "btio/btio.h"

#include "src/shared/queue.h"
#include "src/shared/util.h"
@@ -25,6 +29,8 @@
#include "src/shared/gatt-client.h"
#include "src/shared/bass.h"

+#define MAX_BIS_BITMASK_IDX 31
+
#define DBG(_bass, fmt, arg...) \
bass_debug(_bass, "%s:%s() " fmt, __FILE__, __func__, ## arg)

@@ -45,6 +51,7 @@ struct bt_bcast_recv_state {

struct bt_bass_db {
struct gatt_db *db;
+ bdaddr_t adapter_bdaddr;
struct queue *bcast_srcs;
struct gatt_db_attribute *service;
struct gatt_db_attribute *bcast_audio_scan_cp;
@@ -82,6 +89,35 @@ static struct queue *bass_db;
static struct queue *bass_cbs;
static struct queue *sessions;

+#define DEFAULT_IO_QOS \
+{ \
+ .interval = 10000, \
+ .latency = 10, \
+ .sdu = 40, \
+ .phy = 0x02, \
+ .rtn = 2, \
+}
+
+static struct bt_iso_qos default_qos = {
+ .bcast = {
+ .big = BT_ISO_QOS_BIG_UNSET,
+ .bis = BT_ISO_QOS_BIS_UNSET,
+ .sync_interval = 0x07,
+ .packing = 0x00,
+ .framing = 0x00,
+ .in = DEFAULT_IO_QOS,
+ .out = DEFAULT_IO_QOS,
+ .encryption = 0x00,
+ .bcode = {0x00},
+ .options = 0x00,
+ .skip = 0x0000,
+ .sync_timeout = 0x4000,
+ .sync_cte_type = 0x00,
+ .mse = 0x00,
+ .timeout = 0x4000,
+ }
+};
+
static void bass_bcast_src_free(void *data);

static void bass_debug(struct bt_bass *bass, const char *format, ...)
@@ -461,7 +497,7 @@ static bool bass_check_cp_command_len(const uint8_t *value, size_t len)
return true;
}

-static void bass_handle_remote_scan_stopped_op(struct bt_bass_db *bdb,
+static void bass_handle_remote_scan_stopped_op(struct bt_bass *bass,
struct gatt_db_attribute *attrib,
uint8_t opcode,
unsigned int id,
@@ -472,7 +508,7 @@ static void bass_handle_remote_scan_stopped_op(struct bt_bass_db *bdb,
gatt_db_attribute_write_result(attrib, id, 0x00);
}

-static void bass_handle_remote_scan_started_op(struct bt_bass_db *bdb,
+static void bass_handle_remote_scan_started_op(struct bt_bass *bass,
struct gatt_db_attribute *attrib,
uint8_t opcode,
unsigned int id,
@@ -491,7 +527,7 @@ static bool bass_src_id_match(const void *data, const void *match_data)
return (bcast_src->id == *id);
}

-static void bass_handle_remove_src_op(struct bt_bass_db *bdb,
+static void bass_handle_remove_src_op(struct bt_bass *bass,
struct gatt_db_attribute *attrib,
uint8_t opcode,
unsigned int id,
@@ -504,7 +540,7 @@ static void bass_handle_remove_src_op(struct bt_bass_db *bdb,
/* Get Remove Source command parameters */
params = util_iov_pull_mem(iov, sizeof(*params));

- bcast_src = queue_find(bdb->bcast_srcs,
+ bcast_src = queue_find(bass->ldb->bcast_srcs,
bass_src_id_match,
&params->id);

@@ -531,7 +567,7 @@ static void bass_handle_remove_src_op(struct bt_bass_db *bdb,
return;

/* Accept the operation and remove source */
- queue_remove(bdb->bcast_srcs, bcast_src);
+ queue_remove(bass->ldb->bcast_srcs, bcast_src);
gatt_db_attribute_notify(bcast_src->attr, NULL, 0, att);
bass_bcast_src_free(bcast_src);

@@ -539,6 +575,338 @@ static void bass_handle_remove_src_op(struct bt_bass_db *bdb,
gatt_db_attribute_write_result(attrib, id, 0x00);
}

+static bool bass_src_attr_match(const void *data, const void *match_data)
+{
+ const struct bt_bcast_src *bcast_src = data;
+ const struct gatt_db_attribute *attr = match_data;
+
+ return (bcast_src->attr == attr);
+}
+
+static gboolean check_io_err(GIOChannel *io)
+{
+ struct pollfd fds;
+
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = g_io_channel_unix_get_fd(io);
+ fds.events = POLLERR;
+
+ if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLERR))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void bass_bis_unref(void *data)
+{
+ GIOChannel *io = data;
+
+ g_io_channel_unref(io);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr,
+ gpointer user_data)
+{
+ struct bt_bcast_src *bcast_src = user_data;
+ uint8_t *notify_data;
+ size_t notify_data_len;
+ int bis_idx;
+ int i;
+
+ if (bcast_src->sync_state == BT_BASS_NOT_SYNCHRONIZED_TO_PA)
+ bcast_src->sync_state = BT_BASS_SYNCHRONIZED_TO_PA;
+
+ /* Keep io reference */
+ g_io_channel_ref(io);
+ queue_push_tail(bcast_src->bises, io);
+
+ for (i = 0; i < bcast_src->num_subgroups; i++) {
+ struct bt_bass_subgroup_data *data =
+ &bcast_src->subgroup_data[i];
+
+ for (bis_idx = 0; bis_idx < MAX_BIS_BITMASK_IDX; bis_idx++) {
+ if (data->pending_bis_sync & (1 << bis_idx)) {
+ data->bis_sync |= (1 << bis_idx);
+ data->pending_bis_sync &= ~(1 << bis_idx);
+ break;
+ }
+ }
+
+ if (bis_idx < MAX_BIS_BITMASK_IDX)
+ break;
+ }
+
+ for (i = 0; i < bcast_src->num_subgroups; i++) {
+ if (bcast_src->subgroup_data[i].pending_bis_sync)
+ break;
+ }
+
+ /* If there are still pending bises, wait for their
+ * notifications also before sending notification to
+ * client
+ */
+ if (i != bcast_src->num_subgroups)
+ return;
+
+ /* All connections have been notified */
+ if (check_io_err(io)) {
+ DBG(bcast_src->bass, "BIG sync failed");
+
+ /* Close all connected bises */
+ queue_destroy(bcast_src->bises, bass_bis_unref);
+ bcast_src->bises = NULL;
+
+ /* Close listen io */
+ g_io_channel_shutdown(bcast_src->listen_io, TRUE, NULL);
+ g_io_channel_unref(bcast_src->listen_io);
+ bcast_src->listen_io = NULL;
+
+ for (i = 0; i < bcast_src->num_subgroups; i++)
+ bcast_src->subgroup_data[i].bis_sync =
+ BT_BASS_BIG_SYNC_FAILED_BITMASK;
+ }
+
+ /* Send notification to client */
+ notify_data = bass_build_notif_from_bcast_src(bcast_src,
+ &notify_data_len);
+
+ gatt_db_attribute_notify(bcast_src->attr,
+ (void *)notify_data,
+ notify_data_len,
+ bt_bass_get_att(bcast_src->bass));
+
+ free(notify_data);
+}
+
+static struct bt_bass *bass_get_session(struct bt_att *att, struct gatt_db *db,
+ const bdaddr_t *adapter_bdaddr)
+{
+ const struct queue_entry *entry;
+ struct bt_bass *bass;
+
+ for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
+ struct bt_bass *bass = entry->data;
+
+ if (att == bt_bass_get_att(bass))
+ return bass;
+ }
+
+ bass = bt_bass_new(db, NULL, adapter_bdaddr);
+ bass->att = att;
+
+ bt_bass_attach(bass, NULL);
+
+ return bass;
+}
+
+static void bass_handle_add_src_op(struct bt_bass *bass,
+ struct gatt_db_attribute *attrib,
+ uint8_t opcode,
+ unsigned int id,
+ struct iovec *iov,
+ struct bt_att *att)
+{
+ struct bt_bcast_src *bcast_src, *src;
+ uint8_t src_id = 0;
+ struct gatt_db_attribute *attr;
+ uint8_t *pa_sync;
+ GIOChannel *io;
+ GError *err = NULL;
+ struct bt_iso_qos iso_qos = default_qos;
+ uint8_t num_bis = 0;
+ uint8_t bis[ISO_MAX_NUM_BIS];
+ uint8_t *notify_data;
+ size_t notify_data_len;
+
+ if (opcode == BT_ATT_OP_WRITE_REQ)
+ gatt_db_attribute_write_result(attrib, id, 0x00);
+
+ /* Allocate a new broadcast source */
+ bcast_src = malloc(sizeof(*bcast_src));
+ if (!bcast_src) {
+ DBG(bass, "Unable to allocate broadcast source");
+ return;
+ }
+
+ queue_push_tail(bass->ldb->bcast_srcs, bcast_src);
+
+ memset(bcast_src, 0, sizeof(*bcast_src));
+ memset(bis, 0, ISO_MAX_NUM_BIS);
+
+ bcast_src->bass = bass;
+
+ /* Map the source to a Broadcast Receive State characteristic */
+ for (int i = 0; i < NUM_BCAST_RECV_STATES; i++) {
+ src = queue_find(bass->ldb->bcast_srcs,
+ bass_src_attr_match,
+ bass->ldb->bcast_recv_states[i]->attr);
+ if (!src) {
+ /* Found and empty characteristic */
+ bcast_src->attr =
+ bass->ldb->bcast_recv_states[i]->attr;
+ break;
+ }
+ }
+
+ if (!bcast_src->attr) {
+ /* If no empty characteristic has been found,
+ * overwrite an existing one
+ */
+ attr = bass->ldb->bcast_recv_states[0]->attr;
+
+ src = queue_find(bass->ldb->bcast_srcs,
+ bass_src_attr_match,
+ attr);
+
+ queue_remove(bass->ldb->bcast_srcs, src);
+ bass_bcast_src_free(src);
+ bcast_src->attr = attr;
+ }
+
+ /* Allocate source id */
+ while (true) {
+ src = queue_find(bass->ldb->bcast_srcs,
+ bass_src_id_match,
+ &src_id);
+ if (!src)
+ break;
+
+ if (src_id == 0xFF) {
+ DBG(bass, "Unable to allocate broadcast source id");
+ return;
+ }
+
+ src_id++;
+ }
+
+ bcast_src->id = src_id;
+
+ /* Populate broadcast source fields from command parameters */
+ if (*(uint8_t *)util_iov_pull_mem(iov, sizeof(bcast_src->addr_type)))
+ bcast_src->addr_type = BDADDR_LE_RANDOM;
+ else
+ bcast_src->addr_type = BDADDR_LE_PUBLIC;
+
+ bacpy(&bcast_src->addr, (bdaddr_t *)util_iov_pull_mem(iov,
+ sizeof(bdaddr_t)));
+ bcast_src->sid = *(uint8_t *)util_iov_pull_mem(iov,
+ sizeof(bcast_src->sid));
+ util_iov_pull_le24(iov, &bcast_src->bid);
+
+ pa_sync = util_iov_pull_mem(iov, sizeof(*pa_sync));
+ bcast_src->sync_state = BT_BASS_NOT_SYNCHRONIZED_TO_PA;
+
+ /* TODO: Set the encryption field based on observed BIGInfo reports,
+ * after PA sync establishment
+ */
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_NO_ENC;
+
+ /* TODO: Use the pa_interval field for the sync transfer procedure */
+ util_iov_pull_mem(iov, sizeof(uint16_t));
+
+ bcast_src->num_subgroups = *(uint8_t *)util_iov_pull_mem(iov,
+ sizeof(bcast_src->num_subgroups));
+
+ if (!bcast_src->num_subgroups)
+ return;
+
+ bcast_src->subgroup_data = malloc(bcast_src->num_subgroups *
+ sizeof(*bcast_src->subgroup_data));
+ if (!bcast_src->subgroup_data) {
+ DBG(bass, "Unable to allocate subgroup data");
+ goto err;
+ }
+
+ memset(bcast_src->subgroup_data, 0, sizeof(*bcast_src->subgroup_data));
+
+ for (int i = 0; i < bcast_src->num_subgroups; i++) {
+ struct bt_bass_subgroup_data *data =
+ &bcast_src->subgroup_data[i];
+
+ util_iov_pull_le32(iov, &data->pending_bis_sync);
+
+ if (data->pending_bis_sync != BIS_SYNC_NO_PREF)
+ /* Iterate through the bis sync bitmask written
+ * by the client and store the bis indexes that
+ * the BASS server will try to synchronize to
+ */
+ for (int bis_idx = 0; bis_idx < 31; bis_idx++) {
+ if (data->pending_bis_sync & (1 << bis_idx)) {
+ bis[num_bis] = bis_idx + 1;
+ num_bis++;
+ }
+ }
+
+ data->meta_len = *(uint8_t *)util_iov_pull_mem(iov,
+ sizeof(data->meta_len));
+ if (!data->meta_len)
+ continue;
+
+ data->meta = malloc(data->meta_len);
+ if (!data->meta)
+ goto err;
+
+ memcpy(data->meta, (uint8_t *)util_iov_pull_mem(iov,
+ data->meta_len), data->meta_len);
+ }
+
+ if (pa_sync != PA_SYNC_NO_SYNC && num_bis > 0) {
+ /* If requested by client, try to synchronize to the source */
+ io = bt_io_listen(connect_cb, NULL, bcast_src, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ &bass->ldb->adapter_bdaddr,
+ BT_IO_OPT_DEST_BDADDR,
+ &bcast_src->addr,
+ BT_IO_OPT_DEST_TYPE,
+ bcast_src->addr_type,
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &iso_qos,
+ BT_IO_OPT_ISO_BC_SID, bcast_src->sid,
+ BT_IO_OPT_ISO_BC_NUM_BIS, num_bis,
+ BT_IO_OPT_ISO_BC_BIS, bis,
+ BT_IO_OPT_INVALID);
+
+ if (!io) {
+ DBG(bass, "%s", err->message);
+ g_error_free(err);
+ goto err;
+ }
+
+ bcast_src->listen_io = io;
+ g_io_channel_ref(bcast_src->listen_io);
+
+ if (!bcast_src->bises)
+ bcast_src->bises = queue_new();
+ } else {
+ for (int i = 0; i < bcast_src->num_subgroups; i++)
+ bcast_src->subgroup_data[i].bis_sync =
+ bcast_src->subgroup_data[i].pending_bis_sync;
+
+ notify_data = bass_build_notif_from_bcast_src(bcast_src,
+ &notify_data_len);
+
+ gatt_db_attribute_notify(bcast_src->attr,
+ (void *)notify_data,
+ notify_data_len,
+ bt_bass_get_att(bcast_src->bass));
+
+ free(notify_data);
+ }
+
+ return;
+
+err:
+ if (bcast_src->subgroup_data) {
+ for (int i = 0; i < bcast_src->num_subgroups; i++)
+ free(bcast_src->subgroup_data[i].meta);
+
+ free(bcast_src->subgroup_data);
+ }
+
+ free(bcast_src);
+}
+
+
#define BASS_OP(_str, _op, _size, _func) \
{ \
.str = _str, \
@@ -551,7 +919,7 @@ struct bass_op_handler {
const char *str;
uint8_t op;
size_t size;
- void (*func)(struct bt_bass_db *bdb,
+ void (*func)(struct bt_bass *bass,
struct gatt_db_attribute *attrib,
uint8_t opcode,
unsigned int id,
@@ -564,6 +932,8 @@ struct bass_op_handler {
0, bass_handle_remote_scan_started_op),
BASS_OP("Remove Source", BT_BASS_REMOVE_SRC,
0, bass_handle_remove_src_op),
+ BASS_OP("Add Source", BT_BASS_ADD_SRC,
+ 0, bass_handle_add_src_op),
{}
};

@@ -576,6 +946,8 @@ static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
struct bt_bass_db *bdb = user_data;
struct bt_bass_bcast_audio_scan_cp_hdr *hdr;
struct bass_op_handler *handler;
+ struct bt_bass *bass = bass_get_session(att, bdb->db,
+ &bdb->adapter_bdaddr);
struct iovec iov = {
.iov_base = (void *)value,
.iov_len = len,
@@ -596,7 +968,7 @@ static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
/* Call the appropriate opcode handler */
for (handler = bass_handlers; handler && handler->str; handler++) {
if (handler->op == hdr->op) {
- handler->func(bdb, attrib, opcode, id, &iov, att);
+ handler->func(bass, attrib, opcode, id, &iov, att);
return;
}
}
@@ -625,8 +997,10 @@ static void bass_bcast_recv_state_read(struct gatt_db_attribute *attrib,
uint8_t *rsp;
size_t rsp_len;
struct bt_bcast_src *bcast_src;
+ struct bt_bass *bass = bass_get_session(att, bdb->db,
+ &bdb->adapter_bdaddr);

- bcast_src = queue_find(bdb->bcast_srcs,
+ bcast_src = queue_find(bass->ldb->bcast_srcs,
bass_src_match_attrib,
attrib);

@@ -712,6 +1086,14 @@ static void bass_bcast_src_free(void *data)
free(bcast_src->subgroup_data[i].meta);

free(bcast_src->subgroup_data);
+
+ if (bcast_src->listen_io) {
+ g_io_channel_shutdown(bcast_src->listen_io, TRUE, NULL);
+ g_io_channel_unref(bcast_src->listen_io);
+ }
+
+ queue_destroy(bcast_src->bises, bass_bis_unref);
+
free(bcast_src);
}

@@ -900,6 +1282,14 @@ static void foreach_bass_service(struct gatt_db_attribute *attr,
gatt_db_service_foreach_char(attr, foreach_bass_char, bass);
}

+static void bass_attached(void *data, void *user_data)
+{
+ struct bt_bass_cb *cb = data;
+ struct bt_bass *bass = user_data;
+
+ cb->attached(bass, cb->user_data);
+}
+
bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)
{
bt_uuid_t uuid;
@@ -909,6 +1299,8 @@ bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)

queue_push_tail(sessions, bass);

+ queue_foreach(bass_cbs, bass_attached, bass);
+
if (!client)
return true;

@@ -990,7 +1382,8 @@ bool bt_bass_set_user_data(struct bt_bass *bass, void *user_data)
return true;
}

-static struct bt_bass_db *bass_db_new(struct gatt_db *db)
+static struct bt_bass_db *bass_db_new(struct gatt_db *db,
+ const bdaddr_t *adapter_bdaddr)
{
struct bt_bass_db *bdb;

@@ -999,6 +1392,7 @@ static struct bt_bass_db *bass_db_new(struct gatt_db *db)

bdb = new0(struct bt_bass_db, 1);
bdb->db = gatt_db_ref(db);
+ bacpy(&bdb->adapter_bdaddr, adapter_bdaddr);
bdb->bcast_srcs = queue_new();

if (!bass_db)
@@ -1019,7 +1413,8 @@ static bool bass_db_match(const void *data, const void *match_data)
return (bdb->db == db);
}

-static struct bt_bass_db *bass_get_db(struct gatt_db *db)
+static struct bt_bass_db *bass_get_db(struct gatt_db *db,
+ const bdaddr_t *adapter_bdaddr)
{
struct bt_bass_db *bdb;

@@ -1027,7 +1422,7 @@ static struct bt_bass_db *bass_get_db(struct gatt_db *db)
if (bdb)
return bdb;

- return bass_db_new(db);
+ return bass_db_new(db, adapter_bdaddr);
}

static struct bt_bass *bt_bass_ref(struct bt_bass *bass)
@@ -1040,7 +1435,8 @@ static struct bt_bass *bt_bass_ref(struct bt_bass *bass)
return bass;
}

-struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb)
+struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb,
+ const bdaddr_t *adapter_bdaddr)
{
struct bt_bass *bass;
struct bt_bass_db *db;
@@ -1048,7 +1444,7 @@ struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb)
if (!ldb)
return NULL;

- db = bass_get_db(ldb);
+ db = bass_get_db(ldb, adapter_bdaddr);
if (!db)
return NULL;

@@ -1140,3 +1536,8 @@ bool bt_bass_unregister(unsigned int id)

return true;
}
+
+void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr)
+{
+ bass_db_new(db, adapter_bdaddr);
+}
diff --git a/src/shared/bass.h b/src/shared/bass.h
index d3474f7cf..fb4b72d7d 100644
--- a/src/shared/bass.h
+++ b/src/shared/bass.h
@@ -56,6 +56,8 @@ struct bt_bcast_src {
uint8_t bad_code[BT_BASS_BCAST_CODE_SIZE];
uint8_t num_subgroups;
struct bt_bass_subgroup_data *subgroup_data;
+ GIOChannel *listen_io;
+ struct queue *bises;
};

/* Broadcast Audio Scan Control Point
@@ -71,6 +73,14 @@ struct bt_bass_bcast_audio_scan_cp_hdr {

#define BT_BASS_ADD_SRC 0x02

+/* PA_Sync values */
+#define PA_SYNC_NO_SYNC 0x00
+#define PA_SYNC_PAST 0x01
+#define PA_SYNC_NO_PAST 0x02
+
+/* BIS_Sync no preference bitmask */
+#define BIS_SYNC_NO_PREF 0xFFFFFFFF
+
struct bt_bass_add_src_params {
uint8_t addr_type;
bdaddr_t addr;
@@ -115,8 +125,10 @@ unsigned int bt_bass_register(bt_bass_func_t attached, bt_bass_func_t detached,
bool bt_bass_unregister(unsigned int id);
bool bt_bass_set_debug(struct bt_bass *bass, bt_bass_debug_func_t func,
void *user_data, bt_bass_destroy_func_t destroy);
-struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb);
+struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb,
+ const bdaddr_t *adapter_bdaddr);
bool bt_bass_set_user_data(struct bt_bass *bass, void *user_data);
void bt_bass_unref(struct bt_bass *bass);
bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client);
void bt_bass_detach(struct bt_bass *bass);
+void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr);
diff --git a/unit/test-bass.c b/unit/test-bass.c
index 8937c9478..2ab61f760 100644
--- a/unit/test-bass.c
+++ b/unit/test-bass.c
@@ -309,7 +309,7 @@ static void test_server(const void *user_data)
gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
NULL, data);

- data->bass = bt_bass_new(data->db, NULL);
+ data->bass = bt_bass_new(data->db, NULL, BDADDR_ANY);
g_assert(data->bass);

data->server = bt_gatt_server_new(data->db, att, 64, 0);
--
2.34.1


2023-06-29 09:31:09

by bluez.test.bot

[permalink] [raw]
Subject: RE: shared/bass: Introduce Add Source opcode handler

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

---Test result---

Test Summary:
CheckPatch PASS 1.50 seconds
GitLint PASS 0.68 seconds
BuildEll PASS 26.38 seconds
BluezMake PASS 770.64 seconds
MakeCheck PASS 12.18 seconds
MakeDistcheck PASS 153.70 seconds
CheckValgrind PASS 248.67 seconds
CheckSmatch PASS 334.87 seconds
bluezmakeextell PASS 101.04 seconds
IncrementalBuild PASS 1289.17 seconds
ScanBuild PASS 1002.57 seconds



---
Regards,
Linux Bluetooth

2023-07-10 19:52:05

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH BlueZ v3 0/2] shared/bass: Introduce Add Source opcode handler

Hello:

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

On Thu, 29 Jun 2023 11:07:33 +0300 you wrote:
> This patch series adds the BASS control point handler
> for the Add Source operation.
>
> If instructed by a Client through the Add Source opcode,
> the BASS Server attempts to synchronize to a Broadcast
> Source, by opening a btio channel.
>
> [...]

Here is the summary with links:
- [BlueZ,v3,1/2] btio: Add options for binding iso broadcast address
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=da85360a956f
- [BlueZ,v3,2/2] shared/bass: Introduce Add Source opcode handler
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=8a8f8c143b18

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