1 /* bap_iso.c - BAP ISO handling */
2 
3 /*
4  * Copyright (c) 2022 Codecoup
5  * Copyright (c) 2023 Nordic Semiconductor ASA
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <string.h>
13 
14 #include <zephyr/autoconf.h>
15 #include <zephyr/bluetooth/audio/audio.h>
16 #include <zephyr/bluetooth/audio/bap.h>
17 #include <zephyr/bluetooth/hci_types.h>
18 #include <zephyr/bluetooth/iso.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/sys/__assert.h>
21 #include <zephyr/sys/atomic.h>
22 #include <zephyr/sys/util.h>
23 #include <zephyr/sys/util_macro.h>
24 
25 #include "bap_iso.h"
26 #include "audio_internal.h"
27 #include "bap_endpoint.h"
28 
29 LOG_MODULE_REGISTER(bt_bap_iso, CONFIG_BT_BAP_ISO_LOG_LEVEL);
30 
31 /* TODO: Optimize the ISO_POOL_SIZE */
32 #define ISO_POOL_SIZE CONFIG_BT_ISO_MAX_CHAN
33 
34 static struct bt_bap_iso iso_pool[ISO_POOL_SIZE];
35 
bt_bap_iso_new(void)36 struct bt_bap_iso *bt_bap_iso_new(void)
37 {
38 	struct bt_bap_iso *iso = NULL;
39 
40 	for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
41 		if (atomic_cas(&iso_pool[i].ref, 0, 1)) {
42 			iso = &iso_pool[i];
43 			break;
44 		}
45 	}
46 
47 	if (!iso) {
48 		return NULL;
49 	}
50 
51 	(void)memset(iso, 0, offsetof(struct bt_bap_iso, ref));
52 
53 	return iso;
54 }
55 
bt_bap_iso_ref(struct bt_bap_iso * iso)56 struct bt_bap_iso *bt_bap_iso_ref(struct bt_bap_iso *iso)
57 {
58 	atomic_val_t old;
59 
60 	__ASSERT_NO_MSG(iso != NULL);
61 
62 	/* Reference counter must be checked to avoid incrementing ref from
63 	 * zero, then we should return NULL instead.
64 	 * Loop on clear-and-set in case someone has modified the reference
65 	 * count since the read, and start over again when that happens.
66 	 */
67 	do {
68 		old = atomic_get(&iso->ref);
69 
70 		if (!old) {
71 			return NULL;
72 		}
73 	} while (!atomic_cas(&iso->ref, old, old + 1));
74 
75 	return iso;
76 }
77 
bt_bap_iso_unref(struct bt_bap_iso * iso)78 void bt_bap_iso_unref(struct bt_bap_iso *iso)
79 {
80 	atomic_val_t old;
81 
82 	__ASSERT_NO_MSG(iso != NULL);
83 
84 	old = atomic_dec(&iso->ref);
85 
86 	__ASSERT(old > 0, "iso reference counter is 0");
87 }
88 
bt_bap_iso_foreach(bt_bap_iso_func_t func,void * user_data)89 void bt_bap_iso_foreach(bt_bap_iso_func_t func, void *user_data)
90 {
91 	for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
92 		struct bt_bap_iso *iso = bt_bap_iso_ref(&iso_pool[i]);
93 		bool iter;
94 
95 		if (!iso) {
96 			continue;
97 		}
98 
99 		iter = func(iso, user_data);
100 		bt_bap_iso_unref(iso);
101 
102 		if (!iter) {
103 			return;
104 		}
105 	}
106 }
107 
108 struct bt_bap_iso_find_param {
109 	struct bt_bap_iso *iso;
110 	bt_bap_iso_func_t func;
111 	void *user_data;
112 };
113 
bt_bap_iso_find_cb(struct bt_bap_iso * iso,void * user_data)114 static bool bt_bap_iso_find_cb(struct bt_bap_iso *iso, void *user_data)
115 {
116 	struct bt_bap_iso_find_param *param = user_data;
117 	bool found;
118 
119 	found = param->func(iso, param->user_data);
120 	if (found) {
121 		param->iso = bt_bap_iso_ref(iso);
122 	}
123 
124 	return !found;
125 }
126 
bt_bap_iso_find(bt_bap_iso_func_t func,void * user_data)127 struct bt_bap_iso *bt_bap_iso_find(bt_bap_iso_func_t func, void *user_data)
128 {
129 	struct bt_bap_iso_find_param param = {
130 		.iso = NULL,
131 		.func = func,
132 		.user_data = user_data,
133 	};
134 
135 	bt_bap_iso_foreach(bt_bap_iso_find_cb, &param);
136 
137 	return param.iso;
138 }
139 
bt_bap_iso_init(struct bt_bap_iso * iso,struct bt_iso_chan_ops * ops)140 void bt_bap_iso_init(struct bt_bap_iso *iso, struct bt_iso_chan_ops *ops)
141 {
142 	iso->chan.ops = ops;
143 	iso->chan.qos = &iso->qos;
144 
145 	/* Setup the QoS for both Tx and Rx
146 	 * This is due to the limitation in the ISO API where pointers like
147 	 * the `qos->tx` shall be initialized before the CIS is created
148 	 */
149 	iso->chan.qos->rx = &iso->rx.qos;
150 	iso->chan.qos->tx = &iso->tx.qos;
151 }
152 
bap_iso_get_iso_dir(bool unicast_client,struct bt_bap_iso * iso,enum bt_audio_dir dir)153 static struct bt_bap_iso_dir *bap_iso_get_iso_dir(bool unicast_client, struct bt_bap_iso *iso,
154 						  enum bt_audio_dir dir)
155 {
156 	/* TODO FIX FOR CLIENT */
157 	if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && unicast_client) {
158 		/* For the unicast client, the direction and tx/rx is reversed */
159 		if (dir == BT_AUDIO_DIR_SOURCE) {
160 			return &iso->rx;
161 		} else {
162 			return &iso->tx;
163 		}
164 	}
165 
166 	if (dir == BT_AUDIO_DIR_SINK) {
167 		return &iso->rx;
168 	} else {
169 		return &iso->tx;
170 	}
171 }
172 
bt_bap_iso_configure_data_path(struct bt_bap_ep * ep,struct bt_audio_codec_cfg * codec_cfg)173 void bt_bap_iso_configure_data_path(struct bt_bap_ep *ep, struct bt_audio_codec_cfg *codec_cfg)
174 {
175 	struct bt_bap_iso *bap_iso = ep->iso;
176 	struct bt_iso_chan_qos *qos = bap_iso->chan.qos;
177 	const bool is_unicast_client =
178 		IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && bt_bap_ep_is_unicast_client(ep);
179 	struct bt_bap_iso_dir *iso_dir = bap_iso_get_iso_dir(is_unicast_client, bap_iso, ep->dir);
180 	struct bt_iso_chan_path *path = &iso_dir->path;
181 
182 	/* Setup the data path objects */
183 	if (iso_dir == &bap_iso->rx) {
184 		qos->rx->path = path;
185 	} else {
186 		qos->tx->path = path;
187 	}
188 
189 	/* Configure the data path to either use the controller for transcoding, or set the path to
190 	 * be transparent to indicate that the transcoding happens somewhere else
191 	 */
192 	path->pid = codec_cfg->path_id;
193 
194 	if (codec_cfg->ctlr_transcode) {
195 		path->format = codec_cfg->id;
196 		path->cid = codec_cfg->cid;
197 		path->vid = codec_cfg->vid;
198 		path->delay = 0;
199 		path->cc_len = codec_cfg->data_len;
200 		path->cc = codec_cfg->data;
201 	} else {
202 		path->format = BT_HCI_CODING_FORMAT_TRANSPARENT;
203 		path->cid = 0;
204 		path->vid = 0;
205 		path->delay = 0;
206 		path->cc_len = 0;
207 		path->cc = NULL;
208 	}
209 }
is_unicast_client_ep(struct bt_bap_ep * ep)210 static bool is_unicast_client_ep(struct bt_bap_ep *ep)
211 {
212 	return IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && bt_bap_ep_is_unicast_client(ep);
213 }
214 
bt_bap_iso_bind_ep(struct bt_bap_iso * iso,struct bt_bap_ep * ep)215 void bt_bap_iso_bind_ep(struct bt_bap_iso *iso, struct bt_bap_ep *ep)
216 {
217 	struct bt_bap_iso_dir *iso_dir;
218 
219 	__ASSERT_NO_MSG(ep != NULL);
220 	__ASSERT_NO_MSG(iso != NULL);
221 	__ASSERT(ep->iso == NULL, "ep %p bound with iso %p already", ep, ep->iso);
222 	__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
223 		 "invalid dir: %u", ep->dir);
224 
225 	LOG_DBG("iso %p ep %p dir %s", iso, ep, bt_audio_dir_str(ep->dir));
226 
227 	iso_dir = bap_iso_get_iso_dir(is_unicast_client_ep(ep), iso, ep->dir);
228 	__ASSERT(iso_dir->ep == NULL, "iso %p bound with ep %p", iso, iso_dir);
229 	iso_dir->ep = ep;
230 
231 	ep->iso = bt_bap_iso_ref(iso);
232 }
233 
bt_bap_iso_unbind_ep(struct bt_bap_iso * iso,struct bt_bap_ep * ep)234 void bt_bap_iso_unbind_ep(struct bt_bap_iso *iso, struct bt_bap_ep *ep)
235 {
236 	struct bt_bap_iso_dir *iso_dir;
237 
238 	__ASSERT_NO_MSG(ep != NULL);
239 	__ASSERT_NO_MSG(iso != NULL);
240 	__ASSERT(ep->iso == iso, "ep %p not bound with iso %p, was bound to %p",
241 		 ep, iso, ep->iso);
242 	__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
243 		 "Invalid dir: %u", ep->dir);
244 
245 	LOG_DBG("iso %p ep %p dir %s", iso, ep, bt_audio_dir_str(ep->dir));
246 
247 	iso_dir = bap_iso_get_iso_dir(is_unicast_client_ep(ep), iso, ep->dir);
248 	__ASSERT(iso_dir->ep == ep, "iso %p not bound with ep %p", iso, ep);
249 	iso_dir->ep = NULL;
250 
251 	bt_bap_iso_unref(ep->iso);
252 	ep->iso = NULL;
253 }
254 
bt_bap_iso_get_ep(bool unicast_client,struct bt_bap_iso * iso,enum bt_audio_dir dir)255 struct bt_bap_ep *bt_bap_iso_get_ep(bool unicast_client, struct bt_bap_iso *iso,
256 				    enum bt_audio_dir dir)
257 {
258 	struct bt_bap_iso_dir *iso_dir;
259 
260 	__ASSERT(dir == BT_AUDIO_DIR_SINK || dir == BT_AUDIO_DIR_SOURCE,
261 		 "invalid dir: %u", dir);
262 
263 	LOG_DBG("iso %p dir %s", iso, bt_audio_dir_str(dir));
264 
265 	iso_dir = bap_iso_get_iso_dir(unicast_client, iso, dir);
266 
267 	return iso_dir->ep;
268 }
269 
bt_bap_iso_get_paired_ep(const struct bt_bap_ep * ep)270 struct bt_bap_ep *bt_bap_iso_get_paired_ep(const struct bt_bap_ep *ep)
271 {
272 	if (ep->iso->rx.ep == ep) {
273 		return ep->iso->tx.ep;
274 	} else {
275 		return ep->iso->rx.ep;
276 	}
277 }
278 
279 #if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
bt_bap_iso_bind_stream(struct bt_bap_iso * bap_iso,struct bt_bap_stream * stream,enum bt_audio_dir dir)280 void bt_bap_iso_bind_stream(struct bt_bap_iso *bap_iso, struct bt_bap_stream *stream,
281 			    enum bt_audio_dir dir)
282 {
283 	struct bt_bap_iso_dir *bap_iso_ep;
284 
285 	__ASSERT_NO_MSG(stream != NULL);
286 	__ASSERT_NO_MSG(bap_iso != NULL);
287 	__ASSERT(stream->bap_iso == NULL, "stream %p bound with bap_iso %p already", stream,
288 		 stream->bap_iso);
289 
290 	LOG_DBG("bap_iso %p stream %p dir %s", bap_iso, stream, bt_audio_dir_str(dir));
291 
292 	/* For the unicast client, the direction and tx/rx is reversed */
293 	if (dir == BT_AUDIO_DIR_SOURCE) {
294 		bap_iso_ep = &bap_iso->rx;
295 	} else {
296 		bap_iso_ep = &bap_iso->tx;
297 	}
298 
299 	__ASSERT(bap_iso_ep->stream == NULL, "bap_iso %p bound with stream %p", bap_iso,
300 		 bap_iso_ep->stream);
301 	bap_iso_ep->stream = stream;
302 
303 	stream->bap_iso = bt_bap_iso_ref(bap_iso);
304 }
305 
bt_bap_iso_unbind_stream(struct bt_bap_iso * bap_iso,struct bt_bap_stream * stream,enum bt_audio_dir dir)306 void bt_bap_iso_unbind_stream(struct bt_bap_iso *bap_iso, struct bt_bap_stream *stream,
307 			      enum bt_audio_dir dir)
308 {
309 	struct bt_bap_iso_dir *bap_iso_ep;
310 
311 	__ASSERT_NO_MSG(stream != NULL);
312 	__ASSERT_NO_MSG(bap_iso != NULL);
313 	__ASSERT(stream->bap_iso != NULL, "stream %p not bound with an bap_iso", stream);
314 
315 	LOG_DBG("bap_iso %p stream %p dir %s", bap_iso, stream, bt_audio_dir_str(dir));
316 
317 	/* For the unicast client, the direction and tx/rx is reversed */
318 	if (dir == BT_AUDIO_DIR_SOURCE) {
319 		bap_iso_ep = &bap_iso->rx;
320 	} else {
321 		bap_iso_ep = &bap_iso->tx;
322 	}
323 
324 	__ASSERT(bap_iso_ep->stream == stream, "bap_iso %p (%p) not bound with stream %p (%p)",
325 		 bap_iso, bap_iso_ep->stream, stream, stream->bap_iso);
326 	bap_iso_ep->stream = NULL;
327 
328 	bt_bap_iso_unref(bap_iso);
329 	stream->bap_iso = NULL;
330 }
331 
bt_bap_iso_get_stream(struct bt_bap_iso * iso,enum bt_audio_dir dir)332 struct bt_bap_stream *bt_bap_iso_get_stream(struct bt_bap_iso *iso, enum bt_audio_dir dir)
333 {
334 	__ASSERT(dir == BT_AUDIO_DIR_SINK || dir == BT_AUDIO_DIR_SOURCE,
335 		 "invalid dir: %u", dir);
336 
337 	LOG_DBG("iso %p dir %s", iso, bt_audio_dir_str(dir));
338 
339 	/* For the unicast client, the direction and tx/rx is reversed */
340 	if (dir == BT_AUDIO_DIR_SOURCE) {
341 		return iso->rx.stream;
342 	} else {
343 		return iso->tx.stream;
344 	}
345 }
346 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
347