1 /* Bluetooth CCP - Call Control Profile Call Control Server
2  *
3  * Copyright (c) 2024 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <string.h>
13 
14 #include <zephyr/autoconf.h>
15 #include <zephyr/bluetooth/audio/tbs.h>
16 #include <zephyr/bluetooth/audio/ccp.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/hci_types.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/sys/__assert.h>
21 #include <zephyr/sys/atomic.h>
22 #include <zephyr/sys/check.h>
23 #include <zephyr/sys/slist.h>
24 #include <zephyr/sys/util.h>
25 #include <zephyr/sys/util_macro.h>
26 
27 LOG_MODULE_REGISTER(bt_ccp_call_control_client, CONFIG_BT_CCP_CALL_CONTROL_CLIENT_LOG_LEVEL);
28 
29 static sys_slist_t ccp_call_control_client_cbs =
30 	SYS_SLIST_STATIC_INIT(&ccp_call_control_client_cbs);
31 
32 static struct bt_tbs_client_cb tbs_client_cbs;
33 
34 /* A service instance can either be a GTBS or a TBS insttance */
35 struct bt_ccp_call_control_client_bearer {
36 	uint8_t tbs_index;
37 	bool discovered;
38 };
39 
40 enum ccp_call_control_client_flag {
41 	CCP_CALL_CONTROL_CLIENT_FLAG_BUSY,
42 
43 	CCP_CALL_CONTROL_CLIENT_FLAG_NUM_FLAGS, /* keep as last */
44 };
45 
46 struct bt_ccp_call_control_client {
47 	struct bt_ccp_call_control_client_bearer
48 		bearers[CONFIG_BT_CCP_CALL_CONTROL_CLIENT_BEARER_COUNT];
49 	struct bt_conn *conn;
50 
51 	ATOMIC_DEFINE(flags, CCP_CALL_CONTROL_CLIENT_FLAG_NUM_FLAGS);
52 };
53 
54 static struct bt_ccp_call_control_client clients[CONFIG_BT_MAX_CONN];
55 
get_client_by_conn(const struct bt_conn * conn)56 static struct bt_ccp_call_control_client *get_client_by_conn(const struct bt_conn *conn)
57 {
58 	__ASSERT(bt_conn_is_type(conn, BT_CONN_TYPE_LE), "Invalid connection type for %p", conn);
59 
60 	return &clients[bt_conn_index(conn)];
61 }
62 
connected_cb(struct bt_conn * conn,uint8_t err)63 static void connected_cb(struct bt_conn *conn, uint8_t err)
64 {
65 	static bool cbs_registered;
66 
67 	/* We register the callbacks in the connected callback. That way we ensure that they are
68 	 * registered before any procedures are completed or we receive any notifications, while
69 	 * registering them as late as possible
70 	 */
71 	if (err == BT_HCI_ERR_SUCCESS && !cbs_registered) {
72 		int cb_err;
73 
74 		cb_err = bt_tbs_client_register_cb(&tbs_client_cbs);
75 		__ASSERT(cb_err == 0, "Failed to register TBS callbacks: %d", cb_err);
76 
77 		cbs_registered = true;
78 	}
79 }
80 
disconnected_cb(struct bt_conn * conn,uint8_t reason)81 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
82 {
83 	struct bt_ccp_call_control_client *client = get_client_by_conn(conn);
84 
85 	/* client->conn may be NULL */
86 	if (client->conn == conn) {
87 		bt_conn_unref(client->conn);
88 		client->conn = NULL;
89 	}
90 }
91 
92 BT_CONN_CB_DEFINE(conn_callbacks) = {
93 	.connected = connected_cb,
94 	.disconnected = disconnected_cb,
95 };
96 
populate_bearers(struct bt_ccp_call_control_client * client,struct bt_ccp_call_control_client_bearers * bearers)97 static void populate_bearers(struct bt_ccp_call_control_client *client,
98 			     struct bt_ccp_call_control_client_bearers *bearers)
99 {
100 	size_t i = 0;
101 
102 #if defined(CONFIG_BT_TBS_CLIENT_GTBS)
103 	if (client->bearers[i].discovered) {
104 		bearers->gtbs_bearer = &client->bearers[i++];
105 	}
106 #endif /* CONFIG_BT_TBS_CLIENT_GTBS */
107 
108 #if defined(CONFIG_BT_TBS_CLIENT_TBS)
109 	for (; i < ARRAY_SIZE(client->bearers); i++) {
110 		if (!client->bearers[i].discovered) {
111 			break;
112 		}
113 
114 		bearers->tbs_bearers[bearers->tbs_count++] = &client->bearers[i];
115 	}
116 #endif /* CONFIG_BT_TBS_CLIENT_TBS */
117 }
118 
tbs_client_discover_cb(struct bt_conn * conn,int err,uint8_t tbs_count,bool gtbs_found)119 static void tbs_client_discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count,
120 				   bool gtbs_found)
121 {
122 	struct bt_ccp_call_control_client *client = get_client_by_conn(conn);
123 	struct bt_ccp_call_control_client_bearers bearers = {0};
124 	struct bt_ccp_call_control_client_cb *listener, *next;
125 
126 	LOG_DBG("conn %p err %d tbs_count %u gtbs_found %d", (void *)conn, err, tbs_count,
127 		gtbs_found);
128 
129 	memset(client->bearers, 0, sizeof((client->bearers)));
130 
131 	if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_GTBS) && gtbs_found) {
132 		client->bearers[0].discovered = true;
133 		client->bearers[0].tbs_index = BT_TBS_GTBS_INDEX;
134 	}
135 
136 	if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_TBS)) {
137 		for (uint8_t i = 0U; i < tbs_count; i++) {
138 			const uint8_t idx = i + (gtbs_found ? 1 : 0);
139 
140 			if (idx >= ARRAY_SIZE(client->bearers)) {
141 				LOG_WRN("Discoverd more TBS instances (%u) than the CCP Call "
142 					"Control Client supports %zu",
143 					tbs_count, ARRAY_SIZE(client->bearers));
144 				break;
145 			}
146 
147 			client->bearers[idx].discovered = true;
148 			client->bearers[idx].tbs_index = i;
149 		}
150 	}
151 
152 	populate_bearers(client, &bearers);
153 
154 	atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY);
155 
156 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ccp_call_control_client_cbs, listener, next, _node) {
157 		if (listener->discover != NULL) {
158 			listener->discover(client, err, &bearers);
159 		}
160 	}
161 }
162 
bt_ccp_call_control_client_discover(struct bt_conn * conn,struct bt_ccp_call_control_client ** out_client)163 int bt_ccp_call_control_client_discover(struct bt_conn *conn,
164 					struct bt_ccp_call_control_client **out_client)
165 {
166 	struct bt_ccp_call_control_client *client;
167 	int err;
168 
169 	CHECKIF(conn == NULL) {
170 		LOG_DBG("conn is NULL");
171 
172 		return -EINVAL;
173 	}
174 
175 	CHECKIF(out_client == NULL) {
176 		LOG_DBG("client is NULL");
177 
178 		return -EINVAL;
179 	}
180 
181 	if (!bt_conn_is_type(conn, BT_CONN_TYPE_LE)) {
182 		LOG_DBG("Invalid connection type for %p", conn);
183 		return -EINVAL;
184 	}
185 
186 	client = get_client_by_conn(conn);
187 	if (atomic_test_and_set_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY)) {
188 		return -EBUSY;
189 	}
190 
191 	tbs_client_cbs.discover = tbs_client_discover_cb;
192 
193 	err = bt_tbs_client_discover(conn);
194 	if (err != 0) {
195 		LOG_DBG("Failed to discover TBS for %p: %d", (void *)conn, err);
196 
197 		atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY);
198 
199 		/* Return known errors */
200 		if (err == -EBUSY || err == -ENOTCONN) {
201 			return err;
202 		}
203 
204 		return -ENOEXEC;
205 	}
206 
207 	client->conn = bt_conn_ref(conn);
208 	*out_client = client;
209 
210 	return 0;
211 }
212 
bt_ccp_call_control_client_register_cb(struct bt_ccp_call_control_client_cb * cb)213 int bt_ccp_call_control_client_register_cb(struct bt_ccp_call_control_client_cb *cb)
214 {
215 	CHECKIF(cb == NULL) {
216 		LOG_DBG("cb is NULL");
217 
218 		return -EINVAL;
219 	}
220 
221 	if (sys_slist_find(&ccp_call_control_client_cbs, &cb->_node, NULL)) {
222 		return -EEXIST;
223 	}
224 
225 	sys_slist_append(&ccp_call_control_client_cbs, &cb->_node);
226 
227 	return 0;
228 }
229 
bt_ccp_call_control_client_unregister_cb(struct bt_ccp_call_control_client_cb * cb)230 int bt_ccp_call_control_client_unregister_cb(struct bt_ccp_call_control_client_cb *cb)
231 {
232 	CHECKIF(cb == NULL) {
233 		LOG_DBG("cb is NULL");
234 		return -EINVAL;
235 	}
236 
237 	if (!sys_slist_find_and_remove(&ccp_call_control_client_cbs, &cb->_node)) {
238 		return -EALREADY;
239 	}
240 
241 	return 0;
242 }
243 
bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client * client,struct bt_ccp_call_control_client_bearers * bearers)244 int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *client,
245 					   struct bt_ccp_call_control_client_bearers *bearers)
246 {
247 	CHECKIF(client == NULL) {
248 		LOG_DBG("client is NULL");
249 		return -EINVAL;
250 	}
251 
252 	CHECKIF(bearers == NULL) {
253 		LOG_DBG("bearers is NULL");
254 		return -EINVAL;
255 	}
256 
257 	memset(bearers, 0, sizeof(*bearers));
258 	populate_bearers(client, bearers);
259 
260 	return 0;
261 }
262