bluetooth: tester: Added support for testing Hearing Access Service (HAS).
- Initialisation of Hearing Access Service. - Adding/removing presets. - Changing preset properties. - Changing preset name. Signed-off-by: Henrik Eriksen <heri@demant.com>
This commit is contained in:
parent
22152915ab
commit
070122f55b
7 changed files with 306 additions and 2 deletions
|
@ -35,3 +35,7 @@ endif()
|
||||||
if(CONFIG_BT_BAP_UNICAST)
|
if(CONFIG_BT_BAP_UNICAST)
|
||||||
target_sources(app PRIVATE src/btp_bap.c)
|
target_sources(app PRIVATE src/btp_bap.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_BT_HAS)
|
||||||
|
target_sources(app PRIVATE src/btp_has.c)
|
||||||
|
endif()
|
||||||
|
|
|
@ -45,3 +45,8 @@ CONFIG_BT_VCP_VOL_REND_LOG_LEVEL_DBG=y
|
||||||
# IAS
|
# IAS
|
||||||
CONFIG_BT_IAS=y
|
CONFIG_BT_IAS=y
|
||||||
CONFIG_BT_IAS_CLIENT=y
|
CONFIG_BT_IAS_CLIENT=y
|
||||||
|
|
||||||
|
# HAS
|
||||||
|
CONFIG_BT_HAS=y
|
||||||
|
CONFIG_BT_HAS_PRESET_COUNT=5
|
||||||
|
CONFIG_BT_HAS_PRESET_NAME_DYNAMIC=y
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "btp_pacs.h"
|
#include "btp_pacs.h"
|
||||||
#include "btp_ascs.h"
|
#include "btp_ascs.h"
|
||||||
#include "btp_bap.h"
|
#include "btp_bap.h"
|
||||||
|
#include "btp_has.h"
|
||||||
|
|
||||||
#define BTP_MTU 1024
|
#define BTP_MTU 1024
|
||||||
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
|
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
|
||||||
|
@ -45,7 +46,9 @@
|
||||||
#define BTP_SERVICE_ID_PACS 12
|
#define BTP_SERVICE_ID_PACS 12
|
||||||
#define BTP_SERVICE_ID_ASCS 13
|
#define BTP_SERVICE_ID_ASCS 13
|
||||||
#define BTP_SERVICE_ID_BAP 14
|
#define BTP_SERVICE_ID_BAP 14
|
||||||
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_BAP
|
#define BTP_SERVICE_ID_HAS 15
|
||||||
|
|
||||||
|
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_HAS
|
||||||
|
|
||||||
#define BTP_STATUS_SUCCESS 0x00
|
#define BTP_STATUS_SUCCESS 0x00
|
||||||
#define BTP_STATUS_FAILED 0x01
|
#define BTP_STATUS_FAILED 0x01
|
||||||
|
@ -65,7 +68,7 @@ struct btp_hdr {
|
||||||
uint8_t data[];
|
uint8_t data[];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define BTP_STATUS 0x00
|
#define BTP_STATUS 0x00
|
||||||
struct btp_status {
|
struct btp_status {
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
61
tests/bluetooth/tester/src/btp/btp_has.h
Normal file
61
tests/bluetooth/tester/src/btp/btp_has.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* btp_has.h - Bluetooth tester headers */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Oticon
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* HAS commands */
|
||||||
|
#define BTP_HAS_READ_SUPPORTED_COMMANDS 0x01
|
||||||
|
struct btp_has_read_supported_commands_rp {
|
||||||
|
uint8_t data[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BTP_HAS_SET_ACTIVE_INDEX 0x02
|
||||||
|
struct btp_has_set_active_index_cmd {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BTP_HAS_SET_PRESET_NAME 0x03
|
||||||
|
struct btp_has_set_preset_name_cmd {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t length;
|
||||||
|
char name[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BTP_HAS_REMOVE_PRESET 0x04
|
||||||
|
struct btp_has_remove_preset_cmd {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BTP_HAS_ADD_PRESET 0x05
|
||||||
|
struct btp_has_add_preset_cmd {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t props;
|
||||||
|
uint8_t length;
|
||||||
|
char name[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BTP_HAS_SET_PROPERTIES 0x06
|
||||||
|
struct btp_has_set_properties_cmd {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t props;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* HAS events */
|
||||||
|
#define BTP_HAS_EV_OPERATION_COMPLETED 0x80
|
||||||
|
struct btp_has_operation_completed_ev {
|
||||||
|
bt_addr_le_t address;
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t opcode;
|
||||||
|
uint8_t status;
|
||||||
|
|
||||||
|
/* RFU */
|
||||||
|
uint8_t flags;
|
||||||
|
} __packed;
|
|
@ -86,3 +86,6 @@ uint8_t tester_unregister_ascs(void);
|
||||||
|
|
||||||
uint8_t tester_init_bap(void);
|
uint8_t tester_init_bap(void);
|
||||||
uint8_t tester_unregister_bap(void);
|
uint8_t tester_unregister_bap(void);
|
||||||
|
|
||||||
|
uint8_t tester_init_has(void);
|
||||||
|
uint8_t tester_unregister_has(void);
|
||||||
|
|
|
@ -74,6 +74,9 @@ static uint8_t supported_services(const void *cmd, uint16_t cmd_len,
|
||||||
#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
|
#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
|
||||||
tester_set_bit(rp->data, BTP_SERVICE_ID_VOCS);
|
tester_set_bit(rp->data, BTP_SERVICE_ID_VOCS);
|
||||||
#endif /* CONFIG_BT_VOCS */
|
#endif /* CONFIG_BT_VOCS */
|
||||||
|
#if defined(CONFIG_BT_HAS) || defined(CONFIG_BT_HAS_CLIENT)
|
||||||
|
tester_set_bit(rp->data, BTP_SERVICE_ID_HAS);
|
||||||
|
#endif /* CONFIG_BT_HAS */
|
||||||
|
|
||||||
*rsp_len = sizeof(*rp) + 2;
|
*rsp_len = sizeof(*rp) + 2;
|
||||||
|
|
||||||
|
@ -140,6 +143,11 @@ static uint8_t register_service(const void *cmd, uint16_t cmd_len,
|
||||||
status = tester_init_bap();
|
status = tester_init_bap();
|
||||||
break;
|
break;
|
||||||
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
|
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
|
||||||
|
#if defined(CONFIG_BT_HAS)
|
||||||
|
case BTP_SERVICE_ID_HAS:
|
||||||
|
status = tester_init_has();
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_BT_HAS */
|
||||||
default:
|
default:
|
||||||
LOG_WRN("unknown id: 0x%02x", cp->id);
|
LOG_WRN("unknown id: 0x%02x", cp->id);
|
||||||
status = BTP_STATUS_FAILED;
|
status = BTP_STATUS_FAILED;
|
||||||
|
@ -213,6 +221,11 @@ static uint8_t unregister_service(const void *cmd, uint16_t cmd_len,
|
||||||
status = tester_unregister_bap();
|
status = tester_unregister_bap();
|
||||||
break;
|
break;
|
||||||
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
|
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
|
||||||
|
#if defined(CONFIG_BT_HAS)
|
||||||
|
case BTP_SERVICE_ID_HAS:
|
||||||
|
status = tester_unregister_has();
|
||||||
|
break;
|
||||||
|
#endif /* CONFIG_BT_HAS */
|
||||||
default:
|
default:
|
||||||
LOG_WRN("unknown id: 0x%x", cp->id);
|
LOG_WRN("unknown id: 0x%x", cp->id);
|
||||||
status = BTP_STATUS_FAILED;
|
status = BTP_STATUS_FAILED;
|
||||||
|
|
215
tests/bluetooth/tester/src/btp_has.c
Normal file
215
tests/bluetooth/tester/src/btp_has.c
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
/* btp_has.c - Bluetooth HAS Tester */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Oticon
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <zephyr/bluetooth/audio/has.h>
|
||||||
|
|
||||||
|
#include "btp/btp.h"
|
||||||
|
#include "zephyr/sys/byteorder.h"
|
||||||
|
#include "zephyr/arch/common/ffs.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#define LOG_MODULE_NAME bttester_has
|
||||||
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
|
||||||
|
|
||||||
|
static uint8_t has_supported_commands(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
struct btp_has_read_supported_commands_rp *rp = rsp;
|
||||||
|
|
||||||
|
/* octet 0 */
|
||||||
|
tester_set_bit(rp->data, BTP_HAS_READ_SUPPORTED_COMMANDS);
|
||||||
|
|
||||||
|
*rsp_len = sizeof(*rp) + 1;
|
||||||
|
|
||||||
|
return BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t has_set_active_index(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
const struct btp_has_set_active_index_cmd *cp = cmd;
|
||||||
|
int err = bt_has_preset_active_set(cp->index);
|
||||||
|
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t has_presets;
|
||||||
|
static char temp_name[BT_HAS_PRESET_NAME_MAX + 1];
|
||||||
|
|
||||||
|
static uint8_t has_set_preset_name(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
const struct btp_has_set_preset_name_cmd *cp = cmd;
|
||||||
|
const uint16_t fixed_size = sizeof(*cp);
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
if (cmd_len >= fixed_size && cmd_len >= (fixed_size + cp->length)) {
|
||||||
|
int name_len = MIN(cp->length, BT_HAS_PRESET_NAME_MAX);
|
||||||
|
|
||||||
|
memcpy(temp_name, cp->name, name_len);
|
||||||
|
temp_name[name_len] = '\0';
|
||||||
|
err = bt_has_preset_name_change(cp->index, temp_name);
|
||||||
|
}
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t has_remove_preset(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
const struct btp_has_remove_preset_cmd *cp = cmd;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (cp->index == BT_HAS_PRESET_INDEX_NONE) {
|
||||||
|
while (has_presets) {
|
||||||
|
uint8_t index = find_lsb_set(has_presets);
|
||||||
|
|
||||||
|
err = bt_has_preset_unregister(index);
|
||||||
|
if (err) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
has_presets &= ~(1 << (index - 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = bt_has_preset_unregister(cp->index);
|
||||||
|
if (!err) {
|
||||||
|
has_presets &= ~(1 << (cp->index - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_preset_selected(unsigned char index, bool sync)
|
||||||
|
{
|
||||||
|
return BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bt_has_preset_ops has_preset_ops = {
|
||||||
|
has_preset_selected, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t has_add_preset(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
const struct btp_has_add_preset_cmd *cp = cmd;
|
||||||
|
const uint16_t fixed_size = sizeof(*cp);
|
||||||
|
int err = -1;
|
||||||
|
|
||||||
|
if (cmd_len >= fixed_size && cmd_len >= (fixed_size + cp->length)) {
|
||||||
|
int name_len = MIN(cp->length, BT_HAS_PRESET_NAME_MAX);
|
||||||
|
|
||||||
|
memcpy(temp_name, cp->name, name_len);
|
||||||
|
temp_name[name_len] = '\0';
|
||||||
|
struct bt_has_preset_register_param preset_params = {
|
||||||
|
cp->index, cp->props, temp_name, &has_preset_ops
|
||||||
|
};
|
||||||
|
err = bt_has_preset_register(&preset_params);
|
||||||
|
if (!err) {
|
||||||
|
has_presets |= 1 << (cp->index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t has_set_properties(const void *cmd, uint16_t cmd_len,
|
||||||
|
void *rsp, uint16_t *rsp_len)
|
||||||
|
{
|
||||||
|
const struct btp_has_set_properties_cmd *cp = cmd;
|
||||||
|
int err = (cp->props & BT_HAS_PROP_AVAILABLE) ?
|
||||||
|
bt_has_preset_available(cp->index) :
|
||||||
|
bt_has_preset_unavailable(cp->index);
|
||||||
|
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct btp_handler has_handlers[] = {
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_READ_SUPPORTED_COMMANDS,
|
||||||
|
.index = BTP_INDEX_NONE,
|
||||||
|
.expect_len = 0,
|
||||||
|
.func = has_supported_commands
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_SET_ACTIVE_INDEX,
|
||||||
|
.expect_len = sizeof(struct btp_has_set_active_index_cmd),
|
||||||
|
.func = has_set_active_index
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_SET_PRESET_NAME,
|
||||||
|
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||||
|
.func = has_set_preset_name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_REMOVE_PRESET,
|
||||||
|
.expect_len = sizeof(struct btp_has_remove_preset_cmd),
|
||||||
|
.func = has_remove_preset
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_ADD_PRESET,
|
||||||
|
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
|
||||||
|
.func = has_add_preset
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.opcode = BTP_HAS_SET_PROPERTIES,
|
||||||
|
.expect_len = sizeof(struct btp_has_set_properties_cmd),
|
||||||
|
.func = has_set_properties
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *preset_name(uint8_t index)
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case 0: return "PRESET_0";
|
||||||
|
case 1: return "PRESET_1";
|
||||||
|
case 2: return "PRESET_2";
|
||||||
|
case 3: return "PRESET_3";
|
||||||
|
case 4: return "PRESET_4";
|
||||||
|
case 5: return "PRESET_5";
|
||||||
|
case 6: return "PRESET_6";
|
||||||
|
case 7: return "PRESET_7";
|
||||||
|
default: return "PRESET_?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRESETS_CONFIGURED 3
|
||||||
|
#define LAST_PRESET MIN(PRESETS_CONFIGURED, CONFIG_BT_HAS_PRESET_COUNT)
|
||||||
|
|
||||||
|
uint8_t tester_init_has(void)
|
||||||
|
{
|
||||||
|
tester_register_command_handlers(BTP_SERVICE_ID_HAS, has_handlers,
|
||||||
|
ARRAY_SIZE(has_handlers));
|
||||||
|
|
||||||
|
struct bt_has_register_param params = {
|
||||||
|
BT_HAS_HEARING_AID_TYPE_BINAURAL, false, true
|
||||||
|
};
|
||||||
|
int err = bt_has_register(¶ms);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
for (uint8_t index = 1; index <= LAST_PRESET; index++) {
|
||||||
|
enum bt_has_properties properties = (index < PRESETS_CONFIGURED) ?
|
||||||
|
BT_HAS_PROP_WRITABLE | BT_HAS_PROP_AVAILABLE :
|
||||||
|
BT_HAS_PROP_WRITABLE;
|
||||||
|
struct bt_has_preset_register_param preset_params = {
|
||||||
|
index, properties, preset_name(index), &has_preset_ops
|
||||||
|
};
|
||||||
|
|
||||||
|
err = bt_has_preset_register(&preset_params);
|
||||||
|
if (!err) {
|
||||||
|
has_presets |= 1 << (index - 1);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tester_unregister_has(void)
|
||||||
|
{
|
||||||
|
return BTP_STATUS_SUCCESS;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue