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