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