1 /** @file
2  * @brief Advance Audio Distribution Profile.
3  */
4 
5 /*
6  * Copyright (c) 2015-2016 Intel Corporation
7  * Copyright 2021,2024 NXP
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <zephyr/kernel.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <zephyr/sys/atomic.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/sys/util.h>
18 #include <zephyr/sys/printk.h>
19 
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/l2cap.h>
22 #include <zephyr/bluetooth/classic/avdtp.h>
23 #include <zephyr/bluetooth/classic/a2dp_codec_sbc.h>
24 #include <zephyr/bluetooth/classic/a2dp.h>
25 
26 #include "common/assert.h"
27 
28 #define A2DP_SBC_PAYLOAD_TYPE (0x60U)
29 
30 #define A2DP_AVDTP(_avdtp) CONTAINER_OF(_avdtp, struct bt_a2dp, session)
31 #define DISCOVER_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_discover_params, req)
32 #define DISCOVER_PARAM(_discover_param)                                                            \
33 	CONTAINER_OF(_discover_param, struct bt_a2dp, discover_param)
34 #define GET_CAP_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_get_capabilities_params, req)
35 #define GET_CAP_PARAM(_get_cap_param)                                                              \
36 	CONTAINER_OF(_get_cap_param, struct bt_a2dp, get_capabilities_param)
37 #define SET_CONF_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_set_configuration_params, req)
38 #define SET_CONF_PARAM(_set_conf_param)                                                            \
39 	CONTAINER_OF(_set_conf_param, struct bt_a2dp, set_config_param)
40 #define CTRL_REQ(_req)          CONTAINER_OF(_req, struct bt_avdtp_ctrl_params, req)
41 #define CTRL_PARAM(_ctrl_param) CONTAINER_OF(_ctrl_param, struct bt_a2dp, ctrl_param)
42 
43 #include "host/hci_core.h"
44 #include "host/conn_internal.h"
45 #include "host/l2cap_internal.h"
46 #include "avdtp_internal.h"
47 #include "a2dp_internal.h"
48 
49 #define LOG_LEVEL CONFIG_BT_A2DP_LOG_LEVEL
50 #include <zephyr/logging/log.h>
51 LOG_MODULE_REGISTER(bt_a2dp);
52 
53 #define A2DP_NO_SPACE (-1)
54 
55 enum bt_a2dp_internal_state {
56 	INTERNAL_STATE_IDLE = 0,
57 	INTERNAL_STATE_AVDTP_CONNECTED,
58 };
59 
60 struct bt_a2dp {
61 	struct bt_avdtp session;
62 	struct bt_avdtp_discover_params discover_param;
63 	struct bt_a2dp_discover_param *discover_cb_param;
64 	struct bt_avdtp_get_capabilities_params get_capabilities_param;
65 	struct bt_avdtp_set_configuration_params set_config_param;
66 	struct bt_avdtp_ctrl_params ctrl_param;
67 	uint8_t get_cap_index;
68 	enum bt_a2dp_internal_state a2dp_state;
69 	uint8_t peer_seps_count;
70 };
71 
72 static struct bt_a2dp_cb *a2dp_cb;
73 /* Connections */
74 static struct bt_a2dp connection[CONFIG_BT_MAX_CONN];
75 
76 static int bt_a2dp_get_sep_caps(struct bt_a2dp *a2dp);
77 
a2dp_get_connection(struct bt_conn * conn)78 static struct bt_a2dp *a2dp_get_connection(struct bt_conn *conn)
79 {
80 	struct bt_a2dp *a2dp = &connection[bt_conn_index(conn)];
81 
82 	if (a2dp->session.br_chan.chan.conn == NULL) {
83 		/* Clean the memory area before returning */
84 		(void)memset(a2dp, 0, sizeof(struct bt_a2dp));
85 	}
86 	return a2dp;
87 }
88 
89 /* The AVDTP L2CAP signal channel established */
a2dp_connected(struct bt_avdtp * session)90 static void a2dp_connected(struct bt_avdtp *session)
91 {
92 	struct bt_a2dp *a2dp = A2DP_AVDTP(session);
93 
94 	a2dp->a2dp_state = INTERNAL_STATE_AVDTP_CONNECTED;
95 	/* notify a2dp app the connection. */
96 	if ((a2dp_cb != NULL) && (a2dp_cb->connected != NULL)) {
97 		a2dp_cb->connected(a2dp, 0);
98 	}
99 }
100 
101 /* The AVDTP L2CAP signal channel released */
a2dp_disconnected(struct bt_avdtp * session)102 static void a2dp_disconnected(struct bt_avdtp *session)
103 {
104 	struct bt_a2dp *a2dp = A2DP_AVDTP(session);
105 
106 	/* notify a2dp app the disconnection. */
107 	if ((a2dp_cb != NULL) && (a2dp_cb->disconnected != NULL)) {
108 		a2dp_cb->disconnected(a2dp);
109 	}
110 }
111 
112 /* avdtp alloc buf from upper layer */
a2dp_alloc_buf(struct bt_avdtp * session)113 static struct net_buf *a2dp_alloc_buf(struct bt_avdtp *session)
114 {
115 	return NULL;
116 }
117 
a2dp_discovery_ind(struct bt_avdtp * session,uint8_t * errcode)118 static int a2dp_discovery_ind(struct bt_avdtp *session, uint8_t *errcode)
119 {
120 	*errcode = 0;
121 	return 0;
122 }
123 
a2dp_get_capabilities_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,struct net_buf * rsp_buf,uint8_t * errcode)124 static int a2dp_get_capabilities_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep,
125 				     struct net_buf *rsp_buf, uint8_t *errcode)
126 {
127 	struct bt_a2dp_ep *ep;
128 
129 	__ASSERT(sep, "Invalid sep");
130 	*errcode = 0;
131 	/* Service Category: Media Transport */
132 	net_buf_add_u8(rsp_buf, BT_AVDTP_SERVICE_MEDIA_TRANSPORT);
133 	net_buf_add_u8(rsp_buf, 0);
134 	/* Service Category: Media Codec */
135 	net_buf_add_u8(rsp_buf, BT_AVDTP_SERVICE_MEDIA_CODEC);
136 	ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
137 	/* Length Of Service Capability */
138 	net_buf_add_u8(rsp_buf, ep->codec_cap->len + 2U);
139 	/* Media Type */
140 	net_buf_add_u8(rsp_buf, sep->sep_info.media_type << 4U);
141 	/* Media Codec Type */
142 	net_buf_add_u8(rsp_buf, ep->codec_type);
143 	/* Codec Info Element */
144 	net_buf_add_mem(rsp_buf, &ep->codec_cap->codec_ie[0], ep->codec_cap->len);
145 	return 0;
146 }
147 
a2dp_process_config_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t int_seid,struct net_buf * buf,uint8_t * errcode,bool reconfig)148 static int a2dp_process_config_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep,
149 				   uint8_t int_seid, struct net_buf *buf, uint8_t *errcode,
150 				   bool reconfig)
151 {
152 	struct bt_a2dp *a2dp = A2DP_AVDTP(session);
153 	struct bt_a2dp_ep *ep;
154 	struct bt_a2dp_stream *stream = NULL;
155 	struct bt_a2dp_stream_ops *ops;
156 	uint8_t codec_type;
157 	uint8_t *codec_info_element;
158 	uint16_t codec_info_element_len;
159 	struct bt_a2dp_codec_cfg cfg;
160 	struct bt_a2dp_codec_ie codec_config;
161 	uint8_t rsp_err_code;
162 	int err;
163 
164 	*errcode = 0;
165 
166 	__ASSERT(sep, "Invalid sep");
167 
168 	ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
169 
170 	/* parse the configuration */
171 	codec_info_element_len = 4U;
172 	err = bt_avdtp_parse_capability_codec(buf, &codec_type, &codec_info_element,
173 					      &codec_info_element_len);
174 	if (err) {
175 		*errcode = BT_AVDTP_BAD_ACP_SEID;
176 		return -EINVAL;
177 	}
178 
179 	if (codec_type == BT_A2DP_SBC) {
180 		struct bt_a2dp_codec_sbc_params *sbc_set;
181 		struct bt_a2dp_codec_sbc_params *sbc;
182 
183 		if (codec_info_element_len != 4U) {
184 			*errcode = BT_AVDTP_BAD_ACP_SEID;
185 			return -EINVAL;
186 		}
187 
188 		sbc_set = (struct bt_a2dp_codec_sbc_params *)codec_info_element;
189 		sbc = (struct bt_a2dp_codec_sbc_params *)&ep->codec_cap->codec_ie[0];
190 		if (((BT_A2DP_SBC_SAMP_FREQ(sbc_set) & BT_A2DP_SBC_SAMP_FREQ(sbc)) == 0) ||
191 		    ((BT_A2DP_SBC_CHAN_MODE(sbc_set) & BT_A2DP_SBC_CHAN_MODE(sbc)) == 0) ||
192 		    ((BT_A2DP_SBC_BLK_LEN(sbc_set) & BT_A2DP_SBC_BLK_LEN(sbc)) == 0) ||
193 		    ((BT_A2DP_SBC_SUB_BAND(sbc_set) & BT_A2DP_SBC_SUB_BAND(sbc)) == 0) ||
194 		    ((BT_A2DP_SBC_ALLOC_MTHD(sbc_set) & BT_A2DP_SBC_ALLOC_MTHD(sbc)) == 0)) {
195 			*errcode = BT_AVDTP_BAD_ACP_SEID;
196 			return -EINVAL;
197 		}
198 	}
199 
200 	/* For reconfig, ep->stream must already be valid, callback can be NULL as default accept.
201 	 * For !reconfig, config_req must be set to get stream from upper layer
202 	 */
203 	if (reconfig) {
204 		stream = ep->stream;
205 		if (stream == NULL) {
206 			*errcode = BT_AVDTP_BAD_ACP_SEID;
207 			return -EINVAL;
208 		}
209 
210 		if (a2dp_cb == NULL || a2dp_cb->reconfig_req == NULL) {
211 			goto process_done;
212 		}
213 	} else if (a2dp_cb == NULL || a2dp_cb->config_req == NULL) {
214 		*errcode = BT_AVDTP_BAD_ACP_SEID;
215 		return -EINVAL;
216 	}
217 
218 	cfg.codec_config = &codec_config;
219 	cfg.codec_config->len = codec_info_element_len;
220 	memcpy(&cfg.codec_config->codec_ie[0], codec_info_element,
221 	       (codec_info_element_len > BT_A2DP_MAX_IE_LENGTH ? BT_A2DP_MAX_IE_LENGTH
222 							       : codec_info_element_len));
223 	if (!reconfig) {
224 		err = a2dp_cb->config_req(a2dp, ep, &cfg, &stream, &rsp_err_code);
225 		if (!err && stream) {
226 			stream->a2dp = a2dp;
227 			stream->local_ep = ep;
228 			stream->remote_ep_id = int_seid;
229 			stream->remote_ep = NULL;
230 			stream->codec_config = *cfg.codec_config;
231 			ep->stream = stream;
232 		} else {
233 			*errcode = rsp_err_code != 0 ? rsp_err_code : BT_AVDTP_BAD_ACP_SEID;
234 		}
235 	} else {
236 		err = a2dp_cb->reconfig_req(stream, &cfg, &rsp_err_code);
237 		if (err) {
238 			*errcode = rsp_err_code;
239 		}
240 	}
241 
242 process_done:
243 	if (*errcode == 0) {
244 		ops = stream->ops;
245 		if ((ops != NULL) && (ops->configured != NULL)) {
246 			ops->configured(stream);
247 		}
248 	}
249 
250 	return (*errcode == 0) ? 0 : -EINVAL;
251 }
252 
a2dp_set_config_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t int_seid,struct net_buf * buf,uint8_t * errcode)253 static int a2dp_set_config_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t int_seid,
254 			       struct net_buf *buf, uint8_t *errcode)
255 {
256 	__ASSERT(sep, "Invalid sep");
257 	return a2dp_process_config_ind(session, sep, int_seid, buf, errcode, false);
258 }
259 
a2dp_re_config_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t int_seid,struct net_buf * buf,uint8_t * errcode)260 static int a2dp_re_config_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t int_seid,
261 			      struct net_buf *buf, uint8_t *errcode)
262 {
263 	__ASSERT(sep, "Invalid sep");
264 	return a2dp_process_config_ind(session, sep, int_seid, buf, errcode, true);
265 }
266 
267 #if defined(CONFIG_BT_A2DP_SINK)
bt_a2dp_media_data_callback(struct bt_avdtp_sep * sep,struct net_buf * buf)268 static void bt_a2dp_media_data_callback(struct bt_avdtp_sep *sep, struct net_buf *buf)
269 {
270 	struct bt_avdtp_media_hdr *media_hdr;
271 	struct bt_a2dp_ep *ep;
272 	struct bt_a2dp_stream *stream;
273 
274 	__ASSERT(sep, "Invalid sep");
275 	ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
276 	if (ep->stream == NULL || buf->len < sizeof(*media_hdr)) {
277 		return;
278 	}
279 	stream = ep->stream;
280 
281 	media_hdr = net_buf_pull_mem(buf, sizeof(*media_hdr));
282 
283 	stream->ops->recv(stream, buf, sys_get_be16((uint8_t *)&media_hdr->sequence_number),
284 			  sys_get_be32((uint8_t *)&media_hdr->time_stamp));
285 }
286 #endif
287 
288 typedef int (*bt_a2dp_ctrl_req_cb)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code);
289 typedef void (*bt_a2dp_ctrl_done_cb)(struct bt_a2dp_stream *stream);
290 
a2dp_ctrl_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode,bt_a2dp_ctrl_req_cb req_cb,bt_a2dp_ctrl_done_cb done_cb,bool clear_stream)291 static int a2dp_ctrl_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode,
292 			 bt_a2dp_ctrl_req_cb req_cb, bt_a2dp_ctrl_done_cb done_cb,
293 			 bool clear_stream)
294 {
295 	struct bt_a2dp_ep *ep;
296 	struct bt_a2dp_stream *stream;
297 
298 	*errcode = 0;
299 	ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
300 	if (ep->stream == NULL) {
301 		*errcode = BT_AVDTP_BAD_ACP_SEID;
302 		return -EINVAL;
303 	}
304 	stream = ep->stream;
305 
306 	if (req_cb != NULL) {
307 		uint8_t rsp_err_code;
308 		int err;
309 
310 		err = req_cb(stream, &rsp_err_code);
311 		if (err) {
312 			*errcode = rsp_err_code;
313 		}
314 	}
315 
316 	if (*errcode == 0) {
317 		if (clear_stream) {
318 			ep->stream = NULL;
319 		}
320 
321 		if (done_cb != NULL) {
322 			done_cb(stream);
323 		}
324 	}
325 
326 	return (*errcode == 0) ? 0 : -EINVAL;
327 }
328 
a2dp_open_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode)329 static int a2dp_open_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
330 {
331 	struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
332 	bt_a2dp_ctrl_req_cb req_cb;
333 	bt_a2dp_ctrl_done_cb done_cb;
334 
335 	__ASSERT(sep, "Invalid sep");
336 	req_cb = a2dp_cb != NULL ? a2dp_cb->establish_req : NULL;
337 	done_cb = (ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->established
338 								  : NULL;
339 	return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false);
340 }
341 
a2dp_start_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode)342 static int a2dp_start_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
343 {
344 	struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
345 	bt_a2dp_ctrl_req_cb req_cb;
346 	bt_a2dp_ctrl_done_cb done_cb;
347 
348 	__ASSERT(sep, "Invalid sep");
349 	req_cb = a2dp_cb != NULL ? a2dp_cb->start_req : NULL;
350 	done_cb = (ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->started : NULL;
351 	return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false);
352 }
353 
a2dp_suspend_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode)354 static int a2dp_suspend_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
355 {
356 	struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
357 	bt_a2dp_ctrl_req_cb req_cb;
358 	bt_a2dp_ctrl_done_cb done_cb;
359 
360 	__ASSERT(sep, "Invalid sep");
361 	req_cb = a2dp_cb != NULL ? a2dp_cb->suspend_req : NULL;
362 	done_cb =
363 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->suspended : NULL;
364 	return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false);
365 }
366 
a2dp_close_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode)367 static int a2dp_close_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
368 {
369 	struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
370 	bt_a2dp_ctrl_req_cb req_cb;
371 	bt_a2dp_ctrl_done_cb done_cb;
372 
373 	__ASSERT(sep, "Invalid sep");
374 	req_cb = a2dp_cb != NULL ? a2dp_cb->release_req : NULL;
375 	done_cb =
376 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->released : NULL;
377 	return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, true);
378 }
379 
a2dp_abort_ind(struct bt_avdtp * session,struct bt_avdtp_sep * sep,uint8_t * errcode)380 static int a2dp_abort_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
381 {
382 	struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
383 	bt_a2dp_ctrl_req_cb req_cb;
384 	bt_a2dp_ctrl_done_cb done_cb;
385 
386 	__ASSERT(sep, "Invalid sep");
387 	req_cb = a2dp_cb != NULL ? a2dp_cb->abort_req : NULL;
388 	done_cb = (ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->aborted : NULL;
389 	return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, true);
390 }
391 
bt_a2dp_set_config_cb(struct bt_avdtp_req * req)392 static int bt_a2dp_set_config_cb(struct bt_avdtp_req *req)
393 {
394 	struct bt_a2dp *a2dp = SET_CONF_PARAM(SET_CONF_REQ(req));
395 	struct bt_a2dp_ep *ep;
396 	struct bt_a2dp_stream *stream;
397 	struct bt_a2dp_stream_ops *ops;
398 
399 	ep = CONTAINER_OF(a2dp->set_config_param.sep, struct bt_a2dp_ep, sep);
400 	if (ep->stream == NULL) {
401 		return -EINVAL;
402 	}
403 	if ((ep->stream == NULL) || (SET_CONF_REQ(req) != &a2dp->set_config_param)) {
404 		return -EINVAL;
405 	}
406 	stream = ep->stream;
407 
408 	LOG_DBG("SET CONFIGURATION result:%d", req->status);
409 
410 	if ((a2dp_cb != NULL) && (a2dp_cb->config_rsp != NULL)) {
411 		a2dp_cb->config_rsp(stream, req->status);
412 	}
413 
414 	ops = stream->ops;
415 	if ((!req->status) && (ops->configured != NULL)) {
416 		ops->configured(stream);
417 	}
418 	return 0;
419 }
420 
bt_a2dp_get_capabilities_cb(struct bt_avdtp_req * req)421 static int bt_a2dp_get_capabilities_cb(struct bt_avdtp_req *req)
422 {
423 	int err;
424 	uint8_t *codec_info_element;
425 	struct bt_a2dp *a2dp = GET_CAP_PARAM(GET_CAP_REQ(req));
426 	uint16_t codec_info_element_len;
427 	uint8_t codec_type;
428 	uint8_t user_ret;
429 
430 	if (GET_CAP_REQ(req) != &a2dp->get_capabilities_param) {
431 		return -EINVAL;
432 	}
433 
434 	LOG_DBG("GET CAPABILITIES result:%d", req->status);
435 	if (req->status) {
436 		if ((a2dp->discover_cb_param != NULL) && (a2dp->discover_cb_param->cb != NULL)) {
437 			a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
438 			a2dp->discover_cb_param = NULL;
439 		}
440 		return 0;
441 	}
442 
443 	err = bt_avdtp_parse_capability_codec(a2dp->get_capabilities_param.buf, &codec_type,
444 					      &codec_info_element, &codec_info_element_len);
445 	if (err) {
446 		LOG_DBG("codec capability parsing fail");
447 		return 0;
448 	}
449 
450 	if (codec_info_element_len > BT_A2DP_MAX_IE_LENGTH) {
451 		codec_info_element_len = BT_A2DP_MAX_IE_LENGTH;
452 	}
453 
454 	if ((a2dp->discover_cb_param != NULL) && (a2dp->discover_cb_param->cb != NULL)) {
455 		struct bt_a2dp_ep *ep = NULL;
456 		struct bt_a2dp_ep_info *info = &a2dp->discover_cb_param->info;
457 
458 		info->codec_type = codec_type;
459 		info->sep_info = a2dp->discover_cb_param->seps_info[a2dp->get_cap_index];
460 		memcpy(&info->codec_cap.codec_ie, codec_info_element, codec_info_element_len);
461 		info->codec_cap.len = codec_info_element_len;
462 		user_ret = a2dp->discover_cb_param->cb(a2dp, info, &ep);
463 		if (ep != NULL) {
464 			ep->codec_type = info->codec_type;
465 			ep->sep.sep_info = info->sep_info;
466 			*ep->codec_cap = info->codec_cap;
467 			ep->stream = NULL;
468 		}
469 
470 		if (user_ret == BT_A2DP_DISCOVER_EP_CONTINUE) {
471 			if (bt_a2dp_get_sep_caps(a2dp) != 0) {
472 				a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
473 				a2dp->discover_cb_param = NULL;
474 			}
475 		} else {
476 			a2dp->discover_cb_param = NULL;
477 		}
478 	}
479 
480 	return 0;
481 }
482 
bt_a2dp_get_sep_caps(struct bt_a2dp * a2dp)483 static int bt_a2dp_get_sep_caps(struct bt_a2dp *a2dp)
484 {
485 	int err;
486 
487 	if ((a2dp->discover_cb_param == NULL) || (a2dp->discover_cb_param->cb == NULL)) {
488 		return -EINVAL;
489 	}
490 
491 	if (a2dp->get_cap_index == 0xFFu) {
492 		a2dp->get_cap_index = 0U;
493 	} else {
494 		a2dp->get_cap_index++;
495 	}
496 
497 	for (; a2dp->get_cap_index < a2dp->peer_seps_count; a2dp->get_cap_index++) {
498 		if (a2dp->discover_cb_param->seps_info[a2dp->get_cap_index].media_type ==
499 		    BT_AVDTP_AUDIO) {
500 			a2dp->get_capabilities_param.req.func = bt_a2dp_get_capabilities_cb;
501 			a2dp->get_capabilities_param.buf = NULL;
502 			a2dp->get_capabilities_param.stream_endpoint_id =
503 				a2dp->discover_cb_param->seps_info[a2dp->get_cap_index].id;
504 			err = bt_avdtp_get_capabilities(&a2dp->session,
505 							&a2dp->get_capabilities_param);
506 			if (err) {
507 				LOG_DBG("AVDTP get codec_cap failed");
508 				a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
509 				a2dp->discover_cb_param = NULL;
510 			}
511 			return 0;
512 		}
513 	}
514 	return -EINVAL;
515 }
516 
bt_a2dp_discover_cb(struct bt_avdtp_req * req)517 static int bt_a2dp_discover_cb(struct bt_avdtp_req *req)
518 {
519 	struct bt_a2dp *a2dp = DISCOVER_PARAM(DISCOVER_REQ(req));
520 	struct bt_avdtp_sep_info *sep_info;
521 	int err;
522 
523 	LOG_DBG("DISCOVER result:%d", req->status);
524 	if (a2dp->discover_cb_param == NULL) {
525 		return -EINVAL;
526 	}
527 	a2dp->peer_seps_count = 0U;
528 	if (!(req->status)) {
529 		if (a2dp->discover_cb_param->sep_count == 0) {
530 			if (a2dp->discover_cb_param->cb != NULL) {
531 				a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
532 				a2dp->discover_cb_param = NULL;
533 			}
534 			return -EINVAL;
535 		}
536 
537 		do {
538 			sep_info = &a2dp->discover_cb_param->seps_info[a2dp->peer_seps_count];
539 			err = bt_avdtp_parse_sep(DISCOVER_REQ(req)->buf, sep_info);
540 			if (err) {
541 				break;
542 			}
543 			a2dp->peer_seps_count++;
544 			LOG_DBG("id:%d, inuse:%d, media_type:%d, tsep:%d, ", sep_info->id,
545 				sep_info->inuse, sep_info->media_type, sep_info->tsep);
546 		} while (a2dp->peer_seps_count < a2dp->discover_cb_param->sep_count);
547 
548 		/* trigger the getting capability */
549 		a2dp->get_cap_index = 0xFFu;
550 		if (bt_a2dp_get_sep_caps(a2dp) != 0) {
551 			/* the peer_seps' caps is done.*/
552 			if (a2dp->discover_cb_param->cb != NULL) {
553 				a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
554 				a2dp->discover_cb_param = NULL;
555 			}
556 		}
557 	} else {
558 		if (a2dp->discover_cb_param->cb != NULL) {
559 			a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
560 			a2dp->discover_cb_param = NULL;
561 		}
562 	}
563 
564 	return 0;
565 }
566 
bt_a2dp_discover(struct bt_a2dp * a2dp,struct bt_a2dp_discover_param * param)567 int bt_a2dp_discover(struct bt_a2dp *a2dp, struct bt_a2dp_discover_param *param)
568 {
569 	int err;
570 
571 	if ((a2dp == NULL) || (param == NULL)) {
572 		return -EINVAL;
573 	}
574 
575 	if (a2dp->a2dp_state != INTERNAL_STATE_AVDTP_CONNECTED) {
576 		return -EIO;
577 	}
578 
579 	if (a2dp->discover_cb_param != NULL) {
580 		return -EBUSY;
581 	}
582 
583 	a2dp->discover_cb_param = param;
584 	a2dp->discover_param.req.func = bt_a2dp_discover_cb;
585 	a2dp->discover_param.buf = NULL;
586 	err = bt_avdtp_discover(&a2dp->session, &a2dp->discover_param);
587 	if (err) {
588 		if (a2dp->discover_cb_param->cb != NULL) {
589 			a2dp->discover_cb_param->cb(a2dp, NULL, NULL);
590 		}
591 		a2dp->discover_cb_param = NULL;
592 	}
593 
594 	return err;
595 }
596 
bt_a2dp_stream_cb_register(struct bt_a2dp_stream * stream,struct bt_a2dp_stream_ops * ops)597 void bt_a2dp_stream_cb_register(struct bt_a2dp_stream *stream, struct bt_a2dp_stream_ops *ops)
598 {
599 	__ASSERT_NO_MSG(stream);
600 	stream->ops = ops;
601 }
602 
bt_a2dp_stream_config_set_param(struct bt_a2dp * a2dp,struct bt_a2dp_codec_cfg * config,bt_avdtp_func_t cb,uint8_t remote_id,uint8_t int_id,uint8_t codec_type,struct bt_avdtp_sep * sep)603 static inline void bt_a2dp_stream_config_set_param(struct bt_a2dp *a2dp,
604 						   struct bt_a2dp_codec_cfg *config,
605 						   bt_avdtp_func_t cb, uint8_t remote_id,
606 						   uint8_t int_id, uint8_t codec_type,
607 						   struct bt_avdtp_sep *sep)
608 {
609 	a2dp->set_config_param.req.func = cb;
610 	a2dp->set_config_param.acp_stream_ep_id = remote_id;
611 	a2dp->set_config_param.int_stream_endpoint_id = int_id;
612 	a2dp->set_config_param.media_type = BT_AVDTP_AUDIO;
613 	a2dp->set_config_param.media_codec_type = codec_type;
614 	a2dp->set_config_param.codec_specific_ie_len = config->codec_config->len;
615 	a2dp->set_config_param.codec_specific_ie = &config->codec_config->codec_ie[0];
616 	a2dp->set_config_param.sep = sep;
617 }
618 
bt_a2dp_stream_config(struct bt_a2dp * a2dp,struct bt_a2dp_stream * stream,struct bt_a2dp_ep * local_ep,struct bt_a2dp_ep * remote_ep,struct bt_a2dp_codec_cfg * config)619 int bt_a2dp_stream_config(struct bt_a2dp *a2dp, struct bt_a2dp_stream *stream,
620 			  struct bt_a2dp_ep *local_ep, struct bt_a2dp_ep *remote_ep,
621 			  struct bt_a2dp_codec_cfg *config)
622 {
623 	if ((a2dp == NULL) || (stream == NULL) || (local_ep == NULL) || (remote_ep == NULL) ||
624 	    (config == NULL)) {
625 		return -EINVAL;
626 	}
627 
628 	if ((local_ep->sep.sep_info.tsep == remote_ep->sep.sep_info.tsep) ||
629 	    (local_ep->codec_type != remote_ep->codec_type)) {
630 		return -EINVAL;
631 	}
632 
633 	stream->local_ep = local_ep;
634 	stream->remote_ep = remote_ep;
635 	stream->remote_ep_id = remote_ep->sep.sep_info.id;
636 	stream->a2dp = a2dp;
637 	local_ep->stream = stream;
638 	remote_ep->stream = stream;
639 	bt_a2dp_stream_config_set_param(a2dp, config, bt_a2dp_set_config_cb,
640 					remote_ep->sep.sep_info.id, local_ep->sep.sep_info.id,
641 					local_ep->codec_type, &local_ep->sep);
642 	return bt_avdtp_set_configuration(&a2dp->session, &a2dp->set_config_param);
643 }
644 
645 typedef void (*bt_a2dp_rsp_cb)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code);
646 typedef void (*bt_a2dp_done_cb)(struct bt_a2dp_stream *stream);
647 
bt_a2dp_ctrl_cb(struct bt_avdtp_req * req,bt_a2dp_rsp_cb rsp_cb,bt_a2dp_done_cb done_cb,bool clear_stream)648 static int bt_a2dp_ctrl_cb(struct bt_avdtp_req *req, bt_a2dp_rsp_cb rsp_cb, bt_a2dp_done_cb done_cb,
649 			   bool clear_stream)
650 {
651 	struct bt_a2dp *a2dp = CTRL_PARAM(CTRL_REQ(req));
652 	struct bt_a2dp_ep *ep;
653 	struct bt_a2dp_stream *stream;
654 
655 	ep = CONTAINER_OF(a2dp->ctrl_param.sep, struct bt_a2dp_ep, sep);
656 	if ((ep->stream == NULL) || (CTRL_REQ(req) != &a2dp->ctrl_param)) {
657 		return -EINVAL;
658 	}
659 	stream = ep->stream;
660 	if (clear_stream) {
661 		ep->stream = NULL;
662 	}
663 
664 	LOG_DBG("ctrl result:%d", req->status);
665 
666 	if (rsp_cb != NULL) {
667 		rsp_cb(stream, req->status);
668 	}
669 
670 	if ((!req->status) && (done_cb != NULL)) {
671 		done_cb(stream);
672 	}
673 	return 0;
674 }
675 
bt_a2dp_open_cb(struct bt_avdtp_req * req)676 static int bt_a2dp_open_cb(struct bt_avdtp_req *req)
677 {
678 	struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
679 	bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->establish_rsp : NULL;
680 	bt_a2dp_done_cb done_cb = (ep->stream != NULL && ep->stream->ops != NULL)
681 					  ? ep->stream->ops->established
682 					  : NULL;
683 
684 	return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false);
685 }
686 
bt_a2dp_start_cb(struct bt_avdtp_req * req)687 static int bt_a2dp_start_cb(struct bt_avdtp_req *req)
688 {
689 	struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
690 	bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->start_rsp : NULL;
691 	bt_a2dp_done_cb done_cb =
692 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->started : NULL;
693 
694 	return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false);
695 }
696 
bt_a2dp_suspend_cb(struct bt_avdtp_req * req)697 static int bt_a2dp_suspend_cb(struct bt_avdtp_req *req)
698 {
699 	struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
700 	bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->suspend_rsp : NULL;
701 	bt_a2dp_done_cb done_cb =
702 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->suspended : NULL;
703 
704 	return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false);
705 }
706 
bt_a2dp_close_cb(struct bt_avdtp_req * req)707 static int bt_a2dp_close_cb(struct bt_avdtp_req *req)
708 {
709 	struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
710 	bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->release_rsp : NULL;
711 	bt_a2dp_done_cb done_cb =
712 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->released : NULL;
713 
714 	return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, true);
715 }
716 
bt_a2dp_abort_cb(struct bt_avdtp_req * req)717 static int bt_a2dp_abort_cb(struct bt_avdtp_req *req)
718 {
719 	struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
720 	bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->abort_rsp : NULL;
721 	bt_a2dp_done_cb done_cb =
722 		(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->aborted : NULL;
723 
724 	return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, true);
725 }
726 
bt_a2dp_stream_ctrl_pre(struct bt_a2dp_stream * stream,bt_avdtp_func_t cb)727 static int bt_a2dp_stream_ctrl_pre(struct bt_a2dp_stream *stream, bt_avdtp_func_t cb)
728 {
729 	struct bt_a2dp *a2dp;
730 
731 	if ((stream == NULL) || (stream->local_ep == NULL) || (stream->a2dp == NULL)) {
732 		return -EINVAL;
733 	}
734 
735 	a2dp = stream->a2dp;
736 	a2dp->ctrl_param.req.func = cb;
737 	a2dp->ctrl_param.acp_stream_ep_id = stream->remote_ep != NULL
738 						    ? stream->remote_ep->sep.sep_info.id
739 						    : stream->remote_ep_id;
740 	a2dp->ctrl_param.sep = &stream->local_ep->sep;
741 	return 0;
742 }
743 
bt_a2dp_stream_establish(struct bt_a2dp_stream * stream)744 int bt_a2dp_stream_establish(struct bt_a2dp_stream *stream)
745 {
746 	int err;
747 	struct bt_a2dp *a2dp = stream->a2dp;
748 
749 	err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_open_cb);
750 	if (err) {
751 		return err;
752 	}
753 	return bt_avdtp_open(&a2dp->session, &a2dp->ctrl_param);
754 }
755 
bt_a2dp_stream_release(struct bt_a2dp_stream * stream)756 int bt_a2dp_stream_release(struct bt_a2dp_stream *stream)
757 {
758 	int err;
759 	struct bt_a2dp *a2dp = stream->a2dp;
760 
761 	err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_close_cb);
762 	if (err) {
763 		return err;
764 	}
765 	return bt_avdtp_close(&a2dp->session, &a2dp->ctrl_param);
766 }
767 
bt_a2dp_stream_start(struct bt_a2dp_stream * stream)768 int bt_a2dp_stream_start(struct bt_a2dp_stream *stream)
769 {
770 	int err;
771 	struct bt_a2dp *a2dp = stream->a2dp;
772 
773 	err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_start_cb);
774 	if (err) {
775 		return err;
776 	}
777 	return bt_avdtp_start(&a2dp->session, &a2dp->ctrl_param);
778 }
779 
bt_a2dp_stream_suspend(struct bt_a2dp_stream * stream)780 int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream)
781 {
782 	int err;
783 	struct bt_a2dp *a2dp = stream->a2dp;
784 
785 	err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_suspend_cb);
786 	if (err) {
787 		return err;
788 	}
789 	return bt_avdtp_suspend(&a2dp->session, &a2dp->ctrl_param);
790 }
791 
bt_a2dp_stream_abort(struct bt_a2dp_stream * stream)792 int bt_a2dp_stream_abort(struct bt_a2dp_stream *stream)
793 {
794 	int err;
795 	struct bt_a2dp *a2dp = stream->a2dp;
796 
797 	err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_abort_cb);
798 	if (err) {
799 		return err;
800 	}
801 	return bt_avdtp_abort(&a2dp->session, &a2dp->ctrl_param);
802 }
803 
bt_a2dp_stream_reconfig(struct bt_a2dp_stream * stream,struct bt_a2dp_codec_cfg * config)804 int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config)
805 {
806 	uint8_t remote_id;
807 
808 	if ((stream == NULL) || (config == NULL)) {
809 		return -EINVAL;
810 	}
811 
812 	remote_id = stream->remote_ep != NULL ? stream->remote_ep->sep.sep_info.id
813 					      : stream->remote_ep_id;
814 	bt_a2dp_stream_config_set_param(stream->a2dp, config, bt_a2dp_set_config_cb, remote_id,
815 					stream->local_ep->sep.sep_info.id,
816 					stream->local_ep->codec_type, &stream->local_ep->sep);
817 	return bt_avdtp_reconfigure(&stream->a2dp->session, &stream->a2dp->set_config_param);
818 }
819 
bt_a2dp_get_mtu(struct bt_a2dp_stream * stream)820 uint32_t bt_a2dp_get_mtu(struct bt_a2dp_stream *stream)
821 {
822 	if ((stream == NULL) || (stream->local_ep == NULL)) {
823 		return 0;
824 	}
825 
826 	return bt_avdtp_get_media_mtu(&stream->local_ep->sep);
827 }
828 
829 #if defined(CONFIG_BT_A2DP_SOURCE)
bt_a2dp_stream_send(struct bt_a2dp_stream * stream,struct net_buf * buf,uint16_t seq_num,uint32_t ts)830 int bt_a2dp_stream_send(struct bt_a2dp_stream *stream, struct net_buf *buf, uint16_t seq_num,
831 			uint32_t ts)
832 {
833 	struct bt_avdtp_media_hdr *media_hdr;
834 
835 	if (stream == NULL || stream->local_ep == NULL) {
836 		return -EINVAL;
837 	}
838 
839 	media_hdr = net_buf_push(buf, sizeof(struct bt_avdtp_media_hdr));
840 	memset(media_hdr, 0, sizeof(struct bt_avdtp_media_hdr));
841 	if (stream->local_ep->codec_type == BT_A2DP_SBC) {
842 		media_hdr->playload_type = A2DP_SBC_PAYLOAD_TYPE;
843 	}
844 
845 	media_hdr->RTP_version = BT_AVDTP_RTP_VERSION;
846 	media_hdr->synchronization_source = 0U;
847 	/* update time_stamp in the buf */
848 	sys_put_be32(ts, (uint8_t *)&media_hdr->time_stamp);
849 	/* update sequence_number in the buf */
850 	sys_put_be16(seq_num, (uint8_t *)&media_hdr->sequence_number);
851 	/* send the buf */
852 	return bt_avdtp_send_media_data(&stream->local_ep->sep, buf);
853 }
854 #endif
855 
a2dp_stream_l2cap_disconnected(struct bt_avdtp * session,struct bt_avdtp_sep * sep)856 int a2dp_stream_l2cap_disconnected(struct bt_avdtp *session, struct bt_avdtp_sep *sep)
857 {
858 	struct bt_a2dp_ep *ep;
859 
860 	__ASSERT(sep, "Invalid sep");
861 	ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
862 	if (ep->stream != NULL) {
863 		struct bt_a2dp_stream_ops *ops;
864 		struct bt_a2dp_stream *stream = ep->stream;
865 
866 		ops = stream->ops;
867 		/* Many places set ep->stream as NULL like abort and close.
868 		 * it should be OK without lock protection because
869 		 * all the related callbacks are in the same zephyr task context.
870 		 */
871 		ep->stream = NULL;
872 		if ((ops != NULL) && (ops->released != NULL)) {
873 			ops->released(stream);
874 		}
875 	}
876 
877 	return 0;
878 }
879 
880 static const struct bt_avdtp_ops_cb signaling_avdtp_ops = {
881 	.connected = a2dp_connected,
882 	.disconnected = a2dp_disconnected,
883 	.alloc_buf = a2dp_alloc_buf,
884 	.discovery_ind = a2dp_discovery_ind,
885 	.get_capabilities_ind = a2dp_get_capabilities_ind,
886 	.set_configuration_ind = a2dp_set_config_ind,
887 	.re_configuration_ind = a2dp_re_config_ind,
888 	.open_ind = a2dp_open_ind,
889 	.start_ind = a2dp_start_ind,
890 	.close_ind = a2dp_close_ind,
891 	.suspend_ind = a2dp_suspend_ind,
892 	.abort_ind = a2dp_abort_ind,
893 	.stream_l2cap_disconnected = a2dp_stream_l2cap_disconnected,
894 };
895 
a2dp_accept(struct bt_conn * conn,struct bt_avdtp ** session)896 int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session)
897 {
898 	struct bt_a2dp *a2dp;
899 
900 	a2dp = a2dp_get_connection(conn);
901 	if (!a2dp) {
902 		return -ENOMEM;
903 	}
904 
905 	*session = &(a2dp->session);
906 	a2dp->session.ops = &signaling_avdtp_ops;
907 	LOG_DBG("session: %p", &(a2dp->session));
908 
909 	return 0;
910 }
911 
912 /* The above callback structures need to be packed and passed to AVDTP */
913 static struct bt_avdtp_event_cb avdtp_cb = {
914 	.accept = a2dp_accept,
915 };
916 
bt_a2dp_init(void)917 int bt_a2dp_init(void)
918 {
919 	int err;
920 
921 	/* Register event handlers with AVDTP */
922 	err = bt_avdtp_register(&avdtp_cb);
923 	if (err < 0) {
924 		LOG_ERR("A2DP registration failed");
925 		return err;
926 	}
927 
928 	for (uint8_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
929 		memset(&connection[i], 0, sizeof(struct bt_a2dp));
930 	}
931 
932 	LOG_DBG("A2DP Initialized successfully.");
933 	return 0;
934 }
935 
bt_a2dp_connect(struct bt_conn * conn)936 struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn)
937 {
938 	struct bt_a2dp *a2dp;
939 	int err;
940 
941 	a2dp = a2dp_get_connection(conn);
942 	if (!a2dp) {
943 		LOG_ERR("Cannot allocate memory");
944 		return NULL;
945 	}
946 
947 	a2dp->a2dp_state = INTERNAL_STATE_IDLE;
948 
949 	a2dp->session.ops = &signaling_avdtp_ops;
950 	err = bt_avdtp_connect(conn, &(a2dp->session));
951 	if (err < 0) {
952 		LOG_DBG("AVDTP Connect failed");
953 		return NULL;
954 	}
955 
956 	LOG_DBG("Connect request sent");
957 	return a2dp;
958 }
959 
bt_a2dp_disconnect(struct bt_a2dp * a2dp)960 int bt_a2dp_disconnect(struct bt_a2dp *a2dp)
961 {
962 	__ASSERT_NO_MSG(a2dp);
963 	return bt_avdtp_disconnect(&a2dp->session);
964 }
965 
bt_a2dp_register_ep(struct bt_a2dp_ep * ep,uint8_t media_type,uint8_t sep_type)966 int bt_a2dp_register_ep(struct bt_a2dp_ep *ep, uint8_t media_type, uint8_t sep_type)
967 {
968 	int err;
969 
970 	__ASSERT_NO_MSG(ep);
971 
972 #if defined(CONFIG_BT_A2DP_SINK)
973 	if (sep_type == BT_AVDTP_SINK) {
974 		ep->sep.media_data_cb = bt_a2dp_media_data_callback;
975 	} else {
976 		ep->sep.media_data_cb = NULL;
977 	}
978 #else
979 	ep->sep.media_data_cb = NULL;
980 #endif
981 	err = bt_avdtp_register_sep(media_type, sep_type, &(ep->sep));
982 	if (err < 0) {
983 		return err;
984 	}
985 
986 	return 0;
987 }
988 
bt_a2dp_register_cb(struct bt_a2dp_cb * cb)989 int bt_a2dp_register_cb(struct bt_a2dp_cb *cb)
990 {
991 	a2dp_cb = cb;
992 	return 0;
993 }
994