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