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