bluetooth: tester: Add support for CAP
Add support for CAP test cases. Split btp_bap.c for better maintenance into unicast and broadcast. Signed-off-by: Magdalena Kasenberg <magdalena.kasenberg@codecoup.pl>
This commit is contained in:
parent
dcedb649ca
commit
0d27dd5dd3
|
@ -32,8 +32,19 @@ if(CONFIG_BT_IAS OR CONFIG_BT_IAS_CLIENT)
|
|||
target_sources(app PRIVATE src/btp_ias.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BAP_UNICAST OR BT_BAP_BROADCAST_SOURCE OR BT_BAP_BROADCAST_SINK)
|
||||
target_sources(app PRIVATE
|
||||
src/btp_bap_audio_stream.c
|
||||
src/btp_bap.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BAP_UNICAST)
|
||||
target_sources(app PRIVATE src/btp_bap.c)
|
||||
target_sources(app PRIVATE src/btp_bap_unicast.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BAP_BROADCAST_SOURCE OR CONFIG_BT_BAP_BROADCAST_SOURCE)
|
||||
target_sources(app PRIVATE src/btp_bap_broadcast.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_HAS)
|
||||
|
@ -44,6 +55,10 @@ if (CONFIG_BT_CSIP_SET_MEMBER)
|
|||
target_sources(app PRIVATE src/btp_csis.c)
|
||||
endif()
|
||||
|
||||
if (CONFIG_BT_CSIP_SET_COORDINATOR)
|
||||
target_sources(app PRIVATE src/btp_csip.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_MICP_MIC_DEV)
|
||||
target_sources(app PRIVATE src/btp_micp.c)
|
||||
endif()
|
||||
|
@ -67,3 +82,7 @@ endif()
|
|||
if(CONFIG_BT_HAS)
|
||||
target_sources(app PRIVATE src/btp_hap.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_CAP_INITIATOR)
|
||||
target_sources(app PRIVATE src/btp_cap.c)
|
||||
endif()
|
||||
|
|
|
@ -5,7 +5,7 @@ CONFIG_BT_BAP_UNICAST_CLIENT=y
|
|||
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT=2
|
||||
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT=2
|
||||
CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT=4
|
||||
CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=16
|
||||
CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=22
|
||||
|
||||
# Ring buffer for streaming ISO data
|
||||
CONFIG_RING_BUFFER=y
|
||||
|
@ -20,6 +20,11 @@ CONFIG_BT_BUF_CMD_TX_SIZE=255
|
|||
# were freed too slow. The bt_bap_stream_ops.configured callback comes earlier.
|
||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=4
|
||||
|
||||
# CAP
|
||||
CONFIG_BT_CAP_INITIATOR=y
|
||||
# To have multiple CCIDs
|
||||
CONFIG_BT_TBS=y
|
||||
|
||||
# MICP
|
||||
CONFIG_BT_MICP_MIC_DEV=y
|
||||
CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT=1
|
||||
|
@ -95,6 +100,9 @@ CONFIG_BT_HAS_CLIENT=y
|
|||
# CSIS
|
||||
CONFIG_BT_CSIP_SET_MEMBER=y
|
||||
|
||||
# CSIP
|
||||
CONFIG_BT_CSIP_SET_COORDINATOR=y
|
||||
|
||||
# CCP
|
||||
CONFIG_BT_ATT_TX_COUNT=12
|
||||
CONFIG_BT_TBS_CLIENT_GTBS=y
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "btp_mcp.h"
|
||||
#include "btp_mcs.h"
|
||||
#include "btp_hap.h"
|
||||
#include "btp_csip.h"
|
||||
#include "btp_cap.h"
|
||||
|
||||
#define BTP_MTU 1024
|
||||
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
|
||||
|
@ -65,8 +67,10 @@
|
|||
#define BTP_SERVICE_ID_MCP 22
|
||||
#define BTP_SERVICE_ID_GMCS 23
|
||||
#define BTP_SERVICE_ID_HAP 24
|
||||
#define BTP_SERVICE_ID_CSIP 25
|
||||
#define BTP_SERVICE_ID_CAP 26
|
||||
|
||||
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_HAP
|
||||
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_CAP
|
||||
|
||||
#define BTP_STATUS_SUCCESS 0x00
|
||||
#define BTP_STATUS_FAILED 0x01
|
||||
|
|
|
@ -73,7 +73,7 @@ struct btp_ascs_update_metadata_cmd {
|
|||
uint8_t ase_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_ASCS_ADD_ASE_TO_CIS 0x0a
|
||||
#define BTP_ASCS_ADD_ASE_TO_CIS 0x0a
|
||||
struct btp_ascs_add_ase_to_cis {
|
||||
bt_addr_le_t address;
|
||||
uint8_t ase_id;
|
||||
|
@ -81,6 +81,18 @@ struct btp_ascs_add_ase_to_cis {
|
|||
uint8_t cis_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_ASCS_PRECONFIGURE_QOS 0x0b
|
||||
struct btp_ascs_preconfigure_qos_cmd {
|
||||
uint8_t cig_id;
|
||||
uint8_t cis_id;
|
||||
uint8_t sdu_interval[3];
|
||||
uint8_t framing;
|
||||
uint16_t max_sdu;
|
||||
uint8_t retransmission_num;
|
||||
uint16_t max_transport_latency;
|
||||
uint8_t presentation_delay[3];
|
||||
} __packed;
|
||||
|
||||
/* ASCS events */
|
||||
#define BTP_ASCS_EV_OPERATION_COMPLETED 0x80
|
||||
struct btp_ascs_operation_completed_ev {
|
||||
|
|
165
tests/bluetooth/tester/src/btp/btp_cap.h
Normal file
165
tests/bluetooth/tester/src/btp/btp_cap.h
Normal file
|
@ -0,0 +1,165 @@
|
|||
/* btp_cap.h - Bluetooth tester headers */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* CAP commands */
|
||||
#define BTP_CAP_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct btp_cap_read_supported_commands_rp {
|
||||
uint8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_DISCOVER 0x02
|
||||
struct btp_cap_discover_cmd {
|
||||
bt_addr_le_t address;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_UNICAST_SETUP_ASE 0x03
|
||||
struct btp_cap_unicast_setup_ase_cmd {
|
||||
bt_addr_le_t address;
|
||||
uint8_t ase_id;
|
||||
uint8_t cis_id;
|
||||
uint8_t cig_id;
|
||||
uint8_t coding_format;
|
||||
uint16_t vid;
|
||||
uint16_t cid;
|
||||
uint8_t sdu_interval[3];
|
||||
uint8_t framing;
|
||||
uint16_t max_sdu;
|
||||
uint8_t retransmission_num;
|
||||
uint16_t max_transport_latency;
|
||||
uint8_t presentation_delay[3];
|
||||
uint8_t cc_ltvs_len;
|
||||
uint8_t metadata_ltvs_len;
|
||||
uint8_t ltvs[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_UNICAST_AUDIO_START 0x04
|
||||
struct btp_cap_unicast_audio_start_cmd {
|
||||
uint8_t cig_id;
|
||||
uint8_t set_type;
|
||||
} __packed;
|
||||
#define BTP_CAP_UNICAST_AUDIO_START_SET_TYPE_AD_HOC 0x00
|
||||
#define BTP_CAP_UNICAST_AUDIO_START_SET_TYPE_CSIP 0x01
|
||||
|
||||
#define BTP_CAP_UNICAST_AUDIO_UPDATE 0x05
|
||||
struct btp_cap_unicast_audio_update_cmd {
|
||||
uint8_t stream_count;
|
||||
uint8_t update_data[0];
|
||||
} __packed;
|
||||
struct btp_cap_unicast_audio_update_data {
|
||||
bt_addr_le_t address;
|
||||
uint8_t ase_id;
|
||||
uint8_t metadata_ltvs_len;
|
||||
uint8_t metadata_ltvs[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_UNICAST_AUDIO_STOP 0x06
|
||||
struct btp_cap_unicast_audio_stop_cmd {
|
||||
uint8_t cig_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_SETUP_STREAM 0x07
|
||||
struct btp_cap_broadcast_source_setup_stream_cmd {
|
||||
uint8_t source_id;
|
||||
uint8_t subgroup_id;
|
||||
uint8_t coding_format;
|
||||
uint16_t vid;
|
||||
uint16_t cid;
|
||||
uint8_t cc_ltvs_len;
|
||||
uint8_t metadata_ltvs_len;
|
||||
uint8_t ltvs[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_SETUP_SUBGROUP 0x08
|
||||
struct btp_cap_broadcast_source_setup_subgroup_cmd {
|
||||
uint8_t source_id;
|
||||
uint8_t subgroup_id;
|
||||
uint8_t coding_format;
|
||||
uint16_t vid;
|
||||
uint16_t cid;
|
||||
uint8_t cc_ltvs_len;
|
||||
uint8_t metadata_ltvs_len;
|
||||
uint8_t ltvs[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_SETUP 0x09
|
||||
struct btp_cap_broadcast_source_setup_cmd {
|
||||
uint8_t source_id;
|
||||
uint8_t broadcast_id[3];
|
||||
uint8_t sdu_interval[3];
|
||||
uint8_t framing;
|
||||
uint16_t max_sdu;
|
||||
uint8_t retransmission_num;
|
||||
uint16_t max_transport_latency;
|
||||
uint8_t presentation_delay[3];
|
||||
uint8_t flags;
|
||||
uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
|
||||
} __packed;
|
||||
struct btp_cap_broadcast_source_setup_rp {
|
||||
uint8_t source_id;
|
||||
uint32_t gap_settings;
|
||||
uint8_t broadcast_id[BT_AUDIO_BROADCAST_ID_SIZE];
|
||||
} __packed;
|
||||
#define BTP_CAP_BROADCAST_SOURCE_SETUP_FLAG_ENCRYPTION BIT(0)
|
||||
#define BTP_CAP_BROADCAST_SOURCE_SETUP_FLAG_SUBGROUP_CODEC BIT(1)
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_RELEASE 0x0a
|
||||
struct btp_cap_broadcast_source_release_cmd {
|
||||
uint8_t source_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_ADV_START 0x0b
|
||||
struct btp_cap_broadcast_adv_start_cmd {
|
||||
uint8_t source_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_ADV_STOP 0x0c
|
||||
struct btp_cap_broadcast_adv_stop_cmd {
|
||||
uint8_t source_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_START 0x0d
|
||||
struct btp_cap_broadcast_source_start_cmd {
|
||||
uint8_t source_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_STOP 0x0e
|
||||
struct btp_cap_broadcast_source_stop_cmd {
|
||||
uint8_t source_id;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CAP_BROADCAST_SOURCE_UPDATE 0x0f
|
||||
struct btp_cap_broadcast_source_update_cmd {
|
||||
uint8_t source_id;
|
||||
uint8_t metadata_ltvs_len;
|
||||
uint8_t metadata_ltvs[0];
|
||||
} __packed;
|
||||
|
||||
/* CAP events */
|
||||
#define BTP_CAP_EV_DISCOVERY_COMPLETED 0x80
|
||||
struct btp_cap_discovery_completed_ev {
|
||||
bt_addr_le_t address;
|
||||
uint8_t status;
|
||||
} __packed;
|
||||
#define BTP_CAP_DISCOVERY_STATUS_SUCCESS 0x00
|
||||
#define BTP_CAP_DISCOVERY_STATUS_FAILED 0x01
|
||||
|
||||
#define BTP_CAP_EV_UNICAST_START_COMPLETED 0x81
|
||||
struct btp_cap_unicast_start_completed_ev {
|
||||
uint8_t cig_id;
|
||||
uint8_t status;
|
||||
} __packed;
|
||||
#define BTP_CAP_UNICAST_START_STATUS_SUCCESS 0x00
|
||||
#define BTP_CAP_UNICAST_START_STATUS_FAILED 0x01
|
||||
|
||||
#define BTP_CAP_EV_UNICAST_STOP_COMPLETED 0x82
|
||||
struct btp_cap_unicast_stop_completed_ev {
|
||||
uint8_t cig_id;
|
||||
uint8_t status;
|
||||
} __packed;
|
||||
#define BTP_CAP_UNICAST_STOP_STATUS_SUCCESS 0x00
|
||||
#define BTP_CAP_UNICAST_STOP_STATUS_FAILED 0x01
|
24
tests/bluetooth/tester/src/btp/btp_csip.h
Normal file
24
tests/bluetooth/tester/src/btp/btp_csip.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* btp_csip.h - Bluetooth tester headers */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/bluetooth/audio/csip.h>
|
||||
|
||||
/* CSIP commands */
|
||||
#define BTP_CSIP_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct btp_csip_read_supported_commands_rp {
|
||||
uint8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_CSIP_DISCOVER 0x02
|
||||
struct btp_csip_discover_cmd {
|
||||
bt_addr_le_t address;
|
||||
} __packed;
|
||||
|
||||
#define BTP_CSIP_START_ORDERED_ACCESS 0x03
|
||||
struct btp_csip_start_ordered_access_cmd {
|
||||
uint8_t flags;
|
||||
} __packed;
|
|
@ -100,6 +100,9 @@ uint8_t tester_unregister_has(void);
|
|||
uint8_t tester_init_csis(void);
|
||||
uint8_t tester_unregister_csis(void);
|
||||
|
||||
uint8_t tester_init_csip(void);
|
||||
uint8_t tester_unregister_csip(void);
|
||||
|
||||
uint8_t tester_init_micp(void);
|
||||
uint8_t tester_unregister_micp(void);
|
||||
|
||||
|
@ -115,6 +118,9 @@ uint8_t tester_unregister_vcp(void);
|
|||
uint8_t tester_init_cas(void);
|
||||
uint8_t tester_unregister_cas(void);
|
||||
|
||||
uint8_t tester_init_cap(void);
|
||||
uint8_t tester_unregister_cap(void);
|
||||
|
||||
uint8_t tester_init_mcp(void);
|
||||
uint8_t tester_unregister_mcp(void);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
183
tests/bluetooth/tester/src/btp_bap_audio_stream.c
Normal file
183
tests/bluetooth/tester/src/btp_bap_audio_stream.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/* btp_bap_audio_stream.c - Bluetooth BAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#define LOG_MODULE_NAME bttester_bap_audio_stream
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
|
||||
#include "btp/btp.h"
|
||||
#include "btp_bap_audio_stream.h"
|
||||
|
||||
NET_BUF_POOL_FIXED_DEFINE(tx_pool, MAX(CONFIG_BT_ASCS_ASE_SRC_COUNT,
|
||||
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT),
|
||||
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL);
|
||||
|
||||
RING_BUF_DECLARE(audio_ring_buf, CONFIG_BT_ISO_TX_MTU);
|
||||
|
||||
#define ISO_DATA_THREAD_STACK_SIZE 512
|
||||
#define ISO_DATA_THREAD_PRIORITY -7
|
||||
K_THREAD_STACK_DEFINE(iso_data_thread_stack_area, ISO_DATA_THREAD_STACK_SIZE);
|
||||
static struct k_work_q iso_data_work_q;
|
||||
static bool send_worker_inited;
|
||||
|
||||
static inline struct bt_bap_stream *audio_stream_to_bap_stream(struct btp_bap_audio_stream *stream)
|
||||
{
|
||||
return &stream->cap_stream.bap_stream;
|
||||
}
|
||||
|
||||
static void audio_clock_timeout(struct k_work *work)
|
||||
{
|
||||
struct btp_bap_audio_stream *stream;
|
||||
struct k_work_delayable *dwork;
|
||||
|
||||
dwork = k_work_delayable_from_work(work);
|
||||
stream = CONTAINER_OF(dwork, struct btp_bap_audio_stream, audio_clock_work);
|
||||
atomic_inc(&stream->seq_num);
|
||||
|
||||
k_work_schedule(dwork, K_USEC(audio_stream_to_bap_stream(stream)->qos->interval));
|
||||
}
|
||||
|
||||
static void audio_send_timeout(struct k_work *work)
|
||||
{
|
||||
struct bt_iso_tx_info info;
|
||||
struct btp_bap_audio_stream *stream;
|
||||
struct bt_bap_stream *bap_stream;
|
||||
struct k_work_delayable *dwork;
|
||||
struct net_buf *buf;
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
int err;
|
||||
|
||||
dwork = k_work_delayable_from_work(work);
|
||||
stream = CONTAINER_OF(dwork, struct btp_bap_audio_stream, audio_send_work);
|
||||
bap_stream = audio_stream_to_bap_stream(stream);
|
||||
|
||||
if (stream->last_req_seq_num % 201 == 200) {
|
||||
err = bt_bap_stream_get_tx_sync(bap_stream, &info);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to get last seq num: err %d", err);
|
||||
} else if (stream->last_req_seq_num > info.seq_num) {
|
||||
LOG_DBG("Previous TX request rejected by the controller: requested seq %u,"
|
||||
" last accepted seq %u", stream->last_req_seq_num, info.seq_num);
|
||||
stream->last_sent_seq_num = info.seq_num;
|
||||
} else {
|
||||
LOG_DBG("Host and Controller sequence number is in sync.");
|
||||
stream->last_sent_seq_num = info.seq_num;
|
||||
}
|
||||
/* TODO: Synchronize the Host clock with the Controller clock */
|
||||
}
|
||||
|
||||
/* Get buffer within a ring buffer memory */
|
||||
size = ring_buf_get_claim(&audio_ring_buf, &data, bap_stream->qos->sdu);
|
||||
if (size > 0) {
|
||||
buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
|
||||
if (!buf) {
|
||||
LOG_ERR("Cannot allocate net_buf. Dropping data.");
|
||||
} else {
|
||||
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
|
||||
net_buf_add_mem(buf, data, size);
|
||||
|
||||
/* Because the seq_num field of the audio_stream struct is atomic_val_t
|
||||
* (4 bytes), let's allow an overflow and just cast it to uint16_t.
|
||||
*/
|
||||
stream->last_req_seq_num = (uint16_t)atomic_get(&stream->seq_num);
|
||||
|
||||
LOG_DBG("Sending data to stream %p len %d seq %d", bap_stream, size,
|
||||
stream->last_req_seq_num);
|
||||
|
||||
err = bt_bap_stream_send(bap_stream, buf, 0, BT_ISO_TIMESTAMP_NONE);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to send audio data to stream %p, err %d",
|
||||
bap_stream, err);
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free ring buffer memory */
|
||||
err = ring_buf_get_finish(&audio_ring_buf, size);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Error freeing ring buffer memory: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
k_work_schedule_for_queue(&iso_data_work_q, dwork,
|
||||
K_USEC(bap_stream->qos->interval));
|
||||
}
|
||||
|
||||
void btp_bap_audio_stream_started(struct btp_bap_audio_stream *a_stream)
|
||||
{
|
||||
struct bt_bap_ep_info info;
|
||||
struct bt_bap_stream *bap_stream = audio_stream_to_bap_stream(a_stream);
|
||||
|
||||
/* Callback called on transition to Streaming state */
|
||||
|
||||
LOG_DBG("Started stream %p", bap_stream);
|
||||
|
||||
(void)bt_bap_ep_get_info(bap_stream->ep, &info);
|
||||
if (info.can_send == true) {
|
||||
/* Schedule first TX ISO data at seq_num 1 instead of 0 to ensure
|
||||
* we are in sync with the controller at start of streaming.
|
||||
*/
|
||||
a_stream->seq_num = 1;
|
||||
|
||||
/* Run audio clock work in system work queue */
|
||||
k_work_init_delayable(&a_stream->audio_clock_work, audio_clock_timeout);
|
||||
k_work_schedule(&a_stream->audio_clock_work, K_NO_WAIT);
|
||||
|
||||
/* Run audio send work in user defined work queue */
|
||||
k_work_init_delayable(&a_stream->audio_send_work, audio_send_timeout);
|
||||
k_work_schedule_for_queue(&iso_data_work_q, &a_stream->audio_send_work,
|
||||
K_USEC(bap_stream->qos->interval));
|
||||
}
|
||||
}
|
||||
|
||||
void btp_bap_audio_stream_stopped(struct btp_bap_audio_stream *a_stream)
|
||||
{
|
||||
/* Stop send timer */
|
||||
k_work_cancel_delayable(&a_stream->audio_clock_work);
|
||||
k_work_cancel_delayable(&a_stream->audio_send_work);
|
||||
}
|
||||
|
||||
uint8_t btp_bap_audio_stream_send(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
struct btp_bap_send_rp *rp = rsp;
|
||||
const struct btp_bap_send_cmd *cp = cmd;
|
||||
uint32_t ret;
|
||||
|
||||
ret = ring_buf_put(&audio_ring_buf, cp->data, cp->data_len);
|
||||
|
||||
rp->data_len = ret;
|
||||
*rsp_len = sizeof(*rp) + 1;
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int btp_bap_audio_stream_init_send_worker(void)
|
||||
{
|
||||
if (send_worker_inited) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_work_queue_init(&iso_data_work_q);
|
||||
k_work_queue_start(&iso_data_work_q, iso_data_thread_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(iso_data_thread_stack_area),
|
||||
ISO_DATA_THREAD_PRIORITY, NULL);
|
||||
|
||||
send_worker_inited = true;
|
||||
|
||||
return 0;
|
||||
}
|
23
tests/bluetooth/tester/src/btp_bap_audio_stream.h
Normal file
23
tests/bluetooth/tester/src/btp_bap_audio_stream.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* btp_bap_audio_stream.h - Bluetooth BAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
struct btp_bap_audio_stream {
|
||||
struct bt_cap_stream cap_stream;
|
||||
atomic_t seq_num;
|
||||
uint16_t last_req_seq_num;
|
||||
uint16_t last_sent_seq_num;
|
||||
struct k_work_delayable audio_clock_work;
|
||||
struct k_work_delayable audio_send_work;
|
||||
};
|
||||
|
||||
int btp_bap_audio_stream_init_send_worker(void);
|
||||
void btp_bap_audio_stream_started(struct btp_bap_audio_stream *stream);
|
||||
void btp_bap_audio_stream_stopped(struct btp_bap_audio_stream *a_stream);
|
||||
uint8_t btp_bap_audio_stream_send(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
1571
tests/bluetooth/tester/src/btp_bap_broadcast.c
Normal file
1571
tests/bluetooth/tester/src/btp_bap_broadcast.c
Normal file
File diff suppressed because it is too large
Load diff
100
tests/bluetooth/tester/src/btp_bap_broadcast.h
Normal file
100
tests/bluetooth/tester/src/btp_bap_broadcast.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* btp_bap_broadcast.h - Bluetooth BAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
struct btp_bap_broadcast_stream {
|
||||
struct btp_bap_audio_stream audio_stream;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
uint8_t bis_id;
|
||||
uint8_t subgroup_id;
|
||||
bool bis_synced;
|
||||
uint8_t source_id;
|
||||
bool in_use;
|
||||
bool already_sent;
|
||||
};
|
||||
|
||||
/* According to BT spec, a Broadcast Source can configure and establish one or more BIGs,
|
||||
* each containing one or more BISes that are used to transport broadcast Audio Streams.
|
||||
* For each BIG, the Broadcast Source shall generate a Broadcast_ID.
|
||||
* For the time being, let's treat broadcast source as one BIG.
|
||||
*/
|
||||
struct btp_bap_broadcast_remote_source {
|
||||
bt_addr_le_t address;
|
||||
uint32_t broadcast_id;
|
||||
struct btp_bap_broadcast_stream streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
|
||||
struct bt_bap_stream *sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
|
||||
struct bt_bap_broadcast_sink *sink;
|
||||
struct bt_audio_codec_qos qos;
|
||||
/* BIS Index bitfield read from Base */
|
||||
uint32_t bis_index_bitfield;
|
||||
/* BIS Index bitfield read from sync request */
|
||||
uint32_t requested_bis_sync;
|
||||
bool assistant_request;
|
||||
uint8_t sink_broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
|
||||
const struct bt_bap_scan_delegator_recv_state *sink_recv_state;
|
||||
};
|
||||
|
||||
struct btp_bap_broadcast_local_source {
|
||||
uint32_t broadcast_id;
|
||||
struct bt_audio_codec_qos qos;
|
||||
struct btp_bap_broadcast_stream streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
|
||||
struct bt_audio_codec_cfg subgroup_codec_cfg[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
|
||||
/* Only for BTP BAP commands */
|
||||
struct bt_bap_broadcast_source *bap_broadcast;
|
||||
/* Only for BTP CAP commands */
|
||||
struct bt_cap_broadcast_source *cap_broadcast;
|
||||
};
|
||||
|
||||
int btp_bap_broadcast_init(void);
|
||||
struct btp_bap_broadcast_local_source *btp_bap_broadcast_local_source_get(uint8_t source_id);
|
||||
struct btp_bap_broadcast_stream *btp_bap_broadcast_stream_alloc(
|
||||
struct btp_bap_broadcast_local_source *source);
|
||||
|
||||
uint8_t btp_bap_broadcast_source_setup(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_source_release(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_adv_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_adv_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_source_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_source_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_sink_setup(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_sink_release(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_scan_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_scan_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_sink_sync(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_sink_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_sink_bis_sync(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_discover_scan_delegators(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_scan_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_scan_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_add_src(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_remove_src(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_modify_src(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_set_broadcast_code(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_bap_broadcast_assistant_send_past(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
1649
tests/bluetooth/tester/src/btp_bap_unicast.c
Normal file
1649
tests/bluetooth/tester/src/btp_bap_unicast.c
Normal file
File diff suppressed because it is too large
Load diff
71
tests/bluetooth/tester/src/btp_bap_unicast.h
Normal file
71
tests/bluetooth/tester/src/btp_bap_unicast.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* btp_bap_unicast.h - Bluetooth BAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
#define BTP_BAP_UNICAST_MAX_SNK_STREAMS_COUNT MIN(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, \
|
||||
CONFIG_BT_ASCS_ASE_SNK_COUNT)
|
||||
#define BTP_BAP_UNICAST_MAX_SRC_STREAMS_COUNT MIN(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT, \
|
||||
CONFIG_BT_ASCS_ASE_SRC_COUNT)
|
||||
#define BTP_BAP_UNICAST_MAX_STREAMS_COUNT BTP_BAP_UNICAST_MAX_SNK_STREAMS_COUNT + \
|
||||
BTP_BAP_UNICAST_MAX_SRC_STREAMS_COUNT
|
||||
#define BTP_BAP_UNICAST_MAX_END_POINTS_COUNT CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT + \
|
||||
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT
|
||||
|
||||
struct btp_bap_unicast_group {
|
||||
struct bt_audio_codec_qos qos[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
struct bt_bap_unicast_group *cig;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
struct btp_bap_unicast_stream {
|
||||
struct btp_bap_audio_stream audio_stream;
|
||||
uint8_t ase_id;
|
||||
uint8_t conn_id;
|
||||
uint8_t cig_id;
|
||||
uint8_t cis_id;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
bool already_sent;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
struct btp_bap_unicast_connection {
|
||||
bt_addr_le_t address;
|
||||
struct btp_bap_unicast_stream streams[BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
|
||||
size_t configured_sink_stream_count;
|
||||
size_t configured_source_stream_count;
|
||||
struct bt_bap_ep *end_points[BTP_BAP_UNICAST_MAX_END_POINTS_COUNT];
|
||||
size_t end_points_count;
|
||||
};
|
||||
|
||||
int btp_bap_unicast_init(void);
|
||||
struct btp_bap_unicast_connection *btp_bap_unicast_conn_get(size_t conn_index);
|
||||
int btp_bap_unicast_group_create(uint8_t cig_id, struct btp_bap_unicast_group **out_unicast_group);
|
||||
struct btp_bap_unicast_group *btp_bap_unicast_group_find(uint8_t cig_id);
|
||||
struct bt_bap_ep *btp_bap_unicast_end_point_find(struct btp_bap_unicast_connection *conn,
|
||||
uint8_t ase_id);
|
||||
struct btp_bap_unicast_stream *btp_bap_unicast_stream_find(struct btp_bap_unicast_connection *conn,
|
||||
uint8_t ase_id);
|
||||
struct btp_bap_unicast_stream *btp_bap_unicast_stream_alloc(
|
||||
struct btp_bap_unicast_connection *conn);
|
||||
void btp_bap_unicast_stream_free(struct btp_bap_unicast_stream *stream);
|
||||
|
||||
uint8_t btp_bap_discover(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
|
||||
uint8_t btp_ascs_configure_codec(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_configure_qos(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_enable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_receiver_stop_ready(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_disable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_release(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_update_metadata(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_add_ase_to_cis(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
||||
uint8_t btp_ascs_preconfigure_qos(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len);
|
905
tests/bluetooth/tester/src/btp_cap.c
Normal file
905
tests/bluetooth/tester/src/btp_cap.c
Normal file
|
@ -0,0 +1,905 @@
|
|||
/* btp_cap.c - Bluetooth CAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
#include "btp/btp.h"
|
||||
#include "btp_bap_audio_stream.h"
|
||||
#include "bap_endpoint.h"
|
||||
#include "zephyr/sys/byteorder.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#define LOG_MODULE_NAME bttester_cap
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
|
||||
|
||||
#include "btp_bap_unicast.h"
|
||||
#include "btp_bap_broadcast.h"
|
||||
|
||||
extern struct bt_csip_set_coordinator_set_member *btp_csip_set_members[CONFIG_BT_MAX_CONN];
|
||||
|
||||
static struct bt_bap_stream *stream_unicast_to_bap(struct btp_bap_unicast_stream *stream)
|
||||
{
|
||||
return &stream->audio_stream.cap_stream.bap_stream;
|
||||
}
|
||||
|
||||
static struct bt_cap_stream *stream_unicast_to_cap(struct btp_bap_unicast_stream *stream)
|
||||
{
|
||||
return &stream->audio_stream.cap_stream;
|
||||
}
|
||||
|
||||
static struct bt_cap_stream *stream_broadcast_to_cap(struct btp_bap_broadcast_stream *stream)
|
||||
{
|
||||
return &stream->audio_stream.cap_stream;
|
||||
}
|
||||
|
||||
static void btp_send_discovery_completed_ev(struct bt_conn *conn, uint8_t status)
|
||||
{
|
||||
struct btp_cap_discovery_completed_ev ev;
|
||||
|
||||
bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
|
||||
ev.status = status;
|
||||
|
||||
tester_event(BTP_SERVICE_ID_CAP, BTP_CAP_EV_DISCOVERY_COMPLETED, &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
|
||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to discover CAS: %d", err);
|
||||
btp_send_discovery_completed_ev(conn, BTP_CAP_DISCOVERY_STATUS_FAILED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
|
||||
if (csis_inst == NULL) {
|
||||
LOG_DBG("Failed to discover CAS CSIS");
|
||||
btp_send_discovery_completed_ev(conn, BTP_CAP_DISCOVERY_STATUS_FAILED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Found CAS with CSIS %p", csis_inst);
|
||||
} else {
|
||||
LOG_DBG("Found CAS");
|
||||
}
|
||||
|
||||
btp_send_discovery_completed_ev(conn, BTP_CAP_DISCOVERY_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void btp_send_cap_unicast_start_completed_ev(uint8_t cig_id, uint8_t status)
|
||||
{
|
||||
struct btp_cap_unicast_start_completed_ev ev;
|
||||
|
||||
ev.cig_id = cig_id;
|
||||
ev.status = status;
|
||||
|
||||
tester_event(BTP_SERVICE_ID_CAP, BTP_CAP_EV_UNICAST_START_COMPLETED, &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void btp_send_cap_unicast_stop_completed_ev(uint8_t cig_id, uint8_t status)
|
||||
{
|
||||
struct btp_cap_unicast_stop_completed_ev ev;
|
||||
|
||||
ev.cig_id = cig_id;
|
||||
ev.status = status;
|
||||
|
||||
tester_event(BTP_SERVICE_ID_CAP, BTP_CAP_EV_UNICAST_STOP_COMPLETED, &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void unicast_start_complete_cb(struct bt_bap_unicast_group *group,
|
||||
int err, struct bt_conn *conn)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to unicast-start, err %d", err);
|
||||
btp_send_cap_unicast_start_completed_ev(group->index,
|
||||
BTP_CAP_UNICAST_START_STATUS_FAILED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
btp_send_cap_unicast_start_completed_ev(group->index,
|
||||
BTP_CAP_UNICAST_START_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void unicast_update_complete_cb(int err, struct bt_conn *conn)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to unicast-update, err %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void unicast_stop_complete_cb(struct bt_bap_unicast_group *group, int err,
|
||||
struct bt_conn *conn)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to unicast-stop, err %d", err);
|
||||
btp_send_cap_unicast_stop_completed_ev(group->index,
|
||||
BTP_CAP_UNICAST_START_STATUS_FAILED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
btp_send_cap_unicast_stop_completed_ev(group->index,
|
||||
BTP_CAP_UNICAST_START_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
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 uint8_t btp_cap_supported_commands(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
struct btp_cap_read_supported_commands_rp *rp = rsp;
|
||||
|
||||
/* octet 0 */
|
||||
tester_set_bit(rp->data, BTP_CAP_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(rp->data, BTP_CAP_DISCOVER);
|
||||
|
||||
*rsp_len = sizeof(*rp) + 1;
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_discover(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
const struct btp_cap_discover_cmd *cp = cmd;
|
||||
struct bt_conn *conn;
|
||||
int err;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
|
||||
if (!conn) {
|
||||
LOG_ERR("Unknown connection");
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_discover(conn);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to discover remote ASEs: %d", err);
|
||||
bt_conn_unref(conn);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int cap_unicast_setup_ase(struct bt_conn *conn, uint8_t ase_id, uint8_t cis_id,
|
||||
uint8_t cig_id, struct bt_audio_codec_cfg *codec_cfg,
|
||||
struct bt_audio_codec_qos *qos)
|
||||
{
|
||||
struct btp_bap_unicast_group *group;
|
||||
struct btp_bap_unicast_stream *u_stream;
|
||||
struct bt_bap_stream *stream;
|
||||
struct btp_bap_unicast_connection *u_conn = btp_bap_unicast_conn_get(bt_conn_index(conn));
|
||||
|
||||
u_stream = btp_bap_unicast_stream_find(u_conn, ase_id);
|
||||
if (u_stream == NULL) {
|
||||
/* Configure a new u_stream */
|
||||
u_stream = btp_bap_unicast_stream_alloc(u_conn);
|
||||
if (u_stream == NULL) {
|
||||
LOG_DBG("No streams available");
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
stream = stream_unicast_to_bap(u_stream);
|
||||
bt_cap_stream_ops_register(&u_stream->audio_stream.cap_stream, stream->ops);
|
||||
|
||||
u_stream->conn_id = bt_conn_index(conn);
|
||||
u_stream->ase_id = ase_id;
|
||||
u_stream->cig_id = cig_id;
|
||||
u_stream->cis_id = cis_id;
|
||||
memcpy(&u_stream->codec_cfg, codec_cfg, sizeof(*codec_cfg));
|
||||
|
||||
group = btp_bap_unicast_group_find(cig_id);
|
||||
if (group == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&group->qos[cis_id], qos, sizeof(*qos));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_unicast_setup_ase(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
const struct btp_cap_unicast_setup_ase_cmd *cp = cmd;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
struct bt_audio_codec_qos qos;
|
||||
struct bt_conn *conn;
|
||||
const uint8_t *ltv_ptr;
|
||||
int err;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
|
||||
if (!conn) {
|
||||
LOG_ERR("Unknown connection");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
memset(&qos, 0, sizeof(qos));
|
||||
qos.phy = BT_AUDIO_CODEC_QOS_2M;
|
||||
qos.framing = cp->framing;
|
||||
qos.rtn = cp->retransmission_num;
|
||||
qos.sdu = sys_le16_to_cpu(cp->max_sdu);
|
||||
qos.latency = sys_le16_to_cpu(cp->max_transport_latency);
|
||||
qos.interval = sys_get_le24(cp->sdu_interval);
|
||||
qos.pd = sys_get_le24(cp->presentation_delay);
|
||||
|
||||
memset(&codec_cfg, 0, sizeof(codec_cfg));
|
||||
codec_cfg.id = cp->coding_format;
|
||||
codec_cfg.vid = cp->vid;
|
||||
codec_cfg.cid = cp->cid;
|
||||
|
||||
ltv_ptr = cp->ltvs;
|
||||
if (cp->cc_ltvs_len != 0) {
|
||||
codec_cfg.data_len = cp->cc_ltvs_len;
|
||||
memcpy(codec_cfg.data, ltv_ptr, cp->cc_ltvs_len);
|
||||
ltv_ptr += cp->cc_ltvs_len;
|
||||
}
|
||||
|
||||
if (cp->metadata_ltvs_len != 0) {
|
||||
codec_cfg.meta_len = cp->metadata_ltvs_len;
|
||||
memcpy(codec_cfg.meta, ltv_ptr, cp->metadata_ltvs_len);
|
||||
}
|
||||
|
||||
err = cap_unicast_setup_ase(conn, cp->ase_id, cp->cis_id, cp->cig_id, &codec_cfg, &qos);
|
||||
bt_conn_unref(conn);
|
||||
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_unicast_audio_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
size_t stream_count = 0;
|
||||
struct btp_bap_unicast_group *u_group;
|
||||
const struct btp_cap_unicast_audio_start_cmd *cp = cmd;
|
||||
struct bt_cap_unicast_audio_start_param start_param;
|
||||
struct bt_cap_unicast_audio_start_stream_param stream_params[
|
||||
ARRAY_SIZE(btp_csip_set_members) * BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
err = btp_bap_unicast_group_create(cp->cig_id, &u_group);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to create unicast group");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
for (size_t conn_index = 0; conn_index < ARRAY_SIZE(btp_csip_set_members); conn_index++) {
|
||||
struct btp_bap_unicast_connection *u_conn = btp_bap_unicast_conn_get(conn_index);
|
||||
|
||||
if (u_conn->end_points_count == 0) {
|
||||
/* Connection not initialized */
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(u_conn->streams); i++) {
|
||||
struct bt_cap_unicast_audio_start_stream_param *stream_param;
|
||||
struct btp_bap_unicast_stream *u_stream = &u_conn->streams[i];
|
||||
|
||||
if (!u_stream->in_use || u_stream->cig_id != cp->cig_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stream_param = &stream_params[stream_count++];
|
||||
stream_param->stream = stream_unicast_to_cap(u_stream);
|
||||
stream_param->codec_cfg = &u_stream->codec_cfg;
|
||||
stream_param->member.member = bt_conn_lookup_addr_le(BT_ID_DEFAULT,
|
||||
&u_conn->address);
|
||||
stream_param->ep = btp_bap_unicast_end_point_find(u_conn, u_stream->ase_id);
|
||||
}
|
||||
}
|
||||
|
||||
start_param.type = cp->set_type;
|
||||
start_param.count = stream_count;
|
||||
start_param.stream_params = stream_params;
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_start(&start_param, u_group->cig);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to start unicast audio: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_unicast_audio_update(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
const uint8_t *data_ptr;
|
||||
const struct btp_cap_unicast_audio_update_cmd *cp = cmd;
|
||||
struct bt_cap_unicast_audio_update_param stream_params[
|
||||
ARRAY_SIZE(btp_csip_set_members) * BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (cp->stream_count == 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
data_ptr = cp->update_data;
|
||||
for (size_t i = 0; i < cp->stream_count; i++) {
|
||||
struct btp_bap_unicast_connection *u_conn;
|
||||
struct btp_bap_unicast_stream *u_stream;
|
||||
struct bt_conn *conn;
|
||||
struct btp_cap_unicast_audio_update_data *update_data =
|
||||
(struct btp_cap_unicast_audio_update_data *)data_ptr;
|
||||
struct bt_cap_unicast_audio_update_param *param = &stream_params[i];
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &update_data->address);
|
||||
if (!conn) {
|
||||
LOG_ERR("Unknown connection");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
u_conn = btp_bap_unicast_conn_get(bt_conn_index(conn));
|
||||
bt_conn_unref(conn);
|
||||
if (u_conn->end_points_count == 0) {
|
||||
/* Connection not initialized */
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
u_stream = btp_bap_unicast_stream_find(u_conn, update_data->ase_id);
|
||||
if (u_stream == NULL) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
param->stream = &u_stream->audio_stream.cap_stream;
|
||||
param->meta_len = update_data->metadata_ltvs_len;
|
||||
param->meta = update_data->metadata_ltvs;
|
||||
|
||||
data_ptr = ((uint8_t *)update_data) + param->meta_len +
|
||||
sizeof(struct btp_cap_unicast_audio_update_data);
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_update(stream_params, cp->stream_count);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to start unicast audio: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_unicast_audio_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
|
||||
int err;
|
||||
const struct btp_cap_unicast_audio_stop_cmd *cp = cmd;
|
||||
struct btp_bap_unicast_group *group;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
group = btp_bap_unicast_group_find(cp->cig_id);
|
||||
|
||||
err = bt_cap_initiator_unicast_audio_stop(group->cig);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to start unicast audio: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static struct bt_cap_initiator_broadcast_subgroup_param
|
||||
cap_subgroup_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
|
||||
static struct bt_cap_initiator_broadcast_stream_param
|
||||
cap_stream_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT]
|
||||
[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_setup_stream(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
const uint8_t *ltv_ptr;
|
||||
struct btp_bap_broadcast_stream *stream;
|
||||
const struct btp_cap_broadcast_source_setup_stream_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
struct bt_audio_codec_cfg *codec_cfg;
|
||||
|
||||
stream = btp_bap_broadcast_stream_alloc(source);
|
||||
if (stream == NULL) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
stream->subgroup_id = cp->subgroup_id;
|
||||
codec_cfg = &stream->codec_cfg;
|
||||
memset(codec_cfg, 0, sizeof(*codec_cfg));
|
||||
codec_cfg->id = cp->coding_format;
|
||||
codec_cfg->vid = cp->vid;
|
||||
codec_cfg->cid = cp->cid;
|
||||
|
||||
ltv_ptr = cp->ltvs;
|
||||
if (cp->cc_ltvs_len != 0) {
|
||||
codec_cfg->data_len = cp->cc_ltvs_len;
|
||||
memcpy(codec_cfg->data, ltv_ptr, cp->cc_ltvs_len);
|
||||
ltv_ptr += cp->cc_ltvs_len;
|
||||
}
|
||||
|
||||
if (cp->metadata_ltvs_len != 0) {
|
||||
codec_cfg->meta_len = cp->metadata_ltvs_len;
|
||||
memcpy(codec_cfg->meta, ltv_ptr, cp->metadata_ltvs_len);
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_setup_subgroup(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
const uint8_t *ltv_ptr;
|
||||
struct bt_audio_codec_cfg *codec_cfg;
|
||||
const struct btp_cap_broadcast_source_setup_subgroup_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
|
||||
if (cp->subgroup_id >= sizeof(cap_subgroup_params)) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
cap_subgroup_params[cp->subgroup_id].codec_cfg =
|
||||
&source->subgroup_codec_cfg[cp->subgroup_id];
|
||||
codec_cfg = cap_subgroup_params[cp->subgroup_id].codec_cfg;
|
||||
memset(codec_cfg, 0, sizeof(*codec_cfg));
|
||||
codec_cfg->id = cp->coding_format;
|
||||
codec_cfg->vid = cp->vid;
|
||||
codec_cfg->cid = cp->cid;
|
||||
|
||||
ltv_ptr = cp->ltvs;
|
||||
if (cp->cc_ltvs_len != 0) {
|
||||
codec_cfg->data_len = cp->cc_ltvs_len;
|
||||
memcpy(codec_cfg->data, ltv_ptr, cp->cc_ltvs_len);
|
||||
ltv_ptr += cp->cc_ltvs_len;
|
||||
}
|
||||
|
||||
if (cp->metadata_ltvs_len != 0) {
|
||||
codec_cfg->meta_len = cp->metadata_ltvs_len;
|
||||
memcpy(codec_cfg->meta, ltv_ptr, cp->metadata_ltvs_len);
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int cap_broadcast_source_adv_setup(struct btp_bap_broadcast_local_source *source,
|
||||
uint32_t *gap_settings)
|
||||
{
|
||||
int err;
|
||||
struct bt_le_adv_param *param = BT_LE_EXT_ADV_NCONN_NAME;
|
||||
|
||||
NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
|
||||
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
|
||||
|
||||
/* Broadcast Audio Streaming Endpoint advertising data */
|
||||
struct bt_data base_ad;
|
||||
struct bt_data per_ad;
|
||||
|
||||
err = bt_cap_initiator_broadcast_get_id(source->cap_broadcast, &source->broadcast_id);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Unable to get broadcast ID: %d", err);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*gap_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
|
||||
BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
|
||||
/* Setup extended advertising data */
|
||||
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
|
||||
net_buf_simple_add_le24(&ad_buf, source->broadcast_id);
|
||||
base_ad.type = BT_DATA_SVC_DATA16;
|
||||
base_ad.data_len = ad_buf.len;
|
||||
base_ad.data = ad_buf.data;
|
||||
err = tester_gap_create_adv_instance(param, BTP_GAP_ADDR_TYPE_IDENTITY, &base_ad, 1, NULL,
|
||||
0, gap_settings);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to create extended advertising instance: %d", err);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = tester_gap_padv_configure(BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
|
||||
BT_GAP_PER_ADV_FAST_INT_MAX_2,
|
||||
BT_LE_PER_ADV_OPT_USE_TX_POWER));
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to configure periodic advertising: %d", err);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_get_base(source->cap_broadcast, &base_buf);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to get encoded BASE: %d\n", err);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
per_ad.type = BT_DATA_SVC_DATA16;
|
||||
per_ad.data_len = base_buf.len;
|
||||
per_ad.data = base_buf.data;
|
||||
err = tester_gap_padv_set_data(&per_ad, 1);
|
||||
if (err != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_setup(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
uint32_t gap_settings;
|
||||
static struct bt_cap_initiator_broadcast_create_param create_param;
|
||||
const struct btp_cap_broadcast_source_setup_cmd *cp = cmd;
|
||||
struct btp_cap_broadcast_source_setup_rp *rp = rsp;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
struct bt_audio_codec_qos *qos = &source->qos;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
memset(&create_param, 0, sizeof(create_param));
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(source->streams); i++) {
|
||||
struct btp_bap_broadcast_stream *stream = &source->streams[i];
|
||||
struct bt_cap_initiator_broadcast_stream_param *stream_param;
|
||||
struct bt_cap_initiator_broadcast_subgroup_param *subgroup_param;
|
||||
uint8_t bis_id;
|
||||
|
||||
if (!stream->in_use) {
|
||||
/* No more streams set up */
|
||||
break;
|
||||
}
|
||||
|
||||
subgroup_param = &cap_subgroup_params[stream->subgroup_id];
|
||||
bis_id = subgroup_param->stream_count++;
|
||||
stream_param = &cap_stream_params[stream->subgroup_id][bis_id];
|
||||
stream_param->stream = stream_broadcast_to_cap(stream);
|
||||
|
||||
if (cp->flags & BTP_CAP_BROADCAST_SOURCE_SETUP_FLAG_SUBGROUP_CODEC) {
|
||||
stream_param->data_len = 0;
|
||||
stream_param->data = NULL;
|
||||
} else {
|
||||
stream_param->data_len = stream->codec_cfg.data_len;
|
||||
stream_param->data = stream->codec_cfg.data;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(cap_subgroup_params); i++) {
|
||||
if (cap_subgroup_params[i].stream_count == 0) {
|
||||
/* No gaps allowed */
|
||||
break;
|
||||
}
|
||||
|
||||
cap_subgroup_params[i].stream_params = cap_stream_params[i];
|
||||
create_param.subgroup_count++;
|
||||
}
|
||||
|
||||
if (create_param.subgroup_count == 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
memset(qos, 0, sizeof(*qos));
|
||||
qos->phy = BT_AUDIO_CODEC_QOS_2M;
|
||||
qos->framing = cp->framing;
|
||||
qos->rtn = cp->retransmission_num;
|
||||
qos->sdu = sys_le16_to_cpu(cp->max_sdu);
|
||||
qos->latency = sys_le16_to_cpu(cp->max_transport_latency);
|
||||
qos->interval = sys_get_le24(cp->sdu_interval);
|
||||
qos->pd = sys_get_le24(cp->presentation_delay);
|
||||
|
||||
create_param.subgroup_params = cap_subgroup_params;
|
||||
create_param.qos = qos;
|
||||
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
|
||||
create_param.encryption = cp->flags & BTP_CAP_BROADCAST_SOURCE_SETUP_FLAG_ENCRYPTION;
|
||||
memcpy(create_param.broadcast_code, cp->broadcast_code, sizeof(cp->broadcast_code));
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_create(&create_param, &source->cap_broadcast);
|
||||
memset(cap_subgroup_params, 0, sizeof(cap_subgroup_params));
|
||||
memset(&create_param, 0, sizeof(create_param));
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to create audio source: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = cap_broadcast_source_adv_setup(source, &gap_settings);
|
||||
if (err != 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
rp->gap_settings = gap_settings;
|
||||
sys_put_le24(source->broadcast_id, rp->broadcast_id);
|
||||
*rsp_len = sizeof(*rp) + 1;
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_release(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
const struct btp_cap_broadcast_source_release_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_delete(source->cap_broadcast);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Unable to delete broadcast source: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
memset(source, 0, sizeof(*source));
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_adv_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
struct bt_le_ext_adv *ext_adv = tester_gap_ext_adv_get();
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (ext_adv == NULL) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = tester_gap_start_ext_adv();
|
||||
if (err != 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = tester_gap_padv_start();
|
||||
if (err != 0) {
|
||||
LOG_DBG("Unable to start periodic advertising: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_adv_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
err = tester_gap_padv_stop();
|
||||
if (err != 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = tester_gap_stop_ext_adv();
|
||||
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_start(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
const struct btp_cap_broadcast_source_start_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
struct bt_le_ext_adv *ext_adv = tester_gap_ext_adv_get();
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (ext_adv == NULL) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_start(source->cap_broadcast, ext_adv);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to start audio source: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_stop(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
const struct btp_cap_broadcast_source_stop_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_stop(source->cap_broadcast);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to stop audio source: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t btp_cap_broadcast_source_update(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
struct bt_data per_ad;
|
||||
const struct btp_cap_broadcast_source_update_cmd *cp = cmd;
|
||||
struct btp_bap_broadcast_local_source *source =
|
||||
btp_bap_broadcast_local_source_get(cp->source_id);
|
||||
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (cp->metadata_ltvs_len == 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_update(source->cap_broadcast, cp->metadata_ltvs,
|
||||
cp->metadata_ltvs_len);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to update audio source: %d", err);
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_get_base(source->cap_broadcast, &base_buf);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to get encoded BASE: %d\n", err);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
per_ad.type = BT_DATA_SVC_DATA16;
|
||||
per_ad.data_len = base_buf.len;
|
||||
per_ad.data = base_buf.data;
|
||||
err = tester_gap_padv_set_data(&per_ad, 1);
|
||||
if (err != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const struct btp_handler cap_handlers[] = {
|
||||
{
|
||||
.opcode = BTP_CAP_READ_SUPPORTED_COMMANDS,
|
||||
.index = BTP_INDEX_NONE,
|
||||
.expect_len = 0,
|
||||
.func = btp_cap_supported_commands
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_DISCOVER,
|
||||
.expect_len = sizeof(struct btp_cap_discover_cmd),
|
||||
.func = btp_cap_discover
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_UNICAST_SETUP_ASE,
|
||||
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||
.func = btp_cap_unicast_setup_ase
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_UNICAST_AUDIO_START,
|
||||
.expect_len = sizeof(struct btp_cap_unicast_audio_start_cmd),
|
||||
.func = btp_cap_unicast_audio_start
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_UNICAST_AUDIO_UPDATE,
|
||||
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||
.func = btp_cap_unicast_audio_update
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_UNICAST_AUDIO_STOP,
|
||||
.expect_len = sizeof(struct btp_cap_unicast_audio_stop_cmd),
|
||||
.func = btp_cap_unicast_audio_stop
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_SETUP_STREAM,
|
||||
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||
.func = btp_cap_broadcast_source_setup_stream
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_SETUP_SUBGROUP,
|
||||
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||
.func = btp_cap_broadcast_source_setup_subgroup
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_SETUP,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_source_setup_cmd),
|
||||
.func = btp_cap_broadcast_source_setup
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_RELEASE,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_source_release_cmd),
|
||||
.func = btp_cap_broadcast_source_release
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_ADV_START,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_adv_start_cmd),
|
||||
.func = btp_cap_broadcast_adv_start
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_ADV_STOP,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_adv_stop_cmd),
|
||||
.func = btp_cap_broadcast_adv_stop
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_START,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_source_start_cmd),
|
||||
.func = btp_cap_broadcast_source_start
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_STOP,
|
||||
.expect_len = sizeof(struct btp_cap_broadcast_source_stop_cmd),
|
||||
.func = btp_cap_broadcast_source_stop
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CAP_BROADCAST_SOURCE_UPDATE,
|
||||
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||
.func = btp_cap_broadcast_source_update
|
||||
},
|
||||
};
|
||||
|
||||
uint8_t tester_init_cap(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_initiator_register_cb(&cap_cb);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to register CAP callbacks (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tester_register_command_handlers(BTP_SERVICE_ID_CAP, cap_handlers,
|
||||
ARRAY_SIZE(cap_handlers));
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t tester_unregister_cap(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
|
@ -194,6 +194,11 @@ static uint8_t register_service(const void *cmd, uint16_t cmd_len,
|
|||
status = tester_init_csis();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
|
||||
#if defined(CONFIG_BT_CSIP_SET_COORDINATOR)
|
||||
case BTP_SERVICE_ID_CSIP:
|
||||
status = tester_init_csip();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CSIP_SET_COORDINATOR */
|
||||
#if defined(CONFIG_BT_TBS_CLIENT)
|
||||
case BTP_SERVICE_ID_CCP:
|
||||
status = tester_init_ccp();
|
||||
|
@ -204,6 +209,11 @@ static uint8_t register_service(const void *cmd, uint16_t cmd_len,
|
|||
status = tester_init_cas();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CAP_ACCEPTOR */
|
||||
#if defined(CONFIG_BT_CAP_INITIATOR)
|
||||
case BTP_SERVICE_ID_CAP:
|
||||
status = tester_init_cap();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CAP_INITIATOR */
|
||||
#if defined(CONFIG_BT_MCC)
|
||||
case BTP_SERVICE_ID_MCP:
|
||||
status = tester_init_mcp();
|
||||
|
@ -316,6 +326,11 @@ static uint8_t unregister_service(const void *cmd, uint16_t cmd_len,
|
|||
status = tester_unregister_csis();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
|
||||
#if defined(CONFIG_BT_CSIP_SET_COORDINATOR)
|
||||
case BTP_SERVICE_ID_CSIP:
|
||||
status = tester_unregister_csip();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CSIP_SET_COORDINATOR */
|
||||
#if defined(CONFIG_BT_TBS_CLIENT)
|
||||
case BTP_SERVICE_ID_CCP:
|
||||
status = tester_unregister_ccp();
|
||||
|
@ -326,6 +341,11 @@ static uint8_t unregister_service(const void *cmd, uint16_t cmd_len,
|
|||
status = tester_unregister_cas();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CAP_ACCEPTOR */
|
||||
#if defined(CONFIG_BT_CAP_INITIATOR)
|
||||
case BTP_SERVICE_ID_CAP:
|
||||
status = tester_unregister_cap();
|
||||
break;
|
||||
#endif /* CONFIG_BT_CAP_INITIATOR */
|
||||
#if defined(CONFIG_BT_MCC)
|
||||
case BTP_SERVICE_ID_MCP:
|
||||
status = tester_unregister_mcp();
|
||||
|
|
204
tests/bluetooth/tester/src/btp_csip.c
Normal file
204
tests/bluetooth/tester/src/btp_csip.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
/* btp_csip.c - Bluetooth CSIP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Codecoup
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "btp/btp.h"
|
||||
#include <zephyr/bluetooth/audio/csip.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#define LOG_MODULE_NAME bttester_csip
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
|
||||
|
||||
const struct bt_csip_set_coordinator_set_member *btp_csip_set_members[CONFIG_BT_MAX_CONN];
|
||||
static const struct bt_csip_set_coordinator_csis_inst *cur_csis_inst;
|
||||
|
||||
static uint8_t btp_csip_supported_commands(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
struct btp_csip_read_supported_commands_rp *rp = rsp;
|
||||
|
||||
/* octet 0 */
|
||||
tester_set_bit(rp->data, BTP_CSIP_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(rp->data, BTP_CSIP_START_ORDERED_ACCESS);
|
||||
|
||||
*rsp_len = sizeof(*rp) + 1;
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void csip_set_coordinator_lock_set_cb(int err)
|
||||
{
|
||||
LOG_DBG("");
|
||||
}
|
||||
|
||||
static void csip_set_coordinator_lock_release_cb(int err)
|
||||
{
|
||||
LOG_DBG("");
|
||||
}
|
||||
|
||||
static void csip_discover_cb(struct bt_conn *conn,
|
||||
const struct bt_csip_set_coordinator_set_member *member,
|
||||
int err, size_t set_count)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
uint8_t conn_index;
|
||||
|
||||
if (err != 0) {
|
||||
LOG_DBG("discover failed (%d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set_count == 0) {
|
||||
LOG_DBG("Device has no sets");
|
||||
return;
|
||||
}
|
||||
|
||||
conn_index = bt_conn_index(conn);
|
||||
|
||||
LOG_DBG("Found %zu sets on member[%u]", set_count, conn_index);
|
||||
|
||||
for (size_t i = 0U; i < set_count; i++) {
|
||||
LOG_DBG("CSIS[%zu]: %p", i, &member->insts[i]);
|
||||
LOG_DBG("Rank: %u", member->insts[i].info.rank);
|
||||
LOG_DBG("Set Size: %u", member->insts[i].info.set_size);
|
||||
LOG_DBG("Lockable: %u", member->insts[i].info.lockable);
|
||||
}
|
||||
|
||||
cur_csis_inst = &member->insts[0];
|
||||
btp_csip_set_members[conn_index] = member;
|
||||
}
|
||||
|
||||
static void csip_lock_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst,
|
||||
bool locked)
|
||||
{
|
||||
LOG_DBG("");
|
||||
}
|
||||
|
||||
static void csip_set_coordinator_ordered_access_cb(
|
||||
const struct bt_csip_set_coordinator_set_info *set_info, int err,
|
||||
bool locked, struct bt_csip_set_coordinator_set_member *member)
|
||||
{
|
||||
LOG_DBG("");
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Ordered access failed with err %d", err);
|
||||
} else if (locked) {
|
||||
LOG_DBG("Ordered access procedure locked member %p", member);
|
||||
} else {
|
||||
LOG_DBG("Ordered access procedure finished");
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_csip_set_coordinator_cb set_coordinator_cbs = {
|
||||
.lock_set = csip_set_coordinator_lock_set_cb,
|
||||
.release_set = csip_set_coordinator_lock_release_cb,
|
||||
.discover = csip_discover_cb,
|
||||
.lock_changed = csip_lock_changed_cb,
|
||||
.ordered_access = csip_set_coordinator_ordered_access_cb
|
||||
};
|
||||
|
||||
static bool csip_set_coordinator_oap_cb(const struct bt_csip_set_coordinator_set_info *set_info,
|
||||
struct bt_csip_set_coordinator_set_member *members[],
|
||||
size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
LOG_DBG("Ordered access for members[%zu]: %p", i, members[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t btp_csip_discover(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
int err;
|
||||
struct bt_conn *conn;
|
||||
const struct btp_csip_discover_cmd *cp = cmd;
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
|
||||
if (!conn) {
|
||||
LOG_ERR("Unknown connection");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_csip_set_coordinator_discover(conn);
|
||||
bt_conn_unref(conn);
|
||||
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
static uint8_t btp_csip_start_ordered_access(const void *cmd, uint16_t cmd_len,
|
||||
void *rsp, uint16_t *rsp_len)
|
||||
{
|
||||
const struct bt_csip_set_coordinator_set_member *members[ARRAY_SIZE(btp_csip_set_members)];
|
||||
unsigned long member_count = 0;
|
||||
int err;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (cur_csis_inst == NULL) {
|
||||
LOG_ERR("No CISP instance available");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (size_t)ARRAY_SIZE(btp_csip_set_members); i++) {
|
||||
if (btp_csip_set_members[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
members[member_count++] = btp_csip_set_members[i];
|
||||
}
|
||||
|
||||
if (member_count == 0) {
|
||||
LOG_ERR("No set members available");
|
||||
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
err = bt_csip_set_coordinator_ordered_access(members,
|
||||
member_count,
|
||||
&cur_csis_inst->info,
|
||||
csip_set_coordinator_oap_cb);
|
||||
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
static const struct btp_handler csip_handlers[] = {
|
||||
{
|
||||
.opcode = BTP_CSIP_READ_SUPPORTED_COMMANDS,
|
||||
.index = BTP_INDEX_NONE,
|
||||
.expect_len = 0,
|
||||
.func = btp_csip_supported_commands
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CSIP_DISCOVER,
|
||||
.expect_len = sizeof(struct btp_csip_discover_cmd),
|
||||
.func = btp_csip_discover
|
||||
},
|
||||
{
|
||||
.opcode = BTP_CSIP_START_ORDERED_ACCESS,
|
||||
.expect_len = sizeof(struct btp_csip_start_ordered_access_cmd),
|
||||
.func = btp_csip_start_ordered_access
|
||||
},
|
||||
};
|
||||
|
||||
uint8_t tester_init_csip(void)
|
||||
{
|
||||
bt_csip_set_coordinator_register_cb(&set_coordinator_cbs);
|
||||
|
||||
tester_register_command_handlers(BTP_SERVICE_ID_CSIP, csip_handlers,
|
||||
ARRAY_SIZE(csip_handlers));
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t tester_unregister_csip(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
|
@ -616,6 +616,11 @@ int tester_gap_create_adv_instance(struct bt_le_adv_param *param, uint8_t own_ad
|
|||
BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) {
|
||||
param->options |= BT_LE_ADV_OPT_EXT_ADV;
|
||||
if (ext_adv != NULL) {
|
||||
err = bt_le_ext_adv_stop(ext_adv);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_le_ext_adv_delete(ext_adv);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
|
@ -1563,11 +1568,8 @@ static uint8_t padv_set_data(const void *cmd, uint16_t cmd_len,
|
|||
}
|
||||
|
||||
err = tester_gap_padv_set_data(padv, padv_len);
|
||||
if (err != 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
int tester_gap_padv_create_sync(struct bt_le_per_adv_sync_param *create_params)
|
||||
|
@ -1625,11 +1627,8 @@ static uint8_t padv_create_sync(const void *cmd, uint16_t cmd_len,
|
|||
}
|
||||
|
||||
err = tester_gap_padv_create_sync(&create_params);
|
||||
if (err != 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
return BTP_STATUS_VAL(err);
|
||||
}
|
||||
|
||||
static uint8_t padv_sync_transfer_set_info(const void *cmd, uint16_t cmd_len,
|
||||
|
|
Loading…
Reference in a new issue