Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/connectivity/bluetooth/shell/audio/ccp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,10 @@ Example Usage when connected

uart:~$ ccp_call_control_client discover
Discovery completed with GTBS and 1 TBS bearers

.. code-block:: console

uart:~$ ccp_call_control_client read_bearer_name
Bearer 0x20046254 name: Generic TBS
uart:~$ ccp_call_control_client read_bearer_name 1
Bearer 0x20046256 name: Telephone Bearer #1
32 changes: 32 additions & 0 deletions include/zephyr/bluetooth/audio/ccp.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,21 @@ struct bt_ccp_call_control_client_cb {
void (*discover)(struct bt_ccp_call_control_client *client, int err,
struct bt_ccp_call_control_client_bearers *bearers);

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
/**
* @brief Callback function for bt_ccp_call_control_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 name The bearer provider name. NULL if @p err is not 0.
*/
void (*bearer_provider_name)(struct bt_ccp_call_control_client_bearer *bearer, int err,
const char *name);
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */

/** @cond INTERNAL_HIDDEN */
/** Internally used field for list handling */
sys_snode_t _node;
Expand Down Expand Up @@ -230,6 +245,23 @@ int bt_ccp_call_control_client_unregister_cb(struct bt_ccp_call_control_client_c
int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *client,
struct bt_ccp_call_control_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_call_control_client could not be identified for @p bearer
* @retval -EBUSY The @ref bt_ccp_call_control_client identified by @p bearer is busy, or the TBS
* instance of @p bearer is busy.
* @retval -ENOTCONN The @ref bt_ccp_call_control_client identified by @p bearer is not connected
*/
int bt_ccp_call_control_client_read_bearer_provider_name(
struct bt_ccp_call_control_client_bearer *bearer);
/** @} */ /* End of group bt_ccp_call_control_client */
#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions samples/bluetooth/ccp_call_control_client/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ CONFIG_BT_CCP_CALL_CONTROL_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
82 changes: 75 additions & 7 deletions samples/bluetooth/ccp_call_control_client/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ LOG_MODULE_REGISTER(ccp_call_control_client, CONFIG_LOG_DEFAULT_LEVEL);
#define SEM_TIMEOUT K_SECONDS(10)

static struct bt_conn *peer_conn;
/* client is not static as it is used for testing purposes */
struct bt_ccp_call_control_client *client;
/* call_control_client is not static as it is used for testing purposes */
struct bt_ccp_call_control_client *call_control_client;
static struct bt_ccp_call_control_client_bearers client_bearers;

static K_SEM_DEFINE(sem_conn_state_change, 0, 1);
Expand Down Expand Up @@ -61,7 +61,7 @@ static void disconnected_cb(struct bt_conn *conn, uint8_t reason)

bt_conn_unref(peer_conn);
peer_conn = NULL;
client = NULL;
call_control_client = NULL;
memset(&client_bearers, 0, sizeof(client_bearers));
k_sem_give(&sem_conn_state_change);
}
Expand Down Expand Up @@ -207,6 +207,21 @@ static void ccp_call_control_client_discover_cb(struct bt_ccp_call_control_clien
k_sem_give(&sem_ccp_action_completed);
}

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
static void ccp_call_control_client_read_bearer_provider_name_cb(
struct bt_ccp_call_control_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_call_control_client(void)
{
int err;
Expand Down Expand Up @@ -244,7 +259,7 @@ static int discover_services(void)

LOG_INF("Discovering GTBS and TBS");

err = bt_ccp_call_control_client_discover(peer_conn, &client);
err = bt_ccp_call_control_client_discover(peer_conn, &call_control_client);
if (err != 0) {
LOG_ERR("Failed to discover: %d", err);
return err;
Expand All @@ -259,13 +274,59 @@ static int discover_services(void)
return 0;
}

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

err = bt_ccp_call_control_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_call_control_client(void)
{
static struct bt_le_scan_cb scan_cbs = {
.recv = scan_recv_cb,
};
static struct bt_ccp_call_control_client_cb ccp_call_control_client_cbs = {
.discover = ccp_call_control_client_discover_cb,
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
.bearer_provider_name = ccp_call_control_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 @@ -323,6 +384,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
130 changes: 130 additions & 0 deletions subsys/bluetooth/audio/ccp_call_control_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ LOG_MODULE_REGISTER(bt_ccp_call_control_client, CONFIG_BT_CCP_CALL_CONTROL_CLIEN

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

static struct bt_tbs_client_cb tbs_client_cbs;

Expand All @@ -53,6 +54,32 @@ struct bt_ccp_call_control_client {

static struct bt_ccp_call_control_client clients[CONFIG_BT_MAX_CONN];

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

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

return NULL;
}

static struct bt_ccp_call_control_client *
get_client_by_bearer(const struct bt_ccp_call_control_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_call_control_client *get_client_by_conn(const struct bt_conn *conn)
{
__ASSERT(bt_conn_is_type(conn, BT_CONN_TYPE_LE), "Invalid connection type for %p", conn);
Expand Down Expand Up @@ -86,6 +113,8 @@ static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
if (client->conn == conn) {
bt_conn_unref(client->conn);
client->conn = NULL;

memset(client->bearers, 0, sizeof(client->bearers));
}
}

Expand Down Expand Up @@ -259,3 +288,104 @@ int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *cl

return 0;
}

#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
static void tbs_client_read_bearer_provider_name_cb(struct bt_conn *conn, int err,
uint8_t inst_index, const char *name)
{
struct bt_ccp_call_control_client *client = get_client_by_conn(conn);
struct bt_ccp_call_control_client_cb *listener, *next;
struct bt_ccp_call_control_client_bearer *bearer;

atomic_clear_bit(client->flags, CCP_CALL_CONTROL_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_call_control_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_call_control_client_bearer *bearer,
struct bt_ccp_call_control_client **client)
{
CHECKIF(bearer == NULL) {
LOG_DBG("bearer is NULL");

return -EINVAL;
}

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

return -EEXIST;
}

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

return -EFAULT;
}

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

return -EBUSY;
}

return 0;
}

int bt_ccp_call_control_client_read_bearer_provider_name(
struct bt_ccp_call_control_client_bearer *bearer)
{
struct bt_ccp_call_control_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_bearer_provider_name_cb;

err = bt_tbs_client_read_bearer_provider_name(client->conn, bearer->tbs_index);
if (err != 0) {
atomic_clear_bit(client->flags, CCP_CALL_CONTROL_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