1 /** @file
2  *  @brief Bluetooth Media Control Service
3  */
4 
5 /*
6  * Copyright (c) 2019 - 2021 Nordic Semiconductor ASA
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <errno.h>
12 #include <sys/types.h>
13 #include <stdbool.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <string.h>
17 
18 #include <zephyr/autoconf.h>
19 #include <zephyr/bluetooth/att.h>
20 #include <zephyr/bluetooth/audio/mcs.h>
21 #include <zephyr/bluetooth/audio/media_proxy.h>
22 #include <zephyr/bluetooth/bluetooth.h>
23 #include <zephyr/bluetooth/conn.h>
24 #include <zephyr/bluetooth/gatt.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/bluetooth/services/ots.h>
27 #include <zephyr/device.h>
28 #include <zephyr/init.h>
29 #include <zephyr/kernel.h>
30 #include <zephyr/logging/log.h>
31 #include <zephyr/sys/__assert.h>
32 #include <zephyr/sys/atomic.h>
33 #include <zephyr/sys/byteorder.h>
34 #include <zephyr/sys/util.h>
35 #include <zephyr/sys/util_macro.h>
36 #include <zephyr/types.h>
37 
38 #include "audio_internal.h"
39 #include "media_proxy_internal.h"
40 #include "mcs_internal.h"
41 
42 LOG_MODULE_REGISTER(bt_mcs, CONFIG_BT_MCS_LOG_LEVEL);
43 
44 static void notify(const struct bt_uuid *uuid, const void *data, uint16_t len);
45 
46 static struct media_proxy_sctrl_cbs cbs;
47 
48 enum {
49 	FLAG_PLAYER_NAME_CHANGED,
50 	FLAG_ICON_URL_CHANGED,
51 	FLAG_TRACK_CHANGED,
52 	FLAG_TRACK_TITLE_CHANGED,
53 	FLAG_TRACK_DURATION_CHANGED,
54 	FLAG_TRACK_POSITION_CHANGED,
55 	FLAG_PLAYBACK_SPEED_CHANGED,
56 	FLAG_SEEKING_SPEED_CHANGED,
57 	FLAG_PLAYING_ORDER_CHANGED,
58 	FLAG_MEDIA_STATE_CHANGED,
59 	FLAG_MEDIA_CONTROL_OPCODES_CHANGED,
60 	FLAG_MEDIA_CONTROL_POINT_BUSY,
61 	FLAG_MEDIA_CONTROL_POINT_RESULT,
62 #if defined(CONFIG_BT_OTS)
63 	FLAG_CURRENT_TRACK_OBJ_ID_CHANGED,
64 	FLAG_NEXT_TRACK_OBJ_ID_CHANGED,
65 	FLAG_PARENT_GROUP_OBJ_ID_CHANGED,
66 	FLAG_CURRENT_GROUP_OBJ_ID_CHANGED,
67 	FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED,
68 	FLAG_SEARCH_CONTROL_POINT_BUSY,
69 	FLAG_SEARCH_CONTROL_POINT_RESULT,
70 #endif /* CONFIG_BT_OTS */
71 	FLAG_NUM,
72 };
73 
74 static struct client_state {
75 	ATOMIC_DEFINE(flags, FLAG_NUM);
76 	struct mpl_cmd_ntf cmd_ntf;
77 #if defined(CONFIG_BT_OTS)
78 	uint8_t search_control_point_result;
79 #endif /* CONFIG_BT_OTS */
80 } clients[CONFIG_BT_MAX_CONN];
81 
disconnected(struct bt_conn * conn,uint8_t reason)82 static void disconnected(struct bt_conn *conn, uint8_t reason)
83 {
84 	/* Clear data on disconnect */
85 	memset(&clients[bt_conn_index(conn)], 0, sizeof(struct client_state));
86 }
87 
88 BT_CONN_CB_DEFINE(conn_callbacks) = {
89 	.disconnected = disconnected,
90 };
91 
92 /* Functions for reading and writing attributes, and for keeping track
93  * of attribute configuration changes.
94  * Functions for notifications are placed after the service definition.
95  */
read_player_name(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)96 static ssize_t read_player_name(struct bt_conn *conn,
97 				const struct bt_gatt_attr *attr, void *buf,
98 				uint16_t len, uint16_t offset)
99 {
100 	const char *name = media_proxy_sctrl_get_player_name();
101 
102 	LOG_DBG("Player name read: %s (offset %u)", name, offset);
103 
104 	if (conn != NULL) {
105 		struct client_state *client = &clients[bt_conn_index(conn)];
106 
107 		if (offset == 0) {
108 			atomic_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED);
109 		} else if (atomic_test_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
110 			return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
111 		}
112 	}
113 
114 	return bt_gatt_attr_read(conn, attr, buf, len, offset, name,
115 				 strlen(name));
116 }
117 
player_name_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)118 static void player_name_cfg_changed(const struct bt_gatt_attr *attr,
119 				    uint16_t value)
120 {
121 	LOG_DBG("value 0x%04x", value);
122 }
123 
124 #ifdef CONFIG_BT_OTS
read_icon_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)125 static ssize_t read_icon_id(struct bt_conn *conn,
126 			    const struct bt_gatt_attr *attr, void *buf,
127 			    uint16_t len, uint16_t offset)
128 {
129 	uint64_t icon_id = media_proxy_sctrl_get_icon_id();
130 	uint8_t icon_id_le[BT_OTS_OBJ_ID_SIZE];
131 
132 	sys_put_le48(icon_id, icon_id_le);
133 
134 	LOG_DBG_OBJ_ID("Icon object read: ", icon_id);
135 
136 	return bt_gatt_attr_read(conn, attr, buf, len, offset, icon_id_le,
137 				 sizeof(icon_id_le));
138 }
139 #endif /* CONFIG_BT_OTS */
140 
read_icon_url(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)141 static ssize_t read_icon_url(struct bt_conn *conn,
142 			     const struct bt_gatt_attr *attr, void *buf,
143 			     uint16_t len, uint16_t offset)
144 {
145 	const char *url = media_proxy_sctrl_get_icon_url();
146 
147 	LOG_DBG("Icon URL read, offset: %d, len:%d, URL: %s", offset, len, url);
148 
149 	if (conn != NULL) {
150 		struct client_state *client = &clients[bt_conn_index(conn)];
151 
152 		if (offset == 0) {
153 			atomic_clear_bit(client->flags, FLAG_ICON_URL_CHANGED);
154 		} else if (atomic_test_bit(client->flags, FLAG_ICON_URL_CHANGED)) {
155 			return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
156 		}
157 	}
158 
159 	return bt_gatt_attr_read(conn, attr, buf, len, offset, url,
160 				 strlen(url));
161 }
162 
track_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)163 static void track_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
164 {
165 	LOG_DBG("value 0x%04x", value);
166 }
167 
read_track_title(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)168 static ssize_t read_track_title(struct bt_conn *conn,
169 				const struct bt_gatt_attr *attr,
170 				void *buf, uint16_t len, uint16_t offset)
171 {
172 	const char *title = media_proxy_sctrl_get_track_title();
173 
174 	LOG_DBG("Track title read, offset: %d, len:%d, title: %s", offset, len, title);
175 
176 	if (conn != NULL) {
177 		struct client_state *client = &clients[bt_conn_index(conn)];
178 
179 		if (offset == 0) {
180 			atomic_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED);
181 		} else if (atomic_test_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
182 			return BT_GATT_ERR(BT_MCS_ERR_LONG_VAL_CHANGED);
183 		}
184 	}
185 
186 	return bt_gatt_attr_read(conn, attr, buf, len, offset, title,
187 				 strlen(title));
188 }
189 
track_title_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)190 static void track_title_cfg_changed(const struct bt_gatt_attr *attr,
191 				    uint16_t value)
192 {
193 	LOG_DBG("value 0x%04x", value);
194 }
195 
read_track_duration(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)196 static ssize_t read_track_duration(struct bt_conn *conn,
197 				   const struct bt_gatt_attr *attr, void *buf,
198 				   uint16_t len, uint16_t offset)
199 {
200 	int32_t duration = media_proxy_sctrl_get_track_duration();
201 	int32_t duration_le = sys_cpu_to_le32(duration);
202 
203 	LOG_DBG("Track duration read: %d (0x%08x)", duration, duration);
204 
205 	if (conn != NULL) {
206 		struct client_state *client = &clients[bt_conn_index(conn)];
207 
208 		atomic_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED);
209 	}
210 
211 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &duration_le, sizeof(duration_le));
212 }
213 
track_duration_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)214 static void track_duration_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
215 {
216 	LOG_DBG("value 0x%04x", value);
217 }
218 
read_track_position(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)219 static ssize_t read_track_position(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
220 				   uint16_t len, uint16_t offset)
221 {
222 	int32_t position = media_proxy_sctrl_get_track_position();
223 	int32_t position_le = sys_cpu_to_le32(position);
224 
225 	LOG_DBG("Track position read: %d (0x%08x)", position, position);
226 
227 	if (conn != NULL) {
228 		struct client_state *client = &clients[bt_conn_index(conn)];
229 
230 		atomic_clear_bit(client->flags, FLAG_TRACK_POSITION_CHANGED);
231 	}
232 
233 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &position_le,
234 				 sizeof(position_le));
235 }
236 
write_track_position(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)237 static ssize_t write_track_position(struct bt_conn *conn,
238 				    const struct bt_gatt_attr *attr,
239 				    const void *buf, uint16_t len,
240 				    uint16_t offset, uint8_t flags)
241 {
242 	int32_t position;
243 
244 	if (offset != 0) {
245 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
246 	}
247 
248 	if (len != sizeof(position)) {
249 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
250 	}
251 
252 	position = sys_get_le32((uint8_t *)buf);
253 
254 	media_proxy_sctrl_set_track_position(position);
255 
256 	LOG_DBG("Track position write: %d", position);
257 
258 	return len;
259 }
260 
track_position_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)261 static void track_position_cfg_changed(const struct bt_gatt_attr *attr,
262 				       uint16_t value)
263 {
264 	LOG_DBG("value 0x%04x", value);
265 }
266 
read_playback_speed(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)267 static ssize_t read_playback_speed(struct bt_conn *conn,
268 				   const struct bt_gatt_attr *attr, void *buf,
269 				   uint16_t len, uint16_t offset)
270 {
271 	int8_t speed = media_proxy_sctrl_get_playback_speed();
272 
273 	LOG_DBG("Playback speed read: %d", speed);
274 
275 	if (conn != NULL) {
276 		struct client_state *client = &clients[bt_conn_index(conn)];
277 
278 		atomic_clear_bit(client->flags, FLAG_PLAYBACK_SPEED_CHANGED);
279 	}
280 
281 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed, sizeof(speed));
282 }
283 
write_playback_speed(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)284 static ssize_t write_playback_speed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
285 				    const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
286 {
287 	int8_t speed;
288 
289 	if (offset != 0) {
290 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
291 	}
292 	if (len != sizeof(speed)) {
293 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
294 	}
295 
296 	memcpy(&speed, buf, len);
297 
298 	media_proxy_sctrl_set_playback_speed(speed);
299 
300 	LOG_DBG("Playback speed write: %d", speed);
301 
302 	return len;
303 }
304 
playback_speed_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)305 static void playback_speed_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
306 {
307 	LOG_DBG("value 0x%04x", value);
308 }
309 
read_seeking_speed(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)310 static ssize_t read_seeking_speed(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
311 				  uint16_t len, uint16_t offset)
312 {
313 	int8_t speed = media_proxy_sctrl_get_seeking_speed();
314 
315 	LOG_DBG("Seeking speed read: %d", speed);
316 
317 	if (conn != NULL) {
318 		struct client_state *client = &clients[bt_conn_index(conn)];
319 
320 		atomic_clear_bit(client->flags, FLAG_SEEKING_SPEED_CHANGED);
321 	}
322 
323 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &speed,
324 				 sizeof(speed));
325 }
326 
seeking_speed_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)327 static void seeking_speed_cfg_changed(const struct bt_gatt_attr *attr,
328 				      uint16_t value)
329 {
330 	LOG_DBG("value 0x%04x", value);
331 }
332 
333 #ifdef CONFIG_BT_OTS
read_track_segments_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)334 static ssize_t read_track_segments_id(struct bt_conn *conn,
335 				      const struct bt_gatt_attr *attr,
336 				      void *buf, uint16_t len, uint16_t offset)
337 {
338 	uint64_t track_segments_id = media_proxy_sctrl_get_track_segments_id();
339 	uint8_t track_segments_id_le[BT_OTS_OBJ_ID_SIZE];
340 
341 	sys_put_le48(track_segments_id, track_segments_id_le);
342 
343 	LOG_DBG_OBJ_ID("Track segments ID read: ", track_segments_id);
344 
345 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
346 				 track_segments_id_le, sizeof(track_segments_id_le));
347 }
348 
read_current_track_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)349 static ssize_t read_current_track_id(struct bt_conn *conn,
350 				     const struct bt_gatt_attr *attr, void *buf,
351 				     uint16_t len, uint16_t offset)
352 {
353 	uint64_t track_id = media_proxy_sctrl_get_current_track_id();
354 	uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
355 
356 	sys_put_le48(track_id, track_id_le);
357 
358 	LOG_DBG_OBJ_ID("Current track ID read: ", track_id);
359 
360 	if (conn != NULL) {
361 		struct client_state *client = &clients[bt_conn_index(conn)];
362 
363 		atomic_clear_bit(client->flags, FLAG_CURRENT_TRACK_OBJ_ID_CHANGED);
364 	}
365 
366 	return bt_gatt_attr_read(conn, attr, buf, len, offset, track_id_le,
367 				 sizeof(track_id_le));
368 }
369 
write_current_track_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)370 static ssize_t write_current_track_id(struct bt_conn *conn,
371 				      const struct bt_gatt_attr *attr,
372 				      const void *buf, uint16_t len, uint16_t offset,
373 				      uint8_t flags)
374 {
375 	uint64_t id;
376 
377 	if (offset != 0) {
378 		LOG_DBG("Invalid offset");
379 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
380 	}
381 
382 	if (len != BT_OTS_OBJ_ID_SIZE) {
383 		LOG_DBG("Invalid length");
384 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
385 	}
386 
387 	id = sys_get_le48((uint8_t *)buf);
388 
389 	if (IS_ENABLED(CONFIG_BT_MCS_LOG_LEVEL_DBG)) {
390 		char str[BT_OTS_OBJ_ID_STR_LEN];
391 		(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
392 		LOG_DBG("Current track write: offset: %d, len: %d, track ID: %s", offset, len, str);
393 	}
394 
395 	media_proxy_sctrl_set_current_track_id(id);
396 
397 	return BT_OTS_OBJ_ID_SIZE;
398 }
399 
current_track_id_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)400 static void current_track_id_cfg_changed(const struct bt_gatt_attr *attr,
401 					 uint16_t value)
402 {
403 	LOG_DBG("value 0x%04x", value);
404 }
405 
read_next_track_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)406 static ssize_t read_next_track_id(struct bt_conn *conn,
407 				  const struct bt_gatt_attr *attr, void *buf,
408 				  uint16_t len, uint16_t offset)
409 {
410 	uint64_t track_id = media_proxy_sctrl_get_next_track_id();
411 	uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
412 
413 	sys_put_le48(track_id, track_id_le);
414 
415 	if (conn != NULL) {
416 		struct client_state *client = &clients[bt_conn_index(conn)];
417 
418 		atomic_clear_bit(client->flags, FLAG_NEXT_TRACK_OBJ_ID_CHANGED);
419 	}
420 
421 	if (track_id == MPL_NO_TRACK_ID) {
422 		LOG_DBG("Next track read, but it is empty");
423 		/* "If the media player has no next track, the length of the */
424 		/* characteristic shall be zero." */
425 		return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
426 	}
427 
428 	LOG_DBG_OBJ_ID("Next track read: ", track_id);
429 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
430 				 track_id_le, sizeof(track_id_le));
431 }
432 
write_next_track_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)433 static ssize_t write_next_track_id(struct bt_conn *conn,
434 				   const struct bt_gatt_attr *attr,
435 				   const void *buf, uint16_t len, uint16_t offset,
436 				   uint8_t flags)
437 {
438 	uint64_t id;
439 
440 	if (offset != 0) {
441 		LOG_DBG("Invalid offset");
442 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
443 	}
444 
445 	if (len != BT_OTS_OBJ_ID_SIZE) {
446 		LOG_DBG("Invalid length");
447 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
448 	}
449 
450 	id = sys_get_le48((uint8_t *)buf);
451 
452 	if (IS_ENABLED(CONFIG_BT_MCS_LOG_LEVEL_DBG)) {
453 		char str[BT_OTS_OBJ_ID_STR_LEN];
454 		(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
455 		LOG_DBG("Next  track write: offset: %d, len: %d, track ID: %s", offset, len, str);
456 	}
457 
458 	media_proxy_sctrl_set_next_track_id(id);
459 
460 	return BT_OTS_OBJ_ID_SIZE;
461 }
462 
next_track_id_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)463 static void next_track_id_cfg_changed(const struct bt_gatt_attr *attr,
464 				      uint16_t value)
465 {
466 	LOG_DBG("value 0x%04x", value);
467 }
468 
read_parent_group_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)469 static ssize_t read_parent_group_id(struct bt_conn *conn,
470 				    const struct bt_gatt_attr *attr, void *buf,
471 				    uint16_t len, uint16_t offset)
472 {
473 	uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
474 	uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
475 
476 	sys_put_le48(group_id, group_id_le);
477 
478 	LOG_DBG_OBJ_ID("Parent group read: ", group_id);
479 
480 	if (conn != NULL) {
481 		struct client_state *client = &clients[bt_conn_index(conn)];
482 
483 		atomic_clear_bit(client->flags, FLAG_PARENT_GROUP_OBJ_ID_CHANGED);
484 	}
485 
486 	return bt_gatt_attr_read(conn, attr, buf, len, offset, group_id_le,
487 				 sizeof(group_id_le));
488 }
489 
parent_group_id_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)490 static void parent_group_id_cfg_changed(const struct bt_gatt_attr *attr,
491 					uint16_t value)
492 {
493 	LOG_DBG("value 0x%04x", value);
494 }
495 
read_current_group_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)496 static ssize_t read_current_group_id(struct bt_conn *conn,
497 				     const struct bt_gatt_attr *attr, void *buf,
498 				     uint16_t len, uint16_t offset)
499 {
500 	uint64_t group_id = media_proxy_sctrl_get_current_group_id();
501 	uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
502 
503 	sys_put_le48(group_id, group_id_le);
504 
505 	LOG_DBG_OBJ_ID("Current group read: ", group_id);
506 
507 	if (conn != NULL) {
508 		struct client_state *client = &clients[bt_conn_index(conn)];
509 
510 		atomic_clear_bit(client->flags, FLAG_CURRENT_GROUP_OBJ_ID_CHANGED);
511 	}
512 
513 	return bt_gatt_attr_read(conn, attr, buf, len, offset, group_id_le,
514 				 sizeof(group_id_le));
515 }
516 
write_current_group_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)517 static ssize_t write_current_group_id(struct bt_conn *conn,
518 				      const struct bt_gatt_attr *attr,
519 				      const void *buf, uint16_t len, uint16_t offset,
520 				      uint8_t flags)
521 {
522 	uint64_t id;
523 
524 	if (offset != 0) {
525 		LOG_DBG("Invalid offset");
526 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
527 	}
528 
529 	if (len != BT_OTS_OBJ_ID_SIZE) {
530 		LOG_DBG("Invalid length");
531 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
532 	}
533 
534 	id = sys_get_le48((uint8_t *)buf);
535 
536 	if (IS_ENABLED(CONFIG_BT_MCS_LOG_LEVEL_DBG)) {
537 		char str[BT_OTS_OBJ_ID_STR_LEN];
538 		(void)bt_ots_obj_id_to_str(id, str, sizeof(str));
539 		LOG_DBG("Current group ID write: offset: %d, len: %d, track ID: %s", offset, len,
540 			str);
541 	}
542 
543 	media_proxy_sctrl_set_current_group_id(id);
544 
545 	return BT_OTS_OBJ_ID_SIZE;
546 }
547 
current_group_id_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)548 static void current_group_id_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
549 {
550 	LOG_DBG("value 0x%04x", value);
551 }
552 #endif /* CONFIG_BT_OTS */
553 
read_playing_order(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)554 static ssize_t read_playing_order(struct bt_conn *conn,
555 				  const struct bt_gatt_attr *attr, void *buf,
556 				  uint16_t len, uint16_t offset)
557 {
558 	uint8_t order = media_proxy_sctrl_get_playing_order();
559 
560 	LOG_DBG("Playing order read: %d (0x%02x)", order, order);
561 
562 	if (conn != NULL) {
563 		struct client_state *client = &clients[bt_conn_index(conn)];
564 
565 		atomic_clear_bit(client->flags, FLAG_PLAYING_ORDER_CHANGED);
566 	}
567 
568 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &order, sizeof(order));
569 }
570 
write_playing_order(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)571 static ssize_t write_playing_order(struct bt_conn *conn, const struct bt_gatt_attr *attr,
572 				   const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
573 {
574 	LOG_DBG("Playing order write");
575 
576 	int8_t order;
577 
578 	if (offset != 0) {
579 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
580 	}
581 	if (len != sizeof(order)) {
582 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
583 	}
584 
585 	memcpy(&order, buf, len);
586 
587 	media_proxy_sctrl_set_playing_order(order);
588 
589 	LOG_DBG("Playing order write: %d", order);
590 
591 	return len;
592 }
593 
playing_order_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)594 static void playing_order_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
595 {
596 	LOG_DBG("value 0x%04x", value);
597 }
598 
read_playing_orders_supported(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)599 static ssize_t read_playing_orders_supported(struct bt_conn *conn, const struct bt_gatt_attr *attr,
600 					     void *buf, uint16_t len, uint16_t offset)
601 {
602 	uint16_t orders = media_proxy_sctrl_get_playing_orders_supported();
603 	uint16_t orders_le = sys_cpu_to_le16(orders);
604 
605 	LOG_DBG("Playing orders read: %d (0x%04x)", orders, orders);
606 
607 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &orders_le, sizeof(orders_le));
608 }
609 
read_media_state(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)610 static ssize_t read_media_state(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
611 				uint16_t len, uint16_t offset)
612 {
613 	uint8_t state = media_proxy_sctrl_get_media_state();
614 
615 	LOG_DBG("Media state read: %d", state);
616 
617 	if (conn != NULL) {
618 		struct client_state *client = &clients[bt_conn_index(conn)];
619 
620 		atomic_clear_bit(client->flags, FLAG_MEDIA_STATE_CHANGED);
621 	}
622 
623 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &state,
624 				 sizeof(state));
625 }
626 
media_state_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)627 static void media_state_cfg_changed(const struct bt_gatt_attr *attr,
628 				    uint16_t value)
629 {
630 	LOG_DBG("value 0x%04x", value);
631 }
632 
write_control_point(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)633 static ssize_t write_control_point(struct bt_conn *conn,
634 				   const struct bt_gatt_attr *attr,
635 				   const void *buf, uint16_t len, uint16_t offset,
636 				   uint8_t flags)
637 {
638 	struct mpl_cmd command;
639 
640 	if (offset != 0) {
641 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
642 	}
643 
644 	if (len != sizeof(command.opcode) &&
645 	    len != sizeof(command.opcode) + sizeof(command.param)) {
646 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
647 	}
648 
649 	memcpy(&command.opcode, buf, sizeof(command.opcode));
650 	LOG_DBG("Opcode: %d", command.opcode);
651 	command.use_param = false;
652 
653 	if (!BT_MCS_VALID_OP(command.opcode)) {
654 		/* MCS does not specify what to return in case of an error - Only what to notify*/
655 
656 		const struct mpl_cmd_ntf cmd_ntf = {
657 			.requested_opcode = command.opcode,
658 			.result_code = BT_MCS_OPC_NTF_NOT_SUPPORTED,
659 		};
660 
661 		LOG_DBG("Opcode 0x%02X is invalid", command.opcode);
662 
663 		notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &cmd_ntf, sizeof(cmd_ntf));
664 
665 		return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
666 	}
667 
668 	if (conn != NULL) {
669 		struct client_state *client = &clients[bt_conn_index(conn)];
670 
671 		if (atomic_test_and_set_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_BUSY)) {
672 			const struct mpl_cmd_ntf cmd_ntf = {
673 				.requested_opcode = command.opcode,
674 				.result_code = BT_MCS_OPC_NTF_CANNOT_BE_COMPLETED,
675 			};
676 
677 			LOG_DBG("Busy with other operation");
678 
679 			notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &cmd_ntf, sizeof(cmd_ntf));
680 
681 			return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
682 		}
683 	}
684 
685 	if (len == sizeof(command.opcode) + sizeof(command.param)) {
686 		command.param = sys_get_le32((char *)buf + sizeof(command.opcode));
687 		command.use_param = true;
688 		LOG_DBG("Parameter: %d", command.param);
689 	}
690 
691 	media_proxy_sctrl_send_command(&command);
692 
693 	return len;
694 }
695 
control_point_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)696 static void control_point_cfg_changed(const struct bt_gatt_attr *attr,
697 				      uint16_t value)
698 {
699 	LOG_DBG("value 0x%04x", value);
700 }
701 
read_opcodes_supported(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)702 static ssize_t read_opcodes_supported(struct bt_conn *conn,
703 				      const struct bt_gatt_attr *attr,
704 				      void *buf, uint16_t len, uint16_t offset)
705 {
706 	uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
707 	uint32_t opcodes_le = sys_cpu_to_le32(opcodes);
708 
709 	LOG_DBG("Opcodes_supported read: %d (0x%08x)", opcodes, opcodes);
710 
711 	if (conn != NULL) {
712 		struct client_state *client = &clients[bt_conn_index(conn)];
713 
714 		atomic_clear_bit(client->flags, FLAG_MEDIA_CONTROL_OPCODES_CHANGED);
715 	}
716 
717 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &opcodes_le, sizeof(opcodes_le));
718 }
719 
opcodes_supported_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)720 static void opcodes_supported_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
721 {
722 	LOG_DBG("value 0x%04x", value);
723 }
724 
725 #ifdef CONFIG_BT_OTS
write_search_control_point(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)726 static ssize_t write_search_control_point(struct bt_conn *conn, const struct bt_gatt_attr *attr,
727 					  const void *buf, uint16_t len, uint16_t offset,
728 					  uint8_t flags)
729 {
730 	struct mpl_search search = {0};
731 
732 	if (offset != 0) {
733 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
734 	}
735 
736 	if (len > SEARCH_LEN_MAX || len < SEARCH_LEN_MIN) {
737 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
738 	}
739 
740 	if (conn != NULL) {
741 		struct client_state *client = &clients[bt_conn_index(conn)];
742 
743 		if (atomic_test_and_set_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_BUSY)) {
744 			const uint8_t result_code = BT_MCS_SCP_NTF_FAILURE;
745 
746 			LOG_DBG("Busy with other operation");
747 
748 			notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code, sizeof(result_code));
749 
750 			return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
751 		}
752 	}
753 
754 	memcpy(&search.search, (char *)buf, len);
755 	search.len = len;
756 	LOG_DBG("Search length: %d", len);
757 	LOG_HEXDUMP_DBG(&search.search, search.len, "Search content");
758 
759 	media_proxy_sctrl_send_search(&search);
760 
761 	return len;
762 }
763 
search_control_point_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)764 static void search_control_point_cfg_changed(const struct bt_gatt_attr *attr,
765 					     uint16_t value)
766 {
767 	LOG_DBG("value 0x%04x", value);
768 }
769 
read_search_results_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)770 static ssize_t read_search_results_id(struct bt_conn *conn,
771 				      const struct bt_gatt_attr *attr,
772 				      void *buf, uint16_t len, uint16_t offset)
773 {
774 	uint64_t search_id = media_proxy_sctrl_get_search_results_id();
775 
776 	LOG_DBG_OBJ_ID("Search results id read: ", search_id);
777 
778 	if (conn != NULL) {
779 		struct client_state *client = &clients[bt_conn_index(conn)];
780 
781 		atomic_clear_bit(client->flags, FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED);
782 	}
783 
784 	/* TODO: The permanent solution here should be that the call to */
785 	/* mpl should fill the UUID in a pointed-to value, and return a */
786 	/* length or an error code, to indicate whether this ID has a */
787 	/* value now.  This should be done for all functions of this kind. */
788 	/* For now, fix the issue here - send zero-length data if the */
789 	/* ID is zero. */
790 	/* *Spec requirement - IDs may not be valid, in which case the */
791 	/* characteristic shall be zero length. */
792 
793 	if (search_id == 0) {
794 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
795 					 NULL, 0);
796 	} else {
797 		uint8_t search_id_le[BT_OTS_OBJ_ID_SIZE];
798 
799 		sys_put_le48(search_id, search_id_le);
800 
801 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
802 					 &search_id_le, sizeof(search_id_le));
803 	}
804 }
805 
search_results_id_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)806 static void search_results_id_cfg_changed(const struct bt_gatt_attr *attr,
807 					  uint16_t value)
808 {
809 	LOG_DBG("value 0x%04x", value);
810 }
811 #endif /* CONFIG_BT_OTS */
812 
read_content_ctrl_id(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)813 static ssize_t read_content_ctrl_id(struct bt_conn *conn,
814 				    const struct bt_gatt_attr *attr, void *buf,
815 				    uint16_t len, uint16_t offset)
816 {
817 	uint8_t id = media_proxy_sctrl_get_content_ctrl_id();
818 
819 	LOG_DBG("Content control ID read: %d", id);
820 
821 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &id,
822 				 sizeof(id));
823 }
824 
825 /* Defines for OTS-dependent characteristics - empty if no OTS */
826 #ifdef CONFIG_BT_OTS
827 #define ICON_OBJ_ID_CHARACTERISTIC_IF_OTS  \
828 	BT_AUDIO_CHRC(BT_UUID_MCS_ICON_OBJ_ID, \
829 		      BT_GATT_CHRC_READ, \
830 		      BT_GATT_PERM_READ_ENCRYPT, \
831 		      read_icon_id, NULL, NULL),
832 #define SEGMENTS_TRACK_GROUP_ID_CHARACTERISTICS_IF_OTS  \
833 	BT_AUDIO_CHRC(BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID,	\
834 		      BT_GATT_CHRC_READ, \
835 		      BT_GATT_PERM_READ_ENCRYPT, \
836 		      read_track_segments_id, NULL, NULL), \
837 	BT_AUDIO_CHRC(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, \
838 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
839 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
840 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
841 		      read_current_track_id, write_current_track_id, NULL), \
842 	BT_AUDIO_CCC(current_track_id_cfg_changed), \
843 	BT_AUDIO_CHRC(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, \
844 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
845 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
846 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
847 		      read_next_track_id, write_next_track_id, NULL), \
848 	BT_AUDIO_CCC(next_track_id_cfg_changed), \
849 	BT_AUDIO_CHRC(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, \
850 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
851 		      BT_GATT_PERM_READ_ENCRYPT, \
852 		      read_parent_group_id, NULL, NULL), \
853 	BT_AUDIO_CCC(parent_group_id_cfg_changed), \
854 	BT_AUDIO_CHRC(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, \
855 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
856 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
857 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
858 		      read_current_group_id, write_current_group_id, NULL), \
859 	BT_AUDIO_CCC(current_group_id_cfg_changed),
860 #define	SEARCH_CHARACTERISTICS_IF_OTS \
861 	BT_AUDIO_CHRC(BT_UUID_MCS_SEARCH_CONTROL_POINT, \
862 		      BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
863 		      BT_GATT_PERM_WRITE_ENCRYPT, \
864 		      NULL, write_search_control_point, NULL), \
865 	BT_AUDIO_CCC(search_control_point_cfg_changed), \
866 	BT_AUDIO_CHRC(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, \
867 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
868 		      BT_GATT_PERM_READ_ENCRYPT, \
869 		      read_search_results_id, NULL, NULL), \
870 	BT_AUDIO_CCC(search_results_id_cfg_changed),
871 
872 #else
873 #define ICON_OBJ_ID_CHARACTERISTIC_IF_OTS
874 #define SEGMENTS_TRACK_GROUP_ID_CHARACTERISTICS_IF_OTS
875 #define SEARCH_CHARACTERISTICS_IF_OTS
876 #endif /* CONFIG_BT_OTS */
877 
878 /* Media control service attributes */
879 #define BT_MCS_SERVICE_DEFINITION \
880 	BT_GATT_PRIMARY_SERVICE(BT_UUID_GMCS), \
881 	BT_GATT_INCLUDE_SERVICE(NULL), /* To be overwritten */ \
882 	BT_AUDIO_CHRC(BT_UUID_MCS_PLAYER_NAME, \
883 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
884 		      BT_GATT_PERM_READ_ENCRYPT, \
885 		      read_player_name, NULL, NULL), \
886 	BT_AUDIO_CCC(player_name_cfg_changed), \
887 	ICON_OBJ_ID_CHARACTERISTIC_IF_OTS \
888 	BT_AUDIO_CHRC(BT_UUID_MCS_ICON_URL, \
889 		      BT_GATT_CHRC_READ, \
890 		      BT_GATT_PERM_READ_ENCRYPT, \
891 		      read_icon_url, NULL, NULL), \
892 	BT_AUDIO_CHRC(BT_UUID_MCS_TRACK_CHANGED, \
893 		      BT_GATT_CHRC_NOTIFY, \
894 		      BT_GATT_PERM_NONE, \
895 		      NULL, NULL, NULL), \
896 	BT_AUDIO_CCC(track_cfg_changed), \
897 	BT_AUDIO_CHRC(BT_UUID_MCS_TRACK_TITLE, \
898 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
899 		      BT_GATT_PERM_READ_ENCRYPT, \
900 		      read_track_title, NULL, NULL), \
901 	BT_AUDIO_CCC(track_title_cfg_changed), \
902 	BT_AUDIO_CHRC(BT_UUID_MCS_TRACK_DURATION, \
903 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
904 		      BT_GATT_PERM_READ_ENCRYPT, \
905 		      read_track_duration, NULL, NULL), \
906 	BT_AUDIO_CCC(track_duration_cfg_changed), \
907 	BT_AUDIO_CHRC(BT_UUID_MCS_TRACK_POSITION, \
908 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
909 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
910 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
911 		      read_track_position, write_track_position, NULL), \
912 	BT_AUDIO_CCC(track_position_cfg_changed), \
913 	BT_AUDIO_CHRC(BT_UUID_MCS_PLAYBACK_SPEED, \
914 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
915 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
916 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
917 		      read_playback_speed, write_playback_speed, NULL), \
918 	BT_AUDIO_CCC(playback_speed_cfg_changed), \
919 	BT_AUDIO_CHRC(BT_UUID_MCS_SEEKING_SPEED, \
920 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
921 		      BT_GATT_PERM_READ_ENCRYPT, \
922 		      read_seeking_speed, NULL, NULL), \
923 	BT_AUDIO_CCC(seeking_speed_cfg_changed), \
924 	SEGMENTS_TRACK_GROUP_ID_CHARACTERISTICS_IF_OTS \
925 	BT_AUDIO_CHRC(BT_UUID_MCS_PLAYING_ORDER, \
926 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | \
927 		      BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
928 		      BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT, \
929 		      read_playing_order, write_playing_order, NULL), \
930 	BT_AUDIO_CCC(playing_order_cfg_changed), \
931 	BT_AUDIO_CHRC(BT_UUID_MCS_PLAYING_ORDERS, \
932 		      BT_GATT_CHRC_READ, \
933 		      BT_GATT_PERM_READ_ENCRYPT, \
934 		      read_playing_orders_supported, NULL, NULL), \
935 	BT_AUDIO_CHRC(BT_UUID_MCS_MEDIA_STATE, \
936 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
937 		      BT_GATT_PERM_READ_ENCRYPT, \
938 		      read_media_state, NULL, NULL), \
939 	BT_AUDIO_CCC(media_state_cfg_changed), \
940 	BT_AUDIO_CHRC(BT_UUID_MCS_MEDIA_CONTROL_POINT, \
941 		      BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY, \
942 		      BT_GATT_PERM_WRITE_ENCRYPT, \
943 		      NULL, write_control_point, NULL), \
944 	BT_AUDIO_CCC(control_point_cfg_changed), \
945 	BT_AUDIO_CHRC(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, \
946 		      BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
947 		      BT_GATT_PERM_READ_ENCRYPT, \
948 		      read_opcodes_supported, NULL, NULL), \
949 	BT_AUDIO_CCC(opcodes_supported_cfg_changed), \
950 	SEARCH_CHARACTERISTICS_IF_OTS \
951 	BT_AUDIO_CHRC(BT_UUID_CCID, \
952 		      BT_GATT_CHRC_READ, \
953 		      BT_GATT_PERM_READ_ENCRYPT, \
954 		      read_content_ctrl_id, NULL, NULL)
955 
956 static struct bt_gatt_attr svc_attrs[] = { BT_MCS_SERVICE_DEFINITION };
957 static struct bt_gatt_service mcs;
958 #ifdef CONFIG_BT_OTS
959 static struct bt_ots *ots;
960 #endif /* CONFIG_BT_OTS */
961 
962 #ifdef CONFIG_BT_OTS
bt_mcs_get_ots(void)963 struct bt_ots *bt_mcs_get_ots(void)
964 {
965 	return ots;
966 }
967 #endif /* CONFIG_BT_OTS */
968 
969 /* Callback functions from the media player, notifying attributes */
970 /* Placed here, after the service definition, because they reference it. */
971 
972 /* Helper function to notify non-string values */
notify(const struct bt_uuid * uuid,const void * data,uint16_t len)973 static void notify(const struct bt_uuid *uuid, const void *data, uint16_t len)
974 {
975 	int err = bt_gatt_notify_uuid(NULL, uuid, mcs.attrs, data, len);
976 
977 	if (err) {
978 		if (err == -ENOTCONN) {
979 			LOG_DBG("Notification error: ENOTCONN (%d)", err);
980 		} else {
981 			LOG_ERR("Notification error: %d", err);
982 		}
983 	}
984 }
985 
notify_string(struct bt_conn * conn,const struct bt_uuid * uuid,const char * str)986 static void notify_string(struct bt_conn *conn, const struct bt_uuid *uuid, const char *str)
987 {
988 	const uint8_t att_header_size = 3; /* opcode + handle */
989 	uint16_t att_mtu;
990 	uint16_t maxlen;
991 	int err;
992 
993 	att_mtu = bt_gatt_get_mtu(conn);
994 	__ASSERT(att_mtu > att_header_size, "Could not get valid ATT MTU");
995 	maxlen = att_mtu - att_header_size; /* Subtract opcode and handle */
996 
997 	/* Send notification potentially truncated to the MTU */
998 	err = bt_gatt_notify_uuid(conn, uuid, mcs.attrs, (void *)str,
999 				  MIN(strlen(str), maxlen));
1000 	if (err != 0) {
1001 		LOG_ERR("Notification error: %d", err);
1002 	}
1003 }
1004 
mark_icon_url_changed_cb(struct bt_conn * conn,void * data)1005 static void mark_icon_url_changed_cb(struct bt_conn *conn, void *data)
1006 {
1007 	struct client_state *client = &clients[bt_conn_index(conn)];
1008 	struct bt_conn_info info;
1009 	int err;
1010 
1011 	err = bt_conn_get_info(conn, &info);
1012 	if (err != 0) {
1013 		LOG_ERR("Failed to get conn info: %d", err);
1014 		return;
1015 	}
1016 
1017 	if (info.state != BT_CONN_STATE_CONNECTED) {
1018 		/* Not connected */
1019 		return;
1020 	}
1021 
1022 	atomic_set_bit(client->flags, FLAG_ICON_URL_CHANGED);
1023 }
1024 
notify_cb(struct bt_conn * conn,void * data)1025 static void notify_cb(struct bt_conn *conn, void *data)
1026 {
1027 	struct client_state *client = &clients[bt_conn_index(conn)];
1028 	struct bt_conn_info info;
1029 	int err;
1030 
1031 	err = bt_conn_get_info(conn, &info);
1032 	if (err != 0) {
1033 		LOG_ERR("Failed to get conn info: %d", err);
1034 		return;
1035 	}
1036 
1037 	if (info.state != BT_CONN_STATE_CONNECTED) {
1038 		/* Not connected */
1039 		return;
1040 	}
1041 
1042 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
1043 		const char *name = media_proxy_sctrl_get_player_name();
1044 
1045 		LOG_DBG("Notifying player name: %s", name);
1046 		notify_string(conn, BT_UUID_MCS_PLAYER_NAME, name);
1047 	}
1048 
1049 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
1050 		const char *title = media_proxy_sctrl_get_track_title();
1051 
1052 		LOG_DBG("Notifying track title: %s", title);
1053 		notify_string(conn, BT_UUID_MCS_TRACK_TITLE, title);
1054 	}
1055 
1056 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED)) {
1057 		int32_t duration = media_proxy_sctrl_get_track_duration();
1058 		int32_t duration_le = sys_cpu_to_le32(duration);
1059 
1060 		LOG_DBG("Notifying track duration: %d", duration);
1061 		notify(BT_UUID_MCS_TRACK_DURATION, &duration_le, sizeof(duration_le));
1062 	}
1063 
1064 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_POSITION_CHANGED)) {
1065 		int32_t position = media_proxy_sctrl_get_track_position();
1066 		int32_t position_le = sys_cpu_to_le32(position);
1067 
1068 		LOG_DBG("Notifying track position: %d", position);
1069 		notify(BT_UUID_MCS_TRACK_POSITION, &position_le, sizeof(position_le));
1070 	}
1071 
1072 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYBACK_SPEED_CHANGED)) {
1073 		int8_t speed = media_proxy_sctrl_get_playback_speed();
1074 
1075 		LOG_DBG("Notifying playback speed: %d", speed);
1076 		notify(BT_UUID_MCS_PLAYBACK_SPEED, &speed, sizeof(speed));
1077 	}
1078 
1079 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEEKING_SPEED_CHANGED)) {
1080 		int8_t speed = media_proxy_sctrl_get_seeking_speed();
1081 
1082 		LOG_DBG("Notifying seeking speed: %d", speed);
1083 		notify(BT_UUID_MCS_SEEKING_SPEED, &speed, sizeof(speed));
1084 	}
1085 
1086 #if defined(CONFIG_BT_OTS)
1087 	if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_TRACK_OBJ_ID_CHANGED)) {
1088 		uint64_t track_id = media_proxy_sctrl_get_current_track_id();
1089 		uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
1090 
1091 		sys_put_le48(track_id, track_id_le);
1092 
1093 		LOG_DBG_OBJ_ID("Notifying current track ID: ", track_id);
1094 		notify(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, track_id_le, sizeof(track_id_le));
1095 	}
1096 
1097 	if (atomic_test_and_clear_bit(client->flags, FLAG_NEXT_TRACK_OBJ_ID_CHANGED)) {
1098 		uint64_t track_id = media_proxy_sctrl_get_next_track_id();
1099 
1100 		if (track_id == MPL_NO_TRACK_ID) {
1101 			/* "If the media player has no next track, the length of the
1102 			 * characteristic shall be zero."
1103 			 */
1104 			LOG_DBG_OBJ_ID("Notifying EMPTY next track ID: ", track_id);
1105 			notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, NULL, 0);
1106 		} else {
1107 			uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
1108 
1109 			sys_put_le48(track_id, track_id_le);
1110 
1111 			LOG_DBG_OBJ_ID("Notifying next track ID: ", track_id);
1112 			notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, track_id_le, sizeof(track_id_le));
1113 		}
1114 	}
1115 
1116 	if (atomic_test_and_clear_bit(client->flags, FLAG_PARENT_GROUP_OBJ_ID_CHANGED)) {
1117 		uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
1118 		uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
1119 
1120 		sys_put_le48(group_id, group_id_le);
1121 
1122 		LOG_DBG_OBJ_ID("Notifying parent group ID: ", group_id);
1123 		notify(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, &group_id_le, sizeof(group_id_le));
1124 	}
1125 
1126 	if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_GROUP_OBJ_ID_CHANGED)) {
1127 		uint64_t group_id = media_proxy_sctrl_get_current_group_id();
1128 		uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
1129 
1130 		sys_put_le48(group_id, group_id_le);
1131 
1132 		LOG_DBG_OBJ_ID("Notifying current group ID: ", group_id);
1133 		notify(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, &group_id_le, sizeof(group_id_le));
1134 	}
1135 #endif /* CONFIG_BT_OTS */
1136 
1137 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_CHANGED)) {
1138 		LOG_DBG("Notifying track change");
1139 		notify(BT_UUID_MCS_TRACK_CHANGED, NULL, 0);
1140 	}
1141 
1142 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYING_ORDER_CHANGED)) {
1143 		uint8_t order = media_proxy_sctrl_get_playing_order();
1144 
1145 		LOG_DBG("Notifying playing order: %d", order);
1146 		notify(BT_UUID_MCS_PLAYING_ORDER, &order, sizeof(order));
1147 	}
1148 
1149 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_STATE_CHANGED)) {
1150 		uint8_t state = media_proxy_sctrl_get_media_state();
1151 
1152 		LOG_DBG("Notifying media state: %d", state);
1153 		notify(BT_UUID_MCS_MEDIA_STATE, &state, sizeof(state));
1154 	}
1155 
1156 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_OPCODES_CHANGED)) {
1157 		uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
1158 		uint32_t opcodes_le = sys_cpu_to_le32(opcodes);
1159 
1160 		LOG_DBG("Notifying command opcodes supported: %d (0x%08x)", opcodes, opcodes);
1161 		notify(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, &opcodes_le, sizeof(opcodes_le));
1162 	}
1163 
1164 #if defined(CONFIG_BT_OTS)
1165 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED)) {
1166 		uint64_t search_id = media_proxy_sctrl_get_search_results_id();
1167 		uint8_t search_id_le[BT_OTS_OBJ_ID_SIZE];
1168 
1169 		sys_put_le48(search_id, search_id_le);
1170 
1171 		LOG_DBG_OBJ_ID("Notifying search results ID: ", search_id);
1172 		notify(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, &search_id_le, sizeof(search_id_le));
1173 	}
1174 
1175 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT)) {
1176 		uint8_t result_code = client->search_control_point_result;
1177 
1178 		LOG_DBG("Notifying search control point - result: %d", result_code);
1179 		notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code, sizeof(result_code));
1180 	}
1181 #endif /* CONFIG_BT_OTS */
1182 
1183 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT)) {
1184 		LOG_DBG("Notifying control point command - opcode: %d, result: %d",
1185 			client->cmd_ntf.requested_opcode, client->cmd_ntf.result_code);
1186 		notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &client->cmd_ntf, sizeof(client->cmd_ntf));
1187 	}
1188 }
1189 
deferred_nfy_work_handler(struct k_work * work)1190 static void deferred_nfy_work_handler(struct k_work *work)
1191 {
1192 	bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
1193 }
1194 
1195 static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
1196 
defer_value_ntf(struct bt_conn * conn,void * data)1197 static void defer_value_ntf(struct bt_conn *conn, void *data)
1198 {
1199 	struct client_state *client = &clients[bt_conn_index(conn)];
1200 	struct bt_conn_info info;
1201 	int err;
1202 
1203 	err = bt_conn_get_info(conn, &info);
1204 	if (err != 0) {
1205 		LOG_ERR("Failed to get conn info: %d", err);
1206 		return;
1207 	}
1208 
1209 	if (info.state != BT_CONN_STATE_CONNECTED) {
1210 		/* Not connected */
1211 		return;
1212 	}
1213 
1214 	atomic_set_bit(client->flags, POINTER_TO_UINT(data));
1215 	k_work_submit(&deferred_nfy_work);
1216 }
1217 
media_proxy_sctrl_player_name_cb(const char * name)1218 static void media_proxy_sctrl_player_name_cb(const char *name)
1219 {
1220 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1221 			UINT_TO_POINTER(FLAG_PLAYER_NAME_CHANGED));
1222 }
1223 
media_proxy_sctrl_icon_url_cb(const char * name)1224 void media_proxy_sctrl_icon_url_cb(const char *name)
1225 {
1226 	bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
1227 }
1228 
media_proxy_sctrl_track_changed_cb(void)1229 void media_proxy_sctrl_track_changed_cb(void)
1230 {
1231 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1232 			UINT_TO_POINTER(FLAG_TRACK_CHANGED));
1233 }
1234 
media_proxy_sctrl_track_title_cb(const char * title)1235 void media_proxy_sctrl_track_title_cb(const char *title)
1236 {
1237 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1238 			UINT_TO_POINTER(FLAG_TRACK_TITLE_CHANGED));
1239 }
1240 
media_proxy_sctrl_track_position_cb(int32_t position)1241 void media_proxy_sctrl_track_position_cb(int32_t position)
1242 {
1243 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1244 			UINT_TO_POINTER(FLAG_TRACK_POSITION_CHANGED));
1245 }
1246 
media_proxy_sctrl_track_duration_cb(int32_t duration)1247 void media_proxy_sctrl_track_duration_cb(int32_t duration)
1248 {
1249 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1250 			UINT_TO_POINTER(FLAG_TRACK_DURATION_CHANGED));
1251 }
1252 
media_proxy_sctrl_playback_speed_cb(int8_t speed)1253 void media_proxy_sctrl_playback_speed_cb(int8_t speed)
1254 {
1255 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1256 			UINT_TO_POINTER(FLAG_PLAYBACK_SPEED_CHANGED));
1257 }
1258 
media_proxy_sctrl_seeking_speed_cb(int8_t speed)1259 void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
1260 {
1261 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1262 			UINT_TO_POINTER(FLAG_SEEKING_SPEED_CHANGED));
1263 }
1264 
1265 #if defined(CONFIG_BT_OTS)
media_proxy_sctrl_current_track_id_cb(uint64_t id)1266 void media_proxy_sctrl_current_track_id_cb(uint64_t id)
1267 {
1268 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1269 			UINT_TO_POINTER(FLAG_CURRENT_TRACK_OBJ_ID_CHANGED));
1270 }
1271 
media_proxy_sctrl_next_track_id_cb(uint64_t id)1272 void media_proxy_sctrl_next_track_id_cb(uint64_t id)
1273 {
1274 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1275 			UINT_TO_POINTER(FLAG_NEXT_TRACK_OBJ_ID_CHANGED));
1276 }
1277 
media_proxy_sctrl_parent_group_id_cb(uint64_t id)1278 void media_proxy_sctrl_parent_group_id_cb(uint64_t id)
1279 {
1280 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1281 			UINT_TO_POINTER(FLAG_PARENT_GROUP_OBJ_ID_CHANGED));
1282 }
1283 
media_proxy_sctrl_current_group_id_cb(uint64_t id)1284 void media_proxy_sctrl_current_group_id_cb(uint64_t id)
1285 {
1286 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1287 			UINT_TO_POINTER(FLAG_CURRENT_GROUP_OBJ_ID_CHANGED));
1288 }
1289 #endif /* CONFIG_BT_OTS */
1290 
media_proxy_sctrl_playing_order_cb(uint8_t order)1291 void media_proxy_sctrl_playing_order_cb(uint8_t order)
1292 {
1293 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1294 			UINT_TO_POINTER(FLAG_PLAYING_ORDER_CHANGED));
1295 }
1296 
media_proxy_sctrl_media_state_cb(uint8_t state)1297 void media_proxy_sctrl_media_state_cb(uint8_t state)
1298 {
1299 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1300 			UINT_TO_POINTER(FLAG_MEDIA_STATE_CHANGED));
1301 }
1302 
defer_media_control_point_ntf(struct bt_conn * conn,void * data)1303 static void defer_media_control_point_ntf(struct bt_conn *conn, void *data)
1304 {
1305 	struct client_state *client = &clients[bt_conn_index(conn)];
1306 	const struct mpl_cmd_ntf *cmd_ntf = data;
1307 	struct bt_conn_info info;
1308 	int err;
1309 
1310 	err = bt_conn_get_info(conn, &info);
1311 	if (err != 0) {
1312 		LOG_ERR("Failed to get conn info: %d", err);
1313 		return;
1314 	}
1315 
1316 	if (info.state != BT_CONN_STATE_CONNECTED) {
1317 		/* Not connected */
1318 		return;
1319 	}
1320 
1321 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_BUSY)) {
1322 		client->cmd_ntf = *cmd_ntf;
1323 		atomic_set_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT);
1324 		k_work_submit(&deferred_nfy_work);
1325 	}
1326 }
1327 
media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf * cmd_ntf)1328 void media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf *cmd_ntf)
1329 {
1330 	/* FIXME: Control Point notification shall be sent to operation initiator only */
1331 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_media_control_point_ntf, (void *)cmd_ntf);
1332 }
1333 
media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)1334 void media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)
1335 {
1336 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1337 			UINT_TO_POINTER(FLAG_MEDIA_CONTROL_OPCODES_CHANGED));
1338 }
1339 
1340 #if defined(CONFIG_BT_OTS)
defer_search_control_point_ntf(struct bt_conn * conn,void * data)1341 static void defer_search_control_point_ntf(struct bt_conn *conn, void *data)
1342 {
1343 	struct client_state *client = &clients[bt_conn_index(conn)];
1344 	struct bt_conn_info info;
1345 	int err;
1346 
1347 	err = bt_conn_get_info(conn, &info);
1348 	if (err != 0) {
1349 		LOG_ERR("Failed to get conn info: %d", err);
1350 		return;
1351 	}
1352 
1353 	if (info.state != BT_CONN_STATE_CONNECTED) {
1354 		/* Not connected */
1355 		return;
1356 	}
1357 
1358 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_BUSY)) {
1359 		client->search_control_point_result = POINTER_TO_UINT(data);
1360 		atomic_set_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT);
1361 		k_work_submit(&deferred_nfy_work);
1362 	}
1363 }
1364 
media_proxy_sctrl_search_cb(uint8_t result_code)1365 void media_proxy_sctrl_search_cb(uint8_t result_code)
1366 {
1367 	/* FIXME: Control Point notification shall be sent to operation initiator only */
1368 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_search_control_point_ntf,
1369 			UINT_TO_POINTER(result_code));
1370 }
1371 
media_proxy_sctrl_search_results_id_cb(uint64_t id)1372 void media_proxy_sctrl_search_results_id_cb(uint64_t id)
1373 {
1374 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1375 			UINT_TO_POINTER(FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED));
1376 }
1377 #endif /* CONFIG_BT_OTS */
1378 
1379 /* Register the service */
bt_mcs_init(struct bt_ots_cb * ots_cbs)1380 int bt_mcs_init(struct bt_ots_cb *ots_cbs)
1381 {
1382 	static bool initialized;
1383 	int err;
1384 
1385 	if (initialized) {
1386 		LOG_DBG("Already initialized");
1387 		return -EALREADY;
1388 	}
1389 
1390 
1391 	mcs = (struct bt_gatt_service)BT_GATT_SERVICE(svc_attrs);
1392 
1393 #ifdef CONFIG_BT_OTS
1394 	struct bt_ots_init_param ots_init;
1395 
1396 	ots = bt_ots_free_instance_get();
1397 	if (!ots) {
1398 		LOG_ERR("Failed to retrieve OTS instance\n");
1399 		return -ENOMEM;
1400 	}
1401 
1402 	/* Configure OTS initialization. */
1403 	memset(&ots_init, 0, sizeof(ots_init));
1404 	BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp);
1405 	BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp);
1406 	ots_init.cb = ots_cbs;
1407 
1408 	/* Initialize OTS instance. */
1409 	err = bt_ots_init(ots, &ots_init);
1410 	if (err) {
1411 		LOG_ERR("Failed to init OTS (err:%d)\n", err);
1412 		return err;
1413 	}
1414 
1415 	/* TODO: Maybe the user_data pointer can be in a different way */
1416 	for (int i = 0; i < mcs.attr_count; i++) {
1417 		if (!bt_uuid_cmp(mcs.attrs[i].uuid, BT_UUID_GATT_INCLUDE)) {
1418 			mcs.attrs[i].user_data = bt_ots_svc_decl_get(ots);
1419 		}
1420 	}
1421 #endif /* CONFIG_BT_OTS */
1422 
1423 	err = bt_gatt_service_register(&mcs);
1424 
1425 	if (err) {
1426 		LOG_ERR("Could not register the MCS service");
1427 #ifdef CONFIG_BT_OTS
1428 		/* TODO: How does one un-register the OTS? */
1429 #endif /* CONFIG_BT_OTS */
1430 		return -ENOEXEC;
1431 	}
1432 
1433 	/* Set up the callback structure */
1434 	cbs.player_name          = media_proxy_sctrl_player_name_cb;
1435 	cbs.icon_url             = media_proxy_sctrl_icon_url_cb;
1436 	cbs.track_changed        = media_proxy_sctrl_track_changed_cb;
1437 	cbs.track_title          = media_proxy_sctrl_track_title_cb;
1438 	cbs.track_duration       = media_proxy_sctrl_track_duration_cb;
1439 	cbs.track_position       = media_proxy_sctrl_track_position_cb;
1440 	cbs.playback_speed       = media_proxy_sctrl_playback_speed_cb;
1441 	cbs.seeking_speed        = media_proxy_sctrl_seeking_speed_cb;
1442 #ifdef CONFIG_BT_OTS
1443 	cbs.current_track_id     = media_proxy_sctrl_current_track_id_cb;
1444 	cbs.next_track_id        = media_proxy_sctrl_next_track_id_cb;
1445 	cbs.parent_group_id      = media_proxy_sctrl_parent_group_id_cb;
1446 	cbs.current_group_id     = media_proxy_sctrl_current_group_id_cb;
1447 #endif /* CONFIG_BT_OTS */
1448 	cbs.playing_order        = media_proxy_sctrl_playing_order_cb;
1449 	cbs.media_state          = media_proxy_sctrl_media_state_cb;
1450 	cbs.command              = media_proxy_sctrl_command_cb;
1451 	cbs.commands_supported   = media_proxy_sctrl_commands_supported_cb;
1452 #ifdef CONFIG_BT_OTS
1453 	cbs.search               = media_proxy_sctrl_search_cb;
1454 	cbs.search_results_id    = media_proxy_sctrl_search_results_id_cb;
1455 #endif /* CONFIG_BT_OTS */
1456 
1457 	media_proxy_sctrl_register(&cbs);
1458 
1459 	initialized = true;
1460 	return 0;
1461 }
1462