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 
11 #include <zephyr/autoconf.h>
12 #include <zephyr/bluetooth/addr.h>
13 #include <zephyr/bluetooth/audio/tbs.h>
14 #include <zephyr/bluetooth/audio/ccp.h>
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/gap.h>
18 #include <zephyr/bluetooth/hci_types.h>
19 #include <zephyr/bluetooth/uuid.h>
20 #include <zephyr/kernel.h>
21 #include <zephyr/logging/log.h>
22 #include <zephyr/sys/util.h>
23 #include <zephyr/sys/util_macro.h>
24 
25 LOG_MODULE_REGISTER(ccp_call_control_server, CONFIG_LOG_DEFAULT_LEVEL);
26 
27 #define SEM_TIMEOUT K_SECONDS(5)
28 
29 static const struct bt_data ad[] = {
30 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
31 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
32 	BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_GTBS_VAL)),
33 	BT_DATA_BYTES(BT_DATA_SVC_DATA16, BT_UUID_16_ENCODE(BT_UUID_GTBS_VAL)),
34 	IF_ENABLED(CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT > 1,
35 		   (BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_TBS_VAL)),
36 		    BT_DATA_BYTES(BT_DATA_SVC_DATA16, BT_UUID_16_ENCODE(BT_UUID_TBS_VAL))))};
37 
38 static struct bt_le_ext_adv *adv;
39 static struct bt_conn *peer_conn;
40 static struct bt_ccp_call_control_server_bearer
41 	*bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT];
42 
43 static K_SEM_DEFINE(sem_state_change, 0, 1);
44 
connected_cb(struct bt_conn * conn,uint8_t err)45 static void connected_cb(struct bt_conn *conn, uint8_t err)
46 {
47 	char addr[BT_ADDR_LE_STR_LEN];
48 
49 	(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
50 	LOG_INF("Connected: %s", addr);
51 
52 	peer_conn = bt_conn_ref(conn);
53 	k_sem_give(&sem_state_change);
54 }
55 
disconnected_cb(struct bt_conn * conn,uint8_t reason)56 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
57 {
58 	char addr[BT_ADDR_LE_STR_LEN];
59 
60 	if (conn != peer_conn) {
61 		return;
62 	}
63 
64 	(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
65 	LOG_INF("Disconnected: %s (reason 0x%02x)", addr, reason);
66 
67 	bt_conn_unref(peer_conn);
68 	peer_conn = NULL;
69 	k_sem_give(&sem_state_change);
70 }
71 
72 BT_CONN_CB_DEFINE(conn_callbacks) = {
73 	.connected = connected_cb,
74 	.disconnected = disconnected_cb,
75 };
76 
advertise(void)77 static int advertise(void)
78 {
79 	int err;
80 
81 	err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN, NULL, &adv);
82 	if (err) {
83 		LOG_ERR("Failed to create advertising set: %d", err);
84 
85 		return err;
86 	}
87 
88 	err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
89 	if (err) {
90 		LOG_ERR("Failed to set advertising data: %d", err);
91 
92 		return err;
93 	}
94 
95 	err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
96 	if (err) {
97 		LOG_ERR("Failed to start advertising set: %d", err);
98 
99 		return err;
100 	}
101 
102 	LOG_INF("Advertising successfully started");
103 
104 	/* Wait for connection*/
105 	err = k_sem_take(&sem_state_change, K_FOREVER);
106 	if (err != 0) {
107 		LOG_ERR("Failed to take sem_state_change: err %d", err);
108 
109 		return err;
110 	}
111 
112 	return 0;
113 }
114 
reset_ccp_call_control_server(void)115 static int reset_ccp_call_control_server(void)
116 {
117 	int err;
118 
119 	LOG_INF("Resetting");
120 
121 	if (peer_conn != NULL) {
122 		err = bt_conn_disconnect(peer_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
123 		if (err != 0) {
124 			return err;
125 		}
126 
127 		err = k_sem_take(&sem_state_change, K_FOREVER);
128 		if (err != 0) {
129 			LOG_ERR("Failed to take sem_state_change: %d", err);
130 			return err;
131 		}
132 	}
133 
134 	if (adv != NULL) {
135 		err = bt_le_ext_adv_stop(adv);
136 		if (err != 0) {
137 			LOG_ERR("Failed to stop advertiser: %d", err);
138 			return err;
139 		}
140 
141 		err = bt_le_ext_adv_delete(adv);
142 		if (err != 0) {
143 			LOG_ERR("Failed to delete advertiser: %d", err);
144 			return err;
145 		}
146 
147 		adv = NULL;
148 	}
149 
150 	k_sem_reset(&sem_state_change);
151 
152 	return 0;
153 }
154 
init_ccp_call_control_server(void)155 static int init_ccp_call_control_server(void)
156 {
157 	const struct bt_tbs_register_param gtbs_param = {
158 		.provider_name = "Generic TBS",
159 		.uci = "un000",
160 		.uri_schemes_supported = "tel,skype",
161 		.gtbs = true,
162 		.authorization_required = false,
163 		.technology = BT_TBS_TECHNOLOGY_3G,
164 		.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
165 	};
166 	int err;
167 
168 	err = bt_enable(NULL);
169 	if (err != 0) {
170 		LOG_ERR("Bluetooth enable failed (err %d)", err);
171 
172 		return err;
173 	}
174 
175 	LOG_DBG("Bluetooth initialized");
176 
177 	err = bt_ccp_call_control_server_register_bearer(&gtbs_param, &bearers[0]);
178 	if (err < 0) {
179 		LOG_ERR("Failed to register GTBS (err %d)", err);
180 
181 		return err;
182 	}
183 
184 	LOG_INF("Registered GTBS bearer");
185 
186 	for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
187 		char prov_name[22]; /* Enough to store "Telephone Bearer #255" */
188 		const struct bt_tbs_register_param tbs_param = {
189 			.provider_name = prov_name,
190 			.uci = "un000",
191 			.uri_schemes_supported = "tel,skype",
192 			.gtbs = false,
193 			.authorization_required = false,
194 			/* Set different technologies per bearer */
195 			.technology = (i % BT_TBS_TECHNOLOGY_WCDMA) + 1,
196 			.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
197 		};
198 
199 		snprintf(prov_name, sizeof(prov_name), "Telephone Bearer #%d", i);
200 
201 		err = bt_ccp_call_control_server_register_bearer(&tbs_param, &bearers[i]);
202 		if (err < 0) {
203 			LOG_ERR("Failed to register bearer[%d]: %d", i, err);
204 
205 			return err;
206 		}
207 
208 		LOG_INF("Registered bearer[%d]", i);
209 	}
210 
211 	return 0;
212 }
213 
main(void)214 int main(void)
215 {
216 	int err;
217 
218 	err = init_ccp_call_control_server();
219 	if (err != 0) {
220 		return 0;
221 	}
222 
223 	LOG_INF("CCP Call Control Server initialized");
224 
225 	while (true) {
226 		err = reset_ccp_call_control_server();
227 		if (err != 0) {
228 			LOG_ERR("Failed to reset");
229 
230 			break;
231 		}
232 
233 		/* Start advertising as a CCP Call Control Server, which includes setting the
234 		 * required advertising data based on the roles we support.
235 		 */
236 		err = advertise();
237 		if (err != 0) {
238 			continue;
239 		}
240 
241 		/* After advertising we expect CCP Call Control Clients to connect to us and
242 		 * eventually disconnect again. As a CCP Call Control Server we just react to their
243 		 * requests and not do anything else.
244 		 */
245 
246 		/* Reset if disconnected */
247 		err = k_sem_take(&sem_state_change, K_FOREVER);
248 		if (err != 0) {
249 			LOG_ERR("Failed to take sem_state_change: err %d", err);
250 
251 			break;
252 		}
253 	}
254 
255 	return 0;
256 }
257