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