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