[PATCH BlueZ 2/2] shared: Add initial BASS code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added initial BASS code - added support for the Broadcast
Receive State characteristic and for the Set Broadcast_Code
operation from the Broadcast Audio Scan Control Point
characteristic.
The BASS implementation exposes HCI event callbacks that
enable the BASS server to autonomously synchronize
to BIGs.

---
 src/shared/att-types.h |    1 +
 src/shared/bap.c       | 1065 ++++++++++++++++++++++++++++++++++++++++
 src/shared/bap.h       |   25 +
 src/shared/bass.h      |   43 ++
 4 files changed, 1134 insertions(+)
 create mode 100644 src/shared/bass.h

diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index a08b24155..35bf41118 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -104,6 +104,7 @@ struct bt_att_pdu_error_rsp {
  * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
  * following:
  */
+#define BT_ERROR_WRITE_REQUEST_REJECTED         0xfc
 #define BT_ERROR_CCC_IMPROPERLY_CONFIGURED      0xfd
 #define BT_ERROR_ALREADY_IN_PROGRESS            0xfe
 #define BT_ERROR_OUT_OF_RANGE                   0xff
diff --git a/src/shared/bap.c b/src/shared/bap.c
index db7def799..86374967f 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -17,6 +17,8 @@
 
 #include "lib/bluetooth.h"
 #include "lib/uuid.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
 
 #include "src/shared/io.h"
 #include "src/shared/queue.h"
@@ -28,6 +30,8 @@
 #include "src/shared/gatt-client.h"
 #include "src/shared/bap.h"
 #include "src/shared/ascs.h"
+#include "src/shared/bass.h"
+#include "src/shared/ad.h"
 
 /* Maximum number of ASE(s) */
 #define NUM_SINKS 2
@@ -108,13 +112,55 @@ struct bt_ascs {
 	struct gatt_db_attribute *ase_cp_ccc;
 };
 
+/*
+ * BASS subgroup field of the Broadcast
+ * Receive State characteristic
+ */
+struct bt_bass_subgroup_data {
+	uint32_t bis_sync;
+	uint8_t meta_len;
+	uint8_t *meta;
+} __packed;
+
+/* BASS Broadcast Source structure */
+struct bt_bcst_source {
+	struct gatt_db_attribute *attr;
+	uint8_t id;
+	uint8_t addr_type;
+	uint8_t addr[6];
+	uint8_t sid;
+	uint8_t bid[BT_BAP_BROADCAST_ID_SIZE];
+	uint8_t sync_state;
+	uint8_t encryption;
+	uint8_t bad_code[BT_BAP_BROADCAST_CODE_SIZE];
+	uint8_t num_subgroups;
+	struct bt_bass_subgroup_data *subgroup_data;
+} __packed;
+
+/* Broadcast Receive State characteristic structure */
+struct bt_bcst_recv_state {
+	struct bt_bass *bass;
+	struct gatt_db_attribute *attr;
+	struct gatt_db_attribute *ccc;
+};
+
+/* BASS instance structure */
+struct bt_bass {
+	struct bt_bap_db *bdb;
+	struct gatt_db_attribute *service;
+	struct gatt_db_attribute *broadcast_audio_scan_cp;
+	struct bt_bcst_recv_state *bcst_recv_states[NUM_BCST_RECV_STATES];
+};
+
 struct bt_bap_db {
 	struct gatt_db *db;
 	struct bt_pacs *pacs;
 	struct bt_ascs *ascs;
+	struct bt_bass *bass;
 	struct queue *sinks;
 	struct queue *sources;
 	struct queue *endpoints;
+	struct queue *bass_bcst_sources;
 };
 
 struct bt_bap_req {
@@ -255,6 +301,15 @@ static struct queue *bap_db;
 static struct queue *bap_cbs;
 static struct queue *sessions;
 
+static int hci_fd = -1;
+
+static struct bt_hci_cmd_le_big_create_sync *big_sync_cmd;
+static int big_sync_cmd_len;
+
+static struct bt_hci_le_pa_report *pa_report;
+
+static uint8_t next_available_source_id;
+
 static bool bap_db_match(const void *data, const void *match_data)
 {
 	const struct bt_bap_db *bdb = data;
@@ -2170,6 +2225,643 @@ static struct bt_ascs *ascs_new(struct gatt_db *db)
 	return ascs;
 }
 
+static int bass_build_bcst_source_from_notif(struct bt_bcst_source *bcst_source,
+							const uint8_t *value)
+{
+	struct bt_bass_subgroup_data *subgroup_data;
+
+	if (!bcst_source || !value)
+		return -1;
+
+	bcst_source->id = *value;
+	value++;
+
+	bcst_source->addr_type = *value;
+	value++;
+
+	memcpy(bcst_source->addr, value, 6);
+	value += 6;
+
+	bcst_source->sid = *value;
+	value++;
+
+	memcpy(bcst_source->bid, value, BT_BAP_BROADCAST_ID_SIZE);
+	value += BT_BAP_BROADCAST_ID_SIZE;
+
+	bcst_source->sync_state = *value;
+	value++;
+
+	bcst_source->encryption = *value;
+	value++;
+
+	if (bcst_source->encryption == BT_BASS_BIG_ENC_STATE_BAD_CODE) {
+		memcpy(bcst_source->bad_code, value, BT_BAP_BROADCAST_CODE_SIZE);
+		value += BT_BAP_BROADCAST_CODE_SIZE;
+	} else {
+		memset(bcst_source->bad_code, 0, BT_BAP_BROADCAST_CODE_SIZE);
+	}
+
+	bcst_source->num_subgroups = *value;
+	value++;
+
+	free(bcst_source->subgroup_data);
+	bcst_source->subgroup_data = malloc(bcst_source->num_subgroups *
+					sizeof(struct bt_bass_subgroup_data));
+
+	if (!bcst_source->subgroup_data)
+		return -1;
+
+	for (int i = 0; i < bcst_source->num_subgroups; i++) {
+		subgroup_data = &bcst_source->subgroup_data[i];
+
+		memcpy(&subgroup_data->bis_sync, value, sizeof(uint32_t));
+		value += sizeof(uint32_t);
+
+		subgroup_data->meta_len = *value;
+		value++;
+
+		free(subgroup_data->meta);
+		subgroup_data->meta = malloc(subgroup_data->meta_len);
+		if (!subgroup_data->meta) {
+			for (int j = 0; j < i; j++)
+				free(bcst_source->subgroup_data[j].meta);
+
+			free(bcst_source->subgroup_data);
+			return -1;
+		}
+
+		memcpy(subgroup_data->meta, value, subgroup_data->meta_len);
+		value += subgroup_data->meta_len;
+	}
+
+	return 0;
+}
+
+static int bass_build_bcst_source_from_read_rsp(
+					struct bt_bcst_source *bcst_source,
+					const uint8_t *value)
+{
+	return bass_build_bcst_source_from_notif(bcst_source, value);
+}
+
+static uint8_t *bass_build_notif_from_bcst_source(struct bt_bcst_source *source,
+							size_t *notif_len)
+{
+	size_t len = 0;
+	uint8_t *notif = NULL;
+	uint8_t *ptr;
+
+	*notif_len = 0;
+
+	if (!source)
+		return NULL;
+
+	len = 15 + source->num_subgroups * 5;
+
+	if (source->encryption == BT_BASS_BIG_ENC_STATE_BAD_CODE)
+		len += BT_BAP_BROADCAST_CODE_SIZE;
+
+	for (size_t i = 0; i < source->num_subgroups; i++) {
+		/* add length for subgroup metadata */
+		len += source->subgroup_data[i].meta_len;
+	}
+
+	notif = malloc(len);
+	if (!notif)
+		return NULL;
+
+	memset(notif, 0, len);
+	ptr = notif;
+
+	/* add source_id field */
+	*ptr = source->id;
+	ptr++;
+
+	/* add addr_type field */
+	*ptr = source->addr_type;
+	ptr++;
+
+	/* add addr field */
+	memcpy(ptr, source->addr, 6);
+	ptr += 6;
+
+	/* add sid field */
+	*ptr = source->sid;
+	ptr++;
+
+	/* add bid field */
+	memcpy(ptr, source->bid, BT_BAP_BROADCAST_ID_SIZE);
+	ptr += 3;
+
+	/* add sync_state field */
+	*ptr = source->sync_state;
+	ptr++;
+
+	/* add encryption field */
+	*ptr = source->encryption;
+	ptr++;
+
+	if (source->encryption == BT_BASS_BIG_ENC_STATE_BAD_CODE) {
+		memcpy(ptr, source->bad_code, BT_BAP_BROADCAST_CODE_SIZE);
+		ptr += BT_BAP_BROADCAST_CODE_SIZE;
+	}
+
+	/* add num_subgroups field */
+	*ptr = source->num_subgroups;
+	ptr++;
+
+	for (size_t i = 0; i < source->num_subgroups; i++) {
+		/* add subgroup bis_sync */
+		memcpy(ptr, &source->subgroup_data[i].bis_sync,
+						sizeof(uint32_t));
+		ptr += sizeof(uint32_t);
+
+		/* add subgroup meta_len */
+		*ptr = source->subgroup_data[i].meta_len;
+		ptr++;
+
+		/* add subgroup metadata */
+		if (source->subgroup_data[i].meta_len > 0) {
+			memcpy(ptr, source->subgroup_data[i].meta,
+					source->subgroup_data[i].meta_len);
+			ptr += source->subgroup_data[i].meta_len;
+		}
+	}
+
+	*notif_len = len;
+	return notif;
+}
+
+static uint8_t *bass_build_read_rsp_from_bcst_source(struct bt_bcst_source *source,
+								size_t *rsp_len)
+{
+	return bass_build_notif_from_bcst_source(source, rsp_len);
+}
+
+static bool bass_src_id_match(const void *data, const void *match_data)
+{
+	const struct bt_bcst_source *src = data;
+	const uint8_t *id = match_data;
+
+	return (src->id == *id);
+}
+
+static void bass_fill_create_big_sync_cmd_from_pa_report(
+				struct bt_hci_cmd_le_big_create_sync *cmd)
+{
+	struct iovec iov;
+	struct bt_ad_structure *ad_structure;
+	uint16_t uuid;
+	struct bt_hci_le_pa_base_data *base_data;
+	uint8_t *bis_index = (uint8_t *)cmd->bis;
+
+	if (!pa_report)
+		return;
+
+	iov.iov_base = pa_report->data;
+	iov.iov_len = pa_report->data_len;
+
+	ad_structure = util_iov_pull_mem(&iov, sizeof(*ad_structure));
+	if (ad_structure->ad_type != BT_AD_SERVICE_DATA16)
+		return;
+
+	uuid = bt_get_le16(util_iov_pull_mem(&iov, sizeof(uint16_t)));
+	if (uuid != BASIC_AUDIO_ANNOUNCEMENT_SERVICE_UUID)
+		return;
+
+	base_data = util_iov_pull_mem(&iov, sizeof(*base_data));
+
+	for (int i = 0; i < base_data->num_subgroups; i++) {
+		struct bt_hci_le_pa_base_subgroup *subgroup;
+		struct bt_hci_lv_data *codec_cfg;
+		struct bt_hci_lv_data *metadata;
+
+		subgroup = util_iov_pull_mem(&iov, sizeof(*subgroup));
+
+		codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg));
+		util_iov_pull_mem(&iov, codec_cfg->len);
+
+		metadata = util_iov_pull_mem(&iov, sizeof(*metadata));
+		util_iov_pull_mem(&iov, metadata->len);
+
+		for (int j = 0; j < subgroup->num_bis; j++) {
+			struct bt_hci_le_pa_base_bis *bis;
+			struct bt_hci_lv_data *codec_cfg;
+
+			bis = util_iov_pull_mem(&iov, sizeof(*bis));
+			*bis_index = bis->index;
+			bis_index++;
+
+			codec_cfg = util_iov_pull_mem(&iov,
+						sizeof(*codec_cfg));
+			util_iov_pull_mem(&iov, codec_cfg->len);
+		}
+	}
+}
+
+static void bass_fill_bcst_source_from_pa_report(
+				struct bt_bcst_source *bcst_source)
+{
+	struct iovec iov;
+	struct bt_ad_structure *ad_structure;
+	uint16_t uuid;
+	struct bt_hci_le_pa_base_data *base_data;
+
+	if (!pa_report)
+		return;
+
+	iov.iov_base = pa_report->data;
+	iov.iov_len = pa_report->data_len;
+
+	ad_structure = util_iov_pull_mem(&iov, sizeof(*ad_structure));
+	if (ad_structure->ad_type != BT_AD_SERVICE_DATA16)
+		return;
+
+	uuid = bt_get_le16(util_iov_pull_mem(&iov, sizeof(uint16_t)));
+	if (uuid != BASIC_AUDIO_ANNOUNCEMENT_SERVICE_UUID)
+		return;
+
+	base_data = util_iov_pull_mem(&iov, sizeof(*base_data));
+
+	if (bcst_source->sync_state == BT_BASS_NOT_SYNCHRONIZED_TO_PA) {
+		bcst_source->sync_state = BT_BASS_SYNCHRONIZED_TO_PA;
+		bcst_source->num_subgroups = base_data->num_subgroups;
+
+		bcst_source->subgroup_data = malloc(base_data->num_subgroups *
+						sizeof(struct bt_bcst_source));
+		if (!bcst_source->subgroup_data)
+			return;
+
+		memset(bcst_source->subgroup_data, 0, base_data->num_subgroups *
+						sizeof(struct bt_bcst_source));
+	}
+
+	if (bcst_source->num_subgroups != base_data->num_subgroups)
+		return;
+
+	for (int i = 0; i < base_data->num_subgroups; i++) {
+		struct bt_hci_le_pa_base_subgroup *subgroup;
+		struct bt_hci_lv_data *codec_cfg;
+		struct bt_hci_lv_data *metadata;
+
+		subgroup = util_iov_pull_mem(&iov, sizeof(*subgroup));
+		codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg));
+		util_iov_pull_mem(&iov, codec_cfg->len);
+
+		metadata = util_iov_pull_mem(&iov, sizeof(*metadata));
+		util_iov_pull_mem(&iov, metadata->len);
+
+		bcst_source->subgroup_data[i].meta_len = metadata->len;
+		free(bcst_source->subgroup_data[i].meta);
+
+		bcst_source->subgroup_data[i].meta = malloc(metadata->len);
+		if (!bcst_source->subgroup_data[i].meta)
+			return;
+
+		memcpy(bcst_source->subgroup_data[i].meta,
+					metadata->data, metadata->len);
+
+		for (int j = 0; j < subgroup->num_bis; j++) {
+			struct bt_hci_le_pa_base_bis *bis;
+			struct bt_hci_lv_data *codec_cfg;
+
+			bis = util_iov_pull_mem(&iov, sizeof(*bis));
+			codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg));
+			util_iov_pull_mem(&iov, codec_cfg->len);
+		}
+	}
+}
+
+static void bass_fill_bcst_source_bis_sync_bitmask(
+					struct bt_bcst_source *bcst_source)
+{
+	struct iovec iov;
+	struct bt_ad_structure *ad_structure;
+	uint16_t uuid;
+	struct bt_hci_le_pa_base_data *base_data;
+
+	if (!pa_report)
+		return;
+
+	iov.iov_base = pa_report->data;
+	iov.iov_len = pa_report->data_len;
+
+	ad_structure = util_iov_pull_mem(&iov, sizeof(*ad_structure));
+	if (ad_structure->ad_type != BT_AD_SERVICE_DATA16)
+		return;
+
+	uuid = bt_get_le16(util_iov_pull_mem(&iov, sizeof(uint16_t)));
+	if (uuid != BASIC_AUDIO_ANNOUNCEMENT_SERVICE_UUID)
+		return;
+
+	base_data = util_iov_pull_mem(&iov, sizeof(*base_data));
+
+	for (int i = 0; i < base_data->num_subgroups; i++) {
+		struct bt_hci_le_pa_base_subgroup *subgroup;
+		struct bt_hci_lv_data *codec_cfg;
+		struct bt_hci_lv_data *metadata;
+
+		subgroup = util_iov_pull_mem(&iov, sizeof(*subgroup));
+
+		codec_cfg = util_iov_pull_mem(&iov, sizeof(*codec_cfg));
+		util_iov_pull_mem(&iov, codec_cfg->len);
+
+		metadata = util_iov_pull_mem(&iov, sizeof(*metadata));
+		util_iov_pull_mem(&iov, metadata->len);
+
+		for (int j = 0; j < subgroup->num_bis; j++) {
+			struct bt_hci_le_pa_base_bis *bis;
+			struct bt_hci_lv_data *codec_cfg;
+
+			bis = util_iov_pull_mem(&iov, sizeof(*bis));
+			bcst_source->subgroup_data[i].bis_sync |=
+						(1 << (bis->index - 1));
+
+			codec_cfg = util_iov_pull_mem(&iov,
+						sizeof(*codec_cfg));
+			util_iov_pull_mem(&iov, codec_cfg->len);
+		}
+	}
+}
+
+static void bass_handle_set_broadcast_code_opcode(struct bt_bass *bass,
+						struct gatt_db_attribute *attrib,
+						uint8_t opcode,
+						unsigned int id,
+						struct iovec *iov,
+						struct bt_att *att)
+{
+	struct bt_bap *bap = bap_get_session(att, bass->bdb->db);
+	struct bt_bass_set_bcst_code_params *params;
+	struct bt_bcst_source *bcst_source;
+	uint8_t *notify_data;
+	size_t notify_data_len;
+	struct hci_request rq;
+
+	struct bt_hci_evt_le_big_sync_estabilished *big_sync_established_evt = NULL;
+	int big_sync_established_evt_len = 0;
+
+	if (!big_sync_cmd)
+		goto done;
+
+	big_sync_established_evt_len =
+			sizeof(struct bt_hci_evt_le_big_sync_estabilished) +
+			big_sync_cmd->num_bis * sizeof(uint16_t);
+
+	big_sync_established_evt = malloc(big_sync_established_evt_len);
+	if (big_sync_established_evt == NULL)
+		goto done;
+
+	/* validate Set Broadcast_Code command length */
+	if (iov->iov_len < sizeof(struct bt_bass_set_bcst_code_params)) {
+		if (opcode == BT_ATT_OP_WRITE_REQ)
+			gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+
+		goto done;
+	}
+
+	/* get Set Broadcast_Code command parameters */
+	params = util_iov_pull_mem(iov, sizeof(*params));
+
+	bcst_source = queue_find(bap->ldb->bass_bcst_sources,
+						bass_src_id_match,
+						&params->source_id);
+
+	if (bcst_source == NULL) {
+		/* 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);
+
+		goto done;
+	}
+
+	memcpy(big_sync_cmd->bcode, params->bcst_code,
+			BT_BAP_BROADCAST_CODE_SIZE);
+
+	rq.ogf		= OGF_LE_CTL;
+	rq.ocf		= OCF_LE_BIG_CREATE_SYNC;
+	rq.event	= BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED;
+	rq.cparam	= big_sync_cmd;
+	rq.clen		= big_sync_cmd_len;
+	rq.rparam	= big_sync_established_evt;
+	rq.rlen		= big_sync_established_evt_len;
+
+	if (hci_send_req(hci_fd, &rq, 0) < 0) {
+		DBG(bap, "Failed to send Big Sync Create command: %s",
+						strerror(errno));
+		goto done;
+	}
+
+	if (big_sync_established_evt->status == 0x00) {
+		bcst_source->encryption = BT_BASS_BIG_ENC_STATE_DEC;
+
+		bass_fill_bcst_source_bis_sync_bitmask(bcst_source);
+	} else {
+		bcst_source->encryption = BT_BASS_BIG_ENC_STATE_BAD_CODE;
+		memcpy(bcst_source->bad_code, params->bcst_code,
+					BT_BAP_BROADCAST_CODE_SIZE);
+	}
+
+	notify_data = bass_build_notif_from_bcst_source(bcst_source,
+							&notify_data_len);
+
+	gatt_db_attribute_notify(bcst_source->attr,
+					(void *)notify_data,
+					notify_data_len, att);
+
+	free(notify_data);
+
+done:
+
+	free(big_sync_cmd);
+	big_sync_cmd = NULL;
+	free(big_sync_established_evt);
+	free(pa_report);
+	pa_report = NULL;
+	big_sync_cmd_len = 0;
+}
+
+#define BASS_OP(_str, _op, _size, _func) \
+	{ \
+		.str = _str, \
+		.op = _op, \
+		.size = _size, \
+		.func = _func, \
+	}
+
+struct bass_op_handler {
+	const char	*str;
+	uint8_t		op;
+	size_t		size;
+	void		(*func)(struct bt_bass *bass,
+						struct gatt_db_attribute *attrib,
+						uint8_t opcode,
+						unsigned int id,
+						struct iovec *iov,
+						struct bt_att *att);
+} bass_handlers[] = {
+	BASS_OP("Set Broadcast_Code", BT_BASS_SET_BCST_CODE,
+		sizeof(struct bt_bass_set_bcst_code_params),
+		bass_handle_set_broadcast_code_opcode)
+};
+
+static void bass_broadcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
+				unsigned int id, uint16_t offset,
+				const uint8_t *value, size_t len,
+				uint8_t opcode, struct bt_att *att,
+				void *user_data)
+{
+	struct bt_bass *bass = user_data;
+	struct bt_bass_bcst_audio_scan_cp_hdr *hdr;
+	struct bass_op_handler *handler;
+	struct iovec iov = {
+		.iov_base = (void *)value,
+		.iov_len = len,
+	};
+
+	/* validate written command length */
+	if (len < (sizeof(*hdr))) {
+		if (opcode == BT_ATT_OP_WRITE_REQ) {
+			gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		}
+		return;
+	}
+
+	/* get command header */
+	hdr = util_iov_pull_mem(&iov, sizeof(*hdr));
+
+	if (hdr->op != BT_BASS_SET_BCST_CODE) {
+		if (opcode == BT_ATT_OP_WRITE_REQ) {
+			gatt_db_attribute_write_result(attrib, id,
+					BT_BASS_ERROR_OPCODE_NOT_SUPPORTED);
+		}
+
+		return;
+	}
+
+	/* call the appropriate opcode handler */
+	for (handler = bass_handlers; handler && handler->str; handler++) {
+		if (handler->op == hdr->op) {
+			handler->func(bass, attrib, opcode, id, &iov, att);
+			break;
+		}
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0x00);
+}
+
+static bool bass_source_match_attrib(const void *data, const void *match_data)
+{
+	const struct bt_bcst_source *src = data;
+	const struct gatt_db_attribute *attr = match_data;
+
+	return (src->attr == attr);
+}
+
+static bool bass_source_match_sid(const void *data, const void *match_data)
+{
+	const struct bt_bcst_source *src = data;
+	const uint8_t sid = *(const uint8_t *)match_data;
+
+	return (src->sid == sid);
+}
+
+static void bass_bcst_receive_state_read(struct gatt_db_attribute *attrib,
+							unsigned int id, uint16_t offset,
+							uint8_t opcode, struct bt_att *att,
+							void *user_data)
+{
+	struct bt_bass *bass = user_data;
+	struct bt_bap *bap = bap_get_session(att, bass->bdb->db);
+	uint8_t *rsp;
+	size_t rsp_len;
+	struct bt_bcst_source *bcst_source;
+
+	bcst_source = queue_find(bap->ldb->bass_bcst_sources,
+							bass_source_match_attrib,
+							attrib);
+
+	if (!bcst_source) {
+		gatt_db_attribute_read_result(attrib, id, 0, NULL,
+							0);
+		return;
+	}
+
+	/* build read response */
+	rsp = bass_build_read_rsp_from_bcst_source(bcst_source, &rsp_len);
+
+	if (!rsp) {
+		gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+								NULL, 0);
+		return;
+	}
+
+	gatt_db_attribute_read_result(attrib, id, 0, (void *)rsp,
+						rsp_len);
+
+	free(rsp);
+}
+
+static void bcst_recv_new(struct bt_bass *bass, int i)
+{
+	struct bt_bcst_recv_state *bcst_recv_state;
+	bt_uuid_t uuid;
+
+	if (!bass)
+		return;
+
+	bcst_recv_state = new0(struct bt_bcst_recv_state, 1);
+	bcst_recv_state->bass = bass;
+
+	bt_uuid16_create(&uuid, BCST_RECV_STATE_UUID);
+	bcst_recv_state->attr = gatt_db_service_add_characteristic(bass->service, &uuid,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_NOTIFY,
+					bass_bcst_receive_state_read, NULL,
+					bass);
+
+	bcst_recv_state->ccc = gatt_db_service_add_ccc(bass->service,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+	bass->bcst_recv_states[i] = bcst_recv_state;
+}
+
+static struct bt_bass *bass_new(struct gatt_db *db)
+{
+	struct bt_bass *bass;
+	bt_uuid_t uuid;
+	int i;
+
+	if (!db)
+		return NULL;
+
+	bass = new0(struct bt_bass, 1);
+
+	/* Populate DB with BASS attributes */
+	bt_uuid16_create(&uuid, BASS_UUID);
+	bass->service = gatt_db_add_service(db, &uuid, true,
+						3 + (NUM_BCST_RECV_STATES * 3));
+
+	for (i = 0; i < NUM_BCST_RECV_STATES; i++)
+		bcst_recv_new(bass, i);
+
+	bt_uuid16_create(&uuid, BCST_AUDIO_SCAN_CP_UUID);
+	bass->broadcast_audio_scan_cp = gatt_db_service_add_characteristic(bass->service,
+							&uuid,
+							BT_ATT_PERM_WRITE,
+							BT_GATT_CHRC_PROP_WRITE,
+							NULL, bass_broadcast_audio_scan_cp_write,
+							bass);
+
+	gatt_db_service_set_active(bass->service, true);
+
+	return bass;
+}
+
 static struct bt_bap_db *bap_db_new(struct gatt_db *db)
 {
 	struct bt_bap_db *bdb;
@@ -2182,6 +2874,7 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
 	bdb->sinks = queue_new();
 	bdb->sources = queue_new();
 	bdb->endpoints = queue_new();
+	bdb->bass_bcst_sources = queue_new();
 
 	if (!bap_db)
 		bap_db = queue_new();
@@ -2192,6 +2885,9 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
 	bdb->ascs = ascs_new(db);
 	bdb->ascs->bdb = bdb;
 
+	bdb->bass = bass_new(db);
+	bdb->bass->bdb = bdb;
+
 	queue_push_tail(bap_db, bdb);
 
 	return bdb;
@@ -2236,6 +2932,20 @@ static struct bt_ascs *bap_get_ascs(struct bt_bap *bap)
 	return bap->rdb->ascs;
 }
 
+static struct bt_bass *bap_get_bass(struct bt_bap *bap)
+{
+	if (!bap)
+		return NULL;
+
+	if (bap->rdb->bass)
+		return bap->rdb->bass;
+
+	bap->rdb->bass = new0(struct bt_bass, 1);
+	bap->rdb->bass->bdb = bap->rdb;
+
+	return bap->rdb->bass;
+}
+
 static bool match_codec(const void *data, const void *user_data)
 {
 	const struct bt_bap_pac *pac = data;
@@ -2321,6 +3031,17 @@ static void bap_pac_free(void *data)
 	free(pac);
 }
 
+static void bass_bcst_source_free(void *data)
+{
+	struct bt_bcst_source *bcst_source = data;
+
+	for (int i = 0; i < bcst_source->num_subgroups; i++)
+		free(bcst_source->subgroup_data[i].meta);
+
+	free(bcst_source->subgroup_data);
+	free(bcst_source);
+}
+
 static void bap_add_sink(struct bt_bap_pac *pac)
 {
 	struct iovec iov;
@@ -2512,10 +3233,12 @@ static void bap_db_free(void *data)
 	queue_destroy(bdb->sinks, bap_pac_free);
 	queue_destroy(bdb->sources, bap_pac_free);
 	queue_destroy(bdb->endpoints, free);
+	queue_destroy(bdb->bass_bcst_sources, bass_bcst_source_free);
 	gatt_db_unref(bdb->db);
 
 	free(bdb->pacs);
 	free(bdb->ascs);
+	free(bdb->bass);
 	free(bdb);
 }
 
@@ -2663,6 +3386,7 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
 	bdb->sinks = queue_new();
 	bdb->sources = queue_new();
 	bdb->endpoints = queue_new();
+	bdb->bass_bcst_sources = queue_new();
 
 	bap->rdb = bdb;
 
@@ -3670,6 +4394,119 @@ static void foreach_ascs_char(struct gatt_db_attribute *attr, void *user_data)
 	}
 }
 
+static void read_broadcast_receive_state(struct bt_bap *bap, bool success, uint8_t att_ecode,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct gatt_db_attribute *attr = user_data;
+	struct bt_bcst_source *bcst_source = NULL;
+
+	if (!success) {
+		DBG(bap, "Unable to read Broadcast Receive State: error 0x%02x", att_ecode);
+		return;
+	}
+
+	if (length == 0)
+		return;
+
+	bcst_source = queue_find(bap->rdb->bass_bcst_sources,
+				bass_source_match_attrib, attr);
+
+	if (!bcst_source) {
+		bcst_source = malloc(sizeof(struct bt_bcst_source));
+
+		if (bcst_source == NULL) {
+			DBG(bap, "Failed to allocate memory for broadcast source");
+			return;
+		}
+
+		memset(bcst_source, 0, sizeof(struct bt_bcst_source));
+		bcst_source->attr = attr;
+
+		queue_push_tail(bap->rdb->bass_bcst_sources, bcst_source);
+	}
+
+	if (bass_build_bcst_source_from_read_rsp(bcst_source, value)) {
+		free(bcst_source);
+		DBG(bap, "Failed to populate broadcast source data");
+		return;
+	}
+}
+
+static void bcst_recv_state_notify(struct bt_bap *bap, uint16_t value_handle,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct gatt_db_attribute *attr = user_data;
+	struct bt_bcst_source *bcst_source = NULL;
+
+	bcst_source = queue_find(bap->rdb->bass_bcst_sources,
+				bass_source_match_attrib, attr);
+
+	if (!bcst_source) {
+		bcst_source = malloc(sizeof(struct bt_bcst_source));
+
+		if (bcst_source == NULL) {
+			DBG(bap, "Failed to allocate memory for broadcast source");
+			return;
+		}
+
+		memset(bcst_source, 0, sizeof(struct bt_bcst_source));
+		bcst_source->attr = attr;
+
+		queue_push_tail(bap->rdb->bass_bcst_sources, bcst_source);
+	}
+
+	if (bass_build_bcst_source_from_notif(bcst_source, value)) {
+		free(bcst_source);
+		DBG(bap, "Failed to populate broadcast source data");
+		return;
+	}
+}
+
+static void foreach_bass_char(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct bt_bap *bap = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid, uuid_bcst_audio_scan_cp, uuid_bcst_recv_state;
+	struct bt_bass *bass;
+
+	/* get attribute value handle and uuid */
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+						NULL, NULL, &uuid))
+		return;
+
+	bt_uuid16_create(&uuid_bcst_audio_scan_cp, BCST_AUDIO_SCAN_CP_UUID);
+	bt_uuid16_create(&uuid_bcst_recv_state, BCST_RECV_STATE_UUID);
+
+	if (!bt_uuid_cmp(&uuid, &uuid_bcst_audio_scan_cp)) {
+
+		/* found Broadcast Audio Scan Control Point characteristic */
+		bass = bap_get_bass(bap);
+
+		if (!bass || bass->broadcast_audio_scan_cp)
+			return;
+
+		/* store characteristic reference */
+		bass->broadcast_audio_scan_cp = attr;
+
+		DBG(bap, "Broadcast Audio Scan Control Point found: handle 0x%04x",
+							value_handle);
+	}
+
+	if (!bt_uuid_cmp(&uuid, &uuid_bcst_recv_state)) {
+
+		/* found Broadcast Receive State characteristic */
+		bap_read_value(bap, value_handle, read_broadcast_receive_state, attr);
+
+		(void)bap_register_notify(bap, value_handle,
+						bcst_recv_state_notify, attr);
+
+		DBG(bap, "Broadcast receive State found: handle 0x%04x",
+							value_handle);
+	}
+}
+
 static void foreach_ascs_service(struct gatt_db_attribute *attr,
 						void *user_data)
 {
@@ -3683,6 +4520,19 @@ static void foreach_ascs_service(struct gatt_db_attribute *attr,
 	gatt_db_service_foreach_char(attr, foreach_ascs_char, bap);
 }
 
+static void foreach_bass_service(struct gatt_db_attribute *attr,
+						void *user_data)
+{
+	struct bt_bap *bap = user_data;
+	struct bt_bass *bass = bap_get_bass(bap);
+
+	/* store BASS attribute reference */
+	bass->service = attr;
+
+	/* handle BASS attributes */
+	gatt_db_service_foreach_char(attr, foreach_bass_char, bap);
+}
+
 static void bap_endpoint_foreach(void *data, void *user_data)
 {
 	struct bt_bap_endpoint *ep = data;
@@ -3778,6 +4628,9 @@ clone:
 	bt_uuid16_create(&uuid, ASCS_UUID);
 	gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_ascs_service, bap);
 
+	bt_uuid16_create(&uuid, BASS_UUID);
+	gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_bass_service, bap);
+
 	return true;
 }
 
@@ -4834,3 +5687,215 @@ bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd)
 
 	return io->connecting;
 }
+
+void bap_big_info_adv_report_received_cb(struct gatt_db *db, uint8_t source_id,
+		struct bt_hci_evt_le_big_info_adv_report *big_info_adv_report)
+{
+	struct bt_bap_db *bdb = bap_get_db(db);
+	struct bt_bcst_source *bcst_source = NULL;
+	size_t notify_data_len = 0;
+	uint8_t *notify_data;
+	struct hci_request rq;
+	struct bt_hci_evt_le_big_sync_estabilished *big_sync_established_evt = NULL;
+	int big_sync_established_evt_len = 0;
+
+	if (!pa_report)
+		return;
+
+	bcst_source = queue_find(bdb->bass_bcst_sources,
+					bass_src_id_match,
+					&source_id);
+
+	if (bcst_source == NULL) {
+		free(pa_report);
+		pa_report = NULL;
+		return;
+	}
+
+	big_sync_cmd_len = sizeof(struct bt_hci_cmd_le_big_create_sync)
+				+ big_info_adv_report->num_bis *
+				sizeof(struct bt_hci_bis_sync);
+
+	free(big_sync_cmd);
+	big_sync_cmd = malloc(big_sync_cmd_len);
+
+	if (!big_sync_cmd) {
+		free(pa_report);
+		pa_report = NULL;
+		return;
+	}
+
+	memset(big_sync_cmd, 0, big_sync_cmd_len);
+
+	big_sync_cmd->sync_handle = big_info_adv_report->sync_handle;
+	big_sync_cmd->encryption = big_info_adv_report->encryption;
+	big_sync_cmd->timeout = 0x4000;
+	big_sync_cmd->num_bis = big_info_adv_report->num_bis;
+
+	bass_fill_create_big_sync_cmd_from_pa_report(big_sync_cmd);
+
+	if (big_info_adv_report->encryption == 0x01) {
+		bcst_source->encryption = BT_BASS_BIG_ENC_STATE_BCODE_REQ;
+	} else {
+		bcst_source->encryption = BT_BASS_BIG_ENC_STATE_NO_ENC;
+
+		big_sync_established_evt_len =
+				sizeof(struct bt_hci_evt_le_big_sync_estabilished) +
+				big_sync_cmd->num_bis * sizeof(uint16_t);
+
+		big_sync_established_evt = malloc(big_sync_established_evt_len);
+		if (big_sync_established_evt == NULL) {
+			free(pa_report);
+			pa_report = NULL;
+
+			free(big_sync_cmd);
+			big_sync_cmd = NULL;
+			big_sync_cmd_len = 0;
+
+			goto done;
+		}
+
+		/* create BIG sync */
+		rq.ogf		= OGF_LE_CTL;
+		rq.ocf		= 0x006B;
+		rq.event	= BT_HCI_EVT_LE_BIG_SYNC_ESTABILISHED;
+		rq.cparam	= big_sync_cmd;
+		rq.clen		= big_sync_cmd_len;
+		rq.rparam	= big_sync_established_evt;
+		rq.rlen		= big_sync_established_evt_len;
+
+		if (hci_send_req(hci_fd, &rq, 0) < 0) {
+			free(pa_report);
+			pa_report = NULL;
+
+			free(big_sync_cmd);
+			big_sync_cmd = NULL;
+			big_sync_cmd_len = 0;
+
+			free(big_sync_established_evt);
+
+			goto done;
+		}
+
+		if (!big_sync_established_evt->status)
+			bass_fill_bcst_source_bis_sync_bitmask(bcst_source);
+	}
+
+done:
+
+	notify_data = bass_build_notif_from_bcst_source(bcst_source,
+							&notify_data_len);
+
+	gatt_db_attribute_notify(bcst_source->attr, (void *)notify_data,
+				notify_data_len, NULL);
+
+	free(notify_data);
+}
+
+void bap_pa_report_received_cb(struct gatt_db *db, uint8_t source_id,
+				struct bt_hci_le_pa_report *report)
+{
+	struct bt_bcst_source *bcst_source = NULL;
+	struct bt_bap_db *bdb = bap_get_db(db);
+	uint8_t report_len = sizeof(struct bt_hci_le_pa_report) +
+						report->data_len;
+
+	printf("Report len = %d\n\n", report->data_len);
+
+	free(pa_report);
+	pa_report = malloc(report_len);
+	if (!pa_report)
+		return;
+
+	memcpy(pa_report, report, report_len);
+
+	bcst_source = queue_find(bdb->bass_bcst_sources,
+					bass_src_id_match,
+					&source_id);
+
+	if (!bcst_source)
+		return;
+
+	bass_fill_bcst_source_from_pa_report(bcst_source);
+}
+
+int bap_ext_adv_report_received_cb(struct gatt_db *db,
+				struct bt_hci_le_ext_adv_report *ext_adv_report)
+{
+	struct bt_bcst_source *bcst_source = NULL;
+	struct gatt_db_attribute *attr = NULL;
+	struct bt_bap_db *bdb = bap_get_db(db);
+	struct bt_ad_structure *ad_structure;
+	uint16_t uuid;
+	uint8_t *bid;
+
+	struct iovec iov = {
+		.iov_base = ext_adv_report->data,
+		.iov_len = ext_adv_report->data_len,
+	};
+
+	bcst_source = queue_find(bdb->bass_bcst_sources,
+					bass_source_match_sid,
+					&ext_adv_report->sid);
+
+	if (bcst_source != NULL)
+		return -1;
+
+	bcst_source = malloc(sizeof(struct bt_bcst_source));
+
+	if (bcst_source == NULL)
+		return -1;
+
+	memset(bcst_source, 0, sizeof(struct bt_bcst_source));
+
+	bcst_source->id = next_available_source_id++;
+	bcst_source->addr_type = ext_adv_report->addr_type;
+	memcpy(bcst_source->addr, ext_adv_report->addr, 6);
+	bcst_source->sid = ext_adv_report->sid;
+
+	ad_structure = util_iov_pull_mem(&iov, sizeof(*ad_structure));
+
+	while (ad_structure) {
+		if (ad_structure->ad_type == BT_AD_SERVICE_DATA16) {
+			uuid = bt_get_le16(util_iov_pull_mem(&iov, sizeof(uint16_t)));
+			if (uuid == BCST_AUDIO_ANNOUNCEMENT_SERVICE_UUID) {
+				bid = util_iov_pull_mem(&iov,
+							BT_BAP_BROADCAST_ID_SIZE);
+				memcpy(bcst_source->bid, bid,
+							BT_BAP_BROADCAST_ID_SIZE);
+				break;
+			}
+		}
+
+		ad_structure = util_iov_pull_mem(&iov, sizeof(*ad_structure));
+	}
+
+	for (int i = 0; i < NUM_BCST_RECV_STATES; i++) {
+		attr = bdb->bass->bcst_recv_states[i]->attr;
+
+		if (queue_find(bdb->bass_bcst_sources,
+			bass_source_match_attrib, attr) == NULL) {
+			bcst_source->attr = attr;
+			break;
+		}
+	}
+
+	queue_push_tail(bdb->bass_bcst_sources, bcst_source);
+
+	return bcst_source->id;
+}
+
+int bt_bap_register_device(int dev_id)
+{
+	/* Open HCI */
+	hci_fd = hci_open_dev(dev_id);
+	if (hci_fd == -1)
+		return -1;
+
+	return 0;
+}
+
+void bt_bap_register_db(struct gatt_db *db)
+{
+	bap_db_new(db);
+}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 47a15636c..f72ecbd76 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -9,6 +9,7 @@
 
 #include <stdbool.h>
 #include <inttypes.h>
+#include "monitor/bt.h"
 
 #ifndef __packed
 #define __packed __attribute__((packed))
@@ -33,6 +34,9 @@
 #define BT_BAP_CONFIG_PHY_2M		0x02
 #define BT_BAP_CONFIG_PHY_CODEC		0x03
 
+#define BT_BAP_BROADCAST_CODE_SIZE	16
+#define BT_BAP_BROADCAST_ID_SIZE	3
+
 struct bt_bap;
 struct bt_bap_pac;
 struct bt_bap_stream;
@@ -62,6 +66,17 @@ struct bt_bap_qos {
 	uint8_t  target_latency;	/* Target Latency */
 };
 
+struct bt_ad_structure {
+	uint8_t  ad_len;
+	uint8_t  ad_type;
+	uint8_t  value[0];
+} __packed;
+
+struct bt_broadcast_audio_announcement {
+	uint16_t uuid;
+	uint8_t  bid[BT_BAP_BROADCAST_ID_SIZE];
+} __packed;
+
 typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data);
 typedef void (*bt_bap_destroy_func_t)(void *user_data);
 typedef void (*bt_bap_debug_func_t)(const char *str, void *user_data);
@@ -267,3 +282,13 @@ uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream);
 
 int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd);
 bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);
+
+int bap_ext_adv_report_received_cb(struct gatt_db *db,
+				struct bt_hci_le_ext_adv_report *ext_adv_report);
+void bap_pa_report_received_cb(struct gatt_db *db, uint8_t source_id,
+					struct bt_hci_le_pa_report *report);
+void bap_big_info_adv_report_received_cb(struct gatt_db *db, uint8_t source_id,
+		struct bt_hci_evt_le_big_info_adv_report *big_info_adv_report);
+
+int bt_bap_register_device(int dev_id);
+void bt_bap_register_db(struct gatt_db *db);
diff --git a/src/shared/bass.h b/src/shared/bass.h
new file mode 100644
index 000000000..cdcb5a700
--- /dev/null
+++ b/src/shared/bass.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2022  Intel Corporation. All rights reserved.
+ *
+ */
+
+#define NUM_BCST_RECV_STATES				2
+
+/* Application error codes */
+#define BT_BASS_ERROR_OPCODE_NOT_SUPPORTED		0x80
+
+#define BT_BASS_ERROR_INVALID_SOURCE_ID			0x81
+
+/* PA_Sync_State values */
+#define BT_BASS_NOT_SYNCHRONIZED_TO_PA			0x00
+#define BT_BASS_SYNC_INFO_RE				0x01
+#define BT_BASS_SYNCHRONIZED_TO_PA			0x02
+#define BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA		0x03
+#define BT_BASS_NO_PAST					0x04
+
+/* BIG_Encryption values */
+#define BT_BASS_BIG_ENC_STATE_NO_ENC			0x00
+#define BT_BASS_BIG_ENC_STATE_BCODE_REQ			0x01
+#define BT_BASS_BIG_ENC_STATE_DEC			0x02
+#define BT_BASS_BIG_ENC_STATE_BAD_CODE			0x03
+
+/*
+ * Broadcast Audio Scan Control Point
+ * header structure
+ */
+struct bt_bass_bcst_audio_scan_cp_hdr {
+	uint8_t op;
+} __packed;
+
+#define BT_BASS_SET_BCST_CODE				0x04
+
+struct bt_bass_set_bcst_code_params {
+	uint8_t source_id;
+	uint8_t bcst_code[BT_BAP_BROADCAST_CODE_SIZE];
+} __packed;
-- 
2.34.1




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux