diff --git a/doc/connectivity/bluetooth/api/shell/cap.rst b/doc/connectivity/bluetooth/api/shell/cap.rst index 32d752d71fc8..51a42c90260f 100644 --- a/doc/connectivity/bluetooth/api/shell/cap.rst +++ b/doc/connectivity/bluetooth/api/shell/cap.rst @@ -159,8 +159,10 @@ the optionally included CSIS instance by calling (:code:`cap_commander discover` cap_commander --help cap_commander - Bluetooth CAP commander shell commands Subcommands: - discover :Discover CAS - change_volume :Change volume on all connections + discover :Discover CAS + change_volume :Change volume on all connections + change_volume_offset :Change volume offset per connection Before being able to perform any stream operation, the device must also perform the @@ -193,3 +195,36 @@ Setting the volume on all connected devices: VCP flags 0x01 VCP vol_set done Volume change completed + + +Setting the volume offset on one or more connected devices. The offsets are set by connection index, +so connection index 0 gets the first offset, and index 1 gets the second offset, etc.: + +.. code-block:: console + + uart:~$ bt connect + Connected: + uart:~$ cap_commander discover + discovery completed with CSIS + uart:~$ vcp_vol_ctlr discover + VCP discover done with 1 VOCS and 1 AICS + uart:~$ + uart:~$ bt connect + Connected: + uart:~$ cap_commander discover + discovery completed with CSIS + uart:~$ vcp_vol_ctlr discover + VCP discover done with 1 VOCS and 1 AICS + uart:~$ + uart:~$ cap_commander change_volume_offset 10 + Setting volume offset on 1 connections + VOCS inst 0x200140a4 offset 10 + Offset set for inst 0x200140a4 + Volume offset change completed + uart:~$ + uart:~$ cap_commander change_volume_offset 10 15 + Setting volume offset on 2 connections + Offset set for inst 0x200140a4 + VOCS inst 0x20014188 offset 15 + Offset set for inst 0x20014188 + Volume offset change completed diff --git a/subsys/bluetooth/audio/shell/cap_commander.c b/subsys/bluetooth/audio/shell/cap_commander.c index 01675f8fc6ca..6e4f85eba014 100644 --- a/subsys/bluetooth/audio/shell/cap_commander.c +++ b/subsys/bluetooth/audio/shell/cap_commander.c @@ -8,10 +8,12 @@ */ #include -#include -#include #include #include +#include +#include +#include +#include #include "shell/bt.h" #include "audio.h" @@ -37,12 +39,27 @@ static void cap_volume_changed_cb(struct bt_conn *conn, int err) shell_print(ctx_shell, "Volume change completed"); } + +#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) +static void cap_volume_offset_changed_cb(struct bt_conn *conn, int err) +{ + if (err != 0) { + shell_error(ctx_shell, "Volume offset change failed (%d)", err); + return; + } + + shell_print(ctx_shell, "Volume offset change completed"); +} +#endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ static struct bt_cap_commander_cb cbs = { .discovery_complete = cap_discover_cb, #if defined(CONFIG_BT_VCP_VOL_CTLR) .volume_changed = cap_volume_changed_cb, +#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) + .volume_offset_changed = cap_volume_offset_changed_cb, +#endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ }; @@ -145,6 +162,79 @@ static int cmd_cap_commander_change_volume(const struct shell *sh, size_t argc, return 0; } + +#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) +static int cmd_cap_commander_change_volume_offset(const struct shell *sh, size_t argc, char *argv[]) +{ + struct bt_cap_commander_change_volume_offset_member_param member_params[CONFIG_BT_MAX_CONN]; + const size_t cap_args = argc - 1; /* First argument is the command itself */ + struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + }; + struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN] = {0}; + size_t conn_cnt = 0U; + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + /* Populate the array of connected connections */ + bt_conn_foreach(BT_CONN_TYPE_LE, populate_connected_conns, (void *)connected_conns); + for (size_t i = 0; i < ARRAY_SIZE(connected_conns); i++) { + struct bt_conn *conn = connected_conns[i]; + + if (conn == NULL) { + break; + } + + conn_cnt++; + } + + if (cap_args > conn_cnt) { + shell_error(sh, "Cannot use %zu arguments for %zu connections", argc, conn_cnt); + + return -ENOEXEC; + } + + /* TODO: Add support for coordinated sets */ + + for (size_t i = 0U; i < cap_args; i++) { + const char *arg = argv[i + 1]; + long volume_offset; + + volume_offset = shell_strtol(arg, 10, &err); + if (err != 0) { + shell_error(sh, "Failed to parse volume offset from %s", arg); + + return -ENOEXEC; + } + + if (!IN_RANGE(volume_offset, BT_VOCS_MIN_OFFSET, BT_VOCS_MAX_OFFSET)) { + shell_error(sh, "Invalid volume_offset %lu", volume_offset); + + return -ENOEXEC; + } + + member_params[i].offset = (int16_t)volume_offset; + member_params[i].member.member = connected_conns[i]; + param.count++; + } + + shell_print(sh, "Setting volume offset on %zu connections", param.count); + + err = bt_cap_commander_change_volume_offset(¶m); + if (err != 0) { + shell_print(sh, "Failed to change volume offset: %d", err); + + return -ENOEXEC; + } + + return 0; +} +#endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ static int cmd_cap_commander(const struct shell *sh, size_t argc, char **argv) @@ -164,9 +254,13 @@ SHELL_STATIC_SUBCMD_SET_CREATE( #if defined(CONFIG_BT_VCP_VOL_CTLR) SHELL_CMD_ARG(change_volume, NULL, "Change volume on all connections ", cmd_cap_commander_change_volume, 2, 0), +#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) + SHELL_CMD_ARG(change_volume_offset, NULL, + "Change volume offset per connection ", + cmd_cap_commander_change_volume_offset, 2, CONFIG_BT_MAX_CONN - 1), +#endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ - SHELL_SUBCMD_SET_END -); + SHELL_SUBCMD_SET_END); SHELL_CMD_ARG_REGISTER(cap_commander, &cap_commander_cmds, "Bluetooth CAP commander shell commands", cmd_cap_commander, 1, 1);