diff --git a/include/bluetooth/audio/mics.h b/include/bluetooth/audio/mics.h new file mode 100644 index 00000000000..43152d3e84f --- /dev/null +++ b/include/bluetooth/audio/mics.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_MICS_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_MICS_H_ + +/** + * @brief Microphone Input Control Service (Microphone Input Control Service) + * + * @defgroup bt_gatt_mics Microphone Input Control Service (Microphone Input Control Service) + * + * @ingroup bluetooth + * @{ + * + * [Experimental] Users should note that the APIs can change + * as a part of ongoing development. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_BT_MICS) +#define BT_MICS_AICS_CNT CONFIG_BT_MICS_AICS_INSTANCE_COUNT +#else +#define BT_MICS_AICS_CNT 0 +#endif /* CONFIG_BT_MICS */ + +/** Application error codes */ +#define BT_MICS_ERR_MUTE_DISABLED 0x80 +#define BT_MICS_ERR_VAL_OUT_OF_RANGE 0x81 + +/** Microphone Input Control Service mute states */ +#define BT_MICS_MUTE_UNMUTED 0x00 +#define BT_MICS_MUTE_MUTED 0x01 +#define BT_MICS_MUTE_DISABLED 0x02 + +/** @brief Register parameters structure for Microphone Input Control Service */ +struct bt_mics_register_param { + /** Register parameter structure for Audio Input Control Services */ + struct bt_aics_register_param aics_param[BT_MICS_AICS_CNT]; + + /** Microphone Input Control Service callback structure. */ + struct bt_mics_cb *cb; +}; + +/** + * @brief Microphone Input Control Service instance + * + * Used for to represent a Microphone Input Control Service, for either + * a client or a server instance. The instance pointers either represent local + * server instances, or remote service instances. + */ +struct bt_mics { + /** Number of Audio Input Control Service instances */ + uint8_t aics_cnt; + /** Array of pointers to Audio Input Control Service instances */ + struct bt_aics **aics; +}; + +/** + * @brief Initialize the Microphone Input Control Service + * + * This will enable the service and make it discoverable by clients. + * + * @param param Pointer to a initialization structure. + * + * @return 0 if success, errno on failure. + */ +int bt_mics_register(struct bt_mics_register_param *param); + +/** + * @brief Get Microphone Input Control Service pointer + * + * Returns a pointer to a struct that contains information about the + * Microphone Input Control Service instance, such as pointers to the + * Audio Input Control Service instances. + * + * @param conn Connection to peer device, or NULL to get server value. + * @param[out] service Pointer to store the result in. + * + * @return 0 if success, errno on failure. + */ +int bt_mics_get(struct bt_conn *conn, struct bt_mics *service); + +/** + * @brief Callback function for @ref bt_mics_discover. + * + * This callback is only used for the client. + * + * @param conn The connection that was used to discover + * Microphone Input Control Service. + * @param err Error value. 0 on success, GATT error or errno on fail. + * @param aics_count Number of Audio Input Control Service instances on + * peer device. + */ +typedef void (*bt_mics_discover_cb)(struct bt_conn *conn, int err, + uint8_t aics_count); + +/** + * @brief Callback function for Microphone Input Control Service mute. + * + * Called when the value is read, + * or if the value is changed by either the server or client. + * + * @param conn Connection to peer device, or NULL if local server read. + * @param err Error value. 0 on success, GATT error or errno on fail. + * For notifications, this will always be 0. For reads, if + * this is 0, the @p volume and @p mute values are the last + * known values. + * @param mute The mute setting of the Microphone Input Control Service. + */ +typedef void (*bt_mics_mute_read_cb)(struct bt_conn *conn, int err, + uint8_t mute); + +/** + * @brief Callback function for Microphone Input Control Service mute/unmute. + * + * @param conn Connection to peer device, or NULL if local server read. + * @param err Error value. 0 on success, GATT error or errno on fail. + * @param req_val The requested mute value. + */ +typedef void (*bt_mics_mute_write_cb)(struct bt_conn *conn, int err); + +struct bt_mics_cb { + bt_mics_mute_read_cb mute; + +#if defined(CONFIG_BT_MICS_CLIENT) + bt_mics_discover_cb discover; + bt_mics_mute_write_cb mute_write; + bt_mics_mute_write_cb unmute_write; + + /** Audio Input Control Service client callback */ + struct bt_aics_cb aics_cb; +#endif /* CONFIG_BT_MICS_CLIENT */ +}; + +/** + * @brief Discover Microphone Input Control Service + * + * This will start a GATT discovery and setup handles and subscriptions. + * This shall be called once before any other actions can be executed for + * the peer device. + * + * This shall only be done as the client. + * + * @param conn The connection to initialize the profile for. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_discover(struct bt_conn *conn); + +/** + * @brief Unmute the server. + * + * @param conn Connection to peer device, or NULL to set local server value. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_unmute(struct bt_conn *conn); + +/** + * @brief Mute the server. + * + * @param conn Connection to peer device, or NULL to set local server value. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_mute(struct bt_conn *conn); + +/** + * @brief Disable the mute functionality. + * + * Can be reenabled by called @ref bt_mics_mute or @ref bt_mics_unmute. + * This can only be done as the server. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_mute_disable(void); + +/** + * @brief Read the mute state of a Microphone Input Control Service. + * + * @param conn Connection to peer device, or NULL to read local server value. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_mute_get(struct bt_conn *conn); + +/** + * @brief Read the Audio Input Control Service input state. + * + * @param conn Connection to peer device, + * or NULL to read local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_state_get(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Read the Audio Input Control Service gain settings. + * + * @param conn Connection to peer device, + * or NULL to read local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_gain_setting_get(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Read the Audio Input Control Service input type. + * + * @param conn Connection to peer device, + * or NULL to read local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_type_get(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Read the Audio Input Control Service input status. + * + * @param conn Connection to peer device, + * or NULL to read local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_status_get(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Unmute the Audio Input Control Service input. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_unmute(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Mute the Audio Input Control Service input. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_mute(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Set input gain to manual. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_manual_gain_set(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Set Audio Input Control Service input gain to automatic. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_automatic_gain_set(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Set Audio Input Control Service input gain. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * @param gain The gain in dB to set (-128 to 127). + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_gain_set(struct bt_conn *conn, struct bt_aics *inst, + int8_t gain); + +/** + * @brief Read the Audio Input Control Service description. + * + * @param conn Connection to peer device, + * or NULL to read local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_description_get(struct bt_conn *conn, struct bt_aics *inst); + +/** + * @brief Set the Audio Input Control Service description. + * + * @param conn Connection to peer device, + * or NULL to set local server value. + * @param inst Pointer to the Audio Input Control Service instance. + * @param description The description to set. + * + * @return 0 on success, GATT error value on fail. + */ +int bt_mics_aics_description_set(struct bt_conn *conn, struct bt_aics *inst, + const char *description); + +/** + * @brief Deactivates a Audio Input Control Service instance. + * + * Audio Input Control Services are activated by default, but this will allow + * the server deactivate a Audio Input Control Service. + * This can only be done as the server. + * + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 if success, errno on failure. + */ +int bt_mics_aics_deactivate(struct bt_aics *inst); + +/** + * @brief Activates a Audio Input Control Service instance. + * + * Audio Input Control Services are activated by default, but this will allow + * the server reactivate a Audio Input Control Service instance after it has + * been deactivated with @ref bt_mics_aics_deactivate. + * This can only be done as the server. + * + * @param inst Pointer to the Audio Input Control Service instance. + * + * @return 0 if success, errno on failure. + */ +int bt_mics_aics_activate(struct bt_aics *inst); + +/** + * @brief Registers the callbacks used by Microphone Input Control Service client. + * + * This can only be done as the client. + * + * @param cb The callback structure. + * + * @return 0 if success, errno on failure. + */ +int bt_mics_client_cb_register(struct bt_mics_cb *cb); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_MICS_H_ */ diff --git a/include/bluetooth/uuid.h b/include/bluetooth/uuid.h index f8a16bbc09e..af67ae0d60d 100644 --- a/include/bluetooth/uuid.h +++ b/include/bluetooth/uuid.h @@ -437,6 +437,15 @@ struct bt_uuid_128 { */ #define BT_UUID_VOCS \ BT_UUID_DECLARE_16(BT_UUID_VOCS_VAL) +/** @def BT_UUID_MICS_VAL + * @brief Microphone Input Control Service value + */ +#define BT_UUID_MICS_VAL 0x184D +/** @def BT_UUID_MICS + * @brief Microphone Input Control Service + */ +#define BT_UUID_MICS \ + BT_UUID_DECLARE_16(BT_UUID_MICS_VAL) /** @def BT_UUID_GATT_PRIMARY_VAL * @brief GATT Primary Service UUID value */ @@ -1448,6 +1457,15 @@ struct bt_uuid_128 { */ #define BT_UUID_VOCS_DESCRIPTION \ BT_UUID_DECLARE_16(BT_UUID_VOCS_DESCRIPTION_VAL) +/** @def BT_UUID_MICS_MUTE_VAL + * @brief Microphone Input Control Service Mute value + */ +#define BT_UUID_MICS_MUTE_VAL 0x2BC3 +/** @def BT_UUID_MICS_MUTE + * @brief Microphone Input Control Service Mute + */ +#define BT_UUID_MICS_MUTE \ + BT_UUID_DECLARE_16(BT_UUID_MICS_MUTE_VAL) /* * Protocol UUIDs */ diff --git a/subsys/bluetooth/audio/CMakeLists.txt b/subsys/bluetooth/audio/CMakeLists.txt index 3dd4a7972af..0894dd98656 100644 --- a/subsys/bluetooth/audio/CMakeLists.txt +++ b/subsys/bluetooth/audio/CMakeLists.txt @@ -14,3 +14,8 @@ if (CONFIG_BT_VCS OR CONFIG_BT_VCS_CLIENT) zephyr_library_sources(vcs.c) endif() zephyr_library_sources_ifdef(CONFIG_BT_VCS_CLIENT vcs_client.c) + +if (CONFIG_BT_MICS OR CONFIG_BT_MICS_CLIENT) + zephyr_library_sources(mics.c) +endif() +zephyr_library_sources_ifdef(CONFIG_BT_MICS_CLIENT mics_client.c) diff --git a/subsys/bluetooth/audio/Kconfig b/subsys/bluetooth/audio/Kconfig index cb06deeb6c8..1de8dfff8d1 100644 --- a/subsys/bluetooth/audio/Kconfig +++ b/subsys/bluetooth/audio/Kconfig @@ -53,5 +53,6 @@ config BT_AUDIO_DEBUG source "subsys/bluetooth/audio/Kconfig.vocs" source "subsys/bluetooth/audio/Kconfig.aics" source "subsys/bluetooth/audio/Kconfig.vcs" +source "subsys/bluetooth/audio/Kconfig.mics" endif # BT_AUDIO diff --git a/subsys/bluetooth/audio/Kconfig.mics b/subsys/bluetooth/audio/Kconfig.mics new file mode 100644 index 00000000000..0388f650366 --- /dev/null +++ b/subsys/bluetooth/audio/Kconfig.mics @@ -0,0 +1,84 @@ +# Bluetooth Audio - Microphone Input Control Service options +# +# Copyright (c) 2020 Bose Corporation +# Copyright (c) 2020-2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +if BT_AUDIO + +##################### Microphone Input Control Service ##################### + +config BT_MICS + bool "Microphone Input Control Service Support" + default n + help + This option enables support for Microphone Input Control Service. + +if BT_MICS + +config BT_MICS_AICS_INSTANCE_COUNT + int "Audio Input Control Service instance count for Microphone Input Control Service" + default 0 + range 0 BT_AICS_MAX_INSTANCE_COUNT + help + This option sets the number of instances of Audio Input Control + Services for MICS. + +config BT_MICS_AICS + bool # Hidden + default y if BT_MICS_AICS_INSTANCE_COUNT > 0 + help + This hidden option makes it possible to easily check if AICS is + enabled for MICS. + +############# DEBUG ############# + +config BT_DEBUG_MICS + bool "Microphone Input Control Service debug" + depends on BT_AUDIO_DEBUG + help + Use this option to enable Microphone Input Control Service debug logs + for the Bluetooth Audio functionality. + +endif # BT_MICS + +##################### Microphone Control Profile Client ##################### + +config BT_MICS_CLIENT + bool "Microphone Control Profile Support" + select BT_GATT_CLIENT + select BT_GATT_AUTO_DISCOVER_CCC + help + This option enables support for Microphone Control Profile. + +if BT_MICS_CLIENT + +config BT_MICS_CLIENT_MAX_AICS_INST + int "Maximum number of Audio Input Control Service instances to setup" + default 0 + range 0 3 + help + Sets the maximum number of Audio Input Control Service (AICS) + instances to setup and use. + +config BT_MICS_CLIENT_AICS + bool # Hidden + default y if BT_MICS_CLIENT_MAX_AICS_INST > 0 + help + This hidden option makes it possible to easily check if AICS is + enabled for MICS client. + +############# DEBUG ############# + +config BT_DEBUG_MICS_CLIENT + bool "Microphone Control Profile debug" + depends on BT_AUDIO_DEBUG + help + Use this option to enable Microphone Control Profile debug logs for + the Bluetooth Audio functionality. + +endif # BT_MICS_CLIENT + +endif # BT_AUDIO diff --git a/subsys/bluetooth/audio/mics.c b/subsys/bluetooth/audio/mics.c new file mode 100644 index 00000000000..18083c9b00c --- /dev/null +++ b/subsys/bluetooth/audio/mics.c @@ -0,0 +1,496 @@ +/* Bluetooth MICS + * + * Copyright (c) 2020 Bose Corporation + * Copyright (c) 2020-2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "mics_internal.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_MICS) +#define LOG_MODULE_NAME bt_mics +#include "common/log.h" + +#if defined(CONFIG_BT_MICS) +struct mics_instance { + uint8_t mute; + struct bt_mics_cb *cb; + struct bt_gatt_service *service_p; + struct bt_aics *aics_insts[CONFIG_BT_MICS_AICS_INSTANCE_COUNT]; +}; + +static struct mics_instance mics_inst; + +static void mute_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + BT_DBG("value 0x%04x", value); +} + +static ssize_t read_mute(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + BT_DBG("Mute %u", mics_inst.mute); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + &mics_inst.mute, sizeof(mics_inst.mute)); +} + +static ssize_t write_mute(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) +{ + const uint8_t *val = buf; + + if (offset > 0) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (len != sizeof(mics_inst.mute)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + if ((conn != NULL && *val == BT_MICS_MUTE_DISABLED) || + *val > BT_MICS_MUTE_DISABLED) { + return BT_GATT_ERR(BT_MICS_ERR_VAL_OUT_OF_RANGE); + } + + if (conn != NULL && mics_inst.mute == BT_MICS_MUTE_DISABLED) { + return BT_GATT_ERR(BT_MICS_ERR_MUTE_DISABLED); + } + + BT_DBG("%u", *val); + + if (*val != mics_inst.mute) { + mics_inst.mute = *val; + + bt_gatt_notify_uuid(NULL, BT_UUID_MICS_MUTE, + mics_inst.service_p->attrs, + &mics_inst.mute, sizeof(mics_inst.mute)); + + if (mics_inst.cb != NULL && mics_inst.cb->mute != NULL) { + mics_inst.cb->mute(NULL, 0, mics_inst.mute); + } + } + + return len; +} + + +#define DUMMY_INCLUDE(i, _) BT_GATT_INCLUDE_SERVICE(NULL), +#define AICS_INCLUDES(cnt) UTIL_LISTIFY(cnt, DUMMY_INCLUDE) + +#define BT_MICS_SERVICE_DEFINITION \ + BT_GATT_PRIMARY_SERVICE(BT_UUID_MICS), \ + AICS_INCLUDES(CONFIG_BT_MICS_AICS_INSTANCE_COUNT) \ + BT_GATT_CHARACTERISTIC(BT_UUID_MICS_MUTE, \ + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_NOTIFY, \ + BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \ + read_mute, write_mute, NULL), \ + BT_GATT_CCC(mute_cfg_changed, \ + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT) + +#define MICS_ATTR_COUNT \ + ARRAY_SIZE(((struct bt_gatt_attr []){ BT_MICS_SERVICE_DEFINITION })) +#define MICS_INCL_COUNT (CONFIG_BT_MICS_AICS_INSTANCE_COUNT) + +static struct bt_gatt_attr mics_attrs[] = { BT_MICS_SERVICE_DEFINITION }; +static struct bt_gatt_service mics_svc; + +static int prepare_aics_inst(struct bt_mics_register_param *param) +{ + int i; + int j; + int err; + + for (j = 0, i = 0; i < ARRAY_SIZE(mics_attrs); i++) { + if (bt_uuid_cmp(mics_attrs[i].uuid, BT_UUID_GATT_INCLUDE) == 0) { + mics_inst.aics_insts[j] = bt_aics_free_instance_get(); + if (mics_inst.aics_insts[j] == NULL) { + BT_DBG("Could not get free AICS instances[%u]", j); + return -ENOMEM; + } + + err = bt_aics_register(mics_inst.aics_insts[j], + ¶m->aics_param[j]); + if (err != 0) { + BT_DBG("Could not register AICS instance[%u]: %d", j, err); + return err; + } + + mics_attrs[i].user_data = bt_aics_svc_decl_get(mics_inst.aics_insts[j]); + j++; + + if (j == CONFIG_BT_MICS_AICS_INSTANCE_COUNT) { + break; + } + } + } + + __ASSERT(j == CONFIG_BT_MICS_AICS_INSTANCE_COUNT, + "Invalid AICS instance count"); + + return 0; +} + +/****************************** PUBLIC API ******************************/ +int bt_mics_register(struct bt_mics_register_param *param) +{ + int err; + + __ASSERT(param, "MICS register parameter cannot be NULL"); + + if (CONFIG_BT_MICS_AICS_INSTANCE_COUNT > 0) { + prepare_aics_inst(param); + } + + mics_svc = (struct bt_gatt_service)BT_GATT_SERVICE(mics_attrs); + mics_inst.service_p = &mics_svc; + err = bt_gatt_service_register(&mics_svc); + + if (err != 0) { + BT_ERR("MICS service register failed: %d", err); + } + + mics_inst.cb = param->cb; + + return err; +} + +int bt_mics_aics_deactivate(struct bt_aics *inst) +{ + CHECKIF(inst == NULL) { + return -EINVAL; + } + + if (CONFIG_BT_MICS_AICS_INSTANCE_COUNT > 0) { + return bt_aics_deactivate(inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_activate(struct bt_aics *inst) +{ + CHECKIF(inst == NULL) { + return -EINVAL; + } + + if (CONFIG_BT_MICS_AICS_INSTANCE_COUNT > 0) { + return bt_aics_activate(inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_mute_disable(void) +{ + uint8_t val = BT_MICS_MUTE_DISABLED; + int err = write_mute(NULL, NULL, &val, sizeof(val), 0, 0); + + return err > 0 ? 0 : err; +} + +#endif /* CONFIG_BT_MICS */ + +static bool valid_aics_inst(struct bt_aics *aics) +{ + if (aics == NULL) { + return false; + } + +#if defined(CONFIG_BT_MICS) + for (int i = 0; i < ARRAY_SIZE(mics_inst.aics_insts); i++) { + if (mics_inst.aics_insts[i] == aics) { + return true; + } + } +#endif /* CONFIG_BT_MICS */ + return false; +} + +int bt_mics_get(struct bt_conn *conn, struct bt_mics *service) +{ + CHECKIF(service == NULL) { + BT_DBG("NULL service pointer"); + return -EINVAL; + } + +#if defined(CONFIG_BT_MICS_CLIENT) + if (conn != NULL) { + return bt_mics_client_service_get(conn, service); + } +#endif /* CONFIG_BT_MICS_CLIENT */ + +#if defined(CONFIG_BT_MICS) + if (conn == NULL) { + service->aics_cnt = ARRAY_SIZE(mics_inst.aics_insts); + service->aics = mics_inst.aics_insts; + + return 0; + } +#endif /* CONFIG_BT_MICS */ + return -EOPNOTSUPP; +} + +int bt_mics_unmute(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_MICS_CLIENT) + if (conn != NULL) { + return bt_mics_client_unmute(conn); + } +#endif /* CONFIG_BT_MICS_CLIENT */ + +#if defined(CONFIG_BT_MICS) + if (conn == NULL) { + uint8_t val = BT_MICS_MUTE_UNMUTED; + int err = write_mute(NULL, NULL, &val, sizeof(val), 0, 0); + + return err > 0 ? 0 : err; + } +#endif /* CONFIG_BT_MICS */ + return -EOPNOTSUPP; +} + +int bt_mics_mute(struct bt_conn *conn) +{ + if (conn != NULL) { + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT)) { + return bt_mics_client_mute(conn); + } else { + return -EOPNOTSUPP; + } + } + +#if defined(CONFIG_BT_MICS) + uint8_t val = BT_MICS_MUTE_MUTED; + int err = write_mute(NULL, NULL, &val, sizeof(val), 0, 0); + + return err > 0 ? 0 : err; +#else + return -EOPNOTSUPP; +#endif /* CONFIG_BT_MICS */ +} + +int bt_mics_mute_get(struct bt_conn *conn) +{ + if (conn != NULL) { + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT)) { + return bt_mics_client_mute_get(conn); + } else { + return -EOPNOTSUPP; + } + } + +#if defined(CONFIG_BT_MICS) + if (mics_inst.cb && mics_inst.cb->mute) { + mics_inst.cb->mute(NULL, 0, mics_inst.mute); + } + + return 0; +#else + return -EOPNOTSUPP; +#endif /* CONFIG_BT_MICS */ +} + +int bt_mics_aics_state_get(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_state_get(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_state_get(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_gain_setting_get(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_gain_setting_get(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_gain_setting_get(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_type_get(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_type_get(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_type_get(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_status_get(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_status_get(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_status_get(NULL, inst); + } + + return -EOPNOTSUPP; +} +int bt_mics_aics_unmute(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_unmute(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_unmute(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_mute(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_mute(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_mute(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_manual_gain_set(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_manual_gain_set(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_manual_gain_set(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_automatic_gain_set(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_automatic_gain_set(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_automatic_gain_set(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_gain_set(struct bt_conn *conn, struct bt_aics *inst, + int8_t gain) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_gain_set(conn, inst, gain); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_gain_set(NULL, inst, gain); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_description_get(struct bt_conn *conn, struct bt_aics *inst) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_description_get(conn, inst); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_description_get(NULL, inst); + } + + return -EOPNOTSUPP; +} + +int bt_mics_aics_description_set(struct bt_conn *conn, struct bt_aics *inst, + const char *description) +{ + if (IS_ENABLED(CONFIG_BT_MICS_CLIENT_AICS) && + conn != NULL && + bt_mics_client_valid_aics_inst(conn, inst)) { + return bt_aics_description_set(conn, inst, description); + } + + if (IS_ENABLED(CONFIG_BT_MICS_AICS) && + conn == NULL && + valid_aics_inst(inst)) { + return bt_aics_description_set(NULL, inst, description); + } + + return -EOPNOTSUPP; +} diff --git a/subsys/bluetooth/audio/mics_client.c b/subsys/bluetooth/audio/mics_client.c new file mode 100644 index 00000000000..ff2c07b50c1 --- /dev/null +++ b/subsys/bluetooth/audio/mics_client.c @@ -0,0 +1,544 @@ +/* Bluetooth MICS client - Microphone Control Profile - Client */ + +/* + * Copyright (c) 2020 Bose Corporation + * Copyright (c) 2020-2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_MICS_CLIENT) +#define LOG_MODULE_NAME bt_mics_client +#include "common/log.h" + +struct mics_instance { + uint16_t start_handle; + uint16_t end_handle; + uint16_t mute_handle; + struct bt_gatt_subscribe_params mute_sub_params; + struct bt_gatt_discover_params mute_sub_disc_params; + + bool busy; + uint8_t mute_val_buf[1]; /* Mute value is a single octet */ + struct bt_gatt_write_params write_params; + struct bt_gatt_read_params read_params; + struct bt_gatt_discover_params discover_params; + + uint8_t aics_inst_cnt; + struct bt_aics *aics[CONFIG_BT_MICS_CLIENT_MAX_AICS_INST]; +}; + +/* Callback functions */ +static struct bt_mics_cb *mics_client_cb; + +static struct mics_instance mics_insts[CONFIG_BT_MAX_CONN]; +static struct bt_uuid *mics_uuid = BT_UUID_MICS; + +bool bt_mics_client_valid_aics_inst(struct bt_conn *conn, struct bt_aics *aics) +{ + uint8_t conn_index; + + CHECKIF(conn == NULL) { + return -EINVAL; + } + + if (aics == NULL) { + return false; + } + + conn_index = bt_conn_index(conn); + + for (int i = 0; i < ARRAY_SIZE(mics_insts[conn_index].aics); i++) { + if (mics_insts[conn_index].aics[i] == aics) { + return true; + } + } + + return false; +} + +static uint8_t mute_notify_handler(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) +{ + uint8_t *mute_val; + + if (data != NULL) { + if (length == sizeof(*mute_val)) { + mute_val = (uint8_t *)data; + BT_DBG("Mute %u", *mute_val); + if (mics_client_cb != NULL && + mics_client_cb->mute != NULL) { + mics_client_cb->mute(conn, 0, *mute_val); + } + } else { + BT_DBG("Invalid length %u (expected %zu)", + length, sizeof(*mute_val)); + + } + } + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t mics_client_read_mute_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) +{ + uint8_t cb_err = err; + uint8_t *mute_val = NULL; + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + mics_inst->busy = false; + + if (err > 0) { + BT_DBG("err: 0x%02X", err); + } else if (data != NULL) { + if (length == sizeof(*mute_val)) { + mute_val = (uint8_t *)data; + BT_DBG("Mute %u", *mute_val); + } else { + BT_DBG("Invalid length %u (expected %zu)", + length, sizeof(*mute_val)); + cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN; + } + } + + if (mics_client_cb != NULL && mics_client_cb->mute != NULL) { + mics_client_cb->mute(conn, cb_err, *mute_val); + } + + return BT_GATT_ITER_STOP; +} + +static void mics_client_write_mics_mute_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_write_params *params) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + uint8_t mute_val = mics_inst->mute_val_buf[0]; + + BT_DBG("Write %s (0x%02X)", err ? "failed" : "successful", err); + + mics_inst->busy = false; + + if (mute_val == BT_MICS_MUTE_UNMUTED) { + if (mics_client_cb != NULL && + mics_client_cb->unmute_write != NULL) { + mics_client_cb->unmute_write(conn, err); + } + + } else { + if (mics_client_cb != NULL && + mics_client_cb->mute_write != NULL) { + mics_client_cb->mute_write(conn, err); + } + } +} + +static void aics_discover_cb(struct bt_conn *conn, struct bt_aics *inst, + int err) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (err == 0) { + /* Continue discovery of included services */ + err = bt_gatt_discover(conn, &mics_inst->discover_params); + } + + if (err != 0) { + BT_DBG("Discover failed (err %d)", err); + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, err, 0); + } + } +} + +static uint8_t mics_discover_include_func( + struct bt_conn *conn, const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (attr == NULL) { + BT_DBG("Discover include complete for MICS: %u AICS", + mics_inst->aics_inst_cnt); + (void)memset(params, 0, sizeof(*params)); + + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, 0, 0); + } + + return BT_GATT_ITER_STOP; + } + + BT_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle); + + if (params->type == BT_GATT_DISCOVER_INCLUDE) { + struct bt_gatt_include *include = (struct bt_gatt_include *)attr->user_data; + + BT_DBG("Include UUID %s", bt_uuid_str(include->uuid)); + + if (bt_uuid_cmp(include->uuid, BT_UUID_AICS) == 0 && + mics_inst->aics_inst_cnt < CONFIG_BT_MICS_CLIENT_MAX_AICS_INST) { + uint8_t inst_idx; + int err; + struct bt_aics_discover_param param = { + .start_handle = include->start_handle, + .end_handle = include->end_handle, + }; + + /* Update discover params so we can continue where we + * left off after bt_aics_discover + */ + mics_inst->discover_params.start_handle = attr->handle + 1; + + inst_idx = mics_inst->aics_inst_cnt++; + err = bt_aics_discover(conn, mics_inst->aics[inst_idx], + ¶m); + if (err != 0) { + BT_DBG("AICS Discover failed (err %d)", err); + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, err, 0); + } + } + return BT_GATT_ITER_STOP; + } + } + + return BT_GATT_ITER_CONTINUE; +} + +/** + * @brief This will discover all characteristics on the server, retrieving the + * handles of the writeable characteristics and subscribing to all notify and + * indicate characteristics. + */ +static uint8_t mics_discover_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (attr == NULL) { + int err = 0; + + BT_DBG("Setup complete for MICS"); + (void)memset(params, 0, sizeof(*params)); + if (CONFIG_BT_MICS_CLIENT_MAX_AICS_INST > 0) { + /* Discover included services */ + mics_inst->discover_params.start_handle = mics_inst->start_handle; + mics_inst->discover_params.end_handle = mics_inst->end_handle; + mics_inst->discover_params.type = BT_GATT_DISCOVER_INCLUDE; + mics_inst->discover_params.func = mics_discover_include_func; + + err = bt_gatt_discover(conn, + &mics_inst->discover_params); + if (err != 0) { + BT_DBG("Discover failed (err %d)", err); + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, err, 0); + } + } + } else { + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, err, 0); + } + } + return BT_GATT_ITER_STOP; + } + + BT_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle); + + if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) { + struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; + struct bt_gatt_subscribe_params *sub_params = NULL; + + if (bt_uuid_cmp(chrc->uuid, BT_UUID_MICS_MUTE) == 0) { + BT_DBG("Mute"); + mics_inst->mute_handle = chrc->value_handle; + sub_params = &mics_inst->mute_sub_params; + sub_params->disc_params = &mics_inst->mute_sub_disc_params; + } + + if (sub_params != NULL) { + int err; + + /* With ccc_handle == 0 it will use auto discovery */ + sub_params->ccc_handle = 0; + sub_params->end_handle = mics_inst->end_handle; + sub_params->value = BT_GATT_CCC_NOTIFY; + sub_params->value_handle = chrc->value_handle; + sub_params->notify = mute_notify_handler; + + err = bt_gatt_subscribe(conn, sub_params); + if (err == 0) { + BT_DBG("Subscribed to handle 0x%04X", + attr->handle); + } else { + BT_DBG("Could not subscribe to handle 0x%04X: %d", + attr->handle, err); + } + } + } + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t primary_discover_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (attr == NULL) { + BT_DBG("Could not find a MICS instance on the server"); + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, -ENODATA, 0); + } + return BT_GATT_ITER_STOP; + } + + BT_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle); + + if (params->type == BT_GATT_DISCOVER_PRIMARY) { + struct bt_gatt_service_val *prim_service = + (struct bt_gatt_service_val *)attr->user_data; + int err; + + BT_DBG("Primary discover complete"); + mics_inst->start_handle = attr->handle + 1; + mics_inst->end_handle = prim_service->end_handle; + + /* Discover characteristics */ + mics_inst->discover_params.uuid = NULL; + mics_inst->discover_params.start_handle = mics_inst->start_handle; + mics_inst->discover_params.end_handle = mics_inst->end_handle; + mics_inst->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + mics_inst->discover_params.func = mics_discover_func; + + err = bt_gatt_discover(conn, &mics_inst->discover_params); + if (err != 0) { + BT_DBG("Discover failed (err %d)", err); + if (mics_client_cb != NULL && + mics_client_cb->discover != NULL) { + mics_client_cb->discover(conn, err, 0); + } + } + + return BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_CONTINUE; +} + +static void mics_client_reset(struct bt_conn *conn) +{ + struct mics_instance *mics_inst = &mics_insts[bt_conn_index(conn)]; + + mics_inst->start_handle = 0; + mics_inst->end_handle = 0; + mics_inst->mute_handle = 0; + mics_inst->aics_inst_cnt = 0; + + /* It's okay if this fails */ + (void)bt_gatt_unsubscribe(conn, &mics_inst->mute_sub_params); +} + +int bt_mics_discover(struct bt_conn *conn) +{ + static bool initialized; + struct mics_instance *mics_inst; + /* + * This will initiate a discover procedure. The procedure will do the + * following sequence: + * 1) Primary discover for the MICS + * 2) Characteristic discover of the MICS + * 3) Discover services included in MICS (AICS) + * 4) For each included service found; discovery of the characteristiscs + * 5) When everything above have been discovered, the callback is called + */ + + CHECKIF(conn == NULL) { + BT_DBG("NULL conn"); + return -EINVAL; + } + + mics_inst = &mics_insts[bt_conn_index(conn)]; + + (void)memset(&mics_inst->discover_params, 0, + sizeof(mics_inst->discover_params)); + mics_client_reset(conn); + + if (IS_ENABLED(CONFIG_BT_AICS_CLIENT) && + CONFIG_BT_MICS_CLIENT_MAX_AICS_INST > 0) { + for (int i = 0; i < ARRAY_SIZE(mics_inst->aics); i++) { + if (!initialized) { + mics_inst->aics[i] = bt_aics_client_free_instance_get(); + + if (mics_inst->aics[i] == NULL) { + return -ENOMEM; + } + + bt_aics_client_cb_register(mics_inst->aics[i], + &mics_client_cb->aics_cb); + } + } + } + + mics_inst->discover_params.func = primary_discover_func; + mics_inst->discover_params.uuid = mics_uuid; + mics_inst->discover_params.type = BT_GATT_DISCOVER_PRIMARY; + mics_inst->discover_params.start_handle = BT_ATT_FIRST_ATTTRIBUTE_HANDLE; + mics_inst->discover_params.end_handle = BT_ATT_LAST_ATTTRIBUTE_HANDLE; + + initialized = true; + + return bt_gatt_discover(conn, &mics_inst->discover_params); +} + +int bt_mics_client_cb_register(struct bt_mics_cb *cb) +{ + int i, j; + + if (IS_ENABLED(CONFIG_BT_AICS_CLIENT)) { + struct bt_aics_cb *aics_cb = NULL; + + if (cb != NULL) { + CHECKIF(cb->aics_cb.discover != NULL) { + BT_ERR("AICS discover callback shall not be set"); + return -EINVAL; + } + cb->aics_cb.discover = aics_discover_cb; + + aics_cb = &cb->aics_cb; + } + + for (i = 0; i < ARRAY_SIZE(mics_insts); i++) { + for (j = 0; j < ARRAY_SIZE(mics_insts[i].aics); j++) { + if (mics_insts[i].aics[j] != NULL) { + bt_aics_client_cb_register(mics_insts[i].aics[j], + aics_cb); + } + } + } + } + + mics_client_cb = cb; + + return 0; +} + +int bt_mics_client_service_get(struct bt_conn *conn, struct bt_mics *service) +{ + struct mics_instance *mics_inst; + + CHECKIF(conn == NULL) { + return -EINVAL; + } + + CHECKIF(service == NULL) { + return -EINVAL; + } + + mics_inst = &mics_insts[bt_conn_index(conn)]; + + service->aics_cnt = mics_inst->aics_inst_cnt; + service->aics = mics_inst->aics; + + return 0; +} + +int bt_mics_client_mute_get(struct bt_conn *conn) +{ + int err; + struct mics_instance *mics_inst; + + CHECKIF(conn == NULL) { + BT_DBG("NULL conn"); + return -EINVAL; + } + + mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (mics_inst->mute_handle == 0) { + BT_DBG("Handle not set"); + return -EINVAL; + } else if (mics_inst->busy) { + return -EBUSY; + } + + mics_inst->read_params.func = mics_client_read_mute_cb; + mics_inst->read_params.handle_count = 1; + mics_inst->read_params.single.handle = mics_inst->mute_handle; + mics_inst->read_params.single.offset = 0U; + + err = bt_gatt_read(conn, &mics_inst->read_params); + if (err == 0) { + mics_inst->busy = true; + } + + return err; +} + +int bt_mics_client_write_mute(struct bt_conn *conn, bool mute) +{ + int err; + struct mics_instance *mics_inst; + + CHECKIF(conn == NULL) { + BT_DBG("NULL conn"); + return -EINVAL; + } + + mics_inst = &mics_insts[bt_conn_index(conn)]; + + if (mics_inst->mute_handle == 0) { + BT_DBG("Handle not set"); + return -EINVAL; + } else if (mics_inst->busy) { + return -EBUSY; + } + + mics_inst->mute_val_buf[0] = mute; + mics_inst->write_params.offset = 0; + mics_inst->write_params.data = mics_inst->mute_val_buf; + mics_inst->write_params.length = sizeof(mute); + mics_inst->write_params.handle = mics_inst->mute_handle; + mics_inst->write_params.func = mics_client_write_mics_mute_cb; + + err = bt_gatt_write(conn, &mics_inst->write_params); + if (err == 0) { + mics_inst->busy = true; + } + + return err; +} + +int bt_mics_client_mute(struct bt_conn *conn) +{ + return bt_mics_client_write_mute(conn, true); +} + +int bt_mics_client_unmute(struct bt_conn *conn) +{ + return bt_mics_client_write_mute(conn, false); +} diff --git a/subsys/bluetooth/audio/mics_internal.h b/subsys/bluetooth/audio/mics_internal.h new file mode 100644 index 00000000000..501d8d87955 --- /dev/null +++ b/subsys/bluetooth/audio/mics_internal.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020-2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_MICS_INTERNAL_ +#define ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_MICS_INTERNAL_ +#include +#include + +int bt_mics_client_service_get(struct bt_conn *conn, struct bt_mics *service); +int bt_mics_client_mute_get(struct bt_conn *conn); +int bt_mics_client_mute(struct bt_conn *conn); +int bt_mics_client_unmute(struct bt_conn *conn); +bool bt_mics_client_valid_aics_inst(struct bt_conn *conn, struct bt_aics *aics); + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_MICS_INTERNAL_ */ diff --git a/tests/bluetooth/shell/prj.conf b/tests/bluetooth/shell/prj.conf index 1767e51a188..bead4b688d2 100644 --- a/tests/bluetooth/shell/prj.conf +++ b/tests/bluetooth/shell/prj.conf @@ -54,8 +54,8 @@ CONFIG_BT_ISO_TX_BUF_COUNT=5 CONFIG_BT_VOCS_MAX_INSTANCE_COUNT=1 CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT=1 -CONFIG_BT_AICS_MAX_INSTANCE_COUNT=1 -CONFIG_BT_AICS_CLIENT_MAX_INSTANCE_COUNT=1 +CONFIG_BT_AICS_MAX_INSTANCE_COUNT=2 +CONFIG_BT_AICS_CLIENT_MAX_INSTANCE_COUNT=2 CONFIG_BT_VCS=y CONFIG_BT_VCS_VOCS_INSTANCE_COUNT=1 @@ -63,3 +63,8 @@ CONFIG_BT_VCS_AICS_INSTANCE_COUNT=1 CONFIG_BT_VCS_CLIENT=y CONFIG_BT_VCS_CLIENT_MAX_VOCS_INST=1 CONFIG_BT_VCS_CLIENT_MAX_AICS_INST=1 + +CONFIG_BT_MICS=y +CONFIG_BT_MICS_AICS_INSTANCE_COUNT=1 +CONFIG_BT_MICS_CLIENT=y +CONFIG_BT_MICS_CLIENT_MAX_AICS_INST=1