1 /* btp_bap_unicast.c - Bluetooth BAP Tester */
2
3 /*
4 * Copyright (c) 2023 Codecoup
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <stddef.h>
10 #include <errno.h>
11
12 #include <zephyr/bluetooth/iso.h>
13 #include <zephyr/types.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/ring_buffer.h>
16 #include <zephyr/bluetooth/audio/audio.h>
17 #include <zephyr/bluetooth/hci_types.h>
18 #include <hci_core.h>
19
20 #include "bap_endpoint.h"
21 #include <zephyr/logging/log.h>
22 #include <zephyr/sys/byteorder.h>
23 #define LOG_MODULE_NAME bttester_bap_unicast
24 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
25 #include "btp/btp.h"
26 #include "btp_bap_audio_stream.h"
27 #include "btp_bap_unicast.h"
28
29 static struct bt_bap_qos_cfg_pref qos_pref =
30 BT_BAP_QOS_CFG_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 10000, 40000, 10000, 40000);
31
32 static struct btp_bap_unicast_connection connections[CONFIG_BT_MAX_CONN];
33 static struct btp_bap_unicast_group cigs[CONFIG_BT_ISO_MAX_CIG];
34
stream_bap_to_unicast(struct bt_bap_stream * stream)35 static inline struct btp_bap_unicast_stream *stream_bap_to_unicast(struct bt_bap_stream *stream)
36 {
37 return CONTAINER_OF(CONTAINER_OF(CONTAINER_OF(stream, struct bt_cap_stream, bap_stream),
38 struct btp_bap_audio_stream, cap_stream),
39 struct btp_bap_unicast_stream, audio_stream);
40 }
41
stream_unicast_to_bap(struct btp_bap_unicast_stream * stream)42 static inline struct bt_bap_stream *stream_unicast_to_bap(struct btp_bap_unicast_stream *stream)
43 {
44 return &stream->audio_stream.cap_stream.bap_stream;
45 }
46
print_cb(struct bt_data * data,void * user_data)47 static bool print_cb(struct bt_data *data, void *user_data)
48 {
49 const char *str = (const char *)user_data;
50
51 LOG_DBG("%s: type 0x%02x value_len %u", str, data->type, data->data_len);
52 LOG_HEXDUMP_DBG(data->data, data->data_len, "");
53
54 return true;
55 }
56
print_codec_cfg(const struct bt_audio_codec_cfg * codec_cfg)57 static void print_codec_cfg(const struct bt_audio_codec_cfg *codec_cfg)
58 {
59 LOG_DBG("codec_cfg 0x%02x cid 0x%04x vid 0x%04x count %u", codec_cfg->id, codec_cfg->cid,
60 codec_cfg->vid, codec_cfg->data_len);
61
62 if (codec_cfg->id == BT_HCI_CODING_FORMAT_LC3) {
63 enum bt_audio_location chan_allocation;
64 int ret;
65
66 /* LC3 uses the generic LTV format - other codecs might do as well */
67
68 bt_audio_data_parse(codec_cfg->data, codec_cfg->data_len, print_cb, "data");
69
70 ret = bt_audio_codec_cfg_get_freq(codec_cfg);
71 if (ret > 0) {
72 LOG_DBG(" Frequency: %d Hz", bt_audio_codec_cfg_freq_to_freq_hz(ret));
73 }
74
75 ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg);
76 if (ret > 0) {
77 LOG_DBG(" Frame Duration: %d us",
78 bt_audio_codec_cfg_frame_dur_to_frame_dur_us(ret));
79 }
80
81 ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation, false);
82 if (ret == 0) {
83 LOG_DBG(" Channel allocation: 0x%x", chan_allocation);
84 }
85
86 LOG_DBG(" Octets per frame: %d (negative means value not pressent)",
87 bt_audio_codec_cfg_get_octets_per_frame(codec_cfg));
88 LOG_DBG(" Frames per SDU: %d",
89 bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, true));
90 } else {
91 LOG_HEXDUMP_DBG(codec_cfg->data, codec_cfg->data_len, "data");
92 }
93
94 bt_audio_data_parse(codec_cfg->meta, codec_cfg->meta_len, print_cb, "meta");
95 }
96
print_codec_cap(const struct bt_audio_codec_cap * codec_cap)97 static void print_codec_cap(const struct bt_audio_codec_cap *codec_cap)
98 {
99 LOG_DBG("codec_cap 0x%02x cid 0x%04x vid 0x%04x count %zu", codec_cap->id, codec_cap->cid,
100 codec_cap->vid, codec_cap->data_len);
101
102 if (codec_cap->id == BT_HCI_CODING_FORMAT_LC3) {
103 bt_audio_data_parse(codec_cap->data, codec_cap->data_len, print_cb, "data");
104 } else {
105 LOG_HEXDUMP_DBG(codec_cap->data, codec_cap->data_len, "data");
106 }
107
108 bt_audio_data_parse(codec_cap->meta, codec_cap->meta_len, print_cb, "meta");
109 }
110
btp_bap_unicast_stream_free(struct btp_bap_unicast_stream * stream)111 void btp_bap_unicast_stream_free(struct btp_bap_unicast_stream *stream)
112 {
113 (void)memset(stream, 0, sizeof(*stream));
114 }
115
btp_bap_unicast_stream_find(struct btp_bap_unicast_connection * conn,uint8_t ase_id)116 struct btp_bap_unicast_stream *btp_bap_unicast_stream_find(
117 struct btp_bap_unicast_connection *conn, uint8_t ase_id)
118 {
119 for (size_t i = 0; i < ARRAY_SIZE(conn->streams); i++) {
120 struct bt_bap_stream *stream = stream_unicast_to_bap(&conn->streams[i]);
121 struct bt_bap_ep_info info;
122
123 if (stream->ep == NULL) {
124 continue;
125 }
126
127 (void)bt_bap_ep_get_info(stream->ep, &info);
128 if (info.id == ase_id) {
129 return &conn->streams[i];
130 }
131 }
132
133 return NULL;
134 }
135
btp_bap_unicast_end_point_find(struct btp_bap_unicast_connection * conn,uint8_t ase_id)136 struct bt_bap_ep *btp_bap_unicast_end_point_find(struct btp_bap_unicast_connection *conn,
137 uint8_t ase_id)
138 {
139 for (size_t i = 0; i < ARRAY_SIZE(conn->end_points); i++) {
140 struct bt_bap_ep_info info;
141 struct bt_bap_ep *ep = conn->end_points[i];
142
143 if (ep == NULL) {
144 continue;
145 }
146
147 (void)bt_bap_ep_get_info(ep, &info);
148 if (info.id == ase_id) {
149 return ep;
150 }
151 }
152
153 return NULL;
154 }
155
btp_send_ascs_ase_state_changed_ev(struct bt_conn * conn,uint8_t ase_id,uint8_t state)156 static void btp_send_ascs_ase_state_changed_ev(struct bt_conn *conn, uint8_t ase_id, uint8_t state)
157 {
158 struct btp_ascs_ase_state_changed_ev ev;
159
160 bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
161 ev.ase_id = ase_id;
162 ev.state = state;
163
164 tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_ASE_STATE_CHANGED, &ev, sizeof(ev));
165 }
166
btp_send_ascs_operation_completed_ev(struct bt_conn * conn,uint8_t ase_id,uint8_t opcode,uint8_t status)167 static void btp_send_ascs_operation_completed_ev(struct bt_conn *conn, uint8_t ase_id,
168 uint8_t opcode, uint8_t status)
169 {
170 struct btp_ascs_operation_completed_ev ev;
171
172 bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
173 ev.ase_id = ase_id;
174 ev.opcode = opcode;
175 ev.status = status;
176 ev.flags = 0;
177
178 tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_OPERATION_COMPLETED, &ev, sizeof(ev));
179 }
180
181 struct search_type_param {
182 uint8_t type;
183 const uint8_t *data;
184 };
185
data_type_search_cb(struct bt_data * data,void * user_data)186 static bool data_type_search_cb(struct bt_data *data, void *user_data)
187 {
188 struct search_type_param *param = (struct search_type_param *)user_data;
189
190 if (param->type == data->type) {
191 param->data = data->data;
192
193 return false;
194 }
195
196 return true;
197 }
198
codec_cap_get_val(const struct bt_audio_codec_cap * codec_cap,uint8_t type,const uint8_t ** data)199 static bool codec_cap_get_val(const struct bt_audio_codec_cap *codec_cap, uint8_t type,
200 const uint8_t **data)
201 {
202 struct search_type_param param = {
203 .type = type,
204 .data = NULL,
205 };
206 int err;
207
208 err = bt_audio_data_parse(codec_cap->data, codec_cap->data_len, data_type_search_cb,
209 ¶m);
210 if (err != 0 && err != -ECANCELED) {
211 LOG_DBG("Could not parse the data: %d", err);
212
213 return false;
214 }
215
216 if (param.data == NULL) {
217 LOG_DBG("Could not find the type %u", type);
218
219 return false;
220 }
221
222 *data = param.data;
223
224 return true;
225 }
226
btp_send_pac_codec_found_ev(struct bt_conn * conn,const struct bt_audio_codec_cap * codec_cap,enum bt_audio_dir dir)227 static void btp_send_pac_codec_found_ev(struct bt_conn *conn,
228 const struct bt_audio_codec_cap *codec_cap,
229 enum bt_audio_dir dir)
230 {
231 struct btp_bap_codec_cap_found_ev ev;
232 const uint8_t *data;
233
234 bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
235
236 ev.dir = dir;
237 ev.coding_format = codec_cap->id;
238
239 if (codec_cap_get_val(codec_cap, BT_AUDIO_CODEC_CAP_TYPE_FREQ, &data)) {
240 memcpy(&ev.frequencies, data, sizeof(ev.frequencies));
241 }
242
243 if (codec_cap_get_val(codec_cap, BT_AUDIO_CODEC_CAP_TYPE_DURATION, &data)) {
244 memcpy(&ev.frame_durations, data, sizeof(ev.frame_durations));
245 }
246
247 if (codec_cap_get_val(codec_cap, BT_AUDIO_CODEC_CAP_TYPE_FRAME_LEN, &data)) {
248 memcpy(&ev.octets_per_frame, data, sizeof(ev.octets_per_frame));
249 }
250
251 if (codec_cap_get_val(codec_cap, BT_AUDIO_CODEC_CAP_TYPE_CHAN_COUNT, &data)) {
252 memcpy(&ev.channel_counts, data, sizeof(ev.channel_counts));
253 }
254
255 tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_CODEC_CAP_FOUND, &ev, sizeof(ev));
256 }
257
btp_send_ase_found_ev(struct bt_conn * conn,struct bt_bap_ep * ep)258 static void btp_send_ase_found_ev(struct bt_conn *conn, struct bt_bap_ep *ep)
259 {
260 struct bt_bap_ep_info info;
261 struct btp_ascs_ase_found_ev ev;
262
263 bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
264
265 (void)bt_bap_ep_get_info(ep, &info);
266 ev.ase_id = info.id;
267 ev.dir = info.dir;
268
269 tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_ASE_FOUND, &ev, sizeof(ev));
270 }
271
print_qos(const struct bt_bap_qos_cfg * qos)272 static inline void print_qos(const struct bt_bap_qos_cfg *qos)
273 {
274 LOG_DBG("QoS: interval %u framing 0x%02x phy 0x%02x sdu %u rtn %u latency %u pd %u",
275 qos->interval, qos->framing, qos->phy, qos->sdu, qos->rtn, qos->latency, qos->pd);
276 }
277
validate_codec_parameters(const struct bt_audio_codec_cfg * codec_cfg)278 static int validate_codec_parameters(const struct bt_audio_codec_cfg *codec_cfg)
279 {
280 int frames_per_sdu;
281 int octets_per_frame;
282 int chan_allocation_err;
283 enum bt_audio_location chan_allocation;
284 int ret;
285
286 chan_allocation_err =
287 bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation, false);
288 octets_per_frame = bt_audio_codec_cfg_get_octets_per_frame(codec_cfg);
289 frames_per_sdu = bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, true);
290
291 ret = bt_audio_codec_cfg_get_freq(codec_cfg);
292 if (ret < 0) {
293 LOG_DBG("Error: Invalid codec frequency: %d", ret);
294 return -EINVAL;
295 }
296
297 ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg);
298 if (ret < 0) {
299 LOG_DBG("Error: Invalid frame duration: %d", ret);
300 return -EINVAL;
301 }
302
303 if (octets_per_frame < 0) {
304 LOG_DBG("Error: Invalid octets per frame.");
305 return -EINVAL;
306 }
307
308 if (frames_per_sdu < 0) {
309 /* The absence of the Codec_Frame_Blocks_Per_SDU LTV structure shall be
310 * interpreted as equivalent to a Codec_Frame_Blocks_Per_SDU value of 0x01
311 */
312 LOG_DBG("Codec_Frame_Blocks_Per_SDU LTV structure not defined.");
313 }
314
315 if (chan_allocation_err < 0) {
316 /* The absence of the Audio_Channel_Allocation LTV structure shall be
317 * interpreted as a single channel with no specified Audio Location.
318 */
319 LOG_DBG("Audio_Channel_Allocation LTV structure not defined.");
320 }
321
322 return 0;
323 }
324
lc3_config(struct bt_conn * conn,const struct bt_bap_ep * ep,enum bt_audio_dir dir,const struct bt_audio_codec_cfg * codec_cfg,struct bt_bap_stream ** stream,struct bt_bap_qos_cfg_pref * const pref,struct bt_bap_ascs_rsp * rsp)325 static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_audio_dir dir,
326 const struct bt_audio_codec_cfg *codec_cfg, struct bt_bap_stream **stream,
327 struct bt_bap_qos_cfg_pref *const pref, struct bt_bap_ascs_rsp *rsp)
328 {
329 struct bt_bap_ep_info info;
330 struct btp_bap_unicast_connection *u_conn;
331 struct btp_bap_unicast_stream *u_stream;
332
333 LOG_DBG("ASE Codec Config: ep %p dir %u", ep, dir);
334
335 print_codec_cfg(codec_cfg);
336 (void)bt_bap_ep_get_info(ep, &info);
337
338 if (validate_codec_parameters(codec_cfg)) {
339 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_CONF_REJECTED,
340 BT_BAP_ASCS_REASON_CODEC_DATA);
341
342 btp_send_ascs_operation_completed_ev(conn, info.id, BT_ASCS_CONFIG_OP,
343 BTP_ASCS_STATUS_FAILED);
344
345 return -ENOTSUP;
346 }
347
348 u_conn = &connections[bt_conn_index(conn)];
349 u_stream = btp_bap_unicast_stream_alloc(u_conn);
350 if (u_stream == NULL) {
351 LOG_DBG("No free stream available");
352 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_NO_MEM, BT_BAP_ASCS_REASON_NONE);
353
354 btp_send_ascs_operation_completed_ev(conn, info.id, BT_ASCS_CONFIG_OP,
355 BTP_ASCS_STATUS_FAILED);
356
357 return -ENOMEM;
358 }
359
360 *stream = stream_unicast_to_bap(u_stream);
361 LOG_DBG("ASE Codec Config stream %p", *stream);
362
363 if (dir == BT_AUDIO_DIR_SOURCE) {
364 u_conn->configured_source_stream_count++;
365 } else {
366 u_conn->configured_sink_stream_count++;
367 }
368
369 *pref = qos_pref;
370
371 return 0;
372 }
373
lc3_reconfig(struct bt_bap_stream * stream,enum bt_audio_dir dir,const struct bt_audio_codec_cfg * codec_cfg,struct bt_bap_qos_cfg_pref * const pref,struct bt_bap_ascs_rsp * rsp)374 static int lc3_reconfig(struct bt_bap_stream *stream, enum bt_audio_dir dir,
375 const struct bt_audio_codec_cfg *codec_cfg,
376 struct bt_bap_qos_cfg_pref *const pref, struct bt_bap_ascs_rsp *rsp)
377 {
378 LOG_DBG("ASE Codec Reconfig: stream %p", stream);
379
380 print_codec_cfg(codec_cfg);
381
382 return 0;
383 }
384
lc3_qos(struct bt_bap_stream * stream,const struct bt_bap_qos_cfg * qos,struct bt_bap_ascs_rsp * rsp)385 static int lc3_qos(struct bt_bap_stream *stream, const struct bt_bap_qos_cfg *qos,
386 struct bt_bap_ascs_rsp *rsp)
387 {
388 LOG_DBG("QoS: stream %p qos %p", stream, qos);
389
390 print_qos(qos);
391
392 return 0;
393 }
394
valid_metadata_type(uint8_t type,uint8_t len,const uint8_t * data)395 static bool valid_metadata_type(uint8_t type, uint8_t len, const uint8_t *data)
396 {
397 /* PTS checks if we are able to reject unsupported metadata type or RFU vale.
398 * The only RFU value PTS seems to check for now is the streaming context.
399 */
400 if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(type)) {
401 return false;
402 }
403
404 if (type == BT_AUDIO_METADATA_TYPE_PREF_CONTEXT ||
405 type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
406 /* PTS wants us to reject the parameter if reserved bits are set */
407 if ((sys_get_le16(data) & ~(uint16_t)(BT_AUDIO_CONTEXT_TYPE_ANY)) > 0) {
408 return false;
409 }
410 }
411
412 return true;
413 }
414
data_func_cb(struct bt_data * data,void * user_data)415 static bool data_func_cb(struct bt_data *data, void *user_data)
416 {
417 struct bt_bap_ascs_rsp *rsp = (struct bt_bap_ascs_rsp *)user_data;
418
419 if (!valid_metadata_type(data->type, data->data_len, data->data)) {
420 LOG_DBG("Invalid metadata type %u or length %u", data->type, data->data_len);
421 *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED, data->type);
422 return false;
423 }
424
425 return true;
426 }
427
lc3_enable(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len,struct bt_bap_ascs_rsp * rsp)428 static int lc3_enable(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len,
429 struct bt_bap_ascs_rsp *rsp)
430 {
431 int err;
432 struct bt_bap_ep_info info;
433
434 LOG_DBG("Metadata: stream %p meta_len %zu", stream, meta_len);
435
436 err = bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
437 if (err != 0) {
438 (void)bt_bap_ep_get_info(stream->ep, &info);
439 btp_send_ascs_operation_completed_ev(stream->conn, info.id,
440 BT_ASCS_ENABLE_OP, BTP_ASCS_STATUS_FAILED);
441 }
442
443 return err;
444 }
445
lc3_start(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)446 static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
447 {
448 LOG_DBG("Start: stream %p", stream);
449
450 return 0;
451 }
452
lc3_metadata(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len,struct bt_bap_ascs_rsp * rsp)453 static int lc3_metadata(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len,
454 struct bt_bap_ascs_rsp *rsp)
455 {
456 int err;
457 struct bt_bap_ep_info info;
458
459 LOG_DBG("Metadata: stream %p meta_count %zu", stream, meta_len);
460
461 err = bt_audio_data_parse(meta, meta_len, data_func_cb, rsp);
462 if (err != 0) {
463 (void)bt_bap_ep_get_info(stream->ep, &info);
464 btp_send_ascs_operation_completed_ev(stream->conn, info.id,
465 BT_ASCS_METADATA_OP, BTP_ASCS_STATUS_FAILED);
466 }
467
468 return err;
469 }
470
lc3_disable(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)471 static int lc3_disable(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
472 {
473 LOG_DBG("Disable: stream %p", stream);
474
475 return 0;
476 }
477
lc3_stop(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)478 static int lc3_stop(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
479 {
480 LOG_DBG("Stop: stream %p", stream);
481
482 return 0;
483 }
484
lc3_release(struct bt_bap_stream * stream,struct bt_bap_ascs_rsp * rsp)485 static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
486 {
487 LOG_DBG("Release: stream %p", stream);
488
489 return 0;
490 }
491
492 static const struct bt_bap_unicast_server_cb unicast_server_cb = {
493 .config = lc3_config,
494 .reconfig = lc3_reconfig,
495 .qos = lc3_qos,
496 .enable = lc3_enable,
497 .start = lc3_start,
498 .metadata = lc3_metadata,
499 .disable = lc3_disable,
500 .stop = lc3_stop,
501 .release = lc3_release,
502 };
503
stream_configured(struct bt_bap_stream * stream,const struct bt_bap_qos_cfg_pref * pref)504 static void stream_configured(struct bt_bap_stream *stream, const struct bt_bap_qos_cfg_pref *pref)
505 {
506 struct bt_bap_ep_info info;
507 struct btp_bap_unicast_connection *u_conn;
508 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
509
510 (void)bt_bap_ep_get_info(stream->ep, &info);
511 LOG_DBG("Configured stream %p, ep %u, dir %u", stream, info.id, info.dir);
512
513 u_stream->conn_id = bt_conn_index(stream->conn);
514 u_conn = &connections[u_stream->conn_id];
515 u_stream->ase_id = info.id;
516
517 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
518 BT_ASCS_CONFIG_OP, BTP_ASCS_STATUS_SUCCESS);
519 }
520
stream_qos_set(struct bt_bap_stream * stream)521 static void stream_qos_set(struct bt_bap_stream *stream)
522 {
523 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
524
525 LOG_DBG("QoS set stream %p", stream);
526 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
527 BT_ASCS_QOS_OP, BTP_ASCS_STATUS_SUCCESS);
528 }
529
stream_enabled(struct bt_bap_stream * stream)530 static void stream_enabled(struct bt_bap_stream *stream)
531 {
532 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
533 struct bt_bap_ep_info info;
534 struct bt_conn_info conn_info;
535 int err;
536
537 LOG_DBG("Enabled stream %p", stream);
538
539 (void)bt_bap_ep_get_info(stream->ep, &info);
540 (void)bt_conn_get_info(stream->conn, &conn_info);
541 if (conn_info.role == BT_HCI_ROLE_PERIPHERAL && info.dir == BT_AUDIO_DIR_SINK) {
542 /* Automatically do the receiver start ready operation */
543 err = bt_bap_stream_start(stream);
544 if (err != 0) {
545 LOG_DBG("Failed to start stream %p", stream);
546 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
547 BT_ASCS_ENABLE_OP,
548 BTP_ASCS_STATUS_FAILED);
549
550 return;
551 }
552 }
553
554 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
555 BT_ASCS_ENABLE_OP, BTP_ASCS_STATUS_SUCCESS);
556 }
557
stream_metadata_updated(struct bt_bap_stream * stream)558 static void stream_metadata_updated(struct bt_bap_stream *stream)
559 {
560 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
561
562 LOG_DBG("Metadata updated stream %p", stream);
563 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
564 BT_ASCS_METADATA_OP, BTP_ASCS_STATUS_SUCCESS);
565 }
566
stream_disabled(struct bt_bap_stream * stream)567 static void stream_disabled(struct bt_bap_stream *stream)
568 {
569 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
570
571 LOG_DBG("Disabled stream %p", stream);
572
573 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
574 BT_ASCS_DISABLE_OP, BTP_ASCS_STATUS_SUCCESS);
575 }
576
stream_released(struct bt_bap_stream * stream)577 static void stream_released(struct bt_bap_stream *stream)
578 {
579 uint8_t cig_id;
580 struct bt_bap_ep_info info;
581 struct btp_bap_unicast_connection *u_conn;
582 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
583
584 LOG_DBG("Released stream %p", stream);
585
586 u_conn = &connections[u_stream->conn_id];
587
588 if (stream->ep != NULL) {
589 (void)bt_bap_ep_get_info(stream->ep, &info);
590 if (info.dir == BT_AUDIO_DIR_SINK) {
591 u_conn->configured_sink_stream_count--;
592 } else {
593 u_conn->configured_source_stream_count--;
594 }
595 }
596
597 cig_id = u_stream->cig_id;
598 btp_bap_unicast_stream_free(u_stream);
599
600 if (cigs[cig_id].in_use == true &&
601 u_conn->configured_sink_stream_count == 0 &&
602 u_conn->configured_source_stream_count == 0) {
603 struct btp_bap_unicast_group *u_cig = &cigs[cig_id];
604
605 /* The unicast group will be deleted only at release of the last stream */
606 LOG_DBG("Deleting unicast group");
607
608 int err = bt_bap_unicast_group_delete(u_cig->cig);
609
610 if (err != 0) {
611 LOG_DBG("Unable to delete unicast group: %d", err);
612
613 return;
614 }
615
616 memset(u_cig, 0, sizeof(*u_cig));
617 }
618 }
619
stream_started(struct bt_bap_stream * stream)620 static void stream_started(struct bt_bap_stream *stream)
621 {
622 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
623 struct bt_bap_ep_info info;
624
625 /* Callback called on transition to Streaming state */
626
627 LOG_DBG("Started stream %p", stream);
628
629 /* Start TX */
630 if (btp_bap_audio_stream_can_send(&u_stream->audio_stream)) {
631 int err;
632
633 err = btp_bap_audio_stream_tx_register(&u_stream->audio_stream);
634 if (err != 0) {
635 LOG_ERR("Failed to register stream: %d", err);
636 }
637 }
638
639 (void)bt_bap_ep_get_info(stream->ep, &info);
640 btp_send_ascs_ase_state_changed_ev(stream->conn, u_stream->ase_id, info.state);
641 }
642
stream_connected(struct bt_bap_stream * stream)643 static void stream_connected(struct bt_bap_stream *stream)
644 {
645 struct bt_conn_info conn_info;
646 struct bt_bap_ep_info ep_info;
647 int err;
648
649 LOG_DBG("Connected stream %p", stream);
650
651 (void)bt_bap_ep_get_info(stream->ep, &ep_info);
652 (void)bt_conn_get_info(stream->conn, &conn_info);
653 if (conn_info.role == BT_HCI_ROLE_CENTRAL && ep_info.dir == BT_AUDIO_DIR_SOURCE) {
654 /* Automatically do the receiver start ready operation for source ASEs as the client
655 */
656 err = bt_bap_stream_start(stream);
657 if (err != 0) {
658 LOG_ERR("Failed to start stream %p", stream);
659 }
660 }
661 }
662
stream_stopped(struct bt_bap_stream * stream,uint8_t reason)663 static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
664 {
665 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
666
667 LOG_DBG("Stopped stream %p with reason 0x%02X", stream, reason);
668
669 if (btp_bap_audio_stream_can_send(&u_stream->audio_stream)) {
670 int err;
671
672 err = btp_bap_audio_stream_tx_unregister(&u_stream->audio_stream);
673 if (err != 0) {
674 LOG_ERR("Failed to unregister stream: %d", err);
675 }
676 }
677
678 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
679 BT_ASCS_STOP_OP, BTP_STATUS_SUCCESS);
680 }
681
send_stream_received_ev(struct bt_conn * conn,struct bt_bap_ep * ep,uint8_t data_len,uint8_t * data)682 static void send_stream_received_ev(struct bt_conn *conn, struct bt_bap_ep *ep,
683 uint8_t data_len, uint8_t *data)
684 {
685 struct btp_bap_stream_received_ev *ev;
686
687 tester_rsp_buffer_lock();
688 tester_rsp_buffer_allocate(sizeof(*ev) + data_len, (uint8_t **)&ev);
689
690 LOG_DBG("Stream received, ep %d, dir %d, len %d", ep->status.id, ep->dir,
691 data_len);
692
693 bt_addr_le_copy(&ev->address, bt_conn_get_dst(conn));
694
695 ev->ase_id = ep->status.id;
696 ev->data_len = data_len;
697 memcpy(ev->data, data, data_len);
698
699 tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_STREAM_RECEIVED, ev, sizeof(*ev) + data_len);
700
701 tester_rsp_buffer_free();
702 tester_rsp_buffer_unlock();
703 }
704
stream_recv(struct bt_bap_stream * stream,const struct bt_iso_recv_info * info,struct net_buf * buf)705 static void stream_recv(struct bt_bap_stream *stream,
706 const struct bt_iso_recv_info *info,
707 struct net_buf *buf)
708 {
709 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
710
711 if (u_stream->already_sent == false) {
712 /* For now, send just a first packet, to limit the number
713 * of logs and not unnecessarily spam through btp.
714 */
715 LOG_DBG("Incoming audio on stream %p len %u flags 0x%02X seq_num %u and ts %u",
716 stream, buf->len, info->flags, info->seq_num, info->ts);
717
718 if ((info->flags & BT_ISO_FLAGS_VALID) == 0) {
719 u_stream->already_sent = true;
720 send_stream_received_ev(stream->conn, stream->ep, buf->len, buf->data);
721 }
722 }
723 }
724
725 static struct bt_bap_stream_ops stream_ops = {
726 .configured = stream_configured,
727 .qos_set = stream_qos_set,
728 .enabled = stream_enabled,
729 .metadata_updated = stream_metadata_updated,
730 .disabled = stream_disabled,
731 .released = stream_released,
732 .started = stream_started,
733 .stopped = stream_stopped,
734 .recv = stream_recv,
735 .sent = btp_bap_audio_stream_sent_cb,
736 .connected = stream_connected,
737 };
738
btp_bap_unicast_stream_alloc(struct btp_bap_unicast_connection * conn)739 struct btp_bap_unicast_stream *btp_bap_unicast_stream_alloc(
740 struct btp_bap_unicast_connection *conn)
741 {
742 for (size_t i = 0; i < ARRAY_SIZE(conn->streams); i++) {
743 struct btp_bap_unicast_stream *stream = &conn->streams[i];
744
745 if (stream->in_use == false) {
746 bt_bap_stream_cb_register(stream_unicast_to_bap(stream), &stream_ops);
747 stream->in_use = true;
748
749 return stream;
750 }
751 }
752
753 return NULL;
754 }
755
unicast_client_location_cb(struct bt_conn * conn,enum bt_audio_dir dir,enum bt_audio_location loc)756 static void unicast_client_location_cb(struct bt_conn *conn,
757 enum bt_audio_dir dir,
758 enum bt_audio_location loc)
759 {
760 LOG_DBG("dir %u loc %X", dir, loc);
761 }
762
available_contexts_cb(struct bt_conn * conn,enum bt_audio_context snk_ctx,enum bt_audio_context src_ctx)763 static void available_contexts_cb(struct bt_conn *conn,
764 enum bt_audio_context snk_ctx,
765 enum bt_audio_context src_ctx)
766 {
767 LOG_DBG("snk ctx %u src ctx %u", snk_ctx, src_ctx);
768 }
769
config_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)770 static void config_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
771 enum bt_bap_ascs_reason reason)
772 {
773 LOG_DBG("stream %p config operation rsp_code %u reason %u", stream, rsp_code, reason);
774 }
775
qos_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)776 static void qos_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
777 enum bt_bap_ascs_reason reason)
778 {
779 LOG_DBG("stream %p qos operation rsp_code %u reason %u", stream, rsp_code, reason);
780 }
781
enable_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)782 static void enable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
783 enum bt_bap_ascs_reason reason)
784 {
785 LOG_DBG("stream %p enable operation rsp_code %u reason %u", stream, rsp_code, reason);
786 }
787
start_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)788 static void start_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
789 enum bt_bap_ascs_reason reason)
790 {
791 struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream);
792
793 /* Callback called on Receiver Start Ready notification from ASE Control Point */
794
795 LOG_DBG("stream %p start operation rsp_code %u reason %u", stream, rsp_code, reason);
796 u_stream->already_sent = false;
797
798 btp_send_ascs_operation_completed_ev(stream->conn, u_stream->ase_id,
799 BT_ASCS_START_OP, BTP_STATUS_SUCCESS);
800 }
801
stop_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)802 static void stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
803 enum bt_bap_ascs_reason reason)
804 {
805 LOG_DBG("stream %p stop operation rsp_code %u reason %u", stream, rsp_code, reason);
806 }
807
disable_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)808 static void disable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
809 enum bt_bap_ascs_reason reason)
810 {
811 LOG_DBG("stream %p disable operation rsp_code %u reason %u", stream, rsp_code, reason);
812 }
813
metadata_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)814 static void metadata_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
815 enum bt_bap_ascs_reason reason)
816 {
817 LOG_DBG("stream %p metadata operation rsp_code %u reason %u", stream, rsp_code, reason);
818 }
819
release_cb(struct bt_bap_stream * stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)820 static void release_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
821 enum bt_bap_ascs_reason reason)
822 {
823 LOG_DBG("stream %p release operation rsp_code %u reason %u", stream, rsp_code, reason);
824 }
825
pac_record_cb(struct bt_conn * conn,enum bt_audio_dir dir,const struct bt_audio_codec_cap * codec_cap)826 static void pac_record_cb(struct bt_conn *conn, enum bt_audio_dir dir,
827 const struct bt_audio_codec_cap *codec_cap)
828 {
829 LOG_DBG("");
830
831 if (codec_cap != NULL) {
832 LOG_DBG("Discovered codec capabilities %p", codec_cap);
833 print_codec_cap(codec_cap);
834 btp_send_pac_codec_found_ev(conn, codec_cap, dir);
835 }
836 }
837
btp_send_discovery_completed_ev(struct bt_conn * conn,uint8_t status)838 static void btp_send_discovery_completed_ev(struct bt_conn *conn, uint8_t status)
839 {
840 struct btp_bap_discovery_completed_ev ev;
841
842 bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn));
843 ev.status = status;
844
845 tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_DISCOVERY_COMPLETED, &ev, sizeof(ev));
846 }
847
endpoint_cb(struct bt_conn * conn,enum bt_audio_dir dir,struct bt_bap_ep * ep)848 static void endpoint_cb(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep)
849 {
850 struct btp_bap_unicast_connection *u_conn;
851
852 LOG_DBG("");
853
854 if (ep != NULL) {
855 LOG_DBG("Discovered ASE %p, id %u, dir 0x%02x", ep, ep->status.id, ep->dir);
856
857 u_conn = &connections[bt_conn_index(conn)];
858
859 if (u_conn->end_points_count >= CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT +
860 CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT) {
861 LOG_DBG("Failed to cache ep %p due to configured limit: %zu", ep,
862 u_conn->end_points_count);
863
864 btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
865
866 return;
867 }
868
869 u_conn->end_points[u_conn->end_points_count++] = ep;
870 btp_send_ase_found_ev(conn, ep);
871
872 return;
873 }
874 }
875
discover_cb(struct bt_conn * conn,int err,enum bt_audio_dir dir)876 static void discover_cb(struct bt_conn *conn, int err, enum bt_audio_dir dir)
877 {
878 LOG_DBG("");
879
880 if (err != 0 && err != BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
881 LOG_DBG("Discover remote ASEs failed: %d", err);
882 btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
883 return;
884 }
885
886 LOG_DBG("Discover complete");
887
888 if (err == BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
889 LOG_DBG("Discover remote ASEs completed without finding any source ASEs");
890 } else {
891 LOG_DBG("Discover remote ASEs complete: err %d", err);
892 }
893
894 if (dir == BT_AUDIO_DIR_SINK) {
895 err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SOURCE);
896
897 if (err != 0) {
898 LOG_DBG("Failed to discover source ASEs: %d", err);
899 btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_FAILED);
900 }
901
902 return;
903 }
904
905 btp_send_discovery_completed_ev(conn, BTP_BAP_DISCOVERY_STATUS_SUCCESS);
906 }
907
908 static struct bt_bap_unicast_server_register_param param = {
909 CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT,
910 CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT
911 };
912
913 static struct bt_bap_unicast_client_cb unicast_client_cbs = {
914 .location = unicast_client_location_cb,
915 .available_contexts = available_contexts_cb,
916 .config = config_cb,
917 .qos = qos_cb,
918 .enable = enable_cb,
919 .start = start_cb,
920 .stop = stop_cb,
921 .disable = disable_cb,
922 .metadata = metadata_cb,
923 .release = release_cb,
924 .pac_record = pac_record_cb,
925 .endpoint = endpoint_cb,
926 .discover = discover_cb,
927 };
928
btp_bap_discover(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)929 uint8_t btp_bap_discover(const void *cmd, uint16_t cmd_len,
930 void *rsp, uint16_t *rsp_len)
931 {
932 const struct btp_bap_discover_cmd *cp = cmd;
933 struct bt_conn *conn;
934 struct btp_bap_unicast_connection *u_conn;
935 struct bt_conn_info conn_info;
936 int err;
937
938 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
939 if (!conn) {
940 LOG_ERR("Unknown connection");
941 return BTP_STATUS_FAILED;
942 }
943
944 u_conn = &connections[bt_conn_index(conn)];
945 (void)bt_conn_get_info(conn, &conn_info);
946
947 if (u_conn->end_points_count > 0 || conn_info.role != BT_HCI_ROLE_CENTRAL) {
948 bt_conn_unref(conn);
949
950 return BTP_STATUS_FAILED;
951 }
952
953 err = bt_bap_unicast_client_discover(conn, BT_AUDIO_DIR_SINK);
954 if (err != 0) {
955 LOG_DBG("Failed to discover remote ASEs: %d", err);
956 bt_conn_unref(conn);
957
958 return BTP_STATUS_FAILED;
959 }
960
961 bt_conn_unref(conn);
962
963 return BTP_STATUS_SUCCESS;
964 }
965
server_stream_config(struct bt_conn * conn,struct bt_bap_stream * stream,struct bt_audio_codec_cfg * codec_cfg,struct bt_bap_qos_cfg_pref * qos)966 static int server_stream_config(struct bt_conn *conn, struct bt_bap_stream *stream,
967 struct bt_audio_codec_cfg *codec_cfg,
968 struct bt_bap_qos_cfg_pref *qos)
969 {
970 int err;
971 struct bt_bap_ep_info info;
972
973 err = bt_bap_unicast_server_config_ase(conn, stream, codec_cfg, qos);
974 if (err != 0) {
975 return err;
976 }
977
978 print_codec_cfg(codec_cfg);
979 (void)bt_bap_ep_get_info(stream->ep, &info);
980
981 LOG_DBG("ASE Codec Config: ase_id %u dir %u", info.id, info.dir);
982 LOG_DBG("ASE Codec Config stream %p", stream);
983
984 return 0;
985 }
986
client_add_ase_to_cis(struct btp_bap_unicast_connection * u_conn,uint8_t ase_id,uint8_t cis_id,uint8_t cig_id)987 static uint8_t client_add_ase_to_cis(struct btp_bap_unicast_connection *u_conn, uint8_t ase_id,
988 uint8_t cis_id, uint8_t cig_id)
989 {
990 struct btp_bap_unicast_stream *stream;
991
992 if (cig_id >= CONFIG_BT_ISO_MAX_CIG ||
993 cis_id >= CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) {
994 return -EINVAL;
995 }
996
997 stream = btp_bap_unicast_stream_find(u_conn, ase_id);
998 if (stream == NULL) {
999 return -EINVAL;
1000 }
1001
1002 LOG_DBG("Added ASE %u to CIS %u at CIG %u", ase_id, cis_id, cig_id);
1003
1004 stream->cig_id = cig_id;
1005 stream->cis_id = cis_id;
1006
1007 return 0;
1008 }
1009
client_unicast_group_param_set(struct btp_bap_unicast_connection * u_conn,uint8_t cig_id,struct bt_bap_unicast_group_stream_pair_param * pair_params,struct bt_bap_unicast_group_stream_param ** stream_param_ptr)1010 static int client_unicast_group_param_set(struct btp_bap_unicast_connection *u_conn, uint8_t cig_id,
1011 struct bt_bap_unicast_group_stream_pair_param *pair_params,
1012 struct bt_bap_unicast_group_stream_param **stream_param_ptr)
1013 {
1014 struct bt_bap_unicast_group_stream_param *stream_params = *stream_param_ptr;
1015
1016 for (size_t i = 0; i < ARRAY_SIZE(u_conn->streams); i++) {
1017 struct bt_bap_ep_info info;
1018 struct bt_bap_ep *ep;
1019 struct btp_bap_unicast_stream *u_stream = &u_conn->streams[i];
1020 struct bt_bap_stream *stream = stream_unicast_to_bap(u_stream);
1021
1022 if (u_stream->in_use == false || u_stream->cig_id != cig_id) {
1023 continue;
1024 }
1025
1026 ep = btp_bap_unicast_end_point_find(u_conn, u_stream->ase_id);
1027 if (ep == NULL) {
1028 return -EINVAL;
1029 }
1030
1031 stream_params->stream = stream;
1032 stream_params->qos = &cigs[u_stream->cig_id].qos[u_stream->cis_id];
1033
1034 (void)bt_bap_ep_get_info(ep, &info);
1035 if (info.dir == BT_AUDIO_DIR_SOURCE) {
1036 if (pair_params[u_stream->cis_id].rx_param != NULL) {
1037 return -EINVAL;
1038 }
1039
1040 pair_params[u_stream->cis_id].rx_param = stream_params;
1041 } else {
1042 if (pair_params[u_stream->cis_id].tx_param != NULL) {
1043 return -EINVAL;
1044 }
1045
1046 pair_params[u_stream->cis_id].tx_param = stream_params;
1047 }
1048
1049 stream_params++;
1050 }
1051
1052 *stream_param_ptr = stream_params;
1053
1054 return 0;
1055 }
1056
btp_bap_unicast_group_create(uint8_t cig_id,struct btp_bap_unicast_group ** out_unicast_group)1057 int btp_bap_unicast_group_create(uint8_t cig_id,
1058 struct btp_bap_unicast_group **out_unicast_group)
1059 {
1060 int err;
1061 struct bt_bap_unicast_group_stream_pair_param
1062 pair_params[BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
1063 struct bt_bap_unicast_group_stream_param stream_params[BTP_BAP_UNICAST_MAX_STREAMS_COUNT];
1064 struct bt_bap_unicast_group_stream_param *stream_param_ptr;
1065 struct bt_bap_unicast_group_param param;
1066 size_t cis_cnt = 0;
1067
1068 (void)memset(pair_params, 0, sizeof(pair_params));
1069 (void)memset(stream_params, 0, sizeof(stream_params));
1070 *out_unicast_group = NULL;
1071
1072 if (cig_id >= CONFIG_BT_ISO_MAX_CIG) {
1073 return -EINVAL;
1074 }
1075
1076 /* API does not allow to assign a CIG ID freely, so ensure we create groups
1077 * in the right order.
1078 */
1079 for (uint8_t i = 0; i < cig_id; i++) {
1080 if (cigs[i].in_use == false) {
1081 return -EINVAL;
1082 }
1083 }
1084
1085 if (cigs[cig_id].in_use == true) {
1086 struct btp_bap_unicast_group *u_cig = &cigs[cig_id];
1087
1088 err = bt_bap_unicast_group_delete(u_cig->cig);
1089 if (err != 0) {
1090 LOG_DBG("Failed to delete the unicast group, err %d", err);
1091
1092 return BTP_STATUS_FAILED;
1093 }
1094
1095 memset(u_cig, 0, sizeof(*u_cig));
1096 }
1097
1098 stream_param_ptr = stream_params;
1099 for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
1100 struct btp_bap_unicast_connection *unicast_conn = &connections[i];
1101
1102 if (unicast_conn->end_points_count == 0) {
1103 continue;
1104 }
1105
1106 /* CISes have been assigned earlier to CIGs with client_add_ase_to_cis() */
1107 err = client_unicast_group_param_set(unicast_conn, cig_id, pair_params,
1108 &stream_param_ptr);
1109 if (err != 0) {
1110 return err;
1111 }
1112 }
1113
1114 /* Count CISes to be established */
1115 for (size_t count = ARRAY_SIZE(pair_params); count > 0; --count) {
1116 size_t i = count - 1;
1117
1118 if (pair_params[i].tx_param != NULL || pair_params[i].rx_param != NULL) {
1119 cis_cnt++;
1120
1121 continue;
1122 }
1123
1124 if (cis_cnt > 0) {
1125 /* No gaps allowed */
1126 return -EINVAL;
1127 }
1128 }
1129
1130 param.params = pair_params;
1131 param.params_count = cis_cnt;
1132 param.packing = BT_ISO_PACKING_SEQUENTIAL;
1133
1134 LOG_DBG("Creating unicast group");
1135 err = bt_bap_unicast_group_create(¶m, &cigs[cig_id].cig);
1136 if (err != 0) {
1137 LOG_DBG("Could not create unicast group (err %d)", err);
1138 return -EINVAL;
1139 }
1140
1141 cigs[cig_id].in_use = true;
1142 cigs[cig_id].cig_id = cig_id;
1143 *out_unicast_group = &cigs[cig_id];
1144
1145 return 0;
1146 }
1147
btp_bap_unicast_group_find(uint8_t cig_id)1148 struct btp_bap_unicast_group *btp_bap_unicast_group_find(uint8_t cig_id)
1149 {
1150 if (cig_id >= CONFIG_BT_ISO_MAX_CIG) {
1151 return NULL;
1152 }
1153
1154 return &cigs[cig_id];
1155 }
1156
client_configure_codec(struct btp_bap_unicast_connection * u_conn,struct bt_conn * conn,uint8_t ase_id,struct bt_audio_codec_cfg * codec_cfg)1157 static int client_configure_codec(struct btp_bap_unicast_connection *u_conn, struct bt_conn *conn,
1158 uint8_t ase_id, struct bt_audio_codec_cfg *codec_cfg)
1159 {
1160 int err;
1161 struct bt_bap_ep *ep;
1162 struct btp_bap_unicast_stream *stream;
1163
1164 stream = btp_bap_unicast_stream_find(u_conn, ase_id);
1165 if (stream == NULL) {
1166 /* Configure a new stream */
1167 stream = btp_bap_unicast_stream_alloc(u_conn);
1168 if (stream == NULL) {
1169 LOG_DBG("No streams available");
1170
1171 return -ENOMEM;
1172 }
1173
1174 if (u_conn->end_points_count == 0) {
1175 return -EINVAL;
1176 }
1177
1178 ep = btp_bap_unicast_end_point_find(u_conn, ase_id);
1179 if (ep == NULL) {
1180 return -EINVAL;
1181 }
1182
1183 memcpy(&stream->codec_cfg, codec_cfg, sizeof(*codec_cfg));
1184 err = bt_bap_stream_config(conn, stream_unicast_to_bap(stream), ep,
1185 &stream->codec_cfg);
1186 } else {
1187 /* Reconfigure a stream */
1188 memcpy(&stream->codec_cfg, codec_cfg, sizeof(*codec_cfg));
1189 err = bt_bap_stream_reconfig(stream_unicast_to_bap(stream),
1190 &stream->codec_cfg);
1191 }
1192
1193 return err;
1194 }
1195
server_configure_codec(struct btp_bap_unicast_connection * u_conn,struct bt_conn * conn,uint8_t ase_id,struct bt_audio_codec_cfg * codec_cfg)1196 static int server_configure_codec(struct btp_bap_unicast_connection *u_conn, struct bt_conn *conn,
1197 uint8_t ase_id, struct bt_audio_codec_cfg *codec_cfg)
1198 {
1199 struct btp_bap_unicast_stream *stream;
1200 int err = 0;
1201
1202 stream = btp_bap_unicast_stream_find(u_conn, ase_id);
1203 if (stream == NULL) {
1204 /* Zephyr allocates ASE instances for remote clients dynamically.
1205 * To initiate Codec Config operation autonomously in server the role,
1206 * we have to initialize all ASEs with a smaller ID first.
1207 * Fortunately, the PTS has nothing against such behavior.
1208 */
1209 for (uint8_t i = 1; i <= ase_id; i++) {
1210 stream = btp_bap_unicast_stream_find(u_conn, i);
1211 if (stream != NULL) {
1212 continue;
1213 }
1214
1215 /* Configure a new stream */
1216 stream = btp_bap_unicast_stream_alloc(u_conn);
1217 if (stream == NULL) {
1218 LOG_DBG("No streams available");
1219
1220 return -ENOMEM;
1221 }
1222
1223 memcpy(&stream->codec_cfg, codec_cfg, sizeof(*codec_cfg));
1224 err = server_stream_config(conn, stream_unicast_to_bap(stream),
1225 &stream->codec_cfg, &qos_pref);
1226 }
1227 } else {
1228 /* Reconfigure a stream */
1229 memcpy(&stream->codec_cfg, codec_cfg, sizeof(*codec_cfg));
1230 err = bt_bap_stream_reconfig(stream_unicast_to_bap(stream), &stream->codec_cfg);
1231 }
1232
1233 return err;
1234 }
1235
btp_ascs_configure_codec(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1236 uint8_t btp_ascs_configure_codec(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
1237 {
1238 int err;
1239 const struct btp_ascs_configure_codec_cmd *cp = cmd;
1240 struct bt_conn *conn;
1241 struct bt_conn_info conn_info;
1242 struct btp_bap_unicast_connection *u_conn;
1243 struct bt_audio_codec_cfg codec_cfg;
1244
1245 LOG_DBG("");
1246
1247 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1248 if (!conn) {
1249 LOG_ERR("Unknown connection");
1250 return BTP_STATUS_FAILED;
1251 }
1252
1253 u_conn = &connections[bt_conn_index(conn)];
1254
1255 (void)bt_conn_get_info(conn, &conn_info);
1256
1257 memset(&codec_cfg, 0, sizeof(codec_cfg));
1258
1259 codec_cfg.id = cp->coding_format;
1260 codec_cfg.vid = cp->vid;
1261 codec_cfg.cid = cp->cid;
1262
1263 if (cp->cc_ltvs_len != 0) {
1264 codec_cfg.data_len = cp->cc_ltvs_len;
1265 memcpy(codec_cfg.data, cp->cc_ltvs, cp->cc_ltvs_len);
1266 }
1267
1268 if (conn_info.role == BT_HCI_ROLE_CENTRAL) {
1269 err = client_configure_codec(u_conn, conn, cp->ase_id, &codec_cfg);
1270 } else {
1271 err = server_configure_codec(u_conn, conn, cp->ase_id, &codec_cfg);
1272 }
1273
1274 bt_conn_unref(conn);
1275
1276 if (err) {
1277 LOG_DBG("Failed to configure stream (err %d)", err);
1278 return BTP_STATUS_FAILED;
1279 }
1280
1281 return BTP_STATUS_SUCCESS;
1282 }
1283
btp_ascs_preconfigure_qos(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1284 uint8_t btp_ascs_preconfigure_qos(const void *cmd, uint16_t cmd_len,
1285 void *rsp, uint16_t *rsp_len)
1286 {
1287 const struct btp_ascs_preconfigure_qos_cmd *cp = cmd;
1288 struct bt_bap_qos_cfg *qos;
1289
1290 LOG_DBG("");
1291
1292 qos = &cigs[cp->cig_id].qos[cp->cis_id];
1293 memset(qos, 0, sizeof(*qos));
1294
1295 qos->phy = BT_BAP_QOS_CFG_2M;
1296 qos->framing = cp->framing;
1297 qos->rtn = cp->retransmission_num;
1298 qos->sdu = sys_le16_to_cpu(cp->max_sdu);
1299 qos->latency = sys_le16_to_cpu(cp->max_transport_latency);
1300 qos->interval = sys_get_le24(cp->sdu_interval);
1301 qos->pd = sys_get_le24(cp->presentation_delay);
1302
1303 return BTP_STATUS_SUCCESS;
1304 }
1305
btp_ascs_configure_qos(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1306 uint8_t btp_ascs_configure_qos(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
1307 {
1308 int err;
1309 const struct btp_ascs_configure_qos_cmd *cp = cmd;
1310 struct bt_conn_info conn_info;
1311 struct bt_conn *conn;
1312 struct btp_bap_unicast_group *out_unicast_group;
1313
1314 LOG_DBG("");
1315
1316 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1317 if (!conn) {
1318 LOG_ERR("Unknown connection");
1319
1320 return BTP_STATUS_FAILED;
1321 }
1322
1323 (void)bt_conn_get_info(conn, &conn_info);
1324 if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
1325 bt_conn_unref(conn);
1326
1327 return BTP_STATUS_FAILED;
1328 }
1329
1330 if (cigs[cp->cig_id].in_use == false) {
1331 err = btp_bap_unicast_group_create(cp->cig_id, &out_unicast_group);
1332 if (err != 0) {
1333 LOG_DBG("Unable to create unicast group, err %d", err);
1334 bt_conn_unref(conn);
1335
1336 return BTP_STATUS_FAILED;
1337 }
1338 }
1339
1340 LOG_DBG("QoS configuring streams");
1341 err = bt_bap_stream_qos(conn, cigs[cp->cig_id].cig);
1342 bt_conn_unref(conn);
1343
1344 if (err != 0) {
1345 LOG_DBG("Unable to QoS configure streams: %d", err);
1346
1347 return BTP_STATUS_FAILED;
1348 }
1349
1350 return BTP_STATUS_SUCCESS;
1351 }
1352
btp_ascs_enable(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1353 uint8_t btp_ascs_enable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
1354 {
1355 int err;
1356 const struct btp_ascs_enable_cmd *cp = cmd;
1357 struct btp_bap_unicast_connection *u_conn;
1358 struct btp_bap_unicast_stream *stream;
1359 struct bt_conn *conn;
1360
1361 LOG_DBG("");
1362
1363 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1364 if (!conn) {
1365 LOG_ERR("Unknown connection");
1366 return BTP_STATUS_FAILED;
1367 }
1368
1369 u_conn = &connections[bt_conn_index(conn)];
1370 bt_conn_unref(conn);
1371
1372 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1373 if (stream == NULL) {
1374 return BTP_STATUS_FAILED;
1375 }
1376
1377 LOG_DBG("Enabling stream");
1378 err = bt_bap_stream_enable(stream_unicast_to_bap(stream), NULL, 0);
1379 if (err != 0) {
1380 LOG_DBG("Could not enable stream: %d", err);
1381 return BTP_STATUS_FAILED;
1382 }
1383
1384 return BTP_STATUS_SUCCESS;
1385 }
1386
btp_ascs_disable(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1387 uint8_t btp_ascs_disable(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
1388 {
1389 int err;
1390 const struct btp_ascs_disable_cmd *cp = cmd;
1391 struct btp_bap_unicast_connection *u_conn;
1392 struct btp_bap_unicast_stream *stream;
1393 struct bt_conn *conn;
1394
1395 LOG_DBG("");
1396
1397 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1398 if (!conn) {
1399 LOG_ERR("Unknown connection");
1400 return BTP_STATUS_FAILED;
1401 }
1402
1403 u_conn = &connections[bt_conn_index(conn)];
1404 bt_conn_unref(conn);
1405
1406 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1407 if (stream == NULL) {
1408 return BTP_STATUS_FAILED;
1409 }
1410
1411 LOG_DBG("Disabling stream");
1412
1413 err = bt_bap_stream_disable(stream_unicast_to_bap(stream));
1414 if (err != 0) {
1415 LOG_DBG("Could not disable stream: %d", err);
1416 return BTP_STATUS_FAILED;
1417 }
1418
1419 return BTP_STATUS_SUCCESS;
1420 }
1421
btp_ascs_receiver_start_ready(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1422 uint8_t btp_ascs_receiver_start_ready(const void *cmd, uint16_t cmd_len,
1423 void *rsp, uint16_t *rsp_len)
1424 {
1425 int err;
1426 const struct btp_ascs_receiver_start_ready_cmd *cp = cmd;
1427 struct btp_bap_unicast_connection *u_conn;
1428 struct btp_bap_unicast_stream *stream;
1429 struct bt_bap_stream *bap_stream;
1430 struct bt_bap_ep_info info;
1431 struct bt_conn *conn;
1432
1433 LOG_DBG("");
1434
1435 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1436 if (!conn) {
1437 LOG_ERR("Unknown connection");
1438 return BTP_STATUS_FAILED;
1439 }
1440
1441 u_conn = &connections[bt_conn_index(conn)];
1442 bt_conn_unref(conn);
1443
1444 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1445 if (stream == NULL) {
1446 return BTP_STATUS_FAILED;
1447 }
1448
1449 bap_stream = stream_unicast_to_bap(stream);
1450 (void)bt_bap_ep_get_info(bap_stream->ep, &info);
1451 if (info.state == BT_BAP_EP_STATE_STREAMING) {
1452 /* Already started */
1453 return BTP_STATUS_SUCCESS;
1454 }
1455
1456 LOG_DBG("Starting stream %p, ep %u, dir %u", bap_stream, cp->ase_id, info.dir);
1457
1458 while (true) {
1459 err = bt_bap_stream_connect(bap_stream);
1460 if (err == -EBUSY) {
1461 /* TODO: How to determine if a controller is ready again after
1462 * bt_bap_stream_start? In AC 6(i) tests the PTS sends Receiver Start Ready
1463 * only after all CISes are established.
1464 */
1465 k_sleep(K_MSEC(1000));
1466 continue;
1467 } else if (err != 0 && err != -EALREADY) {
1468 LOG_DBG("Could not connect stream: %d", err);
1469 return BTP_STATUS_FAILED;
1470 }
1471
1472 break;
1473 };
1474
1475 return BTP_STATUS_SUCCESS;
1476 }
1477
btp_ascs_receiver_stop_ready(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1478 uint8_t btp_ascs_receiver_stop_ready(const void *cmd, uint16_t cmd_len,
1479 void *rsp, uint16_t *rsp_len)
1480 {
1481 int err;
1482 const struct btp_ascs_receiver_stop_ready_cmd *cp = cmd;
1483 struct btp_bap_unicast_connection *u_conn;
1484 struct btp_bap_unicast_stream *stream;
1485 struct bt_conn *conn;
1486
1487 LOG_DBG("");
1488
1489 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1490 if (!conn) {
1491 LOG_ERR("Unknown connection");
1492 return BTP_STATUS_FAILED;
1493 }
1494
1495 u_conn = &connections[bt_conn_index(conn)];
1496 bt_conn_unref(conn);
1497
1498 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1499 if (stream == NULL) {
1500 return BTP_STATUS_FAILED;
1501 }
1502
1503 LOG_DBG("Stopping stream");
1504 err = bt_bap_stream_stop(stream_unicast_to_bap(stream));
1505 if (err != 0) {
1506 LOG_DBG("Could not stop stream: %d", err);
1507 return BTP_STATUS_FAILED;
1508 }
1509
1510 return BTP_STATUS_SUCCESS;
1511 }
1512
btp_ascs_release(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1513 uint8_t btp_ascs_release(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len)
1514 {
1515 int err;
1516 const struct btp_ascs_release_cmd *cp = cmd;
1517 struct btp_bap_unicast_connection *u_conn;
1518 struct btp_bap_unicast_stream *stream;
1519 struct bt_conn *conn;
1520
1521 LOG_DBG("");
1522
1523 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1524 if (!conn) {
1525 LOG_ERR("Unknown connection");
1526 return BTP_STATUS_FAILED;
1527 }
1528
1529 u_conn = &connections[bt_conn_index(conn)];
1530 bt_conn_unref(conn);
1531
1532 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1533 if (stream == NULL) {
1534 return BTP_STATUS_FAILED;
1535 }
1536
1537 LOG_DBG("Releasing stream");
1538 err = bt_bap_stream_release(stream_unicast_to_bap(stream));
1539 if (err != 0) {
1540 LOG_DBG("Unable to release stream, err %d", err);
1541 return BTP_STATUS_FAILED;
1542 }
1543
1544 return BTP_STATUS_SUCCESS;
1545 }
1546
btp_ascs_update_metadata(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1547 uint8_t btp_ascs_update_metadata(const void *cmd, uint16_t cmd_len,
1548 void *rsp, uint16_t *rsp_len)
1549 {
1550 const uint8_t meta[] = {
1551 BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT,
1552 BT_BYTES_LIST_LE16(BT_AUDIO_CONTEXT_TYPE_ANY)),
1553 };
1554 const struct btp_ascs_update_metadata_cmd *cp = cmd;
1555 struct btp_bap_unicast_connection *u_conn;
1556 struct btp_bap_unicast_stream *stream;
1557 struct bt_conn *conn;
1558 int err;
1559
1560 LOG_DBG("");
1561
1562 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1563 if (!conn) {
1564 LOG_ERR("Unknown connection");
1565 return BTP_STATUS_FAILED;
1566 }
1567
1568 u_conn = &connections[bt_conn_index(conn)];
1569 bt_conn_unref(conn);
1570
1571 stream = btp_bap_unicast_stream_find(u_conn, cp->ase_id);
1572 if (stream == NULL) {
1573 return BTP_STATUS_FAILED;
1574 }
1575
1576 LOG_DBG("Updating stream metadata");
1577 err = bt_bap_stream_metadata(stream_unicast_to_bap(stream), meta, ARRAY_SIZE(meta));
1578 if (err != 0) {
1579 LOG_DBG("Failed to update stream metadata, err %d", err);
1580 return BTP_STATUS_FAILED;
1581 }
1582
1583 return BTP_STATUS_SUCCESS;
1584 }
1585
btp_ascs_add_ase_to_cis(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)1586 uint8_t btp_ascs_add_ase_to_cis(const void *cmd, uint16_t cmd_len,
1587 void *rsp, uint16_t *rsp_len)
1588 {
1589 int err;
1590 struct bt_conn *conn;
1591 struct btp_bap_unicast_connection *u_conn;
1592 struct bt_conn_info conn_info;
1593 const struct btp_ascs_add_ase_to_cis *cp = cmd;
1594
1595 LOG_DBG("");
1596
1597 conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
1598 if (!conn) {
1599 LOG_ERR("Unknown connection");
1600
1601 return BTP_STATUS_FAILED;
1602 }
1603
1604 (void)bt_conn_get_info(conn, &conn_info);
1605 if (conn_info.role == BT_HCI_ROLE_PERIPHERAL) {
1606 bt_conn_unref(conn);
1607
1608 return BTP_STATUS_FAILED;
1609 }
1610
1611 u_conn = &connections[bt_conn_index(conn)];
1612 bt_conn_unref(conn);
1613
1614 err = client_add_ase_to_cis(u_conn, cp->ase_id, cp->cis_id, cp->cig_id);
1615
1616 return BTP_STATUS_VAL(err);
1617 }
1618
btp_bap_unicast_conn_get(size_t conn_index)1619 struct btp_bap_unicast_connection *btp_bap_unicast_conn_get(size_t conn_index)
1620 {
1621 return &connections[conn_index];
1622 }
1623
connected(struct bt_conn * conn,uint8_t err)1624 static void connected(struct bt_conn *conn, uint8_t err)
1625 {
1626 struct btp_bap_unicast_connection *u_conn;
1627 char addr[BT_ADDR_LE_STR_LEN];
1628
1629 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
1630
1631 if (err != 0) {
1632 LOG_DBG("Failed to connect to %s (%u)", addr, err);
1633 return;
1634 }
1635
1636 LOG_DBG("Connected: %s", addr);
1637
1638 u_conn = &connections[bt_conn_index(conn)];
1639 memset(u_conn, 0, sizeof(*u_conn));
1640 bt_addr_le_copy(&u_conn->address, bt_conn_get_dst(conn));
1641 }
1642
disconnected(struct bt_conn * conn,uint8_t reason)1643 static void disconnected(struct bt_conn *conn, uint8_t reason)
1644 {
1645 char addr[BT_ADDR_LE_STR_LEN];
1646
1647 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
1648
1649 LOG_DBG("Disconnected: %s (reason 0x%02x)", addr, reason);
1650 }
1651
1652 static struct bt_conn_cb conn_callbacks = {
1653 .connected = connected,
1654 .disconnected = disconnected,
1655 };
1656
1657 static bool unicast_inited;
1658
btp_bap_unicast_init(void)1659 int btp_bap_unicast_init(void)
1660 {
1661 int err;
1662
1663 if (unicast_inited) {
1664 return 0;
1665 }
1666
1667 (void)memset(connections, 0, sizeof(connections));
1668
1669 err = bt_bap_unicast_server_register(¶m);
1670 if (err != 0) {
1671 LOG_DBG("Failed to register unicast server (err %d)\n", err);
1672
1673 return err;
1674 }
1675
1676 err = bt_bap_unicast_server_register_cb(&unicast_server_cb);
1677 if (err != 0) {
1678 LOG_DBG("Failed to register client callbacks: %d", err);
1679 return err;
1680 }
1681
1682 err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
1683 if (err != 0) {
1684 LOG_DBG("Failed to register client callbacks: %d", err);
1685 return err;
1686 }
1687
1688 bt_conn_cb_register(&conn_callbacks);
1689
1690 unicast_inited = true;
1691
1692 return 0;
1693 }
1694