blob: bbad4ee89dae98c063e745b205eb8dbad6b81f49 [file] [log] [blame]
Emil Gydesen03862b32022-01-07 17:23:45 +01001/* @file
2 * @brief Bluetooth PACS
3 */
4
5/*
6 * Copyright (c) 2020 Intel Corporation
Emil Gydesen7fc05192023-02-20 14:33:43 +01007 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
Emil Gydesen03862b32022-01-07 17:23:45 +01008 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
Emil Gydesenebadb112024-05-20 19:40:04 +020012#include <errno.h>
13#include <stdbool.h>
14#include <stddef.h>
15#include <stdint.h>
16#include <string.h>
17#include <sys/types.h>
Emil Gydesen03862b32022-01-07 17:23:45 +010018
Emil Gydesenebadb112024-05-20 19:40:04 +020019#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>
Gerard Marull-Paretas5113c142022-05-06 11:12:04 +020024#include <zephyr/bluetooth/bluetooth.h>
25#include <zephyr/bluetooth/conn.h>
26#include <zephyr/bluetooth/gatt.h>
Emil Gydesenebadb112024-05-20 19:40:04 +020027#include <zephyr/bluetooth/uuid.h>
28#include <zephyr/device.h>
29#include <zephyr/init.h>
30#include <zephyr/kernel.h>
31#include <zephyr/logging/log.h>
32#include <zephyr/net/buf.h>
33#include <zephyr/sys/__assert.h>
34#include <zephyr/sys/atomic.h>
35#include <zephyr/sys/byteorder.h>
36#include <zephyr/sys/check.h>
Szymon Czapracki42a402a2023-03-15 22:36:16 +010037#include <zephyr/sys/slist.h>
Emil Gydesenebadb112024-05-20 19:40:04 +020038#include <zephyr/sys/util.h>
39#include <zephyr/sys/util_macro.h>
40
Emil Gydesen03862b32022-01-07 17:23:45 +010041#include "../host/conn_internal.h"
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020042#include "../host/hci_core.h"
Théo Battrelc9d68a52022-10-25 08:48:54 +020043#include "common/bt_str.h"
Emil Gydesen03862b32022-01-07 17:23:45 +010044
Mariusz Skamrae0d36be2022-07-25 09:56:23 +020045#include "audio_internal.h"
Emil Gydesen7fc05192023-02-20 14:33:43 +010046#include "bap_unicast_server.h"
Emil Gydesenebadb112024-05-20 19:40:04 +020047#include "pacs_internal.h"
48
49LOG_MODULE_REGISTER(bt_pacs, CONFIG_BT_PACS_LOG_LEVEL);
Emil Gydesen03862b32022-01-07 17:23:45 +010050
Emil Gydesen4c2a09e2022-01-31 11:28:59 +010051#define PAC_NOTIFY_TIMEOUT K_MSEC(10)
Emil Gydesen98adaf02023-01-30 15:29:14 +010052#define READ_BUF_SEM_TIMEOUT K_MSEC(50)
Emil Gydesen03862b32022-01-07 17:23:45 +010053
Szymon Czapracki42a402a2023-03-15 22:36:16 +010054#if defined(CONFIG_BT_PAC_SRC)
55static uint32_t pacs_src_location;
56static sys_slist_t src_pacs_list = SYS_SLIST_STATIC_INIT(&src_pacs_list);
Mariusz Skamraef362e72023-12-01 13:28:09 +010057static uint16_t src_supported_contexts;
Szymon Czapracki42a402a2023-03-15 22:36:16 +010058#endif /* CONFIG_BT_PAC_SRC */
Mariusz Skamrae635d3a2022-10-20 10:11:27 +020059
Szymon Czapracki42a402a2023-03-15 22:36:16 +010060#if defined(CONFIG_BT_PAC_SNK)
61static uint32_t pacs_snk_location;
62static sys_slist_t snk_pacs_list = SYS_SLIST_STATIC_INIT(&snk_pacs_list);
Mariusz Skamraef362e72023-12-01 13:28:09 +010063static uint16_t snk_supported_contexts;
Szymon Czapracki42a402a2023-03-15 22:36:16 +010064#endif /* CONFIG_BT_PAC_SNK */
Emil Gydesen03862b32022-01-07 17:23:45 +010065
Szymon Czapracki35bd4272022-11-24 09:33:09 +010066static uint16_t src_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
Mariusz Skamraef362e72023-12-01 13:28:09 +010067static uint16_t snk_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +020068
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020069enum {
70 FLAG_ACTIVE,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020071 FLAG_SINK_PAC_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020072 FLAG_SINK_AUDIO_LOCATIONS_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020073 FLAG_SOURCE_PAC_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020074 FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020075 FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020076 FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020077 FLAG_NUM,
78};
79
80static struct pacs_client {
81 bt_addr_le_t addr;
82
Mariusz Skamra78a4b332023-12-01 14:50:35 +010083#if defined(CONFIG_BT_PAC_SNK)
84 /* Sink Available Contexts override value */
85 uint16_t *snk_available_contexts;
86#endif /* CONFIG_BT_PAC_SNK */
87
88#if defined(CONFIG_BT_PAC_SRC)
89 /* Source Available Contexts override value */
90 uint16_t *src_available_contexts;
91#endif /* CONFIG_BT_PAC_SRC */
92
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +020093 /* Pending notification flags */
94 ATOMIC_DEFINE(flags, FLAG_NUM);
95} clients[CONFIG_BT_MAX_PAIRED];
96
97static atomic_t notify_rdy;
98
Emil Gydesen98adaf02023-01-30 15:29:14 +010099static K_SEM_DEFINE(read_buf_sem, 1, 1);
Emil Gydesen3ff3d8d2023-01-30 16:49:57 +0100100NET_BUF_SIMPLE_DEFINE_STATIC(read_buf, BT_ATT_MAX_ATTRIBUTE_LEN);
Emil Gydesenbb6c0862022-04-12 16:11:18 +0200101
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200102static int pacs_gatt_notify(struct bt_conn *conn,
103 const struct bt_uuid *uuid,
104 const struct bt_gatt_attr *attr,
105 const void *data,
106 uint16_t len);
107static void deferred_nfy_work_handler(struct k_work *work);
108
109static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
110
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200111struct pac_records_build_data {
112 struct bt_pacs_read_rsp *rsp;
113 struct net_buf_simple *buf;
114};
115
Mariusz Skamra014c22c2023-12-01 14:44:26 +0100116static struct pacs_client *client_lookup_conn(const struct bt_conn *conn)
117{
118 __ASSERT_NO_MSG(conn != NULL);
119
120 for (size_t i = 0; i < ARRAY_SIZE(clients); i++) {
121 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
122 bt_addr_le_eq(&clients[i].addr, bt_conn_get_dst(conn))) {
123 return &clients[i];
124 }
125 }
126
127 return NULL;
128}
129
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200130static void pacs_set_notify_bit(int bit)
131{
132 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
133 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
134 atomic_set_bit(clients[i].flags, bit);
135 }
136 }
137}
138
Mariusz Skamra26270632022-10-20 11:25:38 +0200139static bool build_pac_records(const struct bt_pacs_cap *cap, void *user_data)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200140{
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200141 struct pac_records_build_data *data = user_data;
Emil Gydesen53502cb2023-05-19 16:55:11 +0200142 const struct bt_audio_codec_cap *codec_cap = cap->codec_cap;
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200143 struct net_buf_simple *buf = data->buf;
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200144 struct net_buf_simple_state state;
Mariusz Skamrafbeee202022-10-28 15:56:32 +0200145 struct bt_pac_codec *pac_codec;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200146
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200147 net_buf_simple_save(buf, &state);
148
Mariusz Skamrafbeee202022-10-28 15:56:32 +0200149 if (net_buf_simple_tailroom(buf) < sizeof(*pac_codec)) {
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200150 goto fail;
151 }
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200152
Mariusz Skamrafbeee202022-10-28 15:56:32 +0200153 pac_codec = net_buf_simple_add(buf, sizeof(*pac_codec));
Emil Gydesen69f7fd92023-05-16 14:16:14 +0200154 pac_codec->id = codec_cap->id;
155 pac_codec->cid = sys_cpu_to_le16(codec_cap->cid);
156 pac_codec->vid = sys_cpu_to_le16(codec_cap->vid);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200157
Emil Gydesenaa990ae2023-05-18 17:37:19 +0200158 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->data_len)) {
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200159 goto fail;
160 }
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200161
Emil Gydesenaa990ae2023-05-18 17:37:19 +0200162 net_buf_simple_add_u8(buf, codec_cap->data_len);
163 net_buf_simple_add_mem(buf, codec_cap->data, codec_cap->data_len);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200164
Emil Gydesenaa990ae2023-05-18 17:37:19 +0200165 if (net_buf_simple_tailroom(buf) < (sizeof(struct bt_pac_ltv_data) + codec_cap->meta_len)) {
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200166 goto fail;
167 }
168
Emil Gydesenaa990ae2023-05-18 17:37:19 +0200169 net_buf_simple_add_u8(buf, codec_cap->meta_len);
170 net_buf_simple_add_mem(buf, codec_cap->meta, codec_cap->meta_len);
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200171
172 data->rsp->num_pac++;
173
174 return true;
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200175
176fail:
Emil Gydesen3d4622b2023-01-30 16:34:22 +0100177 __ASSERT(false, "No space for %p", cap);
Mariusz Skamra2a1cb0a2022-10-20 09:47:34 +0200178
179 net_buf_simple_restore(buf, &state);
180
181 return false;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200182}
183
Mariusz Skamra26270632022-10-20 11:25:38 +0200184static void foreach_cap(sys_slist_t *list, bt_pacs_cap_foreach_func_t func,
185 void *user_data)
Mariusz Skamrae635d3a2022-10-20 10:11:27 +0200186{
Mariusz Skamra26270632022-10-20 11:25:38 +0200187 struct bt_pacs_cap *cap;
Mariusz Skamrae635d3a2022-10-20 10:11:27 +0200188
189 SYS_SLIST_FOR_EACH_CONTAINER(list, cap, _node) {
190 if (!func(cap, user_data)) {
191 break;
192 }
193 }
194}
195
Emil Gydesen5a28d802023-01-30 15:30:52 +0100196static void get_pac_records(sys_slist_t *list, struct net_buf_simple *buf)
Emil Gydesen03862b32022-01-07 17:23:45 +0100197{
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200198 struct pac_records_build_data data;
Emil Gydesen03862b32022-01-07 17:23:45 +0100199
200 /* Reset if buffer before using */
Emil Gydesen9477e612022-01-31 14:28:59 +0100201 net_buf_simple_reset(buf);
Emil Gydesen03862b32022-01-07 17:23:45 +0100202
Mariusz Skamra43ffe3c2022-10-18 22:37:32 +0200203 data.rsp = net_buf_simple_add(buf, sizeof(*data.rsp));
204 data.rsp->num_pac = 0;
205 data.buf = buf;
Emil Gydesen03862b32022-01-07 17:23:45 +0100206
Mariusz Skamra26270632022-10-20 11:25:38 +0200207 foreach_cap(list, build_pac_records, &data);
Emil Gydesen9477e612022-01-31 14:28:59 +0100208}
209
Mariusz Skamra33991f22022-05-19 16:52:44 +0200210static void available_context_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
Emil Gydesen03862b32022-01-07 17:23:45 +0100211{
Théo Battrele458f5a2022-11-02 14:31:13 +0100212 LOG_DBG("attr %p value 0x%04x", attr, value);
Emil Gydesen03862b32022-01-07 17:23:45 +0100213}
214
Emil Gydesen476e7b32024-01-08 14:38:01 +0100215static enum bt_audio_context pacs_get_available_contexts_for_conn(struct bt_conn *conn,
216 enum bt_audio_dir dir)
217{
218 const struct pacs_client *client;
219
220 client = client_lookup_conn(conn);
221 if (client == NULL) {
222 LOG_DBG("No client context for conn %p", (void *)conn);
223 return bt_pacs_get_available_contexts(dir);
224 }
225
226 switch (dir) {
227 case BT_AUDIO_DIR_SINK:
228#if defined(CONFIG_BT_PAC_SNK)
229 if (client->snk_available_contexts != NULL) {
230 return POINTER_TO_UINT(client->snk_available_contexts);
231 }
232#endif /* CONFIG_BT_PAC_SNK */
233 break;
234 case BT_AUDIO_DIR_SOURCE:
235#if defined(CONFIG_BT_PAC_SRC)
236 if (client->src_available_contexts != NULL) {
237 return POINTER_TO_UINT(client->src_available_contexts);
238 }
239#endif /* CONFIG_BT_PAC_SRC */
240 break;
241 }
242
243 return bt_pacs_get_available_contexts(dir);
244}
245
Mariusz Skamra33991f22022-05-19 16:52:44 +0200246static ssize_t available_contexts_read(struct bt_conn *conn,
247 const struct bt_gatt_attr *attr, void *buf,
248 uint16_t len, uint16_t offset)
249{
Mariusz Skamra49f91192022-10-18 22:58:34 +0200250 struct bt_pacs_context context = {
Mariusz Skamra78a4b332023-12-01 14:50:35 +0100251 .snk = sys_cpu_to_le16(
Emil Gydesen476e7b32024-01-08 14:38:01 +0100252 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK)),
Mariusz Skamra78a4b332023-12-01 14:50:35 +0100253 .src = sys_cpu_to_le16(
Emil Gydesen476e7b32024-01-08 14:38:01 +0100254 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE)),
Mariusz Skamra49f91192022-10-18 22:58:34 +0200255 };
Emil Gydesen03862b32022-01-07 17:23:45 +0100256
Théo Battrele458f5a2022-11-02 14:31:13 +0100257 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100258
259 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
260 sizeof(context));
261}
262
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200263#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
Emil Gydesen03862b32022-01-07 17:23:45 +0100264static void supported_context_cfg_changed(const struct bt_gatt_attr *attr,
265 uint16_t value)
266{
Théo Battrele458f5a2022-11-02 14:31:13 +0100267 LOG_DBG("attr %p value 0x%04x", attr, value);
Emil Gydesen03862b32022-01-07 17:23:45 +0100268}
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200269#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
Emil Gydesen03862b32022-01-07 17:23:45 +0100270
Mariusz Skamraef362e72023-12-01 13:28:09 +0100271static uint16_t supported_context_get(enum bt_audio_dir dir)
272{
273 switch (dir) {
274#if defined(CONFIG_BT_PAC_SNK)
275 case BT_AUDIO_DIR_SINK:
276 return snk_supported_contexts | BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
277#endif /* CONFIG_BT_PAC_SNK */
278#if defined(CONFIG_BT_PAC_SRC)
279 case BT_AUDIO_DIR_SOURCE:
280 return src_supported_contexts | BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
281#endif /* CONFIG_BT_PAC_SRC */
282 default:
283 break;
284 }
285
286 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
287}
288
Emil Gydesen03862b32022-01-07 17:23:45 +0100289static ssize_t supported_context_read(struct bt_conn *conn,
290 const struct bt_gatt_attr *attr,
291 void *buf, uint16_t len, uint16_t offset)
292{
Mariusz Skamra49f91192022-10-18 22:58:34 +0200293 struct bt_pacs_context context = {
Mariusz Skamraef362e72023-12-01 13:28:09 +0100294 .snk = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SINK)),
295 .src = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SOURCE)),
Mariusz Skamra49f91192022-10-18 22:58:34 +0200296 };
Emil Gydesen03862b32022-01-07 17:23:45 +0100297
Théo Battrele458f5a2022-11-02 14:31:13 +0100298 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100299
300 return bt_gatt_attr_read(conn, attr, buf, len, offset, &context,
301 sizeof(context));
302}
Emil Gydesen18466532022-02-08 14:43:43 +0100303
Mariusz Skamra49f91192022-10-18 22:58:34 +0200304static int set_available_contexts(uint16_t contexts, uint16_t *available,
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100305 uint16_t supported)
Mariusz Skamra49f91192022-10-18 22:58:34 +0200306{
Mariusz Skamra49f91192022-10-18 22:58:34 +0200307 if (contexts & ~supported) {
308 return -ENOTSUP;
309 }
310
311 if (contexts == *available) {
312 return 0;
313 }
314
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100315 *available = contexts;
316
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200317 pacs_set_notify_bit(FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
318 k_work_submit(&deferred_nfy_work);
319
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100320 return 0;
321}
322
323static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
324 uint16_t *available)
325{
326 int err;
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100327 uint16_t tmp_supported = *supported;
328 uint16_t tmp_available = *available;
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100329
330 /* Ensure unspecified is always supported */
331 contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
332
333 if (*supported == contexts) {
334 return 0;
335 }
336
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100337 *supported = contexts;
338
339 /* Update available contexts if needed*/
340 if ((contexts & *available) != *available) {
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100341 err = set_available_contexts(contexts & *available, available, contexts);
342 if (err) {
343 *available = tmp_available;
344 *supported = tmp_supported;
345
346 return err;
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100347 }
348 }
349
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200350 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)) {
351 pacs_set_notify_bit(FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
352 k_work_submit(&deferred_nfy_work);
353 }
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100354
Mariusz Skamra49f91192022-10-18 22:58:34 +0200355 return 0;
356}
357
Emil Gydesen03862b32022-01-07 17:23:45 +0100358#if defined(CONFIG_BT_PAC_SNK)
Emil Gydesen03862b32022-01-07 17:23:45 +0100359static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
360 void *buf, uint16_t len, uint16_t offset)
361{
Emil Gydesen98adaf02023-01-30 15:29:14 +0100362 ssize_t ret_val;
363 int err;
364
Théo Battrele458f5a2022-11-02 14:31:13 +0100365 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100366
Emil Gydesen98adaf02023-01-30 15:29:14 +0100367 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
368 if (err != 0) {
369 LOG_DBG("Failed to take read_buf_sem: %d", err);
370
371 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
372 }
373
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100374 get_pac_records(&snk_pacs_list, &read_buf);
Mariusz Skamra1432d052022-09-22 14:07:57 +0200375
Emil Gydesen98adaf02023-01-30 15:29:14 +0100376 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
377 read_buf.len);
378
379 k_sem_give(&read_buf_sem);
380
381 return ret_val;
Emil Gydesen03862b32022-01-07 17:23:45 +0100382}
383
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200384#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
Emil Gydesen86fd8992023-08-28 17:00:09 +0200385static const struct bt_uuid *pacs_snk_uuid = BT_UUID_PACS_SNK;
386
Emil Gydesena7969272022-04-13 21:10:18 +0200387static void snk_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
388{
Théo Battrele458f5a2022-11-02 14:31:13 +0100389 LOG_DBG("attr %p value 0x%04x", attr, value);
Emil Gydesena7969272022-04-13 21:10:18 +0200390}
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200391#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200392#endif /* CONFIG_BT_PAC_SNK */
Mariusz Skamra49f91192022-10-18 22:58:34 +0200393
Emil Gydesena7969272022-04-13 21:10:18 +0200394#if defined(CONFIG_BT_PAC_SNK_LOC)
Emil Gydesen03862b32022-01-07 17:23:45 +0100395static ssize_t snk_loc_read(struct bt_conn *conn,
396 const struct bt_gatt_attr *attr, void *buf,
397 uint16_t len, uint16_t offset)
398{
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100399 uint32_t location = sys_cpu_to_le32(pacs_snk_location);
Emil Gydesen18466532022-02-08 14:43:43 +0100400
Théo Battrele458f5a2022-11-02 14:31:13 +0100401 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100402
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200403 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
404 sizeof(location));
Emil Gydesen03862b32022-01-07 17:23:45 +0100405}
406
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200407#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
Emil Gydesen86fd8992023-08-28 17:00:09 +0200408static const struct bt_uuid *pacs_snk_loc_uuid = BT_UUID_PACS_SNK_LOC;
409
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200410static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200411{
Théo Battrele458f5a2022-11-02 14:31:13 +0100412 LOG_DBG("attr %p value 0x%04x", attr, value);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200413}
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200414#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200415
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200416static void set_snk_location(enum bt_audio_location audio_location)
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200417{
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100418 if (audio_location == pacs_snk_location) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200419 return;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200420 }
421
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100422 pacs_snk_location = audio_location;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200423
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200424 if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)) {
425 pacs_set_notify_bit(FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
426 k_work_submit(&deferred_nfy_work);
427 }
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200428}
429#else
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200430static void set_snk_location(enum bt_audio_location location)
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200431{
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200432 return;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200433}
434#endif /* CONFIG_BT_PAC_SNK_LOC */
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +0200435
Emil Gydesena7969272022-04-13 21:10:18 +0200436#if defined(CONFIG_BT_PAC_SNK_LOC_WRITEABLE)
Emil Gydesen03862b32022-01-07 17:23:45 +0100437static ssize_t snk_loc_write(struct bt_conn *conn,
438 const struct bt_gatt_attr *attr, const void *data,
439 uint16_t len, uint16_t offset, uint8_t flags)
440{
Emil Gydesen18466532022-02-08 14:43:43 +0100441 enum bt_audio_location location;
442
Emil Gydesen03862b32022-01-07 17:23:45 +0100443 if (offset) {
444 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
445 }
446
Emil Gydesen18466532022-02-08 14:43:43 +0100447 if (len != sizeof(location)) {
Szymon Czapracki85639f72022-08-23 14:55:23 +0200448 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
Emil Gydesen03862b32022-01-07 17:23:45 +0100449 }
450
Emil Gydesen18466532022-02-08 14:43:43 +0100451 location = (enum bt_audio_location)sys_get_le32(data);
Bi Jian06fa2872023-09-29 11:11:34 +0800452 if (location > BT_AUDIO_LOCATION_MASK) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100453 LOG_DBG("Invalid location value: 0x%08X", location);
Szymon Czapracki85639f72022-08-23 14:55:23 +0200454 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
Emil Gydesen18466532022-02-08 14:43:43 +0100455 }
456
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200457 set_snk_location(location);
Emil Gydesen03862b32022-01-07 17:23:45 +0100458
459 return len;
460}
Emil Gydesena7969272022-04-13 21:10:18 +0200461#endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
Emil Gydesen03862b32022-01-07 17:23:45 +0100462
Emil Gydesen03862b32022-01-07 17:23:45 +0100463#if defined(CONFIG_BT_PAC_SRC)
Emil Gydesen03862b32022-01-07 17:23:45 +0100464static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
465 void *buf, uint16_t len, uint16_t offset)
466{
Emil Gydesen98adaf02023-01-30 15:29:14 +0100467 ssize_t ret_val;
468 int err;
469
Théo Battrele458f5a2022-11-02 14:31:13 +0100470 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100471
Emil Gydesen98adaf02023-01-30 15:29:14 +0100472 err = k_sem_take(&read_buf_sem, READ_BUF_SEM_TIMEOUT);
473 if (err != 0) {
474 LOG_DBG("Failed to take read_buf_sem: %d", err);
475
476 return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
477 }
478
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100479 get_pac_records(&src_pacs_list, &read_buf);
Mariusz Skamra1432d052022-09-22 14:07:57 +0200480
Emil Gydesen98adaf02023-01-30 15:29:14 +0100481 ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
482 read_buf.len);
483
484 k_sem_give(&read_buf_sem);
485
486 return ret_val;
Emil Gydesen03862b32022-01-07 17:23:45 +0100487}
488
Emil Gydesen86fd8992023-08-28 17:00:09 +0200489#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
490static const struct bt_uuid *pacs_src_uuid = BT_UUID_PACS_SRC;
491
Emil Gydesena7969272022-04-13 21:10:18 +0200492static void src_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
493{
Théo Battrele458f5a2022-11-02 14:31:13 +0100494 LOG_DBG("attr %p value 0x%04x", attr, value);
Emil Gydesena7969272022-04-13 21:10:18 +0200495}
Emil Gydesen86fd8992023-08-28 17:00:09 +0200496#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200497#endif /* CONFIG_BT_PAC_SRC */
Mariusz Skamra49f91192022-10-18 22:58:34 +0200498
Emil Gydesena7969272022-04-13 21:10:18 +0200499#if defined(CONFIG_BT_PAC_SRC_LOC)
Emil Gydesen03862b32022-01-07 17:23:45 +0100500static ssize_t src_loc_read(struct bt_conn *conn,
501 const struct bt_gatt_attr *attr, void *buf,
502 uint16_t len, uint16_t offset)
503{
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100504 uint32_t location = sys_cpu_to_le32(pacs_src_location);
Emil Gydesen18466532022-02-08 14:43:43 +0100505
Théo Battrele458f5a2022-11-02 14:31:13 +0100506 LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
Emil Gydesen03862b32022-01-07 17:23:45 +0100507
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200508 return bt_gatt_attr_read(conn, attr, buf, len, offset, &location,
509 sizeof(location));
Emil Gydesen03862b32022-01-07 17:23:45 +0100510}
511
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200512#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
Emil Gydesen86fd8992023-08-28 17:00:09 +0200513static const struct bt_uuid *pacs_src_loc_uuid = BT_UUID_PACS_SRC_LOC;
514
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200515static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
516{
Théo Battrele458f5a2022-11-02 14:31:13 +0100517 LOG_DBG("attr %p value 0x%04x", attr, value);
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200518}
Fredrik Danebjer4c940562023-07-06 14:23:25 +0200519#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200520
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200521static void set_src_location(enum bt_audio_location audio_location)
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200522{
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100523 if (audio_location == pacs_src_location) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200524 return;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200525 }
526
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100527 pacs_src_location = audio_location;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200528
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200529 if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)) {
530 pacs_set_notify_bit(FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
531 k_work_submit(&deferred_nfy_work);
532 }
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200533}
534#else
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200535static void set_src_location(enum bt_audio_location location)
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200536{
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200537 return;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200538}
539#endif /* CONFIG_BT_PAC_SRC_LOC */
540
Hang Fan8d06fd82022-07-22 16:13:15 +0800541#if defined(CONFIG_BT_PAC_SRC_LOC_WRITEABLE)
Emil Gydesen03862b32022-01-07 17:23:45 +0100542static ssize_t src_loc_write(struct bt_conn *conn,
543 const struct bt_gatt_attr *attr, const void *data,
544 uint16_t len, uint16_t offset, uint8_t flags)
545{
Emil Gydesen18466532022-02-08 14:43:43 +0100546 uint32_t location;
547
Emil Gydesen03862b32022-01-07 17:23:45 +0100548 if (offset) {
549 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
550 }
551
Emil Gydesen18466532022-02-08 14:43:43 +0100552 if (len != sizeof(location)) {
Szymon Czapracki85639f72022-08-23 14:55:23 +0200553 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
Emil Gydesen03862b32022-01-07 17:23:45 +0100554 }
555
Emil Gydesen18466532022-02-08 14:43:43 +0100556 location = (enum bt_audio_location)sys_get_le32(data);
Bi Jian06fa2872023-09-29 11:11:34 +0800557 if (location > BT_AUDIO_LOCATION_MASK) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100558 LOG_DBG("Invalid location value: 0x%08X", location);
Szymon Czapracki85639f72022-08-23 14:55:23 +0200559 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
Emil Gydesen18466532022-02-08 14:43:43 +0100560 }
561
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200562 set_src_location(location);
Emil Gydesen03862b32022-01-07 17:23:45 +0100563
564 return len;
565}
Hang Fan8d06fd82022-07-22 16:13:15 +0800566#endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
Emil Gydesen03862b32022-01-07 17:23:45 +0100567
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200568
569static sys_slist_t *pacs_get(enum bt_audio_dir dir)
570{
571 switch (dir) {
572#if defined(CONFIG_BT_PAC_SNK)
573 case BT_AUDIO_DIR_SINK:
574 return &snk_pacs_list;
575#endif /* CONFIG_BT_PAC_SNK */
576#if defined(CONFIG_BT_PAC_SRC)
577 case BT_AUDIO_DIR_SOURCE:
578 return &src_pacs_list;
579#endif /* CONFIG_BT_PAC_SRC */
580 default:
581 return NULL;
582 }
583}
584
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200585#define BT_PACS_SNK_PROP \
586 BT_GATT_CHRC_READ \
587 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
588#define BT_PAC_SNK(_read) \
589 BT_AUDIO_CHRC(BT_UUID_PACS_SNK, \
590 BT_PACS_SNK_PROP, \
591 BT_GATT_PERM_READ_ENCRYPT, \
592 _read, NULL, NULL), \
Emil Gydesen86fd8992023-08-28 17:00:09 +0200593 IF_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE, (BT_AUDIO_CCC(snk_cfg_changed),))
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200594
595#define BT_PACS_SNK_LOC_PROP \
596 BT_GATT_CHRC_READ \
597 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
598 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
Emil Gydesen86fd8992023-08-28 17:00:09 +0200599
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200600#define BT_PACS_SNK_LOC_PERM \
601 BT_GATT_PERM_READ_ENCRYPT \
602 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
Emil Gydesen86fd8992023-08-28 17:00:09 +0200603
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200604#define BT_PACS_SNK_LOC(_read) \
605 BT_AUDIO_CHRC(BT_UUID_PACS_SNK_LOC, \
606 BT_PACS_SNK_LOC_PROP, \
607 BT_PACS_SNK_LOC_PERM, \
608 _read, \
609 COND_CODE_1(CONFIG_BT_PAC_SNK_LOC_WRITEABLE, (snk_loc_write), (NULL)), \
610 NULL), \
611 IF_ENABLED(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE, (BT_AUDIO_CCC(snk_loc_cfg_changed),))
612
613#define BT_PACS_SRC_PROP \
614 BT_GATT_CHRC_READ \
615 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
616#define BT_PAC_SRC(_read) \
617 BT_AUDIO_CHRC(BT_UUID_PACS_SRC, \
618 BT_PACS_SRC_PROP, \
619 BT_GATT_PERM_READ_ENCRYPT, \
620 _read, NULL, NULL), \
Emil Gydesen86fd8992023-08-28 17:00:09 +0200621 IF_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE, (BT_AUDIO_CCC(src_cfg_changed),))
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200622
623#define BT_PACS_SRC_LOC_PROP \
624 BT_GATT_CHRC_READ \
625 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_CHRC_WRITE)) \
626 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
Emil Gydesen86fd8992023-08-28 17:00:09 +0200627
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200628#define BT_PACS_SRC_LOC_PERM \
629 BT_GATT_PERM_READ_ENCRYPT \
630 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (|BT_GATT_PERM_WRITE_ENCRYPT))
Emil Gydesen86fd8992023-08-28 17:00:09 +0200631
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200632#define BT_PACS_SRC_LOC(_read) \
633 BT_AUDIO_CHRC(BT_UUID_PACS_SRC_LOC, \
634 BT_PACS_SRC_LOC_PROP, \
635 BT_PACS_SRC_LOC_PERM, \
636 _read, \
637 COND_CODE_1(CONFIG_BT_PAC_SRC_LOC_WRITEABLE, (src_loc_write), (NULL)), \
638 NULL), \
639 IF_ENABLED(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE, (BT_AUDIO_CCC(src_loc_cfg_changed),))
640
641#define BT_PAC_AVAILABLE_CONTEXT(_read) \
642 BT_AUDIO_CHRC(BT_UUID_PACS_AVAILABLE_CONTEXT, \
643 BT_GATT_CHRC_READ|BT_GATT_CHRC_NOTIFY, \
644 BT_GATT_PERM_READ_ENCRYPT, \
645 _read, NULL, NULL), \
646 BT_AUDIO_CCC(available_context_cfg_changed),
647
648#define BT_PACS_SUPPORTED_CONTEXT_PROP \
649 BT_GATT_CHRC_READ \
650 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, (|BT_GATT_CHRC_NOTIFY))
Emil Gydesen86fd8992023-08-28 17:00:09 +0200651
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200652#define BT_PAC_SUPPORTED_CONTEXT(_read) \
653 BT_AUDIO_CHRC(BT_UUID_PACS_SUPPORTED_CONTEXT, \
654 BT_PACS_SUPPORTED_CONTEXT_PROP, \
655 BT_GATT_PERM_READ_ENCRYPT, \
656 _read, NULL, NULL), \
657 IF_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE, \
658 (BT_AUDIO_CCC(supported_context_cfg_changed),))
659
Emil Gydesen03862b32022-01-07 17:23:45 +0100660BT_GATT_SERVICE_DEFINE(pacs_svc,
661 BT_GATT_PRIMARY_SERVICE(BT_UUID_PACS),
662#if defined(CONFIG_BT_PAC_SNK)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200663 BT_PAC_SNK(snk_read)
Mariusz Skamrafc398ac2022-10-18 22:18:55 +0200664#if defined(CONFIG_BT_PAC_SNK_LOC)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200665 BT_PACS_SNK_LOC(snk_loc_read)
Mariusz Skamrafc398ac2022-10-18 22:18:55 +0200666#endif /* CONFIG_BT_PAC_SNK_LOC */
Emil Gydesen03862b32022-01-07 17:23:45 +0100667#endif /* CONFIG_BT_PAC_SNK */
668#if defined(CONFIG_BT_PAC_SRC)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200669 BT_PAC_SRC(src_read)
Mariusz Skamrafc398ac2022-10-18 22:18:55 +0200670#if defined(CONFIG_BT_PAC_SRC_LOC)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200671 BT_PACS_SRC_LOC(src_loc_read)
Mariusz Skamrafc398ac2022-10-18 22:18:55 +0200672#endif /* CONFIG_BT_PAC_SRC_LOC */
Mariusz Skamraa3904bd2022-07-11 15:28:03 +0200673#endif /* CONFIG_BT_PAC_SRC */
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200674 BT_PAC_AVAILABLE_CONTEXT(available_contexts_read)
675 BT_PAC_SUPPORTED_CONTEXT(supported_context_read)
Emil Gydesen03862b32022-01-07 17:23:45 +0100676);
Emil Gydesen03862b32022-01-07 17:23:45 +0100677
Emil Gydesenbf377842024-01-03 11:33:08 +0100678#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200679static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir)
680{
681 uint32_t location_le;
682 int err;
Emil Gydesen86fd8992023-08-28 17:00:09 +0200683 const struct bt_uuid *uuid;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200684
685 switch (dir) {
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200686#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
Emil Gydesenbf377842024-01-03 11:33:08 +0100687 case BT_AUDIO_DIR_SINK:
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200688 location_le = sys_cpu_to_le32(pacs_snk_location);
Emil Gydesen86fd8992023-08-28 17:00:09 +0200689 uuid = pacs_snk_loc_uuid;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200690 break;
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200691#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200692#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
Emil Gydesenbf377842024-01-03 11:33:08 +0100693 case BT_AUDIO_DIR_SOURCE:
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200694 location_le = sys_cpu_to_le32(pacs_src_location);
Emil Gydesen86fd8992023-08-28 17:00:09 +0200695 uuid = pacs_src_loc_uuid;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200696 break;
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200697#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200698 default:
699 return -EINVAL;
700 }
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200701
Emil Gydesen86fd8992023-08-28 17:00:09 +0200702 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs, &location_le, sizeof(location_le));
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200703 if (err != 0 && err != -ENOTCONN) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100704 LOG_WRN("PACS notify_loc failed: %d", err);
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100705 return err;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200706 }
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100707
708 return 0;
Mariusz Skamrace973ed2022-10-18 23:52:06 +0200709}
Emil Gydesenbf377842024-01-03 11:33:08 +0100710#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE || CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
Emil Gydesena7969272022-04-13 21:10:18 +0200711
Emil Gydesenbf377842024-01-03 11:33:08 +0100712#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200713static int pac_notify(struct bt_conn *conn, enum bt_audio_dir dir)
Mariusz Skamrae635d3a2022-10-20 10:11:27 +0200714{
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100715 int err = 0;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200716 sys_slist_t *pacs;
Emil Gydesen86fd8992023-08-28 17:00:09 +0200717 const struct bt_uuid *uuid;
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200718
719 switch (dir) {
Emil Gydesen86fd8992023-08-28 17:00:09 +0200720#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200721 case BT_AUDIO_DIR_SINK:
Emil Gydesen86fd8992023-08-28 17:00:09 +0200722 uuid = pacs_snk_uuid;
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200723 break;
Emil Gydesen86fd8992023-08-28 17:00:09 +0200724#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
725#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200726 case BT_AUDIO_DIR_SOURCE:
Emil Gydesen86fd8992023-08-28 17:00:09 +0200727 uuid = pacs_src_uuid;
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200728 break;
Emil Gydesen86fd8992023-08-28 17:00:09 +0200729#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200730 default:
731 return -EINVAL;
732 }
Mariusz Skamra8500eb12022-10-20 10:19:44 +0200733
Emil Gydesen98adaf02023-01-30 15:29:14 +0100734 err = k_sem_take(&read_buf_sem, K_NO_WAIT);
735 if (err != 0) {
736 LOG_DBG("Failed to take read_buf_sem: %d", err);
737
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100738 return err;
Emil Gydesen98adaf02023-01-30 15:29:14 +0100739 }
740
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200741 pacs = pacs_get(dir);
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200742 __ASSERT(pacs, "Failed to get pacs.\n");
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200743 get_pac_records(pacs, &read_buf);
Emil Gydesena7969272022-04-13 21:10:18 +0200744
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200745 err = pacs_gatt_notify(conn, uuid, pacs_svc.attrs,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200746 read_buf.data, read_buf.len);
Emil Gydesenbe85f772022-06-24 10:28:17 +0200747 if (err != 0 && err != -ENOTCONN) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100748 LOG_WRN("PACS notify failed: %d", err);
Emil Gydesena7969272022-04-13 21:10:18 +0200749 }
Emil Gydesen98adaf02023-01-30 15:29:14 +0100750
751 k_sem_give(&read_buf_sem);
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100752
753 if (err == -ENOTCONN) {
754 return 0;
755 } else {
756 return 0;
757 }
Emil Gydesena7969272022-04-13 21:10:18 +0200758}
Emil Gydesenbf377842024-01-03 11:33:08 +0100759#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE || CONFIG_BT_PAC_SRC_NOTIFIABLE */
Emil Gydesen18466532022-02-08 14:43:43 +0100760
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200761static int available_contexts_notify(struct bt_conn *conn)
Mariusz Skamra33991f22022-05-19 16:52:44 +0200762{
Mariusz Skamra49f91192022-10-18 22:58:34 +0200763 struct bt_pacs_context context = {
Mariusz Skamra78a4b332023-12-01 14:50:35 +0100764 .snk = sys_cpu_to_le16(
Emil Gydesen476e7b32024-01-08 14:38:01 +0100765 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK)),
Mariusz Skamra78a4b332023-12-01 14:50:35 +0100766 .src = sys_cpu_to_le16(
Emil Gydesen476e7b32024-01-08 14:38:01 +0100767 pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE)),
Mariusz Skamra49f91192022-10-18 22:58:34 +0200768 };
Mariusz Skamra33991f22022-05-19 16:52:44 +0200769 int err;
770
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200771 err = pacs_gatt_notify(conn, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
Mariusz Skamra33991f22022-05-19 16:52:44 +0200772 &context, sizeof(context));
Emil Gydesenbe85f772022-06-24 10:28:17 +0200773 if (err != 0 && err != -ENOTCONN) {
Théo Battrele458f5a2022-11-02 14:31:13 +0100774 LOG_WRN("Available Audio Contexts notify failed: %d", err);
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100775 return err;
Mariusz Skamra33991f22022-05-19 16:52:44 +0200776 }
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100777
778 return 0;
Mariusz Skamra33991f22022-05-19 16:52:44 +0200779}
780
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200781static int supported_contexts_notify(struct bt_conn *conn)
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100782{
783 struct bt_pacs_context context = {
Mariusz Skamraef362e72023-12-01 13:28:09 +0100784 .snk = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SINK)),
785 .src = sys_cpu_to_le16(supported_context_get(BT_AUDIO_DIR_SOURCE)),
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100786 };
787 int err;
788
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200789 err = pacs_gatt_notify(conn, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100790 &context, sizeof(context));
791 if (err != 0 && err != -ENOTCONN) {
792 LOG_WRN("Supported Audio Contexts notify failed: %d", err);
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100793
794 return err;
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100795 }
Szymon Czapracki42a402a2023-03-15 22:36:16 +0100796 return 0;
Szymon Czapracki35bd4272022-11-24 09:33:09 +0100797}
798
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200799void pacs_gatt_notify_complete_cb(struct bt_conn *conn, void *user_data)
800{
801 /* Notification done, clear bit and reschedule work */
802 atomic_clear(&notify_rdy);
803 k_work_submit(&deferred_nfy_work);
804}
805
806static int pacs_gatt_notify(struct bt_conn *conn,
807 const struct bt_uuid *uuid,
808 const struct bt_gatt_attr *attr,
809 const void *data,
810 uint16_t len)
811{
812 int err;
813 struct bt_gatt_notify_params params;
814
815 memset(&params, 0, sizeof(params));
816 params.uuid = uuid;
817 params.attr = attr;
818 params.data = data;
819 params.len = len;
820 params.func = pacs_gatt_notify_complete_cb;
821
822 /* Mark notification in progress */
823 atomic_set(&notify_rdy, 1);
824
825 err = bt_gatt_notify_cb(conn, &params);
826 if (err != 0) {
827 atomic_clear(&notify_rdy);
828 }
829
Fredrik Danebjerfefbb282023-10-06 11:20:14 +0200830 if (err && err != -ENOTCONN) {
831 return err;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200832 }
Fredrik Danebjerfefbb282023-10-06 11:20:14 +0200833
834 return 0;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200835}
836
837static void notify_cb(struct bt_conn *conn, void *data)
838{
Mariusz Skamra014c22c2023-12-01 14:44:26 +0100839 struct pacs_client *client;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200840 struct bt_conn_info info;
841 int err = 0;
842
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200843 LOG_DBG("");
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200844
845 err = bt_conn_get_info(conn, &info);
846 if (err != 0) {
847 LOG_ERR("Failed to get conn info: %d", err);
848 return;
849 }
850
851 if (info.state != BT_CONN_STATE_CONNECTED) {
852 /* Not connected */
853 return;
854 }
855
Mariusz Skamra014c22c2023-12-01 14:44:26 +0100856 client = client_lookup_conn(conn);
857 if (client == NULL) {
858 return;
859 }
860
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200861 /* Check if we have unverified notifications in progress */
862 if (atomic_get(&notify_rdy)) {
863 return;
864 }
865
Emil Gydesenbf377842024-01-03 11:33:08 +0100866#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
867 if (atomic_test_bit(client->flags, FLAG_SINK_PAC_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200868 LOG_DBG("Notifying Sink PAC");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200869 err = pac_notify(conn, BT_AUDIO_DIR_SINK);
870 if (!err) {
871 atomic_clear_bit(client->flags, FLAG_SINK_PAC_CHANGED);
872 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200873 }
Emil Gydesenbf377842024-01-03 11:33:08 +0100874#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200875
Emil Gydesenbf377842024-01-03 11:33:08 +0100876#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
877 if (atomic_test_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200878 LOG_DBG("Notifying Sink Audio Location");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200879 err = pac_notify_loc(conn, BT_AUDIO_DIR_SINK);
880 if (!err) {
881 atomic_clear_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
882 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200883 }
Emil Gydesenbf377842024-01-03 11:33:08 +0100884#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
885
886#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
887 if (atomic_test_bit(client->flags, FLAG_SOURCE_PAC_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200888 LOG_DBG("Notifying Source PAC");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200889 err = pac_notify(conn, BT_AUDIO_DIR_SOURCE);
890 if (!err) {
891 atomic_clear_bit(client->flags, FLAG_SOURCE_PAC_CHANGED);
892 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200893 }
Emil Gydesenbf377842024-01-03 11:33:08 +0100894#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
895
896#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
897 if (atomic_test_and_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200898 LOG_DBG("Notifying Source Audio Location");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200899 err = pac_notify_loc(conn, BT_AUDIO_DIR_SOURCE);
900 if (!err) {
901 atomic_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
902 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200903 }
Emil Gydesenbf377842024-01-03 11:33:08 +0100904#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200905
906 if (atomic_test_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200907 LOG_DBG("Notifying Available Contexts");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200908 err = available_contexts_notify(conn);
909 if (!err) {
910 atomic_clear_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
911 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200912 }
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200913
914 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE) &&
915 atomic_test_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED)) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200916 LOG_DBG("Notifying Supported Contexts");
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +0200917 err = supported_contexts_notify(conn);
918 if (!err) {
919 atomic_clear_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
920 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200921 }
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200922}
923
924static void deferred_nfy_work_handler(struct k_work *work)
925{
926 bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
927}
928
929static void pacs_auth_pairing_complete(struct bt_conn *conn, bool bonded)
930{
931 LOG_DBG("%s paired (%sbonded)", bt_addr_le_str(bt_conn_get_dst(conn)),
932 bonded ? "" : "not ");
933
934 if (!bonded) {
935 return;
936 }
937
938 /* Check if already in list, and do nothing if it is */
939 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
940 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
941 bt_addr_le_eq(bt_conn_get_dst(conn), &clients[i].addr)) {
942 return;
943 }
944 }
945
946 /* Else add the device */
947 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
948 if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
949 atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
950 memcpy(&clients[i].addr, bt_conn_get_dst(conn), sizeof(bt_addr_le_t));
951
952 /* Send out all pending notifications */
953 k_work_submit(&deferred_nfy_work);
954 return;
955 }
956 }
957}
958
959static void pacs_bond_deleted(uint8_t id, const bt_addr_le_t *peer)
960{
961 /* Find the device entry to delete */
962 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
963 /* Check if match, and if active, if so, reset */
964 if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
965 bt_addr_le_eq(peer, &clients[i].addr)) {
966 for (size_t j = 0U; j < FLAG_NUM; j++) {
967 atomic_clear_bit(clients[i].flags, j);
968 }
969 (void)memset(&clients[i].addr, 0, sizeof(bt_addr_le_t));
970 return;
971 }
972 }
973}
974
975static void pacs_security_changed(struct bt_conn *conn, bt_security_t level,
976 enum bt_security_err err)
977{
978 LOG_DBG("%s changed security level to %d", bt_addr_le_str(bt_conn_get_dst(conn)), level);
979
980 if (err != 0 || conn->encrypt == 0) {
981 return;
982 }
983
984 if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
985 return;
986 }
987
988 for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +0200989 for (size_t j = 0U; j < FLAG_NUM; j++) {
990 if (atomic_test_bit(clients[i].flags, j)) {
991
992 /**
993 * It's enough that one flag is set, as the defer work will go
994 * through all notifiable characteristics
995 */
996 k_work_submit(&deferred_nfy_work);
997 return;
998 }
999 }
1000 }
1001}
1002
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001003static void pacs_disconnected(struct bt_conn *conn, uint8_t reason)
1004{
1005 struct pacs_client *client;
1006
1007 client = client_lookup_conn(conn);
1008 if (client == NULL) {
1009 return;
1010 }
1011
1012#if defined(CONFIG_BT_PAC_SNK)
1013 if (client->snk_available_contexts != NULL) {
1014 uint16_t old = POINTER_TO_UINT(client->snk_available_contexts);
1015 uint16_t new;
1016
1017 client->snk_available_contexts = NULL;
Emil Gydesen476e7b32024-01-08 14:38:01 +01001018 new = pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SINK);
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001019
1020 atomic_set_bit_to(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED, old != new);
1021 }
1022#endif /* CONFIG_BT_PAC_SNK */
1023
1024#if defined(CONFIG_BT_PAC_SRC)
1025 if (client->src_available_contexts != NULL) {
1026 uint16_t old = POINTER_TO_UINT(client->src_available_contexts);
1027 uint16_t new;
1028
1029 client->src_available_contexts = NULL;
Emil Gydesen476e7b32024-01-08 14:38:01 +01001030 new = pacs_get_available_contexts_for_conn(conn, BT_AUDIO_DIR_SOURCE);
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001031
1032 atomic_set_bit_to(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED, old != new);
1033 }
1034#endif /* CONFIG_BT_PAC_SRC */
1035}
1036
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001037static struct bt_conn_cb conn_callbacks = {
1038 .security_changed = pacs_security_changed,
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001039 .disconnected = pacs_disconnected,
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001040};
1041
1042static struct bt_conn_auth_info_cb auth_callbacks = {
1043 .pairing_complete = pacs_auth_pairing_complete,
1044 .bond_deleted = pacs_bond_deleted
1045};
1046
Mariusz Skamra26270632022-10-20 11:25:38 +02001047void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001048{
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001049 sys_slist_t *pac;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001050
1051 CHECKIF(func == NULL) {
Théo Battrele458f5a2022-11-02 14:31:13 +01001052 LOG_ERR("func is NULL");
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001053 return;
1054 }
1055
Mariusz Skamra26270632022-10-20 11:25:38 +02001056 pac = pacs_get(dir);
1057 if (!pac) {
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001058 return;
1059 }
1060
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001061 foreach_cap(pac, func, user_data);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001062}
1063
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001064static void add_bonded_addr_to_client_list(const struct bt_bond_info *info, void *data)
1065{
1066 for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
1067 /* Check if device is registered, it not, add it */
1068 if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
1069 char addr_str[BT_ADDR_LE_STR_LEN];
1070
1071 atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
1072 memcpy(&clients[i].addr, &info->addr, sizeof(bt_addr_le_t));
1073 bt_addr_le_to_str(&clients[i].addr, addr_str, sizeof(addr_str));
1074 LOG_DBG("Added %s to bonded list\n", addr_str);
1075 return;
1076 }
1077 }
1078}
1079
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001080/* Register Audio Capability */
Mariusz Skamra26270632022-10-20 11:25:38 +02001081int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001082{
Emil Gydesen69f7fd92023-05-16 14:16:14 +02001083 const struct bt_audio_codec_cap *codec_cap;
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001084 static bool callbacks_registered;
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001085 sys_slist_t *pac;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001086
Emil Gydesen69f7fd92023-05-16 14:16:14 +02001087 if (!cap || !cap->codec_cap) {
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001088 return -EINVAL;
1089 }
1090
Emil Gydesen69f7fd92023-05-16 14:16:14 +02001091 codec_cap = cap->codec_cap;
1092
Mariusz Skamra26270632022-10-20 11:25:38 +02001093 pac = pacs_get(dir);
1094 if (!pac) {
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001095 return -EINVAL;
1096 }
1097
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001098 /* Restore bonding list */
1099 bt_foreach_bond(BT_ID_DEFAULT, add_bonded_addr_to_client_list, NULL);
1100
Emil Gydesen69f7fd92023-05-16 14:16:14 +02001101 LOG_DBG("cap %p dir %s codec_cap id 0x%02x codec_cap cid 0x%04x codec_cap vid 0x%04x", cap,
1102 bt_audio_dir_str(dir), codec_cap->id, codec_cap->cid, codec_cap->vid);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001103
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001104 sys_slist_append(pac, &cap->_node);
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001105
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001106 if (!callbacks_registered) {
1107 bt_conn_cb_register(&conn_callbacks);
1108 bt_conn_auth_info_cb_register(&auth_callbacks);
1109
1110 callbacks_registered = true;
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001111 }
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001112
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +02001113 if (IS_ENABLED(CONFIG_BT_PAC_SNK_NOTIFIABLE) && dir == BT_AUDIO_DIR_SINK) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001114 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1115 k_work_submit(&deferred_nfy_work);
1116 }
1117
Fredrik Danebjer32b00dd2023-08-14 15:21:51 +02001118 if (IS_ENABLED(CONFIG_BT_PAC_SRC_NOTIFIABLE) && dir == BT_AUDIO_DIR_SOURCE) {
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001119 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1120 k_work_submit(&deferred_nfy_work);
1121 }
1122
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001123 return 0;
1124}
1125
1126/* Unregister Audio Capability */
Mariusz Skamra26270632022-10-20 11:25:38 +02001127int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001128{
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001129 sys_slist_t *pac;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001130
1131 if (!cap) {
1132 return -EINVAL;
1133 }
1134
Mariusz Skamra26270632022-10-20 11:25:38 +02001135 pac = pacs_get(dir);
1136 if (!pac) {
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001137 return -EINVAL;
1138 }
1139
Emil Gydesen249d4772023-01-05 11:33:47 +01001140 LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir));
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001141
Szymon Czapracki42a402a2023-03-15 22:36:16 +01001142 if (!sys_slist_find_and_remove(pac, &cap->_node)) {
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001143 return -ENOENT;
1144 }
1145
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001146 switch (dir) {
1147#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
1148 case BT_AUDIO_DIR_SINK:
1149 pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
1150 k_work_submit(&deferred_nfy_work);
1151 break;
1152#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
1153#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
1154 case BT_AUDIO_DIR_SOURCE:
1155 pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
1156 k_work_submit(&deferred_nfy_work);
1157 break;
1158#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
1159 default:
1160 return -EINVAL;
1161 }
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001162
1163 return 0;
1164}
1165
Mariusz Skamra26270632022-10-20 11:25:38 +02001166int bt_pacs_set_location(enum bt_audio_dir dir, enum bt_audio_location location)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001167{
Mariusz Skamrace973ed2022-10-18 23:52:06 +02001168 switch (dir) {
1169 case BT_AUDIO_DIR_SINK:
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001170 set_snk_location(location);
1171 break;
Mariusz Skamrace973ed2022-10-18 23:52:06 +02001172 case BT_AUDIO_DIR_SOURCE:
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001173 set_src_location(location);
1174 break;
1175 default:
1176 return -EINVAL;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001177 }
1178
Fredrik Danebjer2ebbc7b2023-08-14 15:18:57 +02001179 return 0;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001180}
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001181
Mariusz Skamra26270632022-10-20 11:25:38 +02001182int bt_pacs_set_available_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001183{
Mariusz Skamra49f91192022-10-18 22:58:34 +02001184 switch (dir) {
1185 case BT_AUDIO_DIR_SINK:
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001186 return set_available_contexts(contexts, &snk_available_contexts,
1187 supported_context_get(dir));
Mariusz Skamra49f91192022-10-18 22:58:34 +02001188 case BT_AUDIO_DIR_SOURCE:
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001189 return set_available_contexts(contexts, &src_available_contexts,
1190 supported_context_get(dir));
Mariusz Skamra49f91192022-10-18 22:58:34 +02001191 }
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001192
1193 return -EINVAL;
1194}
1195
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001196int bt_pacs_conn_set_available_contexts_for_conn(struct bt_conn *conn, enum bt_audio_dir dir,
1197 enum bt_audio_context *contexts)
1198{
Emil Gydesen476e7b32024-01-08 14:38:01 +01001199 enum bt_audio_context old = pacs_get_available_contexts_for_conn(conn, dir);
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001200 struct bt_conn_info info = { 0 };
1201 struct pacs_client *client;
1202 int err;
1203
1204 client = client_lookup_conn(conn);
1205 if (client == NULL) {
1206 return -ENOENT;
1207 }
1208
1209 err = bt_conn_get_info(conn, &info);
1210 if (err < 0) {
1211 LOG_ERR("Could not get conn info: %d", err);
1212 return err;
1213 }
1214
1215 switch (dir) {
1216#if defined(CONFIG_BT_PAC_SNK)
1217 case BT_AUDIO_DIR_SINK:
1218 if (contexts != NULL) {
1219 client->snk_available_contexts = UINT_TO_POINTER(*contexts);
1220 } else {
1221 client->snk_available_contexts = NULL;
1222 }
1223
1224 break;
1225#endif /* CONFIG_BT_PAC_SNK */
1226#if defined(CONFIG_BT_PAC_SRC)
1227 case BT_AUDIO_DIR_SOURCE:
1228 if (contexts != NULL) {
1229 client->src_available_contexts = UINT_TO_POINTER(*contexts);
1230 } else {
1231 client->src_available_contexts = NULL;
1232 }
1233
1234 break;
1235#endif /* CONFIG_BT_PAC_SRC */
1236 default:
1237 return -EINVAL;
1238 }
1239
Emil Gydesen476e7b32024-01-08 14:38:01 +01001240 if (pacs_get_available_contexts_for_conn(conn, dir) == old) {
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001241 /* No change. Skip notification */
1242 return 0;
1243 }
1244
1245 atomic_set_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
1246
1247 /* Send notification on encrypted link only */
1248 if (info.security.level > BT_SECURITY_L1) {
1249 k_work_submit(&deferred_nfy_work);
1250 }
1251
1252 return 0;
1253}
1254
Szymon Czapracki35bd4272022-11-24 09:33:09 +01001255int bt_pacs_set_supported_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
1256{
Mariusz Skamraef362e72023-12-01 13:28:09 +01001257 uint16_t *supported_contexts = NULL;
1258 uint16_t *available_contexts = NULL;
1259
Szymon Czapracki35bd4272022-11-24 09:33:09 +01001260 switch (dir) {
1261 case BT_AUDIO_DIR_SINK:
Mariusz Skamraef362e72023-12-01 13:28:09 +01001262#if defined(CONFIG_BT_PAC_SNK)
1263 supported_contexts = &snk_supported_contexts;
1264 available_contexts = &snk_available_contexts;
1265 break;
1266#endif /* CONFIG_BT_PAC_SNK */
1267 return -ENOTSUP;
Szymon Czapracki35bd4272022-11-24 09:33:09 +01001268 case BT_AUDIO_DIR_SOURCE:
Mariusz Skamraef362e72023-12-01 13:28:09 +01001269#if defined(CONFIG_BT_PAC_SRC)
1270 supported_contexts = &src_supported_contexts;
1271 available_contexts = &src_available_contexts;
1272 break;
1273#endif /* CONFIG_BT_PAC_SRC */
1274 return -ENOTSUP;
1275 default:
1276 return -EINVAL;
Szymon Czapracki35bd4272022-11-24 09:33:09 +01001277 }
1278
Mariusz Skamraef362e72023-12-01 13:28:09 +01001279 if (IS_ENABLED(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE) || *supported_contexts == 0) {
1280 return set_supported_contexts(contexts, supported_contexts, available_contexts);
1281 }
1282
1283 return -EALREADY;
Szymon Czapracki35bd4272022-11-24 09:33:09 +01001284}
1285
Mariusz Skamra26270632022-10-20 11:25:38 +02001286enum bt_audio_context bt_pacs_get_available_contexts(enum bt_audio_dir dir)
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001287{
Mariusz Skamra49f91192022-10-18 22:58:34 +02001288 switch (dir) {
1289 case BT_AUDIO_DIR_SINK:
1290 return snk_available_contexts;
1291 case BT_AUDIO_DIR_SOURCE:
1292 return src_available_contexts;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001293 }
1294
Mariusz Skamra49f91192022-10-18 22:58:34 +02001295 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
Mariusz Skamra7c1f6d92022-10-18 14:46:12 +02001296}
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001297
1298enum bt_audio_context bt_pacs_get_available_contexts_for_conn(struct bt_conn *conn,
1299 enum bt_audio_dir dir)
1300{
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001301 CHECKIF(conn == NULL) {
1302 LOG_ERR("NULL conn");
Emil Gydesen476e7b32024-01-08 14:38:01 +01001303 return BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001304 }
1305
Emil Gydesen476e7b32024-01-08 14:38:01 +01001306 return pacs_get_available_contexts_for_conn(conn, dir);
Mariusz Skamra78a4b332023-12-01 14:50:35 +01001307}