diff --git a/doc/connectivity/bluetooth/shell/audio/ccp.rst b/doc/connectivity/bluetooth/shell/audio/ccp.rst index 0fbf3ed2181df..8aab2829ded49 100644 --- a/doc/connectivity/bluetooth/shell/audio/ccp.rst +++ b/doc/connectivity/bluetooth/shell/audio/ccp.rst @@ -13,12 +13,18 @@ The Server can be controlled locally, or by a remote device (when in a call). Fo example a remote device may initiate a call to the server, or the Server may initiate a call to remote device, without a client. +For all commands that take an optional :code:`index`, if the index is not supplied then it defaults +to :code:`0` which is the GTBS bearer. + .. code-block:: console ccp_call_control_server --help ccp_call_control_server - Bluetooth CCP Call Control Server shell commands Subcommands: - init : Initialize CCP Call Control Server + init : Initialize CCP Call Control Server + set_bearer_name : Set bearer name [index] + get_bearer_name : Get bearer name [index] + Example Usage ============= @@ -34,6 +40,25 @@ Setup Registered bearer[1] uart:~$ bt connect xx:xx:xx:xx:xx:xx public +Setting and getting the bearer name +----------------------------------- + +.. code-block:: console + + uart:~$ ccp_call_control_server get_bearer_name + Bearer[0] name: Generic TBS + uart:~$ ccp_call_control_server set_bearer_name "New name" + Bearer[0] name: New name + uart:~$ ccp_call_control_server get_bearer_name + Bearer[0] name: New name + uart:~$ ccp_call_control_server get_bearer_name 1 + Bearer[1] name: Telephone Bearer #1 + uart:~$ ccp_call_control_server set_bearer_name 1 "New TBS name" + Bearer[1] name: New TBS name + uart:~$ ccp_call_control_server get_bearer_name 1 + Bearer[1] name: New TBS name + + Call Control Client ******************* The Call Control Client is a role that typically resides on resource constrained devices such as diff --git a/include/zephyr/bluetooth/audio/ccp.h b/include/zephyr/bluetooth/audio/ccp.h index 5c907d569590c..c57432c9cf6b1 100644 --- a/include/zephyr/bluetooth/audio/ccp.h +++ b/include/zephyr/bluetooth/audio/ccp.h @@ -93,6 +93,33 @@ int bt_ccp_call_control_server_register_bearer(const struct bt_tbs_register_para */ int bt_ccp_call_control_server_unregister_bearer(struct bt_ccp_call_control_server_bearer *bearer); +/** + * @brief Set a new bearer provider name. + * + * @param bearer The bearer to set the name for. + * @param name The new bearer provider name. + * + * @retval 0 Success + * @retval -EINVAL @p bearer or @p name is NULL, or @p name is the empty string or @p name is larger + * than @kconfig{CONFIG_BT_TBS_MAX_PROVIDER_NAME_LENGTH} + * @retval -EFAULT @p bearer is not registered + */ +int bt_ccp_call_control_server_set_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] name Pointer that will be updated to be the bearer provider name. + * + * @retval 0 Success + * @retval -EINVAL @p bearer or @p name is NULL + * @retval -EFAULT @p bearer is not registered + */ +int bt_ccp_call_control_server_get_bearer_provider_name( + struct bt_ccp_call_control_server_bearer *bearer, const char **name); + /** @} */ /* End of group bt_ccp_call_control_server */ /** diff --git a/subsys/bluetooth/audio/Kconfig.ccp b/subsys/bluetooth/audio/Kconfig.ccp index c397b831cfc01..95d2aac65e72a 100644 --- a/subsys/bluetooth/audio/Kconfig.ccp +++ b/subsys/bluetooth/audio/Kconfig.ccp @@ -50,6 +50,13 @@ config BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT help The number of supported telephone bearers on the CCP Call Control Server +config BT_CCP_CALL_CONTROL_SERVER_PROVIDER_NAME_MAX_LENGTH + int "The maximum length of the bearer provider name excluding null terminator" + default BT_TBS_MAX_PROVIDER_NAME_LENGTH + range 1 BT_TBS_MAX_PROVIDER_NAME_LENGTH + help + Sets the maximum length of the bearer provider name. + module = BT_CCP_CALL_CONTROL_SERVER module-str = "Call Control Profile Call Control Server" source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/bluetooth/audio/Kconfig.tbs b/subsys/bluetooth/audio/Kconfig.tbs index 557786a833cdb..3a06931495b09 100644 --- a/subsys/bluetooth/audio/Kconfig.tbs +++ b/subsys/bluetooth/audio/Kconfig.tbs @@ -263,7 +263,7 @@ config BT_TBS_MAX_URI_LENGTH config BT_TBS_MAX_PROVIDER_NAME_LENGTH int "The maximum length of the bearer provider name" default 30 - range 0 512 + range 1 512 help Sets the maximum length of the bearer provider name. diff --git a/subsys/bluetooth/audio/ccp_call_control_server.c b/subsys/bluetooth/audio/ccp_call_control_server.c index c1f955743ba87..fc14bb178f1a1 100644 --- a/subsys/bluetooth/audio/ccp_call_control_server.c +++ b/subsys/bluetooth/audio/ccp_call_control_server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,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]; uint8_t tbs_index; bool registered; }; @@ -70,6 +72,8 @@ int bt_ccp_call_control_server_register_bearer(const struct bt_tbs_register_para free_bearer->registered = true; free_bearer->tbs_index = (uint8_t)ret; + (void)utf8_lcpy(free_bearer->provider_name, param->provider_name, + sizeof(free_bearer->provider_name)); *bearer = free_bearer; return 0; @@ -105,3 +109,68 @@ int bt_ccp_call_control_server_unregister_bearer(struct bt_ccp_call_control_serv return 0; } + +int bt_ccp_call_control_server_set_bearer_provider_name( + struct bt_ccp_call_control_server_bearer *bearer, const char *name) +{ + size_t len; + + CHECKIF(bearer == NULL) { + LOG_DBG("bearer is NULL"); + + return -EINVAL; + } + + CHECKIF(name == NULL) { + LOG_DBG("name is NULL"); + + return -EINVAL; + } + + if (!bearer->registered) { + LOG_DBG("Bearer %p not registered", bearer); + + return -EFAULT; + } + + len = strlen(name); + if (len > CONFIG_BT_CCP_CALL_CONTROL_SERVER_PROVIDER_NAME_MAX_LENGTH || len == 0) { + LOG_DBG("Invalid name length: %zu", len); + + return -EINVAL; + } + + if (strcmp(bearer->provider_name, name) == 0) { + return 0; + } + + (void)utf8_lcpy(bearer->provider_name, name, sizeof(bearer->provider_name)); + + return bt_tbs_set_bearer_provider_name(bearer->tbs_index, name); +} + +int bt_ccp_call_control_server_get_bearer_provider_name( + struct bt_ccp_call_control_server_bearer *bearer, const char **name) +{ + CHECKIF(bearer == NULL) { + LOG_DBG("bearer is NULL"); + + return -EINVAL; + } + + CHECKIF(name == NULL) { + LOG_DBG("name is NULL"); + + return -EINVAL; + } + + if (!bearer->registered) { + LOG_DBG("Bearer %p not registered", bearer); + + return -EFAULT; + } + + *name = bearer->provider_name; + + return 0; +} diff --git a/subsys/bluetooth/audio/shell/ccp_call_control_server.c b/subsys/bluetooth/audio/shell/ccp_call_control_server.c index dc0bcc8bf8c07..4934fe8b52c3d 100644 --- a/subsys/bluetooth/audio/shell/ccp_call_control_server.c +++ b/subsys/bluetooth/audio/shell/ccp_call_control_server.c @@ -15,6 +15,7 @@ #include #include #include +#include static struct bt_ccp_call_control_server_bearer *bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT]; @@ -79,6 +80,81 @@ static int cmd_ccp_call_control_server_init(const struct shell *sh, size_t argc, return 0; } +static int validate_and_get_index(const struct shell *sh, const char *index_arg) +{ + unsigned long index; + int err = 0; + + index = shell_strtoul(index_arg, 0, &err); + if (err != 0) { + shell_error(sh, "Could not parse index: %d", err); + + return -ENOEXEC; + } + + if (index >= CONFIG_BT_TBS_BEARER_COUNT) { + shell_error(sh, "Invalid index: %lu", index); + + return -ENOEXEC; + } + + return (int)index; +} + +static int cmd_ccp_call_control_server_set_bearer_name(const struct shell *sh, size_t argc, + char *argv[]) +{ + const char *name; + int index = 0; + int err = 0; + + if (argc > 2) { + index = validate_and_get_index(sh, argv[1]); + if (index < 0) { + return index; + } + } + + name = argv[argc - 1]; + + err = bt_ccp_call_control_server_set_bearer_provider_name(bearers[index], name); + if (err != 0) { + shell_error(sh, "Failed to set bearer[%d] name: %d", index, err); + + return -ENOEXEC; + } + + shell_print(sh, "Bearer[%d] name: %s", index, name); + + return 0; +} + +static int cmd_ccp_call_control_server_get_bearer_name(const struct shell *sh, size_t argc, + char *argv[]) +{ + const char *name; + int index = 0; + 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_provider_name(bearers[index], &name); + if (err != 0) { + shell_error(sh, "Failed to get bearer[%d] name: %d", index, err); + + return -ENOEXEC; + } + + shell_print(sh, "Bearer[%d] name: %s", index, name); + + return 0; +} + static int cmd_ccp_call_control_server(const struct shell *sh, size_t argc, char **argv) { if (argc > 1) { @@ -93,6 +169,11 @@ static int cmd_ccp_call_control_server(const struct shell *sh, size_t argc, char SHELL_STATIC_SUBCMD_SET_CREATE(ccp_call_control_server_cmds, SHELL_CMD_ARG(init, NULL, "Initialize CCP Call Control Server", cmd_ccp_call_control_server_init, 1, 0), + SHELL_CMD_ARG(set_bearer_name, NULL, + "Set bearer name [index] ", + 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_SUBCMD_SET_END); SHELL_CMD_ARG_REGISTER(ccp_call_control_server, &ccp_call_control_server_cmds, diff --git a/subsys/bluetooth/audio/tbs.c b/subsys/bluetooth/audio/tbs.c index 33fddbdd20a33..37bcbf82b22b4 100644 --- a/subsys/bluetooth/audio/tbs.c +++ b/subsys/bluetooth/audio/tbs.c @@ -68,6 +68,10 @@ struct tbs_flags { /* A service instance can either be a GTBS or a TBS instance */ struct tbs_inst { /* Attribute values */ + /* TODO: The provider name should be removed from the tbs_inst and instead by stored by the + * user of TBS. This will be done once the CCP API is complete as the CCP Server will own + * all the data instead of the TBS + */ char provider_name[CONFIG_BT_TBS_MAX_PROVIDER_NAME_LENGTH]; char uci[BT_TBS_MAX_UCI_SIZE]; uint8_t technology; 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 7ed6c1ef0e192..af09be763bb17 100644 --- a/tests/bluetooth/audio/ccp_call_control_server/src/main.c +++ b/tests/bluetooth/audio/ccp_call_control_server/src/main.c @@ -26,6 +26,8 @@ DEFINE_FFF_GLOBALS; +#define DEFAULT_BEARER_NAME "test" + struct ccp_call_control_server_test_suite_fixture { /** Need 1 additional bearer than the max to trigger some corner cases */ struct bt_ccp_call_control_server_bearer @@ -81,7 +83,7 @@ ZTEST_SUITE(ccp_call_control_server_test_suite, NULL, ccp_call_control_server_te static void register_default_bearer(struct ccp_call_control_server_test_suite_fixture *fixture) { const struct bt_tbs_register_param register_param = { - .provider_name = "test", + .provider_name = DEFAULT_BEARER_NAME, .uci = "un999", .uri_schemes_supported = "tel", .gtbs = true, @@ -262,3 +264,148 @@ static ZTEST_F(ccp_call_control_server_test_suite, err = bt_ccp_call_control_server_unregister_bearer(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_set_bearer_provider_name) +{ + const char *new_bearer_name = "New bearer name"; + const char *res_bearer_name; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_set_bearer_provider_name(fixture->bearers[0], + new_bearer_name); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_ccp_call_control_server_get_bearer_provider_name(fixture->bearers[0], + &res_bearer_name); + zassert_equal(err, 0, "Unexpected return value %d", err); + + zassert_str_equal(new_bearer_name, res_bearer_name, "%s != %s", new_bearer_name, + res_bearer_name); +} + +static ZTEST_F(ccp_call_control_server_test_suite, + test_bt_ccp_call_control_server_set_bearer_provider_name_inval_not_registered) +{ + const char *new_bearer_name = "New 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_set_bearer_provider_name(fixture->bearers[0], + new_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_set_bearer_provider_name_inval_null_bearer) +{ + const char *new_bearer_name = "New bearer name"; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_set_bearer_provider_name(NULL, new_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_set_bearer_provider_name_inval_null_name) +{ + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_set_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_set_bearer_provider_name_inval_empty_name) +{ + const char *inval_bearer_name = ""; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_set_bearer_provider_name(fixture->bearers[0], + inval_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_set_bearer_provider_name_inval_long_name) +{ + char inval_bearer_name[CONFIG_BT_CCP_CALL_CONTROL_SERVER_PROVIDER_NAME_MAX_LENGTH + 1]; + int err; + + for (size_t i = 0; i < ARRAY_SIZE(inval_bearer_name); i++) { + inval_bearer_name[i] = 'a'; + } + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_set_bearer_provider_name(fixture->bearers[0], + inval_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_provider_name) +{ + const char *res_bearer_name; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_provider_name(fixture->bearers[0], + &res_bearer_name); + zassert_equal(err, 0, "Unexpected return value %d", err); + + zassert_str_equal(DEFAULT_BEARER_NAME, res_bearer_name, "%s != %s", DEFAULT_BEARER_NAME, + res_bearer_name); +} + +static ZTEST_F(ccp_call_control_server_test_suite, + test_bt_ccp_call_control_server_get_bearer_provider_name_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_provider_name(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_provider_name_inval_null_bearer) +{ + const char *res_bearer_name; + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_provider_name(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_provider_name_inval_null_name) +{ + int err; + + register_default_bearer(fixture); + + err = bt_ccp_call_control_server_get_bearer_provider_name(fixture->bearers[0], NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +}