1 /** @file
2  *  @brief Bluetooth Call Control Profile (CCP) Call Controller role.
3  *
4  *  Copyright (c) 2020 Nordic Semiconductor ASA
5  *  Copyright (c) 2022 Codecoup
6  *  Copyright 2023 NXP
7  *
8  *  SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/sys/util.h>
14 #include <string.h>
15 
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/audio/tbs.h>
18 
19 #define URI_SEPARATOR ":"
20 #define CALLER_ID "friend"
21 
22 static uint8_t new_call_index;
23 static char remote_uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
24 
25 static K_SEM_DEFINE(sem_discovery_done, 0, 1);
26 
27 static struct bt_conn *default_conn;
28 
discover_cb(struct bt_conn * conn,int err,uint8_t tbs_count,bool gtbs_found)29 static void discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count, bool gtbs_found)
30 {
31 	if (!gtbs_found) {
32 		printk("CCP: Failed to discover GTBS\n");
33 		return;
34 	}
35 
36 	printk("CCP: Discovered GTBS\n");
37 
38 	if (err) {
39 		printk("%s (err %d)\n", __func__, err);
40 		return;
41 	}
42 
43 	/* Read Bearer URI Schemes Supported List Characteristic */
44 	bt_tbs_client_read_uri_list(conn, BT_TBS_GTBS_INDEX);
45 }
46 
originate_call_cb(struct bt_conn * conn,int err,uint8_t inst_index,uint8_t call_index)47 static void originate_call_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint8_t call_index)
48 {
49 	if (inst_index != BT_TBS_GTBS_INDEX) {
50 		printk("Unexpected %s for instance %u\n", __func__, inst_index);
51 		return;
52 	}
53 
54 	if (err) {
55 		printk("%s (err %d)\n", __func__, err);
56 		return;
57 	}
58 	printk("CCP: Call originate successful\n");
59 	new_call_index = call_index;
60 }
61 
terminate_call_cb(struct bt_conn * conn,int err,uint8_t inst_index,uint8_t call_index)62 static void terminate_call_cb(struct bt_conn *conn, int err,
63 			      uint8_t inst_index, uint8_t call_index)
64 {
65 	if (inst_index != BT_TBS_GTBS_INDEX) {
66 		printk("Unexpected %s for instance %u\n", __func__, inst_index);
67 		return;
68 	}
69 
70 	if (err) {
71 		printk("%s (err %d)\n", __func__, err);
72 		return;
73 	}
74 	printk("CCP: Call with id %d terminated\n", call_index);
75 }
76 
read_uri_schemes_string_cb(struct bt_conn * conn,int err,uint8_t inst_index,const char * value)77 static void read_uri_schemes_string_cb(struct bt_conn *conn, int err,
78 				       uint8_t inst_index, const char *value)
79 {
80 	size_t i;
81 
82 	if (inst_index != BT_TBS_GTBS_INDEX) {
83 		printk("Unexpected %s for instance %u\n", __func__, inst_index);
84 		return;
85 	}
86 
87 	if (err) {
88 		printk("%s (err %d)\n", __func__, err);
89 		return;
90 	}
91 
92 	/* Save first remote URI
93 	 *
94 	 * First search for the first comma (separator), and use that to determine the end of the
95 	 * first (or only) URI. Then use that length to copy the URI to `remote_uri` for later use.
96 	 */
97 	for (i = 0U; i < strlen(value); i++) {
98 		if (value[i] == ',') {
99 			break;
100 		}
101 	}
102 
103 	if (i >= sizeof(remote_uri)) {
104 		printk("Cannot store URI of length %zu: %s\n", i, value);
105 		return;
106 	}
107 
108 	strncpy(remote_uri, value, i);
109 	remote_uri[i] = '\0';
110 
111 	printk("CCP: Discovered remote URI: %s\n", remote_uri);
112 	k_sem_give(&sem_discovery_done);
113 }
114 
115 struct bt_tbs_client_cb tbs_client_cb = {
116 	.discover = discover_cb,
117 	.uri_list = read_uri_schemes_string_cb,
118 	.originate_call = originate_call_cb,
119 	.terminate_call = terminate_call_cb,
120 };
121 
ccp_call_ctrl_init(struct bt_conn * conn)122 int ccp_call_ctrl_init(struct bt_conn *conn)
123 {
124 	int err;
125 
126 	default_conn = bt_conn_ref(conn);
127 	bt_tbs_client_register_cb(&tbs_client_cb);
128 	err = bt_tbs_client_discover(conn);
129 	if (err != 0) {
130 		return err;
131 	}
132 	k_sem_take(&sem_discovery_done, K_FOREVER);
133 
134 	return err;
135 }
136 
ccp_originate_call(void)137 int ccp_originate_call(void)
138 {
139 	int err;
140 	char uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
141 
142 	strcpy(uri, remote_uri);
143 	strcat(uri, URI_SEPARATOR);
144 	strcat(uri, CALLER_ID);
145 
146 	err = bt_tbs_client_originate_call(default_conn, BT_TBS_GTBS_INDEX, uri);
147 	if (err != BT_TBS_RESULT_CODE_SUCCESS) {
148 		printk("TBS originate call failed: %d\n", err);
149 	}
150 
151 	return err;
152 }
153 
ccp_terminate_call(void)154 int ccp_terminate_call(void)
155 {
156 	int err;
157 
158 	err = bt_tbs_client_terminate_call(default_conn, BT_TBS_GTBS_INDEX, new_call_index);
159 	if (err != BT_TBS_RESULT_CODE_SUCCESS) {
160 		printk("TBS terminate call failed: %d\n", err);
161 	}
162 
163 	return err;
164 }
165