blob: adecbf2f2c37e6a5bc9ef3dfe305e030888a5756 [file] [log] [blame]
/*
* Copyright (c) 2023 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/fff.h>
#include <zephyr/types.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/sys/util_macro.h>
#include "bap_unicast_server.h"
#include "bap_stream.h"
#include "gatt_expects.h"
#include "conn.h"
#include "gatt.h"
#include "iso.h"
#include "mock_kernel.h"
#include "pacs.h"
#include "test_common.h"
void test_mocks_init(void)
{
mock_bap_unicast_server_init();
mock_bt_iso_init();
mock_kernel_init();
mock_bt_pacs_init();
mock_bap_stream_init();
mock_bt_gatt_init();
}
void test_mocks_cleanup(void)
{
mock_bap_unicast_server_cleanup();
mock_bt_iso_cleanup();
mock_kernel_cleanup();
mock_bt_pacs_cleanup();
mock_bap_stream_cleanup();
mock_bt_gatt_cleanup();
}
void test_mocks_reset(void)
{
test_mocks_cleanup();
test_mocks_init();
}
static uint8_t attr_found(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data)
{
const struct bt_gatt_attr **result = user_data;
*result = attr;
return BT_GATT_ITER_STOP;
}
void test_conn_init(struct bt_conn *conn)
{
conn->index = 0;
conn->info.type = BT_CONN_TYPE_LE;
conn->info.role = BT_CONN_ROLE_PERIPHERAL;
conn->info.state = BT_CONN_STATE_CONNECTED;
conn->info.security.level = BT_SECURITY_L2;
conn->info.security.enc_key_size = BT_ENC_KEY_SIZE_MAX;
conn->info.security.flags = BT_SECURITY_FLAG_OOB | BT_SECURITY_FLAG_SC;
}
const struct bt_gatt_attr *test_ase_control_point_get(void)
{
static const struct bt_gatt_attr *attr;
if (attr == NULL) {
bt_gatt_foreach_attr_type(BT_ATT_FIRST_ATTRIBUTE_HANDLE,
BT_ATT_LAST_ATTRIBUTE_HANDLE,
BT_UUID_ASCS_ASE_CP, NULL, 1, attr_found, &attr);
}
zassert_not_null(attr, "ASE Control Point not found");
return attr;
}
uint8_t test_ase_get(const struct bt_uuid *uuid, int num_ase, ...)
{
const struct bt_gatt_attr *attr = NULL;
va_list attrs;
uint8_t i;
va_start(attrs, num_ase);
for (i = 0; i < num_ase; i++) {
const struct bt_gatt_attr *prev = attr;
bt_gatt_foreach_attr_type(BT_ATT_FIRST_ATTRIBUTE_HANDLE,
BT_ATT_LAST_ATTRIBUTE_HANDLE,
uuid, NULL, i + 1, attr_found, &attr);
/* Another attribute was not found */
if (attr == prev) {
break;
}
*(va_arg(attrs, const struct bt_gatt_attr **)) = attr;
}
va_end(attrs);
return i;
}
uint8_t test_ase_id_get(const struct bt_gatt_attr *ase)
{
const struct test_ase_chrc_value_hdr *hdr;
ssize_t ret;
ret = ase->read(NULL, ase, NULL, 0, 0);
zassert_false(ret < 0, "ase->read returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
expect_bt_gatt_attr_read_called_once(NULL, ase, EMPTY, EMPTY, 0, EMPTY, sizeof(*hdr));
hdr = bt_gatt_attr_read_fake.arg5_val;
/* Reset the mock state */
bt_gatt_attr_read_reset();
return hdr->ase_id;
}
static struct bt_bap_stream *stream_allocated;
static const struct bt_codec_qos_pref qos_pref = BT_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M,
0x02, 10, 40000, 40000,
40000, 40000);
static int unicast_server_cb_config_custom_fake(struct bt_conn *conn, const struct bt_bap_ep *ep,
enum bt_audio_dir dir, const struct bt_codec *codec,
struct bt_bap_stream **stream,
struct bt_codec_qos_pref *const pref,
struct bt_bap_ascs_rsp *rsp)
{
*stream = stream_allocated;
*pref = qos_pref;
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_SUCCESS, BT_BAP_ASCS_REASON_NONE);
bt_bap_stream_cb_register(*stream, &mock_bap_stream_ops);
return 0;
}
void test_ase_control_client_config_codec(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
ssize_t ret;
stream_allocated = stream;
mock_bap_unicast_server_cb_config_fake.custom_fake = unicast_server_cb_config_custom_fake;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "cp_attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
stream_allocated = NULL;
}
void test_ase_control_client_config_qos(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x02, /* Opcode = Config QoS */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x01, /* CIG_ID[0] */
0x01, /* CIS_ID[0] */
0xFF, 0x00, 0x00, /* SDU_Interval[0] */
0x00, /* Framing[0] */
0x02, /* PHY[0] */
0x64, 0x00, /* Max_SDU[0] */
0x02, /* Retransmission_Number[0] */
0x0A, 0x00, /* Max_Transport_Latency[0] */
0x40, 0x9C, 0x00, /* Presentation_Delay[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_enable(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x03, /* Opcode = Enable */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x00, /* Metadata_Length[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_disable(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x05, /* Opcode = Disable */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_release(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x08, /* Opcode = Disable */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_update_metadata(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x07, /* Opcode = Update Metadata */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x03, /* Metadata_Length[0] */
0x02, 0x02, 0x04, /* Metadata[0] = Streaming Context (Media) */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_receiver_start_ready(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x04, /* Opcode = Receiver Start Ready */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_ase_control_client_receiver_stop_ready(struct bt_conn *conn, uint8_t ase_id)
{
const struct bt_gatt_attr *attr = test_ase_control_point_get();
const uint8_t buf[] = {
0x06, /* Opcode = Receiver Stop Ready */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
void test_preamble_state_codec_configured(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream)
{
test_ase_control_client_config_codec(conn, ase_id, stream);
test_mocks_reset();
}
void test_preamble_state_qos_configured(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream)
{
test_ase_control_client_config_codec(conn, ase_id, stream);
test_ase_control_client_config_qos(conn, ase_id);
test_mocks_reset();
}
void test_preamble_state_enabling(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream)
{
test_ase_control_client_config_codec(conn, ase_id, stream);
test_ase_control_client_config_qos(conn, ase_id);
test_ase_control_client_enable(conn, ase_id);
test_mocks_reset();
}
void test_preamble_state_streaming(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream, struct bt_iso_chan **chan,
bool source)
{
int err;
test_ase_control_client_config_codec(conn, ase_id, stream);
test_ase_control_client_config_qos(conn, ase_id);
test_ase_control_client_enable(conn, ase_id);
err = mock_bt_iso_accept(conn, 0x01, 0x01, chan);
zassert_equal(0, err, "Failed to connect iso: err %d", err);
if (source) {
test_ase_control_client_receiver_start_ready(conn, ase_id);
} else {
err = bt_bap_stream_start(stream);
zassert_equal(0, err, "bt_bap_stream_start err %d", err);
}
test_mocks_reset();
}
void test_preamble_state_disabling(struct bt_conn *conn, uint8_t ase_id,
struct bt_bap_stream *stream)
{
struct bt_iso_chan *chan;
int err;
test_ase_control_client_config_codec(conn, ase_id, stream);
test_ase_control_client_config_qos(conn, ase_id);
test_ase_control_client_enable(conn, ase_id);
err = mock_bt_iso_accept(conn, 0x01, 0x01, &chan);
zassert_equal(0, err, "Failed to connect iso: err %d", err);
test_ase_control_client_receiver_start_ready(conn, ase_id);
test_ase_control_client_disable(conn, ase_id);
test_mocks_reset();
}