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 "bap_iso.h"
11 #include "audio_internal.h"
12 #include "bap_endpoint.h"
13 
14 #include <zephyr/logging/log.h>
15 
16 LOG_MODULE_REGISTER(bt_bap_iso, CONFIG_BT_BAP_ISO_LOG_LEVEL);
17 
18 /* TODO: Optimize the ISO_POOL_SIZE */
19 #define ISO_POOL_SIZE CONFIG_BT_ISO_MAX_CHAN
20 
21 static struct bt_bap_iso iso_pool[ISO_POOL_SIZE];
22 
bt_bap_iso_new(void)23 struct bt_bap_iso *bt_bap_iso_new(void)
24 {
25 	struct bt_bap_iso *iso = NULL;
26 
27 	for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
28 		if (atomic_cas(&iso_pool[i].ref, 0, 1)) {
29 			iso = &iso_pool[i];
30 			break;
31 		}
32 	}
33 
34 	if (!iso) {
35 		return NULL;
36 	}
37 
38 	(void)memset(iso, 0, offsetof(struct bt_bap_iso, ref));
39 
40 	return iso;
41 }
42 
bt_bap_iso_ref(struct bt_bap_iso * iso)43 struct bt_bap_iso *bt_bap_iso_ref(struct bt_bap_iso *iso)
44 {
45 	atomic_val_t old;
46 
47 	__ASSERT_NO_MSG(iso != NULL);
48 
49 	/* Reference counter must be checked to avoid incrementing ref from
50 	 * zero, then we should return NULL instead.
51 	 * Loop on clear-and-set in case someone has modified the reference
52 	 * count since the read, and start over again when that happens.
53 	 */
54 	do {
55 		old = atomic_get(&iso->ref);
56 
57 		if (!old) {
58 			return NULL;
59 		}
60 	} while (!atomic_cas(&iso->ref, old, old + 1));
61 
62 	return iso;
63 }
64 
bt_bap_iso_unref(struct bt_bap_iso * iso)65 void bt_bap_iso_unref(struct bt_bap_iso *iso)
66 {
67 	atomic_val_t old;
68 
69 	__ASSERT_NO_MSG(iso != NULL);
70 
71 	old = atomic_dec(&iso->ref);
72 
73 	__ASSERT(old > 0, "iso reference counter is 0");
74 }
75 
bt_bap_iso_foreach(bt_bap_iso_func_t func,void * user_data)76 void bt_bap_iso_foreach(bt_bap_iso_func_t func, void *user_data)
77 {
78 	for (size_t i = 0; i < ARRAY_SIZE(iso_pool); i++) {
79 		struct bt_bap_iso *iso = bt_bap_iso_ref(&iso_pool[i]);
80 		bool iter;
81 
82 		if (!iso) {
83 			continue;
84 		}
85 
86 		iter = func(iso, user_data);
87 		bt_bap_iso_unref(iso);
88 
89 		if (!iter) {
90 			return;
91 		}
92 	}
93 }
94 
95 struct bt_bap_iso_find_param {
96 	struct bt_bap_iso *iso;
97 	bt_bap_iso_func_t func;
98 	void *user_data;
99 };
100 
bt_bap_iso_find_cb(struct bt_bap_iso * iso,void * user_data)101 static bool bt_bap_iso_find_cb(struct bt_bap_iso *iso, void *user_data)
102 {
103 	struct bt_bap_iso_find_param *param = user_data;
104 	bool found;
105 
106 	found = param->func(iso, param->user_data);
107 	if (found) {
108 		param->iso = bt_bap_iso_ref(iso);
109 	}
110 
111 	return !found;
112 }
113 
bt_bap_iso_find(bt_bap_iso_func_t func,void * user_data)114 struct bt_bap_iso *bt_bap_iso_find(bt_bap_iso_func_t func, void *user_data)
115 {
116 	struct bt_bap_iso_find_param param = {
117 		.iso = NULL,
118 		.func = func,
119 		.user_data = user_data,
120 	};
121 
122 	bt_bap_iso_foreach(bt_bap_iso_find_cb, &param);
123 
124 	return param.iso;
125 }
126 
bt_bap_iso_init(struct bt_bap_iso * iso,struct bt_iso_chan_ops * ops)127 void bt_bap_iso_init(struct bt_bap_iso *iso, struct bt_iso_chan_ops *ops)
128 {
129 	iso->chan.ops = ops;
130 	iso->chan.qos = &iso->qos;
131 
132 	/* Setup points for both Tx and Rx
133 	 * This is due to the limitation in the ISO API where pointers like
134 	 * the `qos->tx` shall be initialized before the CIS is connected if
135 	 * ever want to use it for TX, and ditto for RX. They cannot be
136 	 * initialized after the CIS has been connected
137 	 */
138 	iso->chan.qos->rx = &iso->rx.qos;
139 	iso->chan.qos->rx->path = &iso->rx.path;
140 	iso->chan.qos->rx->path->cc = iso->rx.cc;
141 
142 	iso->chan.qos->tx = &iso->tx.qos;
143 	iso->chan.qos->tx->path = &iso->tx.path;
144 	iso->chan.qos->tx->path->cc = iso->tx.cc;
145 }
146 
bap_iso_get_iso_dir(bool unicast_client,struct bt_bap_iso * iso,enum bt_audio_dir dir)147 static struct bt_bap_iso_dir *bap_iso_get_iso_dir(bool unicast_client, struct bt_bap_iso *iso,
148 						  enum bt_audio_dir dir)
149 {
150 	/* TODO FIX FOR CLIENT */
151 	if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && unicast_client) {
152 		/* For the unicast client, the direction and tx/rx is reversed */
153 		if (dir == BT_AUDIO_DIR_SOURCE) {
154 			return &iso->rx;
155 		} else {
156 			return &iso->tx;
157 		}
158 	}
159 
160 	if (dir == BT_AUDIO_DIR_SINK) {
161 		return &iso->rx;
162 	} else {
163 		return &iso->tx;
164 	}
165 }
166 
is_unicast_client_ep(struct bt_bap_ep * ep)167 static bool is_unicast_client_ep(struct bt_bap_ep *ep)
168 {
169 	return IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && bt_bap_ep_is_unicast_client(ep);
170 }
171 
bt_bap_iso_bind_ep(struct bt_bap_iso * iso,struct bt_bap_ep * ep)172 void bt_bap_iso_bind_ep(struct bt_bap_iso *iso, struct bt_bap_ep *ep)
173 {
174 	struct bt_bap_iso_dir *iso_dir;
175 
176 	__ASSERT_NO_MSG(ep != NULL);
177 	__ASSERT_NO_MSG(iso != NULL);
178 	__ASSERT(ep->iso == NULL, "ep %p bound with iso %p already", ep, ep->iso);
179 	__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
180 		 "invalid dir: %u", ep->dir);
181 
182 	LOG_DBG("iso %p ep %p dir %s", iso, ep, bt_audio_dir_str(ep->dir));
183 
184 	iso_dir = bap_iso_get_iso_dir(is_unicast_client_ep(ep), iso, ep->dir);
185 	__ASSERT(iso_dir->ep == NULL, "iso %p bound with ep %p", iso, iso_dir);
186 	iso_dir->ep = ep;
187 
188 	ep->iso = bt_bap_iso_ref(iso);
189 }
190 
bt_bap_iso_unbind_ep(struct bt_bap_iso * iso,struct bt_bap_ep * ep)191 void bt_bap_iso_unbind_ep(struct bt_bap_iso *iso, struct bt_bap_ep *ep)
192 {
193 	struct bt_bap_iso_dir *iso_dir;
194 
195 	__ASSERT_NO_MSG(ep != NULL);
196 	__ASSERT_NO_MSG(iso != NULL);
197 	__ASSERT(ep->iso == iso, "ep %p not bound with iso %p, was bound to %p",
198 		 ep, iso, ep->iso);
199 	__ASSERT(ep->dir == BT_AUDIO_DIR_SINK || ep->dir == BT_AUDIO_DIR_SOURCE,
200 		 "Invalid dir: %u", ep->dir);
201 
202 	LOG_DBG("iso %p ep %p dir %s", iso, ep, bt_audio_dir_str(ep->dir));
203 
204 	iso_dir = bap_iso_get_iso_dir(is_unicast_client_ep(ep), iso, ep->dir);
205 	__ASSERT(iso_dir->ep == ep, "iso %p not bound with ep %p", iso, ep);
206 	iso_dir->ep = NULL;
207 
208 	bt_bap_iso_unref(ep->iso);
209 	ep->iso = NULL;
210 }
211 
bt_bap_iso_get_ep(bool unicast_client,struct bt_bap_iso * iso,enum bt_audio_dir dir)212 struct bt_bap_ep *bt_bap_iso_get_ep(bool unicast_client, struct bt_bap_iso *iso,
213 				    enum bt_audio_dir dir)
214 {
215 	struct bt_bap_iso_dir *iso_dir;
216 
217 	__ASSERT(dir == BT_AUDIO_DIR_SINK || dir == BT_AUDIO_DIR_SOURCE,
218 		 "invalid dir: %u", dir);
219 
220 	LOG_DBG("iso %p dir %s", iso, bt_audio_dir_str(dir));
221 
222 	iso_dir = bap_iso_get_iso_dir(unicast_client, iso, dir);
223 
224 	return iso_dir->ep;
225 }
226 
bt_bap_iso_get_paired_ep(const struct bt_bap_ep * ep)227 struct bt_bap_ep *bt_bap_iso_get_paired_ep(const struct bt_bap_ep *ep)
228 {
229 	if (ep->iso->rx.ep == ep) {
230 		return ep->iso->tx.ep;
231 	} else {
232 		return ep->iso->rx.ep;
233 	}
234 }
235 
236 #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)237 void bt_bap_iso_bind_stream(struct bt_bap_iso *bap_iso, struct bt_bap_stream *stream,
238 			    enum bt_audio_dir dir)
239 {
240 	struct bt_bap_iso_dir *bap_iso_ep;
241 
242 	__ASSERT_NO_MSG(stream != NULL);
243 	__ASSERT_NO_MSG(bap_iso != NULL);
244 	__ASSERT(stream->bap_iso == NULL, "stream %p bound with bap_iso %p already", stream,
245 		 stream->bap_iso);
246 
247 	LOG_DBG("bap_iso %p stream %p dir %s", bap_iso, stream, bt_audio_dir_str(dir));
248 
249 	/* For the unicast client, the direction and tx/rx is reversed */
250 	if (dir == BT_AUDIO_DIR_SOURCE) {
251 		bap_iso_ep = &bap_iso->rx;
252 	} else {
253 		bap_iso_ep = &bap_iso->tx;
254 	}
255 
256 	__ASSERT(bap_iso_ep->stream == NULL, "bap_iso %p bound with stream %p", bap_iso,
257 		 bap_iso_ep->stream);
258 	bap_iso_ep->stream = stream;
259 
260 	stream->bap_iso = bt_bap_iso_ref(bap_iso);
261 }
262 
bt_bap_iso_unbind_stream(struct bt_bap_iso * bap_iso,struct bt_bap_stream * stream,enum bt_audio_dir dir)263 void bt_bap_iso_unbind_stream(struct bt_bap_iso *bap_iso, struct bt_bap_stream *stream,
264 			      enum bt_audio_dir dir)
265 {
266 	struct bt_bap_iso_dir *bap_iso_ep;
267 
268 	__ASSERT_NO_MSG(stream != NULL);
269 	__ASSERT_NO_MSG(bap_iso != NULL);
270 	__ASSERT(stream->bap_iso != NULL, "stream %p not bound with an bap_iso", stream);
271 
272 	LOG_DBG("bap_iso %p stream %p dir %s", bap_iso, stream, bt_audio_dir_str(dir));
273 
274 	/* For the unicast client, the direction and tx/rx is reversed */
275 	if (dir == BT_AUDIO_DIR_SOURCE) {
276 		bap_iso_ep = &bap_iso->rx;
277 	} else {
278 		bap_iso_ep = &bap_iso->tx;
279 	}
280 
281 	__ASSERT(bap_iso_ep->stream == stream, "bap_iso %p (%p) not bound with stream %p (%p)",
282 		 bap_iso, bap_iso_ep->stream, stream, stream->bap_iso);
283 	bap_iso_ep->stream = NULL;
284 
285 	bt_bap_iso_unref(bap_iso);
286 	stream->bap_iso = NULL;
287 }
288 
bt_bap_iso_get_stream(struct bt_bap_iso * iso,enum bt_audio_dir dir)289 struct bt_bap_stream *bt_bap_iso_get_stream(struct bt_bap_iso *iso, enum bt_audio_dir dir)
290 {
291 	__ASSERT(dir == BT_AUDIO_DIR_SINK || dir == BT_AUDIO_DIR_SOURCE,
292 		 "invalid dir: %u", dir);
293 
294 	LOG_DBG("iso %p dir %s", iso, bt_audio_dir_str(dir));
295 
296 	/* For the unicast client, the direction and tx/rx is reversed */
297 	if (dir == BT_AUDIO_DIR_SOURCE) {
298 		return iso->rx.stream;
299 	} else {
300 		return iso->tx.stream;
301 	}
302 }
303 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
304