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 <errno.h>
13 #include <stdbool.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <string.h>
17 #include <sys/types.h>
18
19 #include <zephyr/autoconf.h>
20 #include <zephyr/bluetooth/addr.h>
21 #include <zephyr/bluetooth/att.h>
22 #include <zephyr/bluetooth/audio/audio.h>
23 #include <zephyr/bluetooth/audio/pacs.h>
24 #include <zephyr/bluetooth/bluetooth.h>
25 #include <zephyr/bluetooth/conn.h>
26 #include <zephyr/bluetooth/gatt.h>
27 #include <zephyr/bluetooth/uuid.h>
28 #include <zephyr/init.h>
29 #include <zephyr/kernel.h>
30 #include <zephyr/logging/log.h>
31 #include <zephyr/net_buf.h>
32 #include <zephyr/sys/__assert.h>
33 #include <zephyr/sys/atomic.h>
34 #include <zephyr/sys/byteorder.h>
35 #include <zephyr/sys/check.h>
36 #include <zephyr/sys/slist.h>
37 #include <zephyr/sys/util.h>
38 #include <zephyr/sys/util_macro.h>
39
40 #include "common/bt_str.h"
41
42 #include "audio_internal.h"
43 #include "bap_unicast_server.h"
44 #include "pacs_internal.h"
45
46 LOG_MODULE_REGISTER(bt_pacs, CONFIG_BT_PACS_LOG_LEVEL);
47
48 #define PAC_NOTIFY_TIMEOUT K_MSEC(10)
49 #define READ_BUF_SEM_TIMEOUT K_MSEC(50)
50
51 #if defined(CONFIG_BT_PAC_SRC)
52 static sys_slist_t src_pac_list = SYS_SLIST_STATIC_INIT(&src_pac_list);
53 static uint16_t src_supported_contexts;
54 #if defined(CONFIG_BT_PAC_SRC_LOC)
55 static uint32_t pacs_src_location;
56 #endif /* CONFIG_BT_PAC_SRC_LOC */
57 #endif /* CONFIG_BT_PAC_SRC */
58
59 #if defined(CONFIG_BT_PAC_SNK)
60 static sys_slist_t snk_pac_list = SYS_SLIST_STATIC_INIT(&snk_pac_list);
61 static uint16_t snk_supported_contexts;
62 #if defined(CONFIG_BT_PAC_SNK_LOC)
63 static uint32_t pacs_snk_location;
64 #endif /* CONFIG_BT_PAC_SNK_LOC */
65 #endif /* CONFIG_BT_PAC_SNK */
66
67 static uint16_t src_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
68 static uint16_t snk_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
69
70 enum {
71 FLAG_ACTIVE,
72 FLAG_SINK_PAC_CHANGED,
73 FLAG_SINK_AUDIO_LOCATIONS_CHANGED,
74 FLAG_SOURCE_PAC_CHANGED,
75 FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED,
76 FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED,
77 FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED,
78 FLAG_NUM,
79 };
80
81 enum {
82 PACS_FLAG_REGISTERED,
83 PACS_FLAG_SVC_CHANGING,
84 PACS_FLAG_NOTIFY_RDY,
85 PACS_FLAG_SNK_PAC,
86 PACS_FLAG_SNK_LOC,
87 PACS_FLAG_SRC_PAC,
88 PACS_FLAG_SRC_LOC,
89 PACS_FLAG_NUM,
90 };
91
92 struct pacs_client {
93 bt_addr_le_t addr;
94
95 #if defined(CONFIG_BT_PAC_SNK)
96 /* Sink Available Contexts override value */
97 uint16_t *snk_available_contexts;
98 #endif /* CONFIG_BT_PAC_SNK */
99
100 #if defined(CONFIG_BT_PAC_SRC)
101 /* Source Available Contexts override value */
102 uint16_t *src_available_contexts;
103 #endif /* CONFIG_BT_PAC_SRC */
104
105 /* Pending notification flags */
106 ATOMIC_DEFINE(flags, FLAG_NUM);
107 };
108
109 static struct pacs {
110 ATOMIC_DEFINE(flags, PACS_FLAG_NUM);
111
112 struct pacs_client clients[CONFIG_BT_MAX_PAIRED];
113 } pacs;
114
115
116 static K_SEM_DEFINE(read_buf_sem, 1, 1);
117 NET_BUF_SIMPLE_DEFINE_STATIC(read_buf, BT_ATT_MAX_ATTRIBUTE_LEN);
118
119 static int pacs_gatt_notify(struct bt_conn *conn,
120 const struct bt_uuid *uuid,
121 const struct bt_gatt_attr *attr,
122 const void *data,
123 uint16_t len);
124 static void deferred_nfy_work_handler(struct k_work *work);
125
126 static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
127
128 struct pac_records_build_data {
129 struct bt_pacs_read_rsp *rsp;
130 struct net_buf_simple *buf;
131 };
132
client_lookup_conn(const struct bt_conn * conn)133 static struct pacs_client *client_lookup_conn(const struct bt_conn *conn)
134 {
135 __ASSERT_NO_MSG(conn != NULL);
136
137 for (size_t i = 0; i < ARRAY_SIZE(pacs.clients); i++) {
138 if (atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE) &&
139 bt_addr_le_eq(&pacs.clients[i].addr, bt_conn_get_dst(conn))) {
140 return &pacs.clients[i];
141 }
142 }
143
144 return NULL;
145 }
146
pacs_set_notify_bit(int bit)147 static void pacs_set_notify_bit(int bit)
148 {
149 for (size_t i = 0U; i < ARRAY_SIZE(pacs.clients); i++) {
150 if (atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE)) {
151 atomic_set_bit(pacs.clients[i].flags, bit);
152 }
153 }
154 }
155
build_pac_records(const struct bt_pacs_cap * cap,void * user_data)156 static bool build_pac_records(const struct bt_pacs_cap *cap, void *user_data)
157 {
158 struct pac_records_build_data *data = user_data;
159 const struct bt_audio_codec_cap *codec_cap = cap->codec_cap;
160 struct net_buf_simple *buf = data->buf;
161 struct net_buf_simple_state state;
162 struct bt_pac_codec *pac_codec;
163
164 net_buf_simple_save(buf, &state);
165
166 if (net_buf_simple_tailroom(buf) < sizeof(*pac_codec)) {
167 goto fail;
168 }
169
170 pac_codec = net_buf_simple_add(buf, sizeof(*pac_codec));
171 pac_codec->id = codec_cap->id;
172 pac_codec->cid = sys_cpu_to_le16(codec_cap->cid);
173 pac_codec->vid = sys_cpu_to_le16(codec_cap->vid);
174
175 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->data_len)) {
176 goto fail;
177 }
178
179 net_buf_simple_add_u8(buf, codec_cap->data_len);
180 net_buf_simple_add_mem(buf, codec_cap->data, codec_cap->data_len);
181
182 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->meta_len)) {
183 goto fail;
184 }
185
186 net_buf_simple_add_u8(buf, codec_cap->meta_len);
187 net_buf_simple_add_mem(buf, codec_cap->meta, codec_cap->meta_len);
188
189 data->rsp->num_pac++;
190
191 return true;
192
193 fail:
194 __ASSERT(false, "No space for %p", cap);
195
196 net_buf_simple_restore(buf, &state);
197
198 return false;
199 }
200
foreach_cap(sys_slist_t * list,bt_pacs_cap_foreach_func_t func,void * user_data)201 static void foreach_cap(sys_slist_t *list, bt_pacs_cap_foreach_func_t func,
202 void *user_data)
203 {
204 struct bt_pacs_cap *cap;
205
206 SYS_SLIST_FOR_EACH_CONTAINER(list, cap, _node) {
207 if (!func(cap, user_data)) {
208 break;
209 }
210 }
211 }
212
get_pac_records(sys_slist_t * list,struct net_buf_simple * buf)213 static void get_pac_records(sys_slist_t *list, struct net_buf_simple *buf)
214 {
215 struct pac_records_build_data data;
216
217 /* Reset if buffer before using */
218 net_buf_simple_reset(buf);
219
220 data.rsp = net_buf_simple_add(buf, sizeof(*data.rsp));
221 data.rsp->num_pac = 0;
222 data.buf = buf;
223
224 foreach_cap(list, build_pac_records, &data);
225 }
226
available_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)227 static void available_context_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
228 {
229 LOG_DBG("attr %p value 0x%04x", attr, value);
230 }
231
pacs_get_available_contexts_for_conn(struct bt_conn * conn,enum bt_audio_dir dir)232 static enum bt_audio_context pacs_get_available_contexts_for_conn(struct bt_conn *conn,
233 enum bt_audio_dir dir)
234 {
235 const struct pacs_client *client;
236
237 client = client_lookup_conn(conn);
238 if (client == NULL) {
239 LOG_DBG("No client context for conn %p", (void *)conn);
240 return bt_pacs_get_available_contexts(dir);
241 }
242
243 switch (dir) {
244 case BT_AUDIO_DIR_SINK:
245 #if defined(CONFIG_BT_PAC_SNK)
246 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC) &&
247 client->snk_available_contexts != NULL) {
248 return POINTER_TO_UINT(client->snk_available_contexts);
249 }
250 #endif /* CONFIG_BT_PAC_SNK */
251 break;
252 case BT_AUDIO_DIR_SOURCE:
253 #if defined(CONFIG_BT_PAC_SRC)
254 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC) &&
255 client->src_available_contexts != NULL) {
256 return POINTER_TO_UINT(client->src_available_contexts);
257 }
258 #endif /* CONFIG_BT_PAC_SRC */
259 break;
260 }
261
262 return bt_pacs_get_available_contexts(dir);
263 }
264
available_contexts_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)265 static ssize_t available_contexts_read(struct bt_conn *conn,
266 const struct bt_gatt_attr *attr, void *buf,
267 uint16_t len, uint16_t offset)
268 {
269 struct bt_pacs_context context = {
270 .snk = sys_cpu_to_le16(
271 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK)),
272 .src = sys_cpu_to_le16(
273 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE)),
274 };
275
276 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
277
278 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
279 sizeof(context));
280 }
281
282 #if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
supported_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)283 static void supported_context_cfg_changed(const struct bt_gatt_attr *attr,
284 uint16_t value)
285 {
286 LOG_DBG("attr %p value 0x%04x", attr, value);
287 }
288 #endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
289
supported_context_get(enum bt_audio_dir dir)290 static uint16_t supported_context_get(enum bt_audio_dir dir)
291 {
292 switch (dir) {
293 #if defined(CONFIG_BT_PAC_SNK)
294 case BT_AUDIO_DIR_SINK:
295 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
296 return snk_supported_contexts | BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
297 }
298 break;
299 #endif /* CONFIG_BT_PAC_SNK */
300 #if defined(CONFIG_BT_PAC_SRC)
301 case BT_AUDIO_DIR_SOURCE:
302 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
303 return src_supported_contexts | BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
304 }
305 break;
306 #endif /* CONFIG_BT_PAC_SRC */
307 default:
308 break;
309 }
310
311 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
312 }
313
supported_context_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)314 static ssize_t supported_context_read(struct bt_conn *conn,
315 const struct bt_gatt_attr *attr,
316 void *buf, uint16_t len, uint16_t offset)
317 {
318 struct bt_pacs_context context = {
319 .snk = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SINK)),
320 .src = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SOURCE)),
321 };
322
323 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
324
325 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
326 sizeof(context));
327 }
328
set_available_contexts(uint16_t contexts,uint16_t * available,uint16_t supported)329 static int set_available_contexts(uint16_t contexts, uint16_t *available,
330 uint16_t supported)
331 {
332 if (contexts & ~supported) {
333 return -ENOTSUP;
334 }
335
336 if (contexts == *available) {
337 return 0;
338 }
339
340 *available = contexts;
341
342 pacs_set_notify_bit(FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
343 k_work_submit(&deferred_nfy_work);
344
345 return 0;
346 }
347
set_supported_contexts(uint16_t contexts,uint16_t * supported,uint16_t * available)348 static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
349 uint16_t *available)
350 {
351 int err;
352 uint16_t tmp_supported = *supported;
353 uint16_t tmp_available = *available;
354
355 /* Ensure unspecified is always supported */
356 contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
357
358 if (*supported == contexts) {
359 return 0;
360 }
361
362 *supported = contexts;
363
364 /* Update available contexts if needed*/
365 if ((contexts & *available) != *available) {
366 err = set_available_contexts(contexts & *available, available, contexts);
367 if (err) {
368 *available = tmp_available;
369 *supported = tmp_supported;
370
371 return err;
372 }
373 }
374
375 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)) {
376 pacs_set_notify_bit(FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
377 k_work_submit(&deferred_nfy_work);
378 }
379
380 return 0;
381 }
382
383 #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)384 static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
385 void *buf, uint16_t len, uint16_t offset)
386 {
387 ssize_t ret_val;
388 int err;
389
390 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
391
392 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
393 if (err != 0) {
394 LOG_DBG("Failed to take read_buf_sem: %d", err);
395
396 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
397 }
398
399 get_pac_records(&snk_pac_list, &read_buf);
400
401 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
402 read_buf.len);
403
404 k_sem_give(&read_buf_sem);
405
406 return ret_val;
407 }
408
409 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
410 static const struct bt_uuid *pacs_snk_uuid = BT_UUID_PACS_SNK;
411
snk_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)412 static void snk_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
413 {
414 LOG_DBG("attr %p value 0x%04x", attr, value);
415 }
416 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
417 #endif /* CONFIG_BT_PAC_SNK */
418
419 #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)420 static ssize_t snk_loc_read(struct bt_conn *conn,
421 const struct bt_gatt_attr *attr, void *buf,
422 uint16_t len, uint16_t offset)
423 {
424 uint32_t location = sys_cpu_to_le32(pacs_snk_location);
425
426 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
427
428 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
429 sizeof(location));
430 }
431
432 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
433 static const struct bt_uuid *pacs_snk_loc_uuid = BT_UUID_PACS_SNK_LOC;
434
snk_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)435 static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
436 {
437 LOG_DBG("attr %p value 0x%04x", attr, value);
438 }
439 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
440
set_snk_location(enum bt_audio_location audio_location)441 static void set_snk_location(enum bt_audio_location audio_location)
442 {
443 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_LOC)) {
444 if (audio_location == pacs_snk_location) {
445 return;
446 }
447
448 pacs_snk_location = audio_location;
449
450 if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)) {
451 pacs_set_notify_bit(FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
452 k_work_submit(&deferred_nfy_work);
453 }
454 }
455 }
456 #else
set_snk_location(enum bt_audio_location location)457 static void set_snk_location(enum bt_audio_location location)
458 {
459 return;
460 }
461 #endif /* CONFIG_BT_PAC_SNK_LOC */
462
463 #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)464 static ssize_t snk_loc_write(struct bt_conn *conn,
465 const struct bt_gatt_attr *attr, const void *data,
466 uint16_t len, uint16_t offset, uint8_t flags)
467 {
468 enum bt_audio_location location;
469
470 if (offset) {
471 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
472 }
473
474 if (len != sizeof(location)) {
475 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
476 }
477
478 location = (enum bt_audio_location)sys_get_le32(data);
479 if (location > BT_AUDIO_LOCATION_MASK) {
480 LOG_DBG("Invalid location value: 0x%08X", location);
481 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
482 }
483
484 set_snk_location(location);
485
486 return len;
487 }
488 #endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
489
490 #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)491 static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
492 void *buf, uint16_t len, uint16_t offset)
493 {
494 ssize_t ret_val;
495 int err;
496
497 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
498
499 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
500 if (err != 0) {
501 LOG_DBG("Failed to take read_buf_sem: %d", err);
502
503 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
504 }
505
506 get_pac_records(&src_pac_list, &read_buf);
507
508 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
509 read_buf.len);
510
511 k_sem_give(&read_buf_sem);
512
513 return ret_val;
514 }
515
516 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
517 static const struct bt_uuid *pacs_src_uuid = BT_UUID_PACS_SRC;
518
src_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)519 static void src_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
520 {
521 LOG_DBG("attr %p value 0x%04x", attr, value);
522 }
523 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
524 #endif /* CONFIG_BT_PAC_SRC */
525
526 #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)527 static ssize_t src_loc_read(struct bt_conn *conn,
528 const struct bt_gatt_attr *attr, void *buf,
529 uint16_t len, uint16_t offset)
530 {
531 uint32_t location = sys_cpu_to_le32(pacs_src_location);
532
533 LOG_DBG("conn %p attr %p buf %p len %u offset %u", (void *)conn, attr, buf, len, offset);
534
535 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
536 sizeof(location));
537 }
538
539 #if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
540 static const struct bt_uuid *pacs_src_loc_uuid = BT_UUID_PACS_SRC_LOC;
541
src_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)542 static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
543 {
544 LOG_DBG("attr %p value 0x%04x", attr, value);
545 }
546 #endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
547
set_src_location(enum bt_audio_location audio_location)548 static void set_src_location(enum bt_audio_location audio_location)
549 {
550 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_LOC)) {
551 if (audio_location == pacs_src_location) {
552 return;
553 }
554
555 pacs_src_location = audio_location;
556
557 if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)) {
558 pacs_set_notify_bit(FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
559 k_work_submit(&deferred_nfy_work);
560 }
561 }
562 }
563 #else
set_src_location(enum bt_audio_location location)564 static void set_src_location(enum bt_audio_location location)
565 {
566 return;
567 }
568 #endif /* CONFIG_BT_PAC_SRC_LOC */
569
570 #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)571 static ssize_t src_loc_write(struct bt_conn *conn,
572 const struct bt_gatt_attr *attr, const void *data,
573 uint16_t len, uint16_t offset, uint8_t flags)
574 {
575 uint32_t location;
576
577 if (offset) {
578 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
579 }
580
581 if (len != sizeof(location)) {
582 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
583 }
584
585 location = (enum bt_audio_location)sys_get_le32(data);
586 if (location > BT_AUDIO_LOCATION_MASK) {
587 LOG_DBG("Invalid location value: 0x%08X", location);
588 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
589 }
590
591 set_src_location(location);
592
593 return len;
594 }
595 #endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
596
597
pacs_get_pac(enum bt_audio_dir dir)598 static sys_slist_t *pacs_get_pac(enum bt_audio_dir dir)
599 {
600 switch (dir) {
601 #if defined(CONFIG_BT_PAC_SNK)
602 case BT_AUDIO_DIR_SINK:
603 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
604 return &snk_pac_list;
605 }
606 return NULL;
607 #endif /* CONFIG_BT_PAC_SNK */
608 #if defined(CONFIG_BT_PAC_SRC)
609 case BT_AUDIO_DIR_SOURCE:
610 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
611 return &src_pac_list;
612 }
613 return NULL;
614 #endif /* CONFIG_BT_PAC_SRC */
615 default:
616 return NULL;
617 }
618 }
619
620 #if defined(CONFIG_BT_PAC_SNK)
621 #define BT_PACS_SNK_PROP \
622 BT_GATT_CHRC_READ \
623 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
624 #define BT_PAC_SNK \
625 BT_AUDIO_CHRC(BT_UUID_PACS_SNK, BT_PACS_SNK_PROP, BT_GATT_PERM_READ_ENCRYPT, snk_read, \
626 NULL, NULL), \
627 IF_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE, (BT_AUDIO_CCC(snk_cfg_changed),))
628
629 #define BT_PACS_SNK_LOC_PROP \
630 BT_GATT_CHRC_READ \
631 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
632 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
633
634 #define BT_PACS_SNK_LOC_PERM \
635 BT_GATT_PERM_READ_ENCRYPT \
636 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
637
638 /* declaration + value [+ cccd] */
639 #define PACS_SNK_PAC_CHAR_ATTR_COUNT COND_CODE_1(IS_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE), (3), (2))
640 #else
641 #define BT_PAC_SNK
642 #define PACS_SNK_PAC_CHAR_ATTR_COUNT 0
643 #endif /* CONFIG_BT_PAC_SNK */
644
645 #if defined(CONFIG_BT_PAC_SNK_LOC)
646 #define BT_PACS_SNK_LOC \
647 BT_AUDIO_CHRC(BT_UUID_PACS_SNK_LOC, BT_PACS_SNK_LOC_PROP, BT_PACS_SNK_LOC_PERM, \
648 snk_loc_read, \
649 COND_CODE_1(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (snk_loc_write), (NULL)), NULL),\
650 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (BT_AUDIO_CCC(snk_loc_cfg_changed),))
651
652 /* declaration + value [+ cccd] */
653 #define PACS_SNK_PAC_LOC_CHAR_ATTR_COUNT \
654 COND_CODE_1(IS_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE), (3), (2))
655 #else
656 #define BT_PACS_SNK_LOC
657 #define PACS_SNK_PAC_LOC_CHAR_ATTR_COUNT 0
658 #endif /* CONFIG_BT_PAC_SNK_LOC*/
659
660 #if defined(CONFIG_BT_PAC_SRC)
661 #define BT_PACS_SRC_PROP \
662 BT_GATT_CHRC_READ \
663 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
664 #define BT_PAC_SRC \
665 BT_AUDIO_CHRC(BT_UUID_PACS_SRC, BT_PACS_SRC_PROP, BT_GATT_PERM_READ_ENCRYPT, src_read, \
666 NULL, NULL), \
667 IF_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE, (BT_AUDIO_CCC(src_cfg_changed),))
668
669 #define BT_PACS_SRC_LOC_PROP \
670 BT_GATT_CHRC_READ \
671 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
672 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
673
674 #define BT_PACS_SRC_LOC_PERM \
675 BT_GATT_PERM_READ_ENCRYPT \
676 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
677
678 /* declaration + value [+ cccd] */
679 #define PACS_SRC_PAC_CHAR_ATTR_COUNT COND_CODE_1(IS_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE), (3), (2))
680 #else
681 #define BT_PAC_SRC
682 #define PACS_SRC_PAC_CHAR_ATTR_COUNT 0
683 #endif
684
685 #if defined(CONFIG_BT_PAC_SRC_LOC)
686 #define BT_PACS_SRC_LOC \
687 BT_AUDIO_CHRC(BT_UUID_PACS_SRC_LOC, BT_PACS_SRC_LOC_PROP, BT_PACS_SRC_LOC_PERM, \
688 src_loc_read, \
689 COND_CODE_1(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (src_loc_write), (NULL)), NULL),\
690 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (BT_AUDIO_CCC(src_loc_cfg_changed),))
691
692 /* declaration + value [+ cccd] */
693 #define PACS_SRC_PAC_LOC_CHAR_ATTR_COUNT \
694 COND_CODE_1(IS_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE), (3), (2))
695 #else
696 #define BT_PACS_SRC_LOC
697 #define PACS_SRC_PAC_LOC_CHAR_ATTR_COUNT 0
698 #endif
699
700 #define BT_PAC_AVAILABLE_CONTEXT \
701 BT_AUDIO_CHRC(BT_UUID_PACS_AVAILABLE_CONTEXT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
702 BT_GATT_PERM_READ_ENCRYPT, available_contexts_read, NULL, NULL), \
703 BT_AUDIO_CCC(available_context_cfg_changed),
704
705 #define BT_PACS_SUPPORTED_CONTEXT_PROP \
706 BT_GATT_CHRC_READ \
707 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
708
709 #define BT_PAC_SUPPORTED_CONTEXT \
710 BT_AUDIO_CHRC(BT_UUID_PACS_SUPPORTED_CONTEXT, BT_PACS_SUPPORTED_CONTEXT_PROP, \
711 BT_GATT_PERM_READ_ENCRYPT, supported_context_read, NULL, NULL), \
712 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, \
713 (BT_AUDIO_CCC(supported_context_cfg_changed),))
714
715 #define BT_PACS_SERVICE_DEFINITION() { \
716 BT_GATT_PRIMARY_SERVICE(BT_UUID_PACS), \
717 BT_PAC_SNK \
718 BT_PACS_SNK_LOC \
719 BT_PAC_SRC \
720 BT_PACS_SRC_LOC \
721 BT_PAC_AVAILABLE_CONTEXT \
722 BT_PAC_SUPPORTED_CONTEXT \
723 }
724
725 static const struct bt_gatt_attr _pacs_attrs[] = BT_PACS_SERVICE_DEFINITION();
726 static struct bt_gatt_attr pacs_attrs[] = BT_PACS_SERVICE_DEFINITION();
727 static struct bt_gatt_service pacs_svc = (struct bt_gatt_service)BT_GATT_SERVICE(pacs_attrs);
728
configure_pacs_char(const struct bt_pacs_register_param * param)729 static void configure_pacs_char(const struct bt_pacs_register_param *param)
730 {
731 const uint8_t first_attr_offset = 1U;
732 struct bt_gatt_attr *svc_attrs =
733 &pacs_svc.attrs[first_attr_offset]; /* first attribute is the service */
734 uint8_t attrs_to_rem = 0U;
735 uint8_t first_to_rem = 0U;
736
737 /* Remove the Sink PAC and Location */
738 #if defined(CONFIG_BT_PAC_SNK_LOC)
739 if (!param->snk_loc) {
740 first_to_rem = PACS_SNK_PAC_CHAR_ATTR_COUNT;
741 attrs_to_rem = PACS_SNK_PAC_LOC_CHAR_ATTR_COUNT;
742 }
743 #endif /* CONFIG_BT_PAC_SNK_LOC */
744 #if defined(CONFIG_BT_PAC_SNK)
745 if (!param->snk_pac) {
746 first_to_rem = 0U;
747 attrs_to_rem = PACS_SNK_PAC_CHAR_ATTR_COUNT + PACS_SNK_PAC_LOC_CHAR_ATTR_COUNT;
748 }
749 #endif /* CONFIG_BT_PAC_SNK */
750
751 if (attrs_to_rem > 0U) {
752 for (uint8_t i = first_to_rem + attrs_to_rem;
753 i < pacs_svc.attr_count - first_attr_offset; i++) {
754 svc_attrs[i - attrs_to_rem] = svc_attrs[i];
755 }
756 pacs_svc.attr_count -= attrs_to_rem;
757 }
758
759 #if defined(CONFIG_BT_PAC_SRC)
760 /* Set first_to_rem to the start of Source PAC Char, for cleaner offset calc */
761 const uint8_t src_pac_offset =
762 (PACS_SNK_PAC_CHAR_ATTR_COUNT + PACS_SNK_PAC_LOC_CHAR_ATTR_COUNT) - attrs_to_rem;
763 attrs_to_rem = 0U;
764
765 /* Remove the Source PAC and Location */
766 #if defined(CONFIG_BT_PAC_SRC_LOC)
767 if (!param->src_loc) {
768 first_to_rem = src_pac_offset + PACS_SRC_PAC_CHAR_ATTR_COUNT;
769 attrs_to_rem = PACS_SRC_PAC_LOC_CHAR_ATTR_COUNT;
770 }
771 #endif /* CONFIG_BT_PAC_SRC_LOC */
772
773 if (!param->src_pac) {
774 first_to_rem = src_pac_offset;
775 attrs_to_rem = PACS_SRC_PAC_CHAR_ATTR_COUNT + PACS_SRC_PAC_LOC_CHAR_ATTR_COUNT;
776 }
777
778 if (attrs_to_rem > 0U) {
779 for (uint8_t i = first_to_rem + attrs_to_rem;
780 i < pacs_svc.attr_count - first_attr_offset; i++) {
781 svc_attrs[i - attrs_to_rem] = svc_attrs[i];
782 }
783 pacs_svc.attr_count -= attrs_to_rem;
784 }
785 #endif /* CONFIG_BT_PAC_SRC */
786 }
787
valid_pacs_register_param(const struct bt_pacs_register_param * param)788 static bool valid_pacs_register_param(const struct bt_pacs_register_param *param)
789 {
790 bool any_pac_registered = false;
791
792 if (param == NULL) {
793 LOG_DBG("param is NULL");
794 return false;
795 }
796
797 #if defined(CONFIG_BT_PAC_SNK)
798 any_pac_registered |= param->snk_pac;
799 #endif /* CONFIG_BT_PAC_SNK */
800 #if defined(CONFIG_BT_PAC_SNK_LOC)
801 if (param->snk_loc && !param->snk_pac) {
802 LOG_DBG("Cannot register snk_loc without snk_pac");
803 return false;
804 }
805 #endif /* CONFIG_BT_PAC_SNK_LOC */
806 #if defined(CONFIG_BT_PAC_SRC)
807 any_pac_registered |= param->src_pac;
808 #endif /* CONFIG_BT_PAC_SRC */
809 #if defined(CONFIG_BT_PAC_SRC_LOC)
810 if (param->src_loc && !param->src_pac) {
811 LOG_DBG("Cannot register src_loc without src_pac");
812 return false;
813 }
814 #endif /* CONFIG_BT_PAC_SRC_LOC */
815
816 if (!any_pac_registered) {
817 LOG_DBG("Neither snk_pac or src_pac registered");
818 return false;
819 }
820
821 return true;
822 }
823
bt_pacs_register(const struct bt_pacs_register_param * param)824 int bt_pacs_register(const struct bt_pacs_register_param *param)
825 {
826 int err = 0;
827
828 if (!valid_pacs_register_param(param)) {
829 return -EINVAL;
830 }
831
832 if (atomic_test_and_set_bit(pacs.flags, PACS_FLAG_REGISTERED)) {
833 LOG_DBG("PACS already registered");
834
835 return -EALREADY;
836 }
837
838 /* Save registration param so we can guard functions accordingly */
839 #if defined(CONFIG_BT_PAC_SNK)
840 atomic_set_bit_to(pacs.flags, PACS_FLAG_SNK_PAC, param->snk_pac);
841 #endif /* CONFIG_BT_PAC_SNK */
842 #if defined(CONFIG_BT_PAC_SNK_LOC)
843 atomic_set_bit_to(pacs.flags, PACS_FLAG_SNK_LOC, param->snk_loc);
844 #endif /* CONFIG_BT_PAC_SNK_LOC */
845 #if defined(CONFIG_BT_PAC_SRC)
846 atomic_set_bit_to(pacs.flags, PACS_FLAG_SRC_PAC, param->src_pac);
847 #endif /* CONFIG_BT_PAC_SRC */
848 #if defined(CONFIG_BT_PAC_SRC_LOC)
849 atomic_set_bit_to(pacs.flags, PACS_FLAG_SRC_LOC, param->src_loc);
850 #endif /* CONFIG_BT_PAC_SRC_LOC */
851
852 /* Remove characteristics if necessary */
853 configure_pacs_char(param);
854
855 err = bt_gatt_service_register(&pacs_svc);
856 if (err != 0) {
857 LOG_DBG("Failed to register ASCS in gatt DB: %d", err);
858 atomic_clear_bit(pacs.flags, PACS_FLAG_REGISTERED);
859
860 return -ENOEXEC;
861 }
862
863 return 0;
864 }
865
bt_pacs_unregister(void)866 int bt_pacs_unregister(void)
867 {
868 int err;
869
870 if (!atomic_test_bit(pacs.flags, PACS_FLAG_REGISTERED)) {
871 LOG_DBG("No pacs instance registered");
872
873 return -EALREADY;
874 }
875
876 if (atomic_test_and_set_bit(pacs.flags, PACS_FLAG_SVC_CHANGING)) {
877 LOG_DBG("Service change already in progress");
878 atomic_clear_bit(pacs.flags, PACS_FLAG_SVC_CHANGING);
879
880 return -EBUSY;
881 }
882
883 err = bt_gatt_service_unregister(&pacs_svc);
884
885 /* If unregistration was successful, make sure to reset pacs_attrs so it can be used for
886 * new registrations
887 */
888 if (err != 0) {
889 LOG_DBG("Failed to unregister PACS");
890 atomic_clear_bit(pacs.flags, PACS_FLAG_SVC_CHANGING);
891
892 return err;
893 }
894
895 /* Restore to original definition */
896 memcpy(pacs_svc.attrs, &_pacs_attrs, sizeof(_pacs_attrs));
897 pacs_svc.attr_count = ARRAY_SIZE(pacs_attrs);
898
899 atomic_clear_bit(pacs.flags, PACS_FLAG_REGISTERED);
900 atomic_clear_bit(pacs.flags, PACS_FLAG_SVC_CHANGING);
901
902 return err;
903 }
904
905 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
pac_notify_loc(struct bt_conn * conn,enum bt_audio_dir dir)906 static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir)
907 {
908 uint32_t location_le;
909 int err;
910 const struct bt_uuid *uuid;
911
912 switch (dir) {
913 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
914 case BT_AUDIO_DIR_SINK:
915 location_le = sys_cpu_to_le32(pacs_snk_location);
916 uuid = pacs_snk_loc_uuid;
917 break;
918 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
919 #if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
920 case BT_AUDIO_DIR_SOURCE:
921 location_le = sys_cpu_to_le32(pacs_src_location);
922 uuid = pacs_src_loc_uuid;
923 break;
924 #endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
925 default:
926 return -EINVAL;
927 }
928
929 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs, &location_le, sizeof(location_le));
930 if (err != 0 && err != -ENOTCONN) {
931 LOG_WRN("PACS notify_loc failed: %d", err);
932 return err;
933 }
934
935 return 0;
936 }
937 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE || CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
938
939 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
pac_notify(struct bt_conn * conn,enum bt_audio_dir dir)940 static int pac_notify(struct bt_conn *conn, enum bt_audio_dir dir)
941 {
942 int err = 0;
943 sys_slist_t *pac;
944 const struct bt_uuid *uuid;
945
946 switch (dir) {
947 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
948 case BT_AUDIO_DIR_SINK:
949 uuid = pacs_snk_uuid;
950 break;
951 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
952 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
953 case BT_AUDIO_DIR_SOURCE:
954 uuid = pacs_src_uuid;
955 break;
956 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
957 default:
958 return -EINVAL;
959 }
960
961 err = k_sem_take(&read_buf_sem, K_NO_WAIT);
962 if (err != 0) {
963 LOG_DBG("Failed to take read_buf_sem: %d", err);
964
965 return err;
966 }
967
968 pac = pacs_get_pac(dir);
969 __ASSERT(pac, "Failed to get pacs.\n");
970 get_pac_records(pac, &read_buf);
971
972 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs,
973 read_buf.data, read_buf.len);
974 if (err != 0 && err != -ENOTCONN) {
975 LOG_WRN("PACS notify failed: %d", err);
976 }
977
978 k_sem_give(&read_buf_sem);
979
980 if (err == -ENOTCONN) {
981 return 0;
982 } else {
983 return 0;
984 }
985 }
986 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE || CONFIG_BT_PAC_SRC_NOTIFIABLE */
987
available_contexts_notify(struct bt_conn * conn)988 static int available_contexts_notify(struct bt_conn *conn)
989 {
990 struct bt_pacs_context context = {
991 .snk = sys_cpu_to_le16(
992 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK)),
993 .src = sys_cpu_to_le16(
994 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE)),
995 };
996 int err;
997
998 err = pacs_gatt_notify(conn, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
999 &context, sizeof(context));
1000 if (err != 0 && err != -ENOTCONN) {
1001 LOG_WRN("Available Audio Contexts notify failed: %d", err);
1002 return err;
1003 }
1004
1005 return 0;
1006 }
1007
supported_contexts_notify(struct bt_conn * conn)1008 static int supported_contexts_notify(struct bt_conn *conn)
1009 {
1010 struct bt_pacs_context context = {
1011 .snk = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SINK)),
1012 .src = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SOURCE)),
1013 };
1014 int err;
1015
1016 err = pacs_gatt_notify(conn, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
1017 &context, sizeof(context));
1018 if (err != 0 && err != -ENOTCONN) {
1019 LOG_WRN("Supported Audio Contexts notify failed: %d", err);
1020
1021 return err;
1022 }
1023 return 0;
1024 }
1025
pacs_gatt_notify_complete_cb(struct bt_conn * conn,void * user_data)1026 void pacs_gatt_notify_complete_cb(struct bt_conn *conn, void *user_data)
1027 {
1028 /* Notification done, clear bit and reschedule work */
1029 atomic_clear_bit(pacs.flags, PACS_FLAG_NOTIFY_RDY);
1030 k_work_submit(&deferred_nfy_work);
1031 }
1032
pacs_gatt_notify(struct bt_conn * conn,const struct bt_uuid * uuid,const struct bt_gatt_attr * attr,const void * data,uint16_t len)1033 static int pacs_gatt_notify(struct bt_conn *conn,
1034 const struct bt_uuid *uuid,
1035 const struct bt_gatt_attr *attr,
1036 const void *data,
1037 uint16_t len)
1038 {
1039 int err;
1040 struct bt_gatt_notify_params params;
1041
1042 memset(¶ms, 0, sizeof(params));
1043 params.uuid = uuid;
1044 params.attr = attr;
1045 params.data = data;
1046 params.len = len;
1047 params.func = pacs_gatt_notify_complete_cb;
1048
1049 /* Mark notification in progress */
1050 if (atomic_test_bit(pacs.flags, PACS_FLAG_SVC_CHANGING) ||
1051 !atomic_test_bit(pacs.flags, PACS_FLAG_REGISTERED)) {
1052 return 0;
1053 }
1054
1055 atomic_set_bit(pacs.flags, PACS_FLAG_NOTIFY_RDY);
1056
1057 err = bt_gatt_notify_cb(conn, ¶ms);
1058 if (err != 0) {
1059 atomic_clear_bit(pacs.flags, PACS_FLAG_NOTIFY_RDY);
1060 }
1061
1062 if (err && err != -ENOTCONN) {
1063 return err;
1064 }
1065
1066 return 0;
1067 }
1068
notify_cb(struct bt_conn * conn,void * data)1069 static void notify_cb(struct bt_conn *conn, void *data)
1070 {
1071 struct pacs_client *client;
1072 struct bt_conn_info info;
1073 int err = 0;
1074
1075 LOG_DBG("");
1076
1077 err = bt_conn_get_info(conn, &info);
1078 if (err != 0) {
1079 LOG_ERR("Failed to get conn info: %d", err);
1080 return;
1081 }
1082
1083 if (info.state != BT_CONN_STATE_CONNECTED) {
1084 /* Not connected */
1085 return;
1086 }
1087
1088 client = client_lookup_conn(conn);
1089 if (client == NULL) {
1090 return;
1091 }
1092
1093 /* Check if we have unverified notifications in progress */
1094 if (atomic_test_bit(pacs.flags, PACS_FLAG_NOTIFY_RDY)) {
1095 return;
1096 }
1097
1098 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
1099 if (atomic_test_bit(client->flags, FLAG_SINK_PAC_CHANGED)) {
1100 LOG_DBG("Notifying Sink PAC");
1101 err = pac_notify(conn, BT_AUDIO_DIR_SINK);
1102 if (!err) {
1103 atomic_clear_bit(client->flags, FLAG_SINK_PAC_CHANGED);
1104 }
1105 }
1106 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
1107
1108 #if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
1109 if (atomic_test_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED)) {
1110 LOG_DBG("Notifying Sink Audio Location");
1111 err = pac_notify_loc(conn, BT_AUDIO_DIR_SINK);
1112 if (!err) {
1113 atomic_clear_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
1114 }
1115 }
1116 #endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
1117
1118 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
1119 if (atomic_test_bit(client->flags, FLAG_SOURCE_PAC_CHANGED)) {
1120 LOG_DBG("Notifying Source PAC");
1121 err = pac_notify(conn, BT_AUDIO_DIR_SOURCE);
1122 if (!err) {
1123 atomic_clear_bit(client->flags, FLAG_SOURCE_PAC_CHANGED);
1124 }
1125 }
1126 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
1127
1128 #if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
1129 if (atomic_test_and_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED)) {
1130 LOG_DBG("Notifying Source Audio Location");
1131 err = pac_notify_loc(conn, BT_AUDIO_DIR_SOURCE);
1132 if (!err) {
1133 atomic_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
1134 }
1135 }
1136 #endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
1137
1138 if (atomic_test_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED)) {
1139 LOG_DBG("Notifying Available Contexts");
1140 err = available_contexts_notify(conn);
1141 if (!err) {
1142 atomic_clear_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
1143 }
1144 }
1145
1146 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE) &&
1147 atomic_test_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED)) {
1148 LOG_DBG("Notifying Supported Contexts");
1149 err = supported_contexts_notify(conn);
1150 if (!err) {
1151 atomic_clear_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
1152 }
1153 }
1154 }
1155
deferred_nfy_work_handler(struct k_work * work)1156 static void deferred_nfy_work_handler(struct k_work *work)
1157 {
1158 bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
1159 }
1160
pacs_auth_pairing_complete(struct bt_conn * conn,bool bonded)1161 static void pacs_auth_pairing_complete(struct bt_conn *conn, bool bonded)
1162 {
1163 LOG_DBG("%s paired (%sbonded)", bt_addr_le_str(bt_conn_get_dst(conn)),
1164 bonded ? "" : "not ");
1165
1166 if (!bonded) {
1167 return;
1168 }
1169
1170 /* Check if already in list, and do nothing if it is */
1171 for (size_t i = 0U; i < ARRAY_SIZE(pacs.clients); i++) {
1172 if (atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE) &&
1173 bt_addr_le_eq(bt_conn_get_dst(conn), &pacs.clients[i].addr)) {
1174 return;
1175 }
1176 }
1177
1178 /* Else add the device */
1179 for (size_t i = 0U; i < ARRAY_SIZE(pacs.clients); i++) {
1180 if (!atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE)) {
1181 atomic_set_bit(pacs.clients[i].flags, FLAG_ACTIVE);
1182 memcpy(&pacs.clients[i].addr, bt_conn_get_dst(conn), sizeof(bt_addr_le_t));
1183
1184 /* Send out all pending notifications */
1185 k_work_submit(&deferred_nfy_work);
1186 return;
1187 }
1188 }
1189 }
1190
pacs_bond_deleted(uint8_t id,const bt_addr_le_t * peer)1191 static void pacs_bond_deleted(uint8_t id, const bt_addr_le_t *peer)
1192 {
1193 /* Find the device entry to delete */
1194 for (size_t i = 0U; i < ARRAY_SIZE(pacs.clients); i++) {
1195 /* Check if match, and if active, if so, reset */
1196 if (atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE) &&
1197 bt_addr_le_eq(peer, &pacs.clients[i].addr)) {
1198 for (size_t j = 0U; j < FLAG_NUM; j++) {
1199 atomic_clear_bit(pacs.clients[i].flags, j);
1200 }
1201 (void)memset(&pacs.clients[i].addr, 0, sizeof(bt_addr_le_t));
1202 return;
1203 }
1204 }
1205 }
1206
pacs_security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err sec_err)1207 static void pacs_security_changed(struct bt_conn *conn, bt_security_t level,
1208 enum bt_security_err sec_err)
1209 {
1210 struct bt_conn_info info;
1211 int err;
1212
1213 LOG_DBG("%s changed security level to %d", bt_addr_le_str(bt_conn_get_dst(conn)), level);
1214
1215 if (sec_err != BT_SECURITY_ERR_SUCCESS || level <= BT_SECURITY_L1) {
1216 return;
1217 }
1218
1219 err = bt_conn_get_info(conn, &info);
1220 if (err < 0) {
1221 __ASSERT_NO_MSG(false);
1222 return;
1223 }
1224
1225 if (!bt_le_bond_exists(info.id, info.le.dst)) {
1226 return;
1227 }
1228
1229 for (size_t i = 0U; i < ARRAY_SIZE(pacs.clients); i++) {
1230 for (size_t j = 0U; j < FLAG_NUM; j++) {
1231 if (atomic_test_bit(pacs.clients[i].flags, j)) {
1232
1233 /**
1234 * It's enough that one flag is set, as the defer work will go
1235 * through all notifiable characteristics
1236 */
1237 k_work_submit(&deferred_nfy_work);
1238 return;
1239 }
1240 }
1241 }
1242 }
1243
pacs_disconnected(struct bt_conn * conn,uint8_t reason)1244 static void pacs_disconnected(struct bt_conn *conn, uint8_t reason)
1245 {
1246 struct pacs_client *client;
1247
1248 client = client_lookup_conn(conn);
1249 if (client == NULL) {
1250 return;
1251 }
1252
1253 #if defined(CONFIG_BT_PAC_SNK)
1254 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC) &&
1255 client->snk_available_contexts != NULL) {
1256 uint16_t old = POINTER_TO_UINT(client->snk_available_contexts);
1257 uint16_t new;
1258
1259 client->snk_available_contexts = NULL;
1260 new = pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK);
1261
1262 atomic_set_bit_to(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED, old != new);
1263 }
1264 #endif /* CONFIG_BT_PAC_SNK */
1265
1266 #if defined(CONFIG_BT_PAC_SRC)
1267 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC) &&
1268 client->src_available_contexts != NULL) {
1269 uint16_t old = POINTER_TO_UINT(client->src_available_contexts);
1270 uint16_t new;
1271
1272 client->src_available_contexts = NULL;
1273 new = pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE);
1274
1275 atomic_set_bit_to(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED, old != new);
1276 }
1277 #endif /* CONFIG_BT_PAC_SRC */
1278 }
1279
1280 BT_CONN_CB_DEFINE(conn_callbacks) = {
1281 .security_changed = pacs_security_changed,
1282 .disconnected = pacs_disconnected,
1283 };
1284
1285 static struct bt_conn_auth_info_cb auth_callbacks = {
1286 .pairing_complete = pacs_auth_pairing_complete,
1287 .bond_deleted = pacs_bond_deleted
1288 };
1289
bt_pacs_cap_foreach(enum bt_audio_dir dir,bt_pacs_cap_foreach_func_t func,void * user_data)1290 void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
1291 {
1292 sys_slist_t *pac;
1293
1294 CHECKIF(func == NULL) {
1295 LOG_ERR("func is NULL");
1296 return;
1297 }
1298
1299 pac = pacs_get_pac(dir);
1300 if (!pac) {
1301 return;
1302 }
1303
1304 foreach_cap(pac, func, user_data);
1305 }
1306
add_bonded_addr_to_client_list(const struct bt_bond_info * info,void * data)1307 static void add_bonded_addr_to_client_list(const struct bt_bond_info *info, void *data)
1308 {
1309 for (uint8_t i = 0; i < ARRAY_SIZE(pacs.clients); i++) {
1310 /* Check if device is registered, it not, add it */
1311 if (!atomic_test_bit(pacs.clients[i].flags, FLAG_ACTIVE)) {
1312 char addr_str[BT_ADDR_LE_STR_LEN];
1313
1314 atomic_set_bit(pacs.clients[i].flags, FLAG_ACTIVE);
1315 memcpy(&pacs.clients[i].addr, &info->addr, sizeof(bt_addr_le_t));
1316 bt_addr_le_to_str(&pacs.clients[i].addr, addr_str, sizeof(addr_str));
1317 LOG_DBG("Added %s to bonded list\n", addr_str);
1318 return;
1319 }
1320 }
1321 }
1322
1323 /* Register Audio Capability */
bt_pacs_cap_register(enum bt_audio_dir dir,struct bt_pacs_cap * cap)1324 int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
1325 {
1326 const struct bt_audio_codec_cap *codec_cap;
1327 static bool callbacks_registered;
1328 sys_slist_t *pac;
1329
1330 if (!cap || !cap->codec_cap) {
1331 return -EINVAL;
1332 }
1333
1334 codec_cap = cap->codec_cap;
1335
1336 pac = pacs_get_pac(dir);
1337 if (!pac) {
1338 return -EINVAL;
1339 }
1340
1341 /* Restore bonding list */
1342 bt_foreach_bond(BT_ID_DEFAULT, add_bonded_addr_to_client_list, NULL);
1343
1344 LOG_DBG("cap %p dir %s codec_cap id 0x%02x codec_cap cid 0x%04x codec_cap vid 0x%04x", cap,
1345 bt_audio_dir_str(dir), codec_cap->id, codec_cap->cid, codec_cap->vid);
1346
1347 sys_slist_append(pac, &cap->_node);
1348
1349 if (!callbacks_registered) {
1350 bt_conn_auth_info_cb_register(&auth_callbacks);
1351
1352 callbacks_registered = true;
1353 }
1354
1355 if (IS_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE) && dir == BT_AUDIO_DIR_SINK) {
1356 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1357 k_work_submit(&deferred_nfy_work);
1358 }
1359
1360 if (IS_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE) && dir == BT_AUDIO_DIR_SOURCE) {
1361 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1362 k_work_submit(&deferred_nfy_work);
1363 }
1364
1365 return 0;
1366 }
1367
1368 /* Unregister Audio Capability */
bt_pacs_cap_unregister(enum bt_audio_dir dir,struct bt_pacs_cap * cap)1369 int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
1370 {
1371 sys_slist_t *pac;
1372
1373 if (!cap) {
1374 return -EINVAL;
1375 }
1376
1377 pac = pacs_get_pac(dir);
1378 if (!pac) {
1379 return -EINVAL;
1380 }
1381
1382 LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir));
1383
1384 if (!sys_slist_find_and_remove(pac, &cap->_node)) {
1385 return -ENOENT;
1386 }
1387
1388 switch (dir) {
1389 #if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
1390 case BT_AUDIO_DIR_SINK:
1391 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1392 k_work_submit(&deferred_nfy_work);
1393 break;
1394 #endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
1395 #if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
1396 case BT_AUDIO_DIR_SOURCE:
1397 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1398 k_work_submit(&deferred_nfy_work);
1399 break;
1400 #endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
1401 default:
1402 return -EINVAL;
1403 }
1404
1405 return 0;
1406 }
1407
bt_pacs_set_location(enum bt_audio_dir dir,enum bt_audio_location location)1408 int bt_pacs_set_location(enum bt_audio_dir dir, enum bt_audio_location location)
1409 {
1410 switch (dir) {
1411 case BT_AUDIO_DIR_SINK:
1412 set_snk_location(location);
1413 break;
1414 case BT_AUDIO_DIR_SOURCE:
1415 set_src_location(location);
1416 break;
1417 default:
1418 return -EINVAL;
1419 }
1420
1421 return 0;
1422 }
1423
bt_pacs_set_available_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)1424 int bt_pacs_set_available_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
1425 {
1426 if (!atomic_test_bit(pacs.flags, PACS_FLAG_REGISTERED)) {
1427 return -EINVAL;
1428 }
1429 switch (dir) {
1430 case BT_AUDIO_DIR_SINK:
1431 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
1432 return set_available_contexts(contexts, &snk_available_contexts,
1433 supported_context_get(dir));
1434 }
1435 return -EINVAL;
1436 case BT_AUDIO_DIR_SOURCE:
1437 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
1438 return set_available_contexts(contexts, &src_available_contexts,
1439 supported_context_get(dir));
1440 }
1441 return -EINVAL;
1442 }
1443
1444 return -EINVAL;
1445 }
1446
bt_pacs_conn_set_available_contexts_for_conn(struct bt_conn * conn,enum bt_audio_dir dir,enum bt_audio_context * contexts)1447 int bt_pacs_conn_set_available_contexts_for_conn(struct bt_conn *conn, enum bt_audio_dir dir,
1448 enum bt_audio_context *contexts)
1449 {
1450 enum bt_audio_context old = pacs_get_available_contexts_for_conn(conn, dir);
1451 struct bt_conn_info info = { 0 };
1452 struct pacs_client *client;
1453 int err;
1454
1455 client = client_lookup_conn(conn);
1456 if (client == NULL) {
1457 return -ENOENT;
1458 }
1459
1460 err = bt_conn_get_info(conn, &info);
1461 if (err < 0) {
1462 LOG_ERR("Could not get conn info: %d", err);
1463 return err;
1464 }
1465
1466 switch (dir) {
1467 #if defined(CONFIG_BT_PAC_SNK)
1468 case BT_AUDIO_DIR_SINK:
1469 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
1470 if (contexts != NULL) {
1471 client->snk_available_contexts = UINT_TO_POINTER(*contexts);
1472 } else {
1473 client->snk_available_contexts = NULL;
1474 }
1475 break;
1476 }
1477
1478 return -EINVAL;
1479 #endif /* CONFIG_BT_PAC_SNK */
1480 #if defined(CONFIG_BT_PAC_SRC)
1481 case BT_AUDIO_DIR_SOURCE:
1482 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
1483 if (contexts != NULL) {
1484 client->src_available_contexts = UINT_TO_POINTER(*contexts);
1485 } else {
1486 client->src_available_contexts = NULL;
1487 }
1488 break;
1489 }
1490
1491 return -EINVAL;
1492 #endif /* CONFIG_BT_PAC_SRC */
1493 default:
1494 return -EINVAL;
1495 }
1496
1497 if (pacs_get_available_contexts_for_conn(conn, dir) == old) {
1498 /* No change. Skip notification */
1499 return 0;
1500 }
1501
1502 atomic_set_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
1503
1504 /* Send notification on encrypted link only */
1505 if (info.security.level > BT_SECURITY_L1) {
1506 k_work_submit(&deferred_nfy_work);
1507 }
1508
1509 return 0;
1510 }
1511
bt_pacs_set_supported_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)1512 int bt_pacs_set_supported_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
1513 {
1514 uint16_t *supported_contexts = NULL;
1515 uint16_t *available_contexts = NULL;
1516
1517 switch (dir) {
1518 case BT_AUDIO_DIR_SINK:
1519 #if defined(CONFIG_BT_PAC_SNK)
1520 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
1521 supported_contexts = &snk_supported_contexts;
1522 available_contexts = &snk_available_contexts;
1523 break;
1524 }
1525 return -EINVAL;
1526 #endif /* CONFIG_BT_PAC_SNK */
1527 return -ENOTSUP;
1528 case BT_AUDIO_DIR_SOURCE:
1529 #if defined(CONFIG_BT_PAC_SRC)
1530 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
1531 supported_contexts = &src_supported_contexts;
1532 available_contexts = &src_available_contexts;
1533 break;
1534 }
1535 return -EINVAL;
1536 #endif /* CONFIG_BT_PAC_SRC */
1537 return -ENOTSUP;
1538 default:
1539 return -EINVAL;
1540 }
1541
1542 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE) || *supported_contexts == 0) {
1543 return set_supported_contexts(contexts, supported_contexts, available_contexts);
1544 }
1545
1546 return -EALREADY;
1547 }
1548
bt_pacs_get_available_contexts(enum bt_audio_dir dir)1549 enum bt_audio_context bt_pacs_get_available_contexts(enum bt_audio_dir dir)
1550 {
1551 switch (dir) {
1552 case BT_AUDIO_DIR_SINK:
1553 if (atomic_test_bit(pacs.flags, PACS_FLAG_SNK_PAC)) {
1554 return snk_available_contexts;
1555 }
1556 return -EINVAL;
1557 case BT_AUDIO_DIR_SOURCE:
1558 if (atomic_test_bit(pacs.flags, PACS_FLAG_SRC_PAC)) {
1559 return src_available_contexts;
1560 }
1561 return -EINVAL;
1562 }
1563
1564 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
1565 }
1566
bt_pacs_get_available_contexts_for_conn(struct bt_conn * conn,enum bt_audio_dir dir)1567 enum bt_audio_context bt_pacs_get_available_contexts_for_conn(struct bt_conn *conn,
1568 enum bt_audio_dir dir)
1569 {
1570 CHECKIF(conn == NULL) {
1571 LOG_ERR("NULL conn");
1572 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
1573 }
1574
1575 return pacs_get_available_contexts_for_conn(conn, dir);
1576 }
1577
1578 struct codec_cap_lookup_id_data {
1579 const struct bt_pac_codec *codec_id;
1580 const struct bt_audio_codec_cap *codec_cap;
1581 };
1582
codec_lookup_id(const struct bt_pacs_cap * cap,void * user_data)1583 static bool codec_lookup_id(const struct bt_pacs_cap *cap, void *user_data)
1584 {
1585 struct codec_cap_lookup_id_data *data = user_data;
1586
1587 if (cap->codec_cap->id == data->codec_id->id &&
1588 cap->codec_cap->cid == data->codec_id->cid &&
1589 cap->codec_cap->vid == data->codec_id->vid) {
1590 data->codec_cap = cap->codec_cap;
1591
1592 return false;
1593 }
1594
1595 return true;
1596 }
1597
bt_pacs_get_codec_cap(enum bt_audio_dir dir,const struct bt_pac_codec * codec_id)1598 const struct bt_audio_codec_cap *bt_pacs_get_codec_cap(enum bt_audio_dir dir,
1599 const struct bt_pac_codec *codec_id)
1600 {
1601 struct codec_cap_lookup_id_data lookup_data = {
1602 .codec_id = codec_id,
1603 .codec_cap = NULL,
1604 };
1605
1606 bt_pacs_cap_foreach(dir, codec_lookup_id, &lookup_data);
1607
1608 return lookup_data.codec_cap;
1609 }
1610