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