2023-06-28 15:09:29

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ v2 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.

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 | 430 ++++++++++++++++++++++++++++++++++++++++--
src/shared/bass.h | 14 +-
unit/test-bass.c | 2 +-
7 files changed, 526 insertions(+), 29 deletions(-)


base-commit: d6bfbd28420edf91382635b229b9f8b2f94dc060
--
2.34.1



2023-06-28 15:09:36

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ v2 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 | 430 ++++++++++++++++++++++++++++++++++++++++--
src/shared/bass.h | 14 +-
unit/test-bass.c | 2 +-
5 files changed, 456 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..3557dda0f 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,341 @@ 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_sync_bit + 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 */
+ bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
+ bc_addr.bc_bdaddr_type = bcast_src->addr_type;
+
+ 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 +922,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 +935,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 +949,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 +971,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 +1000,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 +1089,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 +1285,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 +1302,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 +1385,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 +1395,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 +1416,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 +1425,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 +1438,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 +1447,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 +1539,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-28 15:09:46

by Iulia Tanasescu

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

This adds the BT_IO_OPT_ISO_BC_ADDR btio option, to allow the user
to set the iso_bc field of an address at bind.
---
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-28 15:55:38

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

---Test result---

Test Summary:
CheckPatch PASS 1.48 seconds
GitLint PASS 0.51 seconds
BuildEll PASS 34.60 seconds
BluezMake FAIL 25.80 seconds
MakeCheck FAIL 40.85 seconds
MakeDistcheck FAIL 44.29 seconds
CheckValgrind FAIL 21.60 seconds
CheckSmatch FAIL 29.55 seconds
bluezmakeextell FAIL 19.27 seconds
IncrementalBuild FAIL 1069.65 seconds
ScanBuild FAIL 746.90 seconds

Details
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:

src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4528: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8182: src/shared/libshared_glib_la-bass.lo] Error 1
make: *** [Makefile:11863: check] Error 2
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

../../src/shared/bass.c: In function ‘bass_handle_add_src_op’:
../../src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
../../src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
../../src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[2]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [Makefile:4528: all] Error 2
make: *** [Makefile:11784: distcheck] Error 1
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:11863: check] Error 2
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:

src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:768:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:830:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1323:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1354:23: warning: Variable length array is used.
src/shared/gatt-server.c:276:25: warning: Variable length array is used.
src/shared/gatt-server.c:619:25: warning: Variable length array is used.
src/shared/gatt-server.c:718:25: warning: Variable length array is used.
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4528: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:

src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4528: all] Error 2
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:
[BlueZ,v2,2/2] shared/bass: Introduce Add Source opcode handler

src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4528: all] Error 2
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:

src/shared/ad.c:369:19: warning: Use of zero-allocated memory
buf[(*pos)++] = ad_type;
^
1 warning generated.
src/shared/gatt-client.c:451:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:696:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:993:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1099:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1291:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1356:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1631:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1636:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2140:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2148:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3236:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3258:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
12 warnings generated.
src/shared/bass.c: In function ‘bass_handle_add_src_op’:
src/shared/bass.c:835:21: error: ‘bis_sync_bit’ undeclared (first use in this function)
835 | bis[num_bis] = bis_sync_bit + 1;
| ^~~~~~~~~~~~
src/shared/bass.c:835:21: note: each undeclared identifier is reported only once for each function it appears in
src/shared/bass.c:855:10: error: ‘bc_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
855 | bacpy(&bc_addr.bc_bdaddr, &bcast_src->addr);
| ^~~~~~~
| in_addr
make[1]: *** [Makefile:8399: src/shared/libshared_mainloop_la-bass.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4528: all] Error 2


---
Regards,
Linux Bluetooth