1 /*
2 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/sys/check.h>
8 #include <zephyr/bluetooth/gatt.h>
9 #include <zephyr/bluetooth/audio/audio.h>
10 #include <zephyr/bluetooth/audio/cap.h>
11 #include <zephyr/bluetooth/audio/tbs.h>
12 #include "cap_internal.h"
13 #include "ccid_internal.h"
14 #include "csip_internal.h"
15 #include "bap_endpoint.h"
16
17 #include <zephyr/logging/log.h>
18
19 LOG_MODULE_REGISTER(bt_cap_initiator, CONFIG_BT_CAP_INITIATOR_LOG_LEVEL);
20
21 #include "common/bt_str.h"
22
23 static const struct bt_cap_initiator_cb *cap_cb;
24
bt_cap_initiator_register_cb(const struct bt_cap_initiator_cb * cb)25 int bt_cap_initiator_register_cb(const struct bt_cap_initiator_cb *cb)
26 {
27 CHECKIF(cb == NULL) {
28 LOG_DBG("cb is NULL");
29 return -EINVAL;
30 }
31
32 CHECKIF(cap_cb != NULL) {
33 LOG_DBG("callbacks already registered");
34 return -EALREADY;
35 }
36
37 cap_cb = cb;
38
39 return 0;
40 }
41
42 struct valid_metadata_param {
43 bool stream_context_found;
44 bool valid;
45 };
46
data_func_cb(struct bt_data * data,void * user_data)47 static bool data_func_cb(struct bt_data *data, void *user_data)
48 {
49 struct valid_metadata_param *metadata_param = (struct valid_metadata_param *)user_data;
50
51 LOG_DBG("type %u len %u data %s", data->type, data->data_len,
52 bt_hex(data->data, data->data_len));
53
54 if (data->type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
55 if (data->data_len != 2) { /* Stream context size */
56 return false;
57 }
58
59 metadata_param->stream_context_found = true;
60 } else if (IS_ENABLED(CONFIG_BT_CCID) && data->type == BT_AUDIO_METADATA_TYPE_CCID_LIST) {
61 /* If the application supplies a CCID list, we verify that the CCIDs exist on our
62 * device
63 */
64 for (uint8_t i = 0U; i < data->data_len; i++) {
65 const uint8_t ccid = data->data[i];
66
67 if (bt_ccid_find_attr(ccid) == NULL) {
68 LOG_DBG("Unknown characterstic for CCID 0x%02X", ccid);
69 metadata_param->valid = false;
70
71 return false;
72 }
73 }
74 }
75
76 return true;
77 }
78
cap_initiator_valid_metadata(const uint8_t meta[],size_t meta_len)79 static bool cap_initiator_valid_metadata(const uint8_t meta[], size_t meta_len)
80 {
81 struct valid_metadata_param metadata_param = {
82 .stream_context_found = false,
83 .valid = true,
84 };
85 int err;
86
87 LOG_DBG("meta %p len %zu", meta, meta_len);
88
89 err = bt_audio_data_parse(meta, meta_len, data_func_cb, &metadata_param);
90 if (err != 0 && err != -ECANCELED) {
91 return false;
92 }
93
94 if (!metadata_param.stream_context_found) {
95 LOG_DBG("No streaming context supplied");
96 }
97
98 return metadata_param.stream_context_found && metadata_param.valid;
99 }
100
101 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
102 static struct bt_cap_broadcast_source {
103 struct bt_bap_broadcast_source *bap_broadcast;
104 } broadcast_sources[CONFIG_BT_BAP_BROADCAST_SRC_COUNT];
105
cap_initiator_broadcast_audio_start_valid_param(const struct bt_cap_initiator_broadcast_create_param * param)106 static bool cap_initiator_broadcast_audio_start_valid_param(
107 const struct bt_cap_initiator_broadcast_create_param *param)
108 {
109
110 for (size_t i = 0U; i < param->subgroup_count; i++) {
111 const struct bt_cap_initiator_broadcast_subgroup_param *subgroup_param;
112 const struct bt_audio_codec_cfg *codec_cfg;
113 bool valid_metadata;
114
115 subgroup_param = ¶m->subgroup_params[i];
116 codec_cfg = subgroup_param->codec_cfg;
117
118 /* Streaming Audio Context shall be present in CAP */
119
120 CHECKIF(codec_cfg == NULL) {
121 LOG_DBG("subgroup[%zu]->codec_cfg is NULL", i);
122 return false;
123 }
124
125 valid_metadata =
126 cap_initiator_valid_metadata(codec_cfg->meta, codec_cfg->meta_len);
127
128 CHECKIF(!valid_metadata) {
129 LOG_DBG("Invalid metadata supplied for subgroup[%zu]", i);
130 return false;
131 }
132 }
133
134 return true;
135 }
136
cap_initiator_broadcast_to_bap_broadcast_param(const struct bt_cap_initiator_broadcast_create_param * cap_param,struct bt_bap_broadcast_source_param * bap_param,struct bt_bap_broadcast_source_subgroup_param bap_subgroup_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT],struct bt_bap_broadcast_source_stream_param bap_stream_params[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT])137 static void cap_initiator_broadcast_to_bap_broadcast_param(
138 const struct bt_cap_initiator_broadcast_create_param *cap_param,
139 struct bt_bap_broadcast_source_param *bap_param,
140 struct bt_bap_broadcast_source_subgroup_param
141 bap_subgroup_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT],
142 struct bt_bap_broadcast_source_stream_param
143 bap_stream_params[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT])
144 {
145 size_t stream_cnt = 0U;
146
147 bap_param->params_count = cap_param->subgroup_count;
148 bap_param->params = bap_subgroup_params;
149 bap_param->qos = cap_param->qos;
150 bap_param->packing = cap_param->packing;
151 bap_param->encryption = cap_param->encryption;
152 if (bap_param->encryption) {
153 memcpy(bap_param->broadcast_code, cap_param->broadcast_code,
154 BT_AUDIO_BROADCAST_CODE_SIZE);
155 } else {
156 memset(bap_param->broadcast_code, 0, BT_AUDIO_BROADCAST_CODE_SIZE);
157 }
158
159 for (size_t i = 0U; i < bap_param->params_count; i++) {
160 const struct bt_cap_initiator_broadcast_subgroup_param *cap_subgroup_param =
161 &cap_param->subgroup_params[i];
162 struct bt_bap_broadcast_source_subgroup_param *bap_subgroup_param =
163 &bap_param->params[i];
164
165 bap_subgroup_param->codec_cfg = cap_subgroup_param->codec_cfg;
166 bap_subgroup_param->params_count = cap_subgroup_param->stream_count;
167 bap_subgroup_param->params = &bap_stream_params[stream_cnt];
168
169 for (size_t j = 0U; j < bap_subgroup_param->params_count; j++, stream_cnt++) {
170 const struct bt_cap_initiator_broadcast_stream_param *cap_stream_param =
171 &cap_subgroup_param->stream_params[j];
172 struct bt_bap_broadcast_source_stream_param *bap_stream_param =
173 &bap_subgroup_param->params[j];
174
175 bap_stream_param->stream = &cap_stream_param->stream->bap_stream;
176 #if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
177 bap_stream_param->data_len = cap_stream_param->data_len;
178 /* We do not need to copy the data, as that is the same type of struct, so
179 * we can just point to the CAP parameter data
180 */
181 bap_stream_param->data = cap_stream_param->data;
182 #endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
183 }
184 }
185 }
186
bt_cap_initiator_broadcast_audio_create(const struct bt_cap_initiator_broadcast_create_param * param,struct bt_cap_broadcast_source ** broadcast_source)187 int bt_cap_initiator_broadcast_audio_create(
188 const struct bt_cap_initiator_broadcast_create_param *param,
189 struct bt_cap_broadcast_source **broadcast_source)
190 {
191 struct bt_bap_broadcast_source_subgroup_param
192 bap_subgroup_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
193 struct bt_bap_broadcast_source_stream_param
194 bap_stream_params[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
195 struct bt_bap_broadcast_source_param bap_create_param = {0};
196
197 CHECKIF(param == NULL) {
198 LOG_DBG("param is NULL");
199 return -EINVAL;
200 }
201
202 CHECKIF(broadcast_source == NULL) {
203 LOG_DBG("source is NULL");
204 return -EINVAL;
205 }
206
207 if (!cap_initiator_broadcast_audio_start_valid_param(param)) {
208 return -EINVAL;
209 }
210
211 for (size_t i = 0; i < ARRAY_SIZE(broadcast_sources); i++) {
212 if (broadcast_sources[i].bap_broadcast == NULL) {
213 *broadcast_source = &broadcast_sources[i];
214 break;
215 }
216 }
217
218 cap_initiator_broadcast_to_bap_broadcast_param(param, &bap_create_param,
219 bap_subgroup_params, bap_stream_params);
220
221 return bt_bap_broadcast_source_create(&bap_create_param,
222 &(*broadcast_source)->bap_broadcast);
223 }
224
bt_cap_initiator_broadcast_audio_start(struct bt_cap_broadcast_source * broadcast_source,struct bt_le_ext_adv * adv)225 int bt_cap_initiator_broadcast_audio_start(struct bt_cap_broadcast_source *broadcast_source,
226 struct bt_le_ext_adv *adv)
227 {
228 CHECKIF(adv == NULL) {
229 LOG_DBG("adv is NULL");
230 return -EINVAL;
231 }
232
233 CHECKIF(broadcast_source == NULL) {
234 LOG_DBG("broadcast_source is NULL");
235 return -EINVAL;
236 }
237
238 return bt_bap_broadcast_source_start(broadcast_source->bap_broadcast, adv);
239 }
240
bt_cap_initiator_broadcast_audio_update(struct bt_cap_broadcast_source * broadcast_source,const uint8_t meta[],size_t meta_len)241 int bt_cap_initiator_broadcast_audio_update(struct bt_cap_broadcast_source *broadcast_source,
242 const uint8_t meta[], size_t meta_len)
243 {
244 CHECKIF(broadcast_source == NULL) {
245 LOG_DBG("broadcast_source is NULL");
246 return -EINVAL;
247 }
248
249 CHECKIF(meta == NULL) {
250 LOG_DBG("meta is NULL");
251 return -EINVAL;
252 }
253
254 if (!cap_initiator_valid_metadata(meta, meta_len)) {
255 LOG_DBG("Invalid metadata");
256 return -EINVAL;
257 }
258
259 return bt_bap_broadcast_source_update_metadata(broadcast_source->bap_broadcast, meta,
260 meta_len);
261 }
262
bt_cap_initiator_broadcast_audio_stop(struct bt_cap_broadcast_source * broadcast_source)263 int bt_cap_initiator_broadcast_audio_stop(struct bt_cap_broadcast_source *broadcast_source)
264 {
265 CHECKIF(broadcast_source == NULL) {
266 LOG_DBG("broadcast_source is NULL");
267 return -EINVAL;
268 }
269
270 return bt_bap_broadcast_source_stop(broadcast_source->bap_broadcast);
271 }
272
bt_cap_initiator_broadcast_audio_delete(struct bt_cap_broadcast_source * broadcast_source)273 int bt_cap_initiator_broadcast_audio_delete(struct bt_cap_broadcast_source *broadcast_source)
274 {
275 int err;
276
277 CHECKIF(broadcast_source == NULL) {
278 LOG_DBG("broadcast_source is NULL");
279 return -EINVAL;
280 }
281
282 err = bt_bap_broadcast_source_delete(broadcast_source->bap_broadcast);
283 if (err == 0) {
284 broadcast_source->bap_broadcast = NULL;
285 }
286
287 return err;
288 }
289
bt_cap_initiator_broadcast_get_id(const struct bt_cap_broadcast_source * broadcast_source,uint32_t * const broadcast_id)290 int bt_cap_initiator_broadcast_get_id(const struct bt_cap_broadcast_source *broadcast_source,
291 uint32_t *const broadcast_id)
292 {
293 CHECKIF(broadcast_source == NULL) {
294 LOG_DBG("broadcast_source is NULL");
295 return -EINVAL;
296 }
297
298 return bt_bap_broadcast_source_get_id(broadcast_source->bap_broadcast, broadcast_id);
299 }
300
bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source * broadcast_source,struct net_buf_simple * base_buf)301 int bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source *broadcast_source,
302 struct net_buf_simple *base_buf)
303 {
304 CHECKIF(broadcast_source == NULL) {
305 LOG_DBG("broadcast_source is NULL");
306 return -EINVAL;
307 }
308
309 return bt_bap_broadcast_source_get_base(broadcast_source->bap_broadcast, base_buf);
310 }
311
312 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
313
314 #if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
315
316 static void
bt_cap_initiator_discover_complete(struct bt_conn * conn,int err,const struct bt_csip_set_coordinator_csis_inst * csis_inst)317 bt_cap_initiator_discover_complete(struct bt_conn *conn, int err,
318 const struct bt_csip_set_coordinator_csis_inst *csis_inst)
319 {
320 if (cap_cb && cap_cb->unicast_discovery_complete) {
321 cap_cb->unicast_discovery_complete(conn, err, csis_inst);
322 }
323 }
324
bt_cap_initiator_unicast_discover(struct bt_conn * conn)325 int bt_cap_initiator_unicast_discover(struct bt_conn *conn)
326 {
327 CHECKIF(conn == NULL) {
328 LOG_DBG("NULL conn");
329 return -EINVAL;
330 }
331
332 return bt_cap_common_discover(conn, bt_cap_initiator_discover_complete);
333 }
334
valid_unicast_audio_start_param(const struct bt_cap_unicast_audio_start_param * param)335 static bool valid_unicast_audio_start_param(const struct bt_cap_unicast_audio_start_param *param)
336 {
337 struct bt_bap_unicast_group *unicast_group = NULL;
338
339 CHECKIF(param == NULL) {
340 LOG_DBG("param is NULL");
341 return false;
342 }
343
344 CHECKIF(param->count == 0) {
345 LOG_DBG("Invalid param->count: %u", param->count);
346 return false;
347 }
348
349 CHECKIF(param->stream_params == NULL) {
350 LOG_DBG("param->stream_params is NULL");
351 return false;
352 }
353
354 CHECKIF(param->count > CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
355 LOG_DBG("param->count (%zu) is larger than "
356 "CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT (%d)",
357 param->count,
358 CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT);
359 return false;
360 }
361
362 for (size_t i = 0U; i < param->count; i++) {
363 const struct bt_cap_unicast_audio_start_stream_param *stream_param =
364 ¶m->stream_params[i];
365 const union bt_cap_set_member *member = &stream_param->member;
366 const struct bt_cap_stream *cap_stream = stream_param->stream;
367 const struct bt_audio_codec_cfg *codec_cfg = stream_param->codec_cfg;
368 const struct bt_bap_stream *bap_stream;
369 const struct bt_cap_common_client *client =
370 bt_cap_common_get_client(param->type, member);
371
372 if (client == NULL) {
373 LOG_DBG("Invalid param->members[%zu]", i);
374 return false;
375 }
376
377 CHECKIF(stream_param->codec_cfg == NULL) {
378 LOG_DBG("param->stream_params[%zu].codec_cfg is NULL", i);
379 return false;
380 }
381
382 CHECKIF(!cap_initiator_valid_metadata(codec_cfg->meta, codec_cfg->meta_len)) {
383 LOG_DBG("param->stream_params[%zu].codec_cfg is invalid", i);
384 return false;
385 }
386
387 CHECKIF(stream_param->ep == NULL) {
388 LOG_DBG("param->stream_params[%zu].ep is NULL", i);
389 return false;
390 }
391
392 CHECKIF(member == NULL) {
393 LOG_DBG("param->stream_params[%zu].member is NULL", i);
394 return false;
395 }
396
397 CHECKIF(cap_stream == NULL) {
398 LOG_DBG("param->streams[%zu] is NULL", i);
399 return false;
400 }
401
402 bap_stream = &cap_stream->bap_stream;
403
404 CHECKIF(bap_stream->ep != NULL) {
405 LOG_DBG("param->streams[%zu] is already started", i);
406 return false;
407 }
408
409 CHECKIF(bap_stream->group == NULL) {
410 LOG_DBG("param->streams[%zu] is not in a unicast group", i);
411 return false;
412 }
413
414 /* Use the group of the first stream for comparison */
415 if (unicast_group == NULL) {
416 unicast_group = bap_stream->group;
417 } else {
418 CHECKIF(bap_stream->group != unicast_group) {
419 LOG_DBG("param->streams[%zu] is not in this group %p", i,
420 unicast_group);
421 return false;
422 }
423 }
424
425 for (size_t j = 0U; j < i; j++) {
426 if (param->stream_params[j].stream == cap_stream) {
427 LOG_DBG("param->stream_params[%zu] (%p) is "
428 "duplicated by "
429 "param->stream_params[%zu] (%p)",
430 j, param->stream_params[j].stream,
431 i, cap_stream);
432 return false;
433 }
434 }
435 }
436
437 return true;
438 }
439
cap_initiator_unicast_audio_proc_complete(void)440 static void cap_initiator_unicast_audio_proc_complete(void)
441 {
442 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
443 enum bt_cap_common_proc_type proc_type;
444 struct bt_conn *failed_conn;
445 int err;
446
447 failed_conn = active_proc->failed_conn;
448 err = active_proc->err;
449 proc_type = active_proc->proc_type;
450 bt_cap_common_clear_active_proc();
451
452 if (cap_cb == NULL) {
453 return;
454 }
455
456 switch (proc_type) {
457 case BT_CAP_COMMON_PROC_TYPE_START:
458 if (cap_cb->unicast_start_complete != NULL) {
459 cap_cb->unicast_start_complete(err, failed_conn);
460 }
461 break;
462 case BT_CAP_COMMON_PROC_TYPE_UPDATE:
463 if (cap_cb->unicast_update_complete != NULL) {
464 cap_cb->unicast_update_complete(err, failed_conn);
465 }
466 break;
467 case BT_CAP_COMMON_PROC_TYPE_STOP:
468 if (cap_cb->unicast_stop_complete != NULL) {
469 cap_cb->unicast_stop_complete(err, failed_conn);
470 }
471 break;
472 case BT_CAP_COMMON_PROC_TYPE_NONE:
473 default:
474 __ASSERT(false, "Invalid proc_type: %u", proc_type);
475 }
476 }
477
cap_initiator_unicast_audio_configure(const struct bt_cap_unicast_audio_start_param * param)478 static int cap_initiator_unicast_audio_configure(
479 const struct bt_cap_unicast_audio_start_param *param)
480 {
481 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
482 struct bt_cap_initiator_proc_param *proc_param;
483 struct bt_audio_codec_cfg *codec_cfg;
484 struct bt_bap_stream *bap_stream;
485 struct bt_bap_ep *ep;
486 struct bt_conn *conn;
487 int err;
488 /** TODO: If this is a CSIP set, then the order of the procedures may
489 * not match the order in the parameters, and the CSIP ordered access
490 * procedure should be used.
491 */
492
493 for (size_t i = 0U; i < param->count; i++) {
494 struct bt_cap_unicast_audio_start_stream_param *stream_param =
495 ¶m->stream_params[i];
496 union bt_cap_set_member *member = &stream_param->member;
497 struct bt_cap_stream *cap_stream = stream_param->stream;
498
499 conn = bt_cap_common_get_member_conn(param->type, member);
500
501 /* Ensure that ops are registered before any procedures are started */
502 bt_cap_stream_ops_register_bap(cap_stream);
503
504 /* Store the necessary parameters as we cannot assume that the supplied parameters
505 * are kept valid
506 */
507 active_proc->proc_param.initiator[i].stream = cap_stream;
508 active_proc->proc_param.initiator[i].start.ep = stream_param->ep;
509 active_proc->proc_param.initiator[i].start.conn = conn;
510 memcpy(&active_proc->proc_param.initiator[i].start.codec_cfg,
511 stream_param->codec_cfg, sizeof(*stream_param->codec_cfg));
512 }
513
514 /* Store the information about the active procedure so that we can
515 * continue the procedure after each step
516 */
517 bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_START, param->count);
518 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_CODEC_CONFIG);
519
520 proc_param = &active_proc->proc_param.initiator[0];
521 bap_stream = &proc_param->stream->bap_stream;
522 codec_cfg = &proc_param->start.codec_cfg;
523 conn = proc_param->start.conn;
524 ep = proc_param->start.ep;
525 active_proc->proc_initiated_cnt++;
526
527 /* Since BAP operations may require a write long or a read long on the notification,
528 * we cannot assume that we can do multiple streams at once, thus do it one at a time.
529 * TODO: We should always be able to do one per ACL, so there is room for optimization.
530 */
531 err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg);
532 if (err != 0) {
533 LOG_DBG("Failed to config stream %p: %d", proc_param->stream, err);
534
535 bt_cap_common_clear_active_proc();
536 }
537
538 return err;
539 }
540
bt_cap_initiator_unicast_audio_start(const struct bt_cap_unicast_audio_start_param * param)541 int bt_cap_initiator_unicast_audio_start(const struct bt_cap_unicast_audio_start_param *param)
542 {
543 if (bt_cap_common_proc_is_active()) {
544 LOG_DBG("A CAP procedure is already in progress");
545
546 return -EBUSY;
547 }
548
549 if (!valid_unicast_audio_start_param(param)) {
550 return -EINVAL;
551 }
552
553 return cap_initiator_unicast_audio_configure(param);
554 }
555
bt_cap_initiator_codec_configured(struct bt_cap_stream * cap_stream)556 void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
557 {
558 struct bt_conn
559 *conns[MIN(CONFIG_BT_MAX_CONN, CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT)];
560 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
561 struct bt_cap_initiator_proc_param *proc_param;
562 struct bt_bap_unicast_group *unicast_group;
563
564 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
565 /* State change happened outside of a procedure; ignore */
566 return;
567 }
568
569 if (bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE)) {
570 /* When releasing a stream, it may go into the codec configured state if
571 * the unicast server caches the configuration - We treat it as a release
572 */
573 bt_cap_initiator_released(cap_stream);
574 return;
575 } else if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CODEC_CONFIG)) {
576 /* Unexpected callback - Abort */
577 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
578 } else {
579 active_proc->proc_done_cnt++;
580
581 LOG_DBG("Stream %p configured (%zu/%zu streams done)", cap_stream,
582 active_proc->proc_done_cnt, active_proc->proc_cnt);
583 }
584
585 if (bt_cap_common_proc_is_aborted()) {
586 if (bt_cap_common_proc_all_handled()) {
587 cap_initiator_unicast_audio_proc_complete();
588 }
589
590 return;
591 }
592
593 if (!bt_cap_common_proc_is_done()) {
594 const size_t proc_done_cnt = active_proc->proc_done_cnt;
595 struct bt_cap_stream *next_cap_stream;
596 struct bt_audio_codec_cfg *codec_cfg;
597 struct bt_bap_stream *bap_stream;
598 struct bt_conn *conn;
599 struct bt_bap_ep *ep;
600 int err;
601
602 proc_param = &active_proc->proc_param.initiator[proc_done_cnt];
603 next_cap_stream = proc_param->stream;
604 conn = proc_param->start.conn;
605 ep = proc_param->start.ep;
606 codec_cfg = &proc_param->start.codec_cfg;
607 bap_stream = &next_cap_stream->bap_stream;
608 active_proc->proc_initiated_cnt++;
609
610 /* Since BAP operations may require a write long or a read long on the notification,
611 * we cannot assume that we can do multiple streams at once, thus do it one at a
612 * time.
613 * TODO: We should always be able to do one per ACL, so there is room for
614 * optimization.
615 */
616 err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg);
617 if (err != 0) {
618 LOG_DBG("Failed to config stream %p: %d", next_cap_stream, err);
619
620 bt_cap_common_abort_proc(conn, err);
621 cap_initiator_unicast_audio_proc_complete();
622 }
623
624 return;
625 }
626
627 /* The QoS Configure procedure works on a set of connections and a
628 * unicast group, so we generate a list of unique connection pointers
629 * for the procedure
630 */
631 (void)memset(conns, 0, sizeof(conns));
632 for (size_t i = 0U; i < active_proc->proc_cnt; i++) {
633 struct bt_conn *stream_conn =
634 active_proc->proc_param.initiator[i].stream->bap_stream.conn;
635 struct bt_conn **free_conn = NULL;
636 bool already_added = false;
637
638 for (size_t j = 0U; j < ARRAY_SIZE(conns); j++) {
639 if (stream_conn == conns[j]) {
640 already_added = true;
641 break;
642 } else if (conns[j] == NULL && free_conn == NULL) {
643 free_conn = &conns[j];
644 }
645 }
646
647 if (already_added) {
648 continue;
649 }
650
651 if (free_conn != NULL) {
652 *free_conn = stream_conn;
653 } else {
654 __ASSERT_PRINT("No free conns");
655 }
656 }
657
658 /* All streams in the procedure share the same unicast group, so we just
659 * use the reference from the first stream
660 */
661 proc_param = &active_proc->proc_param.initiator[0];
662 unicast_group = (struct bt_bap_unicast_group *)proc_param->stream->bap_stream.group;
663 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG);
664
665 for (size_t i = 0U; i < ARRAY_SIZE(conns); i++) {
666 int err;
667
668 /* When conns[i] is NULL, we have QoS Configured all unique connections */
669 if (conns[i] == NULL) {
670 break;
671 }
672
673 active_proc->proc_initiated_cnt++;
674
675 err = bt_bap_stream_qos(conns[i], unicast_group);
676 if (err != 0) {
677 LOG_DBG("Failed to set stream QoS for conn %p and group %p: %d",
678 (void *)conns[i], unicast_group, err);
679
680 /* End or mark procedure as aborted.
681 * If we have sent any requests over air, we will abort
682 * once all sent requests has completed
683 */
684 bt_cap_common_abort_proc(conns[i], err);
685 if (i == 0U) {
686 cap_initiator_unicast_audio_proc_complete();
687 }
688
689 return;
690 }
691 }
692 }
693
bt_cap_initiator_qos_configured(struct bt_cap_stream * cap_stream)694 void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
695 {
696 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
697 struct bt_cap_initiator_proc_param *proc_param;
698 struct bt_cap_stream *next_cap_stream;
699 struct bt_bap_stream *bap_stream;
700 int err;
701
702 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
703 /* State change happened outside of a procedure; ignore */
704 return;
705 }
706
707 if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG)) {
708 /* Unexpected callback - Abort */
709 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
710 } else {
711 active_proc->proc_done_cnt++;
712
713 LOG_DBG("Stream %p QoS configured (%zu/%zu streams done)", cap_stream,
714 active_proc->proc_done_cnt, active_proc->proc_cnt);
715 }
716
717 if (bt_cap_common_proc_is_aborted()) {
718 if (bt_cap_common_proc_all_handled()) {
719 cap_initiator_unicast_audio_proc_complete();
720 }
721
722 return;
723 }
724
725 if (!bt_cap_common_proc_is_done()) {
726 /* Not yet finished, wait for all */
727 return;
728 }
729
730 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_ENABLE);
731 proc_param = &active_proc->proc_param.initiator[0];
732 next_cap_stream = proc_param->stream;
733 bap_stream = &next_cap_stream->bap_stream;
734 active_proc->proc_initiated_cnt++;
735
736 /* Since BAP operations may require a write long or a read long on the notification, we
737 * cannot assume that we can do multiple streams at once, thus do it one at a time.
738 * TODO: We should always be able to do one per ACL, so there is room for optimization.
739 */
740 err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta,
741 bap_stream->codec_cfg->meta_len);
742 if (err != 0) {
743 LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err);
744
745 bt_cap_common_abort_proc(bap_stream->conn, err);
746 cap_initiator_unicast_audio_proc_complete();
747 }
748 }
749
bt_cap_initiator_enabled(struct bt_cap_stream * cap_stream)750 void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
751 {
752 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
753 struct bt_cap_initiator_proc_param *proc_param;
754 struct bt_bap_stream *bap_stream;
755 int err;
756
757 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
758 /* State change happened outside of a procedure; ignore */
759 return;
760 }
761
762 if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_ENABLE)) {
763 /* Unexpected callback - Abort */
764 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
765 } else {
766 active_proc->proc_done_cnt++;
767
768 LOG_DBG("Stream %p enabled (%zu/%zu streams done)", cap_stream,
769 active_proc->proc_done_cnt, active_proc->proc_cnt);
770 }
771
772 if (bt_cap_common_proc_is_aborted()) {
773 if (bt_cap_common_proc_all_handled()) {
774 cap_initiator_unicast_audio_proc_complete();
775 }
776
777 return;
778 }
779
780 if (!bt_cap_common_proc_is_done()) {
781 struct bt_cap_stream *next_cap_stream =
782 active_proc->proc_param.initiator[active_proc->proc_done_cnt].stream;
783 struct bt_bap_stream *next_bap_stream = &next_cap_stream->bap_stream;
784
785 active_proc->proc_initiated_cnt++;
786
787 /* Since BAP operations may require a write long or a read long on the notification,
788 * we cannot assume that we can do multiple streams at once, thus do it one at a
789 * time.
790 * TODO: We should always be able to do one per ACL, so there is room for
791 * optimization.
792 */
793 err = bt_bap_stream_enable(next_bap_stream, next_bap_stream->codec_cfg->meta,
794 next_bap_stream->codec_cfg->meta_len);
795 if (err != 0) {
796 LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err);
797
798 bt_cap_common_abort_proc(next_bap_stream->conn, err);
799 cap_initiator_unicast_audio_proc_complete();
800 }
801
802 return;
803 }
804
805 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_START);
806 proc_param = &active_proc->proc_param.initiator[0];
807 bap_stream = &proc_param->stream->bap_stream;
808
809 /* Since BAP operations may require a write long or a read long on the notification, we
810 * cannot assume that we can do multiple streams at once, thus do it one at a time.
811 * TODO: We should always be able to do one per ACL, so there is room for optimization.
812 */
813 err = bt_bap_stream_start(bap_stream);
814 if (err != 0) {
815 LOG_DBG("Failed to start stream %p: %d", proc_param->stream, err);
816
817 /* End and mark procedure as aborted.
818 * If we have sent any requests over air, we will abort
819 * once all sent requests has completed
820 */
821 bt_cap_common_abort_proc(bap_stream->conn, err);
822 cap_initiator_unicast_audio_proc_complete();
823
824 return;
825 }
826 }
827
bt_cap_initiator_started(struct bt_cap_stream * cap_stream)828 void bt_cap_initiator_started(struct bt_cap_stream *cap_stream)
829 {
830 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
831
832 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
833 /* State change happened outside of a procedure; ignore */
834 return;
835 }
836
837 if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_START)) {
838 /* Unexpected callback - Abort */
839 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
840 } else {
841 active_proc->proc_done_cnt++;
842
843 LOG_DBG("Stream %p started (%zu/%zu streams done)", cap_stream,
844 active_proc->proc_done_cnt, active_proc->proc_cnt);
845 }
846
847 /* Since bt_bap_stream_start connects the ISO, we can, at this point,
848 * only do this one by one due to a restriction in the ISO layer
849 * (maximum 1 outstanding ISO connection request at any one time).
850 */
851 if (!bt_cap_common_proc_is_done()) {
852 struct bt_cap_stream *next_cap_stream =
853 active_proc->proc_param.initiator[active_proc->proc_done_cnt].stream;
854 struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream;
855 int err;
856
857 /* Not yet finished, start next */
858 err = bt_bap_stream_start(bap_stream);
859 if (err != 0) {
860 LOG_DBG("Failed to start stream %p: %d", next_cap_stream, err);
861
862 /* End and mark procedure as aborted.
863 * If we have sent any requests over air, we will abort
864 * once all sent requests has completed
865 */
866 bt_cap_common_abort_proc(bap_stream->conn, err);
867 cap_initiator_unicast_audio_proc_complete();
868 }
869 } else {
870 cap_initiator_unicast_audio_proc_complete();
871 }
872 }
873
can_update_metadata(const struct bt_bap_stream * bap_stream)874 static bool can_update_metadata(const struct bt_bap_stream *bap_stream)
875 {
876 struct bt_bap_ep_info ep_info;
877 int err;
878
879 if (bap_stream->conn == NULL) {
880 return false;
881 }
882
883 err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
884 if (err != 0) {
885 LOG_DBG("Failed to get endpoint info %p: %d", bap_stream, err);
886
887 return false;
888 }
889
890 return ep_info.state == BT_BAP_EP_STATE_ENABLING ||
891 ep_info.state == BT_BAP_EP_STATE_STREAMING;
892 }
893
valid_unicast_audio_update_param(const struct bt_cap_unicast_audio_update_param * param)894 static bool valid_unicast_audio_update_param(const struct bt_cap_unicast_audio_update_param *param)
895 {
896 struct bt_bap_unicast_group *unicast_group = NULL;
897
898 CHECKIF(param == NULL) {
899 LOG_DBG("param is NULL");
900 return false;
901 }
902
903 CHECKIF(param->count == 0) {
904 LOG_DBG("Invalid param->count: %u", param->count);
905 return false;
906 }
907
908 CHECKIF(param->stream_params == NULL) {
909 LOG_DBG("param->stream_params is NULL");
910 return false;
911 }
912
913 CHECKIF(param->count > CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
914 LOG_DBG("param->count (%zu) is larger than "
915 "CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT (%d)",
916 param->count, CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT);
917 return false;
918 }
919
920 for (size_t i = 0U; i < param->count; i++) {
921 const struct bt_cap_unicast_audio_update_stream_param *stream_param =
922 ¶m->stream_params[i];
923 const struct bt_cap_stream *cap_stream = stream_param->stream;
924 const struct bt_bap_stream *bap_stream;
925 struct bt_cap_common_client *client;
926 struct bt_conn *conn;
927
928 CHECKIF(cap_stream == NULL) {
929 LOG_DBG("param->stream_params[%zu] is NULL", i);
930 return false;
931 }
932
933 bap_stream = &cap_stream->bap_stream;
934 conn = bap_stream->conn;
935 CHECKIF(conn == NULL) {
936 LOG_DBG("param->stream_params[%zu].stream->bap_stream.conn is NULL", i);
937
938 return -EINVAL;
939 }
940
941 client = bt_cap_common_get_client_by_acl(conn);
942 if (!client->cas_found) {
943 LOG_DBG("CAS was not found for param->stream_params[%zu].stream", i);
944 return false;
945 }
946
947 CHECKIF(bap_stream->group == NULL) {
948 LOG_DBG("param->stream_params[%zu] is not in a unicast group", i);
949 return false;
950 }
951
952 /* Use the group of the first stream for comparison */
953 if (unicast_group == NULL) {
954 unicast_group = bap_stream->group;
955 } else {
956 CHECKIF(bap_stream->group != unicast_group) {
957 LOG_DBG("param->stream_params[%zu] is not in this group %p", i,
958 unicast_group);
959 return false;
960 }
961 }
962
963 if (!can_update_metadata(bap_stream)) {
964 LOG_DBG("param->stream_params[%zu].stream is not in right state to be "
965 "updated",
966 i);
967
968 return false;
969 }
970
971 if (!cap_initiator_valid_metadata(stream_param->meta, stream_param->meta_len)) {
972 LOG_DBG("param->stream_params[%zu] invalid metadata", i);
973
974 return false;
975 }
976
977 for (size_t j = 0U; j < i; j++) {
978 if (param->stream_params[j].stream == cap_stream) {
979 LOG_DBG("param->stream_params[%zu] (%p) is "
980 "duplicated by "
981 "param->stream_params[%zu] (%p)",
982 j, param->stream_params[j].stream, i, cap_stream);
983 return false;
984 }
985 }
986 }
987
988 return true;
989 }
990
bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_update_param * param)991 int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_update_param *param)
992 {
993 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
994 struct bt_cap_initiator_proc_param *proc_param;
995 struct bt_bap_stream *bap_stream;
996 const uint8_t *meta;
997 size_t meta_len;
998 int err;
999
1000 if (bt_cap_common_proc_is_active()) {
1001 LOG_DBG("A CAP procedure is already in progress");
1002
1003 return -EBUSY;
1004 }
1005
1006 if (!valid_unicast_audio_update_param(param)) {
1007 return -EINVAL;
1008 }
1009
1010 for (size_t i = 0U; i < param->count; i++) {
1011 const struct bt_cap_unicast_audio_update_stream_param *stream_param =
1012 ¶m->stream_params[i];
1013 struct bt_cap_stream *cap_stream = stream_param->stream;
1014
1015 active_proc->proc_param.initiator[i].stream = cap_stream;
1016 active_proc->proc_param.initiator[i].meta_update.meta_len = stream_param->meta_len;
1017 memcpy(&active_proc->proc_param.initiator[i].meta_update.meta, stream_param->meta,
1018 stream_param->meta_len);
1019 }
1020
1021 bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_UPDATE, param->count);
1022 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_META_UPDATE);
1023
1024 proc_param = &active_proc->proc_param.initiator[0];
1025 bap_stream = &proc_param->stream->bap_stream;
1026 meta_len = proc_param->meta_update.meta_len;
1027 meta = proc_param->meta_update.meta;
1028 active_proc->proc_initiated_cnt++;
1029
1030 err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
1031 if (err != 0) {
1032 LOG_DBG("Failed to update metadata for stream %p: %d", proc_param->stream, err);
1033
1034 bt_cap_common_clear_active_proc();
1035 }
1036
1037 return err;
1038 }
1039
bt_cap_initiator_unicast_audio_cancel(void)1040 int bt_cap_initiator_unicast_audio_cancel(void)
1041 {
1042 if (!bt_cap_common_proc_is_active() && !bt_cap_common_proc_is_aborted()) {
1043 LOG_DBG("No CAP procedure is in progress");
1044
1045 return -EALREADY;
1046 }
1047
1048 bt_cap_common_abort_proc(NULL, -ECANCELED);
1049 cap_initiator_unicast_audio_proc_complete();
1050
1051 return 0;
1052 }
1053
bt_cap_initiator_metadata_updated(struct bt_cap_stream * cap_stream)1054 void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
1055 {
1056 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
1057
1058 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
1059 /* State change happened outside of a procedure; ignore */
1060 return;
1061 }
1062
1063 if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_META_UPDATE)) {
1064 /* Unexpected callback - Abort */
1065 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
1066 } else {
1067 active_proc->proc_done_cnt++;
1068
1069 LOG_DBG("Stream %p QoS metadata updated (%zu/%zu streams done)", cap_stream,
1070 active_proc->proc_done_cnt, active_proc->proc_cnt);
1071 }
1072
1073 if (bt_cap_common_proc_is_aborted()) {
1074 if (bt_cap_common_proc_all_handled()) {
1075 cap_initiator_unicast_audio_proc_complete();
1076 }
1077
1078 return;
1079 }
1080
1081 if (!bt_cap_common_proc_is_done()) {
1082 const size_t proc_done_cnt = active_proc->proc_done_cnt;
1083 struct bt_cap_initiator_proc_param *proc_param;
1084 struct bt_cap_stream *next_cap_stream;
1085 struct bt_bap_stream *bap_stream;
1086 const uint8_t *meta;
1087 size_t meta_len;
1088 int err;
1089
1090 proc_param = &active_proc->proc_param.initiator[proc_done_cnt];
1091 meta_len = proc_param->meta_update.meta_len;
1092 meta = proc_param->meta_update.meta;
1093 next_cap_stream = proc_param->stream;
1094 bap_stream = &next_cap_stream->bap_stream;
1095 active_proc->proc_initiated_cnt++;
1096
1097 /* Since BAP operations may require a write long or a read long on the notification,
1098 * we cannot assume that we can do multiple streams at once, thus do it one at a
1099 * time.
1100 * TODO: We should always be able to do one per ACL, so there is room for
1101 * optimization.
1102 */
1103
1104 err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
1105 if (err != 0) {
1106 LOG_DBG("Failed to update metadata for stream %p: %d", next_cap_stream,
1107 err);
1108
1109 bt_cap_common_abort_proc(bap_stream->conn, err);
1110 cap_initiator_unicast_audio_proc_complete();
1111 }
1112
1113 return;
1114 }
1115
1116 cap_initiator_unicast_audio_proc_complete();
1117 }
1118
can_release(const struct bt_bap_stream * bap_stream)1119 static bool can_release(const struct bt_bap_stream *bap_stream)
1120 {
1121 struct bt_bap_ep_info ep_info;
1122 int err;
1123
1124 if (bap_stream->conn == NULL) {
1125 return false;
1126 }
1127
1128 err = bt_bap_ep_get_info(bap_stream->ep, &ep_info);
1129 if (err != 0) {
1130 LOG_DBG("Failed to get endpoint info %p: %d", bap_stream, err);
1131
1132 return false;
1133 }
1134
1135 return ep_info.state != BT_BAP_EP_STATE_IDLE;
1136 }
1137
valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_stop_param * param)1138 static bool valid_unicast_audio_stop_param(const struct bt_cap_unicast_audio_stop_param *param)
1139 {
1140 struct bt_bap_unicast_group *unicast_group = NULL;
1141
1142 CHECKIF(param == NULL) {
1143 LOG_DBG("param is NULL");
1144 return false;
1145 }
1146
1147 CHECKIF(param->count == 0) {
1148 LOG_DBG("Invalid param->count: %u", param->count);
1149 return false;
1150 }
1151
1152 CHECKIF(param->streams == NULL) {
1153 LOG_DBG("param->streams is NULL");
1154 return false;
1155 }
1156
1157 CHECKIF(param->count > CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
1158 LOG_DBG("param->count (%zu) is larger than "
1159 "CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT (%d)",
1160 param->count, CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT);
1161 return false;
1162 }
1163
1164 for (size_t i = 0U; i < param->count; i++) {
1165 const struct bt_cap_stream *cap_stream = param->streams[i];
1166 const struct bt_bap_stream *bap_stream;
1167 struct bt_cap_common_client *client;
1168 struct bt_conn *conn;
1169
1170 CHECKIF(cap_stream == NULL) {
1171 LOG_DBG("param->streams[%zu] is NULL", i);
1172 return false;
1173 }
1174
1175 bap_stream = &cap_stream->bap_stream;
1176 conn = bap_stream->conn;
1177 CHECKIF(conn == NULL) {
1178 LOG_DBG("param->streams[%zu]->bap_stream.conn is NULL", i);
1179
1180 return -EINVAL;
1181 }
1182
1183 client = bt_cap_common_get_client_by_acl(conn);
1184 if (!client->cas_found) {
1185 LOG_DBG("CAS was not found for param->streams[%zu]", i);
1186 return false;
1187 }
1188
1189 CHECKIF(bap_stream->group == NULL) {
1190 LOG_DBG("param->streams[%zu] is not in a unicast group", i);
1191 return false;
1192 }
1193
1194 /* Use the group of the first stream for comparison */
1195 if (unicast_group == NULL) {
1196 unicast_group = bap_stream->group;
1197 } else {
1198 CHECKIF(bap_stream->group != unicast_group) {
1199 LOG_DBG("param->streams[%zu] is not in this group %p", i,
1200 unicast_group);
1201 return false;
1202 }
1203 }
1204
1205 if (!can_release(bap_stream)) {
1206 LOG_DBG("Cannot stop param->streams[%zu]", i);
1207
1208 return false;
1209 }
1210
1211 for (size_t j = 0U; j < i; j++) {
1212 if (param->streams[j] == cap_stream) {
1213 LOG_DBG("param->stream_params[%zu] (%p) is "
1214 "duplicated by "
1215 "param->stream_params[%zu] (%p)",
1216 j, param->streams[j], i, cap_stream);
1217 return false;
1218 }
1219 }
1220 }
1221
1222 return true;
1223 }
1224
bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_param * param)1225 int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_param *param)
1226 {
1227 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
1228 struct bt_cap_initiator_proc_param *proc_param;
1229 struct bt_bap_stream *bap_stream;
1230 int err;
1231
1232 if (bt_cap_common_proc_is_active()) {
1233 LOG_DBG("A CAP procedure is already in progress");
1234
1235 return -EBUSY;
1236 }
1237
1238 if (!valid_unicast_audio_stop_param(param)) {
1239 return -EINVAL;
1240 }
1241
1242 for (size_t i = 0U; i < param->count; i++) {
1243 struct bt_cap_stream *cap_stream = param->streams[i];
1244
1245 active_proc->proc_param.initiator[i].stream = cap_stream;
1246 }
1247
1248 bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_STOP, param->count);
1249
1250 bt_cap_common_set_subproc(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE);
1251
1252 /** TODO: If this is a CSIP set, then the order of the procedures may
1253 * not match the order in the parameters, and the CSIP ordered access
1254 * procedure should be used.
1255 */
1256 proc_param = &active_proc->proc_param.initiator[0];
1257 bap_stream = &proc_param->stream->bap_stream;
1258 active_proc->proc_initiated_cnt++;
1259
1260 err = bt_bap_stream_release(bap_stream);
1261 if (err != 0) {
1262 LOG_DBG("Failed to stop bap_stream %p: %d", proc_param->stream, err);
1263
1264 bt_cap_common_clear_active_proc();
1265 }
1266
1267 return err;
1268 }
1269
bt_cap_initiator_released(struct bt_cap_stream * cap_stream)1270 void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
1271 {
1272 struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
1273
1274 if (!bt_cap_common_stream_in_active_proc(cap_stream)) {
1275 /* State change happened outside of a procedure; ignore */
1276 return;
1277 }
1278
1279 if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_RELEASE)) {
1280 /* Unexpected callback - Abort */
1281 bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG);
1282 } else {
1283 active_proc->proc_done_cnt++;
1284
1285 LOG_DBG("Stream %p released (%zu/%zu streams done)", cap_stream,
1286 active_proc->proc_done_cnt, active_proc->proc_cnt);
1287 }
1288
1289 if (bt_cap_common_proc_is_aborted()) {
1290 if (bt_cap_common_proc_all_handled()) {
1291 cap_initiator_unicast_audio_proc_complete();
1292 }
1293
1294 return;
1295 }
1296
1297 if (!bt_cap_common_proc_is_done()) {
1298 struct bt_cap_stream *next_cap_stream =
1299 active_proc->proc_param.initiator[active_proc->proc_done_cnt].stream;
1300 struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream;
1301 int err;
1302
1303 active_proc->proc_initiated_cnt++;
1304 /* Since BAP operations may require a write long or a read long on the notification,
1305 * we cannot assume that we can do multiple streams at once, thus do it one at a
1306 * time.
1307 * TODO: We should always be able to do one per ACL, so there is room for
1308 * optimization.
1309 */
1310 err = bt_bap_stream_release(bap_stream);
1311 if (err != 0) {
1312 LOG_DBG("Failed to release stream %p: %d", next_cap_stream, err);
1313
1314 bt_cap_common_abort_proc(bap_stream->conn, err);
1315 cap_initiator_unicast_audio_proc_complete();
1316 }
1317 } else {
1318 cap_initiator_unicast_audio_proc_complete();
1319 }
1320 }
1321
1322 #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
1323
1324 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) && defined(CONFIG_BT_BAP_UNICAST_CLIENT)
1325
bt_cap_initiator_unicast_to_broadcast(const struct bt_cap_unicast_to_broadcast_param * param,struct bt_cap_broadcast_source ** source)1326 int bt_cap_initiator_unicast_to_broadcast(
1327 const struct bt_cap_unicast_to_broadcast_param *param,
1328 struct bt_cap_broadcast_source **source)
1329 {
1330 return -ENOSYS;
1331 }
1332
bt_cap_initiator_broadcast_to_unicast(const struct bt_cap_broadcast_to_unicast_param * param,struct bt_bap_unicast_group ** unicast_group)1333 int bt_cap_initiator_broadcast_to_unicast(const struct bt_cap_broadcast_to_unicast_param *param,
1334 struct bt_bap_unicast_group **unicast_group)
1335 {
1336 return -ENOSYS;
1337 }
1338
1339 #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE && CONFIG_BT_BAP_UNICAST_CLIENT */
1340