| /* 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; |
| |
| tester_set_bit(rp->data, BTP_HAS_READ_SUPPORTED_COMMANDS); |
| tester_set_bit(rp->data, BTP_HAS_SET_ACTIVE_INDEX); |
| tester_set_bit(rp->data, BTP_HAS_SET_PRESET_NAME); |
| tester_set_bit(rp->data, BTP_HAS_REMOVE_PRESET); |
| tester_set_bit(rp->data, BTP_HAS_ADD_PRESET); |
| tester_set_bit(rp->data, BTP_HAS_SET_PROPERTIES); |
| |
| *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 BTP_STATUS_VAL(err); |
| } |
| |
| 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 BTP_STATUS_VAL(err); |
| } |
| |
| 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 BTP_STATUS_VAL(err); |
| } |
| |
| 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 BTP_STATUS_VAL(err); |
| } |
| |
| 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 BTP_STATUS_VAL(err); |
| } |
| |
| 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 |
| } |
| }; |
| |
| uint8_t tester_init_has(void) |
| { |
| tester_register_command_handlers(BTP_SERVICE_ID_HAS, has_handlers, |
| ARRAY_SIZE(has_handlers)); |
| |
| struct bt_has_features_param params = { |
| .type = BT_HAS_HEARING_AID_TYPE_BINAURAL, |
| .preset_sync_support = false, |
| .independent_presets = IS_ENABLED(CONFIG_BT_HAS_PRESET_SUPPORT) |
| }; |
| int err = bt_has_register(¶ms); |
| |
| return BTP_STATUS_VAL(err); |
| } |
| |
| uint8_t tester_unregister_has(void) |
| { |
| return BTP_STATUS_SUCCESS; |
| } |