Bluetooth: Audio: Make HAS optional notify characteristics optional

Added Kconfig options to make HAS characteristics that has notify as
optional property selectable and thus optional. These settings are
global, meaning that a chosen notify property will be used for all
registered has instances.

Signed-off-by: Fredrik Danebjer <frdn@demant.com>
This commit is contained in:
Fredrik Danebjer 2023-05-11 12:30:19 +02:00 committed by Anas Nashif
commit 7457bcf0a2
9 changed files with 345 additions and 79 deletions

View file

@ -68,8 +68,8 @@ enum bt_has_capabilities {
BT_HAS_PRESET_SUPPORT = BIT(0),
};
/** @brief Structure for registering a Hearing Access Service instance. */
struct bt_has_register_param {
/** @brief Structure for registering features of a Hearing Access Service instance. */
struct bt_has_features_param {
/** Hearing Aid Type value */
enum bt_has_hearing_aid_type type;
@ -341,11 +341,11 @@ struct bt_has_preset_register_param {
/**
* @brief Register the Hearing Access Service instance.
*
* @param param Hearing Access Service register parameters.
* @param features Hearing Access Service register parameters.
*
* @return 0 if success, errno on failure.
*/
int bt_has_register(const struct bt_has_register_param *param);
int bt_has_register(const struct bt_has_features_param *features);
/**
* @brief Register preset.
@ -470,6 +470,17 @@ static inline int bt_has_preset_active_clear(void)
*/
int bt_has_preset_name_change(uint8_t index, const char *name);
/**
* @brief Change the Hearing Aid Features.
*
* Change the hearing aid features.
*
* @param features The features to be set.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_has_features_set(const struct bt_has_features_param *features);
#ifdef __cplusplus
}
#endif

View file

@ -75,7 +75,7 @@ int has_server_preset_init(void)
return 0;
}
static struct bt_has_register_param param = {
static struct bt_has_features_param features = {
.type = BT_HAS_HEARING_AID_TYPE_MONAURAL,
.preset_sync_support = false,
.independent_presets = false
@ -86,12 +86,12 @@ int has_server_init(void)
int err;
if (IS_ENABLED(CONFIG_HAP_HA_HEARING_AID_BINAURAL)) {
param.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
features.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
} else if (IS_ENABLED(CONFIG_HAP_HA_HEARING_AID_BANDED)) {
param.type = BT_HAS_HEARING_AID_TYPE_BANDED;
features.type = BT_HAS_HEARING_AID_TYPE_BANDED;
}
err = bt_has_register(&param);
err = bt_has_register(&features);
if (err) {
return err;
}

View file

@ -15,6 +15,12 @@ menuconfig BT_HAS
if BT_HAS
config BT_HAS_FEATURES_NOTIFIABLE
bool "Hearing Aid Features Notifiable Support"
help
This option enables support for clients to subscribe for notifications
on the Hearing Aid Features characteristic.
config BT_HAS_PRESET_COUNT
int "Preset record list size"
default 2
@ -33,6 +39,18 @@ config BT_HAS_PRESET_NAME_DYNAMIC
help
Enabling this option allows for runtime configuration of preset name.
config BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE
bool "Preset Control Point Notifiable support"
depends on BT_HAS_PRESET_SUPPORT && BT_EATT
help
This option enables support for clients to subscribe for notifications
on the Hearing Aid Preset Control Point characteristic.
config BT_HAS_ACTIVE_PRESET_INDEX
def_bool BT_HAS_PRESET_SUPPORT
help
This option enables the Hearing Aid Active Preset Index characteristic.
endif # BT_HAS_PRESET_SUPPORT
endif # BT_HAS

View file

@ -32,8 +32,30 @@ LOG_MODULE_REGISTER(bt_has, CONFIG_BT_HAS_LOG_LEVEL);
*/
#define BT_HAS_MAX_CONN MIN(CONFIG_BT_MAX_CONN, CONFIG_BT_MAX_PAIRED)
#define BITS_CHANGED(_new_value, _old_value) ((_new_value) ^ (_old_value))
#define FEATURE_DEVICE_TYPE_UNCHANGED(_new_value) \
!BITS_CHANGED(_new_value, (has.features & BT_HAS_FEAT_HEARING_AID_TYPE_MASK))
#define FEATURE_SYNC_SUPPORT_UNCHANGED(_new_value) \
!BITS_CHANGED(_new_value, ((has.features & BT_HAS_FEAT_PRESET_SYNC_SUPP) != 0 ? 1 : 0))
#define FEATURE_IND_PRESETS_UNCHANGED(_new_value) \
!BITS_CHANGED(_new_value, ((has.features & BT_HAS_FEAT_INDEPENDENT_PRESETS) != 0 ? 1 : 0))
static struct bt_has has;
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
static void preset_cp_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
LOG_DBG("attr %p value 0x%04x", attr, value);
}
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#if defined(CONFIG_BT_HAS_ACTIVE_PRESET_INDEX)
static void active_preset_index_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
LOG_DBG("attr %p value 0x%04x", attr, value);
}
#endif /* CONFIG_BT_HAS_ACTIVE_PRESET_INDEX */
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
static ssize_t write_control_point(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *data, uint16_t len, uint16_t offset, uint8_t flags);
@ -50,12 +72,14 @@ static ssize_t read_active_preset_index(struct bt_conn *conn, const struct bt_ga
return bt_gatt_attr_read(conn, attr, buf, len, offset, &has.active_index,
sizeof(has.active_index));
}
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
static void features_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
LOG_DBG("attr %p value 0x%04x", attr, value);
}
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
static ssize_t read_features(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
@ -70,36 +94,87 @@ static ssize_t read_features(struct bt_conn *conn, const struct bt_gatt_attr *at
sizeof(has.features));
}
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
#define BT_HAS_CHR_FEATURES \
BT_AUDIO_CHRC(BT_UUID_HAS_HEARING_AID_FEATURES, \
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_READ_ENCRYPT, \
read_features, NULL, NULL), \
BT_AUDIO_CCC(features_cfg_changed),
#else
#define BT_HAS_CHR_FEATURES \
BT_AUDIO_CHRC(BT_UUID_HAS_HEARING_AID_FEATURES, \
BT_GATT_CHRC_READ, \
BT_GATT_PERM_READ_ENCRYPT, \
read_features, NULL, NULL),
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
#if defined(CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE)
#define BT_HAS_CHR_PRESET_CONTROL_POINT \
BT_AUDIO_CHRC(BT_UUID_HAS_PRESET_CONTROL_POINT, \
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_WRITE_ENCRYPT, \
NULL, write_control_point, NULL), \
BT_AUDIO_CCC(preset_cp_cfg_changed),
#else
#define BT_HAS_CHR_PRESET_CONTROL_POINT \
BT_AUDIO_CHRC(BT_UUID_HAS_PRESET_CONTROL_POINT, \
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE, \
BT_GATT_PERM_WRITE_ENCRYPT, \
NULL, write_control_point, NULL), \
BT_AUDIO_CCC(preset_cp_cfg_changed),
#endif /* CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE */
#else
#define BT_HAS_CHR_PRESET_CONTROL_POINT
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#if defined(CONFIG_BT_HAS_ACTIVE_PRESET_INDEX)
#define BT_HAS_CHR_ACTIVE_PRESET_INDEX \
BT_AUDIO_CHRC(BT_UUID_HAS_ACTIVE_PRESET_INDEX, \
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_READ_ENCRYPT, \
read_active_preset_index, NULL, NULL), \
BT_AUDIO_CCC(active_preset_index_cfg_changed)
#else
#define BT_HAS_CHR_ACTIVE_PRESET_INDEX
#endif /* CONFIG_BT_HAS_ACTIVE_PRESET_INDEX */
/* Hearing Access Service GATT Attributes */
static struct bt_gatt_attr has_attrs[] = {
BT_GATT_PRIMARY_SERVICE(BT_UUID_HAS),
BT_AUDIO_CHRC(BT_UUID_HAS_HEARING_AID_FEATURES,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ_ENCRYPT,
read_features, NULL, NULL),
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
BT_AUDIO_CHRC(BT_UUID_HAS_PRESET_CONTROL_POINT,
#if defined(CONFIG_BT_EATT)
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE | BT_GATT_CHRC_NOTIFY,
#else
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE,
#endif /* CONFIG_BT_EATT */
BT_GATT_PERM_WRITE_ENCRYPT,
NULL, write_control_point, NULL),
BT_AUDIO_CCC(ccc_cfg_changed),
BT_AUDIO_CHRC(BT_UUID_HAS_ACTIVE_PRESET_INDEX,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT,
read_active_preset_index, NULL, NULL),
BT_AUDIO_CCC(ccc_cfg_changed),
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
BT_HAS_CHR_FEATURES
BT_HAS_CHR_PRESET_CONTROL_POINT
BT_HAS_CHR_ACTIVE_PRESET_INDEX
};
static struct bt_gatt_service has_svc;
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
#define FEATURES_ATTR &has_attrs[2]
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
#define PRESET_CONTROL_POINT_ATTR &has_attrs[5]
#define ACTIVE_PRESET_INDEX_ATTR &has_attrs[8]
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#else
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
#define PRESET_CONTROL_POINT_ATTR &has_attrs[4]
#define ACTIVE_PRESET_INDEX_ATTR &has_attrs[7]
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
enum {
FLAG_ACTIVE_INDEX_CHANGED,
FLAG_CONTROL_POINT_NOTIFY,
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
FLAG_FEATURES_CHANGED,
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
FLAG_NUM,
};
static struct has_client {
struct bt_conn *conn;
@ -110,11 +185,8 @@ static struct has_client {
#endif /* CONFIG_BT_EATT */
} params;
struct {
bool active_index;
bool control_point;
uint8_t preset_changed_index_next;
} pending_ntf;
ATOMIC_DEFINE(flags, FLAG_NUM);
uint8_t preset_changed_index_next;
struct bt_has_cp_read_presets_req read_presets_req;
struct k_work control_point_work;
} has_client_list[BT_HAS_MAX_CONN];
@ -134,6 +206,12 @@ static struct has_preset {
/* Number of registered presets */
static uint8_t has_preset_num;
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
/* Features notification work */
static void features_work_process(struct k_work *work);
static K_WORK_DEFINE(features_work, features_work_process);
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
/* Active preset notification work */
static void active_preset_work_process(struct k_work *work);
static K_WORK_DEFINE(active_preset_work, active_preset_work_process);
@ -180,8 +258,11 @@ static void client_free(struct has_client *client)
read_presets_req_free(client);
client->pending_ntf.control_point = false;
client->pending_ntf.active_index = false;
atomic_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
atomic_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
atomic_clear_bit(client->flags, FLAG_FEATURES_CHANGED);
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
bt_conn_unref(client->conn);
@ -220,15 +301,22 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_
}
/* Notify after reconnection */
if (client->pending_ntf.active_index) {
if (atomic_test_and_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED)) {
/* Emit active preset notification */
k_work_submit(&active_preset_work);
}
if (client->pending_ntf.control_point) {
if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
/* Emit preset changed notifications */
k_work_submit(&client->control_point_work);
}
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
if (atomic_test_and_clear_bit(client->flags, FLAG_FEATURES_CHANGED)) {
/* Emit preset changed notifications */
k_work_submit(&features_work);
}
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
}
static void connected(struct bt_conn *conn, uint8_t err)
@ -359,7 +447,7 @@ static void control_point_ntf_complete(struct bt_conn *conn, void *user_data)
/* Resubmit if needed */
if (client != NULL &&
(read_presets_req_pending_cp(client) ||
client->pending_ntf.control_point)) {
atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY))) {
k_work_submit(&client->control_point_work);
}
}
@ -378,7 +466,7 @@ static void control_point_ind_complete(struct bt_conn *conn,
static int control_point_send(struct has_client *client, struct net_buf_simple *buf)
{
#if defined(CONFIG_BT_EATT)
#if defined(BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE)
if (bt_eatt_count(client->conn) > 0 &&
bt_gatt_is_subscribed(client->conn, PRESET_CONTROL_POINT_ATTR, BT_GATT_CCC_NOTIFY)) {
client->params.ntf.attr = PRESET_CONTROL_POINT_ATTR;
@ -413,12 +501,12 @@ static int control_point_send_all(struct net_buf_simple *buf)
if (!client->conn) {
/* Mark preset changed operation as pending */
client->pending_ntf.control_point = true;
atomic_set_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
/* For simplicity we simply start with the first index,
* rather than keeping detailed logs of which clients
* have knowledge of which presets
*/
client->pending_ntf.preset_changed_index_next = BT_HAS_PRESET_INDEX_FIRST;
client->preset_changed_index_next = BT_HAS_PRESET_INDEX_FIRST;
continue;
}
@ -554,12 +642,12 @@ static void process_control_point_work(struct k_work *work)
client->read_presets_req.start_index = preset->index + 1;
client->read_presets_req.num_presets--;
}
} else if (client->pending_ntf.control_point) {
} else if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
const struct has_preset *preset = NULL;
const struct has_preset *next = NULL;
bool is_last = true;
preset_foreach(client->pending_ntf.preset_changed_index_next,
preset_foreach(client->preset_changed_index_next,
BT_HAS_PRESET_INDEX_LAST, preset_found, &preset);
if (preset == NULL) {
@ -577,9 +665,9 @@ static void process_control_point_work(struct k_work *work)
}
if (err || is_last) {
client->pending_ntf.control_point = false;
atomic_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY);
} else {
client->pending_ntf.preset_changed_index_next = preset->index + 1;
client->preset_changed_index_next = preset->index + 1;
}
}
}
@ -721,15 +809,15 @@ static void active_preset_work_process(struct k_work *work)
if (client->conn == NULL) {
/* mark to notify on reconnect */
client->pending_ntf.active_index = true;
atomic_set_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
continue;
}
err = bt_gatt_notify(client->conn, ACTIVE_PRESET_INDEX_ATTR,
&active_index, sizeof(active_index));
if (err != 0) {
LOG_DBG("failed to notify for %p: %d",
client->conn, err);
} else if (atomic_test_and_clear_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED)) {
err = bt_gatt_notify(client->conn, ACTIVE_PRESET_INDEX_ATTR, &active_index,
sizeof(active_index));
if (err != 0) {
LOG_DBG("failed to notify active_index for %p: %d", client->conn,
err);
}
}
}
}
@ -739,6 +827,12 @@ static void preset_active_set(uint8_t index)
if (index != has.active_index) {
has.active_index = index;
for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
struct has_client *client = &has_client_list[i];
/* mark to notify */
atomic_set_bit(client->flags, FLAG_ACTIVE_INDEX_CHANGED);
}
/* Emit active preset notification */
k_work_submit(&active_preset_work);
}
@ -917,6 +1011,7 @@ static uint8_t handle_control_point_op(struct bt_conn *conn, struct net_buf_simp
return BT_HAS_ERR_INVALID_OPCODE;
}
#if defined(CONFIG_BT_HAS_PRESET_SUPPORT)
static ssize_t write_control_point(struct bt_conn *conn, const struct bt_gatt_attr *attr,
const void *data, uint16_t len, uint16_t offset, uint8_t flags)
{
@ -944,6 +1039,29 @@ static ssize_t write_control_point(struct bt_conn *conn, const struct bt_gatt_at
return len;
}
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
static void features_work_process(struct k_work *work)
{
for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
struct has_client *client = &has_client_list[i];
int err;
if (client->conn == NULL) {
/* mark to notify on reconnect */
atomic_set_bit(client->flags, FLAG_FEATURES_CHANGED);
continue;
} else if (atomic_test_and_clear_bit(client->flags, FLAG_CONTROL_POINT_NOTIFY)) {
err = bt_gatt_notify(client->conn, FEATURES_ATTR, &has.features,
sizeof(has.features));
if (err != 0) {
LOG_DBG("failed to notify features for %p: %d", client->conn, err);
}
}
}
}
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
int bt_has_preset_register(const struct bt_has_preset_register_param *param)
{
@ -1153,30 +1271,16 @@ int bt_has_preset_name_change(uint8_t index, const char *name)
}
#endif /* CONFIG_BT_HAS_PRESET_SUPPORT */
int bt_has_register(const struct bt_has_register_param *param)
static int has_features_register(const struct bt_has_features_param *features)
{
static bool registered;
int err;
LOG_DBG("param %p", param);
CHECKIF(!param) {
LOG_DBG("NULL params pointer");
return -EINVAL;
}
if (registered) {
return -EALREADY;
}
/* Initialize the supported features characteristic value */
has.features = param->type;
has.features = features->type;
if (IS_ENABLED(CONFIG_BT_HAS_PRESET_SUPPORT)) {
has.features |= BT_HAS_FEAT_DYNAMIC_PRESETS;
if (param->preset_sync_support) {
if (param->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
if (features->preset_sync_support) {
if (features->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
LOG_DBG("Preset sync support only available "
"for binaural hearing aid type");
return -EINVAL;
@ -1185,8 +1289,8 @@ int bt_has_register(const struct bt_has_register_param *param)
has.features |= BT_HAS_FEAT_PRESET_SYNC_SUPP;
}
if (param->independent_presets) {
if (param->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
if (features->independent_presets) {
if (features->type != BT_HAS_HEARING_AID_TYPE_BINAURAL) {
LOG_DBG("Independent presets only available "
"for binaural hearing aid type");
return -EINVAL;
@ -1200,6 +1304,83 @@ int bt_has_register(const struct bt_has_register_param *param)
has.features |= BT_HAS_FEAT_WRITABLE_PRESETS_SUPP;
}
return 0;
}
#if defined(CONFIG_BT_HAS_FEATURES_NOTIFIABLE)
int bt_has_features_set(const struct bt_has_features_param *features)
{
uint8_t tmp_features;
int err;
if (!has.registered) {
return -ENOTSUP;
}
/* Check whether any features will change, otherwise we don't want to notify clients */
if (FEATURE_DEVICE_TYPE_UNCHANGED(features->type) &&
FEATURE_SYNC_SUPPORT_UNCHANGED(features->preset_sync_support) &&
FEATURE_IND_PRESETS_UNCHANGED(features->independent_presets)) {
return 0;
}
tmp_features = has.features;
err = has_features_register(features);
if (err != 0) {
LOG_DBG("Failed to register features");
return err;
}
bool tmp_pending_ntf_features[ARRAY_SIZE(has_client_list)];
for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
struct has_client *client = &has_client_list[i];
/* save old state */
tmp_pending_ntf_features[i] = atomic_test_bit(client->flags, FLAG_FEATURES_CHANGED);
/* mark to notify */
atomic_set_bit(client->flags, FLAG_FEATURES_CHANGED);
}
err = k_work_submit(&features_work);
if (err < 0) {
/* restore old values */
for (size_t i = 0U; i < ARRAY_SIZE(has_client_list); i++) {
struct has_client *client = &has_client_list[i];
atomic_set_bit_to(client->flags, FLAG_FEATURES_CHANGED,
tmp_pending_ntf_features[i]);
}
has.features = tmp_features;
return err;
}
return 0;
}
#endif /* CONFIG_BT_HAS_FEATURES_NOTIFIABLE */
int bt_has_register(const struct bt_has_features_param *features)
{
int err;
LOG_DBG("features %p", features);
CHECKIF(!features) {
LOG_DBG("NULL params pointer");
return -EINVAL;
}
if (has.registered) {
return -EALREADY;
}
err = has_features_register(features);
if (err != 0) {
LOG_DBG("HAS service failed to register features: %d", err);
return err;
}
has_svc = (struct bt_gatt_service)BT_GATT_SERVICE(has_attrs);
err = bt_gatt_service_register(&has_svc);
if (err != 0) {
@ -1207,7 +1388,7 @@ int bt_has_register(const struct bt_has_register_param *param)
return err;
}
registered = true;
has.registered = true;
return 0;
}

View file

@ -52,6 +52,9 @@ struct bt_has {
/** Active preset index value */
uint8_t active_index;
/* Whether the service has been registered or not */
bool registered;
};
struct bt_has_cp_hdr {

View file

@ -75,10 +75,47 @@ static int cmd_preset_unreg(const struct shell *sh, size_t argc, char **argv)
return 0;
}
static int cmd_features_set(const struct shell *sh, size_t argc, char **argv)
{
int err;
struct bt_has_features_param param = {
.type = BT_HAS_HEARING_AID_TYPE_MONAURAL,
.preset_sync_support = false,
.independent_presets = false
};
for (size_t argn = 1; argn < argc; argn++) {
const char *arg = argv[argn];
if (strcmp(arg, "binaural") == 0) {
param.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
} else if (strcmp(arg, "monaural") == 0) {
param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
} else if (strcmp(arg, "banded") == 0) {
param.type = BT_HAS_HEARING_AID_TYPE_BANDED;
} else if (strcmp(arg, "sync") == 0) {
param.preset_sync_support = true;
} else if (strcmp(arg, "independent") == 0) {
param.independent_presets = true;
} else {
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
}
err = bt_has_features_set(&param);
if (err != 0) {
shell_error(sh, "Could not set features: %d", err);
return err;
}
return 0;
}
static int cmd_has_register(const struct shell *sh, size_t argc, char **argv)
{
int err;
struct bt_has_register_param param = {
struct bt_has_features_param param = {
.type = BT_HAS_HEARING_AID_TYPE_MONAURAL,
.preset_sync_support = false,
.independent_presets = false
@ -271,6 +308,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(has_cmds,
SHELL_CMD_ARG(preset-active-clear, NULL, "Clear selected preset",
cmd_preset_active_clear, 1, 0),
SHELL_CMD_ARG(set-name, NULL, "Set preset name <index> <name>", cmd_preset_name_set, 3, 0),
SHELL_CMD_ARG(features-set, NULL, "Set hearing aid features "
"[binaural | monaural(default) | banded] [sync] [independent]",
cmd_features_set, 1, 3),
SHELL_SUBCMD_SET_END
);

View file

@ -135,6 +135,8 @@ CONFIG_BT_HAS=y
CONFIG_BT_HAS_PRESET_NAME_DYNAMIC=y
CONFIG_BT_HAS_PRESET_COUNT=4
CONFIG_BT_HAS_CLIENT=y
CONFIG_BT_HAS_FEATURES_NOTIFIABLE=y
CONFIG_BT_HAS_PRESET_CONTROL_POINT_NOTIFIABLE=y
# Common Audio Profile
CONFIG_BT_CAP_ACCEPTOR=y

View file

@ -108,6 +108,7 @@ CONFIG_BT_BAP_BROADCAST_ASSISTANT=y
# Hearing Access
CONFIG_BT_HAS=y
CONFIG_BT_HAS_CLIENT=y
CONFIG_BT_HAS_FEATURES_NOTIFIABLE=y
# Common Audio Profile
CONFIG_BT_CAP_ACCEPTOR=y

View file

@ -29,7 +29,7 @@ static const struct bt_has_preset_ops preset_ops = {
static void test_main(void)
{
struct bt_has_register_param has_param = {0};
struct bt_has_features_param has_param = {0};
struct bt_has_preset_register_param preset_param;
int err;
@ -50,7 +50,8 @@ static void test_main(void)
printk("Advertising successfully started\n");
has_param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
has_param.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
has_param.preset_sync_support = true;
err = bt_has_register(&has_param);
if (err) {
@ -58,6 +59,15 @@ static void test_main(void)
return;
}
has_param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
has_param.preset_sync_support = false;
err = bt_has_features_set(&has_param);
if (err) {
FAIL("HAS register failed (err %d)\n", err);
return;
}
preset_param.index = test_preset_index_5;
preset_param.properties = test_preset_properties;
preset_param.name = test_preset_name_5;