bluetooth: tester: bap: Add support for AC 7(i) tests

Allow to assing end points to CISes before creating a CIG. Previously
the end points were assinged top-down, so the configuration 7(i) and
other similar were not covered.

Signed-off-by: Magdalena Kasenberg <magdalena.kasenberg@codecoup.pl>
This commit is contained in:
Magdalena Kasenberg 2023-07-04 13:51:00 +02:00 committed by Carles Cufí
parent f34fff91bc
commit 927168fb5a
2 changed files with 208 additions and 34 deletions

View File

@ -73,6 +73,14 @@ struct btp_ascs_update_metadata_cmd {
uint8_t ase_id;
} __packed;
#define BTP_ASCS_ADD_ASE_TO_CIS 0x0a
struct btp_ascs_add_ase_to_cis {
bt_addr_le_t address;
uint8_t ase_id;
uint8_t cig_id;
uint8_t cis_id;
} __packed;
/* ASCS events */
#define BTP_ASCS_EV_OPERATION_COMPLETED 0x80
struct btp_ascs_operation_completed_ev {
@ -85,5 +93,14 @@ struct btp_ascs_operation_completed_ev {
uint8_t flags;
} __packed;
#define BTP_ASCS_EV_CHARACTERISTIC_SUBSCRIBED 0x81
#define BTP_ASCS_EV_ASE_STATE_CHANGED 0x82
struct btp_ascs_ase_state_changed_ev {
bt_addr_le_t address;
uint8_t ase_id;
uint8_t state;
} __packed;
#define BTP_ASCS_STATUS_SUCCESS 0x00
#define BTP_ASCS_STATUS_FAILED 0x01

View File

@ -56,6 +56,10 @@ struct audio_stream {
size_t len_to_send;
struct k_work_delayable audio_clock_work;
struct k_work_delayable audio_send_work;
uint8_t cig_id;
uint8_t cis_id;
struct bt_bap_unicast_group **cig;
bool already_sent;
};
#define MAX_STREAMS_COUNT MAX(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, \
@ -69,11 +73,12 @@ struct audio_connection {
size_t configured_source_stream_count;
struct bt_audio_codec_cfg codec_cfg;
struct bt_audio_codec_qos qos;
struct bt_bap_unicast_group *unicast_group;
struct bt_bap_ep *end_points[MAX_END_POINTS_COUNT];
size_t end_points_count;
} connections[CONFIG_BT_MAX_CONN];
static struct bt_bap_unicast_group *cigs[CONFIG_BT_ISO_MAX_CIG];
static struct bt_audio_codec_qos_pref qos_pref =
BT_AUDIO_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 10000, 40000, 10000, 40000);
@ -84,8 +89,6 @@ NET_BUF_POOL_FIXED_DEFINE(tx_pool, MAX(CONFIG_BT_ASCS_ASE_SRC_COUNT,
static struct net_buf_simple *rx_ev_buf = NET_BUF_SIMPLE(CONFIG_BT_ISO_RX_MTU +
sizeof(struct btp_bap_stream_received_ev));
static bool already_sent;
RING_BUF_DECLARE(audio_ring_buf, CONFIG_BT_ISO_TX_MTU);
static void audio_clock_timeout(struct k_work *work);
static void audio_send_timeout(struct k_work *work);
@ -222,6 +225,19 @@ static void btp_send_ascs_operation_completed_ev(struct bt_conn *conn, uint8_t a
tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_OPERATION_COMPLETED, &ev, sizeof(ev));
}
static void btp_send_ascs_ase_state_changed_ev(struct bt_conn *conn, uint8_t ase_id, uint8_t state)
{
struct btp_ascs_ase_state_changed_ev ev;
struct bt_conn_info info;
(void)bt_conn_get_info(conn, &info);
bt_addr_le_copy(&ev.address, info.le.dst);
ev.ase_id = ase_id;
ev.state = state;
tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_ASE_STATE_CHANGED, &ev, sizeof(ev));
}
static int validate_codec_parameters(const struct bt_audio_codec_cfg *codec_cfg)
{
int freq_hz;
@ -528,6 +544,13 @@ static void stream_disabled(struct bt_bap_stream *stream)
struct audio_stream *a_stream = CONTAINER_OF(stream, struct audio_stream, stream);
LOG_DBG("Disabled stream %p", stream);
if (stream->ep->dir == BT_AUDIO_DIR_SINK) {
/* Stop send timer */
k_work_cancel_delayable(&a_stream->audio_clock_work);
k_work_cancel_delayable(&a_stream->audio_send_work);
}
btp_send_ascs_operation_completed_ev(stream->conn, a_stream->ase_id,
BT_ASCS_DISABLE_OP, BTP_ASCS_STATUS_SUCCESS);
}
@ -541,10 +564,17 @@ static void stream_released(struct bt_bap_stream *stream)
audio_conn = &connections[a_stream->conn_id];
if (audio_conn->unicast_group != NULL) {
if (stream->ep->dir == BT_AUDIO_DIR_SINK) {
/* Stop send timer */
k_work_cancel_delayable(&a_stream->audio_clock_work);
k_work_cancel_delayable(&a_stream->audio_send_work);
}
if (cigs[stream->ep->cig_id] != NULL) {
/* The unicast group will be deleted only at release of the last stream */
LOG_DBG("Deleting unicast group");
int err = bt_bap_unicast_group_delete(audio_conn->unicast_group);
int err = bt_bap_unicast_group_delete(cigs[stream->ep->cig_id]);
if (err != 0) {
LOG_DBG("Unable to delete unicast group: %d", err);
@ -552,7 +582,7 @@ static void stream_released(struct bt_bap_stream *stream)
return;
}
audio_conn->unicast_group = NULL;
cigs[stream->ep->cig_id] = NULL;
}
a_stream->ase_id = 0;
@ -564,6 +594,8 @@ static void stream_started(struct bt_bap_stream *stream)
struct bt_bap_ep_info info;
int err;
/* Callback called on transition to Streaming state */
LOG_DBG("Started stream %p", stream);
err = bt_bap_ep_get_info(stream->ep, &info);
@ -587,8 +619,8 @@ static void stream_started(struct bt_bap_stream *stream)
K_USEC(a_stream->stream.qos->interval));
}
btp_send_ascs_operation_completed_ev(stream->conn, a_stream->ase_id,
BT_ASCS_START_OP, BTP_STATUS_SUCCESS);
btp_send_ascs_ase_state_changed_ev(stream->conn, a_stream->ase_id,
stream->ep->status.state);
}
static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
@ -597,7 +629,7 @@ static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
LOG_DBG("Stopped stream %p with reason 0x%02X", stream, reason);
if (stream->dir == BT_AUDIO_DIR_SINK) {
if (stream->ep->dir == BT_AUDIO_DIR_SINK) {
/* Stop send timer */
k_work_cancel_delayable(&a_stream->audio_clock_work);
k_work_cancel_delayable(&a_stream->audio_send_work);
@ -611,12 +643,14 @@ static void stream_recv(struct bt_bap_stream *stream,
const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
if (already_sent == false) {
struct audio_stream *a_stream = CONTAINER_OF(stream, struct audio_stream, stream);
if (a_stream->already_sent == false) {
/* For now, send just a first packet, to limit the number
* of logs and not unnecessarily spam through btp.
*/
LOG_DBG("Incoming audio on stream %p len %u", stream, buf->len);
already_sent = true;
a_stream->already_sent = true;
btp_send_stream_received_ev(stream->conn, stream->ep, buf->len, buf->data);
}
}
@ -749,8 +783,15 @@ static void enable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rs
static void start_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
struct audio_stream *a_stream = CONTAINER_OF(stream, struct audio_stream, stream);
/* Callback called on Receiver Start Ready notification from ASE Control Point */
LOG_DBG("stream %p start operation rsp_code %u reason %u", stream, rsp_code, reason);
already_sent = false;
a_stream->already_sent = false;
btp_send_ascs_operation_completed_ev(stream->conn, a_stream->ase_id,
BT_ASCS_START_OP, BTP_STATUS_SUCCESS);
}
static void stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
@ -972,7 +1013,7 @@ static void audio_send_timeout(struct k_work *work)
BT_ISO_TIMESTAMP_NONE);
if (err != 0) {
LOG_ERR("Failed to send audio data to stream: ase_id %d dir seq %d %d err %d",
stream->ase_id, stream->stream.dir, stream->last_req_seq_num, err);
stream->ase_id, stream->stream.ep->dir, stream->last_req_seq_num, err);
net_buf_unref(buf);
}
@ -1009,7 +1050,7 @@ static uint8_t bap_send(const void *cmd, uint16_t cmd_len,
(void)bt_conn_get_info(conn, &conn_info);
stream = stream_find(audio_conn, cp->ase_id);
if (stream == NULL || stream->stream.dir != BT_AUDIO_DIR_SINK) {
if (stream == NULL || stream->stream.ep->dir != BT_AUDIO_DIR_SINK) {
return BTP_STATUS_FAILED;
}
@ -1103,7 +1144,31 @@ static int server_stream_config(struct bt_conn *conn, struct bt_bap_stream *stre
return 0;
}
static int client_create_unicast_group(struct audio_connection *audio_conn, uint8_t ase_id)
static uint8_t client_add_ase_to_cis(struct audio_connection *audio_conn, uint8_t ase_id,
uint8_t cis_id, uint8_t cig_id)
{
struct audio_stream *stream;
if (cig_id >= CONFIG_BT_ISO_MAX_CIG || cis_id >= UNICAST_GROUP_STREAM_CNT) {
return BTP_STATUS_FAILED;
}
stream = stream_find(audio_conn, ase_id);
if (stream == NULL) {
return BTP_STATUS_FAILED;
}
LOG_DBG("Added ASE %u to CIS %u at CIG %u", ase_id, cis_id, cig_id);
stream->cig = &cigs[cig_id];
stream->cig_id = cig_id;
stream->cis_id = cis_id;
return 0;
}
static int client_create_unicast_group(struct audio_connection *audio_conn, uint8_t ase_id,
uint8_t cig_id)
{
int err;
struct bt_bap_unicast_group_stream_pair_param pair_params[MAX_STREAMS_COUNT];
@ -1112,13 +1177,31 @@ static int client_create_unicast_group(struct audio_connection *audio_conn, uint
size_t stream_cnt = 0;
size_t src_cnt = 0;
size_t sink_cnt = 0;
size_t cis_cnt = 0;
(void)memset(pair_params, 0, sizeof(pair_params));
(void)memset(stream_params, 0, sizeof(stream_params));
for (size_t i = 0; i < MAX_STREAMS_COUNT; i++) {
struct bt_bap_stream *stream = &audio_conn->streams[i].stream;
if (cig_id >= CONFIG_BT_ISO_MAX_CIG) {
return -EINVAL;
}
if (stream == NULL || stream->ep == NULL) {
/* API does not allow to assign a CIG ID freely, so ensure we create groups
* in the right order.
*/
for (uint8_t i = 0; i < cig_id; i++) {
if (cigs[cig_id] == NULL) {
return -EINVAL;
}
}
/* Assign end points to CISes */
for (size_t i = 0; i < MAX_STREAMS_COUNT; i++) {
struct audio_stream *a_stream = &audio_conn->streams[i];
struct bt_bap_stream *stream = &a_stream->stream;
if (stream == NULL || stream->ep == NULL || a_stream->cig == NULL ||
a_stream->cig_id != cig_id) {
continue;
}
@ -1126,26 +1209,45 @@ static int client_create_unicast_group(struct audio_connection *audio_conn, uint
stream_params[stream_cnt].qos = &audio_conn->qos;
if (stream->ep->dir == BT_AUDIO_DIR_SOURCE) {
pair_params[src_cnt].rx_param = &stream_params[stream_cnt];
if (pair_params[a_stream->cis_id].rx_param != NULL) {
return -EINVAL;
}
pair_params[a_stream->cis_id].rx_param = &stream_params[stream_cnt];
src_cnt++;
} else {
pair_params[sink_cnt].tx_param = &stream_params[stream_cnt];
if (pair_params[a_stream->cis_id].tx_param != NULL) {
return -EINVAL;
}
pair_params[a_stream->cis_id].tx_param = &stream_params[stream_cnt];
sink_cnt++;
}
stream_cnt++;
}
if (stream_cnt == 0) {
/* Count CISes to be established */
for (size_t i = 0; i < MAX_STREAMS_COUNT; i++) {
if (pair_params[i].tx_param == NULL && pair_params[i].rx_param == NULL) {
/* No gaps allowed */
break;
}
cis_cnt++;
}
/* Make sure there are no gaps in the pair_params */
if (cis_cnt == 0 || cis_cnt < MAX(sink_cnt, src_cnt)) {
return -EINVAL;
}
param.params = pair_params;
param.params_count = MAX(sink_cnt, src_cnt);
param.params_count = cis_cnt;
param.packing = BT_ISO_PACKING_SEQUENTIAL;
LOG_DBG("Creating unicast group");
err = bt_bap_unicast_group_create(&param, &audio_conn->unicast_group);
err = bt_bap_unicast_group_create(&param, &cigs[cig_id]);
if (err != 0) {
LOG_DBG("Could not create unicast group (err %d)", err);
return -EINVAL;
@ -1305,15 +1407,20 @@ static uint8_t ascs_configure_qos(const void *cmd, uint16_t cmd_len,
audio_conn = &connections[bt_conn_index(conn)];
if (audio_conn->unicast_group != NULL) {
err = bt_bap_unicast_group_delete(audio_conn->unicast_group);
if (cigs[cp->cig_id] != NULL) {
err = bt_bap_unicast_group_delete(cigs[cp->cig_id]);
if (err != 0) {
LOG_DBG("Failed to delete the unicast group, err %d", err);
bt_conn_unref(conn);
return BTP_STATUS_FAILED;
}
audio_conn->unicast_group = NULL;
cigs[cp->cig_id] = NULL;
}
err = client_add_ase_to_cis(audio_conn, cp->ase_id, cp->cis_id, cp->cig_id);
if (err != 0) {
return BTP_STATUS_FAILED;
}
qos = &audio_conn->qos;
@ -1325,7 +1432,7 @@ static uint8_t ascs_configure_qos(const void *cmd, uint16_t cmd_len,
qos->interval = sys_get_le24(cp->sdu_interval);
qos->pd = sys_get_le24(cp->presentation_delay);
err = client_create_unicast_group(audio_conn, cp->ase_id);
err = client_create_unicast_group(audio_conn, cp->ase_id, cp->cig_id);
if (err != 0) {
LOG_DBG("Unable to create unicast group, err %d", err);
bt_conn_unref(conn);
@ -1334,7 +1441,7 @@ static uint8_t ascs_configure_qos(const void *cmd, uint16_t cmd_len,
}
LOG_DBG("QoS configuring streams");
err = bt_bap_stream_qos(conn, audio_conn->unicast_group);
err = bt_bap_stream_qos(conn, cigs[cp->cig_id]);
bt_conn_unref(conn);
if (err != 0) {
@ -1438,11 +1545,22 @@ static uint8_t ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,
LOG_DBG("Starting stream %p, ep %u, dir %u", &stream->stream, cp->ase_id,
stream->stream.ep->dir);
err = bt_bap_stream_start(&stream->stream);
if (err != 0) {
LOG_DBG("Could not start stream: %d", err);
return BTP_STATUS_FAILED;
}
while (true) {
err = bt_bap_stream_start(&stream->stream);
if (err == -EBUSY) {
/* TODO: How to determine if a controller is ready again after
* bt_bap_stream_start? In AC 6(i) tests the PTS sends Receiver Start Ready
* only after all CISes are established.
*/
k_sleep(K_MSEC(1000));
continue;
} else if (err != 0) {
LOG_DBG("Could not start stream: %d", err);
return BTP_STATUS_FAILED;
}
break;
};
return BTP_STATUS_SUCCESS;
}
@ -1473,7 +1591,7 @@ static uint8_t ascs_receiver_stop_ready(const void *cmd, uint16_t cmd_len,
LOG_DBG("Stopping stream");
err = bt_bap_stream_stop(&stream->stream);
if (err != 0) {
LOG_DBG("Could not start stream: %d", err);
LOG_DBG("Could not stop stream: %d", err);
return BTP_STATUS_FAILED;
}
@ -1552,6 +1670,40 @@ static uint8_t ascs_update_metadata(const void *cmd, uint16_t cmd_len, void *rsp
return BTP_STATUS_SUCCESS;
}
static uint8_t ascs_add_ase_to_cis(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
{
int err;
struct bt_conn *conn;
struct audio_connection *audio_conn;
struct bt_conn_info conn_info;
const struct btp_ascs_add_ase_to_cis *cp = cmd;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
if (!conn) {
LOG_ERR("Unknown connection");
return BTP_STATUS_FAILED;
}
(void)bt_conn_get_info(conn, &conn_info);
if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
bt_conn_unref(conn);
return BTP_STATUS_FAILED;
}
audio_conn = &connections[bt_conn_index(conn)];
bt_conn_unref(conn);
err = client_add_ase_to_cis(audio_conn, cp->ase_id, cp->cis_id, cp->cig_id);
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static const struct btp_handler ascs_handlers[] = {
{
.opcode = BTP_ASCS_READ_SUPPORTED_COMMANDS,
@ -1599,6 +1751,11 @@ static const struct btp_handler ascs_handlers[] = {
.expect_len = sizeof(struct btp_ascs_update_metadata_cmd),
.func = ascs_update_metadata,
},
{
.opcode = BTP_ASCS_ADD_ASE_TO_CIS,
.expect_len = sizeof(struct btp_ascs_add_ase_to_cis),
.func = ascs_add_ase_to_cis,
},
};
static int set_location(void)