1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <zephyr/autoconf.h>
13 #include <zephyr/bluetooth/addr.h>
14 #include <zephyr/bluetooth/audio/tbs.h>
15 #include <zephyr/bluetooth/audio/ccp.h>
16 #include <zephyr/bluetooth/bluetooth.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/gap.h>
19 #include <zephyr/bluetooth/hci_types.h>
20 #include <zephyr/bluetooth/uuid.h>
21 #include <zephyr/kernel.h>
22 #include <zephyr/logging/log.h>
23 #include <zephyr/net_buf.h>
24 #include <zephyr/sys/byteorder.h>
25 #include <zephyr/sys/util.h>
26 #include <zephyr/sys/util_macro.h>
27 
28 LOG_MODULE_REGISTER(ccp_call_control_client, CONFIG_LOG_DEFAULT_LEVEL);
29 
30 #define SEM_TIMEOUT K_SECONDS(10)
31 
32 static struct bt_conn *peer_conn;
33 /* client is not static as it is used for testing purposes */
34 struct bt_ccp_call_control_client *client;
35 static struct bt_ccp_call_control_client_bearers client_bearers;
36 
37 static K_SEM_DEFINE(sem_conn_state_change, 0, 1);
38 static K_SEM_DEFINE(sem_security_updated, 0, 1);
39 static K_SEM_DEFINE(sem_ccp_action_completed, 0, 1);
40 
connected_cb(struct bt_conn * conn,uint8_t err)41 static void connected_cb(struct bt_conn *conn, uint8_t err)
42 {
43 	char addr[BT_ADDR_LE_STR_LEN];
44 
45 	(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
46 	LOG_INF("Connected: %s", addr);
47 
48 	k_sem_give(&sem_conn_state_change);
49 }
50 
disconnected_cb(struct bt_conn * conn,uint8_t reason)51 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
52 {
53 	char addr[BT_ADDR_LE_STR_LEN];
54 
55 	if (conn != peer_conn) {
56 		return;
57 	}
58 
59 	(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
60 	LOG_INF("Disconnected: %s (reason 0x%02x)", addr, reason);
61 
62 	bt_conn_unref(peer_conn);
63 	peer_conn = NULL;
64 	client = NULL;
65 	memset(&client_bearers, 0, sizeof(client_bearers));
66 	k_sem_give(&sem_conn_state_change);
67 }
68 
security_changed_cb(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)69 static void security_changed_cb(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
70 {
71 	if (err == 0) {
72 		k_sem_give(&sem_security_updated);
73 	} else {
74 		LOG_ERR("Failed to set security level: %s(%u)", bt_security_err_to_str(err), err);
75 	}
76 }
77 
78 BT_CONN_CB_DEFINE(conn_callbacks) = {
79 	.connected = connected_cb,
80 	.disconnected = disconnected_cb,
81 	.security_changed = security_changed_cb,
82 };
83 
check_gtbs_support(struct bt_data * data,void * user_data)84 static bool check_gtbs_support(struct bt_data *data, void *user_data)
85 {
86 	struct net_buf_simple svc_data;
87 	bool *connect = user_data;
88 	const struct bt_uuid *uuid;
89 	uint16_t uuid_val;
90 
91 	if (data->type != BT_DATA_SVC_DATA16) {
92 		return true; /* Continue parsing to next AD data type */
93 	}
94 
95 	if (data->data_len < sizeof(uuid_val)) {
96 		LOG_WRN("AD invalid size %u", data->data_len);
97 		return true; /* Continue parsing to next AD data type */
98 	}
99 
100 	net_buf_simple_init_with_data(&svc_data, (void *)data->data, data->data_len);
101 
102 	/* Pull the 16-bit service data and compare to what we are searching for */
103 	uuid_val = net_buf_simple_pull_le16(&svc_data);
104 	uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(uuid_val));
105 	if (bt_uuid_cmp(uuid, BT_UUID_GTBS) != 0) {
106 		/* We are looking for the GTBS service data */
107 		return true; /* Continue parsing to next AD data type */
108 	}
109 
110 	*connect = true;
111 
112 	return false; /* Stop parsing */
113 }
114 
scan_recv_cb(const struct bt_le_scan_recv_info * info,struct net_buf_simple * ad)115 static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
116 {
117 	char addr_str[BT_ADDR_LE_STR_LEN];
118 	bool connect = false;
119 
120 	if (peer_conn != NULL) {
121 		/* Already connected */
122 		return;
123 	}
124 
125 	/* CCP mandates that connectbale extended advertising is used by the peripherals so we
126 	 * ignore any scan report this is not that.
127 	 * We also ignore reports with poor RSSI
128 	 */
129 	if (info->adv_type != BT_GAP_ADV_TYPE_EXT_ADV ||
130 	    (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) == 0 ||
131 	    (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) == 0 || info->rssi < -70) {
132 		return;
133 	}
134 
135 	(void)bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str));
136 	LOG_INF("Connectable device found: %s (RSSI %d)", addr_str, info->rssi);
137 
138 	/* Iterate on the advertising data to see if claims GTBS support */
139 	bt_data_parse(ad, check_gtbs_support, &connect);
140 
141 	if (connect) {
142 		int err;
143 
144 		err = bt_le_scan_stop();
145 		if (err != 0) {
146 			LOG_ERR("Scanning failed to stop (err %d)", err);
147 			return;
148 		}
149 
150 		LOG_INF("Connecting to found CCP server");
151 		err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
152 					BT_LE_CONN_PARAM_DEFAULT, &peer_conn);
153 		if (err != 0) {
154 			LOG_ERR("Conn create failed: %d", err);
155 		}
156 	}
157 }
158 
scan_and_connect(void)159 static int scan_and_connect(void)
160 {
161 	int err;
162 
163 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
164 	if (err != 0) {
165 		LOG_ERR("Scanning failed to start (err %d)", err);
166 		return err;
167 	}
168 
169 	LOG_INF("Scanning successfully started");
170 
171 	err = k_sem_take(&sem_conn_state_change, K_FOREVER);
172 	if (err != 0) {
173 		LOG_ERR("failed to take sem_connected (err %d)", err);
174 		return err;
175 	}
176 
177 	err = bt_conn_set_security(peer_conn, BT_SECURITY_L2);
178 	if (err != 0) {
179 		LOG_ERR("failed to set security (err %d)", err);
180 		return err;
181 	}
182 
183 	err = k_sem_take(&sem_security_updated, SEM_TIMEOUT);
184 	if (err != 0) {
185 		LOG_ERR("failed to take sem_security_updated (err %d)", err);
186 		return err;
187 	}
188 
189 	LOG_INF("Security successfully updated");
190 
191 	return 0;
192 }
193 
ccp_call_control_client_discover_cb(struct bt_ccp_call_control_client * client,int err,struct bt_ccp_call_control_client_bearers * bearers)194 static void ccp_call_control_client_discover_cb(struct bt_ccp_call_control_client *client, int err,
195 						struct bt_ccp_call_control_client_bearers *bearers)
196 {
197 	if (err != 0) {
198 		LOG_ERR("Discovery failed: %d", err);
199 		return;
200 	}
201 
202 	LOG_INF("Discovery completed with %s%u TBS bearers",
203 		bearers->gtbs_bearer != NULL ? "GTBS and " : "", bearers->tbs_count);
204 
205 	memcpy(&client_bearers, bearers, sizeof(client_bearers));
206 
207 	k_sem_give(&sem_ccp_action_completed);
208 }
209 
reset_ccp_call_control_client(void)210 static int reset_ccp_call_control_client(void)
211 {
212 	int err;
213 
214 	LOG_INF("Resetting");
215 
216 	if (peer_conn != NULL) {
217 		err = bt_conn_disconnect(peer_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
218 		if (err != 0) {
219 			return err;
220 		}
221 
222 		err = k_sem_take(&sem_conn_state_change, K_FOREVER);
223 		if (err != 0) {
224 			LOG_ERR("Failed to take sem_conn_state_change: %d", err);
225 			return err;
226 		}
227 	}
228 
229 	/* If scanning is already stopped it will still return `0` */
230 	err = bt_le_scan_stop();
231 	if (err != 0) {
232 		LOG_ERR("Scanning failed to stop (err %d)", err);
233 		return err;
234 	}
235 
236 	k_sem_reset(&sem_conn_state_change);
237 
238 	return 0;
239 }
240 
discover_services(void)241 static int discover_services(void)
242 {
243 	int err;
244 
245 	LOG_INF("Discovering GTBS and TBS");
246 
247 	err = bt_ccp_call_control_client_discover(peer_conn, &client);
248 	if (err != 0) {
249 		LOG_ERR("Failed to discover: %d", err);
250 		return err;
251 	}
252 
253 	err = k_sem_take(&sem_ccp_action_completed, SEM_TIMEOUT);
254 	if (err != 0) {
255 		LOG_ERR("Failed to take sem_ccp_action_completed: %d", err);
256 		return err;
257 	}
258 
259 	return 0;
260 }
261 
init_ccp_call_control_client(void)262 static int init_ccp_call_control_client(void)
263 {
264 	static struct bt_le_scan_cb scan_cbs = {
265 		.recv = scan_recv_cb,
266 	};
267 	static struct bt_ccp_call_control_client_cb ccp_call_control_client_cbs = {
268 		.discover = ccp_call_control_client_discover_cb,
269 	};
270 	int err;
271 
272 	err = bt_enable(NULL);
273 	if (err != 0) {
274 		LOG_ERR("Bluetooth enable failed (err %d)", err);
275 
276 		return err;
277 	}
278 
279 	LOG_DBG("Bluetooth initialized");
280 	err = bt_le_scan_cb_register(&scan_cbs);
281 	if (err != 0) {
282 		LOG_ERR("Bluetooth enable failed (err %d)", err);
283 
284 		return err;
285 	}
286 
287 	err = bt_ccp_call_control_client_register_cb(&ccp_call_control_client_cbs);
288 	if (err != 0) {
289 		LOG_ERR("Bluetooth enable failed (err %d)", err);
290 
291 		return err;
292 	}
293 
294 	return 0;
295 }
296 
main(void)297 int main(void)
298 {
299 	int err;
300 
301 	err = init_ccp_call_control_client();
302 	if (err != 0) {
303 		return 0;
304 	}
305 
306 	LOG_INF("CCP Call Control Client initialized");
307 
308 	while (true) {
309 		err = reset_ccp_call_control_client();
310 		if (err != 0) {
311 			break;
312 		}
313 
314 		/* Start scanning for CCP servers and connect to the first we find */
315 		err = scan_and_connect();
316 		if (err != 0) {
317 			continue;
318 		}
319 
320 		/* Discover TBS and GTBS on the remove server */
321 		err = discover_services();
322 		if (err != 0) {
323 			continue;
324 		}
325 
326 		/* Reset if disconnected */
327 		err = k_sem_take(&sem_conn_state_change, K_FOREVER);
328 		if (err != 0) {
329 			LOG_ERR("Failed to take sem_conn_state_change: err %d", err);
330 			break;
331 		}
332 	}
333 
334 	return 0;
335 }
336