1 /* sco.c - Bluetooth sco handling */
2
3 /*
4 * Copyright (c) 2015-2016 Intel Corporation
5 * Copyright 2024 NXP
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12
13 #include <zephyr/sys/atomic.h>
14 #include <zephyr/sys/check.h>
15
16 #include <zephyr/bluetooth/hci.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/conn.h>
19
20 #include "common/bt_str.h"
21
22 #include "host/addr_internal.h"
23 #include "host/hci_core.h"
24 #include "br.h"
25 #include "host/conn_internal.h"
26 #include "sco_internal.h"
27
28 #define LOG_LEVEL CONFIG_BT_CONN_LOG_LEVEL
29 #include <zephyr/logging/log.h>
30 LOG_MODULE_REGISTER(bt_sco);
31
32 struct bt_sco_server *sco_server;
33
34 #define SCO_CHAN(_sco) ((_sco)->sco.chan);
35
36 static sys_slist_t sco_conn_cbs = SYS_SLIST_STATIC_INIT(&sco_conn_cbs);
37
bt_sco_server_register(struct bt_sco_server * server)38 int bt_sco_server_register(struct bt_sco_server *server)
39 {
40 CHECKIF(!server) {
41 LOG_DBG("Invalid parameter: server %p", server);
42 return -EINVAL;
43 }
44
45 if (sco_server) {
46 return -EADDRINUSE;
47 }
48
49 if (!server->accept) {
50 return -EINVAL;
51 }
52
53 if (server->sec_level > BT_SECURITY_L3) {
54 return -EINVAL;
55 }
56
57 LOG_DBG("%p", server);
58
59 sco_server = server;
60
61 return 0;
62 }
63
bt_sco_server_unregister(struct bt_sco_server * server)64 int bt_sco_server_unregister(struct bt_sco_server *server)
65 {
66 CHECKIF(!server) {
67 LOG_DBG("Invalid parameter: server %p", server);
68 return -EINVAL;
69 }
70
71 if (sco_server != server) {
72 return -EINVAL;
73 }
74
75 sco_server = NULL;
76
77 return 0;
78 }
79
notify_connected(struct bt_conn * conn)80 static void notify_connected(struct bt_conn *conn)
81 {
82 struct bt_sco_conn_cb *callback;
83
84 SYS_SLIST_FOR_EACH_CONTAINER(&sco_conn_cbs, callback, _node) {
85 if (callback->connected) {
86 callback->connected(conn, conn->err);
87 }
88 }
89
90 STRUCT_SECTION_FOREACH(bt_sco_conn_cb, cb) {
91 if (cb->connected) {
92 cb->connected(conn, conn->err);
93 }
94 }
95 }
96
notify_disconnected(struct bt_conn * conn)97 static void notify_disconnected(struct bt_conn *conn)
98 {
99 struct bt_sco_conn_cb *callback;
100
101 SYS_SLIST_FOR_EACH_CONTAINER(&sco_conn_cbs, callback, _node) {
102 if (callback->disconnected) {
103 callback->disconnected(conn, conn->err);
104 }
105 }
106
107 STRUCT_SECTION_FOREACH(bt_sco_conn_cb, cb) {
108 if (cb->disconnected) {
109 cb->disconnected(conn, conn->err);
110 }
111 }
112 }
113
bt_sco_connected(struct bt_conn * sco)114 void bt_sco_connected(struct bt_conn *sco)
115 {
116 struct bt_sco_chan *chan;
117
118 if (sco == NULL || sco->type != BT_CONN_TYPE_SCO) {
119 LOG_ERR("Invalid parameters: sco %p sco->type %u", sco, sco ? sco->type : 0);
120 return;
121 }
122
123 LOG_DBG("%p", sco);
124
125 notify_connected(sco);
126
127 chan = SCO_CHAN(sco);
128 if (chan == NULL) {
129 LOG_ERR("Could not lookup chan from connected SCO");
130 return;
131 }
132
133 bt_sco_chan_set_state(chan, BT_SCO_STATE_CONNECTED);
134
135 if (chan->ops && chan->ops->connected) {
136 chan->ops->connected(chan);
137 }
138 }
139
bt_sco_disconnected(struct bt_conn * sco)140 void bt_sco_disconnected(struct bt_conn *sco)
141 {
142 struct bt_sco_chan *chan;
143
144 if (sco == NULL || sco->type != BT_CONN_TYPE_SCO) {
145 LOG_ERR("Invalid parameters: sco %p sco->type %u", sco, sco ? sco->type : 0);
146 return;
147 }
148 LOG_DBG("%p", sco);
149
150 notify_disconnected(sco);
151
152 bt_sco_cleanup_acl(sco);
153
154 chan = SCO_CHAN(sco);
155 if (chan == NULL) {
156 LOG_ERR("Could not lookup chan from connected SCO");
157 return;
158 }
159
160 bt_sco_chan_set_state(chan, BT_SCO_STATE_DISCONNECTED);
161
162 if (chan->ops && chan->ops->disconnected) {
163 chan->ops->disconnected(chan, sco->err);
164 }
165
166 chan->sco = NULL;
167 }
168
sco_server_check_security(struct bt_conn * conn)169 static uint8_t sco_server_check_security(struct bt_conn *conn)
170 {
171 if (IS_ENABLED(CONFIG_BT_CONN_DISABLE_SECURITY)) {
172 return BT_HCI_ERR_SUCCESS;
173 }
174
175 if (conn->sec_level >= sco_server->sec_level) {
176 return BT_HCI_ERR_SUCCESS;
177 }
178
179 return BT_HCI_ERR_INSUFFICIENT_SECURITY;
180 }
181
182 #if defined(CONFIG_BT_CONN_LOG_LEVEL_DBG)
bt_sco_chan_state_str(uint8_t state)183 const char *bt_sco_chan_state_str(uint8_t state)
184 {
185 switch (state) {
186 case BT_SCO_STATE_DISCONNECTED:
187 return "disconnected";
188 case BT_SCO_STATE_CONNECTING:
189 return "connecting";
190 case BT_SCO_STATE_ENCRYPT_PENDING:
191 return "encryption pending";
192 case BT_SCO_STATE_CONNECTED:
193 return "connected";
194 case BT_SCO_STATE_DISCONNECTING:
195 return "disconnecting";
196 default:
197 return "unknown";
198 }
199 }
200
bt_sco_chan_set_state_debug(struct bt_sco_chan * chan,enum bt_sco_state state,const char * func,int line)201 void bt_sco_chan_set_state_debug(struct bt_sco_chan *chan,
202 enum bt_sco_state state,
203 const char *func, int line)
204 {
205 LOG_DBG("chan %p sco %p %s -> %s", chan, chan->sco, bt_sco_chan_state_str(chan->state),
206 bt_sco_chan_state_str(state));
207
208 /* check transitions validness */
209 switch (state) {
210 case BT_SCO_STATE_DISCONNECTED:
211 /* regardless of old state always allows this states */
212 break;
213 case BT_SCO_STATE_ENCRYPT_PENDING:
214 __fallthrough;
215 case BT_SCO_STATE_CONNECTING:
216 if (chan->state != BT_SCO_STATE_DISCONNECTED) {
217 LOG_WRN("%s()%d: invalid transition", func, line);
218 }
219 break;
220 case BT_SCO_STATE_CONNECTED:
221 if (chan->state != BT_SCO_STATE_CONNECTING) {
222 LOG_WRN("%s()%d: invalid transition", func, line);
223 }
224 break;
225 case BT_SCO_STATE_DISCONNECTING:
226 if (chan->state != BT_SCO_STATE_CONNECTING &&
227 chan->state != BT_SCO_STATE_CONNECTED) {
228 LOG_WRN("%s()%d: invalid transition", func, line);
229 }
230 break;
231 default:
232 LOG_ERR("%s()%d: unknown (%u) state was set", func, line, state);
233 return;
234 }
235
236 chan->state = state;
237 }
238 #else
bt_sco_chan_set_state(struct bt_sco_chan * chan,enum bt_sco_state state)239 void bt_sco_chan_set_state(struct bt_sco_chan *chan, enum bt_sco_state state)
240 {
241 chan->state = state;
242 }
243 #endif /* CONFIG_BT_CONN_LOG_LEVEL_DBG */
244
245
bt_sco_chan_add(struct bt_conn * sco,struct bt_sco_chan * chan)246 static void bt_sco_chan_add(struct bt_conn *sco, struct bt_sco_chan *chan)
247 {
248 /* Attach SCO channel to the connection */
249 chan->sco = sco;
250 sco->sco.chan = chan;
251
252 LOG_DBG("sco %p chan %p", sco, chan);
253 }
254
sco_accept(struct bt_conn * acl,struct bt_conn * sco)255 static int sco_accept(struct bt_conn *acl, struct bt_conn *sco)
256 {
257 struct bt_sco_accept_info accept_info;
258 struct bt_sco_chan *chan;
259 int err;
260
261 CHECKIF(!sco || sco->type != BT_CONN_TYPE_SCO) {
262 LOG_ERR("Invalid parameters: sco %p sco->type %u", sco, sco ? sco->type : 0);
263 return -EINVAL;
264 }
265
266 LOG_DBG("%p", sco);
267
268 accept_info.acl = acl;
269 memcpy(accept_info.dev_class, sco->sco.dev_class, sizeof(accept_info.dev_class));
270 accept_info.link_type = sco->sco.link_type;
271
272 err = sco_server->accept(&accept_info, &chan);
273 if (err < 0) {
274 LOG_ERR("Server failed to accept: %d", err);
275 return err;
276 }
277
278 if (chan->ops == NULL) {
279 LOG_ERR("invalid parameter: chan %p chan->ops %p", chan, chan->ops);
280 return -EINVAL;
281 }
282
283 bt_sco_chan_add(sco, chan);
284 bt_sco_chan_set_state(chan, BT_SCO_STATE_CONNECTING);
285
286 return 0;
287 }
288
accept_sco_conn(const bt_addr_t * bdaddr,struct bt_conn * sco_conn)289 static int accept_sco_conn(const bt_addr_t *bdaddr, struct bt_conn *sco_conn)
290 {
291 struct bt_hci_cp_accept_sync_conn_req *cp;
292 struct net_buf *buf;
293 int err;
294
295 err = sco_accept(sco_conn->sco.acl, sco_conn);
296 if (err) {
297 return err;
298 }
299
300 buf = bt_hci_cmd_alloc(K_FOREVER);
301 if (!buf) {
302 return -ENOBUFS;
303 }
304
305 cp = net_buf_add(buf, sizeof(*cp));
306 bt_addr_copy(&cp->bdaddr, bdaddr);
307 cp->pkt_type = sco_conn->sco.pkt_type;
308 cp->tx_bandwidth = 0x00001f40;
309 cp->rx_bandwidth = 0x00001f40;
310 cp->max_latency = 0x0007;
311 cp->retrans_effort = 0x01;
312 cp->content_format = BT_VOICE_CVSD_16BIT;
313
314 err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, buf, NULL);
315 if (err) {
316 return err;
317 }
318
319 return 0;
320 }
321
bt_esco_conn_req(struct bt_hci_evt_conn_request * evt)322 uint8_t bt_esco_conn_req(struct bt_hci_evt_conn_request *evt)
323 {
324 struct bt_conn *sco_conn;
325 uint8_t sec_err;
326
327 if (sco_server == NULL) {
328 LOG_ERR("No SCO server registered");
329 return BT_HCI_ERR_UNSPECIFIED;
330 }
331
332 sco_conn = bt_conn_add_sco(&evt->bdaddr, evt->link_type);
333 if (!sco_conn) {
334 return BT_HCI_ERR_INSUFFICIENT_RESOURCES;
335 }
336
337 sec_err = sco_server_check_security(sco_conn->sco.acl);
338 if (BT_HCI_ERR_SUCCESS != sec_err) {
339 LOG_DBG("Insufficient security %u", sec_err);
340 bt_sco_cleanup(sco_conn);
341 return sec_err;
342 }
343
344 memcpy(sco_conn->sco.dev_class, evt->dev_class, sizeof(sco_conn->sco.dev_class));
345 sco_conn->sco.link_type = evt->link_type;
346
347 if (accept_sco_conn(&evt->bdaddr, sco_conn)) {
348 LOG_ERR("Error accepting connection from %s", bt_addr_str(&evt->bdaddr));
349 bt_sco_cleanup(sco_conn);
350 return BT_HCI_ERR_UNSPECIFIED;
351 }
352
353 sco_conn->role = BT_HCI_ROLE_PERIPHERAL;
354 bt_conn_set_state(sco_conn, BT_CONN_INITIATING);
355 bt_conn_unref(sco_conn);
356
357 return BT_HCI_ERR_SUCCESS;
358 }
359
bt_sco_cleanup_acl(struct bt_conn * sco)360 void bt_sco_cleanup_acl(struct bt_conn *sco)
361 {
362 LOG_DBG("%p", sco);
363
364 if (sco->sco.acl) {
365 bt_conn_unref(sco->sco.acl);
366 sco->sco.acl = NULL;
367 }
368 }
369
sco_setup_sync_conn(struct bt_conn * sco_conn)370 static int sco_setup_sync_conn(struct bt_conn *sco_conn)
371 {
372 struct net_buf *buf;
373 struct bt_hci_cp_setup_sync_conn *cp;
374 int err;
375
376 buf = bt_hci_cmd_alloc(K_FOREVER);
377 if (!buf) {
378 return -ENOBUFS;
379 }
380
381 cp = net_buf_add(buf, sizeof(*cp));
382
383 (void)memset(cp, 0, sizeof(*cp));
384
385 LOG_DBG("handle : %x", sco_conn->sco.acl->handle);
386
387 cp->handle = sco_conn->sco.acl->handle;
388 cp->pkt_type = sco_conn->sco.pkt_type;
389 cp->tx_bandwidth = 0x00001f40;
390 cp->rx_bandwidth = 0x00001f40;
391 cp->max_latency = 0x0007;
392 cp->retrans_effort = 0x01;
393 cp->content_format = BT_VOICE_CVSD_16BIT;
394
395 err = bt_hci_cmd_send_sync(BT_HCI_OP_SETUP_SYNC_CONN, buf, NULL);
396 if (err < 0) {
397 return err;
398 }
399 return 0;
400 }
401
bt_conn_create_sco(const bt_addr_t * peer,struct bt_sco_chan * chan)402 struct bt_conn *bt_conn_create_sco(const bt_addr_t *peer, struct bt_sco_chan *chan)
403 {
404 struct bt_conn *sco_conn;
405 int link_type;
406
407 sco_conn = bt_conn_lookup_addr_sco(peer);
408 if (sco_conn) {
409 switch (sco_conn->state) {
410 case BT_CONN_INITIATING:
411 case BT_CONN_CONNECTED:
412 return sco_conn;
413 default:
414 bt_conn_unref(sco_conn);
415 return NULL;
416 }
417 }
418
419 if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) {
420 link_type = BT_HCI_ESCO;
421 } else {
422 link_type = BT_HCI_SCO;
423 }
424
425 sco_conn = bt_conn_add_sco(peer, link_type);
426 if (!sco_conn) {
427 return NULL;
428 }
429
430 sco_conn->sco.link_type = link_type;
431
432 bt_sco_chan_add(sco_conn, chan);
433 bt_conn_set_state(chan->sco, BT_CONN_INITIATING);
434 bt_sco_chan_set_state(chan, BT_SCO_STATE_CONNECTING);
435
436 if (sco_setup_sync_conn(sco_conn) < 0) {
437 bt_conn_set_state(chan->sco, BT_CONN_DISCONNECTED);
438 bt_sco_chan_set_state(chan, BT_SCO_STATE_DISCONNECTED);
439 bt_sco_cleanup(sco_conn);
440 return NULL;
441 }
442
443 return sco_conn;
444 }
445
bt_sco_conn_cb_register(struct bt_sco_conn_cb * cb)446 int bt_sco_conn_cb_register(struct bt_sco_conn_cb *cb)
447 {
448 CHECKIF(cb == NULL) {
449 return -EINVAL;
450 }
451
452 if (sys_slist_find(&sco_conn_cbs, &cb->_node, NULL)) {
453 return -EEXIST;
454 }
455
456 sys_slist_append(&sco_conn_cbs, &cb->_node);
457
458 return 0;
459 }
460
bt_sco_conn_cb_unregister(struct bt_sco_conn_cb * cb)461 int bt_sco_conn_cb_unregister(struct bt_sco_conn_cb *cb)
462 {
463 CHECKIF(cb == NULL) {
464 return -EINVAL;
465 }
466
467 if (!sys_slist_find_and_remove(&sco_conn_cbs, &cb->_node)) {
468 return -ENOENT;
469 }
470
471 return 0;
472 }
473