bluetooth: audio: Rework PACS notify system

This commits eliminates the old way of
pacs notifications, replacing k_work with
simple bt_gatt_notify.

Signed-off-by: Szymon Czapracki <szymon.czapracki@codecoup.pl>
This commit is contained in:
Szymon Czapracki 2023-03-15 22:36:16 +01:00 committed by Fabio Baltieri
commit 42a402abe3

View file

@ -21,6 +21,7 @@
#include <zephyr/bluetooth/gatt.h> #include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/audio/audio.h> #include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/pacs.h> #include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/sys/slist.h>
#include "../host/conn_internal.h" #include "../host/conn_internal.h"
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
@ -36,25 +37,15 @@ LOG_MODULE_REGISTER(bt_pacs, CONFIG_BT_PACS_LOG_LEVEL);
#define PAC_NOTIFY_TIMEOUT K_MSEC(10) #define PAC_NOTIFY_TIMEOUT K_MSEC(10)
#define READ_BUF_SEM_TIMEOUT K_MSEC(50) #define READ_BUF_SEM_TIMEOUT K_MSEC(50)
#define PACS(_name, _work_handler) \ #if defined(CONFIG_BT_PAC_SRC)
struct pacs _name = { \ static uint32_t pacs_src_location;
.work = Z_WORK_DELAYABLE_INITIALIZER(_work_handler), \ static sys_slist_t src_pacs_list = SYS_SLIST_STATIC_INIT(&src_pacs_list);
}; #endif /* CONFIG_BT_PAC_SRC */
#define PACS_LOCATION(_name, _work_handler) \ #if defined(CONFIG_BT_PAC_SNK)
struct pacs_location _name = { \ static uint32_t pacs_snk_location;
.work = Z_WORK_DELAYABLE_INITIALIZER(_work_handler), \ static sys_slist_t snk_pacs_list = SYS_SLIST_STATIC_INIT(&snk_pacs_list);
}; #endif /* CONFIG_BT_PAC_SNK */
struct pacs_location {
struct k_work_delayable work;
uint32_t location;
};
struct pacs {
struct k_work_delayable work;
sys_slist_t list;
};
#if defined(CONFIG_BT_PAC_SNK) #if defined(CONFIG_BT_PAC_SNK)
static uint16_t snk_available_contexts; static uint16_t snk_available_contexts;
@ -231,10 +222,8 @@ static ssize_t supported_context_read(struct bt_conn *conn,
sizeof(context)); sizeof(context));
} }
static void available_contexts_notify(struct k_work *work); static int available_contexts_notify(void);
static void supported_contexts_notify(struct k_work *work); static int supported_contexts_notify(void);
static K_WORK_DELAYABLE_DEFINE(available_contexts_work, available_contexts_notify);
static K_WORK_DELAYABLE_DEFINE(supported_contexts_work, supported_contexts_notify);
static int set_available_contexts(uint16_t contexts, uint16_t *available, static int set_available_contexts(uint16_t contexts, uint16_t *available,
uint16_t supported) uint16_t supported)
@ -249,8 +238,8 @@ static int set_available_contexts(uint16_t contexts, uint16_t *available,
return 0; return 0;
} }
err = k_work_reschedule(&available_contexts_work, PAC_NOTIFY_TIMEOUT); err = available_contexts_notify();
if (err < 0) { if (err) {
return err; return err;
} }
@ -263,6 +252,8 @@ static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
uint16_t *available) uint16_t *available)
{ {
int err; int err;
uint16_t tmp_supported = *supported;
uint16_t tmp_available = *available;
/* Ensure unspecified is always supported */ /* Ensure unspecified is always supported */
contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED; contexts |= BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED;
@ -271,30 +262,32 @@ static int set_supported_contexts(uint16_t contexts, uint16_t *supported,
return 0; return 0;
} }
err = k_work_reschedule(&supported_contexts_work, PAC_NOTIFY_TIMEOUT);
if (err < 0) {
return err;
}
*supported = contexts; *supported = contexts;
/* Update available contexts if needed*/ /* Update available contexts if needed*/
if ((contexts & *available) != *available) { if ((contexts & *available) != *available) {
*available = *available & contexts; err = set_available_contexts(contexts & *available, available, contexts);
err = k_work_reschedule(&available_contexts_work, if (err) {
PAC_NOTIFY_TIMEOUT); *available = tmp_available;
if (err < 0) { *supported = tmp_supported;
LOG_WRN("Update available contexts notify failed: %d", err);
return err;
} }
} }
err = supported_contexts_notify();
if (err) {
*supported = tmp_supported;
*available = tmp_available;
return err;
}
return 0; return 0;
} }
#if defined(CONFIG_BT_PAC_SNK) #if defined(CONFIG_BT_PAC_SNK)
static void pac_notify_snk(struct k_work *work); static int pac_notify_snk(void);
static PACS(snk_pacs, pac_notify_snk);
static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) void *buf, uint16_t len, uint16_t offset)
{ {
@ -310,7 +303,7 @@ static ssize_t snk_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES); return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
} }
get_pac_records(&snk_pacs.list, &read_buf); get_pac_records(&snk_pacs_list, &read_buf);
ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data, ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
read_buf.len); read_buf.len);
@ -349,14 +342,12 @@ static inline int set_snk_supported_contexts(uint16_t contexts)
#endif /* CONFIG_BT_PAC_SNK */ #endif /* CONFIG_BT_PAC_SNK */
#if defined(CONFIG_BT_PAC_SNK_LOC) #if defined(CONFIG_BT_PAC_SNK_LOC)
static void pac_notify_snk_loc(struct k_work *work); static int pac_notify_snk_loc(void);
static PACS_LOCATION(snk_location, pac_notify_snk_loc);
static ssize_t snk_loc_read(struct bt_conn *conn, static ssize_t snk_loc_read(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf, const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset) uint16_t len, uint16_t offset)
{ {
uint32_t location = sys_cpu_to_le32(snk_location.location); uint32_t location = sys_cpu_to_le32(pacs_snk_location);
LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset); LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
@ -371,15 +362,17 @@ static void snk_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
static int set_snk_location(enum bt_audio_location audio_location) static int set_snk_location(enum bt_audio_location audio_location)
{ {
if (audio_location == snk_location.location) { int err;
if (audio_location == pacs_snk_location) {
return 0; return 0;
} }
snk_location.location = audio_location; pacs_snk_location = audio_location;
k_work_reschedule(&snk_location.work, PAC_NOTIFY_TIMEOUT); err = pac_notify_snk_loc();
return 0; return err;
} }
#else #else
static int set_snk_location(enum bt_audio_location location) static int set_snk_location(enum bt_audio_location location)
@ -421,9 +414,7 @@ static ssize_t snk_loc_write(struct bt_conn *conn,
#endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */ #endif /* CONFIG_BT_PAC_SNK_LOC_WRITEABLE */
#if defined(CONFIG_BT_PAC_SRC) #if defined(CONFIG_BT_PAC_SRC)
static void pac_notify_src(struct k_work *work); static int pac_notify_src(void);
static PACS(src_pacs, pac_notify_src);
static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) void *buf, uint16_t len, uint16_t offset)
{ {
@ -439,7 +430,7 @@ static ssize_t src_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES); return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
} }
get_pac_records(&src_pacs.list, &read_buf); get_pac_records(&src_pacs_list, &read_buf);
ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data, ret_val = bt_gatt_attr_read(conn, attr, buf, len, offset, read_buf.data,
read_buf.len); read_buf.len);
@ -478,14 +469,12 @@ static inline int set_src_supported_contexts(uint16_t contexts)
#endif /* CONFIG_BT_PAC_SRC */ #endif /* CONFIG_BT_PAC_SRC */
#if defined(CONFIG_BT_PAC_SRC_LOC) #if defined(CONFIG_BT_PAC_SRC_LOC)
static void pac_notify_src_loc(struct k_work *work); static int pac_notify_src_loc(void);
static PACS_LOCATION(src_location, pac_notify_src_loc);
static ssize_t src_loc_read(struct bt_conn *conn, static ssize_t src_loc_read(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf, const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset) uint16_t len, uint16_t offset)
{ {
uint32_t location = sys_cpu_to_le32(src_location.location); uint32_t location = sys_cpu_to_le32(pacs_src_location);
LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset); LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset);
@ -500,13 +489,23 @@ static void src_loc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
static int set_src_location(enum bt_audio_location audio_location) static int set_src_location(enum bt_audio_location audio_location)
{ {
if (audio_location == src_location.location) { int err;
enum bt_audio_location tmp_audio_location = audio_location;
if (audio_location == pacs_src_location) {
return 0; return 0;
} }
src_location.location = audio_location; pacs_src_location = audio_location;
k_work_reschedule(&src_location.work, PAC_NOTIFY_TIMEOUT); err = pac_notify_src_loc();
if (err) {
LOG_DBG("Notify failed, previous audio location restored");
audio_location = tmp_audio_location;
return err;
}
return 0; return 0;
} }
@ -606,53 +605,52 @@ BT_GATT_SERVICE_DEFINE(pacs_svc,
); );
#if defined(CONFIG_BT_PAC_SNK_LOC) #if defined(CONFIG_BT_PAC_SNK_LOC)
static void pac_notify_snk_loc(struct k_work *work) static int pac_notify_snk_loc(void)
{ {
struct pacs_location *location = CONTAINER_OF(work, struct pacs_location, work); uint32_t location_le = sys_cpu_to_le32(pacs_snk_location);
uint32_t location_le = sys_cpu_to_le32(location->location);
int err; int err;
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK_LOC, pacs_svc.attrs, &location_le, err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK_LOC, pacs_svc.attrs, &location_le,
sizeof(location_le)); sizeof(location_le));
if (err != 0 && err != -ENOTCONN) { if (err != 0 && err != -ENOTCONN) {
LOG_WRN("PACS notify_loc failed: %d", err); LOG_WRN("PACS notify_loc failed: %d", err);
return err;
} }
return 0;
} }
#endif /* CONFIG_BT_PAC_SNK_LOC */ #endif /* CONFIG_BT_PAC_SNK_LOC */
#if defined(CONFIG_BT_PAC_SRC_LOC) #if defined(CONFIG_BT_PAC_SRC_LOC)
static void pac_notify_src_loc(struct k_work *work) static int pac_notify_src_loc(void)
{ {
struct pacs_location *location = CONTAINER_OF(work, struct pacs_location, work); uint32_t location_le = sys_cpu_to_le32(pacs_src_location);
uint32_t location_le = sys_cpu_to_le32(location->location);
int err; int err;
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC_LOC, pacs_svc.attrs, &location_le, err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC_LOC, pacs_svc.attrs, &location_le,
sizeof(location_le)); sizeof(location_le));
if (err != 0 && err != -ENOTCONN) { if (err != 0 && err != -ENOTCONN) {
LOG_WRN("PACS notify_loc failed: %d", err); LOG_WRN("PACS notify_loc failed: %d", err);
return err;
} }
return 0;
} }
#endif /* CONFIG_BT_PAC_SRC_LOC */ #endif /* CONFIG_BT_PAC_SRC_LOC */
#if defined(CONFIG_BT_PAC_SNK) #if defined(CONFIG_BT_PAC_SNK)
static void pac_notify_snk(struct k_work *work) static int pac_notify_snk(void)
{ {
struct pacs *pac = CONTAINER_OF(work, struct pacs, work); int err = 0;
int err;
err = k_sem_take(&read_buf_sem, K_NO_WAIT); err = k_sem_take(&read_buf_sem, K_NO_WAIT);
if (err != 0) { if (err != 0) {
LOG_DBG("Failed to take read_buf_sem: %d", err); LOG_DBG("Failed to take read_buf_sem: %d", err);
/* Try again later */ return err;
k_work_reschedule(k_work_delayable_from_work(work),
PAC_NOTIFY_TIMEOUT);
return;
} }
get_pac_records(&pac->list, &read_buf); get_pac_records(&snk_pacs_list, &read_buf);
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK, pacs_svc.attrs, err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SNK, pacs_svc.attrs,
read_buf.data, read_buf.len); read_buf.data, read_buf.len);
@ -661,27 +659,28 @@ static void pac_notify_snk(struct k_work *work)
} }
k_sem_give(&read_buf_sem); k_sem_give(&read_buf_sem);
if (err == -ENOTCONN) {
return 0;
} else {
return 0;
}
} }
#endif /* CONFIG_BT_PAC_SNK */ #endif /* CONFIG_BT_PAC_SNK */
#if defined(CONFIG_BT_PAC_SRC) #if defined(CONFIG_BT_PAC_SRC)
static void pac_notify_src(struct k_work *work) static int pac_notify_src(void)
{ {
struct pacs *pac = CONTAINER_OF(work, struct pacs, work);
int err = 0; int err = 0;
err = k_sem_take(&read_buf_sem, K_NO_WAIT); err = k_sem_take(&read_buf_sem, K_NO_WAIT);
if (err != 0) { if (err != 0) {
LOG_DBG("Failed to take read_buf_sem: %d", err); LOG_DBG("Failed to take read_buf_sem: %d", err);
/* Try again later */ return err;
k_work_reschedule(k_work_delayable_from_work(work),
PAC_NOTIFY_TIMEOUT);
return;
} }
get_pac_records(&pac->list, &read_buf); get_pac_records(&src_pacs_list, &read_buf);
err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC, pacs_svc.attrs, err = bt_gatt_notify_uuid(NULL, BT_UUID_PACS_SRC, pacs_svc.attrs,
read_buf.data, read_buf.len); read_buf.data, read_buf.len);
@ -690,15 +689,33 @@ static void pac_notify_src(struct k_work *work)
} }
k_sem_give(&read_buf_sem); k_sem_give(&read_buf_sem);
if (err == -ENOTCONN) {
return 0;
} else {
return 0;
}
} }
#endif /* CONFIG_BT_PAC_SRC */ #endif /* CONFIG_BT_PAC_SRC */
static void pacs_changed(struct pacs *caps) static int pacs_changed(enum bt_audio_dir dir)
{ {
k_work_reschedule(&caps->work, PAC_NOTIFY_TIMEOUT); int err = 0;
if (dir == BT_AUDIO_DIR_SINK) {
#if defined(CONFIG_BT_PAC_SNK)
err = pac_notify_snk();
#endif /* CONFIG_BT_PAC_SNK */
} else {
#if defined(CONFIG_BT_PAC_SRC)
err = pac_notify_src();
#endif /* CONFIG_BT_PAC_SRC */
}
return err;
} }
static void available_contexts_notify(struct k_work *work) static int available_contexts_notify(void)
{ {
struct bt_pacs_context context = { struct bt_pacs_context context = {
.snk = sys_cpu_to_le16(snk_available_contexts), .snk = sys_cpu_to_le16(snk_available_contexts),
@ -710,10 +727,13 @@ static void available_contexts_notify(struct k_work *work)
&context, sizeof(context)); &context, sizeof(context));
if (err != 0 && err != -ENOTCONN) { if (err != 0 && err != -ENOTCONN) {
LOG_WRN("Available Audio Contexts notify failed: %d", err); LOG_WRN("Available Audio Contexts notify failed: %d", err);
return err;
} }
return 0;
} }
static void supported_contexts_notify(struct k_work *work) static int supported_contexts_notify(void)
{ {
struct bt_pacs_context context = { struct bt_pacs_context context = {
.snk = sys_cpu_to_le16(snk_supported_contexts), .snk = sys_cpu_to_le16(snk_supported_contexts),
@ -725,7 +745,11 @@ static void supported_contexts_notify(struct k_work *work)
&context, sizeof(context)); &context, sizeof(context));
if (err != 0 && err != -ENOTCONN) { if (err != 0 && err != -ENOTCONN) {
LOG_WRN("Supported Audio Contexts notify failed: %d", err); LOG_WRN("Supported Audio Contexts notify failed: %d", err);
return err;
} }
return 0;
} }
bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context) bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
@ -741,16 +765,16 @@ bool bt_pacs_context_available(enum bt_audio_dir dir, uint16_t context)
return false; return false;
} }
static struct pacs *pacs_get(enum bt_audio_dir dir) static sys_slist_t *pacs_get(enum bt_audio_dir dir)
{ {
switch (dir) { switch (dir) {
#if defined(CONFIG_BT_PAC_SNK) #if defined(CONFIG_BT_PAC_SNK)
case BT_AUDIO_DIR_SINK: case BT_AUDIO_DIR_SINK:
return &snk_pacs; return &snk_pacs_list;
#endif /* CONFIG_BT_PAC_SNK */ #endif /* CONFIG_BT_PAC_SNK */
#if defined(CONFIG_BT_PAC_SRC) #if defined(CONFIG_BT_PAC_SRC)
case BT_AUDIO_DIR_SOURCE: case BT_AUDIO_DIR_SOURCE:
return &src_pacs; return &src_pacs_list;
#endif /* CONFIG_BT_PAC_SRC */ #endif /* CONFIG_BT_PAC_SRC */
default: default:
return NULL; return NULL;
@ -759,7 +783,7 @@ static struct pacs *pacs_get(enum bt_audio_dir dir)
void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data) void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func, void *user_data)
{ {
struct pacs *pac; sys_slist_t *pac;
CHECKIF(func == NULL) { CHECKIF(func == NULL) {
LOG_ERR("func is NULL"); LOG_ERR("func is NULL");
@ -771,14 +795,15 @@ void bt_pacs_cap_foreach(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func,
return; return;
} }
foreach_cap(&pac->list, func, user_data); foreach_cap(pac, func, user_data);
} }
/* Register Audio Capability */ /* Register Audio Capability */
int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap) int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
{ {
const struct bt_audio_codec_cap *codec_cap; const struct bt_audio_codec_cap *codec_cap;
struct pacs *pac; sys_slist_t *pac;
int err;
if (!cap || !cap->codec_cap) { if (!cap || !cap->codec_cap) {
return -EINVAL; return -EINVAL;
@ -794,9 +819,12 @@ int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
LOG_DBG("cap %p dir %s codec_cap id 0x%02x codec_cap cid 0x%04x codec_cap vid 0x%04x", cap, 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); bt_audio_dir_str(dir), codec_cap->id, codec_cap->cid, codec_cap->vid);
sys_slist_append(&pac->list, &cap->_node); sys_slist_append(pac, &cap->_node);
pacs_changed(pac); err = pacs_changed(dir);
if (err) {
return -EINVAL;
}
return 0; return 0;
} }
@ -804,7 +832,7 @@ int bt_pacs_cap_register(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
/* Unregister Audio Capability */ /* Unregister Audio Capability */
int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap) int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
{ {
struct pacs *pac; sys_slist_t *pac;
if (!cap) { if (!cap) {
return -EINVAL; return -EINVAL;
@ -817,11 +845,11 @@ int bt_pacs_cap_unregister(enum bt_audio_dir dir, struct bt_pacs_cap *cap)
LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir)); LOG_DBG("cap %p dir %s", cap, bt_audio_dir_str(dir));
if (!sys_slist_find_and_remove(&pac->list, &cap->_node)) { if (!sys_slist_find_and_remove(pac, &cap->_node)) {
return -ENOENT; return -ENOENT;
} }
pacs_changed(pac); pacs_changed(dir);
return 0; return 0;
} }