Bluetooth: Audio: Add PACS location read for unicast client
The unicast client will now read the PACS location value during the discovery procedure, as well as subscribing to it. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
5aa1ab9c1a
commit
df75bfd8f6
|
@ -1301,6 +1301,34 @@ struct bt_audio_stream {
|
|||
sys_snode_t _node;
|
||||
};
|
||||
|
||||
/** Unicast Client callback structure */
|
||||
struct bt_audio_unicast_client_cb {
|
||||
/** @brief Remote Unicast Server Audio Locations
|
||||
*
|
||||
* This callback is called whenever the audio locations is read from
|
||||
* the server or otherwise notified to the client.
|
||||
*
|
||||
* @param conn Connection to the remote unicast server.
|
||||
* @param dir Direction of the location.
|
||||
* @param loc The location bitfield value.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
void (*location)(struct bt_conn *conn, enum bt_audio_dir dir,
|
||||
enum bt_audio_location loc);
|
||||
};
|
||||
|
||||
/** @brief Register unicast client callbacks.
|
||||
*
|
||||
* Only one callback structure can be registered, and attempting to
|
||||
* registering more than one will result in an error.
|
||||
*
|
||||
* @param cb Unicast client callback structure.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
int bt_audio_unicast_client_register_cb(const struct bt_audio_unicast_client_cb *cb);
|
||||
|
||||
/** Unicast Server callback structure */
|
||||
struct bt_audio_unicast_server_cb {
|
||||
/** @brief Endpoint config request callback
|
||||
|
|
|
@ -717,6 +717,17 @@ static struct bt_gatt_cb gatt_callbacks = {
|
|||
.att_mtu_updated = att_mtu_updated,
|
||||
};
|
||||
|
||||
static void unicast_client_location_cb(struct bt_conn *conn,
|
||||
enum bt_audio_dir dir,
|
||||
enum bt_audio_location loc)
|
||||
{
|
||||
printk("dir %u loc %X\n", dir, loc);
|
||||
}
|
||||
|
||||
const struct bt_audio_unicast_client_cb unicast_client_cbs = {
|
||||
.location = unicast_client_location_cb
|
||||
};
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
int err;
|
||||
|
@ -1016,6 +1027,12 @@ void main(void)
|
|||
}
|
||||
printk("Initialized\n");
|
||||
|
||||
err = bt_audio_unicast_client_register_cb(&unicast_client_cbs);
|
||||
if (err != 0) {
|
||||
printk("Failed to register client callbacks: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
reset_data();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/check.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
@ -39,6 +40,8 @@ static struct unicast_client_pac {
|
|||
static const struct bt_uuid *snk_uuid = BT_UUID_PACS_SNK;
|
||||
static const struct bt_uuid *src_uuid = BT_UUID_PACS_SRC;
|
||||
static const struct bt_uuid *pacs_context_uuid = BT_UUID_PACS_SUPPORTED_CONTEXT;
|
||||
static const struct bt_uuid *pacs_snk_loc_uuid = BT_UUID_PACS_SNK_LOC;
|
||||
static const struct bt_uuid *pacs_src_loc_uuid = BT_UUID_PACS_SRC_LOC;
|
||||
static const struct bt_uuid *ase_snk_uuid = BT_UUID_ASCS_ASE_SNK;
|
||||
static const struct bt_uuid *ase_src_uuid = BT_UUID_ASCS_ASE_SRC;
|
||||
static const struct bt_uuid *cp_uuid = BT_UUID_ASCS_ASE_CP;
|
||||
|
@ -49,8 +52,18 @@ static struct bt_audio_iso audio_isos[CONFIG_BT_AUDIO_UNICAST_CLIENT_GROUP_COUNT
|
|||
[CONFIG_BT_AUDIO_UNICAST_CLIENT_GROUP_STREAM_COUNT];
|
||||
|
||||
static struct bt_gatt_subscribe_params cp_subscribe[CONFIG_BT_MAX_CONN];
|
||||
static struct bt_gatt_subscribe_params snk_loc_subscribe[CONFIG_BT_MAX_CONN];
|
||||
static struct bt_gatt_subscribe_params src_loc_subscribe[CONFIG_BT_MAX_CONN];
|
||||
/* TODO: We should be able to reduce the number of discover params for CCCD
|
||||
* discovery, but requires additional work if it is to support an arbitrary
|
||||
* number of EATT bearers, as control point and location CCCD discovery
|
||||
* needs to be done in serial to avoid using the same discover parameters
|
||||
* twice
|
||||
*/
|
||||
static struct bt_gatt_discover_params cp_disc[CONFIG_BT_MAX_CONN];
|
||||
static struct bt_gatt_discover_params loc_cc_disc[CONFIG_BT_MAX_CONN];
|
||||
|
||||
static const struct bt_audio_unicast_client_cb *unicast_client_cbs;
|
||||
|
||||
/* TODO: Move the functions to avoid these prototypes */
|
||||
static int unicast_client_ep_set_metadata(struct bt_audio_ep *ep,
|
||||
|
@ -1895,6 +1908,199 @@ static int unicast_client_ase_discover(struct bt_conn *conn,
|
|||
return bt_gatt_read(conn, ¶ms->read);
|
||||
}
|
||||
|
||||
static uint8_t unicast_client_pacs_location_read_func(struct bt_conn *conn,
|
||||
uint8_t err,
|
||||
struct bt_gatt_read_params *read,
|
||||
const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
struct bt_audio_discover_params *params;
|
||||
struct net_buf_simple buf;
|
||||
uint32_t location;
|
||||
|
||||
params = CONTAINER_OF(read, struct bt_audio_discover_params, read);
|
||||
|
||||
BT_DBG("conn %p err 0x%02x len %u", conn, err, length);
|
||||
|
||||
if (err || data == NULL || length != sizeof(location)) {
|
||||
BT_DBG("Unable to read PACS location for dir %u: %u, %p, %u",
|
||||
params->dir, err, data, length);
|
||||
|
||||
params->func(conn, NULL, NULL, params);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, length);
|
||||
location = net_buf_simple_pull_le32(&buf);
|
||||
|
||||
BT_DBG("dir %u loc %X", params->dir, location);
|
||||
|
||||
if (unicast_client_cbs != NULL && unicast_client_cbs->location != NULL) {
|
||||
unicast_client_cbs->location(conn, params->dir,
|
||||
(enum bt_audio_location)location);
|
||||
}
|
||||
|
||||
/* Read ASE instances */
|
||||
if (unicast_client_ase_discover(conn, params) < 0) {
|
||||
BT_ERR("Unable to read ASE");
|
||||
|
||||
params->func(conn, NULL, NULL, params);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static uint8_t unicast_client_pacs_location_notify_cb(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
struct net_buf_simple buf;
|
||||
enum bt_audio_dir dir;
|
||||
uint32_t location;
|
||||
|
||||
BT_DBG("conn %p len %u", conn, length);
|
||||
|
||||
if (!data) {
|
||||
BT_DBG("Unsubscribed");
|
||||
params->value_handle = 0x0000;
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
/* Terminate early if there's no callbacks */
|
||||
if (unicast_client_cbs == NULL || unicast_client_cbs->location == NULL) {
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, length);
|
||||
|
||||
if (buf.len != sizeof(location)) {
|
||||
BT_ERR("Location notification incorrect size: %u", length);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
if (PART_OF_ARRAY(snk_loc_subscribe, params)) {
|
||||
dir = BT_AUDIO_DIR_SINK;
|
||||
} else if (PART_OF_ARRAY(src_loc_subscribe, params)) {
|
||||
dir = BT_AUDIO_DIR_SOURCE;
|
||||
} else {
|
||||
BT_ERR("Invalid notification");
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, length);
|
||||
location = net_buf_simple_pull_le32(&buf);
|
||||
|
||||
BT_DBG("dir %u loc %X", dir, location);
|
||||
|
||||
if (unicast_client_cbs != NULL && unicast_client_cbs->location != NULL) {
|
||||
unicast_client_cbs->location(conn, dir,
|
||||
(enum bt_audio_location)location);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static int unicast_client_pacs_location_read(struct bt_conn *conn,
|
||||
struct bt_audio_discover_params *params,
|
||||
uint16_t handle)
|
||||
{
|
||||
BT_DBG("conn %p params %p", conn, params);
|
||||
|
||||
params->read.func = unicast_client_pacs_location_read_func;
|
||||
params->read.handle_count = 1U;
|
||||
params->read.single.handle = handle;
|
||||
params->read.single.offset = 0U;
|
||||
|
||||
return bt_gatt_read(conn, ¶ms->read);
|
||||
}
|
||||
|
||||
static uint8_t unicast_client_pacs_location_discover_cb(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
struct bt_gatt_discover_params *discover)
|
||||
{
|
||||
struct bt_audio_discover_params *params;
|
||||
uint8_t index = bt_conn_index(conn);
|
||||
const struct bt_gatt_chrc *chrc;
|
||||
int err;
|
||||
|
||||
params = CONTAINER_OF(discover, struct bt_audio_discover_params,
|
||||
discover);
|
||||
|
||||
if (!attr) {
|
||||
/* If location is not found, we just continue reading the ASEs,
|
||||
* as location is optional.
|
||||
*/
|
||||
|
||||
if (unicast_client_ase_discover(conn, params) < 0) {
|
||||
BT_ERR("Unable to read ASE");
|
||||
|
||||
params->func(conn, NULL, NULL, params);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
chrc = attr->user_data;
|
||||
|
||||
BT_DBG("conn %p attr %p handle 0x%04x", conn, attr, chrc->value_handle);
|
||||
|
||||
if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
|
||||
struct bt_gatt_subscribe_params *sub_params;
|
||||
|
||||
if (params->dir == BT_AUDIO_DIR_SINK) {
|
||||
sub_params = &snk_loc_subscribe[index];
|
||||
} else {
|
||||
sub_params = &src_loc_subscribe[index];
|
||||
}
|
||||
|
||||
sub_params->value_handle = chrc->value_handle;
|
||||
sub_params->ccc_handle = 0x0000; /* auto discover ccc */
|
||||
sub_params->end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
sub_params->disc_params = &loc_cc_disc[index];
|
||||
sub_params->notify = unicast_client_pacs_location_notify_cb;
|
||||
sub_params->value = BT_GATT_CCC_NOTIFY;
|
||||
|
||||
err = bt_gatt_subscribe(conn, sub_params);
|
||||
if (err != 0) {
|
||||
BT_ERR("Failed to subscribe to location: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
err = unicast_client_pacs_location_read(conn, params, chrc->value_handle);
|
||||
if (err != 0) {
|
||||
BT_DBG("Failed to read PACS location: %d", err);
|
||||
params->err = err;
|
||||
|
||||
params->func(conn, NULL, NULL, params);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static int unicast_client_pacs_location_discover(struct bt_conn *conn,
|
||||
struct bt_audio_discover_params *params)
|
||||
{
|
||||
BT_DBG("conn %p params %p", conn, params);
|
||||
|
||||
if (params->dir == BT_AUDIO_DIR_SINK) {
|
||||
params->discover.uuid = pacs_snk_loc_uuid;
|
||||
} else if (params->dir == BT_AUDIO_DIR_SOURCE) {
|
||||
params->discover.uuid = pacs_src_loc_uuid;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
params->discover.func = unicast_client_pacs_location_discover_cb;
|
||||
params->discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
||||
params->discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
params->discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
return bt_gatt_discover(conn, ¶ms->discover);
|
||||
}
|
||||
|
||||
static uint8_t unicast_client_pacs_context_read_func(struct bt_conn *conn,
|
||||
uint8_t err,
|
||||
struct bt_gatt_read_params *read,
|
||||
|
@ -1911,7 +2117,7 @@ static uint8_t unicast_client_pacs_context_read_func(struct bt_conn *conn,
|
|||
BT_DBG("conn %p err 0x%02x len %u", conn, err, length);
|
||||
|
||||
if (err || length < sizeof(uint16_t) * 2) {
|
||||
goto discover_ase;
|
||||
goto discover_loc;
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, length);
|
||||
|
@ -1941,10 +2147,10 @@ static uint8_t unicast_client_pacs_context_read_func(struct bt_conn *conn,
|
|||
BT_DBG("pac %p context 0x%04x", pac, pac->context);
|
||||
}
|
||||
|
||||
discover_ase:
|
||||
discover_loc:
|
||||
/* Read ASE instances */
|
||||
if (unicast_client_ase_discover(conn, params) < 0) {
|
||||
BT_ERR("Unable to read ASE");
|
||||
if (unicast_client_pacs_location_discover(conn, params) < 0) {
|
||||
BT_ERR("Unable to read PACS location");
|
||||
|
||||
params->func(conn, NULL, NULL, params);
|
||||
}
|
||||
|
@ -2170,3 +2376,21 @@ int bt_audio_discover(struct bt_conn *conn,
|
|||
|
||||
return bt_gatt_read(conn, ¶ms->read);
|
||||
}
|
||||
|
||||
|
||||
int bt_audio_unicast_client_register_cb(const struct bt_audio_unicast_client_cb *cbs)
|
||||
{
|
||||
CHECKIF(cbs == NULL) {
|
||||
BT_DBG("cbs is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (unicast_client_cbs != NULL) {
|
||||
BT_DBG("Callbacks already registered");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
unicast_client_cbs = cbs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -590,9 +590,21 @@ static void discover_all(struct bt_conn *conn, struct bt_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
static void unicast_client_location_cb(struct bt_conn *conn,
|
||||
enum bt_audio_dir dir,
|
||||
enum bt_audio_location loc)
|
||||
{
|
||||
shell_print(ctx_shell, "dir %u loc %X\n", dir, loc);
|
||||
}
|
||||
|
||||
const struct bt_audio_unicast_client_cb unicast_client_cbs = {
|
||||
.location = unicast_client_location_cb
|
||||
};
|
||||
|
||||
static int cmd_discover(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
static struct bt_audio_discover_params params;
|
||||
static bool cbs_registered;
|
||||
|
||||
if (!default_conn) {
|
||||
shell_error(sh, "Not connected");
|
||||
|
@ -604,6 +616,17 @@ static int cmd_discover(const struct shell *sh, size_t argc, char *argv[])
|
|||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (!cbs_registered) {
|
||||
int err = bt_audio_unicast_client_register_cb(&unicast_client_cbs);
|
||||
|
||||
if (err != 0) {
|
||||
shell_error(sh, "Failed to register unicast client callbacks: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
cbs_registered = true;
|
||||
}
|
||||
|
||||
params.func = discover_all;
|
||||
params.dir = BT_AUDIO_DIR_SINK;
|
||||
|
||||
|
|
|
@ -95,6 +95,17 @@ static struct bt_audio_stream_ops stream_ops = {
|
|||
.released = stream_released,
|
||||
};
|
||||
|
||||
static void unicast_client_location_cb(struct bt_conn *conn,
|
||||
enum bt_audio_dir dir,
|
||||
enum bt_audio_location loc)
|
||||
{
|
||||
printk("dir %u loc %X\n", dir, loc);
|
||||
}
|
||||
|
||||
const struct bt_audio_unicast_client_cb unicast_client_cbs = {
|
||||
.location = unicast_client_location_cb
|
||||
};
|
||||
|
||||
static void add_remote_sink(struct bt_audio_ep *ep, uint8_t index)
|
||||
{
|
||||
printk("Sink #%u: ep %p\n", index, ep);
|
||||
|
@ -207,6 +218,12 @@ static void init(void)
|
|||
}
|
||||
|
||||
bt_gatt_cb_register(&gatt_callbacks);
|
||||
|
||||
err = bt_audio_unicast_client_register_cb(&unicast_client_cbs);
|
||||
if (err != 0) {
|
||||
FAIL("Failed to register client callbacks: %d", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_and_connect(void)
|
||||
|
|
Loading…
Reference in a new issue