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