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