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 uint16_t max_ntf_size = bt_audio_get_max_ntf_size(conn);
989 	int err;
990 
991 	/* Send notification potentially truncated to the MTU */
992 	err = bt_gatt_notify_uuid(conn, uuid, mcs.attrs, (void *)str,
993 				  MIN(strlen(str), max_ntf_size));
994 	if (err != 0) {
995 		LOG_ERR("Notification error: %d", err);
996 	}
997 }
998 
mark_icon_url_changed_cb(struct bt_conn * conn,void * data)999 static void mark_icon_url_changed_cb(struct bt_conn *conn, void *data)
1000 {
1001 	struct client_state *client = &clients[bt_conn_index(conn)];
1002 	struct bt_conn_info info;
1003 	int err;
1004 
1005 	err = bt_conn_get_info(conn, &info);
1006 	if (err != 0) {
1007 		LOG_ERR("Failed to get conn info: %d", err);
1008 		return;
1009 	}
1010 
1011 	if (info.state != BT_CONN_STATE_CONNECTED) {
1012 		/* Not connected */
1013 		return;
1014 	}
1015 
1016 	atomic_set_bit(client->flags, FLAG_ICON_URL_CHANGED);
1017 }
1018 
notify_cb(struct bt_conn * conn,void * data)1019 static void notify_cb(struct bt_conn *conn, void *data)
1020 {
1021 	struct client_state *client = &clients[bt_conn_index(conn)];
1022 	struct bt_conn_info info;
1023 	int err;
1024 
1025 	err = bt_conn_get_info(conn, &info);
1026 	if (err != 0) {
1027 		LOG_ERR("Failed to get conn info: %d", err);
1028 		return;
1029 	}
1030 
1031 	if (info.state != BT_CONN_STATE_CONNECTED) {
1032 		/* Not connected */
1033 		return;
1034 	}
1035 
1036 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYER_NAME_CHANGED)) {
1037 		const char *name = media_proxy_sctrl_get_player_name();
1038 
1039 		LOG_DBG("Notifying player name: %s", name);
1040 		notify_string(conn, BT_UUID_MCS_PLAYER_NAME, name);
1041 	}
1042 
1043 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_TITLE_CHANGED)) {
1044 		const char *title = media_proxy_sctrl_get_track_title();
1045 
1046 		LOG_DBG("Notifying track title: %s", title);
1047 		notify_string(conn, BT_UUID_MCS_TRACK_TITLE, title);
1048 	}
1049 
1050 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_DURATION_CHANGED)) {
1051 		int32_t duration = media_proxy_sctrl_get_track_duration();
1052 		int32_t duration_le = sys_cpu_to_le32(duration);
1053 
1054 		LOG_DBG("Notifying track duration: %d", duration);
1055 		notify(BT_UUID_MCS_TRACK_DURATION, &duration_le, sizeof(duration_le));
1056 	}
1057 
1058 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_POSITION_CHANGED)) {
1059 		int32_t position = media_proxy_sctrl_get_track_position();
1060 		int32_t position_le = sys_cpu_to_le32(position);
1061 
1062 		LOG_DBG("Notifying track position: %d", position);
1063 		notify(BT_UUID_MCS_TRACK_POSITION, &position_le, sizeof(position_le));
1064 	}
1065 
1066 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYBACK_SPEED_CHANGED)) {
1067 		int8_t speed = media_proxy_sctrl_get_playback_speed();
1068 
1069 		LOG_DBG("Notifying playback speed: %d", speed);
1070 		notify(BT_UUID_MCS_PLAYBACK_SPEED, &speed, sizeof(speed));
1071 	}
1072 
1073 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEEKING_SPEED_CHANGED)) {
1074 		int8_t speed = media_proxy_sctrl_get_seeking_speed();
1075 
1076 		LOG_DBG("Notifying seeking speed: %d", speed);
1077 		notify(BT_UUID_MCS_SEEKING_SPEED, &speed, sizeof(speed));
1078 	}
1079 
1080 #if defined(CONFIG_BT_OTS)
1081 	if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_TRACK_OBJ_ID_CHANGED)) {
1082 		uint64_t track_id = media_proxy_sctrl_get_current_track_id();
1083 		uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
1084 
1085 		sys_put_le48(track_id, track_id_le);
1086 
1087 		LOG_DBG_OBJ_ID("Notifying current track ID: ", track_id);
1088 		notify(BT_UUID_MCS_CURRENT_TRACK_OBJ_ID, track_id_le, sizeof(track_id_le));
1089 	}
1090 
1091 	if (atomic_test_and_clear_bit(client->flags, FLAG_NEXT_TRACK_OBJ_ID_CHANGED)) {
1092 		uint64_t track_id = media_proxy_sctrl_get_next_track_id();
1093 
1094 		if (track_id == MPL_NO_TRACK_ID) {
1095 			/* "If the media player has no next track, the length of the
1096 			 * characteristic shall be zero."
1097 			 */
1098 			LOG_DBG_OBJ_ID("Notifying EMPTY next track ID: ", track_id);
1099 			notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, NULL, 0);
1100 		} else {
1101 			uint8_t track_id_le[BT_OTS_OBJ_ID_SIZE];
1102 
1103 			sys_put_le48(track_id, track_id_le);
1104 
1105 			LOG_DBG_OBJ_ID("Notifying next track ID: ", track_id);
1106 			notify(BT_UUID_MCS_NEXT_TRACK_OBJ_ID, track_id_le, sizeof(track_id_le));
1107 		}
1108 	}
1109 
1110 	if (atomic_test_and_clear_bit(client->flags, FLAG_PARENT_GROUP_OBJ_ID_CHANGED)) {
1111 		uint64_t group_id = media_proxy_sctrl_get_parent_group_id();
1112 		uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
1113 
1114 		sys_put_le48(group_id, group_id_le);
1115 
1116 		LOG_DBG_OBJ_ID("Notifying parent group ID: ", group_id);
1117 		notify(BT_UUID_MCS_PARENT_GROUP_OBJ_ID, &group_id_le, sizeof(group_id_le));
1118 	}
1119 
1120 	if (atomic_test_and_clear_bit(client->flags, FLAG_CURRENT_GROUP_OBJ_ID_CHANGED)) {
1121 		uint64_t group_id = media_proxy_sctrl_get_current_group_id();
1122 		uint8_t group_id_le[BT_OTS_OBJ_ID_SIZE];
1123 
1124 		sys_put_le48(group_id, group_id_le);
1125 
1126 		LOG_DBG_OBJ_ID("Notifying current group ID: ", group_id);
1127 		notify(BT_UUID_MCS_CURRENT_GROUP_OBJ_ID, &group_id_le, sizeof(group_id_le));
1128 	}
1129 #endif /* CONFIG_BT_OTS */
1130 
1131 	if (atomic_test_and_clear_bit(client->flags, FLAG_TRACK_CHANGED)) {
1132 		LOG_DBG("Notifying track change");
1133 		notify(BT_UUID_MCS_TRACK_CHANGED, NULL, 0);
1134 	}
1135 
1136 	if (atomic_test_and_clear_bit(client->flags, FLAG_PLAYING_ORDER_CHANGED)) {
1137 		uint8_t order = media_proxy_sctrl_get_playing_order();
1138 
1139 		LOG_DBG("Notifying playing order: %d", order);
1140 		notify(BT_UUID_MCS_PLAYING_ORDER, &order, sizeof(order));
1141 	}
1142 
1143 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_STATE_CHANGED)) {
1144 		uint8_t state = media_proxy_sctrl_get_media_state();
1145 
1146 		LOG_DBG("Notifying media state: %d", state);
1147 		notify(BT_UUID_MCS_MEDIA_STATE, &state, sizeof(state));
1148 	}
1149 
1150 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_OPCODES_CHANGED)) {
1151 		uint32_t opcodes = media_proxy_sctrl_get_commands_supported();
1152 		uint32_t opcodes_le = sys_cpu_to_le32(opcodes);
1153 
1154 		LOG_DBG("Notifying command opcodes supported: %d (0x%08x)", opcodes, opcodes);
1155 		notify(BT_UUID_MCS_MEDIA_CONTROL_OPCODES, &opcodes_le, sizeof(opcodes_le));
1156 	}
1157 
1158 #if defined(CONFIG_BT_OTS)
1159 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED)) {
1160 		uint64_t search_id = media_proxy_sctrl_get_search_results_id();
1161 		uint8_t search_id_le[BT_OTS_OBJ_ID_SIZE];
1162 
1163 		sys_put_le48(search_id, search_id_le);
1164 
1165 		LOG_DBG_OBJ_ID("Notifying search results ID: ", search_id);
1166 		notify(BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID, &search_id_le, sizeof(search_id_le));
1167 	}
1168 
1169 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT)) {
1170 		uint8_t result_code = client->search_control_point_result;
1171 
1172 		LOG_DBG("Notifying search control point - result: %d", result_code);
1173 		notify(BT_UUID_MCS_SEARCH_CONTROL_POINT, &result_code, sizeof(result_code));
1174 	}
1175 #endif /* CONFIG_BT_OTS */
1176 
1177 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT)) {
1178 		LOG_DBG("Notifying control point command - opcode: %d, result: %d",
1179 			client->cmd_ntf.requested_opcode, client->cmd_ntf.result_code);
1180 		notify(BT_UUID_MCS_MEDIA_CONTROL_POINT, &client->cmd_ntf, sizeof(client->cmd_ntf));
1181 	}
1182 }
1183 
deferred_nfy_work_handler(struct k_work * work)1184 static void deferred_nfy_work_handler(struct k_work *work)
1185 {
1186 	bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
1187 }
1188 
1189 static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
1190 
defer_value_ntf(struct bt_conn * conn,void * data)1191 static void defer_value_ntf(struct bt_conn *conn, void *data)
1192 {
1193 	struct client_state *client = &clients[bt_conn_index(conn)];
1194 	struct bt_conn_info info;
1195 	int err;
1196 
1197 	err = bt_conn_get_info(conn, &info);
1198 	if (err != 0) {
1199 		LOG_ERR("Failed to get conn info: %d", err);
1200 		return;
1201 	}
1202 
1203 	if (info.state != BT_CONN_STATE_CONNECTED) {
1204 		/* Not connected */
1205 		return;
1206 	}
1207 
1208 	atomic_set_bit(client->flags, POINTER_TO_UINT(data));
1209 	k_work_submit(&deferred_nfy_work);
1210 }
1211 
media_proxy_sctrl_player_name_cb(const char * name)1212 static void media_proxy_sctrl_player_name_cb(const char *name)
1213 {
1214 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1215 			UINT_TO_POINTER(FLAG_PLAYER_NAME_CHANGED));
1216 }
1217 
media_proxy_sctrl_icon_url_cb(const char * name)1218 void media_proxy_sctrl_icon_url_cb(const char *name)
1219 {
1220 	bt_conn_foreach(BT_CONN_TYPE_LE, mark_icon_url_changed_cb, NULL);
1221 }
1222 
media_proxy_sctrl_track_changed_cb(void)1223 void media_proxy_sctrl_track_changed_cb(void)
1224 {
1225 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1226 			UINT_TO_POINTER(FLAG_TRACK_CHANGED));
1227 }
1228 
media_proxy_sctrl_track_title_cb(const char * title)1229 void media_proxy_sctrl_track_title_cb(const char *title)
1230 {
1231 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1232 			UINT_TO_POINTER(FLAG_TRACK_TITLE_CHANGED));
1233 }
1234 
media_proxy_sctrl_track_position_cb(int32_t position)1235 void media_proxy_sctrl_track_position_cb(int32_t position)
1236 {
1237 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1238 			UINT_TO_POINTER(FLAG_TRACK_POSITION_CHANGED));
1239 }
1240 
media_proxy_sctrl_track_duration_cb(int32_t duration)1241 void media_proxy_sctrl_track_duration_cb(int32_t duration)
1242 {
1243 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1244 			UINT_TO_POINTER(FLAG_TRACK_DURATION_CHANGED));
1245 }
1246 
media_proxy_sctrl_playback_speed_cb(int8_t speed)1247 void media_proxy_sctrl_playback_speed_cb(int8_t speed)
1248 {
1249 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1250 			UINT_TO_POINTER(FLAG_PLAYBACK_SPEED_CHANGED));
1251 }
1252 
media_proxy_sctrl_seeking_speed_cb(int8_t speed)1253 void media_proxy_sctrl_seeking_speed_cb(int8_t speed)
1254 {
1255 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1256 			UINT_TO_POINTER(FLAG_SEEKING_SPEED_CHANGED));
1257 }
1258 
1259 #if defined(CONFIG_BT_OTS)
media_proxy_sctrl_current_track_id_cb(uint64_t id)1260 void media_proxy_sctrl_current_track_id_cb(uint64_t id)
1261 {
1262 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1263 			UINT_TO_POINTER(FLAG_CURRENT_TRACK_OBJ_ID_CHANGED));
1264 }
1265 
media_proxy_sctrl_next_track_id_cb(uint64_t id)1266 void media_proxy_sctrl_next_track_id_cb(uint64_t id)
1267 {
1268 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1269 			UINT_TO_POINTER(FLAG_NEXT_TRACK_OBJ_ID_CHANGED));
1270 }
1271 
media_proxy_sctrl_parent_group_id_cb(uint64_t id)1272 void media_proxy_sctrl_parent_group_id_cb(uint64_t id)
1273 {
1274 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1275 			UINT_TO_POINTER(FLAG_PARENT_GROUP_OBJ_ID_CHANGED));
1276 }
1277 
media_proxy_sctrl_current_group_id_cb(uint64_t id)1278 void media_proxy_sctrl_current_group_id_cb(uint64_t id)
1279 {
1280 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1281 			UINT_TO_POINTER(FLAG_CURRENT_GROUP_OBJ_ID_CHANGED));
1282 }
1283 #endif /* CONFIG_BT_OTS */
1284 
media_proxy_sctrl_playing_order_cb(uint8_t order)1285 void media_proxy_sctrl_playing_order_cb(uint8_t order)
1286 {
1287 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1288 			UINT_TO_POINTER(FLAG_PLAYING_ORDER_CHANGED));
1289 }
1290 
media_proxy_sctrl_media_state_cb(uint8_t state)1291 void media_proxy_sctrl_media_state_cb(uint8_t state)
1292 {
1293 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1294 			UINT_TO_POINTER(FLAG_MEDIA_STATE_CHANGED));
1295 }
1296 
defer_media_control_point_ntf(struct bt_conn * conn,void * data)1297 static void defer_media_control_point_ntf(struct bt_conn *conn, void *data)
1298 {
1299 	struct client_state *client = &clients[bt_conn_index(conn)];
1300 	const struct mpl_cmd_ntf *cmd_ntf = data;
1301 	struct bt_conn_info info;
1302 	int err;
1303 
1304 	err = bt_conn_get_info(conn, &info);
1305 	if (err != 0) {
1306 		LOG_ERR("Failed to get conn info: %d", err);
1307 		return;
1308 	}
1309 
1310 	if (info.state != BT_CONN_STATE_CONNECTED) {
1311 		/* Not connected */
1312 		return;
1313 	}
1314 
1315 	if (atomic_test_and_clear_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_BUSY)) {
1316 		client->cmd_ntf = *cmd_ntf;
1317 		atomic_set_bit(client->flags, FLAG_MEDIA_CONTROL_POINT_RESULT);
1318 		k_work_submit(&deferred_nfy_work);
1319 	}
1320 }
1321 
media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf * cmd_ntf)1322 void media_proxy_sctrl_command_cb(const struct mpl_cmd_ntf *cmd_ntf)
1323 {
1324 	/* FIXME: Control Point notification shall be sent to operation initiator only */
1325 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_media_control_point_ntf, (void *)cmd_ntf);
1326 }
1327 
media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)1328 void media_proxy_sctrl_commands_supported_cb(uint32_t opcodes)
1329 {
1330 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1331 			UINT_TO_POINTER(FLAG_MEDIA_CONTROL_OPCODES_CHANGED));
1332 }
1333 
1334 #if defined(CONFIG_BT_OTS)
defer_search_control_point_ntf(struct bt_conn * conn,void * data)1335 static void defer_search_control_point_ntf(struct bt_conn *conn, void *data)
1336 {
1337 	struct client_state *client = &clients[bt_conn_index(conn)];
1338 	struct bt_conn_info info;
1339 	int err;
1340 
1341 	err = bt_conn_get_info(conn, &info);
1342 	if (err != 0) {
1343 		LOG_ERR("Failed to get conn info: %d", err);
1344 		return;
1345 	}
1346 
1347 	if (info.state != BT_CONN_STATE_CONNECTED) {
1348 		/* Not connected */
1349 		return;
1350 	}
1351 
1352 	if (atomic_test_and_clear_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_BUSY)) {
1353 		client->search_control_point_result = POINTER_TO_UINT(data);
1354 		atomic_set_bit(client->flags, FLAG_SEARCH_CONTROL_POINT_RESULT);
1355 		k_work_submit(&deferred_nfy_work);
1356 	}
1357 }
1358 
media_proxy_sctrl_search_cb(uint8_t result_code)1359 void media_proxy_sctrl_search_cb(uint8_t result_code)
1360 {
1361 	/* FIXME: Control Point notification shall be sent to operation initiator only */
1362 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_search_control_point_ntf,
1363 			UINT_TO_POINTER(result_code));
1364 }
1365 
media_proxy_sctrl_search_results_id_cb(uint64_t id)1366 void media_proxy_sctrl_search_results_id_cb(uint64_t id)
1367 {
1368 	bt_conn_foreach(BT_CONN_TYPE_LE, defer_value_ntf,
1369 			UINT_TO_POINTER(FLAG_SEARCH_RESULTS_OBJ_ID_CHANGED));
1370 }
1371 #endif /* CONFIG_BT_OTS */
1372 
1373 /* Register the service */
bt_mcs_init(struct bt_ots_cb * ots_cbs)1374 int bt_mcs_init(struct bt_ots_cb *ots_cbs)
1375 {
1376 	static bool initialized;
1377 	int err;
1378 
1379 	if (initialized) {
1380 		LOG_DBG("Already initialized");
1381 		return -EALREADY;
1382 	}
1383 
1384 
1385 	mcs = (struct bt_gatt_service)BT_GATT_SERVICE(svc_attrs);
1386 
1387 #ifdef CONFIG_BT_OTS
1388 	struct bt_ots_init_param ots_init;
1389 
1390 	ots = bt_ots_free_instance_get();
1391 	if (!ots) {
1392 		LOG_ERR("Failed to retrieve OTS instance\n");
1393 		return -ENOMEM;
1394 	}
1395 
1396 	/* Configure OTS initialization. */
1397 	memset(&ots_init, 0, sizeof(ots_init));
1398 	BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp);
1399 	BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp);
1400 	ots_init.cb = ots_cbs;
1401 
1402 	/* Initialize OTS instance. */
1403 	err = bt_ots_init(ots, &ots_init);
1404 	if (err) {
1405 		LOG_ERR("Failed to init OTS (err:%d)\n", err);
1406 		return err;
1407 	}
1408 
1409 	/* TODO: Maybe the user_data pointer can be in a different way */
1410 	for (int i = 0; i < mcs.attr_count; i++) {
1411 		if (!bt_uuid_cmp(mcs.attrs[i].uuid, BT_UUID_GATT_INCLUDE)) {
1412 			mcs.attrs[i].user_data = bt_ots_svc_decl_get(ots);
1413 		}
1414 	}
1415 #endif /* CONFIG_BT_OTS */
1416 
1417 	err = bt_gatt_service_register(&mcs);
1418 
1419 	if (err) {
1420 		LOG_ERR("Could not register the MCS service");
1421 #ifdef CONFIG_BT_OTS
1422 		/* TODO: How does one un-register the OTS? */
1423 #endif /* CONFIG_BT_OTS */
1424 		return -ENOEXEC;
1425 	}
1426 
1427 	/* Set up the callback structure */
1428 	cbs.player_name          = media_proxy_sctrl_player_name_cb;
1429 	cbs.icon_url             = media_proxy_sctrl_icon_url_cb;
1430 	cbs.track_changed        = media_proxy_sctrl_track_changed_cb;
1431 	cbs.track_title          = media_proxy_sctrl_track_title_cb;
1432 	cbs.track_duration       = media_proxy_sctrl_track_duration_cb;
1433 	cbs.track_position       = media_proxy_sctrl_track_position_cb;
1434 	cbs.playback_speed       = media_proxy_sctrl_playback_speed_cb;
1435 	cbs.seeking_speed        = media_proxy_sctrl_seeking_speed_cb;
1436 #ifdef CONFIG_BT_OTS
1437 	cbs.current_track_id     = media_proxy_sctrl_current_track_id_cb;
1438 	cbs.next_track_id        = media_proxy_sctrl_next_track_id_cb;
1439 	cbs.parent_group_id      = media_proxy_sctrl_parent_group_id_cb;
1440 	cbs.current_group_id     = media_proxy_sctrl_current_group_id_cb;
1441 #endif /* CONFIG_BT_OTS */
1442 	cbs.playing_order        = media_proxy_sctrl_playing_order_cb;
1443 	cbs.media_state          = media_proxy_sctrl_media_state_cb;
1444 	cbs.command              = media_proxy_sctrl_command_cb;
1445 	cbs.commands_supported   = media_proxy_sctrl_commands_supported_cb;
1446 #ifdef CONFIG_BT_OTS
1447 	cbs.search               = media_proxy_sctrl_search_cb;
1448 	cbs.search_results_id    = media_proxy_sctrl_search_results_id_cb;
1449 #endif /* CONFIG_BT_OTS */
1450 
1451 	media_proxy_sctrl_register(&cbs);
1452 
1453 	initialized = true;
1454 	return 0;
1455 }
1456