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 "../host/conn_internal.h"
25
26 #include <zephyr/logging/log.h>
27
28 LOG_MODULE_REGISTER(bt_pacs, CONFIG_BT_PACS_LOG_LEVEL);
29
30 #include "common/bt_str.h"
31
32 #include "audio_internal.h"
33 #include "pacs_internal.h"
34 #include "bap_unicast_server.h"
35
36 #define PAC_NOTIFY_TIMEOUT K_MSEC(10)
37 #define READ_BUF_SEM_TIMEOUT K_MSEC(50)
38
39 #define PACS(_name, _work_handler) \
40 struct pacs _name = { \
41 .work = Z_WORK_DELAYABLE_INITIALIZER(_work_handler), \
42 };
43
44 #define PACS_LOCATION(_name, _work_handler) \
45 struct pacs_location _name = { \
46 .work = Z_WORK_DELAYABLE_INITIALIZER(_work_handler), \
47 };
48
49 struct pacs_location {
50 struct k_work_delayable work;
51 uint32_t location;
52 };
53
54 struct pacs {
55 struct k_work_delayable work;
56 sys_slist_t list;
57 };
58
59 #if defined(CONFIG_BT_PAC_SNK)
60 static uint16_t snk_available_contexts;
61 static uint16_t snk_supported_contexts = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
62 #else
63 static uint16_t snk_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
64 static uint16_t snk_supported_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
65 #endif /* CONFIG_BT_PAC_SNK */
66
67 #if defined(CONFIG_BT_PAC_SRC)
68 static uint16_t src_available_contexts;
69 static uint16_t src_supported_contexts = BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
70 #else
71 static uint16_t src_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
72 static uint16_t src_supported_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
73 #endif /* CONFIG_BT_PAC_SRC */
74
75 static K_SEM_DEFINE(read_buf_sem, 1, 1);
76 NET_BUF_SIMPLE_DEFINE_STATIC(read_buf, BT_ATT_MAX_ATTRIBUTE_LEN);
77
pac_data_add(struct net_buf_simple * buf,size_t count,struct bt_codec_data * data)78 static ssize_t pac_data_add(struct net_buf_simple *buf, size_t count,
79 struct bt_codec_data *data)
80 {
81 size_t len = 0;
82
83 for (size_t i = 0; i < count; i++) {
84 struct bt_pac_ltv *ltv;
85 struct bt_data *d = &data[i].data;
86 const size_t ltv_len = sizeof(*ltv) + d->data_len;
87
88 if (net_buf_simple_tailroom(buf) < ltv_len) {
89 return -ENOMEM;
90 }
91
92 ltv = net_buf_simple_add(buf, sizeof(*ltv));
93 ltv->len = d->data_len + sizeof(ltv->type);
94 ltv->type = d->type;
95 net_buf_simple_add_mem(buf, d->data, d->data_len);
96
97 len += ltv_len;
98 }
99
100 return len;
101 }
102
103 struct pac_records_build_data {
104 struct bt_pacs_read_rsp *rsp;
105 struct net_buf_simple *buf;
106 };
107
build_pac_records(const struct bt_pacs_cap * cap,void * user_data)108 static bool build_pac_records(const struct bt_pacs_cap *cap, void *user_data)
109 {
110 struct pac_records_build_data *data = user_data;
111 struct bt_codec *codec = cap->codec;
112 struct net_buf_simple *buf = data->buf;
113 struct net_buf_simple_state state;
114 struct bt_pac_ltv_data *cc, *meta;
115 struct bt_pac_codec *pac_codec;
116 ssize_t len;
117
118 net_buf_simple_save(buf, &state);
119
120 if (net_buf_simple_tailroom(buf) < sizeof(*pac_codec)) {
121 goto fail;
122 }
123
124 pac_codec = net_buf_simple_add(buf, sizeof(*pac_codec));
125 pac_codec->id = codec->id;
126 pac_codec->cid = sys_cpu_to_le16(codec->cid);
127 pac_codec->vid = sys_cpu_to_le16(codec->vid);
128
129 if (net_buf_simple_tailroom(buf) < sizeof(*cc)) {
130 goto fail;
131 }
132
133 cc = net_buf_simple_add(buf, sizeof(*cc));
134
135 len = pac_data_add(buf, codec->data_count, codec->data);
136 if (len < 0 || len > UINT8_MAX) {
137 goto fail;
138 }
139
140 cc->len = len;
141
142 if (net_buf_simple_tailroom(buf) < sizeof(*meta)) {
143 goto fail;
144 }
145
146 meta = net_buf_simple_add(buf, sizeof(*meta));
147
148 len = pac_data_add(buf, codec->meta_count, codec->meta);
149 if (len < 0 || len > UINT8_MAX) {
150 goto fail;
151 }
152
153 meta->len = len;
154
155 data->rsp->num_pac++;
156
157 return true;
158
159 fail:
160 __ASSERT(false, "No space for %p", cap);
161
162 net_buf_simple_restore(buf, &state);
163
164 return false;
165 }
166
foreach_cap(sys_slist_t * list,bt_pacs_cap_foreach_func_t func,void * user_data)167 static void foreach_cap(sys_slist_t *list, bt_pacs_cap_foreach_func_t func,
168 void *user_data)
169 {
170 struct bt_pacs_cap *cap;
171
172 SYS_SLIST_FOR_EACH_CONTAINER(list, cap, _node) {
173 if (!func(cap, user_data)) {
174 break;
175 }
176 }
177 }
178
get_pac_records(sys_slist_t * list,struct net_buf_simple * buf)179 static void get_pac_records(sys_slist_t *list, struct net_buf_simple *buf)
180 {
181 struct pac_records_build_data data;
182
183 /* Reset if buffer before using */
184 net_buf_simple_reset(buf);
185
186 data.rsp = net_buf_simple_add(buf, sizeof(*data.rsp));
187 data.rsp->num_pac = 0;
188 data.buf = buf;
189
190 foreach_cap(list, build_pac_records, &data);
191 }
192
available_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)193 static void available_context_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
194 {
195 LOG_DBG("attr %p value 0x%04x", attr, value);
196 }
197
available_contexts_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)198 static ssize_t available_contexts_read(struct bt_conn *conn,
199 const struct bt_gatt_attr *attr, void *buf,
200 uint16_t len, uint16_t offset)
201 {
202 struct bt_pacs_context context = {
203 .snk = sys_cpu_to_le16(snk_available_contexts),
204 .src = sys_cpu_to_le16(src_available_contexts),
205 };
206
207 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
208
209 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
210 sizeof(context));
211 }
212
supported_context_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)213 static void supported_context_cfg_changed(const struct bt_gatt_attr *attr,
214 uint16_t value)
215 {
216 LOG_DBG("attr %p value 0x%04x", attr, value);
217 }
218
supported_context_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)219 static ssize_t supported_context_read(struct bt_conn *conn,
220 const struct bt_gatt_attr *attr,
221 void *buf, uint16_t len, uint16_t offset)
222 {
223 struct bt_pacs_context context = {
224 .snk = sys_cpu_to_le16(snk_supported_contexts),
225 .src = sys_cpu_to_le16(src_supported_contexts),
226 };
227
228 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
229
230 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
231 sizeof(context));
232 }
233
234 static void available_contexts_notify(struct k_work *work);
235 static void supported_contexts_notify(struct k_work *work);
236 static K_WORK_DELAYABLE_DEFINE(available_contexts_work, available_contexts_notify);
237 static K_WORK_DELAYABLE_DEFINE(supported_contexts_work, supported_contexts_notify);
238
set_available_contexts(uint16_t contexts,uint16_t * available,uint16_t supported)239 static int set_available_contexts(uint16_t contexts, uint16_t *available,
240 uint16_t supported)
241 {
242 int err;
243
244 if (contexts & ~supported) {
245 return -ENOTSUP;
246 }
247
248 if (contexts == *available) {
249 return 0;
250 }
251
252 err = k_work_reschedule(&available_contexts_work, PAC_NOTIFY_TIMEOUT);
253 if (err < 0) {
254 return err;
255 }
256
257 *available = contexts;
258
259 return 0;
260 }
261
set_supported_contexts(uint16_t contexts,uint16_t * supported,uint16_t * available)262 static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
263 uint16_t *available)
264 {
265 int err;
266
267 /* Ensure unspecified is always supported */
268 contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
269
270 if (*supported == contexts) {
271 return 0;
272 }
273
274 err = k_work_reschedule(&supported_contexts_work, PAC_NOTIFY_TIMEOUT);
275 if (err < 0) {
276 return err;
277 }
278
279 *supported = contexts;
280
281 /* Update available contexts if needed*/
282 if ((contexts & *available) != *available) {
283 *available = *available & contexts;
284 err = k_work_reschedule(&available_contexts_work,
285 PAC_NOTIFY_TIMEOUT);
286 if (err < 0) {
287 LOG_WRN("Update available contexts notify failed: %d", err);
288 }
289 }
290
291 return 0;
292 }
293
294 #if defined(CONFIG_BT_PAC_SNK)
295 static void pac_notify_snk(struct k_work *work);
296 static PACS(snk_pacs, pac_notify_snk);
297
snk_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)298 static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
299 void *buf, uint16_t len, uint16_t offset)
300 {
301 ssize_t ret_val;
302 int err;
303
304 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
305
306 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
307 if (err != 0) {
308 LOG_DBG("Failed to take read_buf_sem: %d", err);
309
310 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
311 }
312
313 get_pac_records(&snk_pacs.list, &read_buf);
314
315 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
316 read_buf.len);
317
318 k_sem_give(&read_buf_sem);
319
320 return ret_val;
321 }
322
snk_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)323 static void snk_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
324 {
325 LOG_DBG("attr %p value 0x%04x", attr, value);
326 }
327
set_snk_available_contexts(uint16_t contexts)328 static inline int set_snk_available_contexts(uint16_t contexts)
329 {
330 return set_available_contexts(contexts, &snk_available_contexts,
331 snk_supported_contexts);
332 }
333
set_snk_supported_contexts(uint16_t contexts)334 static inline int set_snk_supported_contexts(uint16_t contexts)
335 {
336 return set_supported_contexts(contexts, &snk_supported_contexts,
337 &snk_available_contexts);
338 }
339 #else
set_snk_available_contexts(uint16_t contexts)340 static inline int set_snk_available_contexts(uint16_t contexts)
341 {
342 return -ENOTSUP;
343 }
344
set_snk_supported_contexts(uint16_t contexts)345 static inline int set_snk_supported_contexts(uint16_t contexts)
346 {
347 return -ENOTSUP;
348 }
349 #endif /* CONFIG_BT_PAC_SNK */
350
351 #if defined(CONFIG_BT_PAC_SNK_LOC)
352 static void pac_notify_snk_loc(struct k_work *work);
353 static PACS_LOCATION(snk_location, pac_notify_snk_loc);
354
snk_loc_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)355 static ssize_t snk_loc_read(struct bt_conn *conn,
356 const struct bt_gatt_attr *attr, void *buf,
357 uint16_t len, uint16_t offset)
358 {
359 uint32_t location = sys_cpu_to_le32(snk_location.location);
360
361 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
362
363 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
364 sizeof(location));
365 }
366
snk_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)367 static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
368 {
369 LOG_DBG("attr %p value 0x%04x", attr, value);
370 }
371
set_snk_location(enum bt_audio_location audio_location)372 static int set_snk_location(enum bt_audio_location audio_location)
373 {
374 if (audio_location == snk_location.location) {
375 return 0;
376 }
377
378 snk_location.location = audio_location;
379
380 k_work_reschedule(&snk_location.work, PAC_NOTIFY_TIMEOUT);
381
382 return 0;
383 }
384 #else
set_snk_location(enum bt_audio_location location)385 static int set_snk_location(enum bt_audio_location location)
386 {
387 return -ENOTSUP;
388 }
389 #endif /* CONFIG_BT_PAC_SNK_LOC */
390
391 #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)392 static ssize_t snk_loc_write(struct bt_conn *conn,
393 const struct bt_gatt_attr *attr, const void *data,
394 uint16_t len, uint16_t offset, uint8_t flags)
395 {
396 int err;
397 enum bt_audio_location location;
398
399 if (offset) {
400 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
401 }
402
403 if (len != sizeof(location)) {
404 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
405 }
406
407 location = (enum bt_audio_location)sys_get_le32(data);
408 if (location > BT_AUDIO_LOCATION_MASK || location == 0) {
409 LOG_DBG("Invalid location value: 0x%08X", location);
410 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
411 }
412
413 err = set_snk_location(location);
414 if (err != 0) {
415 LOG_DBG("write_location returned %d", err);
416 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
417 }
418
419 return len;
420 }
421 #endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
422
423 #if defined(CONFIG_BT_PAC_SRC)
424 static void pac_notify_src(struct k_work *work);
425 static PACS(src_pacs, pac_notify_src);
426
src_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)427 static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
428 void *buf, uint16_t len, uint16_t offset)
429 {
430 ssize_t ret_val;
431 int err;
432
433 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
434
435 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
436 if (err != 0) {
437 LOG_DBG("Failed to take read_buf_sem: %d", err);
438
439 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
440 }
441
442 get_pac_records(&src_pacs.list, &read_buf);
443
444 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
445 read_buf.len);
446
447 k_sem_give(&read_buf_sem);
448
449 return ret_val;
450 }
451
src_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)452 static void src_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
453 {
454 LOG_DBG("attr %p value 0x%04x", attr, value);
455 }
456
set_src_available_contexts(uint16_t contexts)457 static inline int set_src_available_contexts(uint16_t contexts)
458 {
459 return set_available_contexts(contexts, &src_available_contexts,
460 src_supported_contexts);
461 }
462
set_src_supported_contexts(uint16_t contexts)463 static inline int set_src_supported_contexts(uint16_t contexts)
464 {
465 return set_supported_contexts(contexts, &src_supported_contexts,
466 &src_available_contexts);
467 }
468 #else
set_src_available_contexts(uint16_t contexts)469 static inline int set_src_available_contexts(uint16_t contexts)
470 {
471 return -ENOTSUP;
472 }
473
set_src_supported_contexts(uint16_t contexts)474 static inline int set_src_supported_contexts(uint16_t contexts)
475 {
476 return -ENOTSUP;
477 }
478 #endif /* CONFIG_BT_PAC_SRC */
479
480 #if defined(CONFIG_BT_PAC_SRC_LOC)
481 static void pac_notify_src_loc(struct k_work *work);
482 static PACS_LOCATION(src_location, pac_notify_src_loc);
483
src_loc_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)484 static ssize_t src_loc_read(struct bt_conn *conn,
485 const struct bt_gatt_attr *attr, void *buf,
486 uint16_t len, uint16_t offset)
487 {
488 uint32_t location = sys_cpu_to_le32(src_location.location);
489
490 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
491
492 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
493 sizeof(location));
494 }
495
src_loc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)496 static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
497 {
498 LOG_DBG("attr %p value 0x%04x", attr, value);
499 }
500
set_src_location(enum bt_audio_location audio_location)501 static int set_src_location(enum bt_audio_location audio_location)
502 {
503 if (audio_location == src_location.location) {
504 return 0;
505 }
506
507 src_location.location = audio_location;
508
509 k_work_reschedule(&src_location.work, PAC_NOTIFY_TIMEOUT);
510
511 return 0;
512 }
513 #else
set_src_location(enum bt_audio_location location)514 static int set_src_location(enum bt_audio_location location)
515 {
516 return -ENOTSUP;
517 }
518 #endif /* CONFIG_BT_PAC_SRC_LOC */
519
520 #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)521 static ssize_t src_loc_write(struct bt_conn *conn,
522 const struct bt_gatt_attr *attr, const void *data,
523 uint16_t len, uint16_t offset, uint8_t flags)
524 {
525 int err;
526 uint32_t location;
527
528 if (offset) {
529 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
530 }
531
532 if (len != sizeof(location)) {
533 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
534 }
535
536 location = (enum bt_audio_location)sys_get_le32(data);
537 if (location > BT_AUDIO_LOCATION_MASK || location == 0) {
538 LOG_DBG("Invalid location value: 0x%08X", location);
539 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
540 }
541
542 err = set_src_location(location);
543 if (err != 0) {
544 LOG_DBG("write_location returned %d", err);
545 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
546 }
547
548 return len;
549 }
550 #endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
551
552 BT_GATT_SERVICE_DEFINE(pacs_svc,
553 BT_GATT_PRIMARY_SERVICE(BT_UUID_PACS),
554 #if defined(CONFIG_BT_PAC_SNK)
555 BT_AUDIO_CHRC(BT_UUID_PACS_SNK,
556 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
557 BT_GATT_PERM_READ_ENCRYPT,
558 snk_read, NULL, NULL),
559 BT_AUDIO_CCC(snk_cfg_changed),
560 #if defined(CONFIG_BT_PAC_SNK_LOC)
561 #if defined(CONFIG_BT_PAC_SNK_LOC_WRITEABLE)
562 BT_AUDIO_CHRC(BT_UUID_PACS_SNK_LOC,
563 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_NOTIFY,
564 BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT,
565 snk_loc_read, snk_loc_write, NULL),
566 #else
567 BT_AUDIO_CHRC(BT_UUID_PACS_SNK_LOC,
568 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
569 BT_GATT_PERM_READ_ENCRYPT,
570 snk_loc_read, NULL, NULL),
571 #endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
572 BT_AUDIO_CCC(snk_loc_cfg_changed),
573 #endif /* CONFIG_BT_PAC_SNK_LOC */
574 #endif /* CONFIG_BT_PAC_SNK */
575 #if defined(CONFIG_BT_PAC_SRC)
576 BT_AUDIO_CHRC(BT_UUID_PACS_SRC,
577 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
578 BT_GATT_PERM_READ_ENCRYPT,
579 src_read, NULL, NULL),
580 BT_AUDIO_CCC(src_cfg_changed),
581 #if defined(CONFIG_BT_PAC_SRC_LOC)
582 #if defined(CONFIG_BT_PAC_SRC_LOC_WRITEABLE)
583 BT_AUDIO_CHRC(BT_UUID_PACS_SRC_LOC,
584 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_NOTIFY,
585 BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT,
586 src_loc_read, src_loc_write, NULL),
587 #else
588 BT_AUDIO_CHRC(BT_UUID_PACS_SRC_LOC,
589 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
590 BT_GATT_PERM_READ_ENCRYPT,
591 src_loc_read, NULL, NULL),
592 #endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
593 BT_AUDIO_CCC(src_loc_cfg_changed),
594 #endif /* CONFIG_BT_PAC_SRC_LOC */
595 #endif /* CONFIG_BT_PAC_SRC */
596 BT_AUDIO_CHRC(BT_UUID_PACS_AVAILABLE_CONTEXT,
597 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
598 BT_GATT_PERM_READ_ENCRYPT,
599 available_contexts_read, NULL, NULL),
600 BT_AUDIO_CCC(available_context_cfg_changed),
601 BT_AUDIO_CHRC(BT_UUID_PACS_SUPPORTED_CONTEXT,
602 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
603 BT_GATT_PERM_READ_ENCRYPT,
604 supported_context_read, NULL, NULL),
605 BT_AUDIO_CCC(supported_context_cfg_changed)
606 );
607
608 #if defined(CONFIG_BT_PAC_SNK_LOC)
pac_notify_snk_loc(struct k_work * work)609 static void pac_notify_snk_loc(struct k_work *work)
610 {
611 struct pacs_location *location = CONTAINER_OF(work, struct pacs_location, work);
612 uint32_t location_le = sys_cpu_to_le32(location->location);
613 int err;
614
615 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK_LOC, pacs_svc.attrs, &location_le,
616 sizeof(location_le));
617 if (err != 0 && err != -ENOTCONN) {
618 LOG_WRN("PACS notify_loc failed: %d", err);
619 }
620 }
621 #endif /* CONFIG_BT_PAC_SNK_LOC */
622
623 #if defined(CONFIG_BT_PAC_SRC_LOC)
pac_notify_src_loc(struct k_work * work)624 static void pac_notify_src_loc(struct k_work *work)
625 {
626 struct pacs_location *location = CONTAINER_OF(work, struct pacs_location, work);
627 uint32_t location_le = sys_cpu_to_le32(location->location);
628 int err;
629
630 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC_LOC, pacs_svc.attrs, &location_le,
631 sizeof(location_le));
632 if (err != 0 && err != -ENOTCONN) {
633 LOG_WRN("PACS notify_loc failed: %d", err);
634 }
635 }
636 #endif /* CONFIG_BT_PAC_SRC_LOC */
637
638 #if defined(CONFIG_BT_PAC_SNK)
pac_notify_snk(struct k_work * work)639 static void pac_notify_snk(struct k_work *work)
640 {
641 struct pacs *pac = CONTAINER_OF(work, struct pacs, work);
642 int err;
643
644 err = k_sem_take(&read_buf_sem, K_NO_WAIT);
645 if (err != 0) {
646 LOG_DBG("Failed to take read_buf_sem: %d", err);
647
648 /* Try again later */
649 k_work_reschedule(k_work_delayable_from_work(work),
650 PAC_NOTIFY_TIMEOUT);
651
652 return;
653 }
654
655 get_pac_records(&pac->list, &read_buf);
656
657 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK, pacs_svc.attrs,
658 read_buf.data, read_buf.len);
659 if (err != 0 && err != -ENOTCONN) {
660 LOG_WRN("PACS notify failed: %d", err);
661 }
662
663 k_sem_give(&read_buf_sem);
664 }
665 #endif /* CONFIG_BT_PAC_SNK */
666
667 #if defined(CONFIG_BT_PAC_SRC)
pac_notify_src(struct k_work * work)668 static void pac_notify_src(struct k_work *work)
669 {
670 struct pacs *pac = CONTAINER_OF(work, struct pacs, work);
671 int err = 0;
672
673 err = k_sem_take(&read_buf_sem, K_NO_WAIT);
674 if (err != 0) {
675 LOG_DBG("Failed to take read_buf_sem: %d", err);
676
677 /* Try again later */
678 k_work_reschedule(k_work_delayable_from_work(work),
679 PAC_NOTIFY_TIMEOUT);
680
681 return;
682 }
683
684 get_pac_records(&pac->list, &read_buf);
685
686 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC, pacs_svc.attrs,
687 read_buf.data, read_buf.len);
688 if (err != 0 && err != -ENOTCONN) {
689 LOG_WRN("PACS notify failed: %d", err);
690 }
691
692 k_sem_give(&read_buf_sem);
693 }
694 #endif /* CONFIG_BT_PAC_SRC */
695
pacs_changed(struct pacs * caps)696 static void pacs_changed(struct pacs *caps)
697 {
698 k_work_reschedule(&caps->work, PAC_NOTIFY_TIMEOUT);
699 }
700
available_contexts_notify(struct k_work * work)701 static void available_contexts_notify(struct k_work *work)
702 {
703 struct bt_pacs_context context = {
704 .snk = sys_cpu_to_le16(snk_available_contexts),
705 .src = sys_cpu_to_le16(src_available_contexts),
706 };
707 int err;
708
709 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
710 &context, sizeof(context));
711 if (err != 0 && err != -ENOTCONN) {
712 LOG_WRN("Available Audio Contexts notify failed: %d", err);
713 }
714 }
715
supported_contexts_notify(struct k_work * work)716 static void supported_contexts_notify(struct k_work *work)
717 {
718 struct bt_pacs_context context = {
719 .snk = sys_cpu_to_le16(snk_supported_contexts),
720 .src = sys_cpu_to_le16(src_supported_contexts),
721 };
722 int err;
723
724 err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
725 &context, sizeof(context));
726 if (err != 0 && err != -ENOTCONN) {
727 LOG_WRN("Supported Audio Contexts notify failed: %d", err);
728 }
729 }
730
bt_pacs_context_available(enum bt_audio_dir dir,uint16_t context)731 bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
732 {
733 if (dir == BT_AUDIO_DIR_SOURCE) {
734 return (context & src_available_contexts) == context;
735 }
736
737 if (dir == BT_AUDIO_DIR_SINK) {
738 return (context & snk_available_contexts) == context;
739 }
740
741 return false;
742 }
743
pacs_get(enum bt_audio_dir dir)744 static struct pacs *pacs_get(enum bt_audio_dir dir)
745 {
746 switch (dir) {
747 #if defined(CONFIG_BT_PAC_SNK)
748 case BT_AUDIO_DIR_SINK:
749 return &snk_pacs;
750 #endif /* CONFIG_BT_PAC_SNK */
751 #if defined(CONFIG_BT_PAC_SRC)
752 case BT_AUDIO_DIR_SOURCE:
753 return &src_pacs;
754 #endif /* CONFIG_BT_PAC_SRC */
755 default:
756 return NULL;
757 }
758 }
759
bt_pacs_cap_foreach(enum bt_audio_dir dir,bt_pacs_cap_foreach_func_t func,void * user_data)760 void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
761 {
762 struct pacs *pac;
763
764 CHECKIF(func == NULL) {
765 LOG_ERR("func is NULL");
766 return;
767 }
768
769 pac = pacs_get(dir);
770 if (!pac) {
771 return;
772 }
773
774 foreach_cap(&pac->list, func, user_data);
775 }
776
777 /* Register Audio Capability */
bt_pacs_cap_register(enum bt_audio_dir dir,struct bt_pacs_cap * cap)778 int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
779 {
780 struct pacs *pac;
781
782 if (!cap || !cap->codec) {
783 return -EINVAL;
784 }
785
786 pac = pacs_get(dir);
787 if (!pac) {
788 return -EINVAL;
789 }
790
791 LOG_DBG("cap %p dir %s codec 0x%02x codec cid 0x%04x "
792 "codec vid 0x%04x", cap, bt_audio_dir_str(dir), cap->codec->id,
793 cap->codec->cid, cap->codec->vid);
794
795 sys_slist_append(&pac->list, &cap->_node);
796
797 pacs_changed(pac);
798
799 return 0;
800 }
801
802 /* Unregister Audio Capability */
bt_pacs_cap_unregister(enum bt_audio_dir dir,struct bt_pacs_cap * cap)803 int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
804 {
805 struct pacs *pac;
806
807 if (!cap) {
808 return -EINVAL;
809 }
810
811 pac = pacs_get(dir);
812 if (!pac) {
813 return -EINVAL;
814 }
815
816 LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir));
817
818 if (!sys_slist_find_and_remove(&pac->list, &cap->_node)) {
819 return -ENOENT;
820 }
821
822 pacs_changed(pac);
823
824 return 0;
825 }
826
bt_pacs_set_location(enum bt_audio_dir dir,enum bt_audio_location location)827 int bt_pacs_set_location(enum bt_audio_dir dir, enum bt_audio_location location)
828 {
829 switch (dir) {
830 case BT_AUDIO_DIR_SINK:
831 return set_snk_location(location);
832 case BT_AUDIO_DIR_SOURCE:
833 return set_src_location(location);
834 }
835
836 return -EINVAL;
837 }
838
bt_pacs_set_available_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)839 int bt_pacs_set_available_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
840 {
841 switch (dir) {
842 case BT_AUDIO_DIR_SINK:
843 return set_snk_available_contexts(contexts);
844 case BT_AUDIO_DIR_SOURCE:
845 return set_src_available_contexts(contexts);
846 }
847
848 return -EINVAL;
849 }
850
bt_pacs_set_supported_contexts(enum bt_audio_dir dir,enum bt_audio_context contexts)851 int bt_pacs_set_supported_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
852 {
853 switch (dir) {
854 case BT_AUDIO_DIR_SINK:
855 return set_snk_supported_contexts(contexts);
856 case BT_AUDIO_DIR_SOURCE:
857 return set_src_supported_contexts(contexts);
858 }
859
860 return -EINVAL;
861 }
862
bt_pacs_get_available_contexts(enum bt_audio_dir dir)863 enum bt_audio_context bt_pacs_get_available_contexts(enum bt_audio_dir dir)
864 {
865 switch (dir) {
866 case BT_AUDIO_DIR_SINK:
867 return snk_available_contexts;
868 case BT_AUDIO_DIR_SOURCE:
869 return src_available_contexts;
870 }
871
872 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
873 }
874