This adds the Set Broadcast_Code opcode handler, enabling a BASS Server to sync to an encrypted BIG. --- src/shared/bass.c | 159 +++++++++++++++++++++++++++++++++++++++++++--- src/shared/bass.h | 1 + 2 files changed, 151 insertions(+), 9 deletions(-) diff --git a/src/shared/bass.c b/src/shared/bass.c index 1bb40877f..86dab03e3 100644 --- a/src/shared/bass.c +++ b/src/shared/bass.c @@ -613,9 +613,6 @@ static void connect_cb(GIOChannel *io, GError *gerr, 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); @@ -661,9 +658,26 @@ static void connect_cb(GIOChannel *io, GError *gerr, 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 */ @@ -678,6 +692,66 @@ static void connect_cb(GIOChannel *io, GError *gerr, free(notify_data); } +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; + 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); + + sk = g_io_channel_unix_get_fd(io); + + 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 (!bt_io_bcast_accept(bcast_src->pa_sync_io, + connect_cb, bcast_src, NULL, &gerr)) { + DBG(bcast_src->bass, "bt_io_accept: %s", gerr->message); + g_error_free(gerr); + } + 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); + + 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) { @@ -796,11 +870,6 @@ static void bass_handle_add_src_op(struct bt_bass *bass, 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)); @@ -852,7 +921,7 @@ static void bass_handle_add_src_op(struct bt_bass *bass, 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, + 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, @@ -906,6 +975,71 @@ err: free(bcast_src); } +static void bass_handle_set_bcast_code_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_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; + + if (opcode == BT_ATT_OP_WRITE_REQ) + gatt_db_attribute_write_result(attrib, id, 0x00); + + /* Get Set Broadcast Code command parameters */ + params = util_iov_pull_mem(iov, sizeof(*params)); + + bcast_src = queue_find(bass->ldb->bcast_srcs, + bass_src_id_match, + ¶ms->id); + + if (!bcast_src) { + /* No source matches the written source id */ + if (opcode == BT_ATT_OP_WRITE_REQ) + gatt_db_attribute_write_result(attrib, id, + BT_BASS_ERROR_INVALID_SOURCE_ID); + + 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; + + sk = g_io_channel_unix_get_fd(bcast_src->pa_sync_io); + + 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)) { + DBG(bcast_src->bass, "bt_io_accept: %s", gerr->message); + g_error_free(gerr); + } +} #define BASS_OP(_str, _op, _size, _func) \ { \ @@ -934,6 +1068,8 @@ struct bass_op_handler { 0, bass_handle_remove_src_op), BASS_OP("Add Source", BT_BASS_ADD_SRC, 0, bass_handle_add_src_op), + BASS_OP("Set Broadcast Code", BT_BASS_SET_BCAST_CODE, + 0, bass_handle_set_bcast_code_op), {} }; @@ -1092,6 +1228,11 @@ static void bass_bcast_src_free(void *data) 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); + } + queue_destroy(bcast_src->bises, bass_bis_unref); free(bcast_src); diff --git a/src/shared/bass.h b/src/shared/bass.h index fb4b72d7d..b3f83b32e 100644 --- a/src/shared/bass.h +++ b/src/shared/bass.h @@ -57,6 +57,7 @@ struct bt_bcast_src { uint8_t num_subgroups; struct bt_bass_subgroup_data *subgroup_data; GIOChannel *listen_io; + GIOChannel *pa_sync_io; struct queue *bises; }; -- 2.34.1