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