Skip to content

Commit

Permalink
Bluetooth: CCP: Client: Add support for get provider name
Browse files Browse the repository at this point in the history
Add support for getting the remote bearer provider
name.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
  • Loading branch information
Thalley committed Oct 9, 2024
1 parent 4f9fb6f commit 2cd92cd
Show file tree
Hide file tree
Showing 16 changed files with 618 additions and 26 deletions.
7 changes: 7 additions & 0 deletions doc/connectivity/bluetooth/api/audio/shell/ccp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,10 @@ Example Usage when connected
uart:~$ ccp_client discover
Discovery completed with GTBS and 1 TBS bearers
.. code-block:: console
uart:~$ ccp_client read_bearer_name
Bearer 0x20046254 name: Generic TBS
uart:~$ ccp_client read_bearer_name 1
Bearer 0x20046256 name: Telephone Bearer #1
33 changes: 33 additions & 0 deletions include/zephyr/bluetooth/audio/ccp.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,23 @@ struct bt_ccp_client_cb {
void (*discover)(struct bt_ccp_client *client, int err,
struct bt_ccp_client_bearers *bearers);

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
/**
* @brief Callback function for bt_ccp_client_read_bearer_provider_name().
*
* This callback is called once the read bearer provider name procedure is completed.
*
* @param client Call Control Client instance pointer.
* @param err Error value. 0 on success, GATT error on positive
* value or errno on negative value.
* @param tbs_count Number of Telephone Bearer Service Service instances
* on the remote Call Control Server.
* @param gtbs_found Whether or not the server has a Generic TBS instance.
*/
void (*bearer_provider_name)(struct bt_ccp_client_bearer *bearer, int err,
const char *name);
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */

/** @internal Internally used field for list handling */
sys_snode_t _node;
};
Expand Down Expand Up @@ -202,6 +219,22 @@ int bt_ccp_client_unregister_cb(struct bt_ccp_client_cb *cb);
*/
int bt_ccp_client_get_bearers(struct bt_ccp_client *client, struct bt_ccp_client_bearers *bearers);

/**
* @brief Read the bearer provider name of a remote TBS bearer.
*
* @kconfig_dep{CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME}
*
* @param bearer The bearer to read the name from
*
* @retval 0 Success
* @retval -EINVAL @p bearer is NULL
* @retval -EFAULT @p bearer has not been discovered
* @retval -EEXIST A @ref bt_ccp_client could not be identified for @p bearer
* @retval -EBUSY The @ref bt_ccp_client identified by @p bearer is busy, or the TBS instance
* of @p bearer is busy.
* @retval -ENOTCONN The @ref bt_ccp_client identified by @p bearer is not connected
*/
int bt_ccp_client_read_bearer_provider_name(struct bt_ccp_client_bearer *bearer);
/** @} */ /* End of group bt_ccp_client */
#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions samples/bluetooth/ccp_client/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CONFIG_BT_CCP_CLIENT_BEARER_COUNT=2
CONFIG_BT_TBS_CLIENT_GTBS=y
CONFIG_BT_TBS_CLIENT_TBS=y
CONFIG_BT_TBS_CLIENT_MAX_TBS_INSTANCES=1
CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME=y
CONFIG_UTF8=y

# TBS Client may require up to 12 buffers
Expand Down
74 changes: 71 additions & 3 deletions samples/bluetooth/ccp_client/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,21 @@ static void ccp_client_discover_cb(struct bt_ccp_client *client, int err,
k_sem_give(&sem_ccp_action_completed);
}

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
static void ccp_client_read_bearer_provider_name_cb(struct bt_ccp_client_bearer *bearer, int err,
const char *name)
{
if (err != 0) {
LOG_ERR("Failed to read bearer %p provider name: %d\n", (void *)bearer, err);
return;
}

LOG_INF("Bearer %p provider name: %s", (void *)bearer, name);

k_sem_give(&sem_ccp_action_completed);
}
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */

static int reset_ccp_client(void)
{
int err;
Expand Down Expand Up @@ -264,13 +279,59 @@ static int discover_services(void)
return 0;
}

static int read_bearer_name(struct bt_ccp_client_bearer *bearer)
{
int err;

err = bt_ccp_client_read_bearer_provider_name(bearer);
if (err != 0) {
return err;
}

err = k_sem_take(&sem_ccp_action_completed, SEM_TIMEOUT);
if (err != 0) {
LOG_ERR("Failed to take sem_ccp_action_completed: %d", err);
return err;
}

return 0;
}

static int read_bearer_names(void)
{
int err;

#if defined(CONFIG_BT_TBS_CLIENT_GTBS)
err = read_bearer_name(client_bearers.gtbs_bearer);
if (err != 0) {
LOG_ERR("Failed to read name for GTBS bearer: %d", err);
return err;
}
#endif /* CONFIG_BT_TBS_CLIENT_GTBS */

#if defined(CONFIG_BT_TBS_CLIENT_TBS)
for (size_t i = 0; i < client_bearers.tbs_count; i++) {
err = read_bearer_name(client_bearers.tbs_bearers[i]);
if (err != 0) {
LOG_ERR("Failed to read name for bearer[%zu]: %d", i, err);
return err;
}
}
#endif /* CONFIG_BT_TBS_CLIENT_TBS */

return 0;
}

static int init_ccp_client(void)
{
static struct bt_le_scan_cb scan_cbs = {
.recv = scan_recv_cb,
};
static struct bt_ccp_client_cb ccp_client_cbs = {
.discover = ccp_client_discover_cb,
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
.bearer_provider_name = ccp_client_read_bearer_provider_name_cb
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */
};
static struct bt_le_scan_cb scan_cbs = {
.recv = scan_recv_cb,
};
int err;

Expand Down Expand Up @@ -328,6 +389,13 @@ int main(void)
continue;
}

if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) {
err = read_bearer_names();
if (err != 0) {
continue;
}
}

/* Reset if disconnected */
err = k_sem_take(&sem_conn_state_change, K_FOREVER);
if (err != 0) {
Expand Down
126 changes: 126 additions & 0 deletions subsys/bluetooth/audio/ccp_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
LOG_MODULE_REGISTER(bt_ccp_client, CONFIG_BT_CCP_CLIENT_LOG_LEVEL);

static sys_slist_t ccp_client_cbs = SYS_SLIST_STATIC_INIT(&ccp_client_cbs);
static struct bt_tbs_client_cb tbs_client_cbs;

static struct bt_tbs_client_cb tbs_client_cbs;

Expand All @@ -50,6 +51,31 @@ struct bt_ccp_client {

static struct bt_ccp_client clients[CONFIG_BT_MAX_CONN];

static struct bt_ccp_client_bearer *get_bearer_by_tbs_index(struct bt_ccp_client *client,
uint8_t index)
{
for (size_t i = 0U; i < ARRAY_SIZE(client->bearers); i++) {
struct bt_ccp_client_bearer *bearer = &client->bearers[i];

if (bearer->discovered && bearer->tbs_index == index) {
return bearer;
}
}

return NULL;
}

static struct bt_ccp_client *get_client_by_bearer(const struct bt_ccp_client_bearer *bearer)
{
for (size_t i = 0U; i < ARRAY_SIZE(clients); i++) {
if (IS_ARRAY_ELEMENT(clients[i].bearers, bearer)) {
return &clients[i];
}
}

return NULL;
}

static struct bt_ccp_client *get_client_by_conn(const struct bt_conn *conn)
{
return &clients[bt_conn_index(conn)];
Expand Down Expand Up @@ -246,3 +272,103 @@ int bt_ccp_client_get_bearers(struct bt_ccp_client *client, struct bt_ccp_client

return 0;
}

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
static void tbs_client_read_string_cb(struct bt_conn *conn, int err, uint8_t inst_index,
const char *name)
{
struct bt_ccp_client *client = get_client_by_conn(conn);
struct bt_ccp_client_cb *listener, *next;
struct bt_ccp_client_bearer *bearer;

atomic_clear_bit(client->flags, CCP_CLIENT_FLAG_BUSY);

bearer = get_bearer_by_tbs_index(client, inst_index);
if (bearer == NULL) {
LOG_DBG("Could not lookup bearer for client %p and index 0x%02X", client,
inst_index);

return;
}

SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ccp_client_cbs, listener, next, _node) {
if (listener->bearer_provider_name != NULL) {
listener->bearer_provider_name(bearer, err, name);
}
}
}

/**
* @brief Validates a bearer and provides a client with ownership of the busy flag
*
* @param[in] bearer The bearer to validate
* @param[out] client A client identified by the @p bearer with the busy flag set if return
* value is 0.
*
* @return 0 if the bearer is valid and the @p client has been populated, else an error.
*/
static int validate_bearer_and_get_client(const struct bt_ccp_client_bearer *bearer,
struct bt_ccp_client **client)
{
CHECKIF(bearer == NULL) {
LOG_DBG("bearer is NULL");

return -EINVAL;
}

if (!bearer->discovered) {
LOG_DBG("bearer %p is not discovered", bearer);

return -EFAULT;
}

*client = get_client_by_bearer(bearer);
if (*client == NULL) {
LOG_DBG("Could not identify client from bearer %p", bearer);

return -EEXIST;
}

if (atomic_test_and_set_bit((*client)->flags, CCP_CLIENT_FLAG_BUSY)) {
LOG_DBG("Client %p identified by bearer %p is busy", *client, bearer);

return -EBUSY;
}

return 0;
}

int bt_ccp_client_read_bearer_provider_name(struct bt_ccp_client_bearer *bearer)
{
struct bt_ccp_client *client;
int err;

err = validate_bearer_and_get_client(bearer, &client);
if (err != 0) {
return err;
}

tbs_client_cbs.bearer_provider_name = tbs_client_read_string_cb;

err = bt_tbs_client_read_bearer_provider_name(client->conn, bearer->tbs_index);
if (err != 0) {
atomic_clear_bit(client->flags, CCP_CLIENT_FLAG_BUSY);

/* Return expected return values directly */
if (err == -ENOTCONN || err == -EBUSY) {
LOG_DBG("bt_tbs_client_read_bearer_provider_name returned %d", err);

return err;
}

/* Assert if the return value is -EINVAL as that means we are missing a check */
__ASSERT(err != -EINVAL, "err shall not be -EINVAL");

LOG_DBG("Unexpected error from bt_tbs_client_read_bearer_provider_name: %d", err);

return -ENOEXEC;
}

return 0;
}
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */
Loading

0 comments on commit 2cd92cd

Please sign in to comment.