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, ¶m);
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