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(>bs_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