2023-10-04 15:32:13

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ 0/4] Add BASS unit tests for the SPE suite

This patch series adds BASS unit tests for the Service Procedures Error
Handling suite.

Some unit tests require the BASS Server to synchronize to a broadcast
source, as instructed by a BASS Client. For this, a testing framework
was added similar to the one used by iso-tester, to emulate hardware.

Iulia Tanasescu (4):
btio: Bind listener to bcaster addr based on dst opt
hciemu: Add support for setting emulator bdaddr
shared/bass: Add miscellaneous fixes
test-bass: Add unit tests for the SPE suite

Makefile.am | 7 +-
btio/btio.c | 24 +-
emulator/hciemu.c | 9 +
emulator/hciemu.h | 2 +
profiles/audio/bap.c | 4 +-
src/shared/bass.c | 179 +++++++---
src/shared/bass.h | 1 +
unit/test-bass.c | 781 ++++++++++++++++++++++++++++++++++++++++++-
8 files changed, 942 insertions(+), 65 deletions(-)


base-commit: 5ab5352531a9cc7058cce569607f3a6831464443
--
2.39.2


2023-10-04 15:32:17

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ 2/4] hciemu: Add support for setting emulator bdaddr

This adds support for the user to explicitly set a desired bdaddr to
a client emulator.

---
emulator/hciemu.c | 9 +++++++++
emulator/hciemu.h | 2 ++
2 files changed, 11 insertions(+)

diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index 25874ded5..f13b4bda1 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -550,6 +550,15 @@ const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client)
return btdev_get_bdaddr(client->dev);
}

+bool hciemu_set_client_bdaddr(struct hciemu_client *client,
+ const uint8_t *bdaddr)
+{
+ if (!client)
+ return NULL;
+
+ return btdev_set_bdaddr(client->dev, bdaddr);
+}
+
const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu)
{
struct hciemu_client *client;
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index 3449eae41..dba920fdd 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -39,6 +39,8 @@ void hciemu_unref(struct hciemu *hciemu);
struct hciemu_client *hciemu_get_client(struct hciemu *hciemu, int num);
struct bthost *hciemu_client_host(struct hciemu_client *client);
const uint8_t *hciemu_client_bdaddr(struct hciemu_client *client);
+bool hciemu_set_client_bdaddr(struct hciemu_client *client,
+ const uint8_t *bdaddr);

typedef void (*hciemu_debug_func_t)(const char *str, void *user_data);
typedef void (*hciemu_destroy_func_t)(void *user_data);
--
2.39.2

2023-10-04 15:32:17

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ 1/4] btio: Bind listener to bcaster addr based on dst opt

This updates the btio module to decide whether to bind a listening socket
to a broadcaster address or not, by looking at the dst address.

---
btio/btio.c | 24 ++++++++++++++----------
profiles/audio/bap.c | 4 ++--
2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index c6d056b89..d45b8240d 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -774,16 +774,21 @@ static int sco_bind(int sock, const bdaddr_t *src, GError **err)
return 0;
}

-static int iso_bind(int sock, const bdaddr_t *src, uint8_t src_type,
- const bdaddr_t *dst, uint8_t dst_type,
- uint8_t bc_sid, uint8_t num_bis,
- uint8_t *bis, GError **err)
+static int iso_bind(int sock, bool server, const bdaddr_t *src,
+ uint8_t src_type, 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 = NULL;
size_t addr_len;
int ret = 0;

- if (num_bis)
+ /* If this is an ISO listener and the destination address
+ * is not BDADDR_ANY, the listener should be bound to the
+ * broadcaster address
+ */
+ if (server && bacmp(dst, BDADDR_ANY))
addr_len = sizeof(*addr) + sizeof(*addr->iso_bc);
else
addr_len = sizeof(*addr);
@@ -798,7 +803,7 @@ static int iso_bind(int sock, const bdaddr_t *src, uint8_t src_type,
bacpy(&addr->iso_bdaddr, src);
addr->iso_bdaddr_type = src_type;

- if (num_bis) {
+ if (addr_len > sizeof(*addr)) {
bacpy(&addr->iso_bc->bc_bdaddr, dst);
addr->iso_bc->bc_bdaddr_type = dst_type;
addr->iso_bc->bc_sid = bc_sid;
@@ -1930,10 +1935,9 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts,
return NULL;
}

- 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)
+ if (iso_bind(sock, server, &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/profiles/audio/bap.c b/profiles/audio/bap.c
index d70ad872e..cab68f779 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -1566,9 +1566,9 @@ static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(adapter),
BT_IO_OPT_DEST_BDADDR,
- device_get_address(data->device),
+ BDADDR_ANY,
BT_IO_OPT_DEST_TYPE,
- device_get_le_address_type(data->device),
+ BDADDR_LE_PUBLIC,
BT_IO_OPT_MODE, BT_IO_MODE_ISO,
BT_IO_OPT_QOS, qos,
BT_IO_OPT_INVALID);
--
2.39.2

2023-10-04 15:32:28

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ 3/4] shared/bass: Add miscellaneous fixes

This introduces miscellaneous BASS updates and fixes discovered
during PTS testing.

---
src/shared/bass.c | 179 +++++++++++++++++++++++++++++++++++-----------
src/shared/bass.h | 1 +
2 files changed, 140 insertions(+), 40 deletions(-)

diff --git a/src/shared/bass.c b/src/shared/bass.c
index 86dab03e3..288e1c7c5 100644
--- a/src/shared/bass.c
+++ b/src/shared/bass.c
@@ -504,8 +504,7 @@ static void bass_handle_remote_scan_stopped_op(struct bt_bass *bass,
struct iovec *iov,
struct bt_att *att)
{
- if (opcode == BT_ATT_OP_WRITE_REQ)
- gatt_db_attribute_write_result(attrib, id, 0x00);
+ gatt_db_attribute_write_result(attrib, id, 0x00);
}

static void bass_handle_remote_scan_started_op(struct bt_bass *bass,
@@ -515,8 +514,7 @@ static void bass_handle_remote_scan_started_op(struct bt_bass *bass,
struct iovec *iov,
struct bt_att *att)
{
- if (opcode == BT_ATT_OP_WRITE_REQ)
- gatt_db_attribute_write_result(attrib, id, 0x00);
+ gatt_db_attribute_write_result(attrib, id, 0x00);
}

static bool bass_src_id_match(const void *data, const void *match_data)
@@ -536,6 +534,7 @@ static void bass_handle_remove_src_op(struct bt_bass *bass,
{
struct bt_bass_remove_src_params *params;
struct bt_bcast_src *bcast_src;
+ int att_err = 0;

/* Get Remove Source command parameters */
params = util_iov_pull_mem(iov, sizeof(*params));
@@ -546,33 +545,31 @@ static void bass_handle_remove_src_op(struct bt_bass *bass,

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;
+ att_err = BT_BASS_ERROR_INVALID_SOURCE_ID;
+ goto done;
}

/* Ignore if server is synchronized to the PA
* of the source
*/
if (bcast_src->sync_state == BT_BASS_SYNCHRONIZED_TO_PA)
- return;
+ goto done;

/* Ignore if server is synchronized to any BIS
* of the source
*/
for (int i = 0; i < bcast_src->num_subgroups; i++)
if (bcast_src->subgroup_data[i].bis_sync)
- return;
+ goto done;

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

- if (opcode == BT_ATT_OP_WRITE_REQ)
- gatt_db_attribute_write_result(attrib, id, 0x00);
+done:
+ gatt_db_attribute_write_result(attrib, id,
+ att_err);
}

static bool bass_src_attr_match(const void *data, const void *match_data)
@@ -692,6 +689,21 @@ static void connect_cb(GIOChannel *io, GError *gerr,
free(notify_data);
}

+static bool bass_trigger_big_sync(struct bt_bcast_src *bcast_src)
+{
+ 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 &&
+ data->pending_bis_sync != BIS_SYNC_NO_PREF)
+ return true;
+ }
+
+ return false;
+}
+
+
static void confirm_cb(GIOChannel *io, gpointer user_data)
{
struct bt_bcast_src *bcast_src = user_data;
@@ -729,12 +741,17 @@ static void confirm_cb(GIOChannel *io, gpointer user_data)
/* 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);
+ if (bass_trigger_big_sync(bcast_src)) {
+ 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;
}
- return;
+
+ goto notify;
}

/* BIG is encrypted. Wait for Client to provide the Broadcast_Code */
@@ -773,6 +790,60 @@ static struct bt_bass *bass_get_session(struct bt_att *att, struct gatt_db *db,
return bass;
}

+static bool bass_validate_bis_sync(uint8_t num_subgroups,
+ struct iovec *iov)
+{
+ uint32_t bis_sync_state;
+ uint32_t bitmask = 0U;
+ uint8_t *meta_len;
+
+ for (int i = 0; i < num_subgroups; i++) {
+ util_iov_pull_le32(iov, &bis_sync_state);
+
+ if (bis_sync_state != BIS_SYNC_NO_PREF)
+ for (int bis_idx = 0; bis_idx < 31; bis_idx++) {
+ if (bis_sync_state & (1 << bis_idx)) {
+ if (bitmask & (1 << bis_idx))
+ return false;
+
+ bitmask |= (1 << bis_idx);
+ }
+ }
+
+ meta_len = util_iov_pull_mem(iov,
+ sizeof(*meta_len));
+ util_iov_pull_mem(iov, *meta_len);
+ }
+
+ return true;
+}
+
+static bool bass_validate_add_src_params(uint8_t *value, size_t len)
+{
+ struct bt_bass_add_src_params *params;
+ struct iovec iov = {
+ .iov_base = (void *)value,
+ .iov_len = len,
+ };
+
+ params = util_iov_pull_mem(&iov, sizeof(*params));
+
+ if (params->pa_sync > PA_SYNC_NO_PAST)
+ return false;
+
+ if (params->addr_type > 0x01)
+ return false;
+
+ if (params->sid > 0x0F)
+ return false;
+
+ if (!bass_validate_bis_sync(params->num_subgroups,
+ &iov))
+ return false;
+
+ return true;
+}
+
static void bass_handle_add_src_op(struct bt_bass *bass,
struct gatt_db_attribute *attrib,
uint8_t opcode,
@@ -791,9 +862,13 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
uint8_t bis[ISO_MAX_NUM_BIS];
uint8_t *notify_data;
size_t notify_data_len;
+ uint8_t addr_type;
+
+ gatt_db_attribute_write_result(attrib, id, 0x00);

- if (opcode == BT_ATT_OP_WRITE_REQ)
- gatt_db_attribute_write_result(attrib, id, 0x00);
+ /* Ignore operation if parameters are invalid */
+ if (!bass_validate_add_src_params(iov->iov_base, iov->iov_len))
+ return;

/* Allocate a new broadcast source */
bcast_src = malloc(sizeof(*bcast_src));
@@ -856,10 +931,8 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
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;
+ bcast_src->addr_type = *(uint8_t *)util_iov_pull_mem(iov,
+ sizeof(bcast_src->addr_type));

bacpy(&bcast_src->addr, (bdaddr_t *)util_iov_pull_mem(iov,
sizeof(bdaddr_t)));
@@ -919,7 +992,13 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
data->meta_len), data->meta_len);
}

- if (pa_sync != PA_SYNC_NO_SYNC && num_bis > 0) {
+ 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,
@@ -927,7 +1006,7 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
BT_IO_OPT_DEST_BDADDR,
&bcast_src->addr,
BT_IO_OPT_DEST_TYPE,
- bcast_src->addr_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,
@@ -944,7 +1023,7 @@ static void bass_handle_add_src_op(struct bt_bass *bass,
bcast_src->listen_io = io;
g_io_channel_ref(bcast_src->listen_io);

- if (!bcast_src->bises)
+ if (num_bis > 0 && !bcast_src->bises)
bcast_src->bises = queue_new();
} else {
for (int i = 0; i < bcast_src->num_subgroups; i++)
@@ -988,9 +1067,8 @@ static void bass_handle_set_bcast_code_op(struct bt_bass *bass,
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);
+ uint8_t *notify_data;
+ size_t notify_data_len;

/* Get Set Broadcast Code command parameters */
params = util_iov_pull_mem(iov, sizeof(*params));
@@ -1001,13 +1079,29 @@ static void bass_handle_set_bcast_code_op(struct bt_bass *bass,

if (!bcast_src) {
/* No source matches the written source id */
- if (opcode == BT_ATT_OP_WRITE_REQ)
- gatt_db_attribute_write_result(attrib, id,
+ gatt_db_attribute_write_result(attrib, id,
BT_BASS_ERROR_INVALID_SOURCE_ID);

return;
}

+ gatt_db_attribute_write_result(attrib, id, 0x00);
+
+ if (!bass_trigger_big_sync(bcast_src)) {
+ bcast_src->enc = BT_BASS_BIG_ENC_STATE_DEC;
+
+ 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;
+ }
+
/* Try to sync to the source using the
* received broadcast code
*/
@@ -1091,10 +1185,8 @@ static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,

/* Validate written command length */
if (!bass_check_cp_command_len(value, len)) {
- if (opcode == BT_ATT_OP_WRITE_REQ) {
- gatt_db_attribute_write_result(attrib, id,
- BT_ERROR_WRITE_REQUEST_REJECTED);
- }
+ gatt_db_attribute_write_result(attrib, id,
+ BT_ERROR_WRITE_REQUEST_REJECTED);
return;
}

@@ -1110,10 +1202,8 @@ static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
}

/* Send error response if unsupported opcode was written */
- if (opcode == BT_ATT_OP_WRITE_REQ) {
- gatt_db_attribute_write_result(attrib, id,
- BT_BASS_ERROR_OPCODE_NOT_SUPPORTED);
- }
+ gatt_db_attribute_write_result(attrib, id,
+ BT_BASS_ERROR_OPCODE_NOT_SUPPORTED);
}

static bool bass_src_match_attrib(const void *data, const void *match_data)
@@ -1459,6 +1549,15 @@ bool bt_bass_attach(struct bt_bass *bass, struct bt_gatt_client *client)
return true;
}

+bool bt_bass_set_att(struct bt_bass *bass, struct bt_att *att)
+{
+ if (!bass)
+ return false;
+
+ bass->att = att;
+ return true;
+}
+
static void bass_detached(void *data, void *user_data)
{
struct bt_bass_cb *cb = data;
diff --git a/src/shared/bass.h b/src/shared/bass.h
index b3f83b32e..c4b5b76ba 100644
--- a/src/shared/bass.h
+++ b/src/shared/bass.h
@@ -131,5 +131,6 @@ struct bt_bass *bt_bass_new(struct gatt_db *ldb, struct gatt_db *rdb,
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);
+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);
--
2.39.2

2023-10-04 15:32:28

by Iulia Tanasescu

[permalink] [raw]
Subject: [PATCH BlueZ 4/4] test-bass: Add unit tests for the SPE suite

This adds BASS unit tests for the Service Procedures Error Handling
suite.

Some unit tests require the BASS Server to synchronize to a broadcast
source, as instructed by a BASS Client. For this, a testing framework
was added similar to the one used by iso-tester, to emulate hardware.

---
Makefile.am | 7 +-
unit/test-bass.c | 781 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 775 insertions(+), 13 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 30db74a0c..088f5aaef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -581,7 +581,12 @@ unit_test_bap_LDADD = src/libshared-glib.la \

unit_tests += unit/test-bass

-unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources)
+unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources) monitor/bt.h \
+ emulator/hciemu.h emulator/hciemu.c \
+ emulator/vhci.h emulator/vhci.c \
+ emulator/btdev.h emulator/btdev.c \
+ emulator/bthost.h emulator/bthost.c \
+ emulator/smp.c
unit_test_bass_LDADD = src/libshared-glib.la \
lib/libbluetooth-internal.la $(GLIB_LIBS)

diff --git a/unit/test-bass.c b/unit/test-bass.c
index 2ab61f760..4407bb4d3 100644
--- a/unit/test-bass.c
+++ b/unit/test-bass.c
@@ -18,13 +18,22 @@
#include <sys/socket.h>
#include <fcntl.h>

+#include <stdbool.h>
+
#include <glib.h>

#include "lib/bluetooth.h"
#include "lib/uuid.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
#include "src/shared/util.h"
#include "src/shared/io.h"
#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
@@ -39,6 +48,9 @@ struct test_data {
struct queue *ccc_states;
size_t iovcnt;
struct iovec *iov;
+ struct mgmt *mgmt;
+ uint16_t mgmt_index;
+ struct hciemu *hciemu;
};

struct ccc_state {
@@ -190,16 +202,397 @@ struct ccc_state {
DISC_BCAST_AUDIO_SCAN_CP, \
BASS_READ_CHAR_DESC

+/* ATT: Write Request (0x12) len 4
+ * Handle: 0x0004 Type: Client Characteristic Configuration (0x2902)
+ * Data: 0100
+ * Notification (0x01)
+ * ATT: Write Response (0x13) len 0
+ * ATT: Write Request (0x12) len 4
+ * Handle: 0x0007 Type: Client Characteristic Configuration (0x2902)
+ * Data: 0100
+ * Notification (0x01)
+ * ATT: Write Response (0x13) len 0
+ */
+#define BASS_WRITE_CHAR_DESC \
+ IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \
+ IOV_DATA(0x13), \
+ IOV_DATA(0x12, 0x07, 0x00, 0x01, 0x00), \
+ IOV_DATA(0x13)
+
+/* ATT: Read Request (0x0a) len 2
+ * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
+ * ATT: Read Response (0x0b) len 0
+ * Handle: 0x0003 Broadcast Receive State (0x2bc8)
+ * ATT: Read Request (0x0a) len 2
+ * Handle: 0x0006 Type: Broadcast Receive State (0x2bc8)
+ * ATT: Read Response (0x0b) len 0
+ * Handle: 0x0006 Broadcast Receive State (0x2bc8)
+ */
+#define BASS_READ_BCAST_RECV_STATE_CHARS \
+ IOV_DATA(0x0a, 0x03, 0x00), \
+ IOV_DATA(0x0b), \
+ IOV_DATA(0x0a, 0x06, 0x00), \
+ IOV_DATA(0x0b)
+
+#define BASS_CP_WRITE_CMD(_op, _args...) \
+ IOV_DATA(0x52, 0x09, 0x00, _op, _args)
+
+#define BASS_CP_WRITE_REQ(_op, _args...) \
+ IOV_DATA(0x12, 0x09, 0x00, _op, _args)
+
+/* ATT: Write Command (0x52) len 19
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0401693C4572685526613465597073275455
+ * Opcode: Set Broadcast_Code
+ * Source_ID: 1
+ * Broadcast_Code: 0x55542773705965346126556872453c69
+ * ATT: Write Command (0x52) len 2
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0501
+ * Opcode: Remove Source
+ * Source_ID: 1
+ */
+#define IGNORE_INVALID_SRC_ID \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC, \
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_CMD(0x04, 0x01, 0x69, 0x3C, 0x45, 0x72, 0x68, \
+ 0x55, 0x26, 0x61, 0x34, 0x65, 0x59, 0x70, \
+ 0x73, 0x27, 0x54, 0x55), \
+ IOV_NULL, \
+ BASS_CP_WRITE_CMD(0x05, 0x01)
+
+/* ATT: Write Command (0x52) len 26
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0200F2698BE807C0003412000610270200000000000000000000
+ * Opcode: Add Source
+ * Advertiser_Address_Type: Public Device or Public Identity Address
+ * Advertiser_Address: c0:07:e8:8b:69:f2
+ * Advertising_SID: 0x00
+ * Broadcast_ID: 0x001234
+ * PA_Sync: 0x06 (Reserved for Future Use)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 2
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * Subgroup #1:
+ * BIS_Sync: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * ATT: Write Command (0x52) len 26
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0205F2698BE807C0003412000210270200000000000000000000
+ * Opcode: Add Source
+ * Advertiser_Address_Type: 0x05 (Reserved for Future Use)
+ * Advertiser_Address: c0:07:e8:8b:69:f2
+ * Advertising_SID: 0x00
+ * Broadcast_ID: 0x001234
+ * PA_Sync: Synchronize to PA (PAST not available)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 2
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * Subgroup #1:
+ * BIS_Sync: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * ATT: Write Command (0x52) len 26
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0200F2698BE807C0003412000210270201000000000100000000
+ * Opcode: Add Source
+ * Advertiser_Address_Type: Public Device or Public Identity Address
+ * Advertiser_Address: c0:07:e8:8b:69:f2
+ * Advertising_SID: 0x00
+ * Broadcast_ID: 0x001234
+ * PA_Sync: Synchronize to PA (PAST not available)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 2
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000001
+ * Metadata_Length: 0
+ * Subgroup #1:
+ * BIS_Sync: 00000000000000000000000000000001
+ * Metadata_Length: 0
+ */
+#define ADD_SRC_INVALID_PARAMS \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC,\
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x00, 0x34, 0x12, 0x00, 0x06, 0x10, 0x27, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00), \
+ IOV_NULL, \
+ BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00), \
+ IOV_NULL, \
+ BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x3F, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00), \
+ IOV_NULL, \
+ BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, \
+ 0x00, 0x00)
+
+/* ATT: Write Request (0x12) len 3
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: FF
+ * Opcode: 0xff (Reserved For Future Use)
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Opcode Not Supported (0x80)
+ */
+#define OPCODE_NOT_SUPPORTED \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC,\
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_REQ(0xFF), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x80)
+
+/* ATT: Write Command (0x52) len 26
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0200F2698BE807C000F5D983021027010000000000
+ * Opcode: Add Source
+ * Advertiser_Address_Type: Public Device or Public Identity Address
+ * Advertiser_Address: c0:07:e8:8b:69:f2
+ * Advertising_SID: 0x00
+ * Broadcast_ID: 0x83d9f5
+ * PA_Sync: Synchronize to PA (PAST not available)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 1
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * ATT: Handle Value Notification (0x1b) len 22
+ * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
+ * Data: 0100F2698BE807C000F5D9830200010000000000
+ * Source_ID: 0x01
+ * Source_Address_Type: Public Device or Public Identity Address
+ * Source_Address: c0:07:e8:8b:69:f2
+ * Source_Adv_SID: 0x00
+ * Broadcast_ID: 0x83d9f5
+ * PA_Sync_State: Synchronized to PA
+ * BIG_Encryption: Not encrypted
+ * Num_Subgroups: 1
+ * Subgroup #0:
+ * BIS_Sync State: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * ATT: Read Request (0x0a) len 2
+ * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
+ * ATT: Read Response (0x0b) len 20
+ * Handle: 0x0003 Broadcast Receive State (0x2bc8)
+ * Source_ID: 0x01
+ * Source_Address_Type: Public Device or Public Identity Address
+ * Source_Address: c0:07:e8:8b:69:f2
+ * Source_Adv_SID: 0x00
+ * Broadcast_ID: 0x83d9f5
+ * PA_Sync_State: Synchronized to PA
+ * BIG_Encryption: Not encrypted
+ * Num_Subgroups: 1
+ * Subgroup #0:
+ * BIS_Sync State: 00000000000000000000000000000000
+ * Metadata_Length: 0
+ * ATT: Write Request (0x12) len 2
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0501
+ * Opcode: Remove Source
+ * Source_ID: 1
+ * ATT: Write Response (0x13) len 0
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ */
+#define RM_SRC_WHILE_SYNC \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC, \
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x10, 0x27, 0x01, \
+ 0x00, 0x00, 0x00, 0x00, 0x00), \
+ IOV_DATA(0x1b, 0x03, 0x00, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, \
+ 0x07, 0xC0, 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, \
+ 0x00, 0x00, 0x00, 0x00, 0x00), \
+ IOV_DATA(0x0a, 0x03, 0x00), \
+ IOV_DATA(0x0b, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, 0x00, \
+ 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, \
+ 0x00), \
+ BASS_CP_WRITE_REQ(0x05, 0x01), \
+ IOV_DATA(0x13)
+
+/* ATT: Write Request (0x12) len 5
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 006dfe
+ * Opcode: Remote Scan Stopped
+ * Extra Data: 0xfe6d
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ * ATT: Write Request (0x12) len 5
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 006dfe
+ * Opcode: Remote Scan Started
+ * Extra Data: 0xa2c2
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ * ATT: Write Request (0x12) len 25
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0200F2698BE807C0003412000210270100000000000000
+ * Opcode: Add Source
+ * Advertiser_Address_Type: Public Device or Public Identity Address
+ * Advertiser_Address: c0:07:e8:8b:69:f2
+ * Advertising_SID: 0x00
+ * Broadcast_ID: 0x001234
+ * PA_Sync: Synchronize to PA (PAST not available)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 1
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000001
+ * Metadata_Length: 0
+ * Extra Data: 0x0000
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ * ATT: Write Request (0x12) len 13
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 03000210270100000000001500
+ * Opcode: Modify Source
+ * Source_ID: 0x00
+ * PA_Sync: Synchronize to PA (PAST not available)
+ * PA_Interval: 0x2710
+ * Num_Subgroups: 1
+ * Subgroup #0:
+ * BIS_Sync: 00000000000000000000000000000001
+ * Metadata_Length: 0
+ * Extra Data: 0x0015
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ * ATT: Write Request (0x12) len 20
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0400B803EAC6AFBB65A25A41F153056802010000
+ * Opcode: Set Broadcast_Code
+ * Source_ID: 0x00
+ * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b8
+ * Extra Data: 0x0000
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ * ATT: Write Request (0x12) len 4
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 05008F13
+ * Opcode: Remove Source
+ * Source_ID: 0x00
+ * Extra Data: 0x138f
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Write Request Rejected (0xFC)
+ */
+#define INVALID_LEN \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC,\
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_REQ(0x00, 0x6D, 0xFE), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
+ BASS_CP_WRITE_REQ(0x01, 0xC2, 0xA2), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
+ BASS_CP_WRITE_REQ(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
+ 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x01, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
+ BASS_CP_WRITE_REQ(0x03, 0x00, 0x02, 0x10, 0x27, 0x01, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x15, 0x00), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
+ BASS_CP_WRITE_REQ(0x04, 0x00, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB, \
+ 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
+ 0x02, 0x01, 0x00, 0x00), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
+ BASS_CP_WRITE_REQ(0x05, 0x00, 0x8F, 0x13), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC)
+
+/* ATT: Write Request (0x12) len 20
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 0400B803EAC6AFBB65A25A41F153056802010000
+ * Opcode: Set Broadcast_Code
+ * Source_ID: 0x05
+ * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Invalid Source ID (0x81)
+ * ATT: Write Request (0x12) len 4
+ * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
+ * Data: 005
+ * Opcode: Remove Source
+ * Source_ID: 0x05
+ * ATT: Error Response (0x01) len 4
+ * Write Request (0x12)
+ * Handle: 0x0009
+ * Error: Invalid Source ID (0x81)
+ */
+#define INVALID_SRC_ID \
+ EXCHANGE_MTU, \
+ BASS_FIND_BY_TYPE_VALUE, \
+ DISC_BASS_CHAR, \
+ BASS_FIND_INFO, \
+ BASS_WRITE_CHAR_DESC, \
+ BASS_READ_BCAST_RECV_STATE_CHARS, \
+ BASS_CP_WRITE_REQ(0x04, 0x05, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB, \
+ 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
+ 0x02, 0x01), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81), \
+ BASS_CP_WRITE_REQ(0x05, 0x05), \
+ IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81)
+
+static const uint8_t client_bdaddr[] = {0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0};
+
+static const uint8_t set_iso_socket_param[] = {
+ 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
+ 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
+ 0x01, /* Action - enable */
+};
+
+static const uint8_t reset_iso_socket_param[] = {
+ 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
+ 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
+ 0x00, /* Action - disable */
+};
+
#define iov_data(args...) ((const struct iovec[]) { args })

-#define define_test(name, function, _cfg, args...) \
+#define define_test(name, pre_setup, setup, function, teardown, \
+ post_teardown, args...) \
do { \
const struct iovec iov[] = { args }; \
static struct test_data data; \
data.iovcnt = ARRAY_SIZE(iov_data(args)); \
data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
- tester_add(name, &data, NULL, function, \
- test_teardown); \
+ tester_add_full(name, &data, pre_setup, setup, function, \
+ teardown, post_teardown, 0, NULL, NULL); \
} while (0)

static void test_complete_cb(const void *user_data)
@@ -287,11 +680,282 @@ done:
gatt_db_attribute_read_result(attrib, id, ecode, value, len);
}

+static void gatt_ccc_write_cb(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 test_data *data = (void *)user_data;
+ struct ccc_state *ccc_state;
+ uint16_t val;
+ uint8_t ecode = 0;
+
+ if (!value || len > 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset > 2) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (len == 1)
+ val = *value;
+ else
+ val = get_le16(value);
+
+ ccc_state = get_ccc_state(data, gatt_db_attribute_get_handle(attrib));
+ if (!ccc_state)
+ return;
+
+ /* If value is identical, then just succeed */
+ if (val == ccc_state->value)
+ goto done;
+
+ ccc_state->value = val;
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void gatt_notify_cb(struct gatt_db_attribute *attrib,
+ struct gatt_db_attribute *ccc,
+ const uint8_t *value, size_t len,
+ struct bt_att *att, void *user_data)
+{
+ struct test_data *data = user_data;
+ struct ccc_state *ccc_state;
+
+ ccc_state = find_ccc_state(data, gatt_db_attribute_get_handle(ccc));
+ if (!ccc_state || !(ccc_state->value & 0x0001))
+ return;
+
+ bt_gatt_server_send_notification(data->server,
+ gatt_db_attribute_get_handle(attrib),
+ value, len, false);
+}
+
+static void set_iso_socket_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_print("ISO socket feature could not be enabled");
+ return;
+ }
+
+ tester_print("ISO socket feature is enabled");
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+ const struct mgmt_rp_read_info *rp = param;
+ char addr[18];
+ uint16_t manufacturer;
+ uint32_t supported_settings, current_settings;
+
+ tester_print("Read Info callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+ manufacturer = btohs(rp->manufacturer);
+ supported_settings = btohl(rp->supported_settings);
+ current_settings = btohl(rp->current_settings);
+
+ tester_print(" Address: %s", addr);
+ tester_print(" Version: 0x%02x", rp->version);
+ tester_print(" Manufacturer: 0x%04x", manufacturer);
+ tester_print(" Supported settings: 0x%08x", supported_settings);
+ tester_print(" Current settings: 0x%08x", current_settings);
+ tester_print(" Class: 0x%02x%02x%02x",
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+ tester_print(" Name: %s", rp->name);
+ tester_print(" Short name: %s", rp->short_name);
+
+ if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+
+ tester_print("Index Added callback");
+ tester_print(" Index: 0x%04x", index);
+
+ data->mgmt_index = index;
+
+ mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+ read_info_callback, data, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+
+ tester_print("Index Removed callback");
+ tester_print(" Index: 0x%04x", index);
+
+ if (index != data->mgmt_index)
+ return;
+
+ mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+ mgmt_unref(data->mgmt);
+ data->mgmt = NULL;
+
+ tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+ struct hciemu_client *client;
+
+ tester_print("Read Index List callback");
+ tester_print(" Status: 0x%02x", status);
+
+ if (status || !param) {
+ tester_pre_setup_failed();
+ return;
+ }
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+ index_added_callback, data, NULL);
+
+ mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+ index_removed_callback, data, NULL);
+
+ data->hciemu = hciemu_new(HCIEMU_TYPE_BREDRLE52);
+ if (!data->hciemu) {
+ tester_warn("Failed to setup HCI emulation");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ client = hciemu_get_client(data->hciemu, 0);
+
+ if (!hciemu_set_client_bdaddr(client, client_bdaddr)) {
+ tester_warn("Failed to setup HCI emulation");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+ struct test_data *data = (void *)test_data;
+
+ data->mgmt = mgmt_new_default();
+ if (!data->mgmt) {
+ tester_warn("Failed to setup management interface");
+ tester_pre_setup_failed();
+ return;
+ }
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE,
+ sizeof(set_iso_socket_param), set_iso_socket_param,
+ set_iso_socket_callback, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+ read_index_list_callback, data, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+ struct test_data *data = (void *)test_data;
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE,
+ sizeof(reset_iso_socket_param), reset_iso_socket_param,
+ NULL, NULL, NULL);
+
+ hciemu_unref(data->hciemu);
+ data->hciemu = NULL;
+}
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+ const void *param, uint8_t len,
+ void *user_data)
+{
+ if (opcode != BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE)
+ return;
+
+ tester_print("Client set connectable status 0x%02x", status);
+
+ if (status)
+ tester_setup_failed();
+ else
+ tester_setup_complete();
+}
+
+static void setup_powered_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+ struct hciemu_client *client;
+ struct bthost *host;
+ uint8_t bcode[16] = {0x00};
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Controller powered on");
+
+ client = hciemu_get_client(data->hciemu, 0);
+ host = hciemu_client_host(client);
+ bthost_set_cmd_complete_cb(host, client_connectable_complete,
+ data);
+ bthost_set_ext_adv_params(host);
+ bthost_set_ext_adv_enable(host, 0x01);
+
+ bthost_set_pa_params(host);
+ bthost_set_pa_enable(host, 0x01);
+ bthost_create_big(host, 1, 0x00, bcode);
+}
+
+static void setup_powered(const void *test_data)
+{
+ struct test_data *data = (void *)test_data;
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Powering on controller");
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+ sizeof(param), param, NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), param, NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), param,
+ setup_powered_callback, data, NULL);
+}
+
static void test_server(const void *user_data)
{
struct test_data *data = (void *)user_data;
struct bt_att *att;
struct io *io;
+ bdaddr_t adapter_bdaddr = {{0, 0, 0, 0, 0, 0}};

io = tester_setup_io(data->iov, data->iovcnt);
g_assert(io);
@@ -306,12 +970,22 @@ static void test_server(const void *user_data)
data->db = gatt_db_new();
g_assert(data->db);

- gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
- NULL, data);
+ gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
+ gatt_notify_cb, data);

- data->bass = bt_bass_new(data->db, NULL, BDADDR_ANY);
+ if (data->hciemu)
+ memcpy(&adapter_bdaddr,
+ hciemu_get_central_bdaddr(data->hciemu),
+ sizeof(adapter_bdaddr));
+
+ data->bass = bt_bass_new(data->db, NULL, &adapter_bdaddr);
g_assert(data->bass);

+ bt_bass_set_att(data->bass, att);
+ bt_bass_attach(data->bass, NULL);
+
+ bt_bass_set_debug(data->bass, print_debug, "bt_bass:", NULL);
+
data->server = bt_gatt_server_new(data->db, att, 64, 0);
g_assert(data->server);

@@ -346,8 +1020,8 @@ static void test_sggit(void)
* handle range of the request. The IUT reports all BASS
* characteristics.
*/
- define_test("BASS/SR/SGGIT/SER/BV-01-C", test_server, NULL,
- DISC_BASS_SER);
+ define_test("BASS/SR/SGGIT/SER/BV-01-C", NULL, NULL, test_server,
+ test_teardown, NULL, DISC_BASS_SER);

/* BASS/SR/SGGIT/CHA/BV-01-C [Service GGIT -
* Broadcast Audio Scan Control Point]
@@ -359,8 +1033,8 @@ static void test_sggit(void)
* handle range of the request. The IUT reports one instance of the
* Broadcast Audio Scan Control Point characteristic.
*/
- define_test("BASS/SR/SGGIT/CHA/BV-01-C", test_server, NULL,
- DISC_BCAST_AUDIO_SCAN_CP);
+ define_test("BASS/SR/SGGIT/CHA/BV-01-C", NULL, NULL, test_server,
+ test_teardown, NULL, DISC_BCAST_AUDIO_SCAN_CP);

/* BASS/SR/SGGIT/CHA/BV-02-C [Service GGIT -
* Broadcast Receive State]
@@ -383,8 +1057,90 @@ static void test_sggit(void)
* The IUT sends an ATT_Read_Response to the Lower Tester for each
* ATT_Read_Request.
*/
- define_test("BASS/SR/SGGIT/CHA/BV-02-C", test_server, NULL,
- DISC_BCAST_RECV_STATE);
+ define_test("BASS/SR/SGGIT/CHA/BV-02-C", NULL, NULL, test_server,
+ test_teardown, NULL, DISC_BCAST_RECV_STATE);
+}
+
+static void test_spe(void)
+{
+ /* BASS/SR/SPE/BI-01-C [Ignore Invalid Source ID]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT does not respond to a control point
+ * procedure call that uses an invalid Source_ID parameter.
+ *
+ * Pass verdict:
+ * The IUT does not send a notification of the Broadcast Receive State
+ * characteristic.
+ */
+ define_test("BASS/SR/SPE/BI-01-C", NULL, NULL, test_server,
+ test_teardown, NULL, IGNORE_INVALID_SRC_ID);
+
+ /* BASS/SR/SPE/BI-03-C [Add Source - Ignore Invalid Values]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT ignores Add Source control point
+ * procedure calls that include an RFU or Invalid parameter.
+ *
+ * Pass verdict:
+ * The IUT does not send a notification of the Broadcast Receive State
+ * characteristic.
+ */
+ define_test("BASS/SR/SPE/BI-03-C", NULL, NULL, test_server,
+ test_teardown, NULL, ADD_SRC_INVALID_PARAMS);
+
+ /* BASS/SR/SPE/BI-04-C [Opcode Not Supported]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT returns an Opcode Not Supported error
+ * response when the opcode written is not supported by the IUT or is
+ * within a range that is reserved for future use being written to the
+ * Broadcast Audio Scan Control Point.
+ *
+ * Pass verdict:
+ * The IUT sends an error response of OPCODE NOT SUPPORTED.
+ */
+ define_test("BASS/SR/SPE/BI-04-C", NULL, NULL, test_server,
+ test_teardown, NULL, OPCODE_NOT_SUPPORTED);
+
+ /* BASS/SR/SPE/BI-05-C [Remove Source While Synchronized to a Source]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT, if synchronized to PA and/or BIS,
+ * does not accept the Remove Source operation request.
+ *
+ * Pass verdict:
+ * The IUT does not send a notification to the Lower Tester for the
+ * Broadcast Receive State characteristic.
+ */
+ define_test("BASS/SR/SPE/BI-05-C", test_pre_setup, setup_powered,
+ test_server, test_teardown, test_post_teardown,
+ RM_SRC_WHILE_SYNC);
+
+ /* BASS/SR/SPE/BI-06-C [Invalid Length]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT rejects writing of an opcode with
+ * an invalid length.
+ *
+ * Pass verdict:
+ * The IUT rejects the opcode.
+ */
+ define_test("BASS/SR/SPE/BI-06-C", NULL, NULL, test_server,
+ test_teardown, NULL, INVALID_LEN);
+
+ /* BASS/SR/SPE/BI-07-C [Invalid Source ID]
+ *
+ * Test Purpose:
+ * Verify that the BASS Server IUT returns an error when a control
+ * point procedure passing an invalid Source_ID parameter is called.
+ *
+ * Pass verdict:
+ * The IUT sends an ATT Error Response with the Error Code set to
+ * Invalid Source_ID.
+ */
+ define_test("BASS/SR/SPE/BI-07-C", NULL, NULL, test_server,
+ test_teardown, NULL, INVALID_SRC_ID);
}

int main(int argc, char *argv[])
@@ -392,6 +1148,7 @@ int main(int argc, char *argv[])
tester_init(&argc, &argv);

test_sggit();
+ test_spe();

return tester_run();
}
--
2.39.2

2023-10-04 17:30:05

by bluez.test.bot

[permalink] [raw]
Subject: RE: Add BASS unit tests for the SPE suite

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

---Test result---

Test Summary:
CheckPatch PASS 7.75 seconds
GitLint PASS 2.94 seconds
BuildEll PASS 29.11 seconds
BluezMake PASS 988.24 seconds
MakeCheck PASS 12.12 seconds
MakeDistcheck PASS 165.30 seconds
CheckValgrind PASS 264.72 seconds
CheckSmatch PASS 354.81 seconds
bluezmakeextell PASS 110.69 seconds
IncrementalBuild PASS 3537.44 seconds
ScanBuild PASS 1170.86 seconds



---
Regards,
Linux Bluetooth

2023-10-05 20:52:16

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 4/4] test-bass: Add unit tests for the SPE suite

Hi Iulia,

On Wed, Oct 4, 2023 at 8:32 AM Iulia Tanasescu <[email protected]> wrote:
>
> This adds BASS unit tests for the Service Procedures Error Handling
> suite.
>
> Some unit tests require the BASS Server to synchronize to a broadcast
> source, as instructed by a BASS Client. For this, a testing framework
> was added similar to the one used by iso-tester, to emulate hardware.
>
> ---
> Makefile.am | 7 +-
> unit/test-bass.c | 781 ++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 775 insertions(+), 13 deletions(-)
>
> diff --git a/Makefile.am b/Makefile.am
> index 30db74a0c..088f5aaef 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -581,7 +581,12 @@ unit_test_bap_LDADD = src/libshared-glib.la \
>
> unit_tests += unit/test-bass
>
> -unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources)
> +unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources) monitor/bt.h \
> + emulator/hciemu.h emulator/hciemu.c \
> + emulator/vhci.h emulator/vhci.c \
> + emulator/btdev.h emulator/btdev.c \
> + emulator/bthost.h emulator/bthost.c \
> + emulator/smp.c
> unit_test_bass_LDADD = src/libshared-glib.la \
> lib/libbluetooth-internal.la $(GLIB_LIBS)
>
> diff --git a/unit/test-bass.c b/unit/test-bass.c
> index 2ab61f760..4407bb4d3 100644
> --- a/unit/test-bass.c
> +++ b/unit/test-bass.c
> @@ -18,13 +18,22 @@
> #include <sys/socket.h>
> #include <fcntl.h>
>
> +#include <stdbool.h>
> +
> #include <glib.h>
>
> #include "lib/bluetooth.h"
> #include "lib/uuid.h"
> +#include "lib/mgmt.h"
> +
> +#include "monitor/bt.h"
> +#include "emulator/bthost.h"
> +#include "emulator/hciemu.h"
> +
> #include "src/shared/util.h"
> #include "src/shared/io.h"
> #include "src/shared/tester.h"
> +#include "src/shared/mgmt.h"
> #include "src/shared/queue.h"
> #include "src/shared/att.h"
> #include "src/shared/gatt-db.h"
> @@ -39,6 +48,9 @@ struct test_data {
> struct queue *ccc_states;
> size_t iovcnt;
> struct iovec *iov;
> + struct mgmt *mgmt;
> + uint16_t mgmt_index;
> + struct hciemu *hciemu;
> };
>
> struct ccc_state {
> @@ -190,16 +202,397 @@ struct ccc_state {
> DISC_BCAST_AUDIO_SCAN_CP, \
> BASS_READ_CHAR_DESC
>
> +/* ATT: Write Request (0x12) len 4
> + * Handle: 0x0004 Type: Client Characteristic Configuration (0x2902)
> + * Data: 0100
> + * Notification (0x01)
> + * ATT: Write Response (0x13) len 0
> + * ATT: Write Request (0x12) len 4
> + * Handle: 0x0007 Type: Client Characteristic Configuration (0x2902)
> + * Data: 0100
> + * Notification (0x01)
> + * ATT: Write Response (0x13) len 0
> + */
> +#define BASS_WRITE_CHAR_DESC \
> + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \
> + IOV_DATA(0x13), \
> + IOV_DATA(0x12, 0x07, 0x00, 0x01, 0x00), \
> + IOV_DATA(0x13)
> +
> +/* ATT: Read Request (0x0a) len 2
> + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> + * ATT: Read Response (0x0b) len 0
> + * Handle: 0x0003 Broadcast Receive State (0x2bc8)
> + * ATT: Read Request (0x0a) len 2
> + * Handle: 0x0006 Type: Broadcast Receive State (0x2bc8)
> + * ATT: Read Response (0x0b) len 0
> + * Handle: 0x0006 Broadcast Receive State (0x2bc8)
> + */
> +#define BASS_READ_BCAST_RECV_STATE_CHARS \
> + IOV_DATA(0x0a, 0x03, 0x00), \
> + IOV_DATA(0x0b), \
> + IOV_DATA(0x0a, 0x06, 0x00), \
> + IOV_DATA(0x0b)
> +
> +#define BASS_CP_WRITE_CMD(_op, _args...) \
> + IOV_DATA(0x52, 0x09, 0x00, _op, _args)
> +
> +#define BASS_CP_WRITE_REQ(_op, _args...) \
> + IOV_DATA(0x12, 0x09, 0x00, _op, _args)
> +
> +/* ATT: Write Command (0x52) len 19
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0401693C4572685526613465597073275455
> + * Opcode: Set Broadcast_Code
> + * Source_ID: 1
> + * Broadcast_Code: 0x55542773705965346126556872453c69
> + * ATT: Write Command (0x52) len 2
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0501
> + * Opcode: Remove Source
> + * Source_ID: 1
> + */
> +#define IGNORE_INVALID_SRC_ID \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC, \
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_CMD(0x04, 0x01, 0x69, 0x3C, 0x45, 0x72, 0x68, \
> + 0x55, 0x26, 0x61, 0x34, 0x65, 0x59, 0x70, \
> + 0x73, 0x27, 0x54, 0x55), \
> + IOV_NULL, \
> + BASS_CP_WRITE_CMD(0x05, 0x01)
> +
> +/* ATT: Write Command (0x52) len 26
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0200F2698BE807C0003412000610270200000000000000000000
> + * Opcode: Add Source
> + * Advertiser_Address_Type: Public Device or Public Identity Address
> + * Advertiser_Address: c0:07:e8:8b:69:f2
> + * Advertising_SID: 0x00
> + * Broadcast_ID: 0x001234
> + * PA_Sync: 0x06 (Reserved for Future Use)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 2
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * Subgroup #1:
> + * BIS_Sync: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * ATT: Write Command (0x52) len 26
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0205F2698BE807C0003412000210270200000000000000000000
> + * Opcode: Add Source
> + * Advertiser_Address_Type: 0x05 (Reserved for Future Use)
> + * Advertiser_Address: c0:07:e8:8b:69:f2
> + * Advertising_SID: 0x00
> + * Broadcast_ID: 0x001234
> + * PA_Sync: Synchronize to PA (PAST not available)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 2
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * Subgroup #1:
> + * BIS_Sync: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * ATT: Write Command (0x52) len 26
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0200F2698BE807C0003412000210270201000000000100000000
> + * Opcode: Add Source
> + * Advertiser_Address_Type: Public Device or Public Identity Address
> + * Advertiser_Address: c0:07:e8:8b:69:f2
> + * Advertising_SID: 0x00
> + * Broadcast_ID: 0x001234
> + * PA_Sync: Synchronize to PA (PAST not available)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 2
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000001
> + * Metadata_Length: 0
> + * Subgroup #1:
> + * BIS_Sync: 00000000000000000000000000000001
> + * Metadata_Length: 0
> + */
> +#define ADD_SRC_INVALID_PARAMS \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC,\
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x00, 0x34, 0x12, 0x00, 0x06, 0x10, 0x27, 0x02, \
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> + 0x00, 0x00), \
> + IOV_NULL, \
> + BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> + 0x00, 0x00), \
> + IOV_NULL, \
> + BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x3F, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> + 0x00, 0x00), \
> + IOV_NULL, \
> + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, \
> + 0x00, 0x00)
> +
> +/* ATT: Write Request (0x12) len 3
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: FF
> + * Opcode: 0xff (Reserved For Future Use)
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Opcode Not Supported (0x80)
> + */
> +#define OPCODE_NOT_SUPPORTED \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC,\
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_REQ(0xFF), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x80)
> +
> +/* ATT: Write Command (0x52) len 26
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0200F2698BE807C000F5D983021027010000000000
> + * Opcode: Add Source
> + * Advertiser_Address_Type: Public Device or Public Identity Address
> + * Advertiser_Address: c0:07:e8:8b:69:f2
> + * Advertising_SID: 0x00
> + * Broadcast_ID: 0x83d9f5
> + * PA_Sync: Synchronize to PA (PAST not available)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 1
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * ATT: Handle Value Notification (0x1b) len 22
> + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> + * Data: 0100F2698BE807C000F5D9830200010000000000
> + * Source_ID: 0x01
> + * Source_Address_Type: Public Device or Public Identity Address
> + * Source_Address: c0:07:e8:8b:69:f2
> + * Source_Adv_SID: 0x00
> + * Broadcast_ID: 0x83d9f5
> + * PA_Sync_State: Synchronized to PA
> + * BIG_Encryption: Not encrypted
> + * Num_Subgroups: 1
> + * Subgroup #0:
> + * BIS_Sync State: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * ATT: Read Request (0x0a) len 2
> + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> + * ATT: Read Response (0x0b) len 20
> + * Handle: 0x0003 Broadcast Receive State (0x2bc8)
> + * Source_ID: 0x01
> + * Source_Address_Type: Public Device or Public Identity Address
> + * Source_Address: c0:07:e8:8b:69:f2
> + * Source_Adv_SID: 0x00
> + * Broadcast_ID: 0x83d9f5
> + * PA_Sync_State: Synchronized to PA
> + * BIG_Encryption: Not encrypted
> + * Num_Subgroups: 1
> + * Subgroup #0:
> + * BIS_Sync State: 00000000000000000000000000000000
> + * Metadata_Length: 0
> + * ATT: Write Request (0x12) len 2
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0501
> + * Opcode: Remove Source
> + * Source_ID: 1
> + * ATT: Write Response (0x13) len 0
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + */
> +#define RM_SRC_WHILE_SYNC \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC, \
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x10, 0x27, 0x01, \
> + 0x00, 0x00, 0x00, 0x00, 0x00), \
> + IOV_DATA(0x1b, 0x03, 0x00, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, \
> + 0x07, 0xC0, 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, \
> + 0x00, 0x00, 0x00, 0x00, 0x00), \
> + IOV_DATA(0x0a, 0x03, 0x00), \
> + IOV_DATA(0x0b, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, 0x00, \
> + 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, \
> + 0x00), \
> + BASS_CP_WRITE_REQ(0x05, 0x01), \
> + IOV_DATA(0x13)
> +
> +/* ATT: Write Request (0x12) len 5
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 006dfe
> + * Opcode: Remote Scan Stopped
> + * Extra Data: 0xfe6d
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + * ATT: Write Request (0x12) len 5
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 006dfe
> + * Opcode: Remote Scan Started
> + * Extra Data: 0xa2c2
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + * ATT: Write Request (0x12) len 25
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0200F2698BE807C0003412000210270100000000000000
> + * Opcode: Add Source
> + * Advertiser_Address_Type: Public Device or Public Identity Address
> + * Advertiser_Address: c0:07:e8:8b:69:f2
> + * Advertising_SID: 0x00
> + * Broadcast_ID: 0x001234
> + * PA_Sync: Synchronize to PA (PAST not available)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 1
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000001
> + * Metadata_Length: 0
> + * Extra Data: 0x0000
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + * ATT: Write Request (0x12) len 13
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 03000210270100000000001500
> + * Opcode: Modify Source
> + * Source_ID: 0x00
> + * PA_Sync: Synchronize to PA (PAST not available)
> + * PA_Interval: 0x2710
> + * Num_Subgroups: 1
> + * Subgroup #0:
> + * BIS_Sync: 00000000000000000000000000000001
> + * Metadata_Length: 0
> + * Extra Data: 0x0015
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + * ATT: Write Request (0x12) len 20
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0400B803EAC6AFBB65A25A41F153056802010000
> + * Opcode: Set Broadcast_Code
> + * Source_ID: 0x00
> + * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b8
> + * Extra Data: 0x0000
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + * ATT: Write Request (0x12) len 4
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 05008F13
> + * Opcode: Remove Source
> + * Source_ID: 0x00
> + * Extra Data: 0x138f
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Write Request Rejected (0xFC)
> + */
> +#define INVALID_LEN \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC,\
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_REQ(0x00, 0x6D, 0xFE), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> + BASS_CP_WRITE_REQ(0x01, 0xC2, 0xA2), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> + BASS_CP_WRITE_REQ(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x01, \
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> + BASS_CP_WRITE_REQ(0x03, 0x00, 0x02, 0x10, 0x27, 0x01, 0x00, 0x00, \
> + 0x00, 0x00, 0x00, 0x15, 0x00), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> + BASS_CP_WRITE_REQ(0x04, 0x00, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB, \
> + 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
> + 0x02, 0x01, 0x00, 0x00), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> + BASS_CP_WRITE_REQ(0x05, 0x00, 0x8F, 0x13), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC)
> +
> +/* ATT: Write Request (0x12) len 20
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 0400B803EAC6AFBB65A25A41F153056802010000
> + * Opcode: Set Broadcast_Code
> + * Source_ID: 0x05
> + * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Invalid Source ID (0x81)
> + * ATT: Write Request (0x12) len 4
> + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> + * Data: 005
> + * Opcode: Remove Source
> + * Source_ID: 0x05
> + * ATT: Error Response (0x01) len 4
> + * Write Request (0x12)
> + * Handle: 0x0009
> + * Error: Invalid Source ID (0x81)
> + */
> +#define INVALID_SRC_ID \
> + EXCHANGE_MTU, \
> + BASS_FIND_BY_TYPE_VALUE, \
> + DISC_BASS_CHAR, \
> + BASS_FIND_INFO, \
> + BASS_WRITE_CHAR_DESC, \
> + BASS_READ_BCAST_RECV_STATE_CHARS, \
> + BASS_CP_WRITE_REQ(0x04, 0x05, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB, \
> + 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
> + 0x02, 0x01), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81), \
> + BASS_CP_WRITE_REQ(0x05, 0x05), \
> + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81)
> +
> +static const uint8_t client_bdaddr[] = {0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0};
> +
> +static const uint8_t set_iso_socket_param[] = {
> + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
> + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
> + 0x01, /* Action - enable */
> +};
> +
> +static const uint8_t reset_iso_socket_param[] = {
> + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
> + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
> + 0x00, /* Action - disable */
> +};
> +
> #define iov_data(args...) ((const struct iovec[]) { args })
>
> -#define define_test(name, function, _cfg, args...) \
> +#define define_test(name, pre_setup, setup, function, teardown, \
> + post_teardown, args...) \
> do { \
> const struct iovec iov[] = { args }; \
> static struct test_data data; \
> data.iovcnt = ARRAY_SIZE(iov_data(args)); \
> data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
> - tester_add(name, &data, NULL, function, \
> - test_teardown); \
> + tester_add_full(name, &data, pre_setup, setup, function, \
> + teardown, post_teardown, 0, NULL, NULL); \
> } while (0)
>
> static void test_complete_cb(const void *user_data)
> @@ -287,11 +680,282 @@ done:
> gatt_db_attribute_read_result(attrib, id, ecode, value, len);
> }
>
> +static void gatt_ccc_write_cb(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 test_data *data = (void *)user_data;
> + struct ccc_state *ccc_state;
> + uint16_t val;
> + uint8_t ecode = 0;
> +
> + if (!value || len > 2) {
> + ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
> + goto done;
> + }
> +
> + if (offset > 2) {
> + ecode = BT_ATT_ERROR_INVALID_OFFSET;
> + goto done;
> + }
> +
> + if (len == 1)
> + val = *value;
> + else
> + val = get_le16(value);
> +
> + ccc_state = get_ccc_state(data, gatt_db_attribute_get_handle(attrib));
> + if (!ccc_state)
> + return;
> +
> + /* If value is identical, then just succeed */
> + if (val == ccc_state->value)
> + goto done;
> +
> + ccc_state->value = val;
> +
> +done:
> + gatt_db_attribute_write_result(attrib, id, ecode);
> +}
> +
> +static void gatt_notify_cb(struct gatt_db_attribute *attrib,
> + struct gatt_db_attribute *ccc,
> + const uint8_t *value, size_t len,
> + struct bt_att *att, void *user_data)
> +{
> + struct test_data *data = user_data;
> + struct ccc_state *ccc_state;
> +
> + ccc_state = find_ccc_state(data, gatt_db_attribute_get_handle(ccc));
> + if (!ccc_state || !(ccc_state->value & 0x0001))
> + return;
> +
> + bt_gatt_server_send_notification(data->server,
> + gatt_db_attribute_get_handle(attrib),
> + value, len, false);
> +}
> +
> +static void set_iso_socket_callback(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + if (status != MGMT_STATUS_SUCCESS) {
> + tester_print("ISO socket feature could not be enabled");
> + return;
> + }
> +
> + tester_print("ISO socket feature is enabled");
> +}
> +
> +static void read_info_callback(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct test_data *data = (void *)user_data;
> + const struct mgmt_rp_read_info *rp = param;
> + char addr[18];
> + uint16_t manufacturer;
> + uint32_t supported_settings, current_settings;
> +
> + tester_print("Read Info callback");
> + tester_print(" Status: 0x%02x", status);
> +
> + if (status || !param) {
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + ba2str(&rp->bdaddr, addr);
> + manufacturer = btohs(rp->manufacturer);
> + supported_settings = btohl(rp->supported_settings);
> + current_settings = btohl(rp->current_settings);
> +
> + tester_print(" Address: %s", addr);
> + tester_print(" Version: 0x%02x", rp->version);
> + tester_print(" Manufacturer: 0x%04x", manufacturer);
> + tester_print(" Supported settings: 0x%08x", supported_settings);
> + tester_print(" Current settings: 0x%08x", current_settings);
> + tester_print(" Class: 0x%02x%02x%02x",
> + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
> + tester_print(" Name: %s", rp->name);
> + tester_print(" Short name: %s", rp->short_name);
> +
> + if (strcmp(hciemu_get_address(data->hciemu), addr)) {
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + tester_pre_setup_complete();
> +}
> +
> +static void index_added_callback(uint16_t index, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct test_data *data = (void *)user_data;
> +
> + tester_print("Index Added callback");
> + tester_print(" Index: 0x%04x", index);
> +
> + data->mgmt_index = index;
> +
> + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
> + read_info_callback, data, NULL);
> +}
> +
> +static void index_removed_callback(uint16_t index, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct test_data *data = (void *)user_data;
> +
> + tester_print("Index Removed callback");
> + tester_print(" Index: 0x%04x", index);
> +
> + if (index != data->mgmt_index)
> + return;
> +
> + mgmt_unregister_index(data->mgmt, data->mgmt_index);
> +
> + mgmt_unref(data->mgmt);
> + data->mgmt = NULL;
> +
> + tester_post_teardown_complete();
> +}
> +
> +static void read_index_list_callback(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct test_data *data = (void *)user_data;
> + struct hciemu_client *client;
> +
> + tester_print("Read Index List callback");
> + tester_print(" Status: 0x%02x", status);
> +
> + if (status || !param) {
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
> + index_added_callback, data, NULL);
> +
> + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
> + index_removed_callback, data, NULL);
> +
> + data->hciemu = hciemu_new(HCIEMU_TYPE_BREDRLE52);
> + if (!data->hciemu) {
> + tester_warn("Failed to setup HCI emulation");
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + client = hciemu_get_client(data->hciemu, 0);
> +
> + if (!hciemu_set_client_bdaddr(client, client_bdaddr)) {
> + tester_warn("Failed to setup HCI emulation");
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + tester_print("New hciemu instance created");
> +}
> +
> +static void test_pre_setup(const void *test_data)
> +{
> + struct test_data *data = (void *)test_data;
> +
> + data->mgmt = mgmt_new_default();
> + if (!data->mgmt) {
> + tester_warn("Failed to setup management interface");
> + tester_pre_setup_failed();
> + return;
> + }
> +
> + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE,
> + sizeof(set_iso_socket_param), set_iso_socket_param,
> + set_iso_socket_callback, NULL, NULL);
> +
> + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
> + read_index_list_callback, data, NULL);
> +}
> +
> +static void test_post_teardown(const void *test_data)
> +{
> + struct test_data *data = (void *)test_data;
> +
> + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE,
> + sizeof(reset_iso_socket_param), reset_iso_socket_param,
> + NULL, NULL, NULL);
> +
> + hciemu_unref(data->hciemu);
> + data->hciemu = NULL;
> +}
> +
> +static void client_connectable_complete(uint16_t opcode, uint8_t status,
> + const void *param, uint8_t len,
> + void *user_data)
> +{
> + if (opcode != BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE)
> + return;
> +
> + tester_print("Client set connectable status 0x%02x", status);
> +
> + if (status)
> + tester_setup_failed();
> + else
> + tester_setup_complete();
> +}
> +
> +static void setup_powered_callback(uint8_t status, uint16_t length,
> + const void *param, void *user_data)
> +{
> + struct test_data *data = (void *)user_data;
> + struct hciemu_client *client;
> + struct bthost *host;
> + uint8_t bcode[16] = {0x00};
> +
> + if (status != MGMT_STATUS_SUCCESS) {
> + tester_setup_failed();
> + return;
> + }
> +
> + tester_print("Controller powered on");
> +
> + client = hciemu_get_client(data->hciemu, 0);
> + host = hciemu_client_host(client);
> + bthost_set_cmd_complete_cb(host, client_connectable_complete,
> + data);
> + bthost_set_ext_adv_params(host);
> + bthost_set_ext_adv_enable(host, 0x01);
> +
> + bthost_set_pa_params(host);
> + bthost_set_pa_enable(host, 0x01);
> + bthost_create_big(host, 1, 0x00, bcode);
> +}
> +
> +static void setup_powered(const void *test_data)
> +{
> + struct test_data *data = (void *)test_data;
> + unsigned char param[] = { 0x01 };
> +
> + tester_print("Powering on controller");
> +
> + mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
> + sizeof(param), param, NULL, NULL, NULL);
> +
> + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
> + sizeof(param), param, NULL, NULL, NULL);
> +
> + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
> + sizeof(param), param,
> + setup_powered_callback, data, NULL);
> +}
> +
> static void test_server(const void *user_data)
> {
> struct test_data *data = (void *)user_data;
> struct bt_att *att;
> struct io *io;
> + bdaddr_t adapter_bdaddr = {{0, 0, 0, 0, 0, 0}};
>
> io = tester_setup_io(data->iov, data->iovcnt);
> g_assert(io);
> @@ -306,12 +970,22 @@ static void test_server(const void *user_data)
> data->db = gatt_db_new();
> g_assert(data->db);
>
> - gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
> - NULL, data);
> + gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
> + gatt_notify_cb, data);
>
> - data->bass = bt_bass_new(data->db, NULL, BDADDR_ANY);
> + if (data->hciemu)
> + memcpy(&adapter_bdaddr,
> + hciemu_get_central_bdaddr(data->hciemu),
> + sizeof(adapter_bdaddr));
> +
> + data->bass = bt_bass_new(data->db, NULL, &adapter_bdaddr);
> g_assert(data->bass);
>
> + bt_bass_set_att(data->bass, att);
> + bt_bass_attach(data->bass, NULL);
> +
> + bt_bass_set_debug(data->bass, print_debug, "bt_bass:", NULL);
> +
> data->server = bt_gatt_server_new(data->db, att, 64, 0);
> g_assert(data->server);
>
> @@ -346,8 +1020,8 @@ static void test_sggit(void)
> * handle range of the request. The IUT reports all BASS
> * characteristics.
> */
> - define_test("BASS/SR/SGGIT/SER/BV-01-C", test_server, NULL,
> - DISC_BASS_SER);
> + define_test("BASS/SR/SGGIT/SER/BV-01-C", NULL, NULL, test_server,
> + test_teardown, NULL, DISC_BASS_SER);
>
> /* BASS/SR/SGGIT/CHA/BV-01-C [Service GGIT -
> * Broadcast Audio Scan Control Point]
> @@ -359,8 +1033,8 @@ static void test_sggit(void)
> * handle range of the request. The IUT reports one instance of the
> * Broadcast Audio Scan Control Point characteristic.
> */
> - define_test("BASS/SR/SGGIT/CHA/BV-01-C", test_server, NULL,
> - DISC_BCAST_AUDIO_SCAN_CP);
> + define_test("BASS/SR/SGGIT/CHA/BV-01-C", NULL, NULL, test_server,
> + test_teardown, NULL, DISC_BCAST_AUDIO_SCAN_CP);
>
> /* BASS/SR/SGGIT/CHA/BV-02-C [Service GGIT -
> * Broadcast Receive State]
> @@ -383,8 +1057,90 @@ static void test_sggit(void)
> * The IUT sends an ATT_Read_Response to the Lower Tester for each
> * ATT_Read_Request.
> */
> - define_test("BASS/SR/SGGIT/CHA/BV-02-C", test_server, NULL,
> - DISC_BCAST_RECV_STATE);
> + define_test("BASS/SR/SGGIT/CHA/BV-02-C", NULL, NULL, test_server,
> + test_teardown, NULL, DISC_BCAST_RECV_STATE);
> +}
> +
> +static void test_spe(void)
> +{
> + /* BASS/SR/SPE/BI-01-C [Ignore Invalid Source ID]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT does not respond to a control point
> + * procedure call that uses an invalid Source_ID parameter.
> + *
> + * Pass verdict:
> + * The IUT does not send a notification of the Broadcast Receive State
> + * characteristic.
> + */
> + define_test("BASS/SR/SPE/BI-01-C", NULL, NULL, test_server,
> + test_teardown, NULL, IGNORE_INVALID_SRC_ID);
> +
> + /* BASS/SR/SPE/BI-03-C [Add Source - Ignore Invalid Values]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT ignores Add Source control point
> + * procedure calls that include an RFU or Invalid parameter.
> + *
> + * Pass verdict:
> + * The IUT does not send a notification of the Broadcast Receive State
> + * characteristic.
> + */
> + define_test("BASS/SR/SPE/BI-03-C", NULL, NULL, test_server,
> + test_teardown, NULL, ADD_SRC_INVALID_PARAMS);
> +
> + /* BASS/SR/SPE/BI-04-C [Opcode Not Supported]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT returns an Opcode Not Supported error
> + * response when the opcode written is not supported by the IUT or is
> + * within a range that is reserved for future use being written to the
> + * Broadcast Audio Scan Control Point.
> + *
> + * Pass verdict:
> + * The IUT sends an error response of OPCODE NOT SUPPORTED.
> + */
> + define_test("BASS/SR/SPE/BI-04-C", NULL, NULL, test_server,
> + test_teardown, NULL, OPCODE_NOT_SUPPORTED);
> +
> + /* BASS/SR/SPE/BI-05-C [Remove Source While Synchronized to a Source]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT, if synchronized to PA and/or BIS,
> + * does not accept the Remove Source operation request.
> + *
> + * Pass verdict:
> + * The IUT does not send a notification to the Lower Tester for the
> + * Broadcast Receive State characteristic.
> + */
> + define_test("BASS/SR/SPE/BI-05-C", test_pre_setup, setup_powered,
> + test_server, test_teardown, test_post_teardown,
> + RM_SRC_WHILE_SYNC);
> +
> + /* BASS/SR/SPE/BI-06-C [Invalid Length]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT rejects writing of an opcode with
> + * an invalid length.
> + *
> + * Pass verdict:
> + * The IUT rejects the opcode.
> + */
> + define_test("BASS/SR/SPE/BI-06-C", NULL, NULL, test_server,
> + test_teardown, NULL, INVALID_LEN);
> +
> + /* BASS/SR/SPE/BI-07-C [Invalid Source ID]
> + *
> + * Test Purpose:
> + * Verify that the BASS Server IUT returns an error when a control
> + * point procedure passing an invalid Source_ID parameter is called.
> + *
> + * Pass verdict:
> + * The IUT sends an ATT Error Response with the Error Code set to
> + * Invalid Source_ID.
> + */
> + define_test("BASS/SR/SPE/BI-07-C", NULL, NULL, test_server,
> + test_teardown, NULL, INVALID_SRC_ID);
> }
>
> int main(int argc, char *argv[])
> @@ -392,6 +1148,7 @@ int main(int argc, char *argv[])
> tester_init(&argc, &argv);
>
> test_sggit();
> + test_spe();
>
> return tester_run();
> }
> --
> 2.39.2

Some tests don't seem to be running:

BASS/SR/SPE/BI-05-C Not Run

And if I try with sudo it just hangs, anyway make check is probably
not required to run with sudo so Id consider removing the hciemu logic
and just hardcode the expect HCI traffic has happened, anyway the idea
of unit test suite is just to test the shared library portion, if we
need a more complete end-to-end then it is probably better to have it
run with test-runner so it run in its own vm with tester.config, etc.


--
Luiz Augusto von Dentz

2023-10-05 21:00:45

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH BlueZ 0/4] Add BASS unit tests for the SPE suite

Hello:

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

On Wed, 4 Oct 2023 18:31:46 +0300 you wrote:
> This patch series adds BASS unit tests for the Service Procedures Error
> Handling suite.
>
> Some unit tests require the BASS Server to synchronize to a broadcast
> source, as instructed by a BASS Client. For this, a testing framework
> was added similar to the one used by iso-tester, to emulate hardware.
>
> [...]

Here is the summary with links:
- [BlueZ,1/4] btio: Bind listener to bcaster addr based on dst opt
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=769268f4b1f4
- [BlueZ,2/4] hciemu: Add support for setting emulator bdaddr
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b94e33f90aa0
- [BlueZ,3/4] shared/bass: Add miscellaneous fixes
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=a56bc5ec376a
- [BlueZ,4/4] test-bass: Add unit tests for the SPE suite
(no matching commit)

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


2023-10-10 15:45:55

by Iulia Tanasescu

[permalink] [raw]
Subject: Re: [PATCH BlueZ 4/4] test-bass: Add unit tests for the SPE suite

Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <[email protected]>
> Sent: Thursday, October 5, 2023 11:52 PM
> To: Iulia Tanasescu <[email protected]>
> Cc: [email protected]; Claudia Cristina Draghicescu
> <[email protected]>; Mihai-Octavian Urzica <mihai-
> [email protected]>; Silviu Florian Barbulescu
> <[email protected]>; Vlad Pruteanu <[email protected]>;
> Andrei Istodorescu <[email protected]>
> Subject: Re: [PATCH BlueZ 4/4] test-bass: Add unit tests for the SPE
> suite
>
> Hi Iulia,
>
> On Wed, Oct 4, 2023 at 8:32 AM Iulia Tanasescu <[email protected]>
> wrote:
> >
> > This adds BASS unit tests for the Service Procedures Error Handling
> > suite.
> >
> > Some unit tests require the BASS Server to synchronize to a broadcast
> > source, as instructed by a BASS Client. For this, a testing framework
> > was added similar to the one used by iso-tester, to emulate hardware.
> >
> > ---
> > Makefile.am | 7 +-
> > unit/test-bass.c | 781
> > ++++++++++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 775 insertions(+), 13 deletions(-)
> >
> > diff --git a/Makefile.am b/Makefile.am index 30db74a0c..088f5aaef
> > 100644
> > --- a/Makefile.am
> > +++ b/Makefile.am
> > @@ -581,7 +581,12 @@ unit_test_bap_LDADD = src/libshared-glib.la \
> >
> > unit_tests += unit/test-bass
> >
> > -unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources)
> > +unit_test_bass_SOURCES = unit/test-bass.c $(btio_sources) monitor/bt.h
> \
> > + emulator/hciemu.h emulator/hciemu.c \
> > + emulator/vhci.h emulator/vhci.c \
> > + emulator/btdev.h emulator/btdev.c \
> > + emulator/bthost.h emulator/bthost.c \
> > + emulator/smp.c
> > unit_test_bass_LDADD = src/libshared-glib.la \
> > lib/libbluetooth-internal.la
> > $(GLIB_LIBS)
> >
> > diff --git a/unit/test-bass.c b/unit/test-bass.c index
> > 2ab61f760..4407bb4d3 100644
> > --- a/unit/test-bass.c
> > +++ b/unit/test-bass.c
> > @@ -18,13 +18,22 @@
> > #include <sys/socket.h>
> > #include <fcntl.h>
> >
> > +#include <stdbool.h>
> > +
> > #include <glib.h>
> >
> > #include "lib/bluetooth.h"
> > #include "lib/uuid.h"
> > +#include "lib/mgmt.h"
> > +
> > +#include "monitor/bt.h"
> > +#include "emulator/bthost.h"
> > +#include "emulator/hciemu.h"
> > +
> > #include "src/shared/util.h"
> > #include "src/shared/io.h"
> > #include "src/shared/tester.h"
> > +#include "src/shared/mgmt.h"
> > #include "src/shared/queue.h"
> > #include "src/shared/att.h"
> > #include "src/shared/gatt-db.h"
> > @@ -39,6 +48,9 @@ struct test_data {
> > struct queue *ccc_states;
> > size_t iovcnt;
> > struct iovec *iov;
> > + struct mgmt *mgmt;
> > + uint16_t mgmt_index;
> > + struct hciemu *hciemu;
> > };
> >
> > struct ccc_state {
> > @@ -190,16 +202,397 @@ struct ccc_state {
> > DISC_BCAST_AUDIO_SCAN_CP, \
> > BASS_READ_CHAR_DESC
> >
> > +/* ATT: Write Request (0x12) len 4
> > + * Handle: 0x0004 Type: Client Characteristic Configuration (0x2902)
> > + * Data: 0100
> > + * Notification (0x01)
> > + * ATT: Write Response (0x13) len 0
> > + * ATT: Write Request (0x12) len 4
> > + * Handle: 0x0007 Type: Client Characteristic Configuration (0x2902)
> > + * Data: 0100
> > + * Notification (0x01)
> > + * ATT: Write Response (0x13) len 0
> > + */
> > +#define BASS_WRITE_CHAR_DESC \
> > + IOV_DATA(0x12, 0x04, 0x00, 0x01, 0x00), \
> > + IOV_DATA(0x13), \
> > + IOV_DATA(0x12, 0x07, 0x00, 0x01, 0x00), \
> > + IOV_DATA(0x13)
> > +
> > +/* ATT: Read Request (0x0a) len 2
> > + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> > + * ATT: Read Response (0x0b) len 0
> > + * Handle: 0x0003 Broadcast Receive State (0x2bc8)
> > + * ATT: Read Request (0x0a) len 2
> > + * Handle: 0x0006 Type: Broadcast Receive State (0x2bc8)
> > + * ATT: Read Response (0x0b) len 0
> > + * Handle: 0x0006 Broadcast Receive State (0x2bc8)
> > + */
> > +#define BASS_READ_BCAST_RECV_STATE_CHARS \
> > + IOV_DATA(0x0a, 0x03, 0x00), \
> > + IOV_DATA(0x0b), \
> > + IOV_DATA(0x0a, 0x06, 0x00), \
> > + IOV_DATA(0x0b)
> > +
> > +#define BASS_CP_WRITE_CMD(_op, _args...) \
> > + IOV_DATA(0x52, 0x09, 0x00, _op, _args)
> > +
> > +#define BASS_CP_WRITE_REQ(_op, _args...) \
> > + IOV_DATA(0x12, 0x09, 0x00, _op, _args)
> > +
> > +/* ATT: Write Command (0x52) len 19
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0401693C4572685526613465597073275455
> > + * Opcode: Set Broadcast_Code
> > + * Source_ID: 1
> > + * Broadcast_Code: 0x55542773705965346126556872453c69
> > + * ATT: Write Command (0x52) len 2
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0501
> > + * Opcode: Remove Source
> > + * Source_ID: 1
> > + */
> > +#define IGNORE_INVALID_SRC_ID \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC, \
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_CMD(0x04, 0x01, 0x69, 0x3C, 0x45, 0x72, 0x68, \
> > + 0x55, 0x26, 0x61, 0x34, 0x65, 0x59, 0x70, \
> > + 0x73, 0x27, 0x54, 0x55), \
> > + IOV_NULL, \
> > + BASS_CP_WRITE_CMD(0x05, 0x01)
> > +
> > +/* ATT: Write Command (0x52) len 26
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0200F2698BE807C0003412000610270200000000000000000000
> > + * Opcode: Add Source
> > + * Advertiser_Address_Type: Public Device or Public Identity Address
> > + * Advertiser_Address: c0:07:e8:8b:69:f2
> > + * Advertising_SID: 0x00
> > + * Broadcast_ID: 0x001234
> > + * PA_Sync: 0x06 (Reserved for Future Use)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 2
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * Subgroup #1:
> > + * BIS_Sync: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * ATT: Write Command (0x52) len 26
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0205F2698BE807C0003412000210270200000000000000000000
> > + * Opcode: Add Source
> > + * Advertiser_Address_Type: 0x05 (Reserved for Future Use)
> > + * Advertiser_Address: c0:07:e8:8b:69:f2
> > + * Advertising_SID: 0x00
> > + * Broadcast_ID: 0x001234
> > + * PA_Sync: Synchronize to PA (PAST not available)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 2
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * Subgroup #1:
> > + * BIS_Sync: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * ATT: Write Command (0x52) len 26
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0200F2698BE807C0003412000210270201000000000100000000
> > + * Opcode: Add Source
> > + * Advertiser_Address_Type: Public Device or Public Identity Address
> > + * Advertiser_Address: c0:07:e8:8b:69:f2
> > + * Advertising_SID: 0x00
> > + * Broadcast_ID: 0x001234
> > + * PA_Sync: Synchronize to PA (PAST not available)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 2
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000001
> > + * Metadata_Length: 0
> > + * Subgroup #1:
> > + * BIS_Sync: 00000000000000000000000000000001
> > + * Metadata_Length: 0
> > + */
> > +#define ADD_SRC_INVALID_PARAMS \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC,\
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0,
> \
> > + 0x00, 0x34, 0x12, 0x00, 0x06, 0x10, 0x27, 0x02, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > + 0x00, 0x00), \
> > + IOV_NULL, \
> > + BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0,
> \
> > + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > + 0x00, 0x00), \
> > + IOV_NULL, \
> > + BASS_CP_WRITE_CMD(0x02, 0x05, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0,
> \
> > + 0x3F, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
> > + 0x00, 0x00), \
> > + IOV_NULL, \
> > + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0,
> \
> > + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x02, \
> > + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, \
> > + 0x00, 0x00)
> > +
> > +/* ATT: Write Request (0x12) len 3
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: FF
> > + * Opcode: 0xff (Reserved For Future Use)
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Opcode Not Supported (0x80)
> > + */
> > +#define OPCODE_NOT_SUPPORTED \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC,\
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_REQ(0xFF), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x80)
> > +
> > +/* ATT: Write Command (0x52) len 26
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0200F2698BE807C000F5D983021027010000000000
> > + * Opcode: Add Source
> > + * Advertiser_Address_Type: Public Device or Public Identity Address
> > + * Advertiser_Address: c0:07:e8:8b:69:f2
> > + * Advertising_SID: 0x00
> > + * Broadcast_ID: 0x83d9f5
> > + * PA_Sync: Synchronize to PA (PAST not available)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 1
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * ATT: Handle Value Notification (0x1b) len 22
> > + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> > + * Data: 0100F2698BE807C000F5D9830200010000000000
> > + * Source_ID: 0x01
> > + * Source_Address_Type: Public Device or Public Identity Address
> > + * Source_Address: c0:07:e8:8b:69:f2
> > + * Source_Adv_SID: 0x00
> > + * Broadcast_ID: 0x83d9f5
> > + * PA_Sync_State: Synchronized to PA
> > + * BIG_Encryption: Not encrypted
> > + * Num_Subgroups: 1
> > + * Subgroup #0:
> > + * BIS_Sync State: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * ATT: Read Request (0x0a) len 2
> > + * Handle: 0x0003 Type: Broadcast Receive State (0x2bc8)
> > + * ATT: Read Response (0x0b) len 20
> > + * Handle: 0x0003 Broadcast Receive State (0x2bc8)
> > + * Source_ID: 0x01
> > + * Source_Address_Type: Public Device or Public Identity Address
> > + * Source_Address: c0:07:e8:8b:69:f2
> > + * Source_Adv_SID: 0x00
> > + * Broadcast_ID: 0x83d9f5
> > + * PA_Sync_State: Synchronized to PA
> > + * BIG_Encryption: Not encrypted
> > + * Num_Subgroups: 1
> > + * Subgroup #0:
> > + * BIS_Sync State: 00000000000000000000000000000000
> > + * Metadata_Length: 0
> > + * ATT: Write Request (0x12) len 2
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0501
> > + * Opcode: Remove Source
> > + * Source_ID: 1
> > + * ATT: Write Response (0x13) len 0
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + */
> > +#define RM_SRC_WHILE_SYNC \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC, \
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_CMD(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0,
> \
> > + 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x10, 0x27, 0x01, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00), \
> > + IOV_DATA(0x1b, 0x03, 0x00, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, \
> > + 0x07, 0xC0, 0x00, 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00), \
> > + IOV_DATA(0x0a, 0x03, 0x00), \
> > + IOV_DATA(0x0b, 0x01, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, 0x00, \
> > + 0xF5, 0xD9, 0x83, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, \
> > + 0x00), \
> > + BASS_CP_WRITE_REQ(0x05, 0x01), \
> > + IOV_DATA(0x13)
> > +
> > +/* ATT: Write Request (0x12) len 5
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 006dfe
> > + * Opcode: Remote Scan Stopped
> > + * Extra Data: 0xfe6d
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + * ATT: Write Request (0x12) len 5
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 006dfe
> > + * Opcode: Remote Scan Started
> > + * Extra Data: 0xa2c2
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + * ATT: Write Request (0x12) len 25
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0200F2698BE807C0003412000210270100000000000000
> > + * Opcode: Add Source
> > + * Advertiser_Address_Type: Public Device or Public Identity Address
> > + * Advertiser_Address: c0:07:e8:8b:69:f2
> > + * Advertising_SID: 0x00
> > + * Broadcast_ID: 0x001234
> > + * PA_Sync: Synchronize to PA (PAST not available)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 1
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000001
> > + * Metadata_Length: 0
> > + * Extra Data: 0x0000
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + * ATT: Write Request (0x12) len 13
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 03000210270100000000001500
> > + * Opcode: Modify Source
> > + * Source_ID: 0x00
> > + * PA_Sync: Synchronize to PA (PAST not available)
> > + * PA_Interval: 0x2710
> > + * Num_Subgroups: 1
> > + * Subgroup #0:
> > + * BIS_Sync: 00000000000000000000000000000001
> > + * Metadata_Length: 0
> > + * Extra Data: 0x0015
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + * ATT: Write Request (0x12) len 20
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0400B803EAC6AFBB65A25A41F153056802010000
> > + * Opcode: Set Broadcast_Code
> > + * Source_ID: 0x00
> > + * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b8
> > + * Extra Data: 0x0000
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + * ATT: Write Request (0x12) len 4
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 05008F13
> > + * Opcode: Remove Source
> > + * Source_ID: 0x00
> > + * Extra Data: 0x138f
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Write Request Rejected (0xFC)
> > + */
> > +#define INVALID_LEN \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC,\
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_REQ(0x00, 0x6D, 0xFE), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> > + BASS_CP_WRITE_REQ(0x01, 0xC2, 0xA2), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> > + BASS_CP_WRITE_REQ(0x02, 0x00, 0xF2, 0x69, 0x8B, 0xE8, 0x07, 0xC0, \
> > + 0x00, 0x34, 0x12, 0x00, 0x02, 0x10, 0x27, 0x01, \
> > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> > + BASS_CP_WRITE_REQ(0x03, 0x00, 0x02, 0x10, 0x27, 0x01, 0x00, 0x00, \
> > + 0x00, 0x00, 0x00, 0x15, 0x00), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> > + BASS_CP_WRITE_REQ(0x04, 0x00, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB,
> \
> > + 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
> > + 0x02, 0x01, 0x00, 0x00), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC), \
> > + BASS_CP_WRITE_REQ(0x05, 0x00, 0x8F, 0x13), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0xFC)
> > +
> > +/* ATT: Write Request (0x12) len 20
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 0400B803EAC6AFBB65A25A41F153056802010000
> > + * Opcode: Set Broadcast_Code
> > + * Source_ID: 0x05
> > + * Broadcast_Code: 0x0102680553f1415aa265bbafc6ea03b
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Invalid Source ID (0x81)
> > + * ATT: Write Request (0x12) len 4
> > + * Handle: 0x0009 Type: Broadcast Audio Scan Control Point (0x2bc7)
> > + * Data: 005
> > + * Opcode: Remove Source
> > + * Source_ID: 0x05
> > + * ATT: Error Response (0x01) len 4
> > + * Write Request (0x12)
> > + * Handle: 0x0009
> > + * Error: Invalid Source ID (0x81)
> > + */
> > +#define INVALID_SRC_ID \
> > + EXCHANGE_MTU, \
> > + BASS_FIND_BY_TYPE_VALUE, \
> > + DISC_BASS_CHAR, \
> > + BASS_FIND_INFO, \
> > + BASS_WRITE_CHAR_DESC, \
> > + BASS_READ_BCAST_RECV_STATE_CHARS, \
> > + BASS_CP_WRITE_REQ(0x04, 0x05, 0xB8, 0x03, 0xEA, 0xC6, 0xAF, 0xBB,
> \
> > + 0x65, 0xA2, 0x5A, 0x41, 0xF1, 0x53, 0x05, 0x68, \
> > + 0x02, 0x01), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81), \
> > + BASS_CP_WRITE_REQ(0x05, 0x05), \
> > + IOV_DATA(0x01, 0x12, 0x09, 0x00, 0x81)
> > +
> > +static const uint8_t client_bdaddr[] = {0xF2, 0x69, 0x8B, 0xE8, 0x07,
> > +0xC0};
> > +
> > +static const uint8_t set_iso_socket_param[] = {
> > + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
> > + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
> > + 0x01, /* Action - enable */
> > +};
> > +
> > +static const uint8_t reset_iso_socket_param[] = {
> > + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */
> > + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
> > + 0x00, /* Action - disable */
> > +};
> > +
> > #define iov_data(args...) ((const struct iovec[]) { args })
> >
> > -#define define_test(name, function, _cfg, args...) \
> > +#define define_test(name, pre_setup, setup, function, teardown, \
> > + post_teardown, args...) \
> > do { \
> > const struct iovec iov[] = { args }; \
> > static struct test_data data; \
> > data.iovcnt = ARRAY_SIZE(iov_data(args)); \
> > data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
> > - tester_add(name, &data, NULL, function, \
> > - test_teardown); \
> > + tester_add_full(name, &data, pre_setup, setup, function, \
> > + teardown, post_teardown, 0, NULL,
> > + NULL); \
> > } while (0)
> >
> > static void test_complete_cb(const void *user_data) @@ -287,11
> > +680,282 @@ done:
> > gatt_db_attribute_read_result(attrib, id, ecode, value, len);
> > }
> >
> > +static void gatt_ccc_write_cb(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 test_data *data = (void *)user_data;
> > + struct ccc_state *ccc_state;
> > + uint16_t val;
> > + uint8_t ecode = 0;
> > +
> > + if (!value || len > 2) {
> > + ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
> > + goto done;
> > + }
> > +
> > + if (offset > 2) {
> > + ecode = BT_ATT_ERROR_INVALID_OFFSET;
> > + goto done;
> > + }
> > +
> > + if (len == 1)
> > + val = *value;
> > + else
> > + val = get_le16(value);
> > +
> > + ccc_state = get_ccc_state(data,
> gatt_db_attribute_get_handle(attrib));
> > + if (!ccc_state)
> > + return;
> > +
> > + /* If value is identical, then just succeed */
> > + if (val == ccc_state->value)
> > + goto done;
> > +
> > + ccc_state->value = val;
> > +
> > +done:
> > + gatt_db_attribute_write_result(attrib, id, ecode); }
> > +
> > +static void gatt_notify_cb(struct gatt_db_attribute *attrib,
> > + struct gatt_db_attribute *ccc,
> > + const uint8_t *value, size_t len,
> > + struct bt_att *att, void *user_data) {
> > + struct test_data *data = user_data;
> > + struct ccc_state *ccc_state;
> > +
> > + ccc_state = find_ccc_state(data, gatt_db_attribute_get_handle(ccc));
> > + if (!ccc_state || !(ccc_state->value & 0x0001))
> > + return;
> > +
> > + bt_gatt_server_send_notification(data->server,
> > + gatt_db_attribute_get_handle(attrib),
> > + value, len, false);
> > +}
> > +
> > +static void set_iso_socket_callback(uint8_t status, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + if (status != MGMT_STATUS_SUCCESS) {
> > + tester_print("ISO socket feature could not be enabled");
> > + return;
> > + }
> > +
> > + tester_print("ISO socket feature is enabled"); }
> > +
> > +static void read_info_callback(uint8_t status, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + struct test_data *data = (void *)user_data;
> > + const struct mgmt_rp_read_info *rp = param;
> > + char addr[18];
> > + uint16_t manufacturer;
> > + uint32_t supported_settings, current_settings;
> > +
> > + tester_print("Read Info callback");
> > + tester_print(" Status: 0x%02x", status);
> > +
> > + if (status || !param) {
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + ba2str(&rp->bdaddr, addr);
> > + manufacturer = btohs(rp->manufacturer);
> > + supported_settings = btohl(rp->supported_settings);
> > + current_settings = btohl(rp->current_settings);
> > +
> > + tester_print(" Address: %s", addr);
> > + tester_print(" Version: 0x%02x", rp->version);
> > + tester_print(" Manufacturer: 0x%04x", manufacturer);
> > + tester_print(" Supported settings: 0x%08x", supported_settings);
> > + tester_print(" Current settings: 0x%08x", current_settings);
> > + tester_print(" Class: 0x%02x%02x%02x",
> > + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
> > + tester_print(" Name: %s", rp->name);
> > + tester_print(" Short name: %s", rp->short_name);
> > +
> > + if (strcmp(hciemu_get_address(data->hciemu), addr)) {
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + tester_pre_setup_complete();
> > +}
> > +
> > +static void index_added_callback(uint16_t index, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + struct test_data *data = (void *)user_data;
> > +
> > + tester_print("Index Added callback");
> > + tester_print(" Index: 0x%04x", index);
> > +
> > + data->mgmt_index = index;
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data-
> >mgmt_index, 0, NULL,
> > + read_info_callback, data,
> > +NULL); }
> > +
> > +static void index_removed_callback(uint16_t index, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + struct test_data *data = (void *)user_data;
> > +
> > + tester_print("Index Removed callback");
> > + tester_print(" Index: 0x%04x", index);
> > +
> > + if (index != data->mgmt_index)
> > + return;
> > +
> > + mgmt_unregister_index(data->mgmt, data->mgmt_index);
> > +
> > + mgmt_unref(data->mgmt);
> > + data->mgmt = NULL;
> > +
> > + tester_post_teardown_complete(); }
> > +
> > +static void read_index_list_callback(uint8_t status, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + struct test_data *data = (void *)user_data;
> > + struct hciemu_client *client;
> > +
> > + tester_print("Read Index List callback");
> > + tester_print(" Status: 0x%02x", status);
> > +
> > + if (status || !param) {
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED,
> MGMT_INDEX_NONE,
> > + index_added_callback, data,
> > + NULL);
> > +
> > + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED,
> MGMT_INDEX_NONE,
> > + index_removed_callback, data,
> > + NULL);
> > +
> > + data->hciemu = hciemu_new(HCIEMU_TYPE_BREDRLE52);
> > + if (!data->hciemu) {
> > + tester_warn("Failed to setup HCI emulation");
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + client = hciemu_get_client(data->hciemu, 0);
> > +
> > + if (!hciemu_set_client_bdaddr(client, client_bdaddr)) {
> > + tester_warn("Failed to setup HCI emulation");
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + tester_print("New hciemu instance created"); }
> > +
> > +static void test_pre_setup(const void *test_data) {
> > + struct test_data *data = (void *)test_data;
> > +
> > + data->mgmt = mgmt_new_default();
> > + if (!data->mgmt) {
> > + tester_warn("Failed to setup management interface");
> > + tester_pre_setup_failed();
> > + return;
> > + }
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE,
> MGMT_INDEX_NONE,
> > + sizeof(set_iso_socket_param), set_iso_socket_param,
> > + set_iso_socket_callback, NULL, NULL);
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST,
> MGMT_INDEX_NONE, 0, NULL,
> > + read_index_list_callback,
> > +data, NULL); }
> > +
> > +static void test_post_teardown(const void *test_data) {
> > + struct test_data *data = (void *)test_data;
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE,
> MGMT_INDEX_NONE,
> > + sizeof(reset_iso_socket_param), reset_iso_socket_param,
> > + NULL, NULL, NULL);
> > +
> > + hciemu_unref(data->hciemu);
> > + data->hciemu = NULL;
> > +}
> > +
> > +static void client_connectable_complete(uint16_t opcode, uint8_t status,
> > + const void *param, uint8_t len,
> > + void *user_data) {
> > + if (opcode != BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE)
> > + return;
> > +
> > + tester_print("Client set connectable status 0x%02x", status);
> > +
> > + if (status)
> > + tester_setup_failed();
> > + else
> > + tester_setup_complete(); }
> > +
> > +static void setup_powered_callback(uint8_t status, uint16_t length,
> > + const void *param, void
> > +*user_data) {
> > + struct test_data *data = (void *)user_data;
> > + struct hciemu_client *client;
> > + struct bthost *host;
> > + uint8_t bcode[16] = {0x00};
> > +
> > + if (status != MGMT_STATUS_SUCCESS) {
> > + tester_setup_failed();
> > + return;
> > + }
> > +
> > + tester_print("Controller powered on");
> > +
> > + client = hciemu_get_client(data->hciemu, 0);
> > + host = hciemu_client_host(client);
> > + bthost_set_cmd_complete_cb(host, client_connectable_complete,
> > + data);
> > + bthost_set_ext_adv_params(host);
> > + bthost_set_ext_adv_enable(host, 0x01);
> > +
> > + bthost_set_pa_params(host);
> > + bthost_set_pa_enable(host, 0x01);
> > + bthost_create_big(host, 1, 0x00, bcode); }
> > +
> > +static void setup_powered(const void *test_data) {
> > + struct test_data *data = (void *)test_data;
> > + unsigned char param[] = { 0x01 };
> > +
> > + tester_print("Powering on controller");
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
> > + sizeof(param), param, NULL, NULL,
> > + NULL);
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
> > + sizeof(param), param, NULL, NULL,
> > + NULL);
> > +
> > + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data-
> >mgmt_index,
> > + sizeof(param), param,
> > + setup_powered_callback, data,
> > +NULL); }
> > +
> > static void test_server(const void *user_data) {
> > struct test_data *data = (void *)user_data;
> > struct bt_att *att;
> > struct io *io;
> > + bdaddr_t adapter_bdaddr = {{0, 0, 0, 0, 0, 0}};
> >
> > io = tester_setup_io(data->iov, data->iovcnt);
> > g_assert(io);
> > @@ -306,12 +970,22 @@ static void test_server(const void *user_data)
> > data->db = gatt_db_new();
> > g_assert(data->db);
> >
> > - gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
> > - NULL, data);
> > + gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
> > + gatt_notify_cb, data);
> >
> > - data->bass = bt_bass_new(data->db, NULL, BDADDR_ANY);
> > + if (data->hciemu)
> > + memcpy(&adapter_bdaddr,
> > + hciemu_get_central_bdaddr(data->hciemu),
> > + sizeof(adapter_bdaddr));
> > +
> > + data->bass = bt_bass_new(data->db, NULL, &adapter_bdaddr);
> > g_assert(data->bass);
> >
> > + bt_bass_set_att(data->bass, att);
> > + bt_bass_attach(data->bass, NULL);
> > +
> > + bt_bass_set_debug(data->bass, print_debug, "bt_bass:", NULL);
> > +
> > data->server = bt_gatt_server_new(data->db, att, 64, 0);
> > g_assert(data->server);
> >
> > @@ -346,8 +1020,8 @@ static void test_sggit(void)
> > * handle range of the request. The IUT reports all BASS
> > * characteristics.
> > */
> > - define_test("BASS/SR/SGGIT/SER/BV-01-C", test_server, NULL,
> > - DISC_BASS_SER);
> > + define_test("BASS/SR/SGGIT/SER/BV-01-C", NULL, NULL, test_server,
> > + test_teardown, NULL, DISC_BASS_SER);
> >
> > /* BASS/SR/SGGIT/CHA/BV-01-C [Service GGIT -
> > * Broadcast Audio Scan Control Point]
> > @@ -359,8 +1033,8 @@ static void test_sggit(void)
> > * handle range of the request. The IUT reports one instance of the
> > * Broadcast Audio Scan Control Point characteristic.
> > */
> > - define_test("BASS/SR/SGGIT/CHA/BV-01-C", test_server, NULL,
> > - DISC_BCAST_AUDIO_SCAN_CP);
> > + define_test("BASS/SR/SGGIT/CHA/BV-01-C", NULL, NULL, test_server,
> > + test_teardown, NULL, DISC_BCAST_AUDIO_SCAN_CP);
> >
> > /* BASS/SR/SGGIT/CHA/BV-02-C [Service GGIT -
> > * Broadcast Receive State]
> > @@ -383,8 +1057,90 @@ static void test_sggit(void)
> > * The IUT sends an ATT_Read_Response to the Lower Tester for each
> > * ATT_Read_Request.
> > */
> > - define_test("BASS/SR/SGGIT/CHA/BV-02-C", test_server, NULL,
> > - DISC_BCAST_RECV_STATE);
> > + define_test("BASS/SR/SGGIT/CHA/BV-02-C", NULL, NULL, test_server,
> > + test_teardown, NULL, DISC_BCAST_RECV_STATE);
> > +}
> > +
> > +static void test_spe(void)
> > +{
> > + /* BASS/SR/SPE/BI-01-C [Ignore Invalid Source ID]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT does not respond to a control point
> > + * procedure call that uses an invalid Source_ID parameter.
> > + *
> > + * Pass verdict:
> > + * The IUT does not send a notification of the Broadcast Receive State
> > + * characteristic.
> > + */
> > + define_test("BASS/SR/SPE/BI-01-C", NULL, NULL, test_server,
> > + test_teardown, NULL, IGNORE_INVALID_SRC_ID);
> > +
> > + /* BASS/SR/SPE/BI-03-C [Add Source - Ignore Invalid Values]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT ignores Add Source control point
> > + * procedure calls that include an RFU or Invalid parameter.
> > + *
> > + * Pass verdict:
> > + * The IUT does not send a notification of the Broadcast Receive State
> > + * characteristic.
> > + */
> > + define_test("BASS/SR/SPE/BI-03-C", NULL, NULL, test_server,
> > + test_teardown, NULL, ADD_SRC_INVALID_PARAMS);
> > +
> > + /* BASS/SR/SPE/BI-04-C [Opcode Not Supported]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT returns an Opcode Not Supported
> error
> > + * response when the opcode written is not supported by the IUT or is
> > + * within a range that is reserved for future use being written to the
> > + * Broadcast Audio Scan Control Point.
> > + *
> > + * Pass verdict:
> > + * The IUT sends an error response of OPCODE NOT SUPPORTED.
> > + */
> > + define_test("BASS/SR/SPE/BI-04-C", NULL, NULL, test_server,
> > + test_teardown, NULL, OPCODE_NOT_SUPPORTED);
> > +
> > + /* BASS/SR/SPE/BI-05-C [Remove Source While Synchronized to a
> Source]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT, if synchronized to PA and/or BIS,
> > + * does not accept the Remove Source operation request.
> > + *
> > + * Pass verdict:
> > + * The IUT does not send a notification to the Lower Tester for the
> > + * Broadcast Receive State characteristic.
> > + */
> > + define_test("BASS/SR/SPE/BI-05-C", test_pre_setup, setup_powered,
> > + test_server, test_teardown, test_post_teardown,
> > + RM_SRC_WHILE_SYNC);
> > +
> > + /* BASS/SR/SPE/BI-06-C [Invalid Length]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT rejects writing of an opcode with
> > + * an invalid length.
> > + *
> > + * Pass verdict:
> > + * The IUT rejects the opcode.
> > + */
> > + define_test("BASS/SR/SPE/BI-06-C", NULL, NULL, test_server,
> > + test_teardown, NULL, INVALID_LEN);
> > +
> > + /* BASS/SR/SPE/BI-07-C [Invalid Source ID]
> > + *
> > + * Test Purpose:
> > + * Verify that the BASS Server IUT returns an error when a control
> > + * point procedure passing an invalid Source_ID parameter is called.
> > + *
> > + * Pass verdict:
> > + * The IUT sends an ATT Error Response with the Error Code set to
> > + * Invalid Source_ID.
> > + */
> > + define_test("BASS/SR/SPE/BI-07-C", NULL, NULL, test_server,
> > + test_teardown, NULL, INVALID_SRC_ID);
> > }
> >
> > int main(int argc, char *argv[])
> > @@ -392,6 +1148,7 @@ int main(int argc, char *argv[])
> > tester_init(&argc, &argv);
> >
> > test_sggit();
> > + test_spe();
> >
> > return tester_run();
> > }
> > --
> > 2.39.2
>
> Some tests don't seem to be running:
>
> BASS/SR/SPE/BI-05-C Not Run
>
> And if I try with sudo it just hangs, anyway make check is probably
> not required to run with sudo so Id consider removing the hciemu logic
> and just hardcode the expect HCI traffic has happened, anyway the idea
> of unit test suite is just to test the shared library portion, if we
> need a more complete end-to-end then it is probably better to have it
> run with test-runner so it run in its own vm with tester.config, etc.
>

I understand, I removed the emulator framework and I submitted a new
patch with the tests that only require the shared library.

>
> --
> Luiz Augusto von Dentz

Regards,
Iulia