1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright 2024 NXP
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/types.h>
10 #include <stddef.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/kernel.h>
16
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/bluetooth/l2cap.h>
20 #include <zephyr/bluetooth/hci.h>
21 #include <zephyr/bluetooth/classic/rfcomm.h>
22 #include <zephyr/bluetooth/classic/sdp.h>
23 #include <zephyr/bluetooth/classic/hfp_ag.h>
24 #include <zephyr/settings/settings.h>
25
26 static struct bt_conn *default_conn;
27 struct bt_hfp_ag *hfp_ag;
28
29 static struct bt_br_discovery_param br_discover;
30
31 static struct bt_br_discovery_param br_discover;
32 static struct bt_br_discovery_result scan_result[CONFIG_BT_HFP_AG_DISCOVER_RESULT_COUNT];
33
34 struct k_work discover_work;
35 struct k_work_delayable call_connect_work;
36 struct k_work_delayable call_disconnect_work;
37
38 struct k_work_delayable call_remote_ringing_work;
39 struct k_work_delayable call_remote_accept_work;
40
41 NET_BUF_POOL_DEFINE(sdp_discover_pool, 10, BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU),
42 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
43
ag_connected(struct bt_hfp_ag * ag)44 static void ag_connected(struct bt_hfp_ag *ag)
45 {
46 printk("HFP AG connected!\n");
47 k_work_schedule(&call_connect_work, K_MSEC(CONFIG_BT_HFP_AG_START_CALL_DELAY_TIME));
48 }
49
ag_disconnected(struct bt_hfp_ag * ag)50 static void ag_disconnected(struct bt_hfp_ag *ag)
51 {
52 printk("HFP AG disconnected!\n");
53 }
54
ag_sco_connected(struct bt_hfp_ag * ag,struct bt_conn * sco_conn)55 static void ag_sco_connected(struct bt_hfp_ag *ag, struct bt_conn *sco_conn)
56 {
57 printk("HFP AG SCO connected!\n");
58 }
59
ag_sco_disconnected(struct bt_hfp_ag * ag)60 static void ag_sco_disconnected(struct bt_hfp_ag *ag)
61 {
62 printk("HFP AG SCO disconnected!\n");
63 }
64
ag_ringing(struct bt_hfp_ag * ag,bool in_band)65 static void ag_ringing(struct bt_hfp_ag *ag, bool in_band)
66 {
67 printk("Ringing (in bond? %s)\n", in_band ? "Yes" : "No");
68 }
69
ag_accept(struct bt_hfp_ag * ag)70 static void ag_accept(struct bt_hfp_ag *ag)
71 {
72 printk("Call Accepted\n");
73 k_work_schedule(&call_disconnect_work, K_SECONDS(10));
74 }
75
ag_reject(struct bt_hfp_ag * ag)76 static void ag_reject(struct bt_hfp_ag *ag)
77 {
78 printk("Call Rejected\n");
79 k_work_schedule(&call_disconnect_work, K_SECONDS(1));
80 }
81
ag_terminate(struct bt_hfp_ag * ag)82 static void ag_terminate(struct bt_hfp_ag *ag)
83 {
84 printk("Call terminated\n");
85 k_work_schedule(&call_disconnect_work, K_SECONDS(1));
86 }
87
ag_outgoing(struct bt_hfp_ag * ag,const char * number)88 static void ag_outgoing(struct bt_hfp_ag *ag, const char *number)
89 {
90 printk("Call outgoing, remote number %s\n", number);
91 k_work_cancel_delayable(&call_connect_work);
92 k_work_schedule(&call_remote_ringing_work, K_SECONDS(1));
93 }
94
ag_incoming(struct bt_hfp_ag * ag,const char * number)95 static void ag_incoming(struct bt_hfp_ag *ag, const char *number)
96 {
97 printk("Incoming call, remote number %s\n", number);
98 k_work_cancel_delayable(&call_connect_work);
99 }
100
101 static struct bt_hfp_ag_cb ag_cb = {
102 .connected = ag_connected,
103 .disconnected = ag_disconnected,
104 .sco_connected = ag_sco_connected,
105 .sco_disconnected = ag_sco_disconnected,
106 .outgoing = ag_outgoing,
107 .incoming = ag_incoming,
108 .ringing = ag_ringing,
109 .accept = ag_accept,
110 .reject = ag_reject,
111 .terminate = ag_terminate,
112 };
113
sdp_discover_cb(struct bt_conn * conn,struct bt_sdp_client_result * result,const struct bt_sdp_discover_params * params)114 static uint8_t sdp_discover_cb(struct bt_conn *conn, struct bt_sdp_client_result *result,
115 const struct bt_sdp_discover_params *params)
116 {
117 int err;
118 uint16_t value;
119
120 printk("Discover done\n");
121
122 if (result->resp_buf != NULL) {
123 err = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_RFCOMM, &value);
124
125 if (err != 0) {
126 printk("Fail to parser RFCOMM the SDP response!\n");
127 } else {
128 printk("The server channel is %d\n", value);
129 err = bt_hfp_ag_connect(conn, &hfp_ag, value);
130 if (err != 0) {
131 printk("Fail to create hfp AG connection (err %d)\n", err);
132 }
133 }
134 }
135
136 return BT_SDP_DISCOVER_UUID_STOP;
137 }
138
139 static struct bt_sdp_discover_params sdp_discover = {
140 .type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR,
141 .func = sdp_discover_cb,
142 .pool = &sdp_discover_pool,
143 .uuid = BT_UUID_DECLARE_16(BT_SDP_HANDSFREE_SVCLASS),
144 };
145
connected(struct bt_conn * conn,uint8_t err)146 static void connected(struct bt_conn *conn, uint8_t err)
147 {
148 int res;
149
150 if (err) {
151 if (default_conn != NULL) {
152 default_conn = NULL;
153 }
154 printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
155 } else {
156 if (default_conn == conn) {
157 struct bt_conn_info info;
158
159 bt_conn_get_info(conn, &info);
160 if (info.type != BT_CONN_TYPE_BR) {
161 return;
162 }
163
164 /*
165 * Do an SDP Query on Successful ACL connection complete with the
166 * required device
167 */
168 res = bt_sdp_discover(default_conn, &sdp_discover);
169 if (res) {
170 printk("SDP discovery failed (err %d)\r\n", res);
171 } else {
172 printk("SDP discovery started\r\n");
173 }
174 printk("Connected\n");
175 }
176 }
177 }
178
disconnected(struct bt_conn * conn,uint8_t reason)179 static void disconnected(struct bt_conn *conn, uint8_t reason)
180 {
181 printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
182
183 if (default_conn != conn) {
184 return;
185 }
186
187 if (default_conn) {
188 default_conn = NULL;
189 } else {
190 return;
191 }
192 }
193
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)194 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
195 {
196 char addr[BT_ADDR_LE_STR_LEN];
197 struct bt_conn_info info;
198
199 bt_conn_get_info(conn, &info);
200
201 bt_addr_to_str(info.br.dst, addr, sizeof(addr));
202
203 printk("Security changed: %s level %u, err %s(%d)\n", addr, level,
204 bt_security_err_to_str(err), err);
205 }
206
207 static struct bt_conn_cb conn_callbacks = {
208 .connected = connected,
209 .disconnected = disconnected,
210 .security_changed = security_changed,
211 };
212
discovery_recv_cb(const struct bt_br_discovery_result * result)213 static void discovery_recv_cb(const struct bt_br_discovery_result *result)
214 {
215 (void)result;
216 }
217
discovery_timeout_cb(const struct bt_br_discovery_result * results,size_t count)218 static void discovery_timeout_cb(const struct bt_br_discovery_result *results, size_t count)
219 {
220 char addr[BT_ADDR_LE_STR_LEN];
221 const uint8_t *eir;
222 bool cod_hf = false;
223 static uint8_t temp[240];
224 size_t len = sizeof(results->eir);
225 uint8_t major_device;
226 uint8_t minor_device;
227 size_t i;
228
229 for (i = 0; i < count; i++) {
230 bt_addr_to_str(&results[i].addr, addr, sizeof(addr));
231 printk("Device[%d]: %s, rssi %d, cod 0x%X%X%X", i, addr, results[i].rssi,
232 results[i].cod[0], results[i].cod[1], results[i].cod[2]);
233
234 major_device = (uint8_t)BT_COD_MAJOR_DEVICE_CLASS(results[i].cod);
235 minor_device = (uint8_t)BT_COD_MINOR_DEVICE_CLASS(results[i].cod);
236
237 if ((major_device & BT_COD_MAJOR_AUDIO_VIDEO) &&
238 (minor_device & BT_COD_MAJOR_AUDIO_VIDEO_MINOR_HANDS_FREE)) {
239 cod_hf = true;
240 }
241
242 eir = results[i].eir;
243
244 while ((eir[0] > 2) && (len > eir[0])) {
245 switch (eir[1]) {
246 case BT_DATA_NAME_SHORTENED:
247 case BT_DATA_NAME_COMPLETE:
248 memcpy(temp, &eir[2], eir[0] - 1);
249 temp[eir[0] - 1] = '\0'; /* Set end flag */
250 printk(", name %s", temp);
251 break;
252 }
253 len = len - eir[0] - 1;
254 eir = eir + eir[0] + 1;
255 }
256 printk("\n");
257
258 if (cod_hf) {
259 break;
260 }
261 }
262
263 if (!cod_hf) {
264 (void)k_work_submit(&discover_work);
265 } else {
266 (void)k_work_cancel(&discover_work);
267 default_conn = bt_conn_create_br(&results[i].addr, BT_BR_CONN_PARAM_DEFAULT);
268
269 if (default_conn == NULL) {
270 printk("Fail to create the connecton\n");
271 } else {
272 bt_conn_unref(default_conn);
273 }
274 }
275 }
276
discover_work_handler(struct k_work * work)277 static void discover_work_handler(struct k_work *work)
278 {
279 int err;
280
281 br_discover.length = 10;
282 br_discover.limited = false;
283
284 err = bt_br_discovery_start(&br_discover, scan_result,
285 CONFIG_BT_HFP_AG_DISCOVER_RESULT_COUNT);
286 if (err) {
287 printk("Fail to start discovery (err %d)\n", err);
288 return;
289 }
290 }
291
call_connect_work_handler(struct k_work * work)292 static void call_connect_work_handler(struct k_work *work)
293 {
294 #if CONFIG_BT_HFP_AG_CALL_OUTGOING
295 int err;
296
297 printk("Dialing\n");
298
299 err = bt_hfp_ag_outgoing(hfp_ag, "test_hf");
300
301 if (err != 0) {
302 printk("Fail to dial a call (err %d)\n", err);
303 }
304 #else
305 int err = bt_hfp_ag_remote_incoming(hfp_ag, "test_hf");
306
307 if (err != 0) {
308 printk("Fail to set remote incoming call (err %d)\n", err);
309 }
310 #endif /* CONFIG_BT_HFP_AG_CALL_OUTGOING */
311 }
312
call_disconnect_work_handler(struct k_work * work)313 static void call_disconnect_work_handler(struct k_work *work)
314 {
315 int err;
316
317 if (default_conn != NULL) {
318 err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
319
320 if (err != 0) {
321 printk("Fail to disconnect acl connection (err %d)\n", err);
322 }
323 }
324 }
325
call_remote_ringing_work_handler(struct k_work * work)326 static void call_remote_ringing_work_handler(struct k_work *work)
327 {
328 int err;
329
330 printk("Remote starts ringing\n");
331
332 err = bt_hfp_ag_remote_ringing(hfp_ag);
333
334 if (err != 0) {
335 printk("Fail to notify hfp unit that the remote starts ringing (err %d)\n", err);
336 } else {
337 k_work_schedule(&call_remote_accept_work, K_SECONDS(1));
338 }
339 }
340
call_remote_accept_work_handler(struct k_work * work)341 static void call_remote_accept_work_handler(struct k_work *work)
342 {
343 int err;
344
345 printk("Remote accepts the call\n");
346
347 err = bt_hfp_ag_remote_accept(hfp_ag);
348
349 if (err != 0) {
350 printk("Fail to notify hfp unit that the remote accepts call (err %d)\n", err);
351 }
352 }
353
354 static struct bt_br_discovery_cb discovery_cb = {
355 .recv = discovery_recv_cb,
356 .timeout = discovery_timeout_cb,
357 };
358
bt_ready(int err)359 static void bt_ready(int err)
360 {
361 if (err) {
362 printk("Bluetooth init failed (err %d)\n", err);
363 return;
364 }
365
366 if (IS_ENABLED(CONFIG_SETTINGS)) {
367 settings_load();
368 }
369
370 printk("Bluetooth initialized\n");
371
372 bt_conn_cb_register(&conn_callbacks);
373
374 bt_br_discovery_cb_register(&discovery_cb);
375
376 bt_hfp_ag_register(&ag_cb);
377
378 k_work_init(&discover_work, discover_work_handler);
379
380 (void)k_work_submit(&discover_work);
381
382 k_work_init_delayable(&call_connect_work, call_connect_work_handler);
383 k_work_init_delayable(&call_disconnect_work, call_disconnect_work_handler);
384
385 k_work_init_delayable(&call_remote_ringing_work, call_remote_ringing_work_handler);
386 k_work_init_delayable(&call_remote_accept_work, call_remote_accept_work_handler);
387 }
388
main(void)389 int main(void)
390 {
391 int err;
392
393 printk("Bluetooth Handsfree AG demo start...\n");
394
395 err = bt_enable(bt_ready);
396 if (err) {
397 printk("Bluetooth init failed (err %d)\n", err);
398 }
399 return 0;
400 }
401