diff --git a/doc/connectivity/bluetooth/shell/audio/ccp.rst b/doc/connectivity/bluetooth/shell/audio/ccp.rst index e5f81db914ad7..bc18e000151e7 100644 --- a/doc/connectivity/bluetooth/shell/audio/ccp.rst +++ b/doc/connectivity/bluetooth/shell/audio/ccp.rst @@ -24,6 +24,7 @@ to :code:`0` which is the GTBS bearer. init : Initialize CCP Call Control Server set_bearer_name : Set bearer name [index] get_bearer_name : Get bearer name [index] + get_bearer_name : Get bearer UCI [index] Example Usage @@ -58,6 +59,15 @@ Setting and getting the bearer name uart:~$ ccp_call_control_server get_bearer_name 1 Bearer[1] name: New TBS name +Getting the bearer UCI +---------------------- + +.. code-block:: console + + uart:~$ ccp_call_control_server get_bearer_uci + Bearer[0] UCI: un999 + uart:~$ ccp_call_control_server get_bearer_name 1 + Bearer[1] UCI: skype Call Control Client ******************* @@ -90,3 +100,7 @@ Example Usage when connected Bearer 0x20046254 name: Generic TBS uart:~$ ccp_call_control_client read_bearer_name 1 Bearer 0x20046256 name: Telephone Bearer #1 + uart:~$ ccp_call_control_client read_bearer_uci + Bearer 0x20046254 UCI: un999 + uart:~$ ccp_call_control_client read_bearer_uci 1 + Bearer 0x20046256 name: skype diff --git a/include/zephyr/bluetooth/audio/ccp.h b/include/zephyr/bluetooth/audio/ccp.h index 0dd68befe2961..1746f55119397 100644 --- a/include/zephyr/bluetooth/audio/ccp.h +++ b/include/zephyr/bluetooth/audio/ccp.h @@ -120,6 +120,19 @@ int bt_ccp_call_control_server_set_bearer_provider_name( int bt_ccp_call_control_server_get_bearer_provider_name( struct bt_ccp_call_control_server_bearer *bearer, const char **name); +/** + * @brief Get the bearer provider name. + * + * @param[in] bearer The bearer to get the name for. + * @param[out] uci Pointer that will be updated to be the bearer uci. + * + * @retval 0 Success + * @retval -EINVAL @p bearer or @p uci is NULL + * @retval -EFAULT @p bearer is not registered + */ +int bt_ccp_call_control_server_get_bearer_uci(struct bt_ccp_call_control_server_bearer *bearer, + const char **uci); + /** @} */ /* End of group bt_ccp_call_control_server */ /** @@ -184,6 +197,21 @@ struct bt_ccp_call_control_client_cb { const char *name); #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#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 uci The UCI of the bearer. NULL if @p err is not 0. + */ + void (*bearer_uci)(struct bt_ccp_call_control_client_bearer *bearer, int err, + const char *uci); +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ + /** @cond INTERNAL_HIDDEN */ /** Internally used field for list handling */ sys_snode_t _node; @@ -262,6 +290,23 @@ int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *cl */ int bt_ccp_call_control_client_read_bearer_provider_name( struct bt_ccp_call_control_client_bearer *bearer); + +/** + * @brief Read the bearer Unicorm Caller Identifier (UCI) of a remote TBS bearer. + * + * @kconfig_dep{CONFIG_BT_TBS_CLIENT_BEARER_UCI} + * + * @param bearer The bearer to read the UCI 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_uci(struct bt_ccp_call_control_client_bearer *bearer); /** @} */ /* End of group bt_ccp_call_control_client */ #ifdef __cplusplus } diff --git a/include/zephyr/bluetooth/audio/tbs.h b/include/zephyr/bluetooth/audio/tbs.h index e1cc6bd36af15..2d8eb8338a829 100644 --- a/include/zephyr/bluetooth/audio/tbs.h +++ b/include/zephyr/bluetooth/audio/tbs.h @@ -199,7 +199,14 @@ extern "C" { * whenever the client should perform on action on the GTBS instance of the * server, rather than any of the specific Telephone Bearer Service instances. */ -#define BT_TBS_GTBS_INDEX 0xFF +#define BT_TBS_GTBS_INDEX 0xFF + +/** Maximum size of bearer uniform caller identifier (UCI) + * + * Includes the NULL terminator. + * Allowed values are defined by Bluetooth Assigned Numbers. + */ +#define BT_TBS_MAX_UCI_SIZE 6 /** @brief Opaque Telephone Bearer Service instance. */ struct bt_tbs_instance; diff --git a/samples/bluetooth/ccp_call_control_client/prj.conf b/samples/bluetooth/ccp_call_control_client/prj.conf index e9efbf1fdf702..e55bcbaea1ccd 100644 --- a/samples/bluetooth/ccp_call_control_client/prj.conf +++ b/samples/bluetooth/ccp_call_control_client/prj.conf @@ -17,6 +17,7 @@ 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_BT_TBS_CLIENT_BEARER_UCI=y CONFIG_UTF8=y # TBS Client may require up to 12 buffers diff --git a/samples/bluetooth/ccp_call_control_client/src/main.c b/samples/bluetooth/ccp_call_control_client/src/main.c index beca758a8600e..bb3c837940f35 100644 --- a/samples/bluetooth/ccp_call_control_client/src/main.c +++ b/samples/bluetooth/ccp_call_control_client/src/main.c @@ -222,6 +222,22 @@ static void ccp_call_control_client_read_bearer_provider_name_cb( } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +static void +ccp_call_control_client_read_bearer_uci_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, const char *name) +{ + if (err != 0) { + LOG_ERR("Failed to read bearer %p UCI: %d\n", (void *)bearer, err); + return; + } + + LOG_INF("Bearer %p UCI: %s", (void *)bearer, name); + + k_sem_give(&sem_ccp_action_completed); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ + static int reset_ccp_call_control_client(void) { int err; @@ -292,24 +308,62 @@ static int read_bearer_name(struct bt_ccp_call_control_client_bearer *bearer) return 0; } -static int read_bearer_names(void) +static int read_bearer_uci(struct bt_ccp_call_control_client_bearer *bearer) { int err; -#if defined(CONFIG_BT_TBS_CLIENT_GTBS) - err = read_bearer_name(client_bearers.gtbs_bearer); + err = bt_ccp_call_control_client_read_bearer_uci(bearer); if (err != 0) { - LOG_ERR("Failed to read name for GTBS bearer: %d", err); 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_values(void) +{ + int err; + +#if defined(CONFIG_BT_TBS_CLIENT_GTBS) + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { + err = read_bearer_name(client_bearers.gtbs_bearer); + if (err != 0) { + LOG_ERR("Failed to read name for GTBS bearer: %d", err); + return err; + } + } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { + err = read_bearer_uci(client_bearers.gtbs_bearer); + if (err != 0) { + LOG_ERR("Failed to read UCI 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; + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { + 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; + } + } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { + err = read_bearer_uci(client_bearers.tbs_bearers[i]); + if (err != 0) { + LOG_ERR("Failed to read UCI for bearer[%zu]: %d", i, err); + return err; + } } } #endif /* CONFIG_BT_TBS_CLIENT_TBS */ @@ -322,8 +376,11 @@ static int init_ccp_call_control_client(void) 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 + .bearer_provider_name = ccp_call_control_client_read_bearer_provider_name_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) + .bearer_uci = ccp_call_control_client_read_bearer_uci_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ }; static struct bt_le_scan_cb scan_cbs = { .recv = scan_recv_cb, @@ -384,12 +441,7 @@ int main(void) continue; } - if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { - err = read_bearer_names(); - if (err != 0) { - continue; - } - } + read_bearer_values(); /* Reset if disconnected */ err = k_sem_take(&sem_conn_state_change, K_FOREVER); diff --git a/subsys/bluetooth/audio/ccp_call_control_client.c b/subsys/bluetooth/audio/ccp_call_control_client.c index 9b1e5283cee22..1e91a70ad9add 100644 --- a/subsys/bluetooth/audio/ccp_call_control_client.c +++ b/subsys/bluetooth/audio/ccp_call_control_client.c @@ -289,31 +289,7 @@ 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); - } - } -} - +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) || defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) /** * @brief Validates a bearer and provides a client with ownership of the busy flag * @@ -353,6 +329,32 @@ static int validate_bearer_and_get_client(const struct bt_ccp_call_control_clien return 0; } +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI || CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ + +#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); + } + } +} int bt_ccp_call_control_client_read_bearer_provider_name( struct bt_ccp_call_control_client_bearer *bearer) @@ -389,3 +391,63 @@ int bt_ccp_call_control_client_read_bearer_provider_name( return 0; } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +static void tbs_client_read_bearer_uci_cb(struct bt_conn *conn, int err, uint8_t inst_index, + const char *uci) +{ + 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_uci != NULL) { + listener->bearer_uci(bearer, err, uci); + } + } +} + +int bt_ccp_call_control_client_read_bearer_uci(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_uci = tbs_client_read_bearer_uci_cb; + + err = bt_tbs_client_read_bearer_uci(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_uci 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_uci: %d", err); + + return -ENOEXEC; + } + + return 0; +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ diff --git a/subsys/bluetooth/audio/ccp_call_control_server.c b/subsys/bluetooth/audio/ccp_call_control_server.c index 053a83ed6bfff..37f766bfc1596 100644 --- a/subsys/bluetooth/audio/ccp_call_control_server.c +++ b/subsys/bluetooth/audio/ccp_call_control_server.c @@ -23,6 +23,7 @@ LOG_MODULE_REGISTER(bt_ccp_call_control_server, CONFIG_BT_CCP_CALL_CONTROL_SERVE /* A service instance can either be a GTBS or a TBS instance */ struct bt_ccp_call_control_server_bearer { char provider_name[CONFIG_BT_CCP_CALL_CONTROL_SERVER_PROVIDER_NAME_MAX_LENGTH + 1]; + char uci[BT_TBS_MAX_UCI_SIZE]; uint8_t tbs_index; bool registered; }; @@ -75,6 +76,7 @@ int bt_ccp_call_control_server_register_bearer(const struct bt_tbs_register_para free_bearer->tbs_index = (uint8_t)ret; (void)utf8_lcpy(free_bearer->provider_name, param->provider_name, sizeof(free_bearer->provider_name)); + (void)utf8_lcpy(free_bearer->uci, param->uci, sizeof(free_bearer->uci)); *bearer = free_bearer; return 0; @@ -175,3 +177,29 @@ int bt_ccp_call_control_server_get_bearer_provider_name( return 0; } + +int bt_ccp_call_control_server_get_bearer_uci(struct bt_ccp_call_control_server_bearer *bearer, + const char **uci) +{ + CHECKIF(bearer == NULL) { + LOG_DBG("bearer is NULL"); + + return -EINVAL; + } + + CHECKIF(uci == NULL) { + LOG_DBG("uci is NULL"); + + return -EINVAL; + } + + if (!bearer->registered) { + LOG_DBG("Bearer %p not registered", bearer); + + return -EFAULT; + } + + *uci = bearer->uci; + + return 0; +} diff --git a/subsys/bluetooth/audio/shell/ccp_call_control_client.c b/subsys/bluetooth/audio/shell/ccp_call_control_client.c index 1152d62fe8fa5..156f9e5c6a302 100644 --- a/subsys/bluetooth/audio/shell/ccp_call_control_client.c +++ b/subsys/bluetooth/audio/shell/ccp_call_control_client.c @@ -65,11 +65,27 @@ ccp_call_control_client_bearer_provider_name_cb(struct bt_ccp_call_control_clien } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +static void ccp_call_control_client_bearer_uci_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, const char *name) +{ + if (err != 0) { + bt_shell_error("Failed to read bearer %p UCI: %d", (void *)bearer, err); + return; + } + + bt_shell_info("Bearer %p UCI: %s", (void *)bearer, name); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ + 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_bearer_provider_name_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) + .bearer_uci = ccp_call_control_client_bearer_uci_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ }; static int cmd_ccp_call_control_client_discover(const struct shell *sh, size_t argc, char *argv[]) @@ -187,6 +203,37 @@ static int cmd_ccp_call_control_client_read_bearer_name(const struct shell *sh, return 0; } +static int cmd_ccp_call_control_client_read_bearer_uci(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct bt_ccp_call_control_client_bearer *bearer; + unsigned long index = 0; + int err; + + if (argc > 1) { + index = validate_and_get_index(sh, argv[1]); + if (index < 0) { + return index; + } + } + + bearer = get_bearer_by_index(index); + if (bearer == NULL) { + shell_error(sh, "Failed to get bearer for index %lu", index); + + return -ENOEXEC; + } + + err = bt_ccp_call_control_client_read_bearer_uci(bearer); + if (err != 0) { + shell_error(sh, "Failed to read bearer[%lu] UCI: %d", index, err); + + return -ENOEXEC; + } + + return 0; +} + static int cmd_ccp_call_control_client(const struct shell *sh, size_t argc, char **argv) { if (argc > 1) { @@ -204,6 +251,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(ccp_call_control_client_cmds, cmd_ccp_call_control_client_discover, 1, 0), SHELL_CMD_ARG(read_bearer_name, NULL, "Get bearer name [index]", cmd_ccp_call_control_client_read_bearer_name, 1, 1), + SHELL_CMD_ARG(read_bearer_name, NULL, "Get bearer UCI [index]", + cmd_ccp_call_control_client_read_bearer_uci, 1, 1), SHELL_SUBCMD_SET_END); SHELL_CMD_ARG_REGISTER(ccp_call_control_client, &ccp_call_control_client_cmds, diff --git a/subsys/bluetooth/audio/shell/ccp_call_control_server.c b/subsys/bluetooth/audio/shell/ccp_call_control_server.c index 4934fe8b52c3d..0f9005a82cea5 100644 --- a/subsys/bluetooth/audio/shell/ccp_call_control_server.c +++ b/subsys/bluetooth/audio/shell/ccp_call_control_server.c @@ -155,6 +155,32 @@ static int cmd_ccp_call_control_server_get_bearer_name(const struct shell *sh, s return 0; } +static int cmd_ccp_call_control_server_get_bearer_uci(const struct shell *sh, size_t argc, + char *argv[]) +{ + unsigned long index = 0; + const char *uci; + int err = 0; + + if (argc > 1) { + index = validate_and_get_index(sh, argv[1]); + if (index < 0) { + return index; + } + } + + err = bt_ccp_call_control_server_get_bearer_uci(bearers[index], &uci); + if (err != 0) { + shell_error(sh, "Failed to get bearer[%lu] UCI: %d", index, err); + + return -ENOEXEC; + } + + shell_print(sh, "Bearer[%lu] UCI: %s", index, uci); + + return 0; +} + static int cmd_ccp_call_control_server(const struct shell *sh, size_t argc, char **argv) { if (argc > 1) { @@ -174,6 +200,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(ccp_call_control_server_cmds, cmd_ccp_call_control_server_set_bearer_name, 2, 1), SHELL_CMD_ARG(get_bearer_name, NULL, "Get bearer name [index]", cmd_ccp_call_control_server_get_bearer_name, 1, 1), + SHELL_CMD_ARG(get_bearer_uci, NULL, "Get bearer UCI [index]", + cmd_ccp_call_control_server_get_bearer_uci, 1, 1), SHELL_SUBCMD_SET_END); SHELL_CMD_ARG_REGISTER(ccp_call_control_server, &ccp_call_control_server_cmds, diff --git a/subsys/bluetooth/audio/tbs_internal.h b/subsys/bluetooth/audio/tbs_internal.h index 08fadf48745fb..11a279efacb5d 100644 --- a/subsys/bluetooth/audio/tbs_internal.h +++ b/subsys/bluetooth/audio/tbs_internal.h @@ -21,7 +21,6 @@ #include #include -#define BT_TBS_MAX_UCI_SIZE 6 #define BT_TBS_MIN_URI_LEN 3 /* a:b */ #define BT_TBS_FREE_CALL_INDEX 0 diff --git a/tests/bluetooth/audio/ccp_call_control_client/include/ccp_call_control_client.h b/tests/bluetooth/audio/ccp_call_control_client/include/ccp_call_control_client.h index 55feddbbeaf35..9ec22d90cd0f3 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/include/ccp_call_control_client.h +++ b/tests/bluetooth/audio/ccp_call_control_client/include/ccp_call_control_client.h @@ -21,5 +21,7 @@ DECLARE_FAKE_VOID_FUNC(mock_ccp_call_control_client_discover_cb, struct bt_ccp_call_control_client_bearers *); DECLARE_FAKE_VOID_FUNC(mock_ccp_call_control_client_bearer_provider_name_cb, struct bt_ccp_call_control_client_bearer *, int, const char *); +DECLARE_FAKE_VOID_FUNC(mock_ccp_call_control_client_bearer_uci_cb, + struct bt_ccp_call_control_client_bearer *, int, const char *); #endif /* MOCKS_CCP_CALL_CONTROL_CLIENT_H_ */ diff --git a/tests/bluetooth/audio/ccp_call_control_client/prj.conf b/tests/bluetooth/audio/ccp_call_control_client/prj.conf index 5f0f8a3f2a9f7..f0c878d552359 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/prj.conf +++ b/tests/bluetooth/audio/ccp_call_control_client/prj.conf @@ -11,6 +11,8 @@ CONFIG_BT_CCP_CALL_CONTROL_CLIENT=y CONFIG_BT_CCP_CALL_CONTROL_CLIENT_BEARER_COUNT=2 CONFIG_BT_TBS_CLIENT_TBS=y CONFIG_BT_TBS_CLIENT_GTBS=y +CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME=y +CONFIG_BT_TBS_CLIENT_BEARER_UCI=y CONFIG_UTF8=y CONFIG_ASSERT=y diff --git a/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c b/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c index 98564f984aceb..ac1628ab84255 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c +++ b/tests/bluetooth/audio/ccp_call_control_client/src/test_procedures.c @@ -163,3 +163,53 @@ static ZTEST_F(ccp_call_control_client_procedures_test_suite, err = bt_ccp_call_control_client_read_bearer_provider_name(invalid_bearer); zassert_equal(err, -EEXIST, "Unexpected return value %d", err); } + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_uci) +{ + int err; + + err = bt_ccp_call_control_client_read_bearer_uci(fixture->bearers[0]); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_ccp_call_control_client_cb.bearer_uci", 1, + mock_ccp_call_control_client_bearer_uci_cb_fake.call_count); + zassert_not_null( + mock_ccp_call_control_client_bearer_uci_cb_fake.arg0_history[0]); /* bearer */ + zassert_equal(0, mock_ccp_call_control_client_bearer_uci_cb_fake.arg1_history[0]); /* err */ + zassert_not_null(mock_ccp_call_control_client_bearer_uci_cb_fake.arg2_history[0]); /* uci */ +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_uci_inval_null_bearer) +{ + int err; + + err = bt_ccp_call_control_client_read_bearer_uci(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_uci_inval_not_discovered) +{ + int err; + + /* Fake disconnection to clear the discovered value for the bearers*/ + mock_bt_conn_disconnected(&fixture->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + /* Mark as connected again but without discovering */ + test_conn_init(&fixture->conn); + + err = bt_ccp_call_control_client_read_bearer_uci(fixture->bearers[0]); + zassert_equal(err, -EFAULT, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_client_procedures_test_suite, + test_ccp_call_control_client_read_bearer_uci_inval_bearer) +{ + struct bt_ccp_call_control_client_bearer *invalid_bearer = + (struct bt_ccp_call_control_client_bearer *)0xdeadbeef; + int err; + + err = bt_ccp_call_control_client_read_bearer_uci(invalid_bearer); + zassert_equal(err, -EEXIST, "Unexpected return value %d", err); +} diff --git a/tests/bluetooth/audio/ccp_call_control_client/uut/ccp_call_control_client.c b/tests/bluetooth/audio/ccp_call_control_client/uut/ccp_call_control_client.c index 8381464c1806c..5f61fbd2e658e 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/uut/ccp_call_control_client.c +++ b/tests/bluetooth/audio/ccp_call_control_client/uut/ccp_call_control_client.c @@ -19,12 +19,19 @@ DEFINE_FAKE_VOID_FUNC(mock_ccp_call_control_client_discover_cb, struct bt_ccp_ca DEFINE_FAKE_VOID_FUNC(mock_ccp_call_control_client_bearer_provider_name_cb, struct bt_ccp_call_control_client_bearer *, int, const char *); #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +DEFINE_FAKE_VOID_FUNC(mock_ccp_call_control_client_bearer_uci_cb, + struct bt_ccp_call_control_client_bearer *, int, const char *); +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ struct bt_ccp_call_control_client_cb mock_ccp_call_control_client_cb = { .discover = mock_ccp_call_control_client_discover_cb, #if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) .bearer_provider_name = mock_ccp_call_control_client_bearer_provider_name_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) + .bearer_uci = mock_ccp_call_control_client_bearer_uci_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ }; void mock_ccp_call_control_client_init(void) diff --git a/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c b/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c index 9eb86f878373e..0df53d9a2a0de 100644 --- a/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c +++ b/tests/bluetooth/audio/ccp_call_control_client/uut/tbs_client.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,7 @@ int bt_tbs_client_discover(struct bt_conn *conn) return 0; } +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) int bt_tbs_client_read_bearer_provider_name(struct bt_conn *conn, uint8_t inst_index) { if (conn == NULL) { @@ -46,3 +48,19 @@ int bt_tbs_client_read_bearer_provider_name(struct bt_conn *conn, uint8_t inst_i return 0; } +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ + +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +int bt_tbs_client_read_bearer_uci(struct bt_conn *conn, uint8_t inst_index) +{ + if (conn == NULL) { + return -ENOTCONN; + } + + if (tbs_cbs != NULL && tbs_cbs->bearer_provider_name != NULL) { + tbs_cbs->bearer_uci(conn, 0, inst_index, "bearer UCI"); + } + + return 0; +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ diff --git a/tests/bluetooth/audio/ccp_call_control_server/src/main.c b/tests/bluetooth/audio/ccp_call_control_server/src/main.c index af09be763bb17..6ee008c035a12 100644 --- a/tests/bluetooth/audio/ccp_call_control_server/src/main.c +++ b/tests/bluetooth/audio/ccp_call_control_server/src/main.c @@ -27,6 +27,7 @@ DEFINE_FFF_GLOBALS; #define DEFAULT_BEARER_NAME "test" +#define DEFAULT_BEARER_UCI "un999" struct ccp_call_control_server_test_suite_fixture { /** Need 1 additional bearer than the max to trigger some corner cases */ @@ -84,7 +85,7 @@ static void register_default_bearer(struct ccp_call_control_server_test_suite_fi { const struct bt_tbs_register_param register_param = { .provider_name = DEFAULT_BEARER_NAME, - .uci = "un999", + .uci = DEFAULT_BEARER_UCI, .uri_schemes_supported = "tel", .gtbs = true, .authorization_required = false, @@ -409,3 +410,55 @@ static ZTEST_F(ccp_call_control_server_test_suite, err = bt_ccp_call_control_server_get_bearer_provider_name(fixture->bearers[0], NULL); zassert_equal(err, -EINVAL, "Unexpected return value %d", err); } + +static ZTEST_F(ccp_call_control_server_test_suite, test_bt_ccp_call_control_server_get_bearer_uci) +{ + const char *res_bearer_name; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_uci(fixture->bearers[0], &res_bearer_name); + zassert_equal(err, 0, "Unexpected return value %d", err); + + zassert_str_equal(DEFAULT_BEARER_UCI, res_bearer_name, "%s != %s", DEFAULT_BEARER_UCI, + res_bearer_name); +} + +static ZTEST_F(ccp_call_control_server_test_suite, + test_bt_ccp_call_control_server_get_bearer_uci_inval_not_registered) +{ + const char *res_bearer_name; + int err; + + /* Register and unregister bearer to get a valid pointer but where it is unregistered*/ + register_default_bearer(fixture); + err = bt_ccp_call_control_server_unregister_bearer(fixture->bearers[0]); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_ccp_call_control_server_get_bearer_uci(fixture->bearers[0], &res_bearer_name); + zassert_equal(err, -EFAULT, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_server_test_suite, + test_bt_ccp_call_control_server_get_bearer_uci_inval_null_bearer) +{ + const char *res_bearer_name; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_uci(NULL, &res_bearer_name); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(ccp_call_control_server_test_suite, + test_bt_ccp_call_control_server_get_bearer_uci_inval_null_name) +{ + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_uci(fixture->bearers[0], NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} diff --git a/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c b/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c index 5631b8cb0ec83..81fc8e2dc11c5 100644 --- a/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c +++ b/tests/bsim/bluetooth/audio/src/ccp_call_control_client_test.c @@ -24,6 +24,7 @@ extern enum bst_result_t bst_result; CREATE_FLAG(flag_discovery_complete); CREATE_FLAG(flag_bearer_name_read); +CREATE_FLAG(flag_bearer_uci); static struct bt_ccp_call_control_client *call_control_client; static struct bt_ccp_call_control_client_bearers client_bearers; @@ -63,6 +64,21 @@ static void ccp_call_control_client_read_bearer_provider_name_cb( SET_FLAG(flag_bearer_name_read); } #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) +static void +ccp_call_control_client_read_bearer_uci_cb(struct bt_ccp_call_control_client_bearer *bearer, + int err, const char *uci) +{ + if (err != 0) { + FAIL("Failed to read bearer %p provider UCI: %d\n", (void *)bearer, err); + return; + } + + LOG_INF("Bearer %p provider UCI: %s", (void *)bearer, uci); + + SET_FLAG(flag_bearer_uci); +} +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ static void discover_tbs(void) { @@ -94,17 +110,46 @@ static void read_bearer_name(struct bt_ccp_call_control_client_bearer *bearer) WAIT_FOR_FLAG(flag_bearer_name_read); } -static void read_bearer_names(void) +static void read_bearer_uci(struct bt_ccp_call_control_client_bearer *bearer) +{ + int err; + + UNSET_FLAG(flag_bearer_uci); + + err = bt_ccp_call_control_client_read_bearer_uci(bearer); + if (err != 0) { + FAIL("Failed to read UCI of bearer %p: %d", bearer, err); + return; + } + + WAIT_FOR_FLAG(flag_bearer_uci); +} + +static int read_bearer_values(void) { #if defined(CONFIG_BT_TBS_CLIENT_GTBS) - read_bearer_name(client_bearers.gtbs_bearer); + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { + read_bearer_name(client_bearers.gtbs_bearer); + } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { + read_bearer_uci(client_bearers.gtbs_bearer); + } #endif /* CONFIG_BT_TBS_CLIENT_GTBS */ #if defined(CONFIG_BT_TBS_CLIENT_TBS) for (size_t i = 0; i < client_bearers.tbs_count; i++) { - read_bearer_name(client_bearers.tbs_bearers[i]); + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { + read_bearer_name(client_bearers.tbs_bearers[i]); + } + + if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_UCI)) { + read_bearer_uci(client_bearers.tbs_bearers[i]); + } } #endif /* CONFIG_BT_TBS_CLIENT_TBS */ + + return 0; } static void init(void) @@ -112,8 +157,11 @@ static void init(void) 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 + .bearer_provider_name = ccp_call_control_client_read_bearer_provider_name_cb, #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */ +#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) + .bearer_uci = ccp_call_control_client_read_bearer_uci_cb, +#endif /* CONFIG_BT_TBS_CLIENT_BEARER_UCI */ }; int err; @@ -146,9 +194,7 @@ static void test_main(void) discover_tbs(); discover_tbs(); /* test that we can discover twice */ - if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) { - read_bearer_names(); - } + read_bearer_values(); err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); if (err != 0) {