From 5ab37692c0ca9fafd90a06d034ef60d7f9d0a684 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 22 Dec 2023 11:14:27 +0100 Subject: [PATCH] Bluetooth: CAP: Commander change microphone mute procedure Adds the CAP Commander Change Microphone Mute procedure. This procedure changes the microphone mute state on one or more CAP Acceptors. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/cap.h | 11 ++ subsys/bluetooth/audio/cap_commander.c | 181 ++++++++++++++++++++++++- subsys/bluetooth/audio/cap_internal.h | 5 +- 3 files changed, 192 insertions(+), 5 deletions(-) diff --git a/include/zephyr/bluetooth/audio/cap.h b/include/zephyr/bluetooth/audio/cap.h index 35a30f742ac..8119c0e2e9e 100644 --- a/include/zephyr/bluetooth/audio/cap.h +++ b/include/zephyr/bluetooth/audio/cap.h @@ -725,6 +725,17 @@ struct bt_cap_commander_cb { #endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ #if defined(CONFIG_BT_MICP_MIC_CTLR) + /** + * @brief Callback for bt_cap_commander_change_microphone_mute_state(). + * + * @param conn Pointer to the connection where the error + * occurred. NULL if @p err is 0 or if cancelled by + * bt_cap_commander_cancel() + * @param err 0 on success, BT_GATT_ERR() with a + * specific ATT (BT_ATT_ERR_*) error code or -ECANCELED if cancelled + * by bt_cap_commander_cancel(). + */ + void (*microphone_mute_changed)(struct bt_conn *conn, int err); #if defined(CONFIG_BT_MICP_MIC_CTLR_AICS) /** * @brief Callback for bt_cap_commander_change_microphone_gain_setting(). diff --git a/subsys/bluetooth/audio/cap_commander.c b/subsys/bluetooth/audio/cap_commander.c index 95e4affc848..b65eb453ce9 100644 --- a/subsys/bluetooth/audio/cap_commander.c +++ b/subsys/bluetooth/audio/cap_commander.c @@ -125,6 +125,11 @@ static void cap_commander_unicast_audio_proc_complete(void) #endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ #if defined(CONFIG_BT_MICP_MIC_CTLR) + case BT_CAP_COMMON_PROC_TYPE_MICROPHONE_MUTE_CHANGE: + if (cap_cb->microphone_mute_changed != NULL) { + cap_cb->microphone_mute_changed(failed_conn, err); + } + break; #if defined(CONFIG_BT_MICP_MIC_CTLR_AICS) case BT_CAP_COMMON_PROC_TYPE_MICROPHONE_GAIN_CHANGE: if (cap_cb->microphone_gain_changed != NULL) { @@ -441,7 +446,7 @@ static void cap_commander_vcp_vol_mute_cb(struct bt_vcp_vol_ctlr *vol_ctlr, int proc_param = &active_proc->proc_param.commander[active_proc->proc_done_cnt]; conn = proc_param->conn; active_proc->proc_initiated_cnt++; - if (proc_param->change_mute.mute) { + if (proc_param->change_vol_mute.mute) { err = bt_vcp_vol_ctlr_mute(bt_vcp_vol_ctlr_get_by_conn(conn)); } else { err = bt_vcp_vol_ctlr_unmute(bt_vcp_vol_ctlr_get_by_conn(conn)); @@ -500,14 +505,14 @@ int bt_cap_commander_change_volume_mute_state( * are kept valid */ active_proc->proc_param.commander[i].conn = member_conn; - active_proc->proc_param.commander[i].change_mute.mute = param->mute; + active_proc->proc_param.commander[i].change_vol_mute.mute = param->mute; } proc_param = &active_proc->proc_param.commander[0]; conn = proc_param->conn; active_proc->proc_initiated_cnt++; - if (proc_param->change_mute.mute) { + if (proc_param->change_vol_mute.mute) { err = bt_vcp_vol_ctlr_mute(bt_vcp_vol_ctlr_get_by_conn(conn)); } else { err = bt_vcp_vol_ctlr_unmute(bt_vcp_vol_ctlr_get_by_conn(conn)); @@ -754,16 +759,184 @@ static int cap_commander_register_micp_callbacks(void) return 0; } +static bool valid_change_microphone_mute_state_param( + const struct bt_cap_commander_change_microphone_mute_state_param *param) +{ + CHECKIF(param == NULL) { + LOG_DBG("param is NULL"); + return false; + } + + CHECKIF(param->count == 0) { + LOG_DBG("Invalid param->count: %u", param->count); + return false; + } + + CHECKIF(param->members == NULL) { + LOG_DBG("param->members is NULL"); + return false; + } + + CHECKIF(param->count > CONFIG_BT_MAX_CONN) { + LOG_DBG("param->count (%zu) is larger than CONFIG_BT_MAX_CONN (%d)", param->count, + CONFIG_BT_MAX_CONN); + return false; + } + + for (size_t i = 0U; i < param->count; i++) { + const union bt_cap_set_member *member = ¶m->members[i]; + const struct bt_cap_common_client *client = + bt_cap_common_get_client(param->type, member); + + CHECKIF(client == NULL) { + LOG_DBG("Invalid param->members[%zu]", i); + return false; + } + + CHECKIF(bt_micp_mic_ctlr_get_by_conn(client->conn) == NULL) { + LOG_DBG("Microphone control not available for param->members[%zu]", i); + return false; + } + + for (size_t j = 0U; j < i; j++) { + const union bt_cap_set_member *other = ¶m->members[j]; + + CHECKIF(other == member) { + LOG_DBG("param->members[%zu] (%p) is duplicated by " + "param->members[%zu] (%p)", + j, other, i, member); + return false; + } + } + } + + return true; +} + +static void cap_commander_micp_mic_mute_cb(struct bt_micp_mic_ctlr *mic_ctlr, int err) +{ + struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc(); + struct bt_conn *conn; + int micp_err; + + LOG_DBG("mic_ctlr %p", (void *)mic_ctlr); + + micp_err = bt_micp_mic_ctlr_conn_get(mic_ctlr, &conn); + if (micp_err != 0) { + LOG_ERR("Failed to get conn by mic_ctlr: %d", micp_err); + return; + } + + LOG_DBG("conn %p", (void *)conn); + if (!bt_cap_common_conn_in_active_proc(conn)) { + /* State change happened outside of a procedure; ignore */ + return; + } + + if (err != 0) { + LOG_DBG("Failed to change microphone mute: %d", err); + bt_cap_common_abort_proc(conn, err); + } else { + active_proc->proc_done_cnt++; + + LOG_DBG("Conn %p mute updated (%zu/%zu streams done)", (void *)conn, + active_proc->proc_done_cnt, active_proc->proc_cnt); + } + + if (bt_cap_common_proc_is_aborted()) { + LOG_DBG("Proc is aborted"); + if (bt_cap_common_proc_all_handled()) { + LOG_DBG("All handled"); + cap_commander_unicast_audio_proc_complete(); + } + + return; + } + + if (!bt_cap_common_proc_is_done()) { + const struct bt_cap_commander_proc_param *proc_param; + + proc_param = &active_proc->proc_param.commander[active_proc->proc_done_cnt]; + conn = proc_param->conn; + active_proc->proc_initiated_cnt++; + if (proc_param->change_mic_mute.mute) { + err = bt_micp_mic_ctlr_mute(bt_micp_mic_ctlr_get_by_conn(conn)); + } else { + err = bt_micp_mic_ctlr_unmute(bt_micp_mic_ctlr_get_by_conn(conn)); + } + + if (err != 0) { + LOG_DBG("Failed to change mute for conn %p: %d", (void *)conn, err); + bt_cap_common_abort_proc(conn, err); + cap_commander_unicast_audio_proc_complete(); + } + } else { + cap_commander_unicast_audio_proc_complete(); + } +} + int bt_cap_commander_change_microphone_mute_state( const struct bt_cap_commander_change_microphone_mute_state_param *param) { + const struct bt_cap_commander_proc_param *proc_param; + struct bt_cap_common_proc *active_proc; + struct bt_conn *conn; + int err; + + if (bt_cap_common_proc_is_active()) { + LOG_DBG("A CAP procedure is already in progress"); + + return -EBUSY; + } + + if (!valid_change_microphone_mute_state_param(param)) { + return -EINVAL; + } + + bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_MICROPHONE_MUTE_CHANGE, param->count); + + mic_ctlr_cb.mute_written = cap_commander_micp_mic_mute_cb; + mic_ctlr_cb.unmute_written = cap_commander_micp_mic_mute_cb; if (!micp_callbacks_registered && cap_commander_register_micp_callbacks() != 0) { LOG_DBG("Failed to register MICP callbacks"); return -ENOEXEC; } - return -ENOSYS; + active_proc = bt_cap_common_get_active_proc(); + + for (size_t i = 0U; i < param->count; i++) { + struct bt_conn *member_conn = + bt_cap_common_get_member_conn(param->type, ¶m->members[i]); + + CHECKIF(member_conn == NULL) { + LOG_DBG("Invalid param->members[%zu]", i); + return -EINVAL; + } + + /* Store the necessary parameters as we cannot assume that the supplied parameters + * are kept valid + */ + active_proc->proc_param.commander[i].conn = member_conn; + active_proc->proc_param.commander[i].change_mic_mute.mute = param->mute; + } + + proc_param = &active_proc->proc_param.commander[0]; + conn = proc_param->conn; + active_proc->proc_initiated_cnt++; + + if (proc_param->change_mic_mute.mute) { + err = bt_micp_mic_ctlr_mute(bt_micp_mic_ctlr_get_by_conn(conn)); + } else { + err = bt_micp_mic_ctlr_unmute(bt_micp_mic_ctlr_get_by_conn(conn)); + } + + if (err != 0) { + LOG_DBG("Failed to set microphone mute state for conn %p: %d", (void *)conn, err); + return -ENOEXEC; + } + + return 0; } #if defined(CONFIG_BT_MICP_MIC_CTLR_AICS) diff --git a/subsys/bluetooth/audio/cap_internal.h b/subsys/bluetooth/audio/cap_internal.h index 2c56ef1f742..c2dc861da3d 100644 --- a/subsys/bluetooth/audio/cap_internal.h +++ b/subsys/bluetooth/audio/cap_internal.h @@ -79,7 +79,7 @@ struct bt_cap_commander_proc_param { } change_volume; struct { bool mute; - } change_mute; + } change_vol_mute; #endif /* CONFIG_BT_VCP_VOL_CTLR */ #if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) struct { @@ -88,6 +88,9 @@ struct bt_cap_commander_proc_param { } change_offset; #endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */ #if defined(CONFIG_BT_MICP_MIC_CTLR) + struct { + bool mute; + } change_mic_mute; #if defined(CONFIG_BT_MICP_MIC_CTLR_AICS) struct { int8_t gain;