blob: 72ef686b1db134d3309b45704cbeab5e4030b366 [file] [log] [blame]
/*
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_CAP_INITIATOR_UNICAST)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/byteorder.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/sys/byteorder.h>
#include "common.h"
#include "bap_common.h"
#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 CAP_AC_MAX_CONN 2U
#define CAP_AC_MAX_SNK (2U * CAP_AC_MAX_CONN)
#define CAP_AC_MAX_SRC (2U * CAP_AC_MAX_CONN)
#define CAP_AC_MAX_PAIR MAX(CAP_AC_MAX_SNK, CAP_AC_MAX_SRC)
#define CAP_AC_MAX_STREAM (CAP_AC_MAX_SNK + CAP_AC_MAX_SRC)
#define CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED)
#define LOCATION (BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT)
struct cap_initiator_ac_param {
char *name;
size_t conn_cnt;
size_t snk_cnt[CAP_AC_MAX_CONN];
size_t src_cnt[CAP_AC_MAX_CONN];
size_t snk_chan_cnt;
size_t src_chan_cnt;
const struct named_lc3_preset *snk_named_preset;
const struct named_lc3_preset *src_named_preset;
};
extern enum bst_result_t bst_result;
static struct bt_bap_lc3_preset unicast_preset_16_2_1 = BT_BAP_LC3_UNICAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
static struct bt_cap_stream unicast_client_sink_streams[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_cap_stream
unicast_client_source_streams[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT];
static struct bt_bap_ep
*unicast_sink_eps[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_bap_ep
*unicast_source_eps[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct unicast_stream unicast_streams[CAP_AC_MAX_STREAM];
static struct bt_conn *connected_conns[CAP_AC_MAX_CONN];
static size_t connected_conn_cnt;
static const struct named_lc3_preset *snk_named_preset;
static const struct named_lc3_preset *src_named_preset;
static struct bt_cap_stream *non_idle_streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
static size_t non_idle_streams_cnt;
CREATE_FLAG(flag_discovered);
CREATE_FLAG(flag_codec_found);
CREATE_FLAG(flag_endpoint_found);
CREATE_FLAG(flag_started);
CREATE_FLAG(flag_start_timeout);
CREATE_FLAG(flag_updated);
CREATE_FLAG(flag_stopped);
CREATE_FLAG(flag_mtu_exchanged);
CREATE_FLAG(flag_sink_discovered);
CREATE_FLAG(flag_source_discovered);
static const struct named_lc3_preset lc3_unicast_presets[] = {
{"8_1_1", BT_BAP_LC3_UNICAST_PRESET_8_1_1(LOCATION, CONTEXT)},
{"8_2_1", BT_BAP_LC3_UNICAST_PRESET_8_2_1(LOCATION, CONTEXT)},
{"16_1_1", BT_BAP_LC3_UNICAST_PRESET_16_1_1(LOCATION, CONTEXT)},
{"16_2_1", BT_BAP_LC3_UNICAST_PRESET_16_2_1(LOCATION, CONTEXT)},
{"24_1_1", BT_BAP_LC3_UNICAST_PRESET_24_1_1(LOCATION, CONTEXT)},
{"24_2_1", BT_BAP_LC3_UNICAST_PRESET_24_2_1(LOCATION, CONTEXT)},
{"32_1_1", BT_BAP_LC3_UNICAST_PRESET_32_1_1(LOCATION, CONTEXT)},
{"32_2_1", BT_BAP_LC3_UNICAST_PRESET_32_2_1(LOCATION, CONTEXT)},
{"441_1_1", BT_BAP_LC3_UNICAST_PRESET_441_1_1(LOCATION, CONTEXT)},
{"441_2_1", BT_BAP_LC3_UNICAST_PRESET_441_2_1(LOCATION, CONTEXT)},
{"48_1_1", BT_BAP_LC3_UNICAST_PRESET_48_1_1(LOCATION, CONTEXT)},
{"48_2_1", BT_BAP_LC3_UNICAST_PRESET_48_2_1(LOCATION, CONTEXT)},
{"48_3_1", BT_BAP_LC3_UNICAST_PRESET_48_3_1(LOCATION, CONTEXT)},
{"48_4_1", BT_BAP_LC3_UNICAST_PRESET_48_4_1(LOCATION, CONTEXT)},
{"48_5_1", BT_BAP_LC3_UNICAST_PRESET_48_5_1(LOCATION, CONTEXT)},
{"48_6_1", BT_BAP_LC3_UNICAST_PRESET_48_6_1(LOCATION, CONTEXT)},
/* High-reliability presets */
{"8_1_2", BT_BAP_LC3_UNICAST_PRESET_8_1_2(LOCATION, CONTEXT)},
{"8_2_2", BT_BAP_LC3_UNICAST_PRESET_8_2_2(LOCATION, CONTEXT)},
{"16_1_2", BT_BAP_LC3_UNICAST_PRESET_16_1_2(LOCATION, CONTEXT)},
{"16_2_2", BT_BAP_LC3_UNICAST_PRESET_16_2_2(LOCATION, CONTEXT)},
{"24_1_2", BT_BAP_LC3_UNICAST_PRESET_24_1_2(LOCATION, CONTEXT)},
{"24_2_2", BT_BAP_LC3_UNICAST_PRESET_24_2_2(LOCATION, CONTEXT)},
{"32_1_2", BT_BAP_LC3_UNICAST_PRESET_32_1_2(LOCATION, CONTEXT)},
{"32_2_2", BT_BAP_LC3_UNICAST_PRESET_32_2_2(LOCATION, CONTEXT)},
{"441_1_2", BT_BAP_LC3_UNICAST_PRESET_441_1_2(LOCATION, CONTEXT)},
{"441_2_2", BT_BAP_LC3_UNICAST_PRESET_441_2_2(LOCATION, CONTEXT)},
{"48_1_2", BT_BAP_LC3_UNICAST_PRESET_48_1_2(LOCATION, CONTEXT)},
{"48_2_2", BT_BAP_LC3_UNICAST_PRESET_48_2_2(LOCATION, CONTEXT)},
{"48_3_2", BT_BAP_LC3_UNICAST_PRESET_48_3_2(LOCATION, CONTEXT)},
{"48_4_2", BT_BAP_LC3_UNICAST_PRESET_48_4_2(LOCATION, CONTEXT)},
{"48_5_2", BT_BAP_LC3_UNICAST_PRESET_48_5_2(LOCATION, CONTEXT)},
{"48_6_2", BT_BAP_LC3_UNICAST_PRESET_48_6_2(LOCATION, CONTEXT)},
};
static void unicast_stream_configured(struct bt_bap_stream *stream,
const struct bt_audio_codec_qos_pref *pref)
{
struct bt_cap_stream *cap_stream = cap_stream_from_bap_stream(stream);
printk("Configured stream %p\n", stream);
for (size_t i = 0U; i < ARRAY_SIZE(non_idle_streams); i++) {
if (non_idle_streams[i] == NULL) {
non_idle_streams[i] = cap_stream;
non_idle_streams_cnt++;
return;
}
}
FAIL("Could not store cap_stream in non_idle_streams\n");
/* TODO: The preference should be used/taken into account when
* setting the QoS
*/
}
static void unicast_stream_qos_set(struct bt_bap_stream *stream)
{
printk("QoS set stream %p\n", stream);
}
static void unicast_stream_enabled(struct bt_bap_stream *stream)
{
printk("Enabled stream %p\n", stream);
}
static void unicast_stream_started(struct bt_bap_stream *stream)
{
printk("Started stream %p\n", stream);
}
static void unicast_stream_metadata_updated(struct bt_bap_stream *stream)
{
printk("Metadata updated stream %p\n", stream);
}
static void unicast_stream_disabled(struct bt_bap_stream *stream)
{
printk("Disabled stream %p\n", stream);
}
static void unicast_stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stopped stream with reason 0x%02X%p\n", stream, reason);
}
static void unicast_stream_released(struct bt_bap_stream *stream)
{
struct bt_cap_stream *cap_stream = cap_stream_from_bap_stream(stream);
printk("Released stream %p\n", stream);
for (size_t i = 0U; i < ARRAY_SIZE(non_idle_streams); i++) {
if (non_idle_streams[i] == cap_stream) {
non_idle_streams[i] = NULL;
non_idle_streams_cnt--;
return;
}
}
FAIL("Could not find cap_stream in non_idle_streams\n");
}
static struct bt_bap_stream_ops unicast_stream_ops = {
.configured = unicast_stream_configured,
.qos_set = unicast_stream_qos_set,
.enabled = unicast_stream_enabled,
.started = unicast_stream_started,
.metadata_updated = unicast_stream_metadata_updated,
.disabled = unicast_stream_disabled,
.stopped = unicast_stream_stopped,
.released = unicast_stream_released,
};
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", err);
return;
}
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
if (csis_inst == NULL) {
FAIL("Failed to discover CAS CSIS");
return;
}
printk("Found CAS with CSIS %p\n", csis_inst);
} else {
printk("Found CAS\n");
}
SET_FLAG(flag_discovered);
}
static void unicast_start_complete_cb(int err, struct bt_conn *conn)
{
if (err == -ECANCELED) {
SET_FLAG(flag_start_timeout);
} else if (err != 0) {
FAIL("Failed to start (failing conn %p): %d", conn, err);
} else {
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", 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", 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(const struct bt_conn *conn, struct bt_bap_ep *ep)
{
const uint8_t conn_index = bt_conn_index(conn);
for (size_t i = 0U; i < ARRAY_SIZE(unicast_sink_eps[conn_index]); i++) {
if (unicast_sink_eps[conn_index][i] == NULL) {
printk("Conn[%u] %p: Sink #%zu: ep %p\n", conn_index, conn, i, ep);
unicast_sink_eps[conn_index][i] = ep;
return;
}
}
FAIL("Could not add sink ep\n");
}
static void add_remote_source(const struct bt_conn *conn, struct bt_bap_ep *ep)
{
const uint8_t conn_index = bt_conn_index(conn);
for (size_t i = 0U; i < ARRAY_SIZE(unicast_source_eps[conn_index]); i++) {
if (unicast_source_eps[conn_index][i] == NULL) {
printk("Conn[%u] %p: Source #%zu: ep %p\n", conn_index, conn, i, ep);
unicast_source_eps[conn_index][i] = ep;
return;
}
}
FAIL("Could not add source ep\n");
}
static void print_remote_codec(const struct bt_audio_codec_cap *codec_cap, enum bt_audio_dir dir)
{
printk("codec_cap %p dir 0x%02x\n", codec_cap, dir);
print_codec_cap(codec_cap);
}
static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
const struct bt_audio_codec_cap *codec_cap)
{
print_remote_codec(codec_cap, dir);
SET_FLAG(flag_codec_found);
}
static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
{
if (err != 0) {
FAIL("Discovery failed: %d\n", 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);
} else {
FAIL("Invalid dir: %u\n", dir);
}
}
static void 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(conn, ep);
SET_FLAG(flag_endpoint_found);
} else if (dir == BT_AUDIO_DIR_SOURCE) {
add_remote_source(conn, ep);
SET_FLAG(flag_endpoint_found);
} else {
FAIL("Invalid param dir: %u\n", dir);
}
}
static const struct bt_bap_unicast_client_cb unicast_client_cbs = {
.discover = discover_cb,
.pac_record = pac_record_cb,
.endpoint = endpoint_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 init(void)
{
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 unicast client 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_client_sink_streams); i++) {
bt_cap_stream_ops_register(&unicast_client_sink_streams[i], &unicast_stream_ops);
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_client_source_streams); i++) {
bt_cap_stream_ops_register(&unicast_client_source_streams[i], &unicast_stream_ops);
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_streams); i++) {
bt_cap_stream_ops_register(&unicast_streams[i].stream, &unicast_stream_ops);
}
}
static void cap_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, cap_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)
{
const uint8_t conn_index = bt_conn_index(conn);
int err;
UNSET_FLAG(flag_sink_discovered);
UNSET_FLAG(flag_codec_found);
UNSET_FLAG(flag_endpoint_found);
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK);
if (err != 0) {
printk("Failed to discover sink: %d\n", err);
return;
}
memset(unicast_sink_eps[conn_index], 0, sizeof(unicast_sink_eps[conn_index]));
WAIT_FOR_FLAG(flag_sink_discovered);
WAIT_FOR_FLAG(flag_endpoint_found);
WAIT_FOR_FLAG(flag_codec_found);
}
static void discover_source(struct bt_conn *conn)
{
const uint8_t conn_index = bt_conn_index(conn);
int err;
UNSET_FLAG(flag_source_discovered);
UNSET_FLAG(flag_codec_found);
UNSET_FLAG(flag_endpoint_found);
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE);
if (err != 0) {
printk("Failed to discover sink: %d\n", err);
return;
}
memset(unicast_source_eps[conn_index], 0, sizeof(unicast_source_eps[conn_index]));
WAIT_FOR_FLAG(flag_source_discovered);
WAIT_FOR_FLAG(flag_endpoint_found);
WAIT_FOR_FLAG(flag_codec_found);
}
static void discover_cas_inval(struct bt_conn *conn)
{
int err;
err = bt_cap_initiator_unicast_discover(NULL);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_discover with NULL conn did not fail\n");
return;
}
/* Test if it handles concurrent request for same connection */
UNSET_FLAG(flag_discovered);
err = bt_cap_initiator_unicast_discover(conn);
if (err != 0) {
printk("Failed to discover CAS: %d\n", err);
return;
}
err = bt_cap_initiator_unicast_discover(conn);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_discover while previous discovery has not completed "
"did not fail\n");
return;
}
WAIT_FOR_FLAG(flag_discovered);
}
static void discover_cas(struct bt_conn *conn)
{
int err;
UNSET_FLAG(flag_discovered);
err = bt_cap_initiator_unicast_discover(conn);
if (err != 0) {
printk("Failed to discover CAS: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_discovered);
}
static void unicast_group_create(struct bt_bap_unicast_group **out_unicast_group)
{
struct bt_bap_unicast_group_stream_param group_source_stream_params;
struct bt_bap_unicast_group_stream_param group_sink_stream_params;
struct bt_bap_unicast_group_stream_pair_param pair_params;
struct bt_bap_unicast_group_param group_param;
int err;
group_sink_stream_params.qos = &unicast_preset_16_2_1.qos;
group_sink_stream_params.stream = &unicast_client_sink_streams[0].bap_stream;
group_source_stream_params.qos = &unicast_preset_16_2_1.qos;
group_source_stream_params.stream = &unicast_client_source_streams[0].bap_stream;
pair_params.tx_param = &group_sink_stream_params;
pair_params.rx_param = &group_source_stream_params;
group_param.packing = BT_ISO_PACKING_SEQUENTIAL;
group_param.params_count = 1;
group_param.params = &pair_params;
err = bt_bap_unicast_group_create(&group_param, out_unicast_group);
if (err != 0) {
FAIL("Failed to create group: %d\n", err);
return;
}
}
static void unicast_audio_start_inval(struct bt_bap_unicast_group *unicast_group)
{
struct bt_audio_codec_cfg invalid_codec = BT_AUDIO_CODEC_LC3_CONFIG_16_2(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_MEDIA);
struct bt_cap_unicast_audio_start_stream_param invalid_stream_param;
struct bt_cap_unicast_audio_start_stream_param valid_stream_param;
struct bt_cap_unicast_audio_start_param invalid_start_param;
struct bt_cap_unicast_audio_start_param valid_start_param;
int err;
valid_start_param.type = BT_CAP_SET_TYPE_AD_HOC;
valid_start_param.count = 1u;
valid_start_param.stream_params = &valid_stream_param;
valid_stream_param.member.member = default_conn;
valid_stream_param.stream = &unicast_client_sink_streams[0];
valid_stream_param.ep = unicast_sink_eps[bt_conn_index(default_conn)][0];
valid_stream_param.codec_cfg = &unicast_preset_16_2_1.codec_cfg;
/* Test NULL parameters */
err = bt_cap_initiator_unicast_audio_start(NULL);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL param did not fail\n");
return;
}
/* Test invalid parameters */
memcpy(&invalid_stream_param, &valid_stream_param, sizeof(valid_stream_param));
memcpy(&invalid_start_param, &valid_start_param, sizeof(valid_start_param));
invalid_start_param.stream_params = &invalid_stream_param;
/* Test invalid stream_start parameters */
invalid_start_param.count = 0U;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with 0 count did not fail\n");
return;
}
memcpy(&invalid_start_param, &valid_start_param, sizeof(valid_start_param));
invalid_start_param.stream_params = &invalid_stream_param;
invalid_start_param.stream_params = NULL;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL stream params did not fail\n");
return;
}
memcpy(&invalid_start_param, &valid_start_param, sizeof(valid_start_param));
invalid_start_param.stream_params = &invalid_stream_param;
/* Test invalid stream_param parameters */
invalid_stream_param.member.member = NULL;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL stream params member did not "
"fail\n");
return;
}
memcpy(&invalid_stream_param, &valid_stream_param, sizeof(valid_stream_param));
invalid_stream_param.stream = NULL;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL stream params stream did not "
"fail\n");
return;
}
memcpy(&invalid_stream_param, &valid_stream_param, sizeof(valid_stream_param));
invalid_stream_param.ep = NULL;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL stream params ep did not "
"fail\n");
return;
}
memcpy(&invalid_stream_param, &valid_stream_param, sizeof(valid_stream_param));
invalid_stream_param.codec_cfg = NULL;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with NULL stream params codec did not "
"fail\n");
return;
}
/* Clear metadata so that it does not contain the mandatory stream context */
memcpy(&invalid_stream_param, &valid_stream_param, sizeof(valid_stream_param));
memset(&invalid_codec.meta, 0, sizeof(invalid_codec.meta));
invalid_stream_param.codec_cfg = &invalid_codec;
err = bt_cap_initiator_unicast_audio_start(&invalid_start_param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_start with invalid Codec metadata did not "
"fail\n");
return;
}
}
static void unicast_audio_start(struct bt_bap_unicast_group *unicast_group, bool wait)
{
struct bt_cap_unicast_audio_start_stream_param stream_param[2];
struct bt_cap_unicast_audio_start_param param;
int err;
param.type = BT_CAP_SET_TYPE_AD_HOC;
param.count = ARRAY_SIZE(stream_param);
param.stream_params = stream_param;
stream_param[0].member.member = default_conn;
stream_param[0].stream = &unicast_client_sink_streams[0];
stream_param[0].ep = unicast_sink_eps[bt_conn_index(default_conn)][0];
stream_param[0].codec_cfg = &unicast_preset_16_2_1.codec_cfg;
stream_param[1].member.member = default_conn;
stream_param[1].stream = &unicast_client_source_streams[0];
stream_param[1].ep = unicast_source_eps[bt_conn_index(default_conn)][0];
stream_param[1].codec_cfg = &unicast_preset_16_2_1.codec_cfg;
UNSET_FLAG(flag_started);
err = bt_cap_initiator_unicast_audio_start(&param);
if (err != 0) {
FAIL("Failed to start unicast audio: %d\n", err);
return;
}
if (wait) {
WAIT_FOR_FLAG(flag_started);
}
}
static void unicast_audio_update_inval(void)
{
struct bt_audio_codec_cfg invalid_codec = BT_AUDIO_CODEC_LC3_CONFIG_16_2(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_MEDIA);
struct bt_cap_unicast_audio_update_stream_param stream_params[1] = {0};
struct bt_cap_unicast_audio_update_param param = {0};
int err;
stream_params[0].stream = &unicast_client_sink_streams[0];
stream_params[0].meta = unicast_preset_16_2_1.codec_cfg.meta;
stream_params[0].meta_len = unicast_preset_16_2_1.codec_cfg.meta_len;
param.count = ARRAY_SIZE(stream_params);
param.stream_params = stream_params;
param.type = BT_CAP_SET_TYPE_AD_HOC;
err = bt_cap_initiator_unicast_audio_update(NULL);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_update with NULL params did not fail\n");
return;
}
param.count = 0U;
err = bt_cap_initiator_unicast_audio_update(&param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_update with 0 param count did not fail\n");
return;
}
/* Clear metadata so that it does not contain the mandatory stream context */
param.count = ARRAY_SIZE(stream_params);
memset(&invalid_codec.meta, 0, sizeof(invalid_codec.meta));
stream_params[0].meta = invalid_codec.meta;
err = bt_cap_initiator_unicast_audio_update(&param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_update with invalid Codec metadata did not "
"fail\n");
return;
}
}
static void unicast_audio_update(void)
{
struct bt_cap_unicast_audio_update_stream_param stream_params[2] = {0};
struct bt_cap_unicast_audio_update_param param = {0};
uint8_t new_meta[] = {
3,
BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT,
BT_BYTES_LIST_LE16(BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED),
LONG_META_LEN,
BT_AUDIO_METADATA_TYPE_VENDOR,
LONG_META,
};
int err;
stream_params[0].stream = &unicast_client_sink_streams[0];
stream_params[0].meta = new_meta;
stream_params[0].meta_len = ARRAY_SIZE(new_meta);
stream_params[1].stream = &unicast_client_source_streams[0];
stream_params[1].meta = new_meta;
stream_params[1].meta_len = ARRAY_SIZE(new_meta);
param.count = ARRAY_SIZE(stream_params);
param.stream_params = stream_params;
param.type = BT_CAP_SET_TYPE_AD_HOC;
UNSET_FLAG(flag_updated);
err = bt_cap_initiator_unicast_audio_update(&param);
if (err != 0) {
FAIL("Failed to update unicast audio: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_updated);
printk("READ LONG META\n");
}
static void unicast_audio_stop_inval(void)
{
int err;
err = bt_cap_initiator_unicast_audio_stop(NULL);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_stop with NULL param did not fail\n");
return;
}
}
static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group)
{
struct bt_cap_unicast_audio_stop_param param;
int err;
param.type = BT_CAP_SET_TYPE_AD_HOC;
param.count = non_idle_streams_cnt;
param.streams = non_idle_streams;
UNSET_FLAG(flag_stopped);
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err != 0) {
FAIL("Failed to stop unicast audio: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_stopped);
/* Verify that it cannot be stopped twice */
err = bt_cap_initiator_unicast_audio_stop(&param);
if (err == 0) {
FAIL("bt_cap_initiator_unicast_audio_stop with already-stopped streams did not "
"fail\n");
return;
}
}
static void unicast_audio_cancel(void)
{
int err;
err = bt_cap_initiator_unicast_audio_cancel();
if (err != 0) {
FAIL("Failed to cancel unicast audio: %d\n", err);
return;
}
}
static void unicast_group_delete_inval(void)
{
int err;
err = bt_bap_unicast_group_delete(NULL);
if (err == 0) {
FAIL("bt_bap_unicast_group_delete with NULL group did not fail\n");
return;
}
}
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;
}
/* Verify that it cannot be deleted twice */
err = bt_bap_unicast_group_delete(unicast_group);
if (err == 0) {
FAIL("bt_bap_unicast_group_delete with already-deleted unicast group did not "
"fail\n");
return;
}
}
static void test_main_cap_initiator_unicast(void)
{
struct bt_bap_unicast_group *unicast_group;
const size_t iterations = 2;
init();
scan_and_connect();
WAIT_FOR_FLAG(flag_mtu_exchanged);
discover_cas(default_conn);
discover_cas(default_conn); /* test that we can discover twice */
discover_sink(default_conn);
discover_source(default_conn);
for (size_t i = 0U; i < iterations; i++) {
unicast_group_create(&unicast_group);
for (size_t j = 0U; j < iterations; j++) {
unicast_audio_start(unicast_group, true);
unicast_audio_update();
unicast_audio_stop(unicast_group);
}
unicast_group_delete(unicast_group);
unicast_group = NULL;
}
PASS("CAP initiator unicast passed\n");
}
static void test_main_cap_initiator_unicast_inval(void)
{
struct bt_bap_unicast_group *unicast_group;
init();
scan_and_connect();
WAIT_FOR_FLAG(flag_mtu_exchanged);
discover_cas_inval(default_conn);
discover_cas(default_conn);
discover_sink(default_conn);
discover_source(default_conn);
unicast_group_create(&unicast_group);
unicast_audio_start_inval(unicast_group);
unicast_audio_start(unicast_group, true);
unicast_audio_update_inval();
unicast_audio_update();
unicast_audio_stop_inval();
unicast_audio_stop(unicast_group);
unicast_group_delete_inval();
unicast_group_delete(unicast_group);
unicast_group = NULL;
PASS("CAP initiator unicast inval passed\n");
}
static void test_cap_initiator_unicast_timeout(void)
{
struct bt_bap_unicast_group *unicast_group;
const k_timeout_t timeout = K_SECONDS(1);
const size_t iterations = 2;
init();
scan_and_connect();
WAIT_FOR_FLAG(flag_mtu_exchanged);
discover_cas(default_conn);
discover_sink(default_conn);
discover_source(default_conn);
unicast_group_create(&unicast_group);
for (size_t j = 0U; j < iterations; j++) {
unicast_audio_start(unicast_group, false);
k_sleep(timeout);
if ((bool)atomic_get(&flag_started)) {
FAIL("Unexpected start complete\n");
} else {
unicast_audio_cancel();
}
WAIT_FOR_FLAG(flag_start_timeout);
unicast_audio_stop(unicast_group);
}
unicast_group_delete(unicast_group);
unicast_group = NULL;
PASS("CAP initiator unicast timeout passed\n");
}
static const struct named_lc3_preset *cap_get_named_preset(const char *preset_arg)
{
for (size_t i = 0U; i < ARRAY_SIZE(lc3_unicast_presets); i++) {
if (strcmp(preset_arg, lc3_unicast_presets[i].name) == 0) {
return &lc3_unicast_presets[i];
}
}
return NULL;
}
static int cap_initiator_ac_create_unicast_group(const struct cap_initiator_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[CAP_AC_MAX_SNK] = {0};
struct bt_bap_unicast_group_stream_param src_group_stream_params[CAP_AC_MAX_SRC] = {0};
struct bt_bap_unicast_group_stream_pair_param pair_params[CAP_AC_MAX_PAIR] = {0};
struct bt_bap_unicast_group_param group_param = {0};
struct bt_audio_codec_qos *snk_qos[CAP_AC_MAX_SNK];
struct bt_audio_codec_qos *src_qos[CAP_AC_MAX_SRC];
size_t snk_stream_cnt = 0U;
size_t src_stream_cnt = 0U;
size_t pair_cnt = 0U;
for (size_t i = 0U; i < snk_cnt; i++) {
snk_qos[i] = &snk_uni_streams[i]->qos;
}
for (size_t i = 0U; i < src_cnt; i++) {
src_qos[i] = &src_uni_streams[i]->qos;
}
/* 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_qos[i];
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_qos[i];
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 cap_initiator_ac_cap_unicast_start(const struct cap_initiator_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[CAP_AC_MAX_STREAM] = {0};
struct bt_audio_codec_cfg *snk_codec_cfgs[CAP_AC_MAX_SNK] = {0};
struct bt_audio_codec_cfg *src_codec_cfgs[CAP_AC_MAX_SRC] = {0};
struct bt_cap_stream *snk_cap_streams[CAP_AC_MAX_SNK] = {0};
struct bt_cap_stream *src_cap_streams[CAP_AC_MAX_SRC] = {0};
struct bt_cap_unicast_audio_start_param start_param = {0};
struct bt_bap_ep *snk_eps[CAP_AC_MAX_SNK] = {0};
struct bt_bap_ep *src_eps[CAP_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;
for (size_t i = 0U; i < param->conn_cnt; i++) {
const uint8_t conn_index = bt_conn_index(connected_conns[i]);
#if UNICAST_SINK_SUPPORTED
for (size_t j = 0U; j < param->snk_cnt[i]; j++) {
snk_eps[snk_ep_cnt] = unicast_sink_eps[conn_index][j];
if (snk_eps[snk_ep_cnt] == NULL) {
FAIL("No sink[%u][%zu] endpoint available\n", conn_index, 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] = unicast_source_eps[conn_index][j];
if (src_eps[src_ep_cnt] == NULL) {
FAIL("No source[%u][%zu] endpoint available\n", conn_index, 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;
return bt_cap_initiator_unicast_audio_start(&start_param);
}
static int cap_initiator_ac_unicast(const struct cap_initiator_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[CAP_AC_MAX_SNK];
struct unicast_stream *src_uni_streams[CAP_AC_MAX_SRC];
size_t snk_cnt = 0;
size_t src_cnt = 0;
int err;
if (param->conn_cnt > CAP_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] > CAP_AC_MAX_SNK) {
FAIL("Invalid param->snk_cnt[%zu]: %zu\n", i, param->snk_cnt[i]);
return -EINVAL;
}
if (param->src_cnt[i] > CAP_AC_MAX_SRC) {
FAIL("Invalid param->src_cnt[%zu]: %zu\n", i, param->src_cnt[i]);
return -EINVAL;
}
}
/* Set all endpoints from multiple connections in a single array, and verify that the known
* endpoints matches the audio configuration
*/
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];
if (param->snk_named_preset == NULL) {
FAIL("No sink preset available\n");
return -EINVAL;
}
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];
if (param->src_named_preset == NULL) {
FAIL("No sink preset available\n");
return -EINVAL;
}
copy_unicast_stream_preset(src_uni_streams[i], param->src_named_preset);
/* Some audio configuration requires multiple source channels,
* so multiply the SDU based on the channel count
*/
src_uni_streams[i]->qos.sdu *= param->src_chan_cnt;
}
err = cap_initiator_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 = cap_initiator_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 test_cap_initiator_ac(const struct cap_initiator_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 > CAP_AC_MAX_CONN) {
FAIL("Invalid conn_cnt: %zu\n", param->conn_cnt);
return;
}
if (param->snk_named_preset == NULL && param->src_named_preset == NULL) {
FAIL("No presets available\n");
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]);
}
}
cap_initiator_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("CAP initiator 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 test_cap_initiator_ac_1(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_1",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {0U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 0U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_2(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_2",
.conn_cnt = 1U,
.snk_cnt = {0U},
.src_cnt = {1U},
.snk_chan_cnt = 0U,
.src_chan_cnt = 1U,
.snk_named_preset = NULL,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_3(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_3",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_4(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_4",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {0U},
.snk_chan_cnt = 2U,
.src_chan_cnt = 0U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_5(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_5",
.conn_cnt = 1U,
.snk_cnt = {1U},
.src_cnt = {1U},
.snk_chan_cnt = 2U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_6_i(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_6_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {0U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 0U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_6_ii(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_6_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {0U, 0U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 0U,
.snk_named_preset = snk_named_preset,
.src_named_preset = NULL,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_7_i(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_7_i",
.conn_cnt = 1U,
/* TODO: These should be in different CIS but will be in the same currently */
.snk_cnt = {1U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_7_ii(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_7_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 0U},
.src_cnt = {0U, 1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_8_i(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_8_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_8_ii(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_8_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {1U, 0U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_9_i(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_9_i",
.conn_cnt = 1U,
.snk_cnt = {0U},
.src_cnt = {2U},
.snk_chan_cnt = 0U,
.src_chan_cnt = 1U,
.snk_named_preset = NULL,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_9_ii(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_9_ii",
.conn_cnt = 2U,
.snk_cnt = {0U, 0U},
.src_cnt = {1U, 1U},
.snk_chan_cnt = 0U,
.src_chan_cnt = 1U,
.snk_named_preset = NULL,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_10(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_10",
.conn_cnt = 1U,
.snk_cnt = {0U},
.src_cnt = {1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 2U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_11_i(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_11_i",
.conn_cnt = 1U,
.snk_cnt = {2U},
.src_cnt = {2U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_ac(&param);
}
static void test_cap_initiator_ac_11_ii(void)
{
const struct cap_initiator_ac_param param = {
.name = "ac_11_ii",
.conn_cnt = 2U,
.snk_cnt = {1U, 1U},
.src_cnt = {1U, 1U},
.snk_chan_cnt = 1U,
.src_chan_cnt = 1U,
.snk_named_preset = snk_named_preset,
.src_named_preset = src_named_preset,
};
test_cap_initiator_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 = cap_get_named_preset(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 = cap_get_named_preset(preset_arg);
if (src_named_preset == NULL) {
FAIL("Failed to get source preset from %s\n", preset_arg);
}
} else {
FAIL("Invalid arg: %s\n", arg);
}
}
}
static const struct bst_test_instance test_cap_initiator_unicast[] = {
{
.test_id = "cap_initiator_unicast",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_cap_initiator_unicast,
},
{
.test_id = "cap_initiator_unicast_timeout",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_unicast_timeout,
},
{
.test_id = "cap_initiator_unicast_inval",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main_cap_initiator_unicast_inval,
},
{
.test_id = "cap_initiator_ac_1",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_1,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_2",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_2,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_3",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_3,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_4",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_4,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_5",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_5,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_6_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_6_i,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_6_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_6_ii,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_7_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_7_i,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_7_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_7_ii,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_8_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_8_i,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_8_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_8_ii,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_9_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_9_i,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_9_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_9_ii,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_10",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_10,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_11_i",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_11_i,
.test_args_f = test_args,
},
{
.test_id = "cap_initiator_ac_11_ii",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_ac_11_ii,
.test_args_f = test_args,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_cap_initiator_unicast_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_cap_initiator_unicast);
}
#else /* !CONFIG_BT_CAP_INITIATOR_UNICAST */
struct bst_test_list *test_cap_initiator_unicast_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_CAP_INITIATOR_UNICAST */