diff --git a/src/shared/lc3.h b/src/shared/lc3.h
index e7a9277..a6f65ee 100644
--- a/src/shared/lc3.h
+++ b/src/shared/lc3.h
#define LC3_FRAME_COUNT (LC3_TYPE_BASE + 4)
#define LC3_CAPABILITIES(_freq, _duration, _chan_count, _len_min, _len_max) \
- UTIL_IOV_INIT(0x02, LC3_FREQ, _freq, _freq >> 8, \
+ UTIL_IOV_INIT(0x03, LC3_FREQ, _freq, _freq >> 8, \
0x02, LC3_DURATION, _duration, \
0x02, LC3_CHAN_COUNT, _chan_count, \
0x05, LC3_FRAME_LEN, _len_min, _len_min >> 8, \
diff --git a/unit/test-bap.c b/unit/test-bap.c
index 0c190d7..cabb9b2 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
#include "src/shared/bap.h"
#include "src/shared/lc3.h"
struct test_data {
struct bt_gatt_client *client;
+ struct bt_gatt_server *server;
struct gatt_db *db;
+ struct queue *ccc_states;
struct bt_bap *bap;
+ unsigned int id;
struct bt_bap_pac *snk;
struct bt_bap_pac *src;
struct bt_bap_pac *bsrc;
struct bt_bap_pac *bsnk;
+ struct bt_bap_pac_qos *qos;
struct iovec *base;
struct iovec *caps;
struct test_config *cfg;
struct iovec *iov;
};
+struct notify {
+ uint16_t handle, ccc_handle;
+ uint8_t *value;
+ uint16_t len;
+ bt_gatt_server_conf_func_t conf;
+ void *user_data;
+};
+
+struct ccc_state {
+ uint16_t handle;
+ uint16_t value;
+};
+
/*
* Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
* Duration: 7.5 ms 10 ms
static struct iovec lc3_caps = LC3_CAPABILITIES(LC3_FREQ_ANY, LC3_DURATION_ANY,
3u, 26, 240);
+static struct bt_bap_pac_qos lc3_qos = {
+ .location = 0x00000003,
+ .supported_context = 0x0fff,
+ .context = 0x0fff
+};
+
#define iov_data(args...) ((const struct iovec[]) { args })
#define define_test(name, setup, function, _cfg, args...) \
const struct iovec iov[] = { args }; \
static struct test_data data; \
data.caps = &lc3_caps; \
+ data.qos = &lc3_qos; \
data.cfg = _cfg; \
data.iovcnt = ARRAY_SIZE(iov_data(args)); \
data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
* Handle: 0x0022
* Error: Attribute Not Found (0x0a)
*/
- IOV_DATA(0x01, 0x08, 0x23, 0x00, 0x0a),
+ IOV_DATA(0x01, 0x08, 0x22, 0x00, 0x0a),
/* ACL Data TX: Handle 42 flags 0x00 dlen 11
* ATT: Read By Type Request (0x08) len 6
* Handle range: 0x0001-0xffff
gatt_db_unref(db);
}
+static bool ccc_state_match(const void *a, const void *b)
+{
+ const struct ccc_state *ccc = a;
+ uint16_t handle = PTR_TO_UINT(b);
+
+ return ccc->handle == handle;
+}
+
+static struct ccc_state *find_ccc_state(struct test_data *data,
+ uint16_t handle)
+{
+ return queue_find(data->ccc_states, ccc_state_match,
+ UINT_TO_PTR(handle));
+}
+
+static struct ccc_state *get_ccc_state(struct test_data *data,
+ uint16_t handle)
+{
+ struct ccc_state *ccc;
+
+ ccc = find_ccc_state(data, handle);
+ if (ccc)
+ return ccc;
+
+ ccc = new0(struct ccc_state, 1);
+ ccc->handle = handle;
+ queue_push_tail(data->ccc_states, ccc);
+
+ return ccc;
+}
+
+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;
+ uint16_t handle = gatt_db_attribute_get_handle(attrib);
+
+ if (!data->server)
+ return;
+
+ if (!bt_gatt_server_send_notification(data->server,
+ handle, value, len, false))
+ printf("%s: Failed to send notification\n", __func__);
+}
+
+static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offest,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct test_data *data = user_data;
+ struct ccc_state *ccc;
+ uint16_t handle;
+ uint8_t ecode = 0;
+ uint16_t value = 0;
+
+ handle = gatt_db_attribute_get_handle(attrib);
+
+ ccc = get_ccc_state(data, handle);
+ if (!ccc) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ value = cpu_to_le16(ccc->value);
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, (void *)&value,
+ sizeof(value));
+}
+
+static void test_setup_pacs(struct test_data *data)
+{
+ if (!data->cfg)
+ return;
+
+ if (data->cfg->src) {
+ if (data->cfg->vs)
+ data->snk = bt_bap_add_vendor_pac(data->db,
+ "test-bap-snk",
+ BT_BAP_SINK, 0x0ff,
+ 0x0001, 0x0001,
+ NULL, data->caps, NULL);
+ else
+ data->snk = bt_bap_add_pac(data->db, "test-bap-snk",
+ BT_BAP_SINK, LC3_ID,
+ NULL, data->caps, NULL);
+ g_assert(data->snk);
+ }
+
+ if (data->cfg->snk) {
+ if (data->cfg->vs)
+ data->src = bt_bap_add_vendor_pac(data->db,
+ "test-bap-src",
+ BT_BAP_SOURCE, 0x0ff,
+ 0x0001, 0x0001,
+ NULL, data->caps, NULL);
+ else
+ data->src = bt_bap_add_pac(data->db, "test-bap-src",
+ BT_BAP_SOURCE, LC3_ID,
+ NULL, data->caps, NULL);
+ g_assert(data->src);
+ }
+}
+
+static void setup_complete_cb(const void *user_data)
+{
+ tester_setup_complete();
+}
+
+static void test_setup_server(const void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+ struct bt_att *att;
+ struct gatt_db *db;
+ struct io *io;
+
+ io = tester_setup_io(setup_data, ARRAY_SIZE(setup_data));
+ g_assert(io);
+
+ tester_io_set_complete_func(setup_complete_cb);
+
+ db = gatt_db_new();
+ g_assert(db);
+
+ gatt_db_ccc_register(db, gatt_ccc_read_cb, NULL, gatt_notify_cb, data);
+
+ data->ccc_states = queue_new();
+
+ /* If there is no configuration, add a sink PAC since otherwise bt_bap
+ * won't even register the required services.
+ */
+ if (!data->cfg) {
+ data->snk = bt_bap_add_pac(db, "test-bap-snk",
+ BT_BAP_SINK, LC3_ID,
+ data->qos, data->caps,
+ NULL);
+ data->src = bt_bap_add_pac(db, "test-bap-src",
+ BT_BAP_SOURCE, LC3_ID,
+ data->qos, data->caps,
+ NULL);
+ g_assert(data->snk);
+ g_assert(data->src);
+ } else {
+ test_setup_pacs(data);
+ }
+
+ att = bt_att_new(io_get_fd(io), false);
+ g_assert(att);
+
+ bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL);
+
+ data->server = bt_gatt_server_new(db, att, 64, 0);
+ g_assert(data->server);
+
+ bt_gatt_server_set_debug(data->server, print_debug, "bt_gatt_server:",
+ NULL);
+
+ tester_io_send();
+
+ bt_att_unref(att);
+ gatt_db_unref(db);
+}
+
static void test_complete_cb(const void *user_data)
{
tester_test_passed();
bt_bap_foreach_pac(bap, BT_BAP_SOURCE, pac_found, user_data);
}
-static void test_client_config(struct test_data *data)
-{
- if (!data->cfg)
- return;
-
- if (data->cfg->src) {
- if (data->cfg->vs)
- data->snk = bt_bap_add_vendor_pac(data->db,
- "test-bap-snk",
- BT_BAP_SINK, 0x0ff,
- 0x0001, 0x0001,
- NULL, data->caps, NULL);
- else
- data->snk = bt_bap_add_pac(data->db, "test-bap-snk",
- BT_BAP_SINK, LC3_ID,
- NULL, data->caps, NULL);
- g_assert(data->snk);
- }
-
- if (data->cfg->snk) {
- if (data->cfg->vs)
- data->src = bt_bap_add_vendor_pac(data->db,
- "test-bap-src",
- BT_BAP_SOURCE, 0x0ff,
- 0x0001, 0x0001,
- NULL, data->caps, NULL);
- else
- data->src = bt_bap_add_pac(data->db, "test-bap-src",
- BT_BAP_SOURCE, LC3_ID,
- NULL, data->caps, NULL);
- g_assert(data->src);
- }
-}
-
static void test_client(const void *user_data)
{
struct test_data *data = (void *)user_data;
data->db = gatt_db_new();
g_assert(data->db);
- test_client_config(data);
+ test_setup_pacs(data);
data->bap = bt_bap_new(data->db, bt_gatt_client_get_db(data->client));
g_assert(data->bap);
{
struct test_data *data = (void *)user_data;
+ bt_bap_unregister(data->id);
bt_bap_unref(data->bap);
bt_gatt_client_unref(data->client);
util_iov_free(data->iov, data->iovcnt);
bt_bap_remove_pac(data->bsrc);
bt_bap_remove_pac(data->bsnk);
gatt_db_unref(data->db);
+ bt_gatt_server_unref(data->server);
+ data->server = NULL;
queue_destroy(data->streams, NULL);
* 1 channel (0x01)
* 2 channels (0x02)
* Codec Specific Capabilities #3: len 0x05 type 0x04
- * Frame Length: 30 (0x001e) - 240 (0x00f0)
+ * Frame Length: 26 (0x001a) - 240 (0x00f0)
* ATT: Read Request (0x0a) len 2
* Handle: 0x0006 Type: Sink Audio Location (0x2bca)
* ATT: Read Response (0x0b) len 4
#define DISC_SNK_LC3 \
DISC_SNK_PAC(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
/* ATT: Read Request (0x0a) len 2
* Handle: 0x0009 Type: Source PAC (0x2bcb)
* 1 channel (0x01)
* 2 channels (0x02)
* Codec Specific Capabilities #3: len 0x05 type 0x04
- * Frame Length: 30 (0x001e) - 240 (0x00f0)
+ * Frame Length: 26 (0x001e) - 240 (0x00f0)
* ATT: Read Request (0x0a) len 2
* Handle: 0x000c Type: Source Audio Location (0x2bcc)
* ATT: Read Response (0x0b) len 4
#define DISC_SRC_LC3 \
DISC_SRC_PAC(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
/* ATT: Read Request (0x0a) len 2
* Handle: 0x000f Type: Available Audio Contexts (0x2bcd)
* ATT: Read Response (0x0b) len 4
- * Value: ff0f0e00
+ * Value: ff0fff0f
* Handle: 0x000f Type: Available Audio Contexts (0x2bcd)
*/
#define DISC_CTX(_caps...) \
DISC_SRC_PAC(_caps), \
IOV_DATA(0x0a, 0x0f, 0x00), \
- IOV_DATA(0x0b, 0xff, 0x0f, 0x0e, 0x00)
+ IOV_DATA(0x0b, 0xff, 0x0f, 0xff, 0x0f)
#define DISC_CTX_LC3 \
DISC_CTX(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
/* ATT: Read Request (0x0a) len 2
* Handle: 0x0012 Type: Supported Audio Contexts (0x2bce)
* ATT: Read Response (0x0b) len 4
- * Value: ff0f0e00
+ * Value: ff0fff0f
* Handle: 0x0012 Type: Supported Audio Contexts (0x2bce)
*/
#define DISC_SUP_CTX(_caps...) \
DISC_CTX(_caps), \
IOV_DATA(0x0a, 0x12, 0x00), \
- IOV_DATA(0x0b, 0xff, 0x0f, 0x0e, 0x00)
+ IOV_DATA(0x0b, 0xff, 0x0f, 0xff, 0x0f)
#define DISC_SUP_CTX_LC3 \
DISC_SUP_CTX(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
/* ATT: Read Request (0x0a) len 2
* Handle: 0x0016 Type: Sink ASE (0x2bc4)
#define DISC_SNK_ASE_LC3 \
DISC_SNK_ASE(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
/* ATT: Read Request (0x0a) len 2
* Handle: 0x001c Type: Source ASE (0x2bc5)
#define DISC_SRC_ASE_LC3 \
DISC_SRC_ASE(0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x01, \
0xff, 0x00, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x05, 0x04, \
- 0x1e, 0x00, 0xf0, 0x00, 0x00)
+ 0x1a, 0x00, 0xf0, 0x00, 0x00)
-static void test_disc(void)
+#define DISC_ASE_LC3 \
+ DISC_SNK_ASE_LC3, \
+ DISC_SRC_ASE_LC3
+
+static void test_ucl_disc(void)
{
/* The IUT discovers the characteristics specified in the PAC
* Characteristic and Location Characteristic columns in Table 4.4.
DISC_SRC_ASE_LC3);
}
+static void bap_attached(struct bt_bap *bap, void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+
+ if (tester_use_debug())
+ tester_debug("bap %p session attached", bap);
+
+ data->bap = bap;
+
+ bt_bap_set_debug(data->bap, print_debug, "bt_bap:", NULL);
+
+ if (data->cfg && data->cfg->state_func)
+ bt_bap_state_register(data->bap, data->cfg->state_func, NULL,
+ data, NULL);
+}
+
+static void test_server(const void *user_data)
+{
+ struct test_data *data = (void *)user_data;
+ struct io *io;
+
+ io = tester_setup_io(data->iov, data->iovcnt);
+ g_assert(io);
+
+ tester_io_set_complete_func(test_complete_cb);
+
+ test_setup_pacs(data);
+
+ data->id = bt_bap_register(bap_attached, NULL, data);
+ g_assert(data->id);
+
+ tester_io_send();
+}
+
+static void test_usr_disc(void)
+{
+ /* BAP/USR/DISC/BV-01-C [Expose Audio Sink Capabilities]
+ * BAP/USR/DISC/BV-02-C [Expose Audio Source Capabilities]
+ *
+ * The specified PAC Characteristic and the Location Characteristic,
+ * if supported, are read on the IUT.
+ */
+ define_test("BAP/USR/DISC/BV-01-C", test_setup_server, test_server,
+ NULL, DISC_SNK_LC3);
+ define_test("BAP/USR/DISC/BV-02-C", test_setup_server, test_server,
+ NULL, DISC_SRC_LC3);
+
+ /* BAP/UCL/DISC/BV-06-C [Discover Available Audio Contexts]
+ *
+ * The IUT successfully reads the value of the Available Audio Contexts
+ * characteristic on the Lower Tester.
+ */
+ define_test("BAP/USR/DISC/BV-06-C", test_setup_server, test_server,
+ NULL, DISC_CTX_LC3);
+
+ /* BAP/USR/DISC/BV-07-C [Expose Supported Audio Contexts]
+ *
+ * The IUT successfully returns the value of its Supported Audio
+ * Contexts characteristic when read by the Lower Tester.
+ */
+ define_test("BAP/USR/DISC/BV-07-C", test_setup_server, test_server,
+ NULL, DISC_SUP_CTX_LC3);
+
+ /* BAP/USR/DISC/BV-03-C [Expose Sink ASE_ID]
+ * BAP/USR/DISC/BV-04-C [Expose Source ASE_ID]
+ * BAP/USR/DISC/BV-05-C [Expose Sink and Source ASE_ID]
+ *
+ * The IUT successfully returns the values of each ASE characteristic
+ * read by the Lower Tester. The value of the ASE_ID field is unique
+ * for each ASE characteristic.
+ */
+ define_test("BAP/USR/DISC/BV-03-C", test_setup_server, test_server,
+ NULL, DISC_SNK_ASE_LC3);
+ define_test("BAP/USR/DISC/BV-04-C", test_setup_server, test_server,
+ NULL, DISC_SRC_ASE_LC3);
+ define_test("BAP/USR/DISC/BV-05-C", test_setup_server, test_server,
+ NULL, DISC_ASE_LC3);
+}
+
+static void test_disc(void)
+{
+ test_ucl_disc();
+ test_usr_disc();
+}
+
/* ATT: Write Command (0x52) len 23
* Handle: 0x0022
* Data: 0101010202_cfg