1 /* @file
2 * @brief Bluetooth PACS
3 */
4
5 /*
6 * Copyright (c) 2020 Intel Corporation
7 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <zephyr/sys/check.h>
15
16 #include <zephyr/device.h>
17 #include <zephyr/init.h>
18
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/conn.h>
21 #include <zephyr/bluetooth/gatt.h>
22 #include <zephyr/bluetooth/audio/audio.h>
23 #include <zephyr/bluetooth/audio/pacs.h>
24 #include <zephyr/sys/slist.h>
25 #include "../host/conn_internal.h"
26 #include "../host/hci_core.h"
27
28 #include <zephyr/logging/log.h>
29
30 LOG_MODULE_REGISTER(bt_pacs, CONFIG_BT_PACS_LOG_LEVEL);
31
32 #include "common/bt_str.h"
33
34 #include "audio_internal.h"
35 #include "pacs_internal.h"
36 #include "bap_unicast_server.h"
37
38 #define PAC_NOTIFY_TIMEOUT K_MSEC(10)
39 #define READ_BUF_SEM_TIMEOUT K_MSEC(50)
40
41 #if defined(CONFIG_BT_PAC_SRC)
42 static uint32_t pacs_src_location;
43 static sys_slist_t src_pacs_list = SYS_SLIST_STATIC_INIT(&src_pacs_list);
44 #endif /* CONFIG_BT_PAC_SRC */
45
46 #if defined(CONFIG_BT_PAC_SNK)
47 static uint32_t pacs_snk_location;
48 static sys_slist_t snk_pacs_list = SYS_SLIST_STATIC_INIT(&snk_pacs_list);
49 #endif /* CONFIG_BT_PAC_SNK */
50
51 #if defined(CONFIG_BT_PAC_SNK)
52 static uint16_t snk_available_contexts;
53 static uint16_t snk_supported_contexts = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
54 #else
55 static uint16_t snk_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
56 static uint16_t snk_supported_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
57 #endif /* CONFIG_BT_PAC_SNK */
58
59 #if defined(CONFIG_BT_PAC_SRC)
60 static uint16_t src_available_contexts;
61 static uint16_t src_supported_contexts = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
62 #else
63 static uint16_t src_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
64 static uint16_t src_supported_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
65 #endif /* CONFIG_BT_PAC_SRC */
66
67 enum {
68 FLAG_ACTIVE,
69 FLAG_SINK_PAC_CHANGED,
70 FLAG_SINK_AUDIO_LOCATIONS_CHANGED,
71 FLAG_SOURCE_PAC_CHANGED,
72 FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED,
73 FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED,
74 FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED,
75 FLAG_NUM,
76 };
77
78 static struct pacs_client {
79 bt_addr_le_t addr;
80
81 /* Pending notification flags */
82 ATOMIC_DEFINE(flags, FLAG_NUM);
83 } clients[CONFIG_BT_MAX_PAIRED];
84
85 static atomic_t notify_rdy;
86
87 static K_SEM_DEFINE(read_buf_sem, 1, 1);
88 NET_BUF_SIMPLE_DEFINE_STATIC(read_buf, BT_ATT_MAX_ATTRIBUTE_LEN);
89
90 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
91 static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir);
92 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE || CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE*/
93 static int pacs_gatt_notify(struct bt_conn *conn,
94 const struct bt_uuid *uuid,
95 const struct bt_gatt_attr *attr,
96 const void *data,
97 uint16_t len);
98 static void deferred_nfy_work_handler(struct k_work *work);
99
100 static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
101
102 struct pac_records_build_data {
103 struct bt_pacs_read_rsp *rsp;
104 struct net_buf_simple *buf;
105 };
106
pacs_set_notify_bit(int bit)107 static void pacs_set_notify_bit(int bit)
108 {
109 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
110 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
111 atomic_set_bit(clients[i].flags, bit);
112 }
113 }
114 }
115
build_pac_records(const struct bt_pacs_cap * cap,void * user_data)116 static bool build_pac_records(const struct bt_pacs_cap *cap, void *user_data)
117 {
118 struct pac_records_build_data *data = user_data;
119 const struct bt_audio_codec_cap *codec_cap = cap->codec_cap;
120 struct net_buf_simple *buf = data->buf;
121 struct net_buf_simple_state state;
122 struct bt_pac_codec *pac_codec;
123
124 net_buf_simple_save(buf, &state);
125
126 if (net_buf_simple_tailroom(buf) < sizeof(*pac_codec)) {
127 goto fail;
128 }
129
130 pac_codec = net_buf_simple_add(buf, sizeof(*pac_codec));
131 pac_codec->id = codec_cap->id;
132 pac_codec->cid = sys_cpu_to_le16(codec_cap->cid);
133 pac_codec->vid = sys_cpu_to_le16(codec_cap->vid);
134
135 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->data_len)) {
136 goto fail;
137 }
138
139 net_buf_simple_add_u8(buf, codec_cap->data_len);
140 net_buf_simple_add_mem(buf, codec_cap->data, codec_cap->data_len);
141
142 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->meta_len)) {
143 goto fail;
144 }
145
146 net_buf_simple_add_u8(buf, codec_cap->meta_len);
147 net_buf_simple_add_mem(buf, codec_cap->meta, codec_cap->meta_len);
148
149 data->rsp->num_pac++;
150
151 return true;
152
153 fail:
154 __ASSERT(false, "No space for %p", cap);
155
156 net_buf_simple_restore(buf, &state);
157
158 return false;
159 }
160
foreach_cap(sys_slist_t * list,bt_pacs_cap_foreach_func_t func,void * user_data)161 static void foreach_cap(sys_slist_t *list, bt_pacs_cap_foreach_func_t func,
162 void *user_data)
163 {
164 struct bt_pacs_cap *cap;
165
166 SYS_SLIST_FOR_EACH_CONTAINER(list, cap, _node) {
167 if (!func(cap, user_data)) {
168 break;
169 }
170 }
171 }
172
get_pac_records(sys_slist_t * list,struct net_buf_simple * buf)173 static void get_pac_records(sys_slist_t *list, struct net_buf_simple *buf)
174 {
175 struct pac_records_build_data data;
176
177 /* Reset if buffer before using */
178 net_buf_simple_reset(buf);
179
180 data.rsp = net_buf_simple_add(buf, sizeof(*data.rsp));
181 data.rsp->num_pac = 0;
182 data.buf = buf;
183
184 foreach_cap(list, build_pac_records, &data);
185 }
186
available_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)187 static void available_context_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
188 {
189 LOG_DBG("attr %p value 0x%04x", attr, value);
190 }
191
available_contexts_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)192 static ssize_t available_contexts_read(struct bt_conn *conn,
193 const struct bt_gatt_attr *attr, void *buf,
194 uint16_t len, uint16_t offset)
195 {
196 struct bt_pacs_context context = {
197 .snk = sys_cpu_to_le16(snk_available_contexts),
198 .src = sys_cpu_to_le16(src_available_contexts),
199 };
200
201 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
202
203 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
204 sizeof(context));
205 }
206
207 #if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
supported_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)208 static void supported_context_cfg_changed(const struct bt_gatt_attr *attr,
209 uint16_t value)
210 {
211 LOG_DBG("attr %p value 0x%04x", attr, value);
212 }
213 #endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
214
supported_context_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)215 static ssize_t supported_context_read(struct bt_conn *conn,
216 const struct bt_gatt_attr *attr,
217 void *buf, uint16_t len, uint16_t offset)
218 {
219 struct bt_pacs_context context = {
220 .snk = sys_cpu_to_le16(snk_supported_contexts),
221 .src = sys_cpu_to_le16(src_supported_contexts),
222 };
223
224 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
225
226 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
227 sizeof(context));
228 }
229
set_available_contexts(uint16_t contexts,uint16_t * available,uint16_t supported)230 static int set_available_contexts(uint16_t contexts, uint16_t *available,
231 uint16_t supported)
232 {
233 if (contexts & ~supported) {
234 return -ENOTSUP;
235 }
236
237 if (contexts == *available) {
238 return 0;
239 }
240
241 *available = contexts;
242
243 pacs_set_notify_bit(FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
244 k_work_submit(&deferred_nfy_work);
245
246 return 0;
247 }
248
set_supported_contexts(uint16_t contexts,uint16_t * supported,uint16_t * available)249 static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
250 uint16_t *available)
251 {
252 int err;
253 uint16_t tmp_supported = *supported;
254 uint16_t tmp_available = *available;
255
256 /* Ensure unspecified is always supported */
257 contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
258
259 if (*supported == contexts) {
260 return 0;
261 }
262
263 *supported = contexts;
264
265 /* Update available contexts if needed*/
266 if ((contexts & *available) != *available) {
267 err = set_available_contexts(contexts & *available, available, contexts);
268 if (err) {
269 *available = tmp_available;
270 *supported = tmp_supported;
271
272 return err;
273 }
274 }
275
276 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)) {
277 pacs_set_notify_bit(FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
278 k_work_submit(&deferred_nfy_work);
279 }
280
281 return 0;
282 }
283
284 #if defined(CONFIG_BT_PAC_SNK)
snk_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)285 static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
286 void *buf, uint16_t len, uint16_t offset)
287 {
288 ssize_t ret_val;
289 int err;
290
291 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
292
293 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
294 if (err != 0) {
295 LOG_DBG("Failed to take read_buf_sem: %d", err);
296
297 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
298 }
299
300 get_pac_records(&snk_pacs_list, &read_buf);
301
302 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
303 read_buf.len);
304
305 k_sem_give(&read_buf_sem);
306
307 return ret_val;
308 }
309
310 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
311 static const struct bt_uuid *pacs_snk_uuid = BT_UUID_PACS_SNK;
312
snk_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)313 static void snk_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
314 {
315 LOG_DBG("attr %p value 0x%04x", attr, value);
316 }
317 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
318
set_snk_available_contexts(uint16_t contexts)319 static inline int set_snk_available_contexts(uint16_t contexts)
320 {
321 return set_available_contexts(contexts, &snk_available_contexts,
322 snk_supported_contexts);
323 }
324
set_snk_supported_contexts(uint16_t contexts)325 static inline int set_snk_supported_contexts(uint16_t contexts)
326 {
327 return set_supported_contexts(contexts, &snk_supported_contexts,
328 &snk_available_contexts);
329 }
330 #else
set_snk_available_contexts(uint16_t contexts)331 static inline int set_snk_available_contexts(uint16_t contexts)
332 {
333 return -ENOTSUP;
334 }
335
set_snk_supported_contexts(uint16_t contexts)336 static inline int set_snk_supported_contexts(uint16_t contexts)
337 {
338 return -ENOTSUP;
339 }
340 #endif /* CONFIG_BT_PAC_SNK */
341
342 #if defined(CONFIG_BT_PAC_SNK_LOC)
snk_loc_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)343 static ssize_t snk_loc_read(struct bt_conn *conn,
344 const struct bt_gatt_attr *attr, void *buf,
345 uint16_t len, uint16_t offset)
346 {
347 uint32_t location = sys_cpu_to_le32(pacs_snk_location);
348
349 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
350
351 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
352 sizeof(location));
353 }
354
355 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
356 static const struct bt_uuid *pacs_snk_loc_uuid = BT_UUID_PACS_SNK_LOC;
357
snk_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)358 static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
359 {
360 LOG_DBG("attr %p value 0x%04x", attr, value);
361 }
362 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
363
set_snk_location(enum bt_audio_location audio_location)364 static void set_snk_location(enum bt_audio_location audio_location)
365 {
366 if (audio_location == pacs_snk_location) {
367 return;
368 }
369
370 pacs_snk_location = audio_location;
371
372 if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)) {
373 pacs_set_notify_bit(FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
374 k_work_submit(&deferred_nfy_work);
375 }
376 }
377 #else
set_snk_location(enum bt_audio_location location)378 static void set_snk_location(enum bt_audio_location location)
379 {
380 return;
381 }
382 #endif /* CONFIG_BT_PAC_SNK_LOC */
383
384 #if defined(CONFIG_BT_PAC_SNK_LOC_WRITEABLE)
snk_loc_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * data,uint16_t len,uint16_t offset,uint8_t flags)385 static ssize_t snk_loc_write(struct bt_conn *conn,
386 const struct bt_gatt_attr *attr, const void *data,
387 uint16_t len, uint16_t offset, uint8_t flags)
388 {
389 enum bt_audio_location location;
390
391 if (offset) {
392 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
393 }
394
395 if (len != sizeof(location)) {
396 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
397 }
398
399 location = (enum bt_audio_location)sys_get_le32(data);
400 if (location > BT_AUDIO_LOCATION_MASK || location == 0) {
401 LOG_DBG("Invalid location value: 0x%08X", location);
402 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
403 }
404
405 set_snk_location(location);
406
407 return len;
408 }
409 #endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
410
411 #if defined(CONFIG_BT_PAC_SRC)
src_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)412 static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
413 void *buf, uint16_t len, uint16_t offset)
414 {
415 ssize_t ret_val;
416 int err;
417
418 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
419
420 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
421 if (err != 0) {
422 LOG_DBG("Failed to take read_buf_sem: %d", err);
423
424 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
425 }
426
427 get_pac_records(&src_pacs_list, &read_buf);
428
429 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
430 read_buf.len);
431
432 k_sem_give(&read_buf_sem);
433
434 return ret_val;
435 }
436
437 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
438 static const struct bt_uuid *pacs_src_uuid = BT_UUID_PACS_SRC;
439
src_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)440 static void src_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
441 {
442 LOG_DBG("attr %p value 0x%04x", attr, value);
443 }
444 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
445
set_src_available_contexts(uint16_t contexts)446 static inline int set_src_available_contexts(uint16_t contexts)
447 {
448 return set_available_contexts(contexts, &src_available_contexts,
449 src_supported_contexts);
450 }
451
set_src_supported_contexts(uint16_t contexts)452 static inline int set_src_supported_contexts(uint16_t contexts)
453 {
454 return set_supported_contexts(contexts, &src_supported_contexts,
455 &src_available_contexts);
456 }
457 #else
set_src_available_contexts(uint16_t contexts)458 static inline int set_src_available_contexts(uint16_t contexts)
459 {
460 return -ENOTSUP;
461 }
462
set_src_supported_contexts(uint16_t contexts)463 static inline int set_src_supported_contexts(uint16_t contexts)
464 {
465 return -ENOTSUP;
466 }
467 #endif /* CONFIG_BT_PAC_SRC */
468
469 #if defined(CONFIG_BT_PAC_SRC_LOC)
src_loc_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)470 static ssize_t src_loc_read(struct bt_conn *conn,
471 const struct bt_gatt_attr *attr, void *buf,
472 uint16_t len, uint16_t offset)
473 {
474 uint32_t location = sys_cpu_to_le32(pacs_src_location);
475
476 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
477
478 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
479 sizeof(location));
480 }
481
482 #if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
483 static const struct bt_uuid *pacs_src_loc_uuid = BT_UUID_PACS_SRC_LOC;
484
src_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)485 static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
486 {
487 LOG_DBG("attr %p value 0x%04x", attr, value);
488 }
489 #endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
490
set_src_location(enum bt_audio_location audio_location)491 static void set_src_location(enum bt_audio_location audio_location)
492 {
493 if (audio_location == pacs_src_location) {
494 return;
495 }
496
497 pacs_src_location = audio_location;
498
499 if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)) {
500 pacs_set_notify_bit(FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
501 k_work_submit(&deferred_nfy_work);
502 }
503 }
504 #else
set_src_location(enum bt_audio_location location)505 static void set_src_location(enum bt_audio_location location)
506 {
507 return;
508 }
509 #endif /* CONFIG_BT_PAC_SRC_LOC */
510
511 #if defined(CONFIG_BT_PAC_SRC_LOC_WRITEABLE)
src_loc_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * data,uint16_t len,uint16_t offset,uint8_t flags)512 static ssize_t src_loc_write(struct bt_conn *conn,
513 const struct bt_gatt_attr *attr, const void *data,
514 uint16_t len, uint16_t offset, uint8_t flags)
515 {
516 uint32_t location;
517
518 if (offset) {
519 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
520 }
521
522 if (len != sizeof(location)) {
523 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
524 }
525
526 location = (enum bt_audio_location)sys_get_le32(data);
527 if (location > BT_AUDIO_LOCATION_MASK || location == 0) {
528 LOG_DBG("Invalid location value: 0x%08X", location);
529 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
530 }
531
532 set_src_location(location);
533
534 return len;
535 }
536 #endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
537
538
pacs_get(enum bt_audio_dir dir)539 static sys_slist_t *pacs_get(enum bt_audio_dir dir)
540 {
541 switch (dir) {
542 #if defined(CONFIG_BT_PAC_SNK)
543 case BT_AUDIO_DIR_SINK:
544 return &snk_pacs_list;
545 #endif /* CONFIG_BT_PAC_SNK */
546 #if defined(CONFIG_BT_PAC_SRC)
547 case BT_AUDIO_DIR_SOURCE:
548 return &src_pacs_list;
549 #endif /* CONFIG_BT_PAC_SRC */
550 default:
551 return NULL;
552 }
553 }
554
555 #define BT_PACS_SNK_PROP \
556 BT_GATT_CHRC_READ \
557 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
558 #define BT_PAC_SNK(_read) \
559 BT_AUDIO_CHRC(BT_UUID_PACS_SNK, \
560 BT_PACS_SNK_PROP, \
561 BT_GATT_PERM_READ_ENCRYPT, \
562 _read, NULL, NULL), \
563 IF_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE, (BT_AUDIO_CCC(snk_cfg_changed),))
564
565 #define BT_PACS_SNK_LOC_PROP \
566 BT_GATT_CHRC_READ \
567 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
568 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
569
570 #define BT_PACS_SNK_LOC_PERM \
571 BT_GATT_PERM_READ_ENCRYPT \
572 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
573
574 #define BT_PACS_SNK_LOC(_read) \
575 BT_AUDIO_CHRC(BT_UUID_PACS_SNK_LOC, \
576 BT_PACS_SNK_LOC_PROP, \
577 BT_PACS_SNK_LOC_PERM, \
578 _read, \
579 COND_CODE_1(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (snk_loc_write), (NULL)), \
580 NULL), \
581 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (BT_AUDIO_CCC(snk_loc_cfg_changed),))
582
583 #define BT_PACS_SRC_PROP \
584 BT_GATT_CHRC_READ \
585 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
586 #define BT_PAC_SRC(_read) \
587 BT_AUDIO_CHRC(BT_UUID_PACS_SRC, \
588 BT_PACS_SRC_PROP, \
589 BT_GATT_PERM_READ_ENCRYPT, \
590 _read, NULL, NULL), \
591 IF_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE, (BT_AUDIO_CCC(src_cfg_changed),))
592
593 #define BT_PACS_SRC_LOC_PROP \
594 BT_GATT_CHRC_READ \
595 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
596 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
597
598 #define BT_PACS_SRC_LOC_PERM \
599 BT_GATT_PERM_READ_ENCRYPT \
600 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
601
602 #define BT_PACS_SRC_LOC(_read) \
603 BT_AUDIO_CHRC(BT_UUID_PACS_SRC_LOC, \
604 BT_PACS_SRC_LOC_PROP, \
605 BT_PACS_SRC_LOC_PERM, \
606 _read, \
607 COND_CODE_1(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (src_loc_write), (NULL)), \
608 NULL), \
609 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (BT_AUDIO_CCC(src_loc_cfg_changed),))
610
611 #define BT_PAC_AVAILABLE_CONTEXT(_read) \
612 BT_AUDIO_CHRC(BT_UUID_PACS_AVAILABLE_CONTEXT, \
613 BT_GATT_CHRC_READ|BT_GATT_CHRC_NOTIFY, \
614 BT_GATT_PERM_READ_ENCRYPT, \
615 _read, NULL, NULL), \
616 BT_AUDIO_CCC(available_context_cfg_changed),
617
618 #define BT_PACS_SUPPORTED_CONTEXT_PROP \
619 BT_GATT_CHRC_READ \
620 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
621
622 #define BT_PAC_SUPPORTED_CONTEXT(_read) \
623 BT_AUDIO_CHRC(BT_UUID_PACS_SUPPORTED_CONTEXT, \
624 BT_PACS_SUPPORTED_CONTEXT_PROP, \
625 BT_GATT_PERM_READ_ENCRYPT, \
626 _read, NULL, NULL), \
627 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, \
628 (BT_AUDIO_CCC(supported_context_cfg_changed),))
629
630 BT_GATT_SERVICE_DEFINE(pacs_svc,
631 BT_GATT_PRIMARY_SERVICE(BT_UUID_PACS),
632 #if defined(CONFIG_BT_PAC_SNK)
633 BT_PAC_SNK(snk_read)
634 #if defined(CONFIG_BT_PAC_SNK_LOC)
635 BT_PACS_SNK_LOC(snk_loc_read)
636 #endif /* CONFIG_BT_PAC_SNK_LOC */
637 #endif /* CONFIG_BT_PAC_SNK */
638 #if defined(CONFIG_BT_PAC_SRC)
639 BT_PAC_SRC(src_read)
640 #if defined(CONFIG_BT_PAC_SRC_LOC)
641 BT_PACS_SRC_LOC(src_loc_read)
642 #endif /* CONFIG_BT_PAC_SRC_LOC */
643 #endif /* CONFIG_BT_PAC_SRC */
644 BT_PAC_AVAILABLE_CONTEXT(available_contexts_read)
645 BT_PAC_SUPPORTED_CONTEXT(supported_context_read)
646 );
647
pac_notify_loc(struct bt_conn * conn,enum bt_audio_dir dir)648 static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir)
649 {
650 uint32_t location_le;
651 int err;
652 const struct bt_uuid *uuid;
653
654 switch (dir) {
655 case BT_AUDIO_DIR_SINK:
656 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
657 location_le = sys_cpu_to_le32(pacs_snk_location);
658 uuid = pacs_snk_loc_uuid;
659 break;
660 #else
661 return -ENOTSUP;
662 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
663 case BT_AUDIO_DIR_SOURCE:
664 #if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
665 location_le = sys_cpu_to_le32(pacs_src_location);
666 uuid = pacs_src_loc_uuid;
667 break;
668 #else
669 return -ENOTSUP;
670 #endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
671 default:
672 return -EINVAL;
673 }
674
675 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs, &location_le, sizeof(location_le));
676 if (err != 0 && err != -ENOTCONN) {
677 LOG_WRN("PACS notify_loc failed: %d", err);
678 return err;
679 }
680
681 return 0;
682 }
683
pac_notify(struct bt_conn * conn,enum bt_audio_dir dir)684 static int pac_notify(struct bt_conn *conn, enum bt_audio_dir dir)
685 {
686 int err = 0;
687 sys_slist_t *pacs;
688 const struct bt_uuid *uuid;
689
690 switch (dir) {
691 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
692 case BT_AUDIO_DIR_SINK:
693 uuid = pacs_snk_uuid;
694 break;
695 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
696 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
697 case BT_AUDIO_DIR_SOURCE:
698 uuid = pacs_src_uuid;
699 break;
700 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
701 default:
702 return -EINVAL;
703 }
704
705 err = k_sem_take(&read_buf_sem, K_NO_WAIT);
706 if (err != 0) {
707 LOG_DBG("Failed to take read_buf_sem: %d", err);
708
709 return err;
710 }
711
712 pacs = pacs_get(dir);
713 __ASSERT(pacs, "Failed to get pacs.\n");
714 get_pac_records(pacs, &read_buf);
715
716 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs,
717 read_buf.data, read_buf.len);
718 if (err != 0 && err != -ENOTCONN) {
719 LOG_WRN("PACS notify failed: %d", err);
720 }
721
722 k_sem_give(&read_buf_sem);
723
724 if (err == -ENOTCONN) {
725 return 0;
726 } else {
727 return 0;
728 }
729 }
730
available_contexts_notify(struct bt_conn * conn)731 static int available_contexts_notify(struct bt_conn *conn)
732 {
733 struct bt_pacs_context context = {
734 .snk = sys_cpu_to_le16(snk_available_contexts),
735 .src = sys_cpu_to_le16(src_available_contexts),
736 };
737 int err;
738
739 err = pacs_gatt_notify(conn, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
740 &context, sizeof(context));
741 if (err != 0 && err != -ENOTCONN) {
742 LOG_WRN("Available Audio Contexts notify failed: %d", err);
743 return err;
744 }
745
746 return 0;
747 }
748
supported_contexts_notify(struct bt_conn * conn)749 static int supported_contexts_notify(struct bt_conn *conn)
750 {
751 struct bt_pacs_context context = {
752 .snk = sys_cpu_to_le16(snk_supported_contexts),
753 .src = sys_cpu_to_le16(src_supported_contexts),
754 };
755 int err;
756
757 err = pacs_gatt_notify(conn, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
758 &context, sizeof(context));
759 if (err != 0 && err != -ENOTCONN) {
760 LOG_WRN("Supported Audio Contexts notify failed: %d", err);
761
762 return err;
763 }
764 return 0;
765 }
766
pacs_gatt_notify_complete_cb(struct bt_conn * conn,void * user_data)767 void pacs_gatt_notify_complete_cb(struct bt_conn *conn, void *user_data)
768 {
769 /* Notification done, clear bit and reschedule work */
770 atomic_clear(¬ify_rdy);
771 k_work_submit(&deferred_nfy_work);
772 }
773
pacs_gatt_notify(struct bt_conn * conn,const struct bt_uuid * uuid,const struct bt_gatt_attr * attr,const void * data,uint16_t len)774 static int pacs_gatt_notify(struct bt_conn *conn,
775 const struct bt_uuid *uuid,
776 const struct bt_gatt_attr *attr,
777 const void *data,
778 uint16_t len)
779 {
780 int err;
781 struct bt_gatt_notify_params params;
782
783 memset(¶ms, 0, sizeof(params));
784 params.uuid = uuid;
785 params.attr = attr;
786 params.data = data;
787 params.len = len;
788 params.func = pacs_gatt_notify_complete_cb;
789
790 /* Mark notification in progress */
791 atomic_set(¬ify_rdy, 1);
792
793 err = bt_gatt_notify_cb(conn, ¶ms);
794 if (err != 0) {
795 atomic_clear(¬ify_rdy);
796 }
797
798 if (err && err != -ENOTCONN) {
799 return err;
800 }
801
802 return 0;
803 }
804
notify_cb(struct bt_conn * conn,void * data)805 static void notify_cb(struct bt_conn *conn, void *data)
806 {
807 struct pacs_client *client = &clients[bt_conn_index(conn)];
808 struct bt_conn_info info;
809 int err = 0;
810
811 LOG_DBG("");
812
813 err = bt_conn_get_info(conn, &info);
814 if (err != 0) {
815 LOG_ERR("Failed to get conn info: %d", err);
816 return;
817 }
818
819 if (info.state != BT_CONN_STATE_CONNECTED) {
820 /* Not connected */
821 return;
822 }
823
824 /* Check if we have unverified notifications in progress */
825 if (atomic_get(¬ify_rdy)) {
826 return;
827 }
828
829 if (IS_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE) &&
830 atomic_test_bit(client->flags, FLAG_SINK_PAC_CHANGED)) {
831 LOG_DBG("Notifying Sink PAC");
832 err = pac_notify(conn, BT_AUDIO_DIR_SINK);
833 if (!err) {
834 atomic_clear_bit(client->flags, FLAG_SINK_PAC_CHANGED);
835 }
836 }
837
838 if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) &&
839 atomic_test_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED)) {
840 LOG_DBG("Notifying Sink Audio Location");
841 err = pac_notify_loc(conn, BT_AUDIO_DIR_SINK);
842 if (!err) {
843 atomic_clear_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
844 }
845 }
846 if (IS_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE) &&
847 atomic_test_bit(client->flags, FLAG_SOURCE_PAC_CHANGED)) {
848 LOG_DBG("Notifying Source PAC");
849 err = pac_notify(conn, BT_AUDIO_DIR_SOURCE);
850 if (!err) {
851 atomic_clear_bit(client->flags, FLAG_SOURCE_PAC_CHANGED);
852 }
853 }
854 if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE) &&
855 atomic_test_and_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED)) {
856 LOG_DBG("Notifying Source Audio Location");
857 err = pac_notify_loc(conn, BT_AUDIO_DIR_SOURCE);
858 if (!err) {
859 atomic_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
860 }
861 }
862
863 if (atomic_test_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED)) {
864 LOG_DBG("Notifying Available Contexts");
865 err = available_contexts_notify(conn);
866 if (!err) {
867 atomic_clear_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
868 }
869 }
870
871 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE) &&
872 atomic_test_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED)) {
873 LOG_DBG("Notifying Supported Contexts");
874 err = supported_contexts_notify(conn);
875 if (!err) {
876 atomic_clear_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
877 }
878 }
879 }
880
deferred_nfy_work_handler(struct k_work * work)881 static void deferred_nfy_work_handler(struct k_work *work)
882 {
883 bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
884 }
885
pacs_auth_pairing_complete(struct bt_conn * conn,bool bonded)886 static void pacs_auth_pairing_complete(struct bt_conn *conn, bool bonded)
887 {
888 LOG_DBG("%s paired (%sbonded)", bt_addr_le_str(bt_conn_get_dst(conn)),
889 bonded ? "" : "not ");
890
891 if (!bonded) {
892 return;
893 }
894
895 /* Check if already in list, and do nothing if it is */
896 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
897 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
898 bt_addr_le_eq(bt_conn_get_dst(conn), &clients[i].addr)) {
899 return;
900 }
901 }
902
903 /* Else add the device */
904 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
905 if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
906 atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
907 memcpy(&clients[i].addr, bt_conn_get_dst(conn), sizeof(bt_addr_le_t));
908
909 /* Send out all pending notifications */
910 k_work_submit(&deferred_nfy_work);
911 return;
912 }
913 }
914 }
915
pacs_bond_deleted(uint8_t id,const bt_addr_le_t * peer)916 static void pacs_bond_deleted(uint8_t id, const bt_addr_le_t *peer)
917 {
918 /* Find the device entry to delete */
919 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
920 /* Check if match, and if active, if so, reset */
921 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
922 bt_addr_le_eq(peer, &clients[i].addr)) {
923 for (size_t j = 0U; j < FLAG_NUM; j++) {
924 atomic_clear_bit(clients[i].flags, j);
925 }
926 (void)memset(&clients[i].addr, 0, sizeof(bt_addr_le_t));
927 return;
928 }
929 }
930 }
931
pacs_security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)932 static void pacs_security_changed(struct bt_conn *conn, bt_security_t level,
933 enum bt_security_err err)
934 {
935 LOG_DBG("%s changed security level to %d", bt_addr_le_str(bt_conn_get_dst(conn)), level);
936
937 if (err != 0 || conn->encrypt == 0) {
938 return;
939 }
940
941 if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
942 return;
943 }
944
945 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
946 for (size_t j = 0U; j < FLAG_NUM; j++) {
947 if (atomic_test_bit(clients[i].flags, j)) {
948
949 /**
950 * It's enough that one flag is set, as the defer work will go
951 * through all notifiable characteristics
952 */
953 k_work_submit(&deferred_nfy_work);
954 return;
955 }
956 }
957 }
958 }
959
960 static struct bt_conn_cb conn_callbacks = {
961 .security_changed = pacs_security_changed,
962 };
963
964 static struct bt_conn_auth_info_cb auth_callbacks = {
965 .pairing_complete = pacs_auth_pairing_complete,
966 .bond_deleted = pacs_bond_deleted
967 };
968
bt_pacs_context_available(enum bt_audio_dir dir,uint16_t context)969 bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
970 {
971 if (dir == BT_AUDIO_DIR_SOURCE) {
972 return (context & src_available_contexts) == context;
973 }
974
975 if (dir == BT_AUDIO_DIR_SINK) {
976 return (context & snk_available_contexts) == context;
977 }
978
979 return false;
980 }
981
bt_pacs_cap_foreach(enum bt_audio_dir dir,bt_pacs_cap_foreach_func_t func,void * user_data)982 void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
983 {
984 sys_slist_t *pac;
985
986 CHECKIF(func == NULL) {
987 LOG_ERR("func is NULL");
988 return;
989 }
990
991 pac = pacs_get(dir);
992 if (!pac) {
993 return;
994 }
995
996 foreach_cap(pac, func, user_data);
997 }
998
add_bonded_addr_to_client_list(const struct bt_bond_info * info,void * data)999 static void add_bonded_addr_to_client_list(const struct bt_bond_info *info, void *data)
1000 {
1001 for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
1002 /* Check if device is registered, it not, add it */
1003 if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
1004 char addr_str[BT_ADDR_LE_STR_LEN];
1005
1006 atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
1007 memcpy(&clients[i].addr, &info->addr, sizeof(bt_addr_le_t));
1008 bt_addr_le_to_str(&clients[i].addr, addr_str, sizeof(addr_str));
1009 LOG_DBG("Added %s to bonded list\n", addr_str);
1010 return;
1011 }
1012 }
1013 }
1014
1015 /* Register Audio Capability */
bt_pacs_cap_register(enum bt_audio_dir dir,struct bt_pacs_cap * cap)1016 int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
1017 {
1018 const struct bt_audio_codec_cap *codec_cap;
1019 static bool callbacks_registered;
1020 sys_slist_t *pac;
1021
1022 if (!cap || !cap->codec_cap) {
1023 return -EINVAL;
1024 }
1025
1026 codec_cap = cap->codec_cap;
1027
1028 pac = pacs_get(dir);
1029 if (!pac) {
1030 return -EINVAL;
1031 }
1032
1033 /* Restore bonding list */
1034 bt_foreach_bond(BT_ID_DEFAULT, add_bonded_addr_to_client_list, NULL);
1035
1036 LOG_DBG("cap %p dir %s codec_cap id 0x%02x codec_cap cid 0x%04x codec_cap vid 0x%04x", cap,
1037 bt_audio_dir_str(dir), codec_cap->id, codec_cap->cid, codec_cap->vid);
1038
1039 sys_slist_append(pac, &cap->_node);
1040
1041 if (!callbacks_registered) {
1042 bt_conn_cb_register(&conn_callbacks);
1043 bt_conn_auth_info_cb_register(&auth_callbacks);
1044
1045 callbacks_registered = true;
1046 }
1047
1048 if (IS_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE) && dir == BT_AUDIO_DIR_SINK) {
1049 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1050 k_work_submit(&deferred_nfy_work);
1051 }
1052
1053 if (IS_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE) && dir == BT_AUDIO_DIR_SOURCE) {
1054 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1055 k_work_submit(&deferred_nfy_work);
1056 }
1057
1058 return 0;
1059 }
1060
1061 /* Unregister Audio Capability */
bt_pacs_cap_unregister(enum bt_audio_dir dir,struct bt_pacs_cap * cap)1062 int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
1063 {
1064 sys_slist_t *pac;
1065
1066 if (!cap) {
1067 return -EINVAL;
1068 }
1069
1070 pac = pacs_get(dir);
1071 if (!pac) {
1072 return -EINVAL;
1073 }
1074
1075 LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir));
1076
1077 if (!sys_slist_find_and_remove(pac, &cap->_node)) {
1078 return -ENOENT;
1079 }
1080
1081 switch (dir) {
1082 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
1083 case BT_AUDIO_DIR_SINK:
1084 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1085 k_work_submit(&deferred_nfy_work);
1086 break;
1087 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
1088 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
1089 case BT_AUDIO_DIR_SOURCE:
1090 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1091 k_work_submit(&deferred_nfy_work);
1092 break;
1093 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
1094 default:
1095 return -EINVAL;
1096 }
1097
1098 return 0;
1099 }
1100
bt_pacs_set_location(enum bt_audio_dir dir,enum bt_audio_location location)1101 int bt_pacs_set_location(enum bt_audio_dir dir, enum bt_audio_location location)
1102 {
1103 switch (dir) {
1104 case BT_AUDIO_DIR_SINK:
1105 set_snk_location(location);
1106 break;
1107 case BT_AUDIO_DIR_SOURCE:
1108 set_src_location(location);
1109 break;
1110 default:
1111 return -EINVAL;
1112 }
1113
1114 return 0;
1115 }
1116
bt_pacs_set_available_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)1117 int bt_pacs_set_available_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
1118 {
1119 switch (dir) {
1120 case BT_AUDIO_DIR_SINK:
1121 return set_snk_available_contexts(contexts);
1122 case BT_AUDIO_DIR_SOURCE:
1123 return set_src_available_contexts(contexts);
1124 }
1125
1126 return -EINVAL;
1127 }
1128
bt_pacs_set_supported_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)1129 int bt_pacs_set_supported_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
1130 {
1131 switch (dir) {
1132 case BT_AUDIO_DIR_SINK:
1133 return set_snk_supported_contexts(contexts);
1134 case BT_AUDIO_DIR_SOURCE:
1135 return set_src_supported_contexts(contexts);
1136 }
1137
1138 return -EINVAL;
1139 }
1140
bt_pacs_get_available_contexts(enum bt_audio_dir dir)1141 enum bt_audio_context bt_pacs_get_available_contexts(enum bt_audio_dir dir)
1142 {
1143 switch (dir) {
1144 case BT_AUDIO_DIR_SINK:
1145 return snk_available_contexts;
1146 case BT_AUDIO_DIR_SOURCE:
1147 return src_available_contexts;
1148 }
1149
1150 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
1151 }
1152