blob: 6c0d18258c3a1b62f434cc4805311aba34483aa8 [file] [log] [blame]
/* btp_ccp.c - Bluetooth CCP Tester */
/*
* Copyright (c) 2023 Oticon
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "btp/btp.h"
#include "zephyr/sys/byteorder.h"
#include <stdint.h>
#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME bttester_ccp
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
static uint8_t ccp_supported_commands(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
struct btp_ccp_read_supported_commands_rp *rp = rsp;
/* octet 0 */
tester_set_bit(rp->data, BTP_CCP_READ_SUPPORTED_COMMANDS);
tester_set_bit(rp->data, BTP_CCP_DISCOVER_TBS);
tester_set_bit(rp->data, BTP_CCP_ACCEPT_CALL);
tester_set_bit(rp->data, BTP_CCP_TERMINATE_CALL);
tester_set_bit(rp->data, BTP_CCP_ORIGINATE_CALL);
tester_set_bit(rp->data, BTP_CCP_READ_CALL_STATE);
*rsp_len = sizeof(*rp) + 1;
return BTP_STATUS_SUCCESS;
}
static void tbs_client_discovered_ev(int err, uint8_t tbs_count, bool gtbs_found)
{
struct btp_ccp_discovered_ev ev;
ev.status = sys_cpu_to_le32(err);
ev.tbs_count = tbs_count;
ev.gtbs_found = gtbs_found;
tester_event(BTP_SERVICE_ID_CCP, BTP_CCP_EV_DISCOVERED, &ev, sizeof(ev));
}
/**
* @brief Callback function for ccp_discover.
*
* @param conn The connection that was used to discover CCP for a
* device.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param tbs_count Number of TBS instances on peer device.
* @param gtbs_found Whether or not the server has a Generic TBS instance.
*/
static void tbs_client_discover_cb(struct bt_conn *conn,
int err,
uint8_t tbs_count,
bool gtbs_found)
{
LOG_DBG("Discovered TBS - err (%u) GTBS (%u)", err, gtbs_found);
tbs_client_discovered_ev(err, tbs_count, gtbs_found);
}
/**
* @brief Callback function for the CCP call control functions.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_index The call index. For #bt_tbs_client_originate_call this will
* always be 0, and does not reflect the actual call index.
*/
static void tbs_client_originate_call_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_index)
{
LOG_DBG("Originate call - err (%u) Call Index (%u)", err, call_index);
}
/**
* @brief Callback function for the CCP call control functions.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_index The call index. For #bt_tbs_client_originate_call this will
* always be 0, and does not reflect the actual call index.
*/
static void tbs_client_terminate_call_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_index)
{
LOG_DBG("Terminate call - err (%u) Call Index (%u)", err, call_index);
}
/**
* @brief Callback function for the CCP call control functions.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_index The call index. For #bt_tbs_client_originate_call this will
* always be 0, and does not reflect the actual call index.
*/
static void tbs_client_accept_call_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_index)
{
LOG_DBG("Accept call - err (%u) Call Index (%u)", err, call_index);
}
/**
* @brief Callback function for the CCP call control functions.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_index The call index. For #bt_tbs_client_originate_call this will
* always be 0, and does not reflect the actual call index.
*/
static void tbs_client_retrieve_call_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_index)
{
LOG_DBG("Retrieve call - err (%u) Call Index (%u)", err, call_index);
}
typedef struct bt_tbs_client_call_state bt_tbs_client_call_state_t;
#define CALL_STATES_EV_SIZE sizeof(struct btp_ccp_call_states_ev) + \
sizeof(bt_tbs_client_call_state_t) * \
CONFIG_BT_TBS_CLIENT_MAX_CALLS
static void tbs_client_call_states_ev(int err,
uint8_t inst_index,
uint8_t call_count,
const bt_tbs_client_call_state_t *call_states)
{
struct net_buf_simple *buf = NET_BUF_SIMPLE(CALL_STATES_EV_SIZE);
struct btp_ccp_call_states_ev ev = {
sys_cpu_to_le32(err), inst_index, call_count
};
net_buf_simple_init(buf, 0);
net_buf_simple_add_mem(buf, &ev, sizeof(ev));
for (uint8_t n = 0; n < call_count; n++, call_states++) {
net_buf_simple_add_mem(buf, call_states, sizeof(bt_tbs_client_call_state_t));
}
tester_event(BTP_SERVICE_ID_CCP, BTP_CCP_EV_CALL_STATES, buf->data, buf->len);
}
/**
* @brief Callback function for ccp_read_call_state.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_count Number of call states read.
* @param call_states Array of call states. The array is not kept by
* the client, so must be copied to be saved.
*/
static void tbs_client_call_states_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_count,
const bt_tbs_client_call_state_t *call_states)
{
LOG_DBG("Call states - err (%u) Call Count (%u)", err, call_count);
tbs_client_call_states_ev(err, inst_index, call_count, call_states);
}
/**
* @brief Callback function for ccp_read_termination_reason.
*
* @param conn The connection used in the function.
* @param err Error value. BT_TBS_CLIENT_RESULT_CODE_*,
* GATT error or errno value.
* @param inst_index The index of the TBS instance that was updated.
* @param call_index The call index.
* @param reason The termination reason.
*/
static void tbs_client_termination_reason_cb(struct bt_conn *conn,
int err,
uint8_t inst_index,
uint8_t call_index,
uint8_t reason)
{
LOG_DBG("Termination reason - err (%u) Call Index (%u) Reason (%u)",
err, call_index, reason);
}
static const struct bt_tbs_client_cb tbs_client_callbacks = {
.discover = tbs_client_discover_cb,
.originate_call = tbs_client_originate_call_cb,
.terminate_call = tbs_client_terminate_call_cb,
.accept_call = tbs_client_accept_call_cb,
.retrieve_call = tbs_client_retrieve_call_cb,
.call_state = tbs_client_call_states_cb,
.termination_reason = tbs_client_termination_reason_cb
};
static uint8_t ccp_discover_tbs(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_ccp_discover_tbs_cmd *cp = cmd;
struct bt_conn *conn;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
err = (conn) ? bt_tbs_client_discover(conn) : -ENOTCONN;
if (conn) {
bt_conn_unref(conn);
}
return BTP_STATUS_VAL(err);
}
static uint8_t ccp_accept_call(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_ccp_accept_call_cmd *cp = cmd;
struct bt_conn *conn;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
err = (conn) ? bt_tbs_client_accept_call(conn, cp->inst_index, cp->call_id) : -ENOTCONN;
if (conn) {
bt_conn_unref(conn);
}
return BTP_STATUS_VAL(err);
}
static uint8_t ccp_terminate_call(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_ccp_terminate_call_cmd *cp = cmd;
struct bt_conn *conn;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
err = (conn) ? bt_tbs_client_terminate_call(conn, cp->inst_index, cp->call_id) :
-ENOTCONN;
if (conn) {
bt_conn_unref(conn);
}
return BTP_STATUS_VAL(err);
}
static uint8_t ccp_originate_call(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_ccp_originate_call_cmd *cp = cmd;
struct bt_conn *conn;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
err = (conn) ? bt_tbs_client_originate_call(conn, cp->inst_index, cp->uri) : -ENOTCONN;
if (conn) {
bt_conn_unref(conn);
}
return BTP_STATUS_VAL(err);
}
static uint8_t ccp_read_call_state(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_ccp_read_call_state_cmd *cp = cmd;
struct bt_conn *conn;
int err;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address);
err = (conn) ? bt_tbs_client_read_call_state(conn, cp->inst_index) : -ENOTCONN;
if (conn) {
bt_conn_unref(conn);
}
return BTP_STATUS_VAL(err);
}
static const struct btp_handler ccp_handlers[] = {
{
.opcode = BTP_CCP_READ_SUPPORTED_COMMANDS,
.index = BTP_INDEX_NONE,
.expect_len = 0,
.func = ccp_supported_commands
},
{
.opcode = BTP_CCP_DISCOVER_TBS,
.expect_len = sizeof(struct btp_ccp_discover_tbs_cmd),
.func = ccp_discover_tbs
},
{
.opcode = BTP_CCP_ACCEPT_CALL,
.expect_len = sizeof(struct btp_ccp_accept_call_cmd),
.func = ccp_accept_call
},
{
.opcode = BTP_CCP_TERMINATE_CALL,
.expect_len = sizeof(struct btp_ccp_terminate_call_cmd),
.func = ccp_terminate_call
},
{
.opcode = BTP_CCP_ORIGINATE_CALL,
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = ccp_originate_call
},
{
.opcode = BTP_CCP_READ_CALL_STATE,
.expect_len = sizeof(struct btp_ccp_read_call_state_cmd),
.func = ccp_read_call_state
}
};
uint8_t tester_init_ccp(void)
{
tester_register_command_handlers(BTP_SERVICE_ID_CCP, ccp_handlers,
ARRAY_SIZE(ccp_handlers));
bt_tbs_client_register_cb(&tbs_client_callbacks);
return BTP_STATUS_SUCCESS;
}
uint8_t tester_unregister_ccp(void)
{
return BTP_STATUS_SUCCESS;
}