blob: 72aaae2aafd0f56d31f5120702552bc75b18e329 [file] [log] [blame]
/** @file
* @brief Bluetooth Call Control Profile (CCP) Call Controller role.
*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2022 Codecoup
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <string.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/audio/tbs.h>
#define URI_SEPARATOR ":"
#define CALLER_ID "friend"
static uint8_t new_call_index;
static char remote_uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
static K_SEM_DEFINE(sem_discovery_done, 0, 1);
static struct bt_conn *default_conn;
static void discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count, bool gtbs_found)
{
if (!gtbs_found) {
printk("CCP: Failed to discover GTBS\n");
return;
}
printk("CCP: Discovered GTBS\n");
if (err) {
printk("%s (err %d)\n", __func__, err);
return;
}
/* Read Bearer URI Schemes Supported List Characteristic */
bt_tbs_client_read_uri_list(conn, BT_TBS_GTBS_INDEX);
}
static void originate_call_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint8_t call_index)
{
if (inst_index != BT_TBS_GTBS_INDEX) {
printk("Unexpected %s for instance %u\n", __func__, inst_index);
return;
}
if (err) {
printk("%s (err %d)\n", __func__, err);
return;
}
printk("CCP: Call originate successful\n");
new_call_index = call_index;
}
static void terminate_call_cb(struct bt_conn *conn, int err,
uint8_t inst_index, uint8_t call_index)
{
if (inst_index != BT_TBS_GTBS_INDEX) {
printk("Unexpected %s for instance %u\n", __func__, inst_index);
return;
}
if (err) {
printk("%s (err %d)\n", __func__, err);
return;
}
printk("CCP: Call with id %d terminated\n", call_index);
}
static void read_uri_schemes_string_cb(struct bt_conn *conn, int err,
uint8_t inst_index, const char *value)
{
size_t i;
if (inst_index != BT_TBS_GTBS_INDEX) {
printk("Unexpected %s for instance %u\n", __func__, inst_index);
return;
}
if (err) {
printk("%s (err %d)\n", __func__, err);
return;
}
/* Save first remote URI
*
* First search for the first comma (separator), and use that to determine the end of the
* first (or only) URI. Then use that length to copy the URI to `remote_uri` for later use.
*/
for (i = 0U; i < strlen(value); i++) {
if (value[i] == ',') {
break;
}
}
if (i > sizeof(remote_uri)) {
printk("Cannot store URI of length %zu: %s\n", i, value);
return;
}
strncpy(remote_uri, value, i);
remote_uri[i] = '\0';
printk("CCP: Discovered remote URI: %s\n", remote_uri);
k_sem_give(&sem_discovery_done);
}
struct bt_tbs_client_cb tbs_client_cb = {
.discover = discover_cb,
.uri_list = read_uri_schemes_string_cb,
.originate_call = originate_call_cb,
.terminate_call = terminate_call_cb,
};
int ccp_call_ctrl_init(struct bt_conn *conn)
{
int err;
default_conn = bt_conn_ref(conn);
bt_tbs_client_register_cb(&tbs_client_cb);
err = bt_tbs_client_discover(conn, true);
if (err != 0) {
return err;
}
k_sem_take(&sem_discovery_done, K_FOREVER);
return err;
}
int ccp_originate_call(void)
{
int err;
char uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
strcpy(uri, remote_uri);
strcat(uri, URI_SEPARATOR);
strcat(uri, CALLER_ID);
err = bt_tbs_client_originate_call(default_conn, BT_TBS_GTBS_INDEX, uri);
if (err != BT_TBS_RESULT_CODE_SUCCESS) {
printk("TBS originate call failed: %d\n", err);
}
return err;
}
int ccp_terminate_call(void)
{
int err;
err = bt_tbs_client_terminate_call(default_conn, BT_TBS_GTBS_INDEX, new_call_index);
if (err != BT_TBS_RESULT_CODE_SUCCESS) {
printk("TBS terminate call failed: %d\n", err);
}
return err;
}