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 /* call_control_client is not static as it is used for testing purposes */
34 struct bt_ccp_call_control_client *call_control_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 call_control_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
210 #if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
ccp_call_control_client_read_bearer_provider_name_cb(struct bt_ccp_call_control_client_bearer * bearer,int err,const char * name)211 static void ccp_call_control_client_read_bearer_provider_name_cb(
212 struct bt_ccp_call_control_client_bearer *bearer, int err, const char *name)
213 {
214 if (err != 0) {
215 LOG_ERR("Failed to read bearer %p provider name: %d\n", (void *)bearer, err);
216 return;
217 }
218
219 LOG_INF("Bearer %p provider name: %s", (void *)bearer, name);
220
221 k_sem_give(&sem_ccp_action_completed);
222 }
223 #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */
224
reset_ccp_call_control_client(void)225 static int reset_ccp_call_control_client(void)
226 {
227 int err;
228
229 LOG_INF("Resetting");
230
231 if (peer_conn != NULL) {
232 err = bt_conn_disconnect(peer_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
233 if (err != 0) {
234 return err;
235 }
236
237 err = k_sem_take(&sem_conn_state_change, K_FOREVER);
238 if (err != 0) {
239 LOG_ERR("Failed to take sem_conn_state_change: %d", err);
240 return err;
241 }
242 }
243
244 /* If scanning is already stopped it will still return `0` */
245 err = bt_le_scan_stop();
246 if (err != 0) {
247 LOG_ERR("Scanning failed to stop (err %d)", err);
248 return err;
249 }
250
251 k_sem_reset(&sem_conn_state_change);
252
253 return 0;
254 }
255
discover_services(void)256 static int discover_services(void)
257 {
258 int err;
259
260 LOG_INF("Discovering GTBS and TBS");
261
262 err = bt_ccp_call_control_client_discover(peer_conn, &call_control_client);
263 if (err != 0) {
264 LOG_ERR("Failed to discover: %d", err);
265 return err;
266 }
267
268 err = k_sem_take(&sem_ccp_action_completed, SEM_TIMEOUT);
269 if (err != 0) {
270 LOG_ERR("Failed to take sem_ccp_action_completed: %d", err);
271 return err;
272 }
273
274 return 0;
275 }
276
read_bearer_name(struct bt_ccp_call_control_client_bearer * bearer)277 static int read_bearer_name(struct bt_ccp_call_control_client_bearer *bearer)
278 {
279 int err;
280
281 err = bt_ccp_call_control_client_read_bearer_provider_name(bearer);
282 if (err != 0) {
283 return err;
284 }
285
286 err = k_sem_take(&sem_ccp_action_completed, SEM_TIMEOUT);
287 if (err != 0) {
288 LOG_ERR("Failed to take sem_ccp_action_completed: %d", err);
289 return err;
290 }
291
292 return 0;
293 }
294
read_bearer_names(void)295 static int read_bearer_names(void)
296 {
297 int err;
298
299 #if defined(CONFIG_BT_TBS_CLIENT_GTBS)
300 err = read_bearer_name(client_bearers.gtbs_bearer);
301 if (err != 0) {
302 LOG_ERR("Failed to read name for GTBS bearer: %d", err);
303 return err;
304 }
305 #endif /* CONFIG_BT_TBS_CLIENT_GTBS */
306
307 #if defined(CONFIG_BT_TBS_CLIENT_TBS)
308 for (size_t i = 0; i < client_bearers.tbs_count; i++) {
309 err = read_bearer_name(client_bearers.tbs_bearers[i]);
310 if (err != 0) {
311 LOG_ERR("Failed to read name for bearer[%zu]: %d", i, err);
312 return err;
313 }
314 }
315 #endif /* CONFIG_BT_TBS_CLIENT_TBS */
316
317 return 0;
318 }
319
init_ccp_call_control_client(void)320 static int init_ccp_call_control_client(void)
321 {
322 static struct bt_ccp_call_control_client_cb ccp_call_control_client_cbs = {
323 .discover = ccp_call_control_client_discover_cb,
324 #if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
325 .bearer_provider_name = ccp_call_control_client_read_bearer_provider_name_cb
326 #endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME */
327 };
328 static struct bt_le_scan_cb scan_cbs = {
329 .recv = scan_recv_cb,
330 };
331 int err;
332
333 err = bt_enable(NULL);
334 if (err != 0) {
335 LOG_ERR("Bluetooth enable failed (err %d)", err);
336
337 return err;
338 }
339
340 LOG_DBG("Bluetooth initialized");
341 err = bt_le_scan_cb_register(&scan_cbs);
342 if (err != 0) {
343 LOG_ERR("Bluetooth enable failed (err %d)", err);
344
345 return err;
346 }
347
348 err = bt_ccp_call_control_client_register_cb(&ccp_call_control_client_cbs);
349 if (err != 0) {
350 LOG_ERR("Bluetooth enable failed (err %d)", err);
351
352 return err;
353 }
354
355 return 0;
356 }
357
main(void)358 int main(void)
359 {
360 int err;
361
362 err = init_ccp_call_control_client();
363 if (err != 0) {
364 return 0;
365 }
366
367 LOG_INF("CCP Call Control Client initialized");
368
369 while (true) {
370 err = reset_ccp_call_control_client();
371 if (err != 0) {
372 break;
373 }
374
375 /* Start scanning for CCP servers and connect to the first we find */
376 err = scan_and_connect();
377 if (err != 0) {
378 continue;
379 }
380
381 /* Discover TBS and GTBS on the remove server */
382 err = discover_services();
383 if (err != 0) {
384 continue;
385 }
386
387 if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)) {
388 err = read_bearer_names();
389 if (err != 0) {
390 continue;
391 }
392 }
393
394 /* Reset if disconnected */
395 err = k_sem_take(&sem_conn_state_change, K_FOREVER);
396 if (err != 0) {
397 LOG_ERR("Failed to take sem_conn_state_change: err %d", err);
398 break;
399 }
400 }
401
402 return 0;
403 }
404