bluetooth: tester: Add support for streaming

Unlocks BAP/UCL/STR tests.

Signed-off-by: Magdalena Kasenberg <magdalena.kasenberg@codecoup.pl>
This commit is contained in:
Magdalena Kasenberg 2023-04-06 13:50:21 +02:00 committed by Carles Cufí
parent 69d0dce978
commit 567c39250e
2 changed files with 236 additions and 40 deletions

View file

@ -20,6 +20,14 @@ struct btp_bap_discover_cmd {
#define BTP_BAP_DISCOVERY_STATUS_SUCCESS 0x00
#define BTP_BAP_DISCOVERY_STATUS_FAILED 0x01
#define BTP_BAP_SEND 0x03
struct btp_bap_send_cmd {
bt_addr_le_t address;
uint8_t ase_id;
uint8_t data_len;
uint8_t data[0];
} __packed;
/* BAP events */
#define BTP_BAP_EV_DISCOVERY_COMPLETED 0x80
struct btp_bap_discovery_completed_ev {
@ -43,3 +51,11 @@ struct btp_ascs_ase_found_ev {
uint8_t dir;
uint8_t ase_id;
} __packed;
#define BTP_BAP_EV_STREAM_RECEIVED 0x83
struct btp_bap_stream_received_ev {
bt_addr_le_t address;
uint8_t ase_id;
uint8_t data_len;
uint8_t data[0];
} __packed;

View file

@ -71,11 +71,18 @@ struct audio_connection {
size_t end_points_count;
} connections[CONFIG_BT_MAX_CONN];
static struct bt_bap_unicast_client_discover_params ase_discover_params;
static struct bt_codec_qos_pref qos_pref = BT_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02,
10, 10000, 40000, 10000, 40000);
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);
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;
static void print_codec(const struct bt_codec *codec)
{
LOG_DBG("codec 0x%02x cid 0x%04x vid 0x%04x count %zu",
@ -412,6 +419,30 @@ static const struct bt_bap_unicast_server_cb unicast_server_cb = {
.release = lc3_release,
};
static void btp_send_stream_received_ev(struct bt_conn *conn, struct bt_bap_ep *ep,
uint8_t data_len, uint8_t *data)
{
struct btp_bap_stream_received_ev *ev;
struct bt_conn_info info;
LOG_DBG("Stream received, ep %d, dir %d, len %d", ep->status.id, ep->dir,
data_len);
(void)bt_conn_get_info(conn, &info);
net_buf_simple_init(rx_ev_buf, 0);
ev = net_buf_simple_add(rx_ev_buf, sizeof(*ev));
bt_addr_le_copy(&ev->address, info.le.dst);
ev->ase_id = ep->status.id;
ev->data_len = data_len;
memcpy(ev->data, data, data_len);
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_STREAM_RECEIVED, ev, sizeof(*ev) + data_len);
}
static void stream_configured(struct bt_bap_stream *stream,
const struct bt_codec_qos_pref *pref)
{
@ -511,7 +542,14 @@ static void stream_recv(struct bt_bap_stream *stream,
const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
LOG_DBG("Incoming audio on stream %p len %u", stream, buf->len);
if (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;
btp_send_stream_received_ev(stream->conn, stream->ep, buf->len, buf->data);
}
}
static void stream_sent(struct bt_bap_stream *stream)
@ -583,27 +621,96 @@ static void btp_send_ase_found_ev(struct bt_conn *conn, struct bt_bap_ep *ep)
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_ASE_FOUND, &ev, sizeof(ev));
}
static void discover_remote_ases_cb(struct bt_conn *conn, struct bt_codec *codec,
struct bt_bap_ep *ep,
struct bt_bap_unicast_client_discover_params *params)
static void unicast_client_location_cb(struct bt_conn *conn,
enum bt_audio_dir dir,
enum bt_audio_location loc)
{
enum bt_audio_dir dir = params->dir;
struct audio_connection *audio_conn;
LOG_DBG("unicast_client_location_cb");
LOG_DBG("dir %u loc %X", dir, loc);
}
if (params->err != 0 && params->err != BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
LOG_DBG("Discover remote ASEs failed: %d", params->err);
btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
static void available_contexts_cb(struct bt_conn *conn,
enum bt_audio_context snk_ctx,
enum bt_audio_context src_ctx)
{
LOG_DBG("available_contexts_cb");
LOG_DBG("snk ctx %u src ctx %u", snk_ctx, src_ctx);
}
return;
}
static void config_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("config_cb");
LOG_DBG("stream %p config operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void qos_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("qos_cb");
LOG_DBG("stream %p qos operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void enable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("enable_cb");
LOG_DBG("stream %p enable operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void start_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("start_cb");
LOG_DBG("stream %p start operation rsp_code %u reason %u", stream, rsp_code, reason);
already_sent = false;
}
static void stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("stop_cb");
LOG_DBG("stream %p stop operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void disable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("disable_cb");
LOG_DBG("stream %p disable operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void metadata_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("metadata_cb");
LOG_DBG("stream %p metadata operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void release_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
LOG_DBG("release_cb");
LOG_DBG("stream %p release operation rsp_code %u reason %u", stream, rsp_code, reason);
}
static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir, const struct bt_codec *codec)
{
LOG_DBG("pac_record_cb");
if (codec != NULL) {
LOG_DBG("Discovered codec capabilities %p", codec);
print_codec(codec);
btp_send_pac_codec_found_ev(conn, codec, dir);
return;
}
}
static void endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep)
{
struct audio_connection *audio_conn;
LOG_DBG("endpoint_cb");
if (ep != NULL) {
LOG_DBG("Discovered ASE %p, id %u, dir 0x%02x", ep, ep->status.id, ep->dir);
@ -611,7 +718,7 @@ static void discover_remote_ases_cb(struct bt_conn *conn, struct bt_codec *codec
audio_conn = &connections[bt_conn_index(conn)];
if (audio_conn->end_points_count >= CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT +
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT) {
CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT) {
LOG_DBG("Failed to cache ep %p due to configured limit: %zu", ep,
audio_conn->end_points_count);
@ -625,24 +732,29 @@ static void discover_remote_ases_cb(struct bt_conn *conn, struct bt_codec *codec
return;
}
}
static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
{
LOG_DBG("discover_cb");
if (err != 0 && err != BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
LOG_DBG("Discover remote ASEs failed: %d", err);
btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
return;
}
LOG_DBG("Discover complete");
if (params->err == BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
if (err == BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
LOG_DBG("Discover remote ASEs completed without finding any source ASEs");
} else {
LOG_DBG("Discover remote ASEs complete: err %d", params->err);
LOG_DBG("Discover remote ASEs complete: err %d", err);
}
(void)memset(params, 0, sizeof(*params));
if (dir == BT_AUDIO_DIR_SINK) {
int err;
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE);
params->func = discover_remote_ases_cb;
params->dir = BT_AUDIO_DIR_SOURCE;
err = bt_bap_unicast_client_discover(conn, params);
if (err != 0) {
LOG_DBG("Failed to discover source ASEs: %d", err);
btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
@ -654,6 +766,22 @@ static void discover_remote_ases_cb(struct bt_conn *conn, struct bt_codec *codec
btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_SUCCESS);
}
static struct bt_bap_unicast_client_cb unicast_client_cbs = {
.location = unicast_client_location_cb,
.available_contexts = available_contexts_cb,
.config = config_cb,
.qos = qos_cb,
.enable = enable_cb,
.start = start_cb,
.stop = stop_cb,
.disable = disable_cb,
.metadata = metadata_cb,
.release = release_cb,
.pac_record = pac_record_cb,
.endpoint = endpoint_cb,
.discover = discover_cb,
};
static uint8_t bap_discover(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
@ -661,7 +789,6 @@ static uint8_t bap_discover(const void *cmd, uint16_t cmd_len,
struct bt_conn *conn;
struct audio_connection *audio_conn;
struct bt_conn_info conn_info;
static struct bt_bap_unicast_client_discover_params *params = &ase_discover_params;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
@ -679,11 +806,7 @@ static uint8_t bap_discover(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_FAILED;
}
(void)memset(params, 0, sizeof(*params));
params->func = discover_remote_ases_cb;
params->dir = BT_AUDIO_DIR_SINK;
err = bt_bap_unicast_client_discover(conn, params);
err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK);
if (err != 0) {
LOG_DBG("Failed to discover remote ASEs: %d", err);
bt_conn_unref(conn);
@ -696,6 +819,51 @@ static uint8_t bap_discover(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_SUCCESS;
}
static uint8_t bap_send(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
const struct btp_bap_send_cmd *cp = cmd;
struct audio_connection *audio_conn;
struct audio_stream *stream;
struct bt_conn *conn;
struct net_buf *buf;
struct bt_conn_info conn_info;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
if (!conn) {
LOG_ERR("Unknown connection");
return BTP_STATUS_FAILED;
}
audio_conn = &connections[bt_conn_index(conn)];
(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) {
return BTP_STATUS_FAILED;
}
LOG_DBG("Sending data to ASE: ase_id %d len %d seq %d", cp->ase_id, cp->data_len,
stream->seq_num);
buf = net_buf_alloc(&tx_pool, K_FOREVER);
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, cp->data, cp->data_len);
err = bt_bap_stream_send(&stream->stream, buf, stream->seq_num++, BT_ISO_TIMESTAMP_NONE);
if (err != 0) {
LOG_ERR("Failed to send audio data to stream: ase_id %d dir %d err %d",
stream->ase_id, stream->stream.dir, err);
net_buf_unref(buf);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static void connected(struct bt_conn *conn, uint8_t err)
{
struct audio_connection *audio_conn;
@ -784,6 +952,10 @@ static int client_create_unicast_group(struct audio_connection *audio_conn, uint
struct bt_bap_unicast_group_stream_param stream_params[MAX_STREAMS_COUNT];
struct bt_bap_unicast_group_param param;
size_t stream_cnt = 0;
size_t src_cnt = 0;
size_t sink_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;
@ -792,20 +964,15 @@ static int client_create_unicast_group(struct audio_connection *audio_conn, uint
continue;
}
if (stream->ep->status.id != ase_id) {
/* TODO: For now one stream per group is configured */
continue;
}
stream_params[stream_cnt].stream = stream;
stream_params[stream_cnt].qos = &audio_conn->qos;
if (stream->ep->dir == BT_AUDIO_DIR_SOURCE) {
pair_params[stream_cnt].tx_param = NULL;
pair_params[stream_cnt].rx_param = &stream_params[stream_cnt];
pair_params[src_cnt].rx_param = &stream_params[stream_cnt];
src_cnt++;
} else {
pair_params[stream_cnt].tx_param = &stream_params[stream_cnt];
pair_params[stream_cnt].rx_param = NULL;
pair_params[sink_cnt].tx_param = &stream_params[stream_cnt];
sink_cnt++;
}
stream_cnt++;
@ -1418,6 +1585,11 @@ static const struct btp_handler bap_handlers[] = {
.expect_len = sizeof(struct btp_bap_discover_cmd),
.func = bap_discover,
},
{
.opcode = BTP_BAP_SEND,
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = bap_send,
},
};
uint8_t tester_init_pacs(void)
@ -1472,9 +1644,17 @@ uint8_t tester_unregister_ascs(void)
uint8_t tester_init_bap(void)
{
int err;
/* reset data */
(void)memset(connections, 0, sizeof(connections));
err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
if (err != 0) {
LOG_DBG("Failed to register client callbacks: %d", err);
return BTP_STATUS_FAILED;
}
tester_register_command_handlers(BTP_SERVICE_ID_BAP, bap_handlers,
ARRAY_SIZE(bap_handlers));