1 /* btp_bap_audio_stream.c - Bluetooth BAP Tester */
2
3 /*
4 * Copyright (c) 2023 Codecoup
5 * Copyright (c) 2024 Nordic Semiconductor ASA
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/cap.h>
18 #include <zephyr/bluetooth/audio/bap.h>
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/hci_types.h>
21 #include <zephyr/bluetooth/iso.h>
22 #include <zephyr/kernel.h>
23 #include <zephyr/kernel/thread_stack.h>
24 #include <zephyr/logging/log.h>
25 #include <zephyr/logging/log_core.h>
26 #include <zephyr/net_buf.h>
27 #include <zephyr/sys/__assert.h>
28 #include <zephyr/sys/atomic.h>
29 #include <zephyr/sys/atomic_types.h>
30 #include <zephyr/sys/byteorder.h>
31 #include <zephyr/sys/util.h>
32 #include <zephyr/sys/util_macro.h>
33 #include <zephyr/types.h>
34
35 #include "btp/btp.h"
36 #include "btp_bap_audio_stream.h"
37
38 LOG_MODULE_REGISTER(bttester_bap_audio_stream, CONFIG_BTTESTER_LOG_LEVEL);
39
40 /** Enqueue at least 2 buffers per stream, but otherwise equal distribution based on the buf count*/
41 #define MAX_ENQUEUE_CNT MAX(2, (CONFIG_BT_ISO_TX_BUF_COUNT / CONFIG_BT_ISO_MAX_CHAN))
42
audio_stream_to_bap_stream(struct btp_bap_audio_stream * stream)43 static inline struct bt_bap_stream *audio_stream_to_bap_stream(struct btp_bap_audio_stream *stream)
44 {
45 return &stream->cap_stream.bap_stream;
46 }
47 struct tx_stream {
48 struct bt_bap_stream *bap_stream;
49 uint16_t seq_num;
50 size_t tx_completed;
51 atomic_t enqueued;
52 } tx_streams[CONFIG_BT_ISO_MAX_CHAN];
53
stream_is_streaming(const struct bt_bap_stream * bap_stream)54 static bool stream_is_streaming(const struct bt_bap_stream *bap_stream)
55 {
56 struct bt_bap_ep_info ep_info;
57 int err;
58
59 if (bap_stream == NULL) {
60 return false;
61 }
62
63 /* No-op if stream is not configured */
64 if (bap_stream->ep == NULL) {
65 return false;
66 }
67
68 err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
69 __ASSERT_NO_MSG(err == 0);
70
71 if (ep_info.iso_chan == NULL || ep_info.iso_chan->state != BT_ISO_STATE_CONNECTED) {
72 return false;
73 }
74
75 return ep_info.state == BT_BAP_EP_STATE_STREAMING;
76 }
77
tx_thread_func(void * arg1,void * arg2,void * arg3)78 static void tx_thread_func(void *arg1, void *arg2, void *arg3)
79 {
80 NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
81 BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
82 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
83
84 /* This loop will attempt to send on all streams in the streaming state in a round robin
85 * fashion.
86 * The TX is controlled by the number of buffers configured, and increasing
87 * CONFIG_BT_ISO_TX_BUF_COUNT will allow for more streams in parallel, or to submit more
88 * buffers per stream.
89 * Once a buffer has been freed by the stack, it triggers the next TX.
90 */
91 while (true) {
92 int err = -ENOEXEC;
93
94 for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
95 struct bt_bap_stream *bap_stream = tx_streams[i].bap_stream;
96 struct net_buf *buf;
97
98 if (!stream_is_streaming(bap_stream) ||
99 atomic_get(&tx_streams[i].enqueued) >= MAX_ENQUEUE_CNT) {
100 continue;
101 }
102
103 buf = net_buf_alloc(&tx_pool, K_SECONDS(1));
104 __ASSERT(buf != NULL, "Failed to get a TX buffer");
105
106 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
107
108 net_buf_add_mem(buf, btp_bap_audio_stream_mock_data, bap_stream->qos->sdu);
109
110 err = bt_bap_stream_send(bap_stream, buf, tx_streams[i].seq_num);
111 if (err == 0) {
112 tx_streams[i].seq_num++;
113 atomic_inc(&tx_streams[i].enqueued);
114 } else {
115 if (!stream_is_streaming(bap_stream)) {
116 /* Can happen if we disconnected while waiting for a
117 * buffer - Ignore
118 */
119 } else {
120 LOG_ERR("Unable to send: %d", err);
121 }
122
123 net_buf_unref(buf);
124 }
125 }
126
127 if (err != 0) {
128 /* In case of any errors, retry with a delay */
129 k_sleep(K_MSEC(10));
130 }
131 }
132 }
133
btp_bap_audio_stream_tx_register(struct btp_bap_audio_stream * stream)134 int btp_bap_audio_stream_tx_register(struct btp_bap_audio_stream *stream)
135 {
136 if (stream == NULL) {
137 return -EINVAL;
138 }
139
140 if (!btp_bap_audio_stream_can_send(stream)) {
141 return -EINVAL;
142 }
143
144 for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
145 if (tx_streams[i].bap_stream == NULL) {
146 tx_streams[i].bap_stream = audio_stream_to_bap_stream(stream);
147
148 LOG_INF("Registered %p (%p) for TX", stream, tx_streams[i].bap_stream);
149
150 return 0;
151 }
152 }
153
154 return -ENOMEM;
155 }
156
btp_bap_audio_stream_tx_unregister(struct btp_bap_audio_stream * stream)157 int btp_bap_audio_stream_tx_unregister(struct btp_bap_audio_stream *stream)
158 {
159 const struct bt_bap_stream *bap_stream;
160
161 if (stream == NULL) {
162 return -EINVAL;
163 }
164
165 bap_stream = audio_stream_to_bap_stream(stream);
166
167 for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
168 if (tx_streams[i].bap_stream == bap_stream) {
169 memset(&tx_streams[i], 0, sizeof(tx_streams[i]));
170
171 LOG_INF("Unregistered %p for TX", bap_stream);
172
173 return 0;
174 }
175 }
176
177 return -ENODATA;
178 }
179
btp_bap_audio_stream_tx_init(void)180 void btp_bap_audio_stream_tx_init(void)
181 {
182 static bool thread_started;
183
184 if (!thread_started) {
185 static K_KERNEL_STACK_DEFINE(tx_thread_stack, 1024U);
186 const int tx_thread_prio = K_PRIO_PREEMPT(5);
187 static struct k_thread tx_thread;
188
189 k_thread_create(&tx_thread, tx_thread_stack, K_KERNEL_STACK_SIZEOF(tx_thread_stack),
190 tx_thread_func, NULL, NULL, NULL, tx_thread_prio, 0, K_NO_WAIT);
191 k_thread_name_set(&tx_thread, "TX thread");
192 thread_started = true;
193 }
194 }
195
btp_bap_audio_stream_can_send(struct btp_bap_audio_stream * stream)196 bool btp_bap_audio_stream_can_send(struct btp_bap_audio_stream *stream)
197 {
198 struct bt_bap_stream *bap_stream;
199 struct bt_bap_ep_info info;
200 int err;
201
202 if (stream == NULL) {
203 return false;
204 }
205
206 bap_stream = audio_stream_to_bap_stream(stream);
207 if (bap_stream->ep == NULL) {
208 return false;
209 }
210
211 err = bt_bap_ep_get_info(bap_stream->ep, &info);
212 __ASSERT_NO_MSG(err == 0);
213
214 return info.can_send;
215 }
216
btp_bap_audio_stream_sent_cb(struct bt_bap_stream * stream)217 void btp_bap_audio_stream_sent_cb(struct bt_bap_stream *stream)
218 {
219 for (size_t i = 0U; i < ARRAY_SIZE(tx_streams); i++) {
220 if (tx_streams[i].bap_stream == stream) {
221 const atomic_val_t old = atomic_dec(&tx_streams[i].enqueued);
222
223 __ASSERT_NO_MSG(old != 0);
224
225 tx_streams[i].tx_completed++;
226 if ((tx_streams[i].tx_completed % 100U) == 0U) {
227 LOG_INF("Stream %p sent %zu SDUs of size %u", stream,
228 tx_streams[i].tx_completed, stream->qos->sdu);
229 }
230
231 break;
232 }
233 }
234 }
235