blob: 0d912eae73e84c3fbc1aab6d37c62b3093db8377 [file] [log] [blame]
/*
* Copyright (c) 2023-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_GMAP)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/byteorder.h>
#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/gmap.h>
#include <zephyr/bluetooth/audio/gmap_lc3_preset.h>
#include <zephyr/sys/byteorder.h>
#include "common.h"
#include "bap_common.h"
/* Zephyr Controller works best while Extended Advertising interval to be a multiple
* of the ISO Interval minus 10 ms (max. advertising random delay). This is
* required to place the AUX_ADV_IND PDUs in a non-overlapping interval with the
* Broadcast ISO radio events.
*/
#define BT_LE_EXT_ADV_CUSTOM \
BT_LE_ADV_PARAM(BT_LE_ADV_OPT_EXT_ADV | \
BT_LE_ADV_OPT_USE_NAME, \
0x0080, 0x0080, NULL)
#define BT_LE_PER_ADV_CUSTOM \
BT_LE_PER_ADV_PARAM(0x0048, \
0x0048, \
BT_LE_PER_ADV_OPT_NONE)
#define UNICAST_SINK_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0)
#define UNICAST_SRC_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0)
#define CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | BT_AUDIO_CONTEXT_TYPE_GAME)
#define LOCATION (BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT)
#define GMAP_BROADCAST_AC_MAX_STREAM 2
#define GMAP_UNICAST_AC_MAX_CONN 2U
#define GMAP_UNICAST_AC_MAX_SNK (2U * GMAP_UNICAST_AC_MAX_CONN)
#define GMAP_UNICAST_AC_MAX_SRC (2U * GMAP_UNICAST_AC_MAX_CONN)
#define GMAP_UNICAST_AC_MAX_PAIR MAX(GMAP_UNICAST_AC_MAX_SNK, GMAP_UNICAST_AC_MAX_SRC)
#define GMAP_UNICAST_AC_MAX_STREAM (GMAP_UNICAST_AC_MAX_SNK + GMAP_UNICAST_AC_MAX_SRC)
#define MAX_ISO_CHAN_COUNT 2U
#define ISO_ENQUEUE_COUNT 2U
#define TOTAL_BUF_NEEDED (ISO_ENQUEUE_COUNT * MAX_ISO_CHAN_COUNT)
BUILD_ASSERT(
CONFIG_BT_ISO_TX_BUF_COUNT >= TOTAL_BUF_NEEDED,
"CONFIG_BT_ISO_TX_BUF_COUNT should be at least ISO_ENQUEUE_COUNT * MAX_ISO_CHAN_COUNT");
NET_BUF_POOL_FIXED_DEFINE(tx_pool, TOTAL_BUF_NEEDED, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
extern enum bst_result_t bst_result;
static const struct named_lc3_preset *snk_named_preset;
static const struct named_lc3_preset *src_named_preset;
static const struct named_lc3_preset *broadcast_named_preset;
struct gmap_unicast_ac_param {
char *name;
size_t conn_cnt;
size_t snk_cnt[GMAP_UNICAST_AC_MAX_CONN];
size_t src_cnt[GMAP_UNICAST_AC_MAX_CONN];
size_t snk_chan_cnt;
const struct named_lc3_preset *snk_named_preset;
const struct named_lc3_preset *src_named_preset;
};
struct gmap_broadcast_ac_param {
char *name;
size_t stream_cnt;
size_t chan_cnt;
const struct named_lc3_preset *named_preset;
};
static struct named_lc3_preset gmap_unicast_snk_presets[] = {
{"32_1_gr", BT_GMAP_LC3_PRESET_32_1_GR(LOCATION, CONTEXT)},
{"32_2_gr", BT_GMAP_LC3_PRESET_32_2_GR(LOCATION, CONTEXT)},
{"48_1_gr", BT_GMAP_LC3_PRESET_48_1_GR(LOCATION, CONTEXT)},
{"48_2_gr", BT_GMAP_LC3_PRESET_48_2_GR(LOCATION, CONTEXT)},
{"48_3_gr", BT_GMAP_LC3_PRESET_48_3_GR(LOCATION, CONTEXT)},
{"48_4_gr", BT_GMAP_LC3_PRESET_48_4_GR(LOCATION, CONTEXT)},
};
static struct named_lc3_preset gmap_unicast_src_presets[] = {
{"16_1_gs", BT_GMAP_LC3_PRESET_16_1_GS(LOCATION, CONTEXT)},
{"16_2_gs", BT_GMAP_LC3_PRESET_16_2_GS(LOCATION, CONTEXT)},
{"32_1_gs", BT_GMAP_LC3_PRESET_32_1_GS(LOCATION, CONTEXT)},
{"32_2_gs", BT_GMAP_LC3_PRESET_32_2_GS(LOCATION, CONTEXT)},
{"48_1_gs", BT_GMAP_LC3_PRESET_48_1_GS(LOCATION, CONTEXT)},
{"48_2_gs", BT_GMAP_LC3_PRESET_48_2_GS(LOCATION, CONTEXT)},
};
static struct named_lc3_preset gmap_broadcast_presets[] = {
{"48_1_g", BT_GMAP_LC3_PRESET_48_1_G(LOCATION, CONTEXT)},
{"48_2_g", BT_GMAP_LC3_PRESET_48_2_G(LOCATION, CONTEXT)},
{"48_3_g", BT_GMAP_LC3_PRESET_48_3_G(LOCATION, CONTEXT)},
{"48_4_g", BT_GMAP_LC3_PRESET_48_4_G(LOCATION, CONTEXT)},
};
struct named_lc3_preset named_preset;
static struct audio_test_stream broadcast_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
static struct unicast_stream unicast_streams[GMAP_UNICAST_AC_MAX_STREAM];
static struct bt_cap_stream *started_unicast_streams[GMAP_UNICAST_AC_MAX_STREAM];
static size_t started_unicast_streams_cnt;
static struct bt_bap_ep
*sink_eps[GMAP_UNICAST_AC_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_bap_ep
*source_eps[GMAP_UNICAST_AC_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT];
static struct bt_conn *connected_conns[GMAP_UNICAST_AC_MAX_CONN];
static size_t connected_conn_cnt;
static K_SEM_DEFINE(sem_stream_started, 0U,
MAX(ARRAY_SIZE(unicast_streams), ARRAY_SIZE(broadcast_streams)));
static K_SEM_DEFINE(sem_stream_stopped, 0U,
MAX(ARRAY_SIZE(unicast_streams), ARRAY_SIZE(broadcast_streams)));
CREATE_FLAG(flag_cas_discovered);
CREATE_FLAG(flag_started);
CREATE_FLAG(flag_updated);
CREATE_FLAG(flag_stopped);
CREATE_FLAG(flag_mtu_exchanged);
CREATE_FLAG(flag_sink_discovered);
CREATE_FLAG(flag_source_discovered);
CREATE_FLAG(flag_gmap_discovered);
const struct named_lc3_preset *gmap_get_named_preset(bool is_unicast, enum bt_audio_dir dir,
const char *preset_arg)
{
if (is_unicast) {
if (dir == BT_AUDIO_DIR_SINK) {
for (size_t i = 0U; i < ARRAY_SIZE(gmap_unicast_snk_presets); i++) {
if (!strcmp(preset_arg, gmap_unicast_snk_presets[i].name)) {
return &gmap_unicast_snk_presets[i];
}
}
} else if (dir == BT_AUDIO_DIR_SOURCE) {
for (size_t i = 0U; i < ARRAY_SIZE(gmap_unicast_src_presets); i++) {
if (!strcmp(preset_arg, gmap_unicast_src_presets[i].name)) {
return &gmap_unicast_src_presets[i];
}
}
}
} else {
for (size_t i = 0U; i < ARRAY_SIZE(gmap_broadcast_presets); i++) {
if (!strcmp(preset_arg, gmap_broadcast_presets[i].name)) {
return &gmap_broadcast_presets[i];
}
}
}
return NULL;
}
static void stream_sent_cb(struct bt_bap_stream *bap_stream)
{
struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(bap_stream);
struct bt_cap_stream *cap_stream = cap_stream_from_audio_test_stream(test_stream);
struct net_buf *buf;
int ret;
if (!test_stream->tx_active) {
return;
}
if ((test_stream->tx_cnt % 100U) == 0U) {
printk("[%zu]: Stream %p sent with seq_num %u\n", test_stream->tx_cnt, cap_stream,
test_stream->seq_num);
}
if (test_stream->tx_sdu_size > CONFIG_BT_ISO_TX_MTU) {
FAIL("Invalid SDU %u for the MTU: %d", test_stream->tx_sdu_size,
CONFIG_BT_ISO_TX_MTU);
return;
}
buf = net_buf_alloc(&tx_pool, K_FOREVER);
if (buf == NULL) {
printk("Could not allocate buffer when sending on %p\n", bap_stream);
return;
}
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, mock_iso_data, test_stream->tx_sdu_size);
ret = bt_cap_stream_send(cap_stream, buf, test_stream->seq_num++, BT_ISO_TIMESTAMP_NONE);
if (ret < 0) {
/* This will end broadcasting on this stream. */
net_buf_unref(buf);
/* Only fail if tx is active (may fail if we are disabling the stream) */
if (test_stream->tx_active) {
FAIL("Unable to broadcast data on %p: %d\n", cap_stream, ret);
}
return;
}
test_stream->tx_cnt++;
}
static void stream_configured_cb(struct bt_bap_stream *stream,
const struct bt_audio_codec_qos_pref *pref)
{
printk("Configured stream %p\n", stream);
/* TODO: The preference should be used/taken into account when
* setting the QoS
*/
}
static void stream_qos_set_cb(struct bt_bap_stream *stream)
{
printk("QoS set stream %p\n", stream);
}
static void stream_enabled_cb(struct bt_bap_stream *stream)
{
printk("Enabled stream %p\n", stream);
}
static void stream_started_cb(struct bt_bap_stream *stream)
{
printk("Started stream %p\n", stream);
k_sem_give(&sem_stream_started);
}
static void stream_metadata_updated_cb(struct bt_bap_stream *stream)
{
printk("Metadata updated stream %p\n", stream);
}
static void stream_disabled_cb(struct bt_bap_stream *stream)
{
printk("Disabled stream %p\n", stream);
}
static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
k_sem_give(&sem_stream_stopped);
}
static void stream_released_cb(struct bt_bap_stream *stream)
{
printk("Released stream %p\n", stream);
}
static struct bt_bap_stream_ops stream_ops = {
.configured = stream_configured_cb,
.qos_set = stream_qos_set_cb,
.enabled = stream_enabled_cb,
.started = stream_started_cb,
.metadata_updated = stream_metadata_updated_cb,
.disabled = stream_disabled_cb,
.stopped = stream_stopped_cb,
.released = stream_released_cb,
.sent = stream_sent_cb,
};
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
{
if (err != 0) {
FAIL("Failed to discover CAS: %d\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
if (csis_inst == NULL) {
FAIL("Failed to discover CAS CSIS\n");
return;
}
printk("Found CAS with CSIS %p\n", csis_inst);
} else {
printk("Found CAS\n");
}
SET_FLAG(flag_cas_discovered);
}
static void unicast_start_complete_cb(int err, struct bt_conn *conn)
{
if (err != 0) {
FAIL("Failed to start (failing conn %p): %d\n", conn, err);
return;
}
SET_FLAG(flag_started);
}
static void unicast_update_complete_cb(int err, struct bt_conn *conn)
{
if (err != 0) {
FAIL("Failed to update (failing conn %p): %d\n", conn, err);
return;
}
SET_FLAG(flag_updated);
}
static void unicast_stop_complete_cb(int err, struct bt_conn *conn)
{
if (err != 0) {
FAIL("Failed to stop (failing conn %p): %d\n", conn, err);
return;
}
SET_FLAG(flag_stopped);
}
static struct bt_cap_initiator_cb cap_cb = {
.unicast_discovery_complete = cap_discovery_complete_cb,
.unicast_start_complete = unicast_start_complete_cb,
.unicast_update_complete = unicast_update_complete_cb,
.unicast_stop_complete = unicast_stop_complete_cb,
};
static void add_remote_sink_ep(struct bt_conn *conn, struct bt_bap_ep *ep)
{
for (size_t i = 0U; i < ARRAY_SIZE(sink_eps[bt_conn_index(conn)]); i++) {
if (sink_eps[bt_conn_index(conn)][i] == NULL) {
printk("Conn %p: Sink #%zu: ep %p\n", conn, i, ep);
sink_eps[bt_conn_index(conn)][i] = ep;
break;
}
}
}
static void add_remote_source_ep(struct bt_conn *conn, struct bt_bap_ep *ep)
{
for (size_t i = 0U; i < ARRAY_SIZE(source_eps[bt_conn_index(conn)]); i++) {
if (source_eps[bt_conn_index(conn)][i] == NULL) {
printk("Conn %p: Source #%zu: ep %p\n", conn, i, ep);
source_eps[bt_conn_index(conn)][i] = ep;
break;
}
}
}
static void bap_pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
const struct bt_audio_codec_cap *codec_cap)
{
printk("conn %p codec_cap %p dir 0x%02x\n", conn, codec_cap, dir);
print_codec_cap(codec_cap);
}
static void bap_endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep)
{
if (dir == BT_AUDIO_DIR_SINK) {
add_remote_sink_ep(conn, ep);
} else if (dir == BT_AUDIO_DIR_SOURCE) {
add_remote_source_ep(conn, ep);
}
}
static void bap_discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
{
if (err != 0) {
FAIL("Discovery failed for dir %u: %d\n", dir, err);
return;
}
if (dir == BT_AUDIO_DIR_SINK) {
printk("Sink discover complete\n");
SET_FLAG(flag_sink_discovered);
} else if (dir == BT_AUDIO_DIR_SOURCE) {
printk("Source discover complete\n");
SET_FLAG(flag_source_discovered);
}
}
static struct bt_bap_unicast_client_cb unicast_client_cbs = {
.pac_record = bap_pac_record_cb,
.endpoint = bap_endpoint_cb,
.discover = bap_discover_cb,
};
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
printk("MTU exchanged\n");
SET_FLAG(flag_mtu_exchanged);
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated,
};
static void gmap_discover_cb(struct bt_conn *conn, int err, enum bt_gmap_role role,
struct bt_gmap_feat features)
{
enum bt_gmap_ugt_feat ugt_feat;
if (err != 0) {
FAIL("gmap discovery (err %d)\n", err);
return;
}
printk("GMAP discovered for conn %p:\n\trole 0x%02x\n\tugg_feat 0x%02x\n\tugt_feat "
"0x%02x\n\tbgs_feat 0x%02x\n\tbgr_feat 0x%02x\n",
conn, role, features.ugg_feat, features.ugt_feat, features.bgs_feat,
features.bgr_feat);
if ((role & BT_GMAP_ROLE_UGT) == 0) {
FAIL("Remote GMAP device is not a UGT\n");
return;
}
ugt_feat = features.ugt_feat;
if ((ugt_feat & BT_GMAP_UGT_FEAT_SOURCE) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_80KBPS_SOURCE) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_SINK) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_64KBPS_SINK) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_MULTIPLEX) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_MULTISINK) == 0 ||
(ugt_feat & BT_GMAP_UGT_FEAT_MULTISOURCE) == 0) {
FAIL("Remote GMAP device does not have expected UGT features: %d\n", ugt_feat);
return;
}
SET_FLAG(flag_gmap_discovered);
}
static const struct bt_gmap_cb gmap_cb = {
.discover = gmap_discover_cb,
};
static void init(void)
{
const struct bt_gmap_feat features = {
.ugg_feat = (BT_GMAP_UGG_FEAT_MULTIPLEX | BT_GMAP_UGG_FEAT_96KBPS_SOURCE |
BT_GMAP_UGG_FEAT_MULTISINK),
};
const enum bt_gmap_role role = BT_GMAP_ROLE_UGG;
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
bt_gatt_cb_register(&gatt_callbacks);
err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
if (err != 0) {
FAIL("Failed to register BAP callbacks (err %d)\n", err);
return;
}
err = bt_cap_initiator_register_cb(&cap_cb);
if (err != 0) {
FAIL("Failed to register CAP callbacks (err %d)\n", err);
return;
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_streams); i++) {
bt_cap_stream_ops_register(&unicast_streams[i].stream, &stream_ops);
}
for (size_t i = 0; i < ARRAY_SIZE(broadcast_streams); i++) {
bt_cap_stream_ops_register(cap_stream_from_audio_test_stream(&broadcast_streams[i]),
&stream_ops);
}
err = bt_gmap_register(role, features);
if (err != 0) {
FAIL("Failed to register GMAS (err %d)\n", err);
return;
}
err = bt_gmap_cb_register(&gmap_cb);
if (err != 0) {
FAIL("Failed to register callbacks (err %d)\n", err);
return;
}
}
static void gmap_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
struct bt_conn *conn;
int err;
/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
return;
}
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr);
if (conn != NULL) {
/* Already connected to this device */
bt_conn_unref(conn);
return;
}
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
/* connect only to devices in close proximity */
if (rssi < -70) {
FAIL("RSSI too low");
return;
}
printk("Stopping scan\n");
if (bt_le_scan_stop()) {
FAIL("Could not stop scan");
return;
}
err = bt_conn_le_create(
addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM(BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MIN, 0, 400),
&connected_conns[connected_conn_cnt]);
if (err) {
FAIL("Could not connect to peer: %d", err);
}
}
static void scan_and_connect(void)
{
int err;
UNSET_FLAG(flag_connected);
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, gmap_device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
connected_conn_cnt++;
}
static void discover_sink(struct bt_conn *conn)
{
int err;
UNSET_FLAG(flag_sink_discovered);
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK);
if (err != 0) {
printk("Failed to discover sink: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_sink_discovered);
}
static void discover_source(struct bt_conn *conn)
{
int err;
UNSET_FLAG(flag_source_discovered);
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE);
if (err != 0) {
printk("Failed to discover source: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_source_discovered);
}
static void discover_gmas(struct bt_conn *conn)
{
int err;
UNSET_FLAG(flag_gmap_discovered);
err = bt_gmap_discover(conn);
if (err != 0) {
printk("Failed to discover GMAS: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_gmap_discovered);
}
static void discover_cas(struct bt_conn *conn)
{
int err;
UNSET_FLAG(flag_cas_discovered);
err = bt_cap_initiator_unicast_discover(conn);
if (err != 0) {
printk("Failed to discover CAS: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_cas_discovered);
}
static int gmap_unicast_ac_create_unicast_group(const struct gmap_unicast_ac_param *param,
struct unicast_stream *snk_uni_streams[],
size_t snk_cnt,
struct unicast_stream *src_uni_streams[],
size_t src_cnt,
struct bt_bap_unicast_group **unicast_group)
{
struct bt_bap_unicast_group_stream_param
snk_group_stream_params[GMAP_UNICAST_AC_MAX_SNK] = {0};
struct bt_bap_unicast_group_stream_param
src_group_stream_params[GMAP_UNICAST_AC_MAX_SRC] = {0};
struct bt_bap_unicast_group_stream_pair_param pair_params[GMAP_UNICAST_AC_MAX_PAIR] = {0};
struct bt_bap_unicast_group_param group_param = {0};
size_t snk_stream_cnt = 0U;
size_t src_stream_cnt = 0U;
size_t pair_cnt = 0U;
/* Create Group
*
* First setup the individual stream parameters and then match them in pairs by connection
* and direction
*/
for (size_t i = 0U; i < snk_cnt; i++) {
snk_group_stream_params[i].qos = &snk_uni_streams[i]->qos;
snk_group_stream_params[i].stream = &snk_uni_streams[i]->stream.bap_stream;
}
for (size_t i = 0U; i < src_cnt; i++) {
src_group_stream_params[i].qos = &src_uni_streams[i]->qos;
src_group_stream_params[i].stream = &src_uni_streams[i]->stream.bap_stream;
}
for (size_t i = 0U; i < param->conn_cnt; i++) {
for (size_t j = 0; j < MAX(param->snk_cnt[i], param->src_cnt[i]); j++) {
if (param->snk_cnt[i] > j) {
pair_params[pair_cnt].tx_param =
&snk_group_stream_params[snk_stream_cnt++];
} else {
pair_params[pair_cnt].tx_param = NULL;
}
if (param->src_cnt[i] > j) {
pair_params[pair_cnt].rx_param =
&src_group_stream_params[src_stream_cnt++];
} else {
pair_params[pair_cnt].rx_param = NULL;
}
pair_cnt++;
}
}
group_param.packing = BT_ISO_PACKING_SEQUENTIAL;
group_param.params = pair_params;
group_param.params_count = pair_cnt;
return bt_bap_unicast_group_create(&group_param, unicast_group);
}
static int gmap_ac_cap_unicast_start(const struct gmap_unicast_ac_param *param,
struct unicast_stream *snk_uni_streams[], size_t snk_cnt,
struct unicast_stream *src_uni_streams[], size_t src_cnt,
struct bt_bap_unicast_group *unicast_group)
{
struct bt_cap_unicast_audio_start_stream_param stream_params[GMAP_UNICAST_AC_MAX_STREAM] = {
0};
struct bt_audio_codec_cfg *snk_codec_cfgs[GMAP_UNICAST_AC_MAX_SNK] = {0};
struct bt_audio_codec_cfg *src_codec_cfgs[GMAP_UNICAST_AC_MAX_SRC] = {0};
struct bt_cap_stream *snk_cap_streams[GMAP_UNICAST_AC_MAX_SNK] = {0};
struct bt_cap_stream *src_cap_streams[GMAP_UNICAST_AC_MAX_SRC] = {0};
struct bt_cap_unicast_audio_start_param start_param = {0};
struct bt_bap_ep *snk_eps[GMAP_UNICAST_AC_MAX_SNK] = {0};
struct bt_bap_ep *src_eps[GMAP_UNICAST_AC_MAX_SRC] = {0};
size_t snk_stream_cnt = 0U;
size_t src_stream_cnt = 0U;
size_t stream_cnt = 0U;
size_t snk_ep_cnt = 0U;
size_t src_ep_cnt = 0U;
int err;
for (size_t i = 0U; i < param->conn_cnt; i++) {
#if UNICAST_SINK_SUPPORTED
for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
snk_eps[snk_ep_cnt] = sink_eps[bt_conn_index(connected_conns[i])][j];
if (snk_eps[snk_ep_cnt] == NULL) {
FAIL("No sink[%zu][%zu] endpoint available\n", i, j);
return -ENODEV;
}
snk_ep_cnt++;
}
#endif /* UNICAST_SINK_SUPPORTED */
#if UNICAST_SRC_SUPPORTED
for (size_t j = 0U; j < param->src_cnt[i]; j++) {
src_eps[src_ep_cnt] = source_eps[bt_conn_index(connected_conns[i])][j];
if (src_eps[src_ep_cnt] == NULL) {
FAIL("No source[%zu][%zu] endpoint available\n", i, j);
return -ENODEV;
}
src_ep_cnt++;
}
#endif /* UNICAST_SRC_SUPPORTED > 0 */
}
if (snk_ep_cnt != snk_cnt) {
FAIL("Sink endpoint and stream count mismatch: %zu != %zu\n", snk_ep_cnt, snk_cnt);
return -EINVAL;
}
if (src_ep_cnt != src_cnt) {
FAIL("Source endpoint and stream count mismatch: %zu != %zu\n", src_ep_cnt,
src_cnt);
return -EINVAL;
}
/* Setup arrays of parameters based on the preset for easier access. This also copies the
* preset so that we can modify them (e.g. update the metadata)
*/
for (size_t i = 0U; i < snk_cnt; i++) {
snk_cap_streams[i] = &snk_uni_streams[i]->stream;
snk_codec_cfgs[i] = &snk_uni_streams[i]->codec_cfg;
}
for (size_t i = 0U; i < src_cnt; i++) {
src_cap_streams[i] = &src_uni_streams[i]->stream;
src_codec_cfgs[i] = &src_uni_streams[i]->codec_cfg;
}
/* CAP Start */
for (size_t i = 0U; i < param->conn_cnt; i++) {
for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
struct bt_cap_unicast_audio_start_stream_param *stream_param =
&stream_params[stream_cnt];
stream_param->member.member = connected_conns[i];
stream_param->codec_cfg = snk_codec_cfgs[snk_stream_cnt];
stream_param->ep = snk_eps[snk_stream_cnt];
stream_param->stream = snk_cap_streams[snk_stream_cnt];
snk_stream_cnt++;
stream_cnt++;
/* If we have more than 1 connection or stream in one direction, we set the
* location bit accordingly
*/
if (param->conn_cnt > 1U || param->snk_cnt[i] > 1U) {
const int err = bt_audio_codec_cfg_set_chan_allocation(
stream_param->codec_cfg, (enum bt_audio_location)BIT(i));
if (err < 0) {
FAIL("Failed to set channel allocation: %d\n", err);
return err;
}
}
}
for (size_t j = 0U; j < param->src_cnt[i]; j++) {
struct bt_cap_unicast_audio_start_stream_param *stream_param =
&stream_params[stream_cnt];
stream_param->member.member = connected_conns[i];
stream_param->codec_cfg = src_codec_cfgs[src_stream_cnt];
stream_param->ep = src_eps[src_stream_cnt];
stream_param->stream = src_cap_streams[src_stream_cnt];
src_stream_cnt++;
stream_cnt++;
/* If we have more than 1 connection or stream in one direction, we set the
* location bit accordingly
*/
if (param->conn_cnt > 1U || param->src_cnt[i] > 1U) {
const int err = bt_audio_codec_cfg_set_chan_allocation(
stream_param->codec_cfg, (enum bt_audio_location)BIT(i));
if (err < 0) {
FAIL("Failed to set channel allocation: %d\n", err);
return err;
}
}
}
}
start_param.stream_params = stream_params;
start_param.count = stream_cnt;
start_param.type = BT_CAP_SET_TYPE_AD_HOC;
err = bt_cap_initiator_unicast_audio_start(&start_param);
if (err == 0) {
for (size_t i = 0U; i < start_param.count; i++) {
started_unicast_streams[i] = start_param.stream_params[i].stream;
}
started_unicast_streams_cnt = start_param.count;
}
return err;
}
static int gmap_ac_unicast(const struct gmap_unicast_ac_param *param,
struct bt_bap_unicast_group **unicast_group)
{
/* Allocate params large enough for any params, but only use what is required */
struct unicast_stream *snk_uni_streams[GMAP_UNICAST_AC_MAX_SNK];
struct unicast_stream *src_uni_streams[GMAP_UNICAST_AC_MAX_SRC];
size_t snk_cnt = 0;
size_t src_cnt = 0;
int err;
if (param->conn_cnt > GMAP_UNICAST_AC_MAX_CONN) {
FAIL("Invalid conn_cnt: %zu\n", param->conn_cnt);
return -EINVAL;
}
for (size_t i = 0; i < param->conn_cnt; i++) {
/* Verify conn values */
if (param->snk_cnt[i] > GMAP_UNICAST_AC_MAX_SNK) {
FAIL("Invalid conn_snk_cnt[%zu]: %zu\n", i, param->snk_cnt[i]);
return -EINVAL;
}
if (param->src_cnt[i] > GMAP_UNICAST_AC_MAX_SRC) {
FAIL("Invalid conn_src_cnt[%zu]: %zu\n", i, param->src_cnt[i]);
return -EINVAL;
}
}
/* Get total count of sink and source streams to setup */
for (size_t i = 0U; i < param->conn_cnt; i++) {
for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
snk_cnt++;
}
for (size_t j = 0U; j < param->src_cnt[i]; j++) {
src_cnt++;
}
}
/* Setup arrays of parameters based on the preset for easier access. This also copies the
* preset so that we can modify them (e.g. update the metadata)
*/
for (size_t i = 0U; i < snk_cnt; i++) {
snk_uni_streams[i] = &unicast_streams[i];
copy_unicast_stream_preset(snk_uni_streams[i], param->snk_named_preset);
/* Some audio configuration requires multiple sink channels,
* so multiply the SDU based on the channel count
*/
snk_uni_streams[i]->qos.sdu *= param->snk_chan_cnt;
}
for (size_t i = 0U; i < src_cnt; i++) {
src_uni_streams[i] = &unicast_streams[i + snk_cnt];
copy_unicast_stream_preset(src_uni_streams[i], param->src_named_preset);
}
err = gmap_unicast_ac_create_unicast_group(param, snk_uni_streams, snk_cnt, src_uni_streams,
src_cnt, unicast_group);
if (err != 0) {
FAIL("Failed to create group: %d\n", err);
return err;
}
UNSET_FLAG(flag_started);
printk("Starting %zu streams for %s\n", snk_cnt + src_cnt, param->name);
err = gmap_ac_cap_unicast_start(param, snk_uni_streams, snk_cnt, src_uni_streams, src_cnt,
*unicast_group);
if (err != 0) {
FAIL("Failed to start unicast audio: %d\n\n", err);
return err;
}
WAIT_FOR_FLAG(flag_started);
return 0;
}
static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
{
struct bt_cap_unicast_audio_stop_param param;
int err;
UNSET_FLAG(flag_stopped);
param.type = BT_CAP_SET_TYPE_AD_HOC;
param.count = started_unicast_streams_cnt;
param.streams = started_unicast_streams;
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
FAIL("Failed to start unicast audio: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_stopped);
started_unicast_streams_cnt = 0U;
memset(started_unicast_streams, 0, sizeof(started_unicast_streams));
}
static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group)
{
int err;
err = bt_bap_unicast_group_delete(unicast_group);
if (err != 0) {
FAIL("Failed to create group: %d\n", err);
return;
}
}
static void test_gmap_ugg_unicast_ac(const struct gmap_unicast_ac_param *param)
{
struct bt_bap_unicast_group *unicast_group;
printk("Running test for %s with Sink Preset %s and Source Preset %s\n", param->name,
param->snk_named_preset != NULL ? param->snk_named_preset->name : "None",
param->src_named_preset != NULL ? param->src_named_preset->name : "None");
if (param->conn_cnt > GMAP_UNICAST_AC_MAX_CONN) {
FAIL("Invalid conn_cnt: %zu\n", param->conn_cnt);
return;
}
init();
for (size_t i = 0U; i < param->conn_cnt; i++) {
UNSET_FLAG(flag_mtu_exchanged);
scan_and_connect();
WAIT_FOR_FLAG(flag_mtu_exchanged);
printk("Connected %zu/%zu\n", i + 1, param->conn_cnt);
}
if (connected_conn_cnt < param->conn_cnt) {
FAIL("Only %zu/%u connected devices, please connect additional devices for this "
"audio configuration\n",
connected_conn_cnt, param->conn_cnt);
return;
}
for (size_t i = 0U; i < param->conn_cnt; i++) {
discover_cas(connected_conns[i]);
if (param->snk_cnt[i] > 0U) {
discover_sink(connected_conns[i]);
}
if (param->src_cnt[i] > 0U) {
discover_source(connected_conns[i]);
}
discover_gmas(connected_conns[i]);
discover_gmas(connected_conns[i]); /* test that we can discover twice */
}
gmap_ac_unicast(param, &unicast_group);
unicast_audio_stop(unicast_group);
unicast_group_delete(unicast_group);
unicast_group = NULL;
for (size_t i = 0U; i < param->conn_cnt; i++) {
const int err =
bt_conn_disconnect(connected_conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (err != 0) {
FAIL("Failed to disconnect conn[%zu]: %d\n", i, err);
}
bt_conn_unref(connected_conns[i]);
connected_conns[i] = NULL;
}
PASS("GMAP UGG passed for %s with Sink Preset %s and Source Preset %s\n", param->name,
param->snk_named_preset != NULL ? param->snk_named_preset->name : "None",
param->src_named_preset != NULL ? param->src_named_preset->name : "None");
}
static void setup_extended_adv(struct bt_le_ext_adv **adv)
{
int err;
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CUSTOM, NULL, adv);
if (err != 0) {
FAIL("Unable to create extended advertising set: %d\n", err);
return;
}
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_CUSTOM);
if (err) {
FAIL("Failed to set periodic advertising parameters: %d\n", err);
return;
}
}
static void setup_extended_adv_data(struct bt_cap_broadcast_source *source,
struct bt_le_ext_adv *adv)
{
/* Broadcast Audio Streaming Endpoint advertising data */
NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
struct bt_data ext_ad;
struct bt_data per_ad;
uint32_t broadcast_id;
int err;
err = bt_cap_initiator_broadcast_get_id(source, &broadcast_id);
if (err != 0) {
FAIL("Unable to get broadcast ID: %d\n", err);
return;
}
/* Setup extended advertising data */
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
net_buf_simple_add_le24(&ad_buf, broadcast_id);
ext_ad.type = BT_DATA_SVC_DATA16;
ext_ad.data_len = ad_buf.len;
ext_ad.data = ad_buf.data;
err = bt_le_ext_adv_set_data(adv, &ext_ad, 1, NULL, 0);
if (err != 0) {
FAIL("Failed to set extended advertising data: %d\n", err);
return;
}
/* Setup periodic advertising data */
err = bt_cap_initiator_broadcast_get_base(source, &base_buf);
if (err != 0) {
FAIL("Failed to get encoded BASE: %d\n", err);
return;
}
per_ad.type = BT_DATA_SVC_DATA16;
per_ad.data_len = base_buf.len;
per_ad.data = base_buf.data;
err = bt_le_per_adv_set_data(adv, &per_ad, 1);
if (err != 0) {
FAIL("Failed to set periodic advertising data: %d\n", err);
return;
}
}
static void start_extended_adv(struct bt_le_ext_adv *adv)
{
int err;
/* Start extended advertising */
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
FAIL("Failed to start extended advertising: %d\n", err);
return;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(adv);
if (err) {
FAIL("Failed to enable periodic advertising: %d\n", err);
return;
}
}
static void stop_and_delete_extended_adv(struct bt_le_ext_adv *adv)
{
int err;
/* Stop extended advertising */
err = bt_le_per_adv_stop(adv);
if (err) {
FAIL("Failed to stop periodic advertising: %d\n", err);
return;
}
err = bt_le_ext_adv_stop(adv);
if (err) {
FAIL("Failed to stop extended advertising: %d\n", err);
return;
}
err = bt_le_ext_adv_delete(adv);
if (err) {
FAIL("Failed to delete extended advertising: %d\n", err);
return;
}
}
static void broadcast_audio_start(struct bt_cap_broadcast_source *broadcast_source,
struct bt_le_ext_adv *adv)
{
int err;
err = bt_cap_initiator_broadcast_audio_start(broadcast_source, adv);
if (err != 0) {
FAIL("Unable to start broadcast source: %d\n", err);
return;
}
printk("Broadcast source created\n");
}
static void broadcast_audio_stop(struct bt_cap_broadcast_source *broadcast_source,
size_t stream_count)
{
int err;
printk("Stopping broadcast source\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_streams); i++) {
broadcast_streams[i].tx_active = false;
}
err = bt_cap_initiator_broadcast_audio_stop(broadcast_source);
if (err != 0) {
FAIL("Failed to stop broadcast source: %d\n", err);
return;
}
/* Wait for all to be stopped */
printk("Waiting for broadcast_streams to be stopped\n");
for (size_t i = 0U; i < stream_count; i++) {
k_sem_take(&sem_stream_stopped, K_FOREVER);
}
printk("Broadcast source stopped\n");
}
static void broadcast_audio_delete(struct bt_cap_broadcast_source *broadcast_source)
{
int err;
printk("Deleting broadcast source\n");
err = bt_cap_initiator_broadcast_audio_delete(broadcast_source);
if (err != 0) {
FAIL("Failed to stop broadcast source: %d\n", err);
return;
}
printk("Broadcast source deleted\n");
}
static int test_gmap_ugg_broadcast_ac(const struct gmap_broadcast_ac_param *param)
{
uint8_t stereo_data[] = {
BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_AUDIO_LOCATION_FRONT_RIGHT | BT_AUDIO_LOCATION_FRONT_LEFT)};
uint8_t right_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_AUDIO_LOCATION_FRONT_RIGHT)};
uint8_t left_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_AUDIO_LOCATION_FRONT_LEFT)};
struct bt_cap_initiator_broadcast_subgroup_param subgroup_param = {0};
struct bt_cap_initiator_broadcast_create_param create_param = {0};
struct bt_cap_initiator_broadcast_stream_param
stream_params[GMAP_BROADCAST_AC_MAX_STREAM] = {0};
struct bt_cap_broadcast_source *broadcast_source;
struct bt_audio_codec_cfg codec_cfg;
struct bt_audio_codec_qos qos;
struct bt_le_ext_adv *adv;
int err;
for (size_t i = 0U; i < param->stream_cnt; i++) {
stream_params[i].stream = cap_stream_from_audio_test_stream(&broadcast_streams[i]);
if (param->stream_cnt == 1U) {
stream_params[i].data_len = ARRAY_SIZE(stereo_data);
stream_params[i].data = stereo_data;
} else if (i == 0U) {
stream_params[i].data_len = ARRAY_SIZE(left_data);
stream_params[i].data = left_data;
} else if (i == 1U) {
stream_params[i].data_len = ARRAY_SIZE(right_data);
stream_params[i].data = right_data;
}
}
memcpy(&codec_cfg, &broadcast_named_preset->preset.codec_cfg, sizeof(codec_cfg));
memcpy(&qos, &broadcast_named_preset->preset.qos, sizeof(qos));
qos.sdu *= param->chan_cnt;
subgroup_param.stream_count = param->stream_cnt;
subgroup_param.stream_params = stream_params;
subgroup_param.codec_cfg = &codec_cfg;
create_param.subgroup_count = 1U;
create_param.subgroup_params = &subgroup_param;
create_param.qos = &qos;
init();
setup_extended_adv(&adv);
err = bt_cap_initiator_broadcast_audio_create(&create_param, &broadcast_source);
if (err != 0) {
FAIL("Failed to create broadcast source: %d\n", err);
return -ENOEXEC;
}
for (size_t i = 0U; i < param->stream_cnt; i++) {
struct audio_test_stream *test_stream = &broadcast_streams[i];
test_stream->tx_sdu_size = create_param.qos->sdu;
}
broadcast_audio_start(broadcast_source, adv);
setup_extended_adv_data(broadcast_source, adv);
start_extended_adv(adv);
/* Wait for all to be started */
printk("Waiting for broadcast_streams to be started\n");
for (size_t i = 0U; i < param->stream_cnt; i++) {
k_sem_take(&sem_stream_started, K_FOREVER);
}
/* Initialize sending */
printk("Starting sending\n");
for (size_t i = 0U; i < param->stream_cnt; i++) {
struct audio_test_stream *test_stream = &broadcast_streams[i];
test_stream->tx_active = true;
for (unsigned int j = 0U; j < ISO_ENQUEUE_COUNT; j++) {
stream_sent_cb(bap_stream_from_audio_test_stream(test_stream));
}
}
/* Wait for other devices to have received what they wanted */
backchannel_sync_wait_any();
broadcast_audio_stop(broadcast_source, param->stream_cnt);
broadcast_audio_delete(broadcast_source);
broadcast_source = NULL;
stop_and_delete_extended_adv(adv);
adv = NULL;
PASS("CAP initiator broadcast passed\n");
return 0;
}
static void test_gmap_ac_1(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_1",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {0U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_2(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_2",
.conn_cnt = 1U,
.snk_cnt = {0U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.snk_named_preset = NULL,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_3(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_3",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_4(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_4",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {0U},
.snk_chan_cnt = 2U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_5(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_5",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {1U},
.snk_chan_cnt = 2U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_6_i(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_6_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {0U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_6_ii(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_6_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {0U, 0U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_7_ii(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_7_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 0U},
.src_cnt = {0U, 1U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_8_i(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_8_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_8_ii(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_8_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {1U, 0U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_11_i(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_11_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {2U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_11_ii(void)
{
const struct gmap_unicast_ac_param param = {
.name = "ac_11_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {1U, 1U},
.snk_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_gmap_ugg_unicast_ac(&param);
}
static void test_gmap_ac_12(void)
{
const struct gmap_broadcast_ac_param param = {
.name = "ac_12",
.stream_cnt = 1U,
.chan_cnt = 1U,
.named_preset = broadcast_named_preset,
};
test_gmap_ugg_broadcast_ac(&param);
}
#if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT >= GMAP_BROADCAST_AC_MAX_STREAM
static void test_gmap_ac_13(void)
{
const struct gmap_broadcast_ac_param param = {
.name = "ac_13",
.stream_cnt = 2U,
.chan_cnt = 1U,
.named_preset = broadcast_named_preset,
};
test_gmap_ugg_broadcast_ac(&param);
}
#endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT >= GMAP_BROADCAST_AC_MAX_STREAM */
static void test_gmap_ac_14(void)
{
const struct gmap_broadcast_ac_param param = {
.name = "ac_14",
.stream_cnt = 1U,
.chan_cnt = 2U,
.named_preset = broadcast_named_preset,
};
test_gmap_ugg_broadcast_ac(&param);
}
static void test_args(int argc, char *argv[])
{
for (size_t argn = 0; argn < argc; argn++) {
const char *arg = argv[argn];
if (strcmp(arg, "sink_preset") == 0) {
const char *preset_arg = argv[++argn];
snk_named_preset =
gmap_get_named_preset(true, BT_AUDIO_DIR_SINK, preset_arg);
if (snk_named_preset == NULL) {
FAIL("Failed to get sink preset from %s\n", preset_arg);
}
} else if (strcmp(arg, "source_preset") == 0) {
const char *preset_arg = argv[++argn];
src_named_preset =
gmap_get_named_preset(true, BT_AUDIO_DIR_SOURCE, preset_arg);
if (src_named_preset == NULL) {
FAIL("Failed to get source preset from %s\n", preset_arg);
}
} else if (strcmp(arg, "broadcast_preset") == 0) {
const char *preset_arg = argv[++argn];
broadcast_named_preset = gmap_get_named_preset(
false, BT_AUDIO_DIR_SINK /* unused */, preset_arg);
if (broadcast_named_preset == NULL) {
FAIL("Failed to get broadcast preset from %s\n", preset_arg);
}
} else {
FAIL("Invalid arg: %s\n", arg);
}
}
}
static const struct bst_test_instance test_gmap_ugg[] = {
{
.test_id = "gmap_ugg_ac_1",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_1,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_2",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_2,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_3",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_3,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_4",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_4,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_5",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_5,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_6_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_6_i,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_6_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_6_ii,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_7_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_7_ii,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_8_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_8_i,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_8_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_8_ii,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_11_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_11_i,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_11_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_11_ii,
.test_args_f = test_args,
},
{
.test_id = "gmap_ugg_ac_12",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_12,
.test_args_f = test_args,
},
#if CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT >= GMAP_BROADCAST_AC_MAX_STREAM
{
.test_id = "gmap_ugg_ac_13",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_13,
.test_args_f = test_args,
},
#endif /* CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT >= GMAP_BROADCAST_AC_MAX_STREAM */
{
.test_id = "gmap_ugg_ac_14",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_gmap_ac_14,
.test_args_f = test_args,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_gmap_ugg_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_gmap_ugg);
}
#else /* !(CONFIG_BT_GMAP) */
struct bst_test_list *test_gmap_ugg_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_GMAP */