Skip to content

Commit 5b23509

Browse files
author
ganghe
committed
[int][new][zbt] Add CCP profiles
Project: Bluetooth redmine: #5146, REDMINE-id ext-redmine: bug|feat#id [Description in detail] Affected branch: [master] Change-Id: I8853c96d16a7c66c042d373f07768280d650b1fb
1 parent ef44d16 commit 5b23509

File tree

7 files changed

+714
-0
lines changed

7 files changed

+714
-0
lines changed

zephyr_bt/audio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ config BT_AUDIO_NOTIFY_RETRY_DELAY
4040
available.
4141

4242
rsource "Kconfig.bap"
43+
rsource "Kconfig.ccp"
4344
rsource "Kconfig.vocs"
4445
rsource "Kconfig.aics"
4546
rsource "Kconfig.vcp"

zephyr_bt/audio/Kconfig.ccp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Bluetooth Audio - Call Control Profile (CCP) configuration options
2+
#
3+
# Copyright (c) 2024 Nordic Semiconductor ASA
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
#
7+
8+
if BT_AUDIO
9+
10+
config BT_CCP_CALL_CONTROL_CLIENT
11+
bool "Call Control Profile Client Support"
12+
depends on BT_EXT_ADV
13+
depends on BT_TBS_CLIENT
14+
depends on BT_BONDABLE
15+
help
16+
This option enables support for the Call Control Profile Client which uses the Telephone
17+
Bearer Service (TBS) client to control calls on a remote device.
18+
19+
if BT_CCP_CALL_CONTROL_CLIENT
20+
21+
config BT_CCP_CALL_CONTROL_CLIENT_BEARER_COUNT
22+
int "Telephone bearer count"
23+
default 1
24+
range 1 256 if BT_TBS_CLIENT_TBS
25+
range 1 1
26+
help
27+
The number of supported telephone bearers on the CCP Call Control Client
28+
29+
module = BT_CCP_CALL_CONTROL_CLIENT
30+
module-str = "Call Control Profile Client"
31+
32+
endif # BT_CCP_CALL_CONTROL_CLIENT
33+
34+
config BT_CCP_CALL_CONTROL_SERVER
35+
bool "Call Control Profile Call Control Server Support"
36+
depends on BT_EXT_ADV
37+
depends on BT_TBS
38+
depends on BT_BONDABLE
39+
help
40+
This option enables support for the Call Control Profile Call Control Server which uses
41+
the Telephone Bearer Service (TBS) to hold and control calls on a device.
42+
43+
if BT_CCP_CALL_CONTROL_SERVER
44+
45+
config BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT
46+
int "Telephone bearer count"
47+
default 1
48+
range 1 256
49+
help
50+
The number of supported telephone bearers on the CCP Call Control Server
51+
52+
module = BT_CCP_CALL_CONTROL_SERVER
53+
module-str = "Call Control Profile Call Control Server"
54+
55+
endif # BT_CCP_CALL_CONTROL_SERVER
56+
57+
endif # BT_AUDIO

zephyr_bt/audio/SConscript

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ if GetDepend(['BT_BAP_SCAN_DELEGATOR']):
9191
if GetDepend(['BT_BAP_BROADCAST_ASSISTANT']):
9292
src+= ['bap_broadcast_assistant.c']
9393

94+
if GetDepend(['BT_CCP_CALL_CONTROL_CLIENT']):
95+
src+= ['ccp_call_control_client.c']
96+
97+
if GetDepend(['BT_CCP_CALL_CONTROL_SERVER']):
98+
src+= ['ccp_call_control_server.c']
99+
94100
if GetDepend(['BT_HAS']):
95101
src+= ['has.c']
96102

@@ -121,6 +127,7 @@ if GetDepend(['BT_GMAP']):
121127
if GetDepend(['BT_PBP']):
122128
src+= ['pbp.c']
123129

130+
124131
CPPPATH = [cwd]
125132

126133
group = DefineGroup('zbt_audio', src, depend = ['BT_AUDIO'], CPPPATH=CPPPATH)
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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+
{
37+
uint8_t tbs_index;
38+
bool discovered;
39+
};
40+
41+
enum ccp_call_control_client_flag
42+
{
43+
CCP_CALL_CONTROL_CLIENT_FLAG_BUSY,
44+
45+
CCP_CALL_CONTROL_CLIENT_FLAG_NUM_FLAGS, /* keep as last */
46+
};
47+
48+
struct bt_ccp_call_control_client
49+
{
50+
struct bt_ccp_call_control_client_bearer
51+
bearers[CONFIG_BT_CCP_CALL_CONTROL_CLIENT_BEARER_COUNT];
52+
struct bt_conn *conn;
53+
54+
ATOMIC_DEFINE(flags, CCP_CALL_CONTROL_CLIENT_FLAG_NUM_FLAGS);
55+
};
56+
57+
static struct bt_ccp_call_control_client clients[CONFIG_BT_MAX_CONN];
58+
59+
static struct bt_ccp_call_control_client *get_client_by_conn(const struct bt_conn *conn)
60+
{
61+
return &clients[bt_conn_index(conn)];
62+
}
63+
64+
static void connected_cb(struct bt_conn *conn, uint8_t err)
65+
{
66+
static bool cbs_registered;
67+
68+
/* We register the callbacks in the connected callback. That way we ensure that they are
69+
* registered before any procedures are completed or we receive any notifications, while
70+
* registering them as late as possible
71+
*/
72+
if (err == BT_HCI_ERR_SUCCESS && !cbs_registered)
73+
{
74+
int cb_err;
75+
76+
cb_err = bt_tbs_client_register_cb(&tbs_client_cbs);
77+
__ASSERT(cb_err == 0, "Failed to register TBS callbacks: %d", cb_err);
78+
79+
cbs_registered = true;
80+
}
81+
}
82+
83+
static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
84+
{
85+
struct bt_ccp_call_control_client *client = get_client_by_conn(conn);
86+
87+
/* client->conn may be NULL */
88+
if (client->conn == conn)
89+
{
90+
bt_conn_unref(client->conn);
91+
client->conn = NULL;
92+
}
93+
}
94+
95+
BT_CONN_CB_DEFINE(conn_callbacks) =
96+
{
97+
.connected = connected_cb,
98+
.disconnected = disconnected_cb,
99+
};
100+
101+
static void populate_bearers(struct bt_ccp_call_control_client *client,
102+
struct bt_ccp_call_control_client_bearers *bearers)
103+
{
104+
size_t i = 0;
105+
106+
#if defined(CONFIG_BT_TBS_CLIENT_GTBS)
107+
if (client->bearers[i].discovered)
108+
{
109+
bearers->gtbs_bearer = &client->bearers[i++];
110+
}
111+
#endif /* CONFIG_BT_TBS_CLIENT_GTBS */
112+
113+
#if defined(CONFIG_BT_TBS_CLIENT_TBS)
114+
for (; i < ARRAY_SIZE(client->bearers); i++)
115+
{
116+
if (!client->bearers[i].discovered)
117+
{
118+
break;
119+
}
120+
121+
bearers->tbs_bearers[bearers->tbs_count++] = &client->bearers[i];
122+
}
123+
#endif /* CONFIG_BT_TBS_CLIENT_TBS */
124+
}
125+
126+
static void tbs_client_discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count,
127+
bool gtbs_found)
128+
{
129+
struct bt_ccp_call_control_client *client = get_client_by_conn(conn);
130+
struct bt_ccp_call_control_client_bearers bearers = {0};
131+
struct bt_ccp_call_control_client_cb *listener, *next;
132+
133+
LOG_DBG("conn %p err %d tbs_count %u gtbs_found %d", (void *)conn, err, tbs_count,
134+
gtbs_found);
135+
136+
memset(client->bearers, 0, sizeof((client->bearers)));
137+
138+
if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_GTBS) && gtbs_found)
139+
{
140+
client->bearers[0].discovered = true;
141+
client->bearers[0].tbs_index = BT_TBS_GTBS_INDEX;
142+
}
143+
144+
if (IS_ENABLED(CONFIG_BT_TBS_CLIENT_TBS))
145+
{
146+
for (uint8_t i = 0U; i < tbs_count; i++)
147+
{
148+
const uint8_t idx = i + (gtbs_found ? 1 : 0);
149+
150+
if (idx >= ARRAY_SIZE(client->bearers))
151+
{
152+
LOG_WRN("Discoverd more TBS instances (%u) than the CCP Call "
153+
"Control Client supports %zu",
154+
tbs_count, ARRAY_SIZE(client->bearers));
155+
break;
156+
}
157+
158+
client->bearers[idx].discovered = true;
159+
client->bearers[idx].tbs_index = i;
160+
}
161+
}
162+
163+
populate_bearers(client, &bearers);
164+
165+
atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY);
166+
167+
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ccp_call_control_client_cbs, listener, next, _node)
168+
{
169+
if (listener->discover != NULL)
170+
{
171+
listener->discover(client, err, &bearers);
172+
}
173+
}
174+
}
175+
176+
int bt_ccp_call_control_client_discover(struct bt_conn *conn,
177+
struct bt_ccp_call_control_client **out_client)
178+
{
179+
struct bt_ccp_call_control_client *client;
180+
int err;
181+
182+
CHECKIF(conn == NULL)
183+
{
184+
LOG_DBG("conn is NULL");
185+
186+
return -EINVAL;
187+
}
188+
189+
CHECKIF(out_client == NULL)
190+
{
191+
LOG_DBG("client is NULL");
192+
193+
return -EINVAL;
194+
}
195+
196+
client = get_client_by_conn(conn);
197+
if (atomic_test_and_set_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY))
198+
{
199+
return -EBUSY;
200+
}
201+
202+
tbs_client_cbs.discover = tbs_client_discover_cb;
203+
204+
err = bt_tbs_client_discover(conn);
205+
if (err != 0)
206+
{
207+
LOG_DBG("Failed to discover TBS for %p: %d", (void *)conn, err);
208+
209+
atomic_clear_bit(client->flags, CCP_CALL_CONTROL_CLIENT_FLAG_BUSY);
210+
211+
/* Return known errors */
212+
if (err == -EBUSY || err == -ENOTCONN)
213+
{
214+
return err;
215+
}
216+
217+
return -ENOEXEC;
218+
}
219+
220+
client->conn = bt_conn_ref(conn);
221+
*out_client = client;
222+
223+
return 0;
224+
}
225+
226+
int bt_ccp_call_control_client_register_cb(struct bt_ccp_call_control_client_cb *cb)
227+
{
228+
CHECKIF(cb == NULL)
229+
{
230+
LOG_DBG("cb is NULL");
231+
232+
return -EINVAL;
233+
}
234+
235+
if (sys_slist_find(&ccp_call_control_client_cbs, &cb->_node, NULL))
236+
{
237+
return -EEXIST;
238+
}
239+
240+
sys_slist_append(&ccp_call_control_client_cbs, &cb->_node);
241+
242+
return 0;
243+
}
244+
245+
int bt_ccp_call_control_client_unregister_cb(struct bt_ccp_call_control_client_cb *cb)
246+
{
247+
CHECKIF(cb == NULL)
248+
{
249+
LOG_DBG("cb is NULL");
250+
return -EINVAL;
251+
}
252+
253+
if (!sys_slist_find_and_remove(&ccp_call_control_client_cbs, &cb->_node))
254+
{
255+
return -EALREADY;
256+
}
257+
258+
return 0;
259+
}
260+
261+
int bt_ccp_call_control_client_get_bearers(struct bt_ccp_call_control_client *client,
262+
struct bt_ccp_call_control_client_bearers *bearers)
263+
{
264+
CHECKIF(client == NULL)
265+
{
266+
LOG_DBG("client is NULL");
267+
return -EINVAL;
268+
}
269+
270+
CHECKIF(bearers == NULL)
271+
{
272+
LOG_DBG("bearers is NULL");
273+
return -EINVAL;
274+
}
275+
276+
memset(bearers, 0, sizeof(*bearers));
277+
populate_bearers(client, bearers);
278+
279+
return 0;
280+
}

0 commit comments

Comments
 (0)