@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <poll.h>
#include <glib.h>
@@ -29,6 +30,9 @@
#include "lib/bluetooth.h"
#include "lib/uuid.h"
+#include "lib/iso.h"
+
+#include "btio/btio.h"
#include "src/dbus-common.h"
#include "src/shared/util.h"
@@ -54,10 +58,51 @@ struct bass_data {
struct btd_device *device;
struct btd_service *service;
struct bt_bass *bass;
+
+ unsigned int io_cb_id;
};
static struct queue *sessions;
+struct bt_bass_io {
+ GIOChannel *listen;
+ guint listen_io_id;
+ GIOChannel *pa;
+ guint pa_io_id;
+ struct queue *bises;
+};
+
+#define MAX_BIS_BITMASK_IDX 31
+
+#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_factor = 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_debug(const char *str, void *user_data)
{
DBG_IDX(0xffff, "%s", str);
@@ -148,6 +193,350 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
bass_data_remove(data);
}
+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 gboolean pa_io_disconnect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ struct bt_bcast_src *bcast_src = data;
+
+ DBG("PA sync io has been disconnected");
+
+ bcast_src->io->pa_io_id = 0;
+ g_io_channel_unref(bcast_src->io->pa);
+ bcast_src->io->pa = NULL;
+
+ return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+ struct bt_bcast_src *bcast_src = user_data;
+ int sk, err;
+ socklen_t len;
+ struct bt_iso_qos qos;
+
+ if (!bcast_src || !bcast_src->bass)
+ return;
+
+ if (check_io_err(io)) {
+ DBG("PA sync failed");
+
+ /* Mark PA sync as failed and notify client */
+ bcast_src->sync_state = BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA;
+ goto notify;
+ }
+
+ bcast_src->sync_state = BT_BASS_SYNCHRONIZED_TO_PA;
+ bcast_src->io->pa = io;
+ g_io_channel_ref(bcast_src->io->pa);
+
+ bcast_src->io->pa_io_id = g_io_add_watch(io, G_IO_ERR |
+ G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) pa_io_disconnect_cb,
+ bcast_src);
+
+ len = sizeof(qos);
+ memset(&qos, 0, len);
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len);
+ if (err < 0) {
+ DBG("Failed to get iso qos");
+ return;
+ }
+
+ if (!qos.bcast.encryption)
+ /* BIG is not encrypted. Try to synchronize */
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_NO_ENC;
+ else
+ /* BIG is encrypted. Wait for Client to provide the
+ * Broadcast_Code
+ */
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_BCODE_REQ;
+
+notify:
+ if (bcast_src->confirm_cb)
+ bcast_src->confirm_cb(bcast_src);
+}
+
+static gboolean listen_io_disconnect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ struct bt_bcast_src *bcast_src = data;
+
+ DBG("Listen io has been disconnected");
+
+ bcast_src->io->listen_io_id = 0;
+ g_io_channel_unref(bcast_src->io->listen);
+ bcast_src->io->listen = NULL;
+
+ return FALSE;
+}
+
+static int bass_io_listen(struct bt_bcast_src *bcast_src,
+ const bdaddr_t *src)
+{
+ uint8_t addr_type;
+ GError *err = NULL;
+ struct bt_iso_qos iso_qos = default_qos;
+ uint8_t num_bis = 0;
+ uint8_t bis[ISO_MAX_NUM_BIS];
+
+ if (!bcast_src)
+ return -1;
+
+ if (!bcast_src->io) {
+ bcast_src->io = malloc(sizeof(*bcast_src->io));
+ if (!bcast_src->io)
+ return -1;
+
+ memset(bcast_src->io, 0, sizeof(*bcast_src->io));
+ }
+
+ memset(bis, 0, ISO_MAX_NUM_BIS);
+
+ for (int i = 0; i < bcast_src->num_subgroups; i++) {
+ struct bt_bass_subgroup_data *data =
+ &bcast_src->subgroup_data[i];
+
+ 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++;
+ }
+ }
+ }
+
+ /* Convert to three-value type */
+ if (bcast_src->addr_type)
+ addr_type = BDADDR_LE_RANDOM;
+ else
+ addr_type = BDADDR_LE_PUBLIC;
+
+ /* Try to synchronize to the source */
+ bcast_src->io->listen = bt_io_listen(NULL, confirm_cb,
+ bcast_src, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ src,
+ BT_IO_OPT_DEST_BDADDR,
+ &bcast_src->addr,
+ BT_IO_OPT_DEST_TYPE,
+ 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 (!bcast_src->io->listen) {
+ DBG("%s", err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ g_io_channel_ref(bcast_src->io->listen);
+
+ bcast_src->io->listen_io_id = g_io_add_watch(bcast_src->io->listen,
+ G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc)listen_io_disconnect_cb,
+ bcast_src);
+
+ if (num_bis > 0 && !bcast_src->io->bises)
+ bcast_src->io->bises = queue_new();
+
+ return 0;
+}
+
+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;
+ int bis_idx;
+ int i;
+
+ if (!bcast_src || !bcast_src->bass)
+ return;
+
+ /* Keep io reference */
+ g_io_channel_ref(io);
+ queue_push_tail(bcast_src->io->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;
+ }
+
+ /* Wait until all BISes have been connected */
+ if (i != bcast_src->num_subgroups)
+ return;
+
+ if (check_io_err(io)) {
+ DBG("BIG sync failed");
+
+ queue_destroy(bcast_src->io->bises, bass_bis_unref);
+
+ bcast_src->io->bises = NULL;
+
+ /* Close listen io */
+ g_io_channel_shutdown(bcast_src->io->listen, TRUE, NULL);
+ g_io_channel_unref(bcast_src->io->listen);
+
+ bcast_src->io->listen = NULL;
+
+ if (bcast_src->io->listen_io_id > 0) {
+ g_source_remove(bcast_src->io->listen_io_id);
+ bcast_src->io->listen_io_id = 0;
+ }
+
+ /* Close PA io */
+ g_io_channel_shutdown(bcast_src->io->pa, TRUE, NULL);
+ g_io_channel_unref(bcast_src->io->pa);
+
+ bcast_src->io->pa = NULL;
+
+ if (bcast_src->io->pa_io_id > 0) {
+ g_source_remove(bcast_src->io->pa_io_id);
+ bcast_src->io->pa_io_id = 0;
+ }
+
+ for (i = 0; i < bcast_src->num_subgroups; i++)
+ bcast_src->subgroup_data[i].bis_sync =
+ BT_BASS_BIG_SYNC_FAILED_BITMASK;
+
+ /* If BIG sync failed because of an incorrect broadcast code,
+ * inform client
+ */
+ if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ)
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_BAD_CODE;
+ } else {
+ if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ)
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_DEC;
+ }
+
+ if (bcast_src->connect_cb)
+ bcast_src->connect_cb(bcast_src);
+}
+
+static int bass_io_accept(struct bt_bcast_src *bcast_src)
+{
+ int sk, err;
+ socklen_t len;
+ struct bt_iso_qos qos;
+ GError *gerr = NULL;
+
+ if (!bcast_src || !bcast_src->io || !bcast_src->io->pa)
+ return -1;
+
+ if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ) {
+ /* Update socket QoS with Broadcast Code */
+ len = sizeof(qos);
+ memset(&qos, 0, len);
+
+ sk = g_io_channel_unix_get_fd(bcast_src->io->pa);
+
+ err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len);
+ if (err < 0) {
+ DBG("Failed to get iso qos");
+ return -1;
+ }
+
+ memcpy(qos.bcast.bcode, bcast_src->bcode,
+ BT_BASS_BCAST_CODE_SIZE);
+
+ if (setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos,
+ sizeof(qos)) < 0) {
+ DBG("Failed to set iso qos");
+ return -1;
+ }
+ }
+
+ if (!bt_io_bcast_accept(bcast_src->io->pa,
+ connect_cb, bcast_src, NULL, &gerr,
+ BT_IO_OPT_INVALID)) {
+ DBG("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void bass_io_destroy(struct bt_bcast_src *bcast_src)
+{
+ if (!bcast_src || !bcast_src->io)
+ return;
+
+ queue_destroy(bcast_src->io->bises, bass_bis_unref);
+ bcast_src->io->bises = NULL;
+
+ if (bcast_src->io->listen) {
+ g_io_channel_shutdown(bcast_src->io->listen, TRUE, NULL);
+ g_io_channel_unref(bcast_src->io->listen);
+ bcast_src->io->listen = NULL;
+ }
+
+ if (bcast_src->io->listen_io_id > 0) {
+ g_source_remove(bcast_src->io->listen_io_id);
+ bcast_src->io->listen_io_id = 0;
+ }
+
+ if (bcast_src->io->pa) {
+ g_io_channel_shutdown(bcast_src->io->pa, TRUE, NULL);
+ g_io_channel_unref(bcast_src->io->pa);
+ bcast_src->io->pa = NULL;
+ }
+
+ if (bcast_src->io->pa_io_id > 0) {
+ g_source_remove(bcast_src->io->pa_io_id);
+ bcast_src->io->pa_io_id = 0;
+ }
+
+ free(bcast_src->io);
+}
+
static void bass_attached(struct bt_bass *bass, void *user_data)
{
struct bass_data *data;
@@ -173,6 +562,10 @@ static void bass_attached(struct bt_bass *bass, void *user_data)
data = bass_data_new(device);
data->bass = bass;
+ /* Register io callbacks */
+ data->io_cb_id = bt_bass_io_cb_register(bass, bass_io_listen,
+ bass_io_accept, bass_io_destroy);
+
bass_data_add(data);
}
@@ -205,6 +598,10 @@ static int bass_probe(struct btd_service *service)
return -EINVAL;
}
+ /* Register io callbacks */
+ data->io_cb_id = bt_bass_io_cb_register(data->bass, bass_io_listen,
+ bass_io_accept, bass_io_destroy);
+
bass_data_add(data);
bt_bass_set_user_data(data->bass, service);
@@ -14,13 +14,9 @@
#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"
@@ -29,8 +25,6 @@
#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)
@@ -43,6 +37,13 @@ struct bt_bass_cb {
void *user_data;
};
+struct bt_bass_io_cb {
+ unsigned int id;
+ bt_bass_listen_func_t listen;
+ bt_bass_accept_func_t accept;
+ bt_bass_io_destroy_func_t io_destroy;
+};
+
struct bt_bcast_recv_state {
struct bt_bass_db *bdb;
struct gatt_db_attribute *attr;
@@ -64,6 +65,7 @@ struct bt_bass {
struct bt_bass_db *rdb;
struct bt_gatt_client *client;
struct bt_att *att;
+ unsigned int disconn_id;
struct queue *notify;
@@ -71,6 +73,8 @@ struct bt_bass {
bt_bass_destroy_func_t debug_destroy;
void *debug_data;
+ struct queue *io_cbs;
+
void *user_data;
};
@@ -89,35 +93,6 @@ 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_factor = 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, ...)
@@ -580,102 +555,11 @@ static bool bass_src_attr_match(const void *data, const void *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)
+static void connect_cb(void *user_data)
{
struct bt_bcast_src *bcast_src = user_data;
uint8_t *notify_data;
size_t notify_data_len;
- int bis_idx;
- int i;
-
- /* 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;
-
- /* Close pa sync io */
- if (bcast_src->pa_sync_io) {
- g_io_channel_shutdown(bcast_src->pa_sync_io,
- TRUE, NULL);
- g_io_channel_unref(bcast_src->pa_sync_io);
- bcast_src->pa_sync_io = NULL;
- }
-
- for (i = 0; i < bcast_src->num_subgroups; i++)
- bcast_src->subgroup_data[i].bis_sync =
- BT_BASS_BIG_SYNC_FAILED_BITMASK;
-
- /* If BIG sync failed because of an incorrect broadcast code,
- * inform client
- */
- if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ)
- bcast_src->enc = BT_BASS_BIG_ENC_STATE_BAD_CODE;
- } else {
- if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BCODE_REQ)
- bcast_src->enc = BT_BASS_BIG_ENC_STATE_DEC;
- }
/* Send notification to client */
notify_data = bass_build_notif_from_bcast_src(bcast_src,
@@ -703,62 +587,25 @@ static bool bass_trigger_big_sync(struct bt_bcast_src *bcast_src)
return false;
}
-
-static void confirm_cb(GIOChannel *io, gpointer user_data)
+static void confirm_cb(void *user_data)
{
struct bt_bcast_src *bcast_src = user_data;
- int sk, err;
- socklen_t len;
- struct bt_iso_qos qos;
uint8_t *notify_data;
size_t notify_data_len;
- GError *gerr = NULL;
-
- if (check_io_err(io)) {
- DBG(bcast_src->bass, "PA sync failed");
-
- /* Mark PA sync as failed and notify client */
- bcast_src->sync_state = BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA;
- goto notify;
- }
-
- bcast_src->sync_state = BT_BASS_SYNCHRONIZED_TO_PA;
- bcast_src->pa_sync_io = io;
- g_io_channel_ref(bcast_src->pa_sync_io);
-
- len = sizeof(qos);
- memset(&qos, 0, len);
+ const struct queue_entry *entry;
- sk = g_io_channel_unix_get_fd(io);
+ if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_NO_ENC &&
+ bass_trigger_big_sync(bcast_src)) {
+ for (entry = queue_get_entries(bcast_src->bass->io_cbs);
+ entry; entry = entry->next) {
+ struct bt_bass_io_cb *cb = entry->data;
- err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len);
- if (err < 0) {
- DBG(bcast_src->bass, "Failed to get iso qos");
- return;
- }
-
- if (!qos.bcast.encryption) {
- /* BIG is not encrypted. Try to synchronize */
- bcast_src->enc = BT_BASS_BIG_ENC_STATE_NO_ENC;
-
- if (bass_trigger_big_sync(bcast_src)) {
- if (!bt_io_bcast_accept(bcast_src->pa_sync_io,
- connect_cb, bcast_src, NULL, &gerr,
- BT_IO_OPT_INVALID)) {
- DBG(bcast_src->bass, "bt_io_bcast_accept: %s",
- gerr->message);
- g_error_free(gerr);
- }
- return;
+ if (cb->accept && (cb->accept(bcast_src) < 0))
+ DBG(bcast_src->bass, "Unable to accept");
}
-
- goto notify;
+ return;
}
- /* BIG is encrypted. Wait for Client to provide the Broadcast_Code */
- bcast_src->enc = BT_BASS_BIG_ENC_STATE_BCODE_REQ;
-
-notify:
notify_data = bass_build_notif_from_bcast_src(bcast_src,
¬ify_data_len);
@@ -856,14 +703,9 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
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;
- uint8_t addr_type;
+ const struct queue_entry *entry;
gatt_db_attribute_write_result(attrib, id, 0x00);
@@ -881,7 +723,6 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
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;
@@ -968,18 +809,6 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
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)
@@ -993,39 +822,20 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
data->meta_len), data->meta_len);
}
- if (pa_sync != PA_SYNC_NO_SYNC) {
- /* Convert to three-value type */
- if (bcast_src->addr_type)
- addr_type = BDADDR_LE_RANDOM;
- else
- addr_type = BDADDR_LE_PUBLIC;
-
- /* If requested by client, try to synchronize to the source */
- io = bt_io_listen(NULL, confirm_cb, 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,
- 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->confirm_cb = confirm_cb;
+ bcast_src->connect_cb = connect_cb;
- bcast_src->listen_io = io;
- g_io_channel_ref(bcast_src->listen_io);
+ if (*pa_sync != PA_SYNC_NO_SYNC) {
+ for (entry = queue_get_entries(bass->io_cbs); entry;
+ entry = entry->next) {
+ struct bt_bass_io_cb *cb = entry->data;
- if (num_bis > 0 && !bcast_src->bises)
- bcast_src->bises = queue_new();
+ if (cb->listen && (cb->listen(bcast_src,
+ &bass->ldb->adapter_bdaddr) < 0)) {
+ DBG(bass, "Unable to listen for source");
+ goto err;
+ }
+ }
} else {
for (int i = 0; i < bcast_src->num_subgroups; i++)
bcast_src->subgroup_data[i].bis_sync =
@@ -1064,12 +874,9 @@ static void bass_handle_set_bcast_code_op(struct bt_bass *bass,
{
struct bt_bass_set_bcast_code_params *params;
struct bt_bcast_src *bcast_src;
- int sk, err;
- socklen_t len;
- struct bt_iso_qos qos;
- GError *gerr = NULL;
uint8_t *notify_data;
size_t notify_data_len;
+ const struct queue_entry *entry;
/* Get Set Broadcast Code command parameters */
params = util_iov_pull_mem(iov, sizeof(*params));
@@ -1086,6 +893,9 @@ static void bass_handle_set_bcast_code_op(struct bt_bass *bass,
return;
}
+ if (!bcast_src->bass)
+ return;
+
gatt_db_attribute_write_result(attrib, id, 0x00);
if (!bass_trigger_big_sync(bcast_src)) {
@@ -1103,36 +913,15 @@ static void bass_handle_set_bcast_code_op(struct bt_bass *bass,
return;
}
- /* Try to sync to the source using the
- * received broadcast code
- */
- len = sizeof(qos);
- memset(&qos, 0, len);
-
- if (!bcast_src->pa_sync_io)
- return;
+ /* Set Broadcast Code */
+ memcpy(bcast_src->bcode, params->bcast_code, BT_BASS_BCAST_CODE_SIZE);
- sk = g_io_channel_unix_get_fd(bcast_src->pa_sync_io);
+ for (entry = queue_get_entries(bcast_src->bass->io_cbs); entry;
+ entry = entry->next) {
+ struct bt_bass_io_cb *cb = entry->data;
- err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len);
- if (err < 0) {
- DBG(bcast_src->bass, "Failed to get iso qos");
- return;
- }
-
- /* Update socket QoS with Broadcast Code */
- memcpy(qos.bcast.bcode, params->bcast_code, BT_BASS_BCAST_CODE_SIZE);
-
- if (setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos,
- sizeof(qos)) < 0) {
- DBG(bcast_src->bass, "Failed to set iso qos");
- return;
- }
-
- if (!bt_io_bcast_accept(bcast_src->pa_sync_io, connect_cb,
- bcast_src, NULL, &gerr, BT_IO_OPT_INVALID)) {
- DBG(bcast_src->bass, "bt_io_bcast_accept: %s", gerr->message);
- g_error_free(gerr);
+ if (cb->accept && (cb->accept(bcast_src) < 0))
+ DBG(bass, "Unable to accept");
}
}
@@ -1308,23 +1097,24 @@ static void bass_new(struct bt_bass_db *bdb)
static void bass_bcast_src_free(void *data)
{
struct bt_bcast_src *bcast_src = data;
+ const struct queue_entry *entry;
+
+ if (!bcast_src)
+ return;
for (int i = 0; i < bcast_src->num_subgroups; i++)
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);
- }
-
- if (bcast_src->pa_sync_io) {
- g_io_channel_shutdown(bcast_src->pa_sync_io, TRUE, NULL);
- g_io_channel_unref(bcast_src->pa_sync_io);
- }
+ if (bcast_src->bass)
+ for (entry = queue_get_entries(bcast_src->bass->io_cbs);
+ entry; entry = entry->next) {
+ struct bt_bass_io_cb *cb = entry->data;
- queue_destroy(bcast_src->bises, bass_bis_unref);
+ if (cb->io_destroy)
+ cb->io_destroy(bcast_src);
+ }
free(bcast_src);
}
@@ -1335,6 +1125,9 @@ static void read_bcast_recv_state(bool success, uint8_t att_ecode,
{
struct bt_bcast_src *bcast_src = user_data;
+ if (!bcast_src || !bcast_src->bass)
+ return;
+
if (!success) {
DBG(bcast_src->bass, "Unable to read "
"Broadcast Receive State: error 0x%02x",
@@ -1522,6 +1315,30 @@ static void bass_attached(void *data, void *user_data)
cb->attached(bass, cb->user_data);
}
+static void bass_disconnected(int err, void *user_data)
+{
+ struct bt_bass *bass = user_data;
+
+ bass->disconn_id = 0;
+
+ DBG(bass, "bass %p disconnected err %d", bass, err);
+
+ bt_bass_detach(bass);
+}
+
+static void bass_attach_att(struct bt_bass *bass, struct bt_att *att)
+{
+ if (bass->disconn_id) {
+ if (att == bt_bass_get_att(bass))
+ return;
+ bt_att_unregister_disconnect(att, bass->disconn_id);
+ }
+
+ bass->disconn_id = bt_att_register_disconnect(att,
+ bass_disconnected,
+ bass, NULL);
+}
+
bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)
{
bt_uuid_t uuid;
@@ -1533,8 +1350,10 @@ bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)
queue_foreach(bass_cbs, bass_attached, bass);
- if (!client)
+ if (!client) {
+ bass_attach_att(bass, bass->att);
return true;
+ }
if (bass->client)
return false;
@@ -1543,6 +1362,8 @@ bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)
if (!bass->client)
return false;
+ bass_attach_att(bass, bt_gatt_client_get_att(client));
+
bt_uuid16_create(&uuid, BASS_UUID);
gatt_db_foreach_service(bass->rdb->db, &uuid, foreach_bass_service,
bass);
@@ -1572,8 +1393,16 @@ void bt_bass_detach(struct bt_bass *bass)
if (!queue_remove(sessions, bass))
return;
- bt_gatt_client_unref(bass->client);
- bass->client = NULL;
+ if (bass->client) {
+ bt_att_unregister_disconnect(
+ bt_gatt_client_get_att(bass->client),
+ bass->disconn_id);
+ bt_gatt_client_unref(bass->client);
+ bass->client = NULL;
+ } else {
+ bt_att_unregister_disconnect(bass->att, bass->disconn_id);
+ bass->att = NULL;
+ }
queue_foreach(bass_cbs, bass_detached, bass);
}
@@ -1591,13 +1420,32 @@ static void bass_db_free(void *data)
free(bdb);
}
+static void bass_io_cb_free(void *data)
+{
+ struct bt_bass_io_cb *cb = data;
+
+ free(cb);
+}
+
static void bass_free(void *data)
{
struct bt_bass *bass = data;
+ const struct queue_entry *entry;
bt_bass_detach(bass);
bass_db_free(bass->rdb);
+
+ if (bass->ldb)
+ for (entry = queue_get_entries(bass->ldb->bcast_srcs);
+ entry; entry = entry->next) {
+ struct bt_bcast_src *bcast_src = entry->data;
+
+ if (bcast_src && bcast_src->bass == bass)
+ bcast_src->bass = NULL;
+ }
+
queue_destroy(bass->notify, NULL);
+ queue_destroy(bass->io_cbs, bass_io_cb_free);
free(bass);
}
@@ -1692,6 +1540,7 @@ struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb,
bass = new0(struct bt_bass, 1);
bass->ldb = db;
bass->notify = queue_new();
+ bass->io_cbs = queue_new();
if (!rdb)
goto done;
@@ -1782,3 +1631,51 @@ void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr)
{
bass_db_new(db, adapter_bdaddr);
}
+
+unsigned int bt_bass_io_cb_register(struct bt_bass *bass,
+ bt_bass_listen_func_t listen,
+ bt_bass_accept_func_t accept,
+ bt_bass_io_destroy_func_t io_destroy)
+{
+ struct bt_bass_io_cb *cb;
+ static unsigned int id;
+
+ if (!bass)
+ return 0;
+
+ cb = new0(struct bt_bass_io_cb, 1);
+ cb->id = ++id ? id : ++id;
+ cb->listen = listen;
+ cb->accept = accept;
+ cb->io_destroy = io_destroy;
+
+ queue_push_tail(bass->io_cbs, cb);
+
+ return cb->id;
+}
+
+static bool match_cb_id(const void *data, const void *match_data)
+{
+ const struct bt_bass_io_cb *cb = data;
+ unsigned int id = PTR_TO_UINT(match_data);
+
+ return (cb->id == id);
+}
+
+bool bt_bass_io_cb_unregister(struct bt_bass *bass,
+ unsigned int id)
+{
+ struct bt_bass_io_cb *cb;
+
+ if (!bass)
+ return false;
+
+ cb = queue_remove_if(bass->io_cbs, match_cb_id,
+ UINT_TO_PTR(id));
+ if (!cb)
+ return false;
+
+ free(cb);
+
+ return true;
+}
@@ -8,6 +8,7 @@
*/
struct bt_bass;
+struct bt_bass_io;
#define NUM_BCAST_RECV_STATES 2
#define BT_BASS_BCAST_CODE_SIZE 16
@@ -32,6 +33,9 @@ struct bt_bass;
#define BT_BASS_BIG_ENC_STATE_DEC 0x02
#define BT_BASS_BIG_ENC_STATE_BAD_CODE 0x03
+typedef void (*bt_bass_confirm_func_t)(void *user_data);
+typedef void (*bt_bass_connect_func_t)(void *user_data);
+
/* BASS subgroup field of the Broadcast
* Receive State characteristic
*/
@@ -53,12 +57,13 @@ struct bt_bcast_src {
uint32_t bid;
uint8_t sync_state;
uint8_t enc;
+ uint8_t bcode[BT_BASS_BCAST_CODE_SIZE];
uint8_t bad_code[BT_BASS_BCAST_CODE_SIZE];
uint8_t num_subgroups;
struct bt_bass_subgroup_data *subgroup_data;
- GIOChannel *listen_io;
- GIOChannel *pa_sync_io;
- struct queue *bises;
+ struct bt_bass_io *io;
+ bt_bass_confirm_func_t confirm_cb;
+ bt_bass_connect_func_t connect_cb;
};
/* Broadcast Audio Scan Control Point
@@ -120,6 +125,11 @@ typedef void (*bt_bass_func_t)(struct bt_bass *bass, void *user_data);
typedef void (*bt_bass_destroy_func_t)(void *user_data);
typedef void (*bt_bass_debug_func_t)(const char *str, void *user_data);
+typedef int (*bt_bass_listen_func_t)(struct bt_bcast_src *bcast_src,
+ const bdaddr_t *src);
+typedef int (*bt_bass_accept_func_t)(struct bt_bcast_src *bcast_src);
+typedef void (*bt_bass_io_destroy_func_t)(struct bt_bcast_src *bcast_src);
+
struct bt_att *bt_bass_get_att(struct bt_bass *bass);
unsigned int bt_bass_register(bt_bass_func_t attached, bt_bass_func_t detached,
void *user_data);
@@ -134,3 +144,10 @@ bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client);
bool bt_bass_set_att(struct bt_bass *bass, struct bt_att *att);
void bt_bass_detach(struct bt_bass *bass);
void bt_bass_add_db(struct gatt_db *db, const bdaddr_t *adapter_bdaddr);
+
+unsigned int bt_bass_io_cb_register(struct bt_bass *bass,
+ bt_bass_listen_func_t listen,
+ bt_bass_accept_func_t accept,
+ bt_bass_io_destroy_func_t io_destroy);
+bool bt_bass_io_cb_unregister(struct bt_bass *bass,
+ unsigned int id);