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