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