Skip to content

Commit

Permalink
tests: Bluetooth: Audio: Remove uses of K_FOREVER in syswg for TX
Browse files Browse the repository at this point in the history
Several tests were using K_FOREVER when allocating the
buffer for TX in the system workqueue, which is illegal behavior.

The solution chosen was to create a TX thread to handle TX,
similar to the solution used in the audio shell and some
sample applications.

This way we can continue to use K_FOREVER when allocting buffers
and it will always be done in a round-robin fashion while
TXing as much as possible, by always enqueuing all the buffers
with mock data.

Since this works for all streams (both broadcast and unicast),
it was obvious to use the same implementation for all tests,
and thus cleaning up the tests a bit and more them more similar.

Signed-off-by: Emil Gydesen <[email protected]>
  • Loading branch information
Thalley committed Nov 3, 2024
1 parent e90c58a commit ee52081
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 236 deletions.
75 changes: 19 additions & 56 deletions tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <zephyr/toolchain.h>

#include "bap_common.h"
#include "bap_stream_tx.h"
#include "bstests.h"
#include "common.h"

Expand All @@ -50,7 +51,6 @@ NET_BUF_POOL_FIXED_DEFINE(tx_pool,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);

extern enum bst_result_t bst_result;
static struct audio_test_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
Expand Down Expand Up @@ -210,51 +210,38 @@ static void started_cb(struct bt_bap_stream *stream)
return;
}

err = stream_tx_register(stream);
if (err != 0) {
FAIL("Failed to register stream %p for TX: %d\n", stream, err);
return;
}

printk("Stream %p started\n", stream);
validate_stream_codec_cfg(stream);
k_sem_give(&sem_started);
}

static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
int err;

printk("Stream %p stopped with reason 0x%02X\n", stream, reason);

err = stream_tx_unregister(stream);
if (err != 0) {
FAIL("Failed to unregister stream %p for TX: %d\n", stream, err);
return;
}

k_sem_give(&sem_stopped);
}

static void stream_sent_cb(struct bt_bap_stream *stream)
{
struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream);
struct net_buf *buf;
int ret;

if (!test_stream->tx_active) {
return;
}

if ((test_stream->tx_cnt % 100U) == 0U) {
printk("Sent with seq_num %u\n", test_stream->seq_num);
}

buf = net_buf_alloc(&tx_pool, K_FOREVER);
if (buf == NULL) {
printk("Could not allocate buffer when sending on %p\n",
stream);
return;
}

net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, mock_iso_data, test_stream->tx_sdu_size);
ret = bt_bap_stream_send(stream, buf, test_stream->seq_num++);
if (ret < 0) {
/* This will end broadcasting on this stream. */
net_buf_unref(buf);

/* Only fail if tx is active (may fail if we are disabling the stream) */
if (test_stream->tx_active) {
FAIL("Unable to broadcast data on %p: %d\n", stream, ret);
}

return;
printk("Stream %p sent %zu SDUs\n", stream, test_stream->tx_cnt);
}

test_stream->tx_cnt++;
Expand Down Expand Up @@ -505,10 +492,6 @@ static void test_broadcast_source_stop(struct bt_bap_broadcast_source *source)

printk("Stopping broadcast source\n");

for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) {
broadcast_source_streams[i].tx_active = false;
}

err = bt_bap_broadcast_source_stop(source);
if (err != 0) {
FAIL("Unable to stop broadcast source: %d\n", err);
Expand Down Expand Up @@ -573,6 +556,7 @@ static void test_main(void)
}

printk("Bluetooth initialized\n");
stream_tx_init();

err = setup_broadcast_source(&source, false);
if (err != 0) {
Expand All @@ -590,17 +574,6 @@ static void test_main(void)

test_broadcast_source_start(source, adv);

/* Initialize sending */
printk("Sending data\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) {
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
struct audio_test_stream *test_stream = &broadcast_source_streams[i];

test_stream->tx_active = true;
stream_sent_cb(&test_stream->stream.bap_stream);
}
}

/* Wait for other devices to have received what they wanted */
backchannel_sync_wait_any();

Expand Down Expand Up @@ -653,6 +626,7 @@ static void test_main_encrypted(void)
}

printk("Bluetooth initialized\n");
stream_tx_init();

err = setup_broadcast_source(&source, true);
if (err != 0) {
Expand All @@ -668,17 +642,6 @@ static void test_main_encrypted(void)

test_broadcast_source_start(source, adv);

/* Initialize sending */
printk("Sending data\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) {
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
struct audio_test_stream *test_stream = &broadcast_source_streams[i];

test_stream->tx_active = true;
stream_sent_cb(&test_stream->stream.bap_stream);
}
}

/* Wait for other devices to have received data */
backchannel_sync_wait_any();

Expand Down
186 changes: 186 additions & 0 deletions tests/bsim/bluetooth/audio/src/bap_stream_tx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <autoconf.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/bluetooth/iso.h>
#include <zephyr/kernel.h>
#include <zephyr/kernel/thread_stack.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_core.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/types.h>

#include "bap_stream_tx.h"
#include "common.h"

LOG_MODULE_REGISTER(stream_tx, LOG_LEVEL_INF);

static struct tx_stream tx_streams[CONFIG_BT_ISO_MAX_CHAN];

static bool stream_is_streaming(const struct bt_bap_stream *bap_stream)
{
struct bt_bap_ep_info ep_info;
int err;

if (bap_stream == NULL) {
return false;
}

/* No-op if stream is not configured */
if (bap_stream->ep == NULL) {
return false;
}

err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
if (err != 0) {
return false;
}

if (ep_info.iso_chan == NULL || ep_info.iso_chan->state != BT_ISO_STATE_CONNECTED) {
return false;
}

return ep_info.state == BT_BAP_EP_STATE_STREAMING;
}

static void tx_thread_func(void *arg1, void *arg2, void *arg3)
{
NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);

/* This loop will attempt to send on all streams in the streaming state in a round robin
* fashion.
* The TX is controlled by the number of buffers configured, and increasing
* CONFIG_BT_ISO_TX_BUF_COUNT will allow for more streams in parallel, or to submit more
* buffers per stream.
* Once a buffer has been freed by the stack, it triggers the next TX.
*/
while (true) {
int err = -ENOEXEC;

for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
struct bt_bap_stream *bap_stream = tx_streams[i].bap_stream;

if (stream_is_streaming(bap_stream)) {
struct net_buf *buf;

buf = net_buf_alloc(&tx_pool, K_FOREVER);
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);

net_buf_add_mem(buf, mock_iso_data, bap_stream->qos->sdu);

err = bt_bap_stream_send(bap_stream, buf, tx_streams[i].seq_num);
if (err == 0) {
tx_streams[i].seq_num++;
} else {
if (!stream_is_streaming(bap_stream)) {
/* Can happen if we disconnected while waiting for a
* buffer - Ignore
*/
} else {
FAIL("Unable to send: %d", err);
}

net_buf_unref(buf);
}
} /* No-op if stream is not streaming */
}

if (err != 0) {
/* In case of any errors, retry with a delay */
k_sleep(K_MSEC(10));
}
}
}

int stream_tx_register(struct bt_bap_stream *bap_stream)
{
if (bap_stream == NULL) {
return -EINVAL;
}

if (!stream_is_tx(bap_stream)) {
return -EINVAL;
}

for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
if (tx_streams[i].bap_stream == NULL) {
tx_streams[i].bap_stream = bap_stream;
tx_streams[i].seq_num = 0U;

LOG_INF("Registered %p for TX", bap_stream);

return 0;
}
}

return -ENOMEM;
}

int stream_tx_unregister(struct bt_bap_stream *bap_stream)
{
if (bap_stream == NULL) {
return -EINVAL;
}

for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
if (tx_streams[i].bap_stream == bap_stream) {
tx_streams[i].bap_stream = NULL;

LOG_INF("Unregistered %p for TX", bap_stream);

return 0;
}
}

return -ENODATA;
}

void stream_tx_init(void)
{
static bool thread_started;

if (!thread_started) {
static K_KERNEL_STACK_DEFINE(tx_thread_stack, 1024U);
const int tx_thread_prio = K_PRIO_PREEMPT(5);
static struct k_thread tx_thread;

k_thread_create(&tx_thread, tx_thread_stack, K_KERNEL_STACK_SIZEOF(tx_thread_stack),
tx_thread_func, NULL, NULL, NULL, tx_thread_prio, 0, K_NO_WAIT);
k_thread_name_set(&tx_thread, "TX thread");
thread_started = true;
}
}

bool stream_is_tx(const struct bt_bap_stream *stream)
{
struct bt_bap_ep_info info;
int err;

if (stream == NULL || stream->ep == NULL) {
return false;
}

err = bt_bap_ep_get_info(stream->ep, &info);
if (err != 0) {
return false;
}

return info.can_send;
}
Loading

0 comments on commit ee52081

Please sign in to comment.