blob: 5076d7c4a1bd4f8e9b4e6b7f3cd16aea26a08c8b [file] [log] [blame]
/* btp_core.c - Bluetooth Core service */
/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright (c) 2023 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/atomic.h>
#include <zephyr/types.h>
#include <string.h>
#include <zephyr/toolchain.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/net/buf.h>
#include <hci_core.h>
#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME bttester_core
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
#include "btp/btp.h"
static ATOMIC_DEFINE(registered_services, BTP_SERVICE_ID_MAX);
static uint8_t supported_commands(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
struct btp_core_read_supported_commands_rp *rp = rsp;
tester_set_bit(rp->data, BTP_CORE_READ_SUPPORTED_COMMANDS);
tester_set_bit(rp->data, BTP_CORE_READ_SUPPORTED_SERVICES);
tester_set_bit(rp->data, BTP_CORE_REGISTER_SERVICE);
tester_set_bit(rp->data, BTP_CORE_UNREGISTER_SERVICE);
*rsp_len = sizeof(*rp) + 1;
return BTP_STATUS_SUCCESS;
}
static uint8_t supported_services(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
struct btp_core_read_supported_services_rp *rp = rsp;
/* octet 0 */
tester_set_bit(rp->data, BTP_SERVICE_ID_CORE);
tester_set_bit(rp->data, BTP_SERVICE_ID_GAP);
tester_set_bit(rp->data, BTP_SERVICE_ID_GATT);
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
tester_set_bit(rp->data, BTP_SERVICE_ID_L2CAP);
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
#if defined(CONFIG_BT_MESH)
tester_set_bit(rp->data, BTP_SERVICE_ID_MESH);
#endif /* CONFIG_BT_MESH */
/* octet 1 */
#if defined(CONFIG_BT_VCP_VOL_REND)
tester_set_bit(rp->data, BTP_SERVICE_ID_VCS);
#endif /* CONFIG_BT_VCP_VOL_REND */
#if defined(CONFIG_BT_IAS) || defined(CONFIG_BT_IAS_CLIENT)
tester_set_bit(rp->data, BTP_SERVICE_ID_IAS);
#endif /* CONFIG_BT_IAS */
#if defined(CONFIG_BT_AICS) || defined(CONFIG_BT_AICS_CLIENT)
tester_set_bit(rp->data, BTP_SERVICE_ID_AICS);
#endif /*CONFIG_BT_AICS */
#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
tester_set_bit(rp->data, BTP_SERVICE_ID_VOCS);
#endif /* CONFIG_BT_VOCS */
*rsp_len = sizeof(*rp) + 2;
return BTP_STATUS_SUCCESS;
}
static uint8_t register_service(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_core_register_service_cmd *cp = cmd;
uint8_t status;
/* invalid service */
if ((cp->id == BTP_SERVICE_ID_CORE) || (cp->id > BTP_SERVICE_ID_MAX)) {
return BTP_STATUS_FAILED;
}
/* already registered */
if (atomic_test_bit(registered_services, cp->id)) {
return BTP_STATUS_FAILED;
}
switch (cp->id) {
case BTP_SERVICE_ID_GAP:
status = tester_init_gap();
break;
case BTP_SERVICE_ID_GATT:
status = tester_init_gatt();
break;
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
case BTP_SERVICE_ID_L2CAP:
status = tester_init_l2cap();
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
break;
#if defined(CONFIG_BT_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_init_mesh();
break;
#endif /* CONFIG_BT_MESH */
#if defined(CONFIG_BT_VCP_VOL_REND)
case BTP_SERVICE_ID_VCS:
status = tester_init_vcs();
break;
case BTP_SERVICE_ID_VOCS:
status = tester_init_vocs();
break;
case BTP_SERVICE_ID_AICS:
status = tester_init_aics();
break;
#endif /* CONFIG_BT_VCP_VOL_REND */
#if defined(CONFIG_BT_IAS)
case BTP_SERVICE_ID_IAS:
status = tester_init_ias();
break;
#endif /* CONFIG_BT_IAS */
#if defined(CONFIG_BT_BAP_UNICAST_CLIENT) || defined(CONFIG_BT_BAP_UNICAST_SERVER)
case BTP_SERVICE_ID_PACS:
status = tester_init_pacs();
break;
case BTP_SERVICE_ID_ASCS:
status = tester_init_ascs();
break;
case BTP_SERVICE_ID_BAP:
status = tester_init_bap();
break;
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
default:
LOG_WRN("unknown id: 0x%02x", cp->id);
status = BTP_STATUS_FAILED;
break;
}
if (status == BTP_STATUS_SUCCESS) {
atomic_set_bit(registered_services, cp->id);
}
return status;
}
static uint8_t unregister_service(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_core_unregister_service_cmd *cp = cmd;
uint8_t status;
/* invalid service ID */
if ((cp->id == BTP_SERVICE_ID_CORE) || (cp->id > BTP_SERVICE_ID_MAX)) {
return BTP_STATUS_FAILED;
}
/* not registered */
if (!atomic_test_bit(registered_services, cp->id)) {
return BTP_STATUS_FAILED;
}
switch (cp->id) {
case BTP_SERVICE_ID_GAP:
status = tester_unregister_gap();
break;
case BTP_SERVICE_ID_GATT:
status = tester_unregister_gatt();
break;
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
case BTP_SERVICE_ID_L2CAP:
status = tester_unregister_l2cap();
break;
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
#if defined(CONFIG_BT_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_unregister_mesh();
break;
#endif /* CONFIG_BT_MESH */
#if defined(CONFIG_BT_VCP_VOL_REND)
case BTP_SERVICE_ID_VCS:
status = tester_unregister_vcs();
break;
case BTP_SERVICE_ID_AICS:
status = tester_unregister_aics();
break;
case BTP_SERVICE_ID_VOCS:
status = tester_unregister_vocs();
break;
#endif /* CONFIG_BT_VCP_VOL_REND */
#if defined(CONFIG_BT_IAS)
case BTP_SERVICE_ID_IAS:
status = tester_unregister_ias();
break;
#endif /* CONFIG_BT_IAS */
#if defined(CONFIG_BT_BAP_UNICAST_CLIENT) || defined(CONFIG_BT_BAP_UNICAST_SERVER)
case BTP_SERVICE_ID_PACS:
status = tester_unregister_pacs();
break;
case BTP_SERVICE_ID_ASCS:
status = tester_unregister_ascs();
break;
case BTP_SERVICE_ID_BAP:
status = tester_unregister_bap();
break;
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT or CONFIG_BT_BAP_UNICAST_SERVER */
default:
LOG_WRN("unknown id: 0x%x", cp->id);
status = BTP_STATUS_FAILED;
break;
}
if (status == BTP_STATUS_SUCCESS) {
atomic_clear_bit(registered_services, cp->id);
}
return BTP_STATUS_FAILED;
}
static const struct btp_handler handlers[] = {
{
.opcode = BTP_CORE_READ_SUPPORTED_COMMANDS,
.index = BTP_INDEX_NONE,
.expect_len = 0,
.func = supported_commands,
},
{
.opcode = BTP_CORE_READ_SUPPORTED_SERVICES,
.index = BTP_INDEX_NONE,
.expect_len = 0,
.func = supported_services,
},
{
.opcode = BTP_CORE_REGISTER_SERVICE,
.index = BTP_INDEX_NONE,
.expect_len = sizeof(struct btp_core_register_service_cmd),
.func = register_service,
},
{
.opcode = BTP_CORE_UNREGISTER_SERVICE,
.index = BTP_INDEX_NONE,
.expect_len = sizeof(struct btp_core_unregister_service_cmd),
.func = unregister_service,
},
};
void tester_init_core(void)
{
tester_register_command_handlers(BTP_SERVICE_ID_CORE, handlers,
ARRAY_SIZE(handlers));
atomic_set_bit(registered_services, BTP_SERVICE_ID_CORE);
}