1 /*
2 * Copyright (c) 2023 Codecoup
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
13 #include <zephyr/bluetooth/audio/audio.h>
14 #include <zephyr/bluetooth/audio/bap.h>
15 #include <zephyr/bluetooth/conn.h>
16 #include <zephyr/bluetooth/hci_types.h>
17 #include <zephyr/sys/printk.h>
18 #include <zephyr/sys/slist.h>
19 #include <sys/errno.h>
20
21 #include "bap_endpoint.h"
22 #include "bap_iso.h"
23 #include "ztest_assert.h"
24
25 static struct bt_bap_unicast_client_cb *unicast_client_cb;
26
bt_bap_ep_is_unicast_client(const struct bt_bap_ep * ep)27 bool bt_bap_ep_is_unicast_client(const struct bt_bap_ep *ep)
28 {
29 return false;
30 }
31
bt_bap_unicast_client_config(struct bt_bap_stream * stream,const struct bt_audio_codec_cfg * codec_cfg)32 int bt_bap_unicast_client_config(struct bt_bap_stream *stream,
33 const struct bt_audio_codec_cfg *codec_cfg)
34 {
35 if (stream == NULL || stream->ep == NULL || codec_cfg == NULL) {
36 return -EINVAL;
37 }
38
39 switch (stream->ep->status.state) {
40 case BT_BAP_EP_STATE_IDLE:
41 case BT_BAP_EP_STATE_CODEC_CONFIGURED:
42 break;
43 default:
44 return -EINVAL;
45 }
46
47 if (unicast_client_cb != NULL && unicast_client_cb->config != NULL) {
48 unicast_client_cb->config(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
49 BT_BAP_ASCS_REASON_NONE);
50 }
51
52 stream->ep->status.state = BT_BAP_EP_STATE_CODEC_CONFIGURED;
53
54 if (stream->ops != NULL && stream->ops->configured != NULL) {
55 const struct bt_bap_qos_cfg_pref pref = {0};
56
57 stream->ops->configured(stream, &pref);
58 }
59
60 return 0;
61 }
62
bt_bap_unicast_client_qos(struct bt_conn * conn,struct bt_bap_unicast_group * group)63 int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group *group)
64 {
65 struct bt_bap_stream *stream;
66
67 if (conn == NULL || group == NULL) {
68 return -EINVAL;
69 }
70
71 SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
72 if (stream->conn == conn) {
73 switch (stream->ep->status.state) {
74 case BT_BAP_EP_STATE_CODEC_CONFIGURED:
75 case BT_BAP_EP_STATE_QOS_CONFIGURED:
76 break;
77 default:
78 return -EINVAL;
79 }
80 }
81 }
82
83 SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
84 if (stream->conn == conn) {
85 if (unicast_client_cb != NULL && unicast_client_cb->qos != NULL) {
86 unicast_client_cb->qos(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
87 BT_BAP_ASCS_REASON_NONE);
88 }
89
90 stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
91
92 if (stream->ops != NULL && stream->ops->qos_set != NULL) {
93 stream->ops->qos_set(stream);
94 }
95 }
96 }
97
98 return 0;
99 }
100
bt_bap_unicast_client_enable(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len)101 int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t meta[],
102 size_t meta_len)
103 {
104 if (stream == NULL) {
105 return -EINVAL;
106 }
107
108 switch (stream->ep->status.state) {
109 case BT_BAP_EP_STATE_QOS_CONFIGURED:
110 break;
111 default:
112 return -EINVAL;
113 }
114
115 if (unicast_client_cb != NULL && unicast_client_cb->enable != NULL) {
116 unicast_client_cb->enable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
117 BT_BAP_ASCS_REASON_NONE);
118 }
119
120 stream->ep->status.state = BT_BAP_EP_STATE_ENABLING;
121
122 if (stream->ops != NULL && stream->ops->enabled != NULL) {
123 stream->ops->enabled(stream);
124 }
125
126 return 0;
127 }
128
bt_bap_unicast_client_metadata(struct bt_bap_stream * stream,const uint8_t meta[],size_t meta_len)129 int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, const uint8_t meta[],
130 size_t meta_len)
131 {
132 if (stream == NULL) {
133 return -EINVAL;
134 }
135
136 switch (stream->ep->status.state) {
137 case BT_BAP_EP_STATE_ENABLING:
138 case BT_BAP_EP_STATE_STREAMING:
139 break;
140 default:
141 return -EINVAL;
142 }
143
144 if (unicast_client_cb != NULL && unicast_client_cb->metadata != NULL) {
145 unicast_client_cb->metadata(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
146 BT_BAP_ASCS_REASON_NONE);
147 }
148
149 if (stream->ops != NULL && stream->ops->metadata_updated != NULL) {
150 stream->ops->metadata_updated(stream);
151 }
152
153 return 0;
154 }
155
bt_bap_unicast_client_connect(struct bt_bap_stream * stream)156 int bt_bap_unicast_client_connect(struct bt_bap_stream *stream)
157 {
158 if (stream == NULL) {
159 return -EINVAL;
160 }
161
162 switch (stream->ep->status.state) {
163 case BT_BAP_EP_STATE_QOS_CONFIGURED:
164 case BT_BAP_EP_STATE_ENABLING:
165 break;
166 default:
167 return -EINVAL;
168 }
169
170 if (stream->ops != NULL && stream->ops->connected != NULL) {
171 stream->ops->connected(stream);
172 }
173
174 if (stream->ep != NULL && stream->ep->dir == BT_AUDIO_DIR_SINK) {
175 /* Mocking that the unicast server automatically starts the stream */
176 stream->ep->status.state = BT_BAP_EP_STATE_STREAMING;
177
178 if (stream->ops != NULL && stream->ops->started != NULL) {
179 stream->ops->started(stream);
180 }
181 }
182
183 return 0;
184 }
185
bt_bap_unicast_client_start(struct bt_bap_stream * stream)186 int bt_bap_unicast_client_start(struct bt_bap_stream *stream)
187 {
188 /* As per the ASCS spec, only source streams can be started by the client */
189 if (stream == NULL || stream->ep == NULL || stream->ep->dir == BT_AUDIO_DIR_SINK) {
190 return -EINVAL;
191 }
192
193 switch (stream->ep->status.state) {
194 case BT_BAP_EP_STATE_ENABLING:
195 break;
196 default:
197 return -EINVAL;
198 }
199
200 if (unicast_client_cb != NULL && unicast_client_cb->start != NULL) {
201 unicast_client_cb->start(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
202 BT_BAP_ASCS_REASON_NONE);
203 }
204
205 stream->ep->status.state = BT_BAP_EP_STATE_STREAMING;
206
207 if (stream->ops != NULL && stream->ops->started != NULL) {
208 stream->ops->started(stream);
209 }
210
211 return 0;
212 }
213
bt_bap_unicast_client_disable(struct bt_bap_stream * stream)214 int bt_bap_unicast_client_disable(struct bt_bap_stream *stream)
215 {
216 printk("%s %p %d\n", __func__, stream, stream->ep->dir);
217
218 if (stream == NULL || stream->ep == NULL) {
219 return -EINVAL;
220 }
221
222 switch (stream->ep->status.state) {
223 case BT_BAP_EP_STATE_ENABLING:
224 case BT_BAP_EP_STATE_STREAMING:
225 break;
226 default:
227 return -EINVAL;
228 }
229
230 /* Even though the ASCS spec does not have the disabling state for sink ASEs, the unicast
231 * client implementation fakes the behavior of it and always calls the disabled callback
232 * when leaving the streaming state in a non-release manner
233 */
234
235 if (unicast_client_cb != NULL && unicast_client_cb->disable != NULL) {
236 unicast_client_cb->disable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
237 BT_BAP_ASCS_REASON_NONE);
238 }
239
240 /* Disabled sink ASEs go directly to the QoS configured state */
241 if (stream->ep->dir == BT_AUDIO_DIR_SINK) {
242 stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
243
244 if (stream->ops != NULL && stream->ops->disabled != NULL) {
245 stream->ops->disabled(stream);
246 }
247
248 if (stream->ops != NULL && stream->ops->stopped != NULL) {
249 stream->ops->stopped(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
250 }
251
252 if (stream->ops != NULL && stream->ops->qos_set != NULL) {
253 stream->ops->qos_set(stream);
254 }
255 } else if (stream->ep->dir == BT_AUDIO_DIR_SOURCE) {
256 stream->ep->status.state = BT_BAP_EP_STATE_DISABLING;
257
258 if (stream->ops != NULL && stream->ops->disabled != NULL) {
259 stream->ops->disabled(stream);
260 }
261 }
262
263 return 0;
264 }
265
bt_bap_unicast_client_stop(struct bt_bap_stream * stream)266 int bt_bap_unicast_client_stop(struct bt_bap_stream *stream)
267 {
268 printk("%s %p\n", __func__, stream);
269
270 /* As per the ASCS spec, only source streams can be stopped by the client */
271 if (stream == NULL || stream->ep == NULL || stream->ep->dir == BT_AUDIO_DIR_SINK) {
272 return -EINVAL;
273 }
274
275 switch (stream->ep->status.state) {
276 case BT_BAP_EP_STATE_DISABLING:
277 break;
278 default:
279 return -EINVAL;
280 }
281
282 if (unicast_client_cb != NULL && unicast_client_cb->stop != NULL) {
283 unicast_client_cb->stop(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
284 BT_BAP_ASCS_REASON_NONE);
285 }
286
287 stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
288
289 if (stream->ops != NULL && stream->ops->stopped != NULL) {
290 stream->ops->stopped(stream, BT_HCI_ERR_LOCALHOST_TERM_CONN);
291 }
292
293 if (stream->ops != NULL && stream->ops->qos_set != NULL) {
294 stream->ops->qos_set(stream);
295 }
296
297 /* If the stream can be disconnected, BAP will disconnect the stream once it reaches the
298 * QoS Configured state. We simulator that behavior here, and if the stream is disconnected,
299 * then the Unicast Server will set any paired stream to the QoS Configured state
300 * autonomously as well.
301 */
302 if (bt_bap_stream_can_disconnect(stream)) {
303 struct bt_bap_ep *pair_ep = bt_bap_iso_get_paired_ep(stream->ep);
304
305 if (pair_ep != NULL && pair_ep->stream != NULL) {
306 struct bt_bap_stream *pair_stream = pair_ep->stream;
307
308 pair_stream->ep->status.state = BT_BAP_EP_STATE_QOS_CONFIGURED;
309
310 if (pair_stream->ops != NULL && pair_stream->ops->stopped != NULL) {
311 pair_stream->ops->stopped(pair_stream,
312 BT_HCI_ERR_LOCALHOST_TERM_CONN);
313 }
314
315 if (pair_stream->ops != NULL && pair_stream->ops->qos_set != NULL) {
316 pair_stream->ops->qos_set(pair_stream);
317 }
318 }
319 }
320
321 return 0;
322 }
323
bt_bap_unicast_client_release(struct bt_bap_stream * stream)324 int bt_bap_unicast_client_release(struct bt_bap_stream *stream)
325 {
326 printk("%s %p\n", __func__, stream);
327
328 if (stream == NULL || stream->ep == NULL) {
329 return -EINVAL;
330 }
331
332 switch (stream->ep->status.state) {
333 case BT_BAP_EP_STATE_CODEC_CONFIGURED:
334 case BT_BAP_EP_STATE_QOS_CONFIGURED:
335 case BT_BAP_EP_STATE_ENABLING:
336 case BT_BAP_EP_STATE_STREAMING:
337 case BT_BAP_EP_STATE_DISABLING:
338 break;
339 default:
340 return -EINVAL;
341 }
342
343 if (unicast_client_cb != NULL && unicast_client_cb->release != NULL) {
344 unicast_client_cb->release(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS,
345 BT_BAP_ASCS_REASON_NONE);
346 }
347
348 stream->ep->status.state = BT_BAP_EP_STATE_IDLE;
349 bt_bap_stream_reset(stream);
350
351 if (stream->ops != NULL && stream->ops->released != NULL) {
352 stream->ops->released(stream);
353 }
354
355 return 0;
356 }
357
bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb * cb)358 int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb)
359 {
360 unicast_client_cb = cb;
361
362 return 0;
363 }
364