Bluetooth: has: Add stubs for handling control point requests
This adds server stubs for handling the preset control point requests. Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
parent
da40ffb4a8
commit
f6d0dc355f
3 changed files with 238 additions and 1 deletions
|
@ -55,6 +55,41 @@ config BT_HAS_HEARING_AID_TYPE
|
||||||
help
|
help
|
||||||
The value shall be one of 3 defined by the HAS 1.0 specification table 3.2
|
The value shall be one of 3 defined by the HAS 1.0 specification table 3.2
|
||||||
|
|
||||||
|
config BT_HAS_PRESET_COUNT
|
||||||
|
int "Preset record list size"
|
||||||
|
default 2
|
||||||
|
range 0 255
|
||||||
|
help
|
||||||
|
This option sets the number of Hearing Access Service Presets
|
||||||
|
that can be registered. Setting this value to 0 disables Presets support.
|
||||||
|
|
||||||
|
if BT_HAS_PRESET_COUNT > 0
|
||||||
|
|
||||||
|
if BT_HAS_HEARING_AID_BINAURAL
|
||||||
|
|
||||||
|
config BT_HAS_IDENTICAL_PRESET_RECORDS
|
||||||
|
bool "Identical preset records in Binaural Hearing Aid Set"
|
||||||
|
help
|
||||||
|
Set if the list of preset records is identical to the list of preset records
|
||||||
|
on the other member in the Binaural Hearing Aid Set.
|
||||||
|
This option sets Independent Presets field in Hearing Aid Features to 0b0.
|
||||||
|
|
||||||
|
config BT_HAS_PRESET_SYNC_SUPPORT
|
||||||
|
bool "Preset synchronization support"
|
||||||
|
depends on BT_HAS_IDENTICAL_PRESET_RECORDS
|
||||||
|
help
|
||||||
|
Set if the hearing aid has support for relaying active preset changes to the other
|
||||||
|
member in the Binaural Hearing Aid Set.
|
||||||
|
|
||||||
|
endif # BT_HAS_HEARING_AID_BINAURAL
|
||||||
|
|
||||||
|
config BT_HAS_PRESET_NAME_DYNAMIC
|
||||||
|
bool "Allow to set preset name on runtime"
|
||||||
|
help
|
||||||
|
Enabling this option allows for runtime configuration of preset name.
|
||||||
|
|
||||||
|
endif # BT_HAS_PRESET_COUNT > 0
|
||||||
|
|
||||||
config BT_DEBUG_HAS
|
config BT_DEBUG_HAS
|
||||||
bool "Hearing Access Service debug"
|
bool "Hearing Access Service debug"
|
||||||
help
|
help
|
||||||
|
|
|
@ -34,11 +34,87 @@ static ssize_t read_features(struct bt_conn *conn, const struct bt_gatt_attr *at
|
||||||
sizeof(has.features));
|
sizeof(has.features));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CONFIG_BT_HAS_PRESET_COUNT > 0
|
||||||
|
static uint8_t handle_control_point_op(struct bt_conn *conn, struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
const struct bt_has_cp_hdr *hdr;
|
||||||
|
|
||||||
|
hdr = net_buf_simple_pull_mem(buf, sizeof(*hdr));
|
||||||
|
|
||||||
|
BT_DBG("conn %p opcode %s (0x%02x)", (void *)conn, bt_has_op_str(hdr->opcode),
|
||||||
|
hdr->opcode);
|
||||||
|
|
||||||
|
/* TODO: handle request here */
|
||||||
|
|
||||||
|
return BT_HAS_ERR_INVALID_OPCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct net_buf_simple buf;
|
||||||
|
uint8_t err;
|
||||||
|
|
||||||
|
BT_DBG("conn %p attr %p data %p len %d offset %d flags 0x%02x", (void *)conn, attr, data,
|
||||||
|
len, offset, flags);
|
||||||
|
|
||||||
|
if (offset > 0) {
|
||||||
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
net_buf_simple_init_with_data(&buf, (void *)data, len);
|
||||||
|
|
||||||
|
err = handle_control_point_op(conn, &buf);
|
||||||
|
if (err) {
|
||||||
|
BT_WARN("err 0x%02x", err);
|
||||||
|
return BT_GATT_ERR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t read_active_preset_index(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||||
|
void *buf, uint16_t len, uint16_t offset)
|
||||||
|
{
|
||||||
|
BT_DBG("conn %p attr %p offset %d", (void *)conn, attr, offset);
|
||||||
|
|
||||||
|
if (offset > sizeof(has.active_index)) {
|
||||||
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &has.active_index,
|
||||||
|
sizeof(has.active_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
||||||
|
{
|
||||||
|
BT_DBG("attr %p value 0x%04x", attr, value);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_HAS_PRESET_COUNT > 0 */
|
||||||
|
|
||||||
/* Hearing Access Service GATT Attributes */
|
/* Hearing Access Service GATT Attributes */
|
||||||
BT_GATT_SERVICE_DEFINE(has_svc,
|
BT_GATT_SERVICE_DEFINE(has_svc,
|
||||||
BT_GATT_PRIMARY_SERVICE(BT_UUID_HAS),
|
BT_GATT_PRIMARY_SERVICE(BT_UUID_HAS),
|
||||||
BT_GATT_CHARACTERISTIC(BT_UUID_HAS_HEARING_AID_FEATURES, BT_GATT_CHRC_READ,
|
BT_GATT_CHARACTERISTIC(BT_UUID_HAS_HEARING_AID_FEATURES, BT_GATT_CHRC_READ,
|
||||||
BT_GATT_PERM_READ_ENCRYPT, read_features, NULL, NULL),
|
BT_GATT_PERM_READ_ENCRYPT, read_features, NULL, NULL),
|
||||||
|
#if CONFIG_BT_HAS_PRESET_COUNT > 0
|
||||||
|
BT_GATT_CHARACTERISTIC(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_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||||
|
BT_GATT_CHARACTERISTIC(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_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||||
|
#endif /* CONFIG_BT_HAS_PRESET_COUNT > 0 */
|
||||||
);
|
);
|
||||||
|
|
||||||
static int has_init(const struct device *dev)
|
static int has_init(const struct device *dev)
|
||||||
|
@ -48,6 +124,20 @@ static int has_init(const struct device *dev)
|
||||||
/* Initialize the supported features characteristic value */
|
/* Initialize the supported features characteristic value */
|
||||||
has.features = CONFIG_BT_HAS_HEARING_AID_TYPE & BT_HAS_FEAT_HEARING_AID_TYPE_MASK;
|
has.features = CONFIG_BT_HAS_HEARING_AID_TYPE & BT_HAS_FEAT_HEARING_AID_TYPE_MASK;
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_HAS_HEARING_AID_BINAURAL)) {
|
||||||
|
if (IS_ENABLED(CONFIG_BT_HAS_PRESET_SYNC_SUPPORT)) {
|
||||||
|
has.features |= BT_HAS_FEAT_PRESET_SYNC_SUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_BT_HAS_IDENTICAL_PRESET_RECORDS)) {
|
||||||
|
has.features |= BT_HAS_FEAT_INDEPENDENT_PRESETS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_HAS_PRESET_NAME_DYNAMIC)) {
|
||||||
|
has.features |= BT_HAS_FEAT_WRITABLE_PRESETS_SUPP;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_HAS_HEARING_AID_BANDED)) {
|
if (IS_ENABLED(CONFIG_BT_HAS_HEARING_AID_BANDED)) {
|
||||||
/* HAP_d1.0r00; 3.7 BAP Unicast Server role requirements
|
/* HAP_d1.0r00; 3.7 BAP Unicast Server role requirements
|
||||||
* A Banded Hearing Aid in the HA role shall set the Front Left and the Front
|
* A Banded Hearing Aid in the HA role shall set the Front Left and the Front
|
||||||
|
|
|
@ -8,9 +8,121 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BT_HAS_FEAT_HEARING_AID_TYPE_MASK 0x03
|
/* Control Point opcodes */
|
||||||
|
#define BT_HAS_OP_READ_PRESET_REQ 0x01
|
||||||
|
#define BT_HAS_OP_READ_PRESET_RSP 0x02
|
||||||
|
#define BT_HAS_OP_PRESET_CHANGED 0x03
|
||||||
|
#define BT_HAS_OP_WRITE_PRESET_NAME 0x04
|
||||||
|
#define BT_HAS_OP_SET_ACTIVE_PRESET 0x05
|
||||||
|
#define BT_HAS_OP_SET_NEXT_PRESET 0x06
|
||||||
|
#define BT_HAS_OP_SET_PREV_PRESET 0x07
|
||||||
|
#define BT_HAS_OP_SET_ACTIVE_PRESET_SYNC 0x08
|
||||||
|
#define BT_HAS_OP_SET_NEXT_PRESET_SYNC 0x09
|
||||||
|
#define BT_HAS_OP_SET_PREV_PRESET_SYNC 0x0a
|
||||||
|
|
||||||
|
/* Application error codes */
|
||||||
|
#define BT_HAS_ERR_INVALID_OPCODE 0x80
|
||||||
|
#define BT_HAS_ERR_WRITE_NAME_NOT_ALLOWED 0x81
|
||||||
|
#define BT_HAS_ERR_PRESET_SYNC_NOT_SUPP 0x82
|
||||||
|
#define BT_HAS_ERR_OPERATION_NOT_POSSIBLE 0x83
|
||||||
|
#define BT_HAS_ERR_INVALID_PARAM_LEN 0x84
|
||||||
|
|
||||||
|
/* Hearing Aid Feature bits */
|
||||||
|
#define BT_HAS_FEAT_HEARING_AID_TYPE_LSB BIT(0)
|
||||||
|
#define BT_HAS_FEAT_HEARING_AID_TYPE_MSB BIT(1)
|
||||||
|
#define BT_HAS_FEAT_PRESET_SYNC_SUPP BIT(2)
|
||||||
|
#define BT_HAS_FEAT_INDEPENDENT_PRESETS BIT(3)
|
||||||
|
#define BT_HAS_FEAT_DYNAMIC_PRESETS BIT(4)
|
||||||
|
#define BT_HAS_FEAT_WRITABLE_PRESETS_SUPP BIT(5)
|
||||||
|
|
||||||
|
#define BT_HAS_FEAT_HEARING_AID_TYPE_MASK (BT_HAS_FEAT_HEARING_AID_TYPE_LSB | \
|
||||||
|
BT_HAS_FEAT_HEARING_AID_TYPE_MSB)
|
||||||
|
|
||||||
|
/* Preset Changed Change ID values */
|
||||||
|
#define BT_HAS_CHANGE_ID_GENERIC_UPDATE 0x00
|
||||||
|
#define BT_HAS_CHANGE_ID_PRESET_DELETED 0x01
|
||||||
|
#define BT_HAS_CHANGE_ID_PRESET_AVAILABLE 0x02
|
||||||
|
#define BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE 0x03
|
||||||
|
|
||||||
struct bt_has {
|
struct bt_has {
|
||||||
/** Hearing Aid Features value */
|
/** Hearing Aid Features value */
|
||||||
uint8_t features;
|
uint8_t features;
|
||||||
|
|
||||||
|
/** Active preset index value */
|
||||||
|
uint8_t active_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct bt_has_cp_hdr {
|
||||||
|
uint8_t opcode;
|
||||||
|
uint8_t data[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_read_presets_req {
|
||||||
|
uint8_t start_index;
|
||||||
|
uint8_t num_presets;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_read_preset_rsp {
|
||||||
|
uint8_t is_last;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t properties;
|
||||||
|
uint8_t name[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_preset_changed {
|
||||||
|
uint8_t change_id;
|
||||||
|
uint8_t is_last;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_write_preset_name {
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t name[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_has_cp_set_active_preset {
|
||||||
|
uint8_t index;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
static inline const char *bt_has_op_str(uint8_t op)
|
||||||
|
{
|
||||||
|
switch (op) {
|
||||||
|
case BT_HAS_OP_READ_PRESET_REQ:
|
||||||
|
return "Read preset request";
|
||||||
|
case BT_HAS_OP_READ_PRESET_RSP:
|
||||||
|
return "Read preset response";
|
||||||
|
case BT_HAS_OP_PRESET_CHANGED:
|
||||||
|
return "Preset changed";
|
||||||
|
case BT_HAS_OP_WRITE_PRESET_NAME:
|
||||||
|
return "Write preset name";
|
||||||
|
case BT_HAS_OP_SET_ACTIVE_PRESET:
|
||||||
|
return "Set active preset";
|
||||||
|
case BT_HAS_OP_SET_NEXT_PRESET:
|
||||||
|
return "Set next preset";
|
||||||
|
case BT_HAS_OP_SET_PREV_PRESET:
|
||||||
|
return "Set previous preset";
|
||||||
|
case BT_HAS_OP_SET_ACTIVE_PRESET_SYNC:
|
||||||
|
return "Set active preset (sync)";
|
||||||
|
case BT_HAS_OP_SET_NEXT_PRESET_SYNC:
|
||||||
|
return "Set next preset (sync)";
|
||||||
|
case BT_HAS_OP_SET_PREV_PRESET_SYNC:
|
||||||
|
return "Set previous preset (sync)";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *bt_has_change_id_str(uint8_t change_id)
|
||||||
|
{
|
||||||
|
switch (change_id) {
|
||||||
|
case BT_HAS_CHANGE_ID_GENERIC_UPDATE:
|
||||||
|
return "Generic update";
|
||||||
|
case BT_HAS_CHANGE_ID_PRESET_DELETED:
|
||||||
|
return "Preset deleted";
|
||||||
|
case BT_HAS_CHANGE_ID_PRESET_AVAILABLE:
|
||||||
|
return "Preset available";
|
||||||
|
case BT_HAS_CHANGE_ID_PRESET_UNAVAILABLE:
|
||||||
|
return "Preset unavailable";
|
||||||
|
default:
|
||||||
|
return "Unknown changeId";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue