1 /*
2 * Copyright (c) 2022-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11
12 #include <zephyr/autoconf.h>
13 #include <zephyr/bluetooth/audio/audio.h>
14 #include <zephyr/bluetooth/audio/bap.h>
15 #include <zephyr/bluetooth/audio/cap.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/hci_types.h>
18 #include <zephyr/bluetooth/iso.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/net_buf.h>
21 #include <zephyr/sys/__assert.h>
22 #include <zephyr/sys/check.h>
23 #include <zephyr/sys/util.h>
24 #include <zephyr/sys/util_macro.h>
25
26 #include "cap_internal.h"
27
28 LOG_MODULE_REGISTER(bt_cap_stream, CONFIG_BT_CAP_STREAM_LOG_LEVEL);
29
stream_is_central(struct bt_bap_stream * bap_stream)30 static bool stream_is_central(struct bt_bap_stream *bap_stream)
31 {
32 if (IS_ENABLED(CONFIG_BT_CONN)) {
33 struct bt_conn_info info;
34 int err;
35
36 if (bap_stream->conn == NULL) {
37 return false;
38 }
39
40 err = bt_conn_get_info(bap_stream->conn, &info);
41 if (err == 0 && info.role == BT_HCI_ROLE_CENTRAL) {
42 return true;
43 }
44 }
45
46 return false;
47 }
48
49 #if defined(CONFIG_BT_BAP_UNICAST)
cap_stream_configured_cb(struct bt_bap_stream * bap_stream,const struct bt_bap_qos_cfg_pref * pref)50 static void cap_stream_configured_cb(struct bt_bap_stream *bap_stream,
51 const struct bt_bap_qos_cfg_pref *pref)
52 {
53 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
54 struct bt_cap_stream,
55 bap_stream);
56 struct bt_bap_stream_ops *ops = cap_stream->ops;
57
58 LOG_DBG("%p", cap_stream);
59
60 if (ops != NULL && ops->configured != NULL) {
61 ops->configured(bap_stream, pref);
62 }
63
64 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
65 stream_is_central(bap_stream)) {
66 bt_cap_initiator_codec_configured(cap_stream);
67 }
68 }
69
cap_stream_qos_set_cb(struct bt_bap_stream * bap_stream)70 static void cap_stream_qos_set_cb(struct bt_bap_stream *bap_stream)
71 {
72 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
73 struct bt_cap_stream,
74 bap_stream);
75 struct bt_bap_stream_ops *ops = cap_stream->ops;
76
77 LOG_DBG("%p", cap_stream);
78
79 if (ops != NULL && ops->qos_set != NULL) {
80 ops->qos_set(bap_stream);
81 }
82
83 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
84 stream_is_central(bap_stream)) {
85 bt_cap_initiator_qos_configured(cap_stream);
86 }
87 }
88
cap_stream_enabled_cb(struct bt_bap_stream * bap_stream)89 static void cap_stream_enabled_cb(struct bt_bap_stream *bap_stream)
90 {
91 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
92 struct bt_cap_stream,
93 bap_stream);
94 struct bt_bap_stream_ops *ops = cap_stream->ops;
95
96 LOG_DBG("%p", cap_stream);
97
98 if (ops != NULL && ops->enabled != NULL) {
99 ops->enabled(bap_stream);
100 }
101
102 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
103 stream_is_central(bap_stream)) {
104 bt_cap_initiator_enabled(cap_stream);
105 }
106 }
107
cap_stream_metadata_updated_cb(struct bt_bap_stream * bap_stream)108 static void cap_stream_metadata_updated_cb(struct bt_bap_stream *bap_stream)
109 {
110 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
111 struct bt_cap_stream,
112 bap_stream);
113 struct bt_bap_stream_ops *ops = cap_stream->ops;
114
115 LOG_DBG("%p", cap_stream);
116
117 if (ops != NULL && ops->metadata_updated != NULL) {
118 ops->metadata_updated(bap_stream);
119 }
120
121 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
122 stream_is_central(bap_stream)) {
123 bt_cap_initiator_metadata_updated(cap_stream);
124 }
125 }
126
cap_stream_disabled_cb(struct bt_bap_stream * bap_stream)127 static void cap_stream_disabled_cb(struct bt_bap_stream *bap_stream)
128 {
129 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
130 struct bt_cap_stream,
131 bap_stream);
132 struct bt_bap_stream_ops *ops = cap_stream->ops;
133
134 LOG_DBG("%p", cap_stream);
135
136 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
137 stream_is_central(bap_stream)) {
138 bt_cap_initiator_disabled(cap_stream);
139 }
140
141 if (ops != NULL && ops->disabled != NULL) {
142 ops->disabled(bap_stream);
143 }
144 }
145
cap_stream_released_cb(struct bt_bap_stream * bap_stream)146 static void cap_stream_released_cb(struct bt_bap_stream *bap_stream)
147 {
148 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
149 struct bt_cap_stream,
150 bap_stream);
151 struct bt_bap_stream_ops *ops = cap_stream->ops;
152
153 LOG_DBG("%p", cap_stream);
154
155 if (ops != NULL && ops->released != NULL) {
156 ops->released(bap_stream);
157 }
158
159 /* Here we cannot use stream_is_central as bap_stream->conn is NULL, so fall back to
160 * a more generic, less accurate check
161 */
162 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT)) {
163 bt_cap_initiator_released(cap_stream);
164 }
165 }
166
167 #endif /* CONFIG_BT_BAP_UNICAST */
168
cap_stream_started_cb(struct bt_bap_stream * bap_stream)169 static void cap_stream_started_cb(struct bt_bap_stream *bap_stream)
170 {
171 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
172 struct bt_cap_stream,
173 bap_stream);
174 struct bt_bap_stream_ops *ops = cap_stream->ops;
175
176 LOG_DBG("%p", cap_stream);
177
178 if (ops != NULL && ops->started != NULL) {
179 ops->started(bap_stream);
180 }
181
182 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
183 stream_is_central(bap_stream)) {
184 bt_cap_initiator_started(cap_stream);
185 }
186 }
187
cap_stream_stopped_cb(struct bt_bap_stream * bap_stream,uint8_t reason)188 static void cap_stream_stopped_cb(struct bt_bap_stream *bap_stream, uint8_t reason)
189 {
190 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
191 struct bt_cap_stream,
192 bap_stream);
193 struct bt_bap_stream_ops *ops = cap_stream->ops;
194
195 LOG_DBG("%p", cap_stream);
196
197 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
198 stream_is_central(bap_stream)) {
199 bt_cap_initiator_stopped(cap_stream);
200 }
201
202 if (ops != NULL && ops->stopped != NULL) {
203 ops->stopped(bap_stream, reason);
204 }
205 }
206
207 #if defined(CONFIG_BT_AUDIO_RX)
cap_stream_recv_cb(struct bt_bap_stream * bap_stream,const struct bt_iso_recv_info * info,struct net_buf * buf)208 static void cap_stream_recv_cb(struct bt_bap_stream *bap_stream,
209 const struct bt_iso_recv_info *info, struct net_buf *buf)
210 {
211 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
212 struct bt_cap_stream,
213 bap_stream);
214 struct bt_bap_stream_ops *ops = cap_stream->ops;
215
216 if (ops != NULL && ops->recv != NULL) {
217 ops->recv(bap_stream, info, buf);
218 }
219 }
220 #endif /* CONFIG_BT_AUDIO_RX */
221
222 #if defined(CONFIG_BT_AUDIO_TX)
cap_stream_sent_cb(struct bt_bap_stream * bap_stream)223 static void cap_stream_sent_cb(struct bt_bap_stream *bap_stream)
224 {
225 struct bt_cap_stream *cap_stream = CONTAINER_OF(bap_stream,
226 struct bt_cap_stream,
227 bap_stream);
228 struct bt_bap_stream_ops *ops = cap_stream->ops;
229
230 if (ops != NULL && ops->sent != NULL) {
231 ops->sent(bap_stream);
232 }
233 }
234 #endif /* CONFIG_BT_AUDIO_TX */
235
cap_stream_connected_cb(struct bt_bap_stream * bap_stream)236 static void cap_stream_connected_cb(struct bt_bap_stream *bap_stream)
237 {
238 struct bt_cap_stream *cap_stream =
239 CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
240 struct bt_bap_stream_ops *ops = cap_stream->ops;
241
242 if (ops != NULL && ops->connected != NULL) {
243 ops->connected(bap_stream);
244 }
245
246 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
247 stream_is_central(bap_stream)) {
248 bt_cap_initiator_connected(cap_stream);
249 }
250 }
251
cap_stream_disconnected_cb(struct bt_bap_stream * bap_stream,uint8_t reason)252 static void cap_stream_disconnected_cb(struct bt_bap_stream *bap_stream, uint8_t reason)
253 {
254 struct bt_cap_stream *cap_stream =
255 CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
256 struct bt_bap_stream_ops *ops = cap_stream->ops;
257
258 if (ops != NULL && ops->disconnected != NULL) {
259 ops->disconnected(bap_stream, reason);
260 }
261 }
262
263 static struct bt_bap_stream_ops bap_stream_ops = {
264 #if defined(CONFIG_BT_BAP_UNICAST)
265 .configured = cap_stream_configured_cb,
266 .qos_set = cap_stream_qos_set_cb,
267 .enabled = cap_stream_enabled_cb,
268 .metadata_updated = cap_stream_metadata_updated_cb,
269 .disabled = cap_stream_disabled_cb,
270 .released = cap_stream_released_cb,
271 #endif /* CONFIG_BT_BAP_UNICAST */
272 .started = cap_stream_started_cb,
273 .stopped = cap_stream_stopped_cb,
274 #if defined(CONFIG_BT_AUDIO_RX)
275 .recv = cap_stream_recv_cb,
276 #endif /* CONFIG_BT_AUDIO_RX */
277 #if defined(CONFIG_BT_AUDIO_TX)
278 .sent = cap_stream_sent_cb,
279 #endif /* CONFIG_BT_AUDIO_TX */
280 .connected = cap_stream_connected_cb,
281 .disconnected = cap_stream_disconnected_cb,
282 };
283
unicast_client_cp_cb(struct bt_bap_stream * bap_stream,enum bt_bap_ascs_rsp_code rsp_code,enum bt_bap_ascs_reason reason)284 static void unicast_client_cp_cb(struct bt_bap_stream *bap_stream,
285 enum bt_bap_ascs_rsp_code rsp_code, enum bt_bap_ascs_reason reason)
286 {
287 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) &&
288 stream_is_central(bap_stream)) {
289 struct bt_cap_stream *cap_stream =
290 CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream);
291
292 bt_cap_initiator_cp_cb(cap_stream, rsp_code, reason);
293 }
294 }
295
bt_cap_stream_ops_register_bap(struct bt_cap_stream * cap_stream)296 void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream)
297 {
298 bt_bap_stream_cb_register(&cap_stream->bap_stream, &bap_stream_ops);
299
300 if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT)) {
301 /* The CAP initiator can use the same callback for all of these as the result is the
302 * same: Abort current procedure
303 */
304 static struct bt_bap_unicast_client_cb unicast_client_cb = {
305 .config = unicast_client_cp_cb,
306 .qos = unicast_client_cp_cb,
307 .enable = unicast_client_cp_cb,
308 .start = unicast_client_cp_cb,
309 .stop = unicast_client_cp_cb,
310 .disable = unicast_client_cp_cb,
311 .metadata = unicast_client_cp_cb,
312 .release = unicast_client_cp_cb,
313 };
314 int err;
315
316 err = bt_bap_unicast_client_register_cb(&unicast_client_cb);
317 __ASSERT_NO_MSG(err == 0 || err == -EEXIST);
318 }
319 }
320
bt_cap_stream_ops_register(struct bt_cap_stream * stream,struct bt_bap_stream_ops * ops)321 void bt_cap_stream_ops_register(struct bt_cap_stream *stream,
322 struct bt_bap_stream_ops *ops)
323 {
324 stream->ops = ops;
325
326 /* CAP basically just forwards the BAP callbacks after doing what it (CAP) needs to do,
327 * so we can just always register the BAP callbacks here
328 *
329 * It is, however, only the CAP Initiator Unicast that depend on the callbacks being set in
330 * order to work, so for the CAP Initiator Unicast we need an additional register to ensure
331 * correctness.
332 */
333
334 bt_cap_stream_ops_register_bap(stream);
335 }
336
337 #if defined(CONFIG_BT_AUDIO_TX)
bt_cap_stream_send(struct bt_cap_stream * stream,struct net_buf * buf,uint16_t seq_num)338 int bt_cap_stream_send(struct bt_cap_stream *stream, struct net_buf *buf, uint16_t seq_num)
339 {
340 CHECKIF(stream == NULL) {
341 LOG_DBG("stream is NULL");
342
343 return -EINVAL;
344 }
345
346 return bt_bap_stream_send(&stream->bap_stream, buf, seq_num);
347 }
348
bt_cap_stream_send_ts(struct bt_cap_stream * stream,struct net_buf * buf,uint16_t seq_num,uint32_t ts)349 int bt_cap_stream_send_ts(struct bt_cap_stream *stream, struct net_buf *buf, uint16_t seq_num,
350 uint32_t ts)
351 {
352 CHECKIF(stream == NULL) {
353 LOG_DBG("stream is NULL");
354
355 return -EINVAL;
356 }
357
358 return bt_bap_stream_send_ts(&stream->bap_stream, buf, seq_num, ts);
359 }
360
bt_cap_stream_get_tx_sync(struct bt_cap_stream * stream,struct bt_iso_tx_info * info)361 int bt_cap_stream_get_tx_sync(struct bt_cap_stream *stream, struct bt_iso_tx_info *info)
362 {
363 CHECKIF(stream == NULL) {
364 LOG_DBG("stream is NULL");
365
366 return -EINVAL;
367 }
368
369 return bt_bap_stream_get_tx_sync(&stream->bap_stream, info);
370 }
371 #endif /* CONFIG_BT_AUDIO_TX */
372