/* * Copyright (c) 2023 Codecoup * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #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) { struct test_ase_chrc_value_hdr hdr = { 0 }; ssize_t ret; ret = ase->read(NULL, ase, &hdr, sizeof(hdr), 0); zassert_false(ret < 0, "ase->read returned unexpected (err 0x%02x)", BT_GATT_ERR(ret)); return hdr.ase_id; } static struct bt_bap_stream *stream_allocated; static const struct bt_audio_codec_qos_pref qos_pref = BT_AUDIO_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_audio_codec_cfg *codec_cfg, struct bt_bap_stream **stream, struct bt_audio_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] */ 0x04, /* Metadata_Length[0] */ 0x03, 0x02, 0x04, 0x00, /* 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(); }