Bluetooth: Audio: Make PACS notify bonded clients on reconnect
This fix allows PACS to notify bonded clients, that were disconnected when a characteristic with notify property was changed, are being reconnected. This fix also implements a defer work so to not block the bt rx thread during auth callbacks. All other notify calls are for consistency and style also moved to use the defer work. Signed-off-by: Fredrik Danebjer <frdn@demant.com>
This commit is contained in:
parent
4c94056f74
commit
2ebbc7b130
1 changed files with 403 additions and 169 deletions
|
@ -23,6 +23,7 @@
|
|||
#include <zephyr/bluetooth/audio/pacs.h>
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include "../host/conn_internal.h"
|
||||
#include "../host/hci_core.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
|
@ -63,14 +64,90 @@ static uint16_t src_available_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
|
|||
static uint16_t src_supported_contexts = BT_AUDIO_CONTEXT_TYPE_PROHIBITED;
|
||||
#endif /* CONFIG_BT_PAC_SRC */
|
||||
|
||||
enum {
|
||||
FLAG_ACTIVE,
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
FLAG_SINK_PAC_CHANGED,
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
|
||||
FLAG_SINK_AUDIO_LOCATIONS_CHANGED,
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
FLAG_SOURCE_PAC_CHANGED,
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED,
|
||||
#endif
|
||||
FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED,
|
||||
#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
|
||||
FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED,
|
||||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
FLAG_NUM,
|
||||
};
|
||||
|
||||
static struct pacs_client {
|
||||
bt_addr_le_t addr;
|
||||
|
||||
/* Pending notification flags */
|
||||
ATOMIC_DEFINE(flags, FLAG_NUM);
|
||||
} clients[CONFIG_BT_MAX_PAIRED];
|
||||
|
||||
static atomic_t notify_rdy;
|
||||
|
||||
static K_SEM_DEFINE(read_buf_sem, 1, 1);
|
||||
NET_BUF_SIMPLE_DEFINE_STATIC(read_buf, BT_ATT_MAX_ATTRIBUTE_LEN);
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir);
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE || CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE*/
|
||||
static int pacs_gatt_notify(struct bt_conn *conn,
|
||||
const struct bt_uuid *uuid,
|
||||
const struct bt_gatt_attr *attr,
|
||||
const void *data,
|
||||
uint16_t len);
|
||||
static void deferred_nfy_work_handler(struct k_work *work);
|
||||
|
||||
static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
|
||||
|
||||
static ssize_t pac_data_add(struct net_buf_simple *buf, size_t count,
|
||||
struct bt_audio_codec_data *data)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (size_t i = 0U; i < count; i++) {
|
||||
struct bt_pac_ltv *ltv;
|
||||
struct bt_data *d = &data[i].data;
|
||||
const size_t ltv_len = sizeof(*ltv) + d->data_len;
|
||||
|
||||
if (net_buf_simple_tailroom(buf) < ltv_len) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ltv = net_buf_simple_add(buf, sizeof(*ltv));
|
||||
ltv->len = d->data_len + sizeof(ltv->type);
|
||||
ltv->type = d->type;
|
||||
net_buf_simple_add_mem(buf, d->data, d->data_len);
|
||||
|
||||
len += ltv_len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
struct pac_records_build_data {
|
||||
struct bt_pacs_read_rsp *rsp;
|
||||
struct net_buf_simple *buf;
|
||||
};
|
||||
|
||||
static void pacs_set_notify_bit(int bit)
|
||||
{
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
|
||||
if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
|
||||
atomic_set_bit(clients[i].flags, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool build_pac_records(const struct bt_pacs_cap *cap, void *user_data)
|
||||
{
|
||||
struct pac_records_build_data *data = user_data;
|
||||
|
@ -185,16 +262,9 @@ static ssize_t supported_context_read(struct bt_conn *conn,
|
|||
sizeof(context));
|
||||
}
|
||||
|
||||
static int available_contexts_notify(void);
|
||||
#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
|
||||
static int supported_contexts_notify(void);
|
||||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
|
||||
static int set_available_contexts(uint16_t contexts, uint16_t *available,
|
||||
uint16_t supported)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (contexts & ~supported) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
@ -203,13 +273,11 @@ static int set_available_contexts(uint16_t contexts, uint16_t *available,
|
|||
return 0;
|
||||
}
|
||||
|
||||
err = available_contexts_notify();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*available = contexts;
|
||||
|
||||
pacs_set_notify_bit(FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,13 +309,8 @@ static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
|
|||
}
|
||||
|
||||
#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
|
||||
err = supported_contexts_notify();
|
||||
if (err) {
|
||||
*supported = tmp_supported;
|
||||
*available = tmp_available;
|
||||
|
||||
return err;
|
||||
}
|
||||
pacs_set_notify_bit(FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
|
||||
return 0;
|
||||
|
@ -310,7 +373,6 @@ static inline int set_snk_supported_contexts(uint16_t contexts)
|
|||
#endif /* CONFIG_BT_PAC_SNK */
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC)
|
||||
static int pac_notify_snk_loc(void);
|
||||
static ssize_t snk_loc_read(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
|
@ -330,24 +392,23 @@ static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
|||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
|
||||
static int set_snk_location(enum bt_audio_location audio_location)
|
||||
static void set_snk_location(enum bt_audio_location audio_location)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (audio_location == pacs_snk_location) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pacs_snk_location = audio_location;
|
||||
|
||||
err = pac_notify_snk_loc();
|
||||
|
||||
return err;
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
|
||||
pacs_set_notify_bit(FLAG_SINK_AUDIO_LOCATIONS_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
}
|
||||
#else
|
||||
static int set_snk_location(enum bt_audio_location location)
|
||||
static void set_snk_location(enum bt_audio_location location)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC */
|
||||
|
||||
|
@ -356,7 +417,6 @@ static ssize_t 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)
|
||||
{
|
||||
int err;
|
||||
enum bt_audio_location location;
|
||||
|
||||
if (offset) {
|
||||
|
@ -373,11 +433,7 @@ static ssize_t snk_loc_write(struct bt_conn *conn,
|
|||
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
|
||||
}
|
||||
|
||||
err = set_snk_location(location);
|
||||
if (err != 0) {
|
||||
LOG_DBG("write_location returned %d", err);
|
||||
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
|
||||
}
|
||||
set_snk_location(location);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -438,9 +494,6 @@ static inline int set_src_supported_contexts(uint16_t contexts)
|
|||
#endif /* CONFIG_BT_PAC_SRC */
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC)
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
static int pac_notify_src_loc(void);
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
static ssize_t src_loc_read(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
|
@ -460,35 +513,23 @@ static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
|||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
|
||||
static int set_src_location(enum bt_audio_location audio_location)
|
||||
static void set_src_location(enum bt_audio_location audio_location)
|
||||
{
|
||||
int err = 0;
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
enum bt_audio_location tmp_audio_location = pacs_src_location;
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
|
||||
if (audio_location == pacs_src_location) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pacs_src_location = audio_location;
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
err = pac_notify_src_loc();
|
||||
if (err) {
|
||||
LOG_DBG("Notify failed, previous audio location restored");
|
||||
audio_location = tmp_audio_location;
|
||||
|
||||
return err;
|
||||
}
|
||||
pacs_set_notify_bit(FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
static int set_src_location(enum bt_audio_location location)
|
||||
static void set_src_location(enum bt_audio_location location)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC */
|
||||
|
||||
|
@ -497,7 +538,6 @@ static ssize_t 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)
|
||||
{
|
||||
int err;
|
||||
uint32_t location;
|
||||
|
||||
if (offset) {
|
||||
|
@ -514,16 +554,29 @@ static ssize_t src_loc_write(struct bt_conn *conn,
|
|||
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
|
||||
}
|
||||
|
||||
err = set_src_location(location);
|
||||
if (err != 0) {
|
||||
LOG_DBG("write_location returned %d", err);
|
||||
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
|
||||
}
|
||||
set_src_location(location);
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_WRITEABLE */
|
||||
|
||||
|
||||
static sys_slist_t *pacs_get(enum bt_audio_dir dir)
|
||||
{
|
||||
switch (dir) {
|
||||
#if defined(CONFIG_BT_PAC_SNK)
|
||||
case BT_AUDIO_DIR_SINK:
|
||||
return &snk_pacs_list;
|
||||
#endif /* CONFIG_BT_PAC_SNK */
|
||||
#if defined(CONFIG_BT_PAC_SRC)
|
||||
case BT_AUDIO_DIR_SOURCE:
|
||||
return &src_pacs_list;
|
||||
#endif /* CONFIG_BT_PAC_SRC */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(pacs_svc,
|
||||
BT_GATT_PRIMARY_SERVICE(BT_UUID_PACS),
|
||||
#if defined(CONFIG_BT_PAC_SNK)
|
||||
|
@ -624,30 +677,28 @@ BT_GATT_SERVICE_DEFINE(pacs_svc,
|
|||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
);
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
static int pac_notify_loc(struct bt_conn *conn, enum bt_audio_dir dir)
|
||||
{
|
||||
uint32_t location_le;
|
||||
int err;
|
||||
|
||||
switch (dir) {
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
|
||||
static int pac_notify_snk_loc(void)
|
||||
{
|
||||
uint32_t location_le = sys_cpu_to_le32(pacs_snk_location);
|
||||
int err;
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK_LOC, pacs_svc.attrs, &location_le,
|
||||
sizeof(location_le));
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("PACS notify_loc failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
|
||||
case BT_AUDIO_DIR_SINK:
|
||||
location_le = sys_cpu_to_le32(pacs_snk_location);
|
||||
break;
|
||||
#endif /* CONFIG_BT_PAC_SNK */
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
static int pac_notify_src_loc(void)
|
||||
{
|
||||
uint32_t location_le = sys_cpu_to_le32(pacs_src_location);
|
||||
int err;
|
||||
case BT_AUDIO_DIR_SOURCE:
|
||||
location_le = sys_cpu_to_le32(pacs_src_location);
|
||||
break;
|
||||
#endif /* CONFIG_BT_PAC_SRC */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC_LOC, pacs_svc.attrs, &location_le,
|
||||
err = pacs_gatt_notify(conn, BT_UUID_PACS_SRC_LOC, pacs_svc.attrs, &location_le,
|
||||
sizeof(location_le));
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("PACS notify_loc failed: %d", err);
|
||||
|
@ -656,12 +707,13 @@ static int pac_notify_src_loc(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE || CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE*/
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
static int pac_notify_snk(void)
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE) || defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
static int pac_notify(struct bt_conn *conn, enum bt_audio_dir dir)
|
||||
{
|
||||
int err = 0;
|
||||
sys_slist_t *pacs;
|
||||
|
||||
err = k_sem_take(&read_buf_sem, K_NO_WAIT);
|
||||
if (err != 0) {
|
||||
|
@ -670,10 +722,15 @@ static int pac_notify_snk(void)
|
|||
return err;
|
||||
}
|
||||
|
||||
get_pac_records(&snk_pacs_list, &read_buf);
|
||||
pacs = pacs_get(dir);
|
||||
if (!pacs) {
|
||||
LOG_DBG("Failed to get pacs");
|
||||
return -EINVAL;
|
||||
}
|
||||
get_pac_records(pacs, &read_buf);
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK, pacs_svc.attrs,
|
||||
read_buf.data, read_buf.len);
|
||||
err = pacs_gatt_notify(conn, BT_UUID_PACS_SNK, pacs_svc.attrs,
|
||||
read_buf.data, read_buf.len);
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("PACS notify failed: %d", err);
|
||||
}
|
||||
|
@ -686,56 +743,9 @@ static int pac_notify_snk(void)
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE|| CONFIG_BT_PAC_SNK_NOTIFIABLE */
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
static int pac_notify_src(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = k_sem_take(&read_buf_sem, K_NO_WAIT);
|
||||
if (err != 0) {
|
||||
LOG_DBG("Failed to take read_buf_sem: %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
get_pac_records(&src_pacs_list, &read_buf);
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC, pacs_svc.attrs,
|
||||
read_buf.data, read_buf.len);
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("PACS notify failed: %d", err);
|
||||
}
|
||||
|
||||
k_sem_give(&read_buf_sem);
|
||||
|
||||
if (err == -ENOTCONN) {
|
||||
return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
|
||||
static int pacs_changed(enum bt_audio_dir dir)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (dir == BT_AUDIO_DIR_SINK) {
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
err = pac_notify_snk();
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
|
||||
} else {
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
err = pac_notify_src();
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int available_contexts_notify(void)
|
||||
static int available_contexts_notify(struct bt_conn *conn)
|
||||
{
|
||||
struct bt_pacs_context context = {
|
||||
.snk = sys_cpu_to_le16(snk_available_contexts),
|
||||
|
@ -743,7 +753,7 @@ static int available_contexts_notify(void)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
|
||||
err = pacs_gatt_notify(conn, BT_UUID_PACS_AVAILABLE_CONTEXT, pacs_svc.attrs,
|
||||
&context, sizeof(context));
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("Available Audio Contexts notify failed: %d", err);
|
||||
|
@ -754,7 +764,7 @@ static int available_contexts_notify(void)
|
|||
}
|
||||
|
||||
#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
|
||||
static int supported_contexts_notify(void)
|
||||
static int supported_contexts_notify(struct bt_conn *conn)
|
||||
{
|
||||
struct bt_pacs_context context = {
|
||||
.snk = sys_cpu_to_le16(snk_supported_contexts),
|
||||
|
@ -762,7 +772,7 @@ static int supported_contexts_notify(void)
|
|||
};
|
||||
int err;
|
||||
|
||||
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
|
||||
err = pacs_gatt_notify(conn, BT_UUID_PACS_SUPPORTED_CONTEXT, pacs_svc.attrs,
|
||||
&context, sizeof(context));
|
||||
if (err != 0 && err != -ENOTCONN) {
|
||||
LOG_WRN("Supported Audio Contexts notify failed: %d", err);
|
||||
|
@ -773,6 +783,193 @@ static int supported_contexts_notify(void)
|
|||
}
|
||||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
|
||||
void pacs_gatt_notify_complete_cb(struct bt_conn *conn, void *user_data)
|
||||
{
|
||||
/* Notification done, clear bit and reschedule work */
|
||||
atomic_clear(¬ify_rdy);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
}
|
||||
|
||||
static int pacs_gatt_notify(struct bt_conn *conn,
|
||||
const struct bt_uuid *uuid,
|
||||
const struct bt_gatt_attr *attr,
|
||||
const void *data,
|
||||
uint16_t len)
|
||||
{
|
||||
int err;
|
||||
struct bt_gatt_notify_params params;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.uuid = uuid;
|
||||
params.attr = attr;
|
||||
params.data = data;
|
||||
params.len = len;
|
||||
params.func = pacs_gatt_notify_complete_cb;
|
||||
|
||||
/* Mark notification in progress */
|
||||
atomic_set(¬ify_rdy, 1);
|
||||
|
||||
err = bt_gatt_notify_cb(conn, ¶ms);
|
||||
if (err != 0) {
|
||||
atomic_clear(¬ify_rdy);
|
||||
}
|
||||
|
||||
if (err == -ENOTCONN) {
|
||||
return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void notify_cb(struct bt_conn *conn, void *data)
|
||||
{
|
||||
struct pacs_client *client = &clients[bt_conn_index(conn)];
|
||||
struct bt_conn_info info;
|
||||
int err = 0;
|
||||
|
||||
LOG_DBG("Notify cb");
|
||||
|
||||
err = bt_conn_get_info(conn, &info);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to get conn info: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.state != BT_CONN_STATE_CONNECTED) {
|
||||
/* Not connected */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we have unverified notifications in progress */
|
||||
if (atomic_get(¬ify_rdy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_SINK_PAC_CHANGED)) {
|
||||
LOG_DBG("Notifying Sink PAC");
|
||||
pac_notify(conn, BT_AUDIO_DIR_SINK);
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_SINK_AUDIO_LOCATIONS_CHANGED)) {
|
||||
LOG_DBG("Notifying Sink Audio Location");
|
||||
pac_notify_loc(conn, BT_AUDIO_DIR_SINK);
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_SOURCE_PAC_CHANGED)) {
|
||||
LOG_DBG("Notifying Source PAC");
|
||||
pac_notify(conn, BT_AUDIO_DIR_SOURCE);
|
||||
}
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_SOURCE_AUDIO_LOCATIONS_CHANGED)) {
|
||||
LOG_DBG("Notifying Source Audio Location");
|
||||
pac_notify_loc(conn, BT_AUDIO_DIR_SOURCE);
|
||||
}
|
||||
#endif
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_AVAILABLE_AUDIO_CONTEXT_CHANGED)) {
|
||||
LOG_DBG("Notifying Available Contexts");
|
||||
available_contexts_notify(conn);
|
||||
}
|
||||
#if defined(CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE)
|
||||
if (atomic_test_and_clear_bit(client->flags, FLAG_SUPPORTED_AUDIO_CONTEXT_CHANGED)) {
|
||||
LOG_DBG("Notifying Supported Contexts");
|
||||
supported_contexts_notify(conn);
|
||||
}
|
||||
#endif /* CONFIG_BT_PACS_SUPPORTED_CONTEXT_NOTIFIABLE */
|
||||
}
|
||||
|
||||
static void deferred_nfy_work_handler(struct k_work *work)
|
||||
{
|
||||
bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
|
||||
}
|
||||
|
||||
static void pacs_auth_pairing_complete(struct bt_conn *conn, bool bonded)
|
||||
{
|
||||
LOG_DBG("%s paired (%sbonded)", bt_addr_le_str(bt_conn_get_dst(conn)),
|
||||
bonded ? "" : "not ");
|
||||
|
||||
if (!bonded) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if already in list, and do nothing if it is */
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
|
||||
if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
|
||||
bt_addr_le_eq(bt_conn_get_dst(conn), &clients[i].addr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Else add the device */
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
|
||||
if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
|
||||
atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
|
||||
memcpy(&clients[i].addr, bt_conn_get_dst(conn), sizeof(bt_addr_le_t));
|
||||
|
||||
/* Send out all pending notifications */
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pacs_bond_deleted(uint8_t id, const bt_addr_le_t *peer)
|
||||
{
|
||||
/* Find the device entry to delete */
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
|
||||
/* Check if match, and if active, if so, reset */
|
||||
if (atomic_test_bit(clients[i].flags, FLAG_ACTIVE) &&
|
||||
bt_addr_le_eq(peer, &clients[i].addr)) {
|
||||
for (size_t j = 0U; j < FLAG_NUM; j++) {
|
||||
atomic_clear_bit(clients[i].flags, j);
|
||||
}
|
||||
(void)memset(&clients[i].addr, 0, sizeof(bt_addr_le_t));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pacs_security_changed(struct bt_conn *conn, bt_security_t level,
|
||||
enum bt_security_err err)
|
||||
{
|
||||
LOG_DBG("%s changed security level to %d", bt_addr_le_str(bt_conn_get_dst(conn)), level);
|
||||
|
||||
if (err != 0 || conn->encrypt == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
|
||||
|
||||
for (size_t j = 0U; j < FLAG_NUM; j++) {
|
||||
if (atomic_test_bit(clients[i].flags, j)) {
|
||||
|
||||
/**
|
||||
* It's enough that one flag is set, as the defer work will go
|
||||
* through all notifiable characteristics
|
||||
*/
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_callbacks = {
|
||||
.security_changed = pacs_security_changed,
|
||||
};
|
||||
|
||||
static struct bt_conn_auth_info_cb auth_callbacks = {
|
||||
.pairing_complete = pacs_auth_pairing_complete,
|
||||
.bond_deleted = pacs_bond_deleted
|
||||
};
|
||||
|
||||
bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
|
||||
{
|
||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
||||
|
@ -786,22 +983,6 @@ bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
|
|||
return false;
|
||||
}
|
||||
|
||||
static sys_slist_t *pacs_get(enum bt_audio_dir dir)
|
||||
{
|
||||
switch (dir) {
|
||||
#if defined(CONFIG_BT_PAC_SNK)
|
||||
case BT_AUDIO_DIR_SINK:
|
||||
return &snk_pacs_list;
|
||||
#endif /* CONFIG_BT_PAC_SNK */
|
||||
#if defined(CONFIG_BT_PAC_SRC)
|
||||
case BT_AUDIO_DIR_SOURCE:
|
||||
return &src_pacs_list;
|
||||
#endif /* CONFIG_BT_PAC_SRC */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
|
||||
{
|
||||
sys_slist_t *pac;
|
||||
|
@ -819,12 +1000,28 @@ void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func,
|
|||
foreach_cap(pac, func, user_data);
|
||||
}
|
||||
|
||||
static void add_bonded_addr_to_client_list(const struct bt_bond_info *info, void *data)
|
||||
{
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
|
||||
/* Check if device is registered, it not, add it */
|
||||
if (!atomic_test_bit(clients[i].flags, FLAG_ACTIVE)) {
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
atomic_set_bit(clients[i].flags, FLAG_ACTIVE);
|
||||
memcpy(&clients[i].addr, &info->addr, sizeof(bt_addr_le_t));
|
||||
bt_addr_le_to_str(&clients[i].addr, addr_str, sizeof(addr_str));
|
||||
LOG_DBG("Added %s to bonded list\n", addr_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Register Audio Capability */
|
||||
int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
|
||||
{
|
||||
const struct bt_audio_codec_cap *codec_cap;
|
||||
static bool callbacks_registered;
|
||||
sys_slist_t *pac;
|
||||
int err;
|
||||
|
||||
if (!cap || !cap->codec_cap) {
|
||||
return -EINVAL;
|
||||
|
@ -837,16 +1034,38 @@ int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Restore bonding list */
|
||||
bt_foreach_bond(BT_ID_DEFAULT, add_bonded_addr_to_client_list, NULL);
|
||||
|
||||
LOG_DBG("cap %p dir %s codec_cap id 0x%02x codec_cap cid 0x%04x codec_cap vid 0x%04x", cap,
|
||||
bt_audio_dir_str(dir), codec_cap->id, codec_cap->cid, codec_cap->vid);
|
||||
|
||||
sys_slist_append(pac, &cap->_node);
|
||||
|
||||
err = pacs_changed(dir);
|
||||
if (err) {
|
||||
return -EINVAL;
|
||||
if (!callbacks_registered) {
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
bt_conn_auth_info_cb_register(&auth_callbacks);
|
||||
|
||||
callbacks_registered = true;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
if (dir == BT_AUDIO_DIR_SINK) {
|
||||
pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE */
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
if (dir == BT_AUDIO_DIR_SOURCE) {
|
||||
pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -870,7 +1089,22 @@ int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
pacs_changed(dir);
|
||||
switch (dir) {
|
||||
#if defined(CONFIG_BT_PAC_SNK_NOTIFIABLE)
|
||||
case BT_AUDIO_DIR_SINK:
|
||||
pacs_set_notify_bit(FLAG_SINK_PAC_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
break;
|
||||
#endif /* CONFIG_BT_PAC_SNK_NOTIFIABLE) */
|
||||
#if defined(CONFIG_BT_PAC_SRC_NOTIFIABLE)
|
||||
case BT_AUDIO_DIR_SOURCE:
|
||||
pacs_set_notify_bit(FLAG_SOURCE_PAC_CHANGED);
|
||||
k_work_submit(&deferred_nfy_work);
|
||||
break;
|
||||
#endif /* CONFIG_BT_PAC_SRC_NOTIFIABLE */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -879,16 +1113,16 @@ int bt_pacs_set_location(enum bt_audio_dir dir, enum bt_audio_location location)
|
|||
{
|
||||
switch (dir) {
|
||||
case BT_AUDIO_DIR_SINK:
|
||||
#if defined(CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE)
|
||||
return set_snk_location(location);
|
||||
#endif /* CONFIG_BT_PAC_SNK_LOC_NOTIFIABLE */
|
||||
set_snk_location(location);
|
||||
break;
|
||||
case BT_AUDIO_DIR_SOURCE:
|
||||
#if defined(CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE)
|
||||
return set_src_location(location);
|
||||
#endif /* CONFIG_BT_PAC_SRC_LOC_NOTIFIABLE */
|
||||
set_src_location(location);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_pacs_set_available_contexts(enum bt_audio_dir dir, enum bt_audio_context contexts)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue