1 /*  Media player skeleton implementation */
2 
3 /*
4  * Copyright (c) 2019 - 2023 Nordic Semiconductor ASA
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <sys/types.h>
15 
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/ccid.h>
18 #include <zephyr/bluetooth/audio/mcs.h>
19 #include <zephyr/bluetooth/audio/media_proxy.h>
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/services/ots.h>
22 #include <zephyr/bluetooth/conn.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/kernel.h>
25 #include <zephyr/logging/log.h>
26 #include <zephyr/net_buf.h>
27 #include <zephyr/sys/__assert.h>
28 #include <zephyr/sys/atomic.h>
29 #include <zephyr/sys/util.h>
30 #include <zephyr/sys/time_units.h>
31 #include <zephyr/sys/util_macro.h>
32 
33 #include "media_proxy_internal.h"
34 #include "mcs_internal.h"
35 #include "mpl_internal.h"
36 
37 LOG_MODULE_REGISTER(bt_mpl, CONFIG_BT_MPL_LOG_LEVEL);
38 
39 #define TRACK_STATUS_INVALID 0x00
40 #define TRACK_STATUS_VALID 0x01
41 
42 #define TRACK_POS_WORK_DELAY_MS 1000
43 #define TRACK_POS_WORK_DELAY    K_MSEC(TRACK_POS_WORK_DELAY_MS)
44 
45 #define PLAYBACK_SPEED_PARAM_DEFAULT MEDIA_PROXY_PLAYBACK_SPEED_UNITY
46 
47 /* Temporary hardcoded setup for groups, tracks and segments */
48 /* There is one parent group, which is the parent of a number of groups. */
49 /* The groups have a number of tracks.  */
50 /* (There is only one level of groups, there are no groups of groups.) */
51 /* The first track of the first group has track segments, other tracks not. */
52 
53 /* Track segments */
54 static struct mpl_tseg seg_2;
55 static struct mpl_tseg seg_3;
56 
57 static struct mpl_tseg seg_1 = {
58 	.name_len = 5,
59 	.name	  = "Start",
60 	.pos	  = 0,
61 	.prev	  = NULL,
62 	.next	  = &seg_2,
63 };
64 
65 static struct mpl_tseg seg_2 = {
66 	.name_len = 6,
67 	.name	  = "Middle",
68 	.pos	  = 2000,
69 	.prev	  = &seg_1,
70 	.next	  = &seg_3,
71 };
72 
73 static struct mpl_tseg seg_3 = {
74 	.name_len = 3,
75 	.name	  = "End",
76 	.pos	  = 5000,
77 	.prev	  = &seg_2,
78 	.next	  = NULL,
79 };
80 
81 static struct mpl_track track_1_2;
82 static struct mpl_track track_1_3;
83 static struct mpl_track track_1_4;
84 static struct mpl_track track_1_5;
85 
86 /* Tracks */
87 static struct mpl_track track_1_1 = {
88 	.title	     = "Interlude #1 (Song for Alison)",
89 	.duration    = 6300,
90 	.segment     = &seg_1,
91 	.prev	     = NULL,
92 	.next	     = &track_1_2,
93 };
94 
95 
96 static struct mpl_track track_1_2 = {
97 	.title	     = "Interlude #2 (For Bobbye)",
98 	.duration    = 7500,
99 	.segment     = NULL,
100 	.prev	     = &track_1_1,
101 	.next	     = &track_1_3,
102 };
103 
104 static struct mpl_track track_1_3 = {
105 	.title	     = "Interlude #3 (Levanto Seventy)",
106 	.duration    = 7800,
107 	.segment     = NULL,
108 	.prev	     = &track_1_2,
109 	.next	     = &track_1_4,
110 };
111 
112 static struct mpl_track track_1_4 = {
113 	.title	     = "Interlude #4 (Vesper Dreams)",
114 	.duration    = 13500,
115 	.segment     = NULL,
116 	.prev	     = &track_1_3,
117 	.next	     = &track_1_5,
118 };
119 
120 static struct mpl_track track_1_5 = {
121 	.title	     = "Interlude #5 (Shasti)",
122 	.duration    = 7500,
123 	.segment     = NULL,
124 	.prev	     = &track_1_4,
125 	.next	     = NULL,
126 };
127 
128 static struct mpl_track track_2_2;
129 static struct mpl_track track_2_3;
130 
131 static struct mpl_track track_2_1 = {
132 	.title	     = "Track 2.1",
133 	.duration    = 30000,
134 	.segment     = NULL,
135 	.prev	     = NULL,
136 	.next	     = &track_2_2,
137 };
138 
139 static struct mpl_track track_2_2 = {
140 	.title	     = "Track 2.2",
141 	.duration    = 30000,
142 	.segment     = NULL,
143 	.prev	     = &track_2_1,
144 	.next	     = &track_2_3,
145 };
146 
147 static struct mpl_track track_2_3 = {
148 	.title	     = "Track 2.3",
149 	.duration    = 30000,
150 	.segment     = NULL,
151 	.prev	     = &track_2_2,
152 	.next	     = NULL,
153 };
154 
155 static struct mpl_track track_3_2;
156 static struct mpl_track track_3_3;
157 
158 static struct mpl_track track_3_1 = {
159 	.title	     = "Track 3.1",
160 	.duration    = 30000,
161 	.segment     = NULL,
162 	.prev	     = NULL,
163 	.next	     = &track_3_2,
164 };
165 
166 static struct mpl_track track_3_2 = {
167 	.title	     = "Track 3.2",
168 	.duration    = 30000,
169 	.segment     = NULL,
170 	.prev	     = &track_3_1,
171 	.next	     = &track_3_3,
172 };
173 
174 static struct mpl_track track_3_3 = {
175 	.title	     = "Track 3.3",
176 	.duration    = 30000,
177 	.segment     = NULL,
178 	.prev	     = &track_3_2,
179 	.next	     = NULL,
180 };
181 
182 static struct mpl_track track_4_2;
183 
184 static struct mpl_track track_4_1 = {
185 	.title	     = "Track 4.1",
186 	.duration    = 30000,
187 	.segment     = NULL,
188 	.prev	     = NULL,
189 	.next	     = &track_4_2,
190 };
191 
192 static struct mpl_track track_4_2 = {
193 	.title	     = "Track 4.2",
194 	.duration    = 30000,
195 	.segment     = NULL,
196 	.prev	     = &track_4_1,
197 	.next	     = NULL,
198 };
199 
200 /* Groups */
201 static struct mpl_group group_2;
202 static struct mpl_group group_3;
203 static struct mpl_group group_4;
204 static struct mpl_group group_p;
205 
206 static struct mpl_group group_1 = {
207 	.title  = "Joe Pass - Guitar Interludes",
208 	.track	= &track_1_1,
209 	.parent = &group_p,
210 	.prev	= NULL,
211 	.next	= &group_2,
212 };
213 
214 static struct mpl_group group_2 = {
215 	.title  = "Group 2",
216 	.track	= &track_2_2,
217 	.parent = &group_p,
218 	.prev	= &group_1,
219 	.next	= &group_3,
220 };
221 
222 static struct mpl_group group_3 = {
223 	.title  = "Group 3",
224 	.track	= &track_3_3,
225 	.parent = &group_p,
226 	.prev	= &group_2,
227 	.next	= &group_4,
228 };
229 
230 static struct mpl_group group_4 = {
231 	.title  = "Group 4",
232 	.track	= &track_4_2,
233 	.parent = &group_p,
234 	.prev	= &group_3,
235 	.next	= NULL,
236 };
237 
238 static struct mpl_group group_p = {
239 	.title  = "Parent group",
240 	.track	= &track_4_1,
241 	.parent = &group_p,
242 	.prev	= NULL,
243 	.next	= NULL,
244 };
245 
246 static struct mpl_mediaplayer media_player = {
247 	.name			  = CONFIG_BT_MPL_MEDIA_PLAYER_NAME,
248 	.icon_url		  = CONFIG_BT_MPL_ICON_URL,
249 	.group			  = &group_1,
250 	.track_pos		  = 0,
251 	.state			  = MEDIA_PROXY_STATE_PAUSED,
252 	.playback_speed_param	  = PLAYBACK_SPEED_PARAM_DEFAULT,
253 	.seeking_speed_factor	  = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO,
254 	.playing_order		  = MEDIA_PROXY_PLAYING_ORDER_INORDER_REPEAT,
255 	.playing_orders_supported = MEDIA_PROXY_PLAYING_ORDERS_SUPPORTED_INORDER_ONCE |
256 				    MEDIA_PROXY_PLAYING_ORDERS_SUPPORTED_INORDER_REPEAT,
257 	.opcodes_supported	  = 0x001fffff, /* All opcodes */
258 #ifdef CONFIG_BT_MPL_OBJECTS
259 	.search_results_id	  = 0,
260 	.calls = { 0 },
261 #endif /* CONFIG_BT_MPL_OBJECTS */
262 	.next_track_set           = false
263 };
264 
265 static void set_track_position(int32_t position);
266 static void set_relative_track_position(int32_t rel_pos);
267 static void do_track_change_notifications(struct mpl_mediaplayer *pl);
268 static void do_group_change_notifications(struct mpl_mediaplayer *pl);
269 
270 #ifdef CONFIG_BT_MPL_OBJECTS
271 
272 /* The types of objects we keep in the Object Transfer Service */
273 enum mpl_objects {
274 	MPL_OBJ_NONE = 0,
275 	MPL_OBJ_ICON,
276 	MPL_OBJ_TRACK_SEGMENTS,
277 	MPL_OBJ_TRACK,
278 	MPL_OBJ_PARENT_GROUP,
279 	MPL_OBJ_GROUP,
280 	MPL_OBJ_SEARCH_RESULTS,
281 };
282 
283 enum mpl_obj_flag {
284 	MPL_OBJ_FLAG_BUSY,
285 
286 	MPL_OBJ_FLAG_NUM_FLAGS, /* keep as last */
287 };
288 
289 /* The active object */
290 /* Only a single object is selected or being added (active) at a time. */
291 /* And, except for the icon object, all objects can be created dynamically. */
292 /* So a single buffer to hold object content is sufficient.  */
293 struct obj_t {
294 	/* ID of the currently selected object*/
295 	uint64_t selected_id;
296 
297 	/* Type of object being added, e.g. MPL_OBJ_ICON */
298 	uint8_t add_type;
299 
300 	/* Descriptor of object being added */
301 	struct bt_ots_obj_created_desc *desc;
302 	union {
303 		/* Pointer to track being added */
304 		struct mpl_track *add_track;
305 
306 		/* Pointer to group being added */
307 		struct mpl_group *add_group;
308 	};
309 	struct net_buf_simple *content;
310 
311 	ATOMIC_DEFINE(flags, MPL_OBJ_FLAG_NUM_FLAGS);
312 };
313 
314 static struct obj_t obj = {
315 	.selected_id = 0,
316 	.add_type = MPL_OBJ_NONE,
317 	.add_track = NULL,
318 	.add_group = NULL,
319 	.content = NET_BUF_SIMPLE(CONFIG_BT_MPL_MAX_OBJ_SIZE),
320 };
321 
322 /* Set up content buffer for the icon object */
setup_icon_object(void)323 static int setup_icon_object(void)
324 {
325 	uint16_t index;
326 	uint8_t k;
327 
328 	/* The icon object is supposed to be a bitmap. */
329 	/* For now, fill it with dummy data. */
330 
331 	net_buf_simple_reset(obj.content);
332 
333 	/* Size may be larger than what fits in 8 bits, use 16-bit for index */
334 	for (index = 0, k = 0;
335 	     index < MIN(CONFIG_BT_MPL_MAX_OBJ_SIZE,
336 			 CONFIG_BT_MPL_ICON_BITMAP_SIZE);
337 	     index++, k++) {
338 		net_buf_simple_add_u8(obj.content, k);
339 	}
340 
341 	return obj.content->len;
342 }
343 
344 /* Set up content buffer for a track segments object */
setup_segments_object(struct mpl_track * track)345 static uint32_t setup_segments_object(struct mpl_track *track)
346 {
347 	struct mpl_tseg *seg = track->segment;
348 
349 	net_buf_simple_reset(obj.content);
350 
351 	if (seg) {
352 		uint32_t tot_size = 0;
353 
354 		while (seg->prev) {
355 			seg = seg->prev;
356 		}
357 		while (seg) {
358 			uint32_t seg_size = sizeof(seg->name_len);
359 
360 			seg_size += seg->name_len;
361 			seg_size += sizeof(seg->pos);
362 			if (tot_size + seg_size > obj.content->size) {
363 				LOG_DBG("Segments object out of space");
364 				break;
365 			}
366 			net_buf_simple_add_u8(obj.content, seg->name_len);
367 			net_buf_simple_add_mem(obj.content, seg->name,
368 					       seg->name_len);
369 			net_buf_simple_add_le32(obj.content, seg->pos);
370 
371 			tot_size += seg_size;
372 			seg = seg->next;
373 		}
374 
375 		LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Segments Object");
376 		LOG_DBG("Segments object length: %d", obj.content->len);
377 	} else {
378 		LOG_ERR("No seg!");
379 	}
380 
381 	return obj.content->len;
382 }
383 
384 /* Set up content buffer for a track object */
setup_track_object(struct mpl_track * track)385 static uint32_t setup_track_object(struct mpl_track *track)
386 {
387 	uint16_t index;
388 	uint8_t k;
389 
390 	/* The track object is supposed to be in Id3v2 format */
391 	/* For now, fill it with dummy data */
392 
393 	net_buf_simple_reset(obj.content);
394 
395 	/* Size may be larger than what fits in 8 bits, use 16-bit for index */
396 	for (index = 0, k = 0;
397 	     index < MIN(CONFIG_BT_MPL_MAX_OBJ_SIZE,
398 			 CONFIG_BT_MPL_TRACK_MAX_SIZE);
399 	     index++, k++) {
400 		net_buf_simple_add_u8(obj.content, k);
401 	}
402 
403 	return obj.content->len;
404 }
405 
406 /* Set up content buffer for the parent group object */
setup_parent_group_object(struct mpl_group * group)407 static uint32_t setup_parent_group_object(struct mpl_group *group)
408 {
409 	/* This function actually does not use the parent. */
410 	/* It just follows the list of groups. */
411 	/* The implementation has a fixed structure, with one parent group, */
412 	/* and one level of groups containing tracks only. */
413 	/* The track groups have a pointer to the parent, but there is no */
414 	/* pointer in the other direction, so it is not possible to go from */
415 	/* the parent group to a group of tracks. */
416 
417 	uint8_t type = MEDIA_PROXY_GROUP_OBJECT_GROUP_TYPE;
418 	uint8_t record_size = sizeof(type) + BT_OTS_OBJ_ID_SIZE;
419 	int next_size = record_size;
420 
421 	net_buf_simple_reset(obj.content);
422 
423 	if (group) {
424 		while (group->prev) {
425 			group = group->prev;
426 		}
427 		/* While there is a group, and the record fits in the object */
428 		while (group && (next_size <= obj.content->size)) {
429 			net_buf_simple_add_u8(obj.content, type);
430 			net_buf_simple_add_le48(obj.content, group->id);
431 			group = group->next;
432 			next_size += record_size;
433 		}
434 		if (next_size > obj.content->size) {
435 			LOG_WRN("Not room for full group in object");
436 		}
437 		LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Parent Group Object");
438 		LOG_DBG("Group object length: %d", obj.content->len);
439 	}
440 	return obj.content->len;
441 }
442 
443 /* Set up contents for a group object */
444 /* The group object contains a concatenated list of records, where each */
445 /* record consists of a type byte and a UUID */
setup_group_object(struct mpl_group * group)446 static uint32_t setup_group_object(struct mpl_group *group)
447 {
448 	struct mpl_track *track = group->track;
449 	uint8_t type = MEDIA_PROXY_GROUP_OBJECT_TRACK_TYPE;
450 	uint8_t record_size = sizeof(type) + BT_OTS_OBJ_ID_SIZE;
451 	int next_size = record_size;
452 
453 	net_buf_simple_reset(obj.content);
454 
455 	if (track) {
456 		while (track->prev) {
457 			track = track->prev;
458 		}
459 		/* While there is a track, and the record fits in the object */
460 		while (track && (next_size <= obj.content->size)) {
461 			net_buf_simple_add_u8(obj.content, type);
462 			net_buf_simple_add_le48(obj.content, track->id);
463 			track = track->next;
464 			next_size += record_size;
465 		}
466 		if (next_size > obj.content->size) {
467 			LOG_WRN("Not room for full group in object");
468 		}
469 		LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Group Object");
470 		LOG_DBG("Group object length: %d", obj.content->len);
471 	}
472 	return obj.content->len;
473 }
474 
475 /* Add the icon object to the OTS */
add_icon_object(struct mpl_mediaplayer * pl)476 static int add_icon_object(struct mpl_mediaplayer *pl)
477 {
478 	int ret;
479 	struct bt_ots_obj_add_param add_param = {};
480 	struct bt_ots_obj_created_desc created_desc = {};
481 	const struct bt_uuid *icon_type = BT_UUID_OTS_TYPE_MPL_ICON;
482 	static char *icon_name = "Icon";
483 
484 	obj.add_type = MPL_OBJ_ICON;
485 	obj.desc = &created_desc;
486 
487 	obj.desc->size.alloc = obj.desc->size.cur = setup_icon_object();
488 	obj.desc->name = icon_name;
489 	BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
490 
491 	add_param.size = obj.desc->size.alloc;
492 	add_param.type.uuid.type = BT_UUID_TYPE_16;
493 	add_param.type.uuid_16.val = BT_UUID_16(icon_type)->val;
494 
495 	ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
496 	if (ret < 0) {
497 		LOG_WRN("Unable to add icon object, error %d", ret);
498 
499 		return ret;
500 	}
501 
502 	return 0;
503 }
504 
505 /* Add a track segments object to the OTS */
add_current_track_segments_object(struct mpl_mediaplayer * pl)506 static int add_current_track_segments_object(struct mpl_mediaplayer *pl)
507 {
508 	int ret;
509 	struct bt_ots_obj_add_param add_param = {};
510 	struct bt_ots_obj_created_desc created_desc = {};
511 	const struct bt_uuid *segs_type = BT_UUID_OTS_TYPE_TRACK_SEGMENT;
512 
513 	obj.add_type = MPL_OBJ_TRACK_SEGMENTS;
514 	obj.desc = &created_desc;
515 
516 	obj.desc->size.alloc = obj.desc->size.cur = setup_segments_object(pl->group->track);
517 	obj.desc->name = pl->group->track->title;
518 	BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
519 
520 	add_param.size = obj.desc->size.alloc;
521 	add_param.type.uuid.type = BT_UUID_TYPE_16;
522 	add_param.type.uuid_16.val = BT_UUID_16(segs_type)->val;
523 
524 	ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
525 	if (ret < 0) {
526 		LOG_WRN("Unable to add track segments object: %d", ret);
527 
528 		return ret;
529 	}
530 
531 	return 0;
532 }
533 
534 /* Add a single track to the OTS */
add_track_object(struct mpl_track * track)535 static int add_track_object(struct mpl_track *track)
536 {
537 	struct bt_ots_obj_add_param add_param = {};
538 	struct bt_ots_obj_created_desc created_desc = {};
539 	const struct bt_uuid *track_type = BT_UUID_OTS_TYPE_TRACK;
540 	int ret;
541 
542 	if (!track) {
543 		LOG_ERR("No track");
544 		return -EINVAL;
545 	}
546 
547 	obj.add_type = MPL_OBJ_TRACK;
548 	obj.add_track = track;
549 	obj.desc = &created_desc;
550 
551 	obj.desc->size.alloc = obj.desc->size.cur = setup_track_object(track);
552 	obj.desc->name = track->title;
553 	BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
554 
555 	add_param.size = obj.desc->size.alloc;
556 	add_param.type.uuid.type = BT_UUID_TYPE_16;
557 	add_param.type.uuid_16.val = BT_UUID_16(track_type)->val;
558 
559 	ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
560 	if (ret < 0) {
561 		LOG_WRN("Unable to add track object: %d", ret);
562 
563 		return ret;
564 	}
565 
566 	return 0;
567 }
568 
569 /* Add the parent group to the OTS */
add_parent_group_object(struct mpl_mediaplayer * pl)570 static int add_parent_group_object(struct mpl_mediaplayer *pl)
571 {
572 	int ret;
573 	struct bt_ots_obj_add_param add_param = {};
574 	struct bt_ots_obj_created_desc created_desc = {};
575 	const struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
576 
577 	obj.add_type = MPL_OBJ_PARENT_GROUP;
578 	obj.desc = &created_desc;
579 
580 	obj.desc->size.alloc = obj.desc->size.cur = setup_parent_group_object(pl->group);
581 	obj.desc->name = pl->group->parent->title;
582 	BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
583 
584 	add_param.size = obj.desc->size.alloc;
585 	add_param.type.uuid.type = BT_UUID_TYPE_16;
586 	add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
587 
588 	ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
589 	if (ret < 0) {
590 		LOG_WRN("Unable to add parent group object");
591 
592 		return ret;
593 	}
594 
595 	return 0;
596 }
597 
598 /* Add a single group to the OTS */
add_group_object(struct mpl_group * group)599 static int add_group_object(struct mpl_group *group)
600 {
601 	struct bt_ots_obj_add_param add_param = {};
602 	struct bt_ots_obj_created_desc created_desc = {};
603 	const struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
604 	int ret;
605 
606 	if (!group) {
607 		LOG_ERR("No group");
608 		return -EINVAL;
609 	}
610 
611 	obj.add_type = MPL_OBJ_GROUP;
612 	obj.add_group = group;
613 	obj.desc = &created_desc;
614 
615 	obj.desc->size.alloc = obj.desc->size.cur = setup_group_object(group);
616 	obj.desc->name = group->title;
617 	BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
618 
619 	add_param.size = obj.desc->size.alloc;
620 	add_param.type.uuid.type = BT_UUID_TYPE_16;
621 	add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
622 
623 	ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
624 	if (ret < 0) {
625 		LOG_WRN("Unable to add group object: %d", ret);
626 
627 		return ret;
628 	}
629 
630 	return 0;
631 }
632 
633 /* Add all tracks of a group to the OTS */
add_group_tracks(struct mpl_group * group)634 static int add_group_tracks(struct mpl_group *group)
635 {
636 	int ret_overall = 0;
637 	struct mpl_track *track = group->track;
638 
639 	if (track) {
640 		while (track->prev) {
641 			track = track->prev;
642 		}
643 
644 		while (track) {
645 			int ret = add_track_object(track);
646 
647 			if (ret && !ret_overall) {
648 				ret_overall = ret;
649 			}
650 			track = track->next;
651 		}
652 	}
653 	return ret_overall;
654 }
655 
656 /* Add all groups (except the parent group) and their tracks to the OTS */
add_group_and_track_objects(struct mpl_mediaplayer * pl)657 static int add_group_and_track_objects(struct mpl_mediaplayer *pl)
658 {
659 	int ret_overall = 0;
660 	int ret;
661 	struct mpl_group *group = pl->group;
662 
663 	if (group) {
664 		while (group->prev) {
665 			group = group->prev;
666 		}
667 
668 		while (group) {
669 			ret = add_group_tracks(group);
670 			if (ret && !ret_overall) {
671 				ret_overall = ret;
672 			}
673 
674 			ret = add_group_object(group);
675 			if (ret && !ret_overall) {
676 				ret_overall = ret;
677 			}
678 			group = group->next;
679 		}
680 	}
681 
682 	ret = add_parent_group_object(pl);
683 	if (ret && !ret_overall) {
684 		ret_overall = ret;
685 	}
686 
687 	return ret_overall;
688 }
689 
690 /**** Callbacks from the object transfer service ******************************/
691 
on_obj_deleted(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)692 static int on_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
693 			   uint64_t id)
694 {
695 	LOG_DBG_OBJ_ID("Object Id deleted: ", id);
696 
697 	return 0;
698 }
699 
on_obj_selected(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)700 static void on_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
701 			    uint64_t id)
702 {
703 	if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
704 		/* TODO: Can there be a collision between select and internal */
705 		/* activities, like adding new objects? */
706 		LOG_ERR("Object busy - select not performed");
707 		return;
708 	}
709 
710 	LOG_DBG_OBJ_ID("Object Id selected: ", id);
711 
712 	if (id == media_player.icon_id) {
713 		LOG_DBG("Icon Object ID");
714 		(void)setup_icon_object();
715 	} else if (id == media_player.group->track->segments_id) {
716 		LOG_DBG("Current Track Segments Object ID");
717 		(void)setup_segments_object(media_player.group->track);
718 	} else if (id == media_player.group->track->id) {
719 		LOG_DBG("Current Track Object ID");
720 		(void)setup_track_object(media_player.group->track);
721 	} else if (media_player.next_track_set && id == media_player.next.track->id) {
722 		/* Next track, if the next track has been explicitly set */
723 		LOG_DBG("Next Track Object ID");
724 		(void)setup_track_object(media_player.next.track);
725 	} else if (id == media_player.group->track->next->id) {
726 		/* Next track, if next track has not been explicitly set */
727 		LOG_DBG("Next Track Object ID");
728 		(void)setup_track_object(media_player.group->track->next);
729 	} else if (id == media_player.group->parent->id) {
730 		LOG_DBG("Parent Group Object ID");
731 		(void)setup_parent_group_object(media_player.group);
732 	} else if (id == media_player.group->id) {
733 		LOG_DBG("Current Group Object ID");
734 		(void)setup_group_object(media_player.group);
735 	} else {
736 		LOG_ERR("Unknown Object ID");
737 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
738 		return;
739 	}
740 
741 	obj.selected_id = id;
742 	atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
743 }
744 
on_obj_created(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const struct bt_ots_obj_add_param * add_param,struct bt_ots_obj_created_desc * created_desc)745 static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
746 			  const struct bt_ots_obj_add_param *add_param,
747 			  struct bt_ots_obj_created_desc *created_desc)
748 {
749 	/* Objects are always created locally so we do not need to check for MPL_OBJ_FLAG_BUSY */
750 
751 	LOG_DBG_OBJ_ID("Object Id created: ", id);
752 
753 	*created_desc = *obj.desc;
754 
755 	if (!bt_uuid_cmp(&add_param->type.uuid, BT_UUID_OTS_TYPE_MPL_ICON)) {
756 		LOG_DBG("Icon Obj Type");
757 		if (obj.add_type == MPL_OBJ_ICON) {
758 			obj.add_type = MPL_OBJ_NONE;
759 			media_player.icon_id = id;
760 		} else {
761 			LOG_DBG("Unexpected object creation");
762 		}
763 
764 	} else if (!bt_uuid_cmp(&add_param->type.uuid,
765 				BT_UUID_OTS_TYPE_TRACK_SEGMENT)) {
766 		LOG_DBG("Track Segments Obj Type");
767 		if (obj.add_type == MPL_OBJ_TRACK_SEGMENTS) {
768 			obj.add_type = MPL_OBJ_NONE;
769 			media_player.group->track->segments_id = id;
770 		} else {
771 			LOG_DBG("Unexpected object creation");
772 		}
773 
774 	} else if (!bt_uuid_cmp(&add_param->type.uuid,
775 				 BT_UUID_OTS_TYPE_TRACK)) {
776 		LOG_DBG("Track Obj Type");
777 		if (obj.add_type == MPL_OBJ_TRACK) {
778 			obj.add_type = MPL_OBJ_NONE;
779 			obj.add_track->id = id;
780 			obj.add_track = NULL;
781 		} else {
782 			LOG_DBG("Unexpected object creation");
783 		}
784 
785 	} else if (!bt_uuid_cmp(&add_param->type.uuid,
786 				 BT_UUID_OTS_TYPE_GROUP)) {
787 		LOG_DBG("Group Obj Type");
788 		if (obj.add_type == MPL_OBJ_PARENT_GROUP) {
789 			LOG_DBG("Parent group");
790 			obj.add_type = MPL_OBJ_NONE;
791 			media_player.group->parent->id = id;
792 		} else if (obj.add_type == MPL_OBJ_GROUP) {
793 			LOG_DBG("Other group");
794 			obj.add_type = MPL_OBJ_NONE;
795 			obj.add_group->id = id;
796 			obj.add_group = NULL;
797 		} else {
798 			LOG_DBG("Unexpected object creation");
799 		}
800 
801 	} else {
802 		LOG_DBG("Unknown Object ID");
803 	}
804 
805 	return 0;
806 }
807 
on_object_send(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,void ** data,size_t len,off_t offset)808 static ssize_t on_object_send(struct bt_ots *ots, struct bt_conn *conn,
809 			      uint64_t id, void **data, size_t len,
810 			      off_t offset)
811 {
812 	if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
813 		/* TODO: Can there be a collision between select and internal */
814 		/* activities, like adding new objects? */
815 		LOG_ERR("Object busy");
816 		return -EBUSY;
817 	}
818 
819 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
820 		char t[BT_OTS_OBJ_ID_STR_LEN];
821 		(void)bt_ots_obj_id_to_str(id, t, sizeof(t));
822 		LOG_DBG("Object Id %s, offset %lu, length %zu", t, (long)offset, len);
823 	}
824 
825 	if (id != obj.selected_id) {
826 		LOG_ERR("Read from unselected object");
827 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
828 		return -EINVAL;
829 	}
830 
831 	if (!data) {
832 		LOG_DBG("Read complete");
833 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
834 		return 0;
835 	}
836 
837 	if (offset >= obj.content->len) {
838 		LOG_DBG("Offset too large");
839 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
840 		return -EINVAL;
841 	}
842 
843 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
844 		if (len > obj.content->len - offset) {
845 			LOG_DBG("Requested len too large");
846 		}
847 	}
848 
849 	*data = &obj.content->data[offset];
850 	atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
851 
852 	return MIN(len, obj.content->len - offset);
853 }
854 
855 static struct bt_ots_cb ots_cbs = {
856 	.obj_created = on_obj_created,
857 	.obj_read = on_object_send,
858 	.obj_selected = on_obj_selected,
859 	.obj_deleted = on_obj_deleted,
860 };
861 
862 #endif /* CONFIG_BT_MPL_OBJECTS */
863 
864 
865 /* TODO: It must be possible to replace the do_prev_segment(), do_prev_track */
866 /* and do_prev_group() with a generic do_prev() command that can be used at */
867 /* all levels.	Similarly for do_next, do_prev, and so on. */
868 
do_prev_segment(struct mpl_mediaplayer * pl)869 static void do_prev_segment(struct mpl_mediaplayer *pl)
870 {
871 	LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
872 
873 	if (pl->group->track->segment->prev != NULL) {
874 		pl->group->track->segment = pl->group->track->segment->prev;
875 	}
876 
877 	LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
878 }
879 
do_next_segment(struct mpl_mediaplayer * pl)880 static void do_next_segment(struct mpl_mediaplayer *pl)
881 {
882 	LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
883 
884 	if (pl->group->track->segment->next != NULL) {
885 		pl->group->track->segment = pl->group->track->segment->next;
886 	}
887 
888 	LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
889 }
890 
do_first_segment(struct mpl_mediaplayer * pl)891 static void do_first_segment(struct mpl_mediaplayer *pl)
892 {
893 	LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
894 
895 	while (pl->group->track->segment->prev != NULL) {
896 		pl->group->track->segment = pl->group->track->segment->prev;
897 	}
898 
899 	LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
900 }
901 
do_last_segment(struct mpl_mediaplayer * pl)902 static void do_last_segment(struct mpl_mediaplayer *pl)
903 {
904 	LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
905 
906 	while (pl->group->track->segment->next != NULL) {
907 		pl->group->track->segment = pl->group->track->segment->next;
908 	}
909 
910 	LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
911 }
912 
do_goto_segment(struct mpl_mediaplayer * pl,int32_t segnum)913 static void do_goto_segment(struct mpl_mediaplayer *pl, int32_t segnum)
914 {
915 	int32_t k;
916 
917 	LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
918 
919 	if (segnum > 0) {
920 		/* Goto first segment */
921 		while (pl->group->track->segment->prev != NULL) {
922 			pl->group->track->segment =
923 				pl->group->track->segment->prev;
924 		}
925 
926 		/* Then go segnum - 1 tracks forward */
927 		for (k = 0; k < (segnum - 1); k++) {
928 			if (pl->group->track->segment->next != NULL) {
929 				pl->group->track->segment =
930 					pl->group->track->segment->next;
931 			}
932 		}
933 	} else if (segnum < 0) {
934 		/* Goto last track */
935 		while (pl->group->track->segment->next != NULL) {
936 			pl->group->track->segment =
937 				pl->group->track->segment->next;
938 		}
939 
940 		/* Then go |segnum - 1| tracks back */
941 		for (k = 0; k < (-segnum - 1); k++) {
942 			if (pl->group->track->segment->prev != NULL) {
943 				pl->group->track->segment =
944 					pl->group->track->segment->prev;
945 			}
946 		}
947 	}
948 
949 	LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
950 
951 	set_track_position(pl->group->track->segment->pos);
952 }
953 
do_prev_track(struct mpl_mediaplayer * pl)954 static void do_prev_track(struct mpl_mediaplayer *pl)
955 {
956 #ifdef CONFIG_BT_MPL_OBJECTS
957 	LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
958 #endif /* CONFIG_BT_MPL_OBJECTS */
959 
960 	if (pl->group->track->prev != NULL) {
961 		pl->group->track = pl->group->track->prev;
962 		pl->track_pos = 0;
963 		do_track_change_notifications(pl);
964 	} else {
965 		/* For previous track, the position is reset to 0 */
966 		/* even if we stay at the same track (goto start of */
967 		/* track) */
968 		set_track_position(0);
969 	}
970 
971 #ifdef CONFIG_BT_MPL_OBJECTS
972 	LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
973 #endif /* CONFIG_BT_MPL_OBJECTS */
974 }
975 
976 /* Change to next track according to the current track's next track */
do_next_track_normal_order(struct mpl_mediaplayer * pl)977 static void do_next_track_normal_order(struct mpl_mediaplayer *pl)
978 {
979 #ifdef CONFIG_BT_MPL_OBJECTS
980 	LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
981 #endif /* CONFIG_BT_MPL_OBJECTS */
982 
983 	if (pl->group->track->next != NULL) {
984 		pl->group->track = pl->group->track->next;
985 		pl->track_pos = 0;
986 		do_track_change_notifications(pl);
987 	}
988 
989 #ifdef CONFIG_BT_MPL_OBJECTS
990 	LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
991 #endif /* CONFIG_BT_MPL_OBJECTS */
992 }
993 
994 /* Change to next track when the next track has been explicitly set
995  *
996  * ALWAYS changes the track, changes the group if required
997  * Resets the next_track_set and the "next" pointers
998  *
999  * Returns true if the _group_ has been changed, otherwise false
1000  */
do_next_track_next_track_set(struct mpl_mediaplayer * pl)1001 static void do_next_track_next_track_set(struct mpl_mediaplayer *pl)
1002 {
1003 	if (pl->next.group != pl->group) {
1004 		pl->group = pl->next.group;
1005 		do_group_change_notifications(pl);
1006 	}
1007 
1008 	pl->group->track = pl->next.track;
1009 
1010 	pl->next.track = NULL;
1011 	pl->next.group = NULL;
1012 	pl->next_track_set = false;
1013 	pl->track_pos = 0;
1014 	do_track_change_notifications(pl);
1015 }
1016 
do_next_track(struct mpl_mediaplayer * pl)1017 static void do_next_track(struct mpl_mediaplayer *pl)
1018 {
1019 	if (pl->next_track_set) {
1020 		LOG_DBG("Next track set");
1021 		do_next_track_next_track_set(pl);
1022 	} else {
1023 		do_next_track_normal_order(pl);
1024 	}
1025 }
1026 
do_first_track(struct mpl_mediaplayer * pl,bool group_change)1027 static void do_first_track(struct mpl_mediaplayer *pl, bool group_change)
1028 {
1029 	bool track_changed = false;
1030 
1031 #ifdef CONFIG_BT_MPL_OBJECTS
1032 	LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1033 #endif /* CONFIG_BT_MPL_OBJECTS */
1034 
1035 	/* Set first track */
1036 	while (pl->group->track->prev != NULL) {
1037 		pl->group->track = pl->group->track->prev;
1038 		track_changed = true;
1039 	}
1040 
1041 	/* Notify about new track */
1042 	if (group_change || track_changed) {
1043 		media_player.track_pos = 0;
1044 		do_track_change_notifications(&media_player);
1045 	} else {
1046 		/* For first track, the position is reset to 0 even */
1047 		/* if we stay at the same track (goto start of track) */
1048 		set_track_position(0);
1049 	}
1050 
1051 #ifdef CONFIG_BT_MPL_OBJECTS
1052 	LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1053 #endif /* CONFIG_BT_MPL_OBJECTS */
1054 }
1055 
do_last_track(struct mpl_mediaplayer * pl)1056 static void do_last_track(struct mpl_mediaplayer *pl)
1057 {
1058 #ifdef CONFIG_BT_MPL_OBJECTS
1059 	LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1060 #endif /* CONFIG_BT_MPL_OBJECTS */
1061 
1062 	if (pl->group->track->next != NULL) {
1063 		pl->group->track = pl->group->track->next;
1064 		media_player.track_pos = 0;
1065 		do_track_change_notifications(&media_player);
1066 	} else {
1067 
1068 		/* For last track, the position is reset to 0 even */
1069 		/* if we stay at the same track (goto start of track) */
1070 		set_track_position(0);
1071 	}
1072 
1073 	while (pl->group->track->next != NULL) {
1074 		pl->group->track = pl->group->track->next;
1075 	}
1076 
1077 #ifdef CONFIG_BT_MPL_OBJECTS
1078 	LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1079 #endif /* CONFIG_BT_MPL_OBJECTS */
1080 }
1081 
do_goto_track(struct mpl_mediaplayer * pl,int32_t tracknum)1082 static void do_goto_track(struct mpl_mediaplayer *pl, int32_t tracknum)
1083 {
1084 	int32_t count = 0;
1085 	int32_t k;
1086 
1087 #ifdef CONFIG_BT_MPL_OBJECTS
1088 	LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1089 #endif /* CONFIG_BT_MPL_OBJECTS */
1090 
1091 	if (tracknum > 0) {
1092 		/* Goto first track */
1093 		while (pl->group->track->prev != NULL) {
1094 			pl->group->track = pl->group->track->prev;
1095 			count--;
1096 		}
1097 
1098 		/* Then go tracknum - 1 tracks forward */
1099 		for (k = 0; k < (tracknum - 1); k++) {
1100 			if (pl->group->track->next != NULL) {
1101 				pl->group->track = pl->group->track->next;
1102 				count++;
1103 			}
1104 		}
1105 	} else if (tracknum < 0) {
1106 		/* Goto last track */
1107 		while (pl->group->track->next != NULL) {
1108 			pl->group->track = pl->group->track->next;
1109 			count++;
1110 		}
1111 
1112 		/* Then go |tracknum - 1| tracks back */
1113 		for (k = 0; k < (-tracknum - 1); k++) {
1114 			if (pl->group->track->prev != NULL) {
1115 				pl->group->track = pl->group->track->prev;
1116 				count--;
1117 			}
1118 		}
1119 	}
1120 
1121 #ifdef CONFIG_BT_MPL_OBJECTS
1122 	LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1123 #endif /* CONFIG_BT_MPL_OBJECTS */
1124 
1125 	/* The track has changed if we have moved more in one direction */
1126 	/* than in the other */
1127 	if (count != 0) {
1128 		media_player.track_pos = 0;
1129 		do_track_change_notifications(&media_player);
1130 	} else {
1131 		/* For goto track, the position is reset to 0 */
1132 		/* even if we stay at the same track (goto */
1133 		/* start of track) */
1134 		set_track_position(0);
1135 	}
1136 }
1137 
do_prev_group(struct mpl_mediaplayer * pl)1138 static void do_prev_group(struct mpl_mediaplayer *pl)
1139 {
1140 #ifdef CONFIG_BT_MPL_OBJECTS
1141 	LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1142 #endif /* CONFIG_BT_MPL_OBJECTS */
1143 
1144 	if (pl->group->prev != NULL) {
1145 		pl->group = pl->group->prev;
1146 		do_group_change_notifications(pl);
1147 	}
1148 
1149 #ifdef CONFIG_BT_MPL_OBJECTS
1150 	LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1151 #endif /* CONFIG_BT_MPL_OBJECTS */
1152 }
1153 
do_next_group(struct mpl_mediaplayer * pl)1154 static void do_next_group(struct mpl_mediaplayer *pl)
1155 {
1156 
1157 #ifdef CONFIG_BT_MPL_OBJECTS
1158 	LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1159 #endif /* CONFIG_BT_MPL_OBJECTS */
1160 
1161 	if (pl->group->next != NULL) {
1162 		pl->group = pl->group->next;
1163 		do_group_change_notifications(pl);
1164 	}
1165 
1166 #ifdef CONFIG_BT_MPL_OBJECTS
1167 	LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1168 #endif /* CONFIG_BT_MPL_OBJECTS */
1169 }
1170 
do_first_group(struct mpl_mediaplayer * pl)1171 static void do_first_group(struct mpl_mediaplayer *pl)
1172 {
1173 #ifdef CONFIG_BT_MPL_OBJECTS
1174 	LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1175 #endif /* CONFIG_BT_MPL_OBJECTS */
1176 
1177 	if (pl->group->prev != NULL) {
1178 		pl->group = pl->group->prev;
1179 		do_group_change_notifications(pl);
1180 	}
1181 
1182 	while (pl->group->prev != NULL) {
1183 		pl->group = pl->group->prev;
1184 	}
1185 
1186 #ifdef CONFIG_BT_MPL_OBJECTS
1187 	LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1188 #endif /* CONFIG_BT_MPL_OBJECTS */
1189 }
1190 
do_last_group(struct mpl_mediaplayer * pl)1191 static void do_last_group(struct mpl_mediaplayer *pl)
1192 {
1193 #ifdef CONFIG_BT_MPL_OBJECTS
1194 	LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1195 #endif /* CONFIG_BT_MPL_OBJECTS */
1196 
1197 	if (pl->group->next != NULL) {
1198 		pl->group = pl->group->next;
1199 		do_group_change_notifications(pl);
1200 	}
1201 
1202 	while (pl->group->next != NULL) {
1203 		pl->group = pl->group->next;
1204 	}
1205 
1206 #ifdef CONFIG_BT_MPL_OBJECTS
1207 	LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1208 #endif /* CONFIG_BT_MPL_OBJECTS */
1209 }
1210 
do_goto_group(struct mpl_mediaplayer * pl,int32_t groupnum)1211 static void do_goto_group(struct mpl_mediaplayer *pl, int32_t groupnum)
1212 {
1213 	int32_t count = 0;
1214 	int32_t k;
1215 
1216 #ifdef CONFIG_BT_MPL_OBJECTS
1217 	LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1218 #endif /* CONFIG_BT_MPL_OBJECTS */
1219 
1220 	if (groupnum > 0) {
1221 		/* Goto first group */
1222 		while (pl->group->prev != NULL) {
1223 			pl->group = pl->group->prev;
1224 			count--;
1225 		}
1226 
1227 		/* Then go groupnum - 1 groups forward */
1228 		for (k = 0; k < (groupnum - 1); k++) {
1229 			if (pl->group->next != NULL) {
1230 				pl->group = pl->group->next;
1231 				count++;
1232 			}
1233 		}
1234 	} else if (groupnum < 0) {
1235 		/* Goto last group */
1236 		while (pl->group->next != NULL) {
1237 			pl->group = pl->group->next;
1238 			count++;
1239 		}
1240 
1241 		/* Then go |groupnum - 1| groups back */
1242 		for (k = 0; k < (-groupnum - 1); k++) {
1243 			if (pl->group->prev != NULL) {
1244 				pl->group = pl->group->prev;
1245 				count--;
1246 			}
1247 		}
1248 	}
1249 
1250 #ifdef CONFIG_BT_MPL_OBJECTS
1251 	LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1252 #endif /* CONFIG_BT_MPL_OBJECTS */
1253 
1254 	/* The group has changed if we have moved more in one direction */
1255 	/* than in the other */
1256 	if (count != 0) {
1257 		do_group_change_notifications(pl);
1258 	}
1259 }
1260 
do_track_change_notifications(struct mpl_mediaplayer * pl)1261 static void do_track_change_notifications(struct mpl_mediaplayer *pl)
1262 {
1263 	media_proxy_pl_track_changed_cb();
1264 	media_proxy_pl_track_title_cb(pl->group->track->title);
1265 	media_proxy_pl_track_duration_cb(pl->group->track->duration);
1266 	media_proxy_pl_track_position_cb(pl->track_pos);
1267 #ifdef CONFIG_BT_MPL_OBJECTS
1268 	media_proxy_pl_current_track_id_cb(pl->group->track->id);
1269 	if (pl->group->track->next) {
1270 		media_proxy_pl_next_track_id_cb(pl->group->track->next->id);
1271 	} else {
1272 		/* Send a zero value to indicate that there is no next track */
1273 		media_proxy_pl_next_track_id_cb(MPL_NO_TRACK_ID);
1274 	}
1275 #endif /* CONFIG_BT_MPL_OBJECTS */
1276 }
1277 
do_group_change_notifications(struct mpl_mediaplayer * pl)1278 static void do_group_change_notifications(struct mpl_mediaplayer *pl)
1279 {
1280 #ifdef CONFIG_BT_MPL_OBJECTS
1281 	media_proxy_pl_current_group_id_cb(pl->group->id);
1282 #endif /* CONFIG_BT_MPL_OBJECTS */
1283 }
1284 
do_full_prev_group(struct mpl_mediaplayer * pl)1285 static void do_full_prev_group(struct mpl_mediaplayer *pl)
1286 {
1287 	/* Change the group (if not already on first group) */
1288 	do_prev_group(pl);
1289 
1290 	/* Whether there is a group change or not, we always go to the first track */
1291 	do_first_track(pl, true);
1292 }
1293 
do_full_next_group(struct mpl_mediaplayer * pl)1294 static void do_full_next_group(struct mpl_mediaplayer *pl)
1295 {
1296 	/* Change the group (if not already on last group) */
1297 	do_next_group(pl);
1298 
1299 	/* Whether there is a group change or not, we always go to the first track */
1300 	do_first_track(pl, true);
1301 }
1302 
do_full_first_group(struct mpl_mediaplayer * pl)1303 static void do_full_first_group(struct mpl_mediaplayer *pl)
1304 {
1305 	/* Change the group (if not already on first group) */
1306 	do_first_group(pl);
1307 
1308 	/* Whether there is a group change or not, we always go to the first track */
1309 	do_first_track(pl, true);
1310 }
1311 
do_full_last_group(struct mpl_mediaplayer * pl)1312 static void do_full_last_group(struct mpl_mediaplayer *pl)
1313 {
1314 	/* Change the group (if not already on last group) */
1315 	do_last_group(pl);
1316 
1317 	/* Whether there is a group change or not, we always go to the first track */
1318 	do_first_track(pl, true);
1319 }
1320 
do_full_goto_group(struct mpl_mediaplayer * pl,int32_t groupnum)1321 static void do_full_goto_group(struct mpl_mediaplayer *pl, int32_t groupnum)
1322 {
1323 	/* Change the group (if not already on given group) */
1324 	do_goto_group(pl, groupnum);
1325 
1326 	/* Whether there is a group change or not, we always go to the first track */
1327 	do_first_track(pl, true);
1328 }
1329 
mpl_set_state(uint8_t state)1330 static void mpl_set_state(uint8_t state)
1331 {
1332 	switch (state) {
1333 	case MEDIA_PROXY_STATE_INACTIVE:
1334 	case MEDIA_PROXY_STATE_PLAYING:
1335 	case MEDIA_PROXY_STATE_PAUSED:
1336 		(void)k_work_cancel_delayable(&media_player.pos_work);
1337 		break;
1338 	case MEDIA_PROXY_STATE_SEEKING:
1339 		(void)k_work_schedule(&media_player.pos_work, TRACK_POS_WORK_DELAY);
1340 		break;
1341 	default:
1342 		__ASSERT(false, "Invalid state: %u", state);
1343 	}
1344 
1345 	media_player.state = state;
1346 	media_proxy_pl_media_state_cb(media_player.state);
1347 }
1348 
1349 /* Command handlers (state machines) */
inactive_state_command_handler(const struct mpl_cmd * command)1350 static uint8_t inactive_state_command_handler(const struct mpl_cmd *command)
1351 {
1352 	uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1353 
1354 	LOG_DBG("Command opcode: %d", command->opcode);
1355 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1356 		if (command->use_param) {
1357 			LOG_DBG("Command parameter: %d", command->param);
1358 		}
1359 	}
1360 	switch (command->opcode) {
1361 	case MEDIA_PROXY_OP_PLAY: /* Fall-through - handle several cases identically */
1362 	case MEDIA_PROXY_OP_PAUSE:
1363 	case MEDIA_PROXY_OP_FAST_REWIND:
1364 	case MEDIA_PROXY_OP_FAST_FORWARD:
1365 	case MEDIA_PROXY_OP_STOP:
1366 	case MEDIA_PROXY_OP_MOVE_RELATIVE:
1367 	case MEDIA_PROXY_OP_PREV_SEGMENT:
1368 	case MEDIA_PROXY_OP_NEXT_SEGMENT:
1369 	case MEDIA_PROXY_OP_FIRST_SEGMENT:
1370 	case MEDIA_PROXY_OP_LAST_SEGMENT:
1371 	case MEDIA_PROXY_OP_GOTO_SEGMENT:
1372 		result_code = MEDIA_PROXY_CMD_PLAYER_INACTIVE;
1373 		break;
1374 	case MEDIA_PROXY_OP_PREV_TRACK:
1375 		do_prev_track(&media_player);
1376 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1377 		break;
1378 	case MEDIA_PROXY_OP_NEXT_TRACK:
1379 		/* TODO:
1380 		 * The case where the next track has been set explicitly breaks somewhat
1381 		 * with the "next" order hardcoded into the group and track structure
1382 		 */
1383 		do_next_track(&media_player);
1384 
1385 		/* For next track, the position is kept if the track */
1386 		/* does not change */
1387 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1388 		break;
1389 	case MEDIA_PROXY_OP_FIRST_TRACK:
1390 		do_first_track(&media_player, false);
1391 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1392 		break;
1393 	case MEDIA_PROXY_OP_LAST_TRACK:
1394 		do_last_track(&media_player);
1395 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1396 		break;
1397 	case MEDIA_PROXY_OP_GOTO_TRACK:
1398 		if (command->use_param) {
1399 			do_goto_track(&media_player, command->param);
1400 			mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1401 		} else {
1402 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1403 		}
1404 		break;
1405 	case MEDIA_PROXY_OP_PREV_GROUP:
1406 		do_full_prev_group(&media_player);
1407 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1408 		break;
1409 	case MEDIA_PROXY_OP_NEXT_GROUP:
1410 		do_full_next_group(&media_player);
1411 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1412 		break;
1413 	case MEDIA_PROXY_OP_FIRST_GROUP:
1414 		do_full_first_group(&media_player);
1415 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1416 		break;
1417 	case MEDIA_PROXY_OP_LAST_GROUP:
1418 		do_full_last_group(&media_player);
1419 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1420 		break;
1421 	case MEDIA_PROXY_OP_GOTO_GROUP:
1422 		if (command->use_param) {
1423 			do_full_goto_group(&media_player, command->param);
1424 			mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1425 		} else {
1426 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1427 		}
1428 
1429 		break;
1430 	default:
1431 		LOG_DBG("Invalid command: %d", command->opcode);
1432 		result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1433 		break;
1434 	}
1435 
1436 	return result_code;
1437 }
1438 
playing_state_command_handler(const struct mpl_cmd * command)1439 static uint8_t playing_state_command_handler(const struct mpl_cmd *command)
1440 {
1441 	uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1442 
1443 	LOG_DBG("Command opcode: %d", command->opcode);
1444 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1445 		if (command->use_param) {
1446 			LOG_DBG("Command parameter: %d", command->param);
1447 		}
1448 	}
1449 
1450 	switch (command->opcode) {
1451 	case MEDIA_PROXY_OP_PLAY:
1452 		/* Continue playing - i.e. do nothing */
1453 		break;
1454 	case MEDIA_PROXY_OP_PAUSE:
1455 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1456 		break;
1457 	case MEDIA_PROXY_OP_FAST_REWIND:
1458 		/* We're in playing state, seeking speed must have been zero */
1459 		media_player.seeking_speed_factor = -MPL_SEEKING_SPEED_FACTOR_STEP;
1460 		mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1461 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1462 		break;
1463 	case MEDIA_PROXY_OP_FAST_FORWARD:
1464 		/* We're in playing state, seeking speed must have been zero */
1465 		media_player.seeking_speed_factor = MPL_SEEKING_SPEED_FACTOR_STEP;
1466 		mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1467 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1468 		break;
1469 	case MEDIA_PROXY_OP_STOP:
1470 		set_track_position(0);
1471 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1472 		break;
1473 	case MEDIA_PROXY_OP_MOVE_RELATIVE:
1474 		if (command->use_param) {
1475 			set_relative_track_position(command->param);
1476 		} else {
1477 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1478 		}
1479 
1480 		break;
1481 	case MEDIA_PROXY_OP_PREV_SEGMENT:
1482 		/* Switch to previous segment if we are less than <margin> */
1483 		/* into the segment, otherwise go to start of segment */
1484 		if (media_player.track_pos - PREV_MARGIN <
1485 		    media_player.group->track->segment->pos) {
1486 			do_prev_segment(&media_player);
1487 		}
1488 		set_track_position(media_player.group->track->segment->pos);
1489 		break;
1490 	case MEDIA_PROXY_OP_NEXT_SEGMENT:
1491 		do_next_segment(&media_player);
1492 		set_track_position(media_player.group->track->segment->pos);
1493 		break;
1494 	case MEDIA_PROXY_OP_FIRST_SEGMENT:
1495 		do_first_segment(&media_player);
1496 		set_track_position(media_player.group->track->segment->pos);
1497 		break;
1498 	case MEDIA_PROXY_OP_LAST_SEGMENT:
1499 		do_last_segment(&media_player);
1500 		set_track_position(media_player.group->track->segment->pos);
1501 		break;
1502 	case MEDIA_PROXY_OP_GOTO_SEGMENT:
1503 		if (command->use_param) {
1504 			if (command->param != 0) {
1505 				do_goto_segment(&media_player, command->param);
1506 			}
1507 			/* If the argument to "goto segment" is zero, */
1508 			/* the segment shall stay the same, and the */
1509 			/* track position shall not change. */
1510 		} else {
1511 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1512 		}
1513 
1514 		break;
1515 	case MEDIA_PROXY_OP_PREV_TRACK:
1516 		do_prev_track(&media_player);
1517 		break;
1518 	case MEDIA_PROXY_OP_NEXT_TRACK:
1519 		do_next_track(&media_player);
1520 		break;
1521 	case MEDIA_PROXY_OP_FIRST_TRACK:
1522 		do_first_track(&media_player, false);
1523 		break;
1524 	case MEDIA_PROXY_OP_LAST_TRACK:
1525 		do_last_track(&media_player);
1526 		break;
1527 	case MEDIA_PROXY_OP_GOTO_TRACK:
1528 		if (command->use_param) {
1529 			do_goto_track(&media_player, command->param);
1530 		} else {
1531 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1532 		}
1533 
1534 		break;
1535 	case MEDIA_PROXY_OP_PREV_GROUP:
1536 		do_full_prev_group(&media_player);
1537 		break;
1538 	case MEDIA_PROXY_OP_NEXT_GROUP:
1539 		do_full_next_group(&media_player);
1540 		break;
1541 	case MEDIA_PROXY_OP_FIRST_GROUP:
1542 		do_full_first_group(&media_player);
1543 		break;
1544 	case MEDIA_PROXY_OP_LAST_GROUP:
1545 		do_full_last_group(&media_player);
1546 		break;
1547 	case MEDIA_PROXY_OP_GOTO_GROUP:
1548 		if (command->use_param) {
1549 			do_full_goto_group(&media_player, command->param);
1550 		} else {
1551 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1552 		}
1553 		break;
1554 	default:
1555 		LOG_DBG("Invalid command: %d", command->opcode);
1556 		result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1557 		break;
1558 	}
1559 
1560 	return result_code;
1561 }
1562 
paused_state_command_handler(const struct mpl_cmd * command)1563 static uint8_t paused_state_command_handler(const struct mpl_cmd *command)
1564 {
1565 	uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1566 
1567 	LOG_DBG("Command opcode: %d", command->opcode);
1568 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1569 		if (command->use_param) {
1570 			LOG_DBG("Command parameter: %d", command->param);
1571 		}
1572 	}
1573 
1574 	switch (command->opcode) {
1575 	case MEDIA_PROXY_OP_PLAY:
1576 		mpl_set_state(MEDIA_PROXY_STATE_PLAYING);
1577 		break;
1578 	case MEDIA_PROXY_OP_PAUSE:
1579 		/* No change */
1580 		break;
1581 	case MEDIA_PROXY_OP_FAST_REWIND:
1582 		/* We're in paused state, seeking speed must have been zero */
1583 		media_player.seeking_speed_factor = -MPL_SEEKING_SPEED_FACTOR_STEP;
1584 		mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1585 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1586 		break;
1587 	case MEDIA_PROXY_OP_FAST_FORWARD:
1588 		/* We're in paused state, seeking speed must have been zero */
1589 		media_player.seeking_speed_factor = MPL_SEEKING_SPEED_FACTOR_STEP;
1590 		mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1591 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1592 		break;
1593 	case MEDIA_PROXY_OP_STOP:
1594 		set_track_position(0);
1595 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1596 		break;
1597 	case MEDIA_PROXY_OP_MOVE_RELATIVE:
1598 		if (command->use_param) {
1599 			set_relative_track_position(command->param);
1600 		} else {
1601 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1602 		}
1603 
1604 		break;
1605 	case MEDIA_PROXY_OP_PREV_SEGMENT:
1606 		/* Switch to previous segment if we are less than 5 seconds */
1607 		/* into the segment, otherwise go to start of segment */
1608 		if (media_player.group->track->segment != NULL) {
1609 			if (media_player.track_pos - PREV_MARGIN <
1610 			    media_player.group->track->segment->pos) {
1611 				do_prev_segment(&media_player);
1612 			}
1613 
1614 			set_track_position(media_player.group->track->segment->pos);
1615 		} else {
1616 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1617 		}
1618 
1619 		break;
1620 	case MEDIA_PROXY_OP_NEXT_SEGMENT:
1621 		if (media_player.group->track->segment != NULL) {
1622 			do_next_segment(&media_player);
1623 			set_track_position(media_player.group->track->segment->pos);
1624 		} else {
1625 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1626 		}
1627 
1628 		break;
1629 	case MEDIA_PROXY_OP_FIRST_SEGMENT:
1630 		if (media_player.group->track->segment != NULL) {
1631 			do_first_segment(&media_player);
1632 			set_track_position(media_player.group->track->segment->pos);
1633 		} else {
1634 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1635 		}
1636 
1637 		break;
1638 	case MEDIA_PROXY_OP_LAST_SEGMENT:
1639 		if (media_player.group->track->segment != NULL) {
1640 			do_last_segment(&media_player);
1641 			set_track_position(media_player.group->track->segment->pos);
1642 		} else {
1643 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1644 		}
1645 
1646 		break;
1647 	case MEDIA_PROXY_OP_GOTO_SEGMENT:
1648 		if (command->use_param && media_player.group->track->segment != NULL) {
1649 			if (command->param != 0) {
1650 				do_goto_segment(&media_player, command->param);
1651 			}
1652 			/* If the argument to "goto segment" is zero, */
1653 			/* the segment shall stay the same, and the */
1654 			/* track position shall not change. */
1655 		} else {
1656 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1657 		}
1658 
1659 		break;
1660 	case MEDIA_PROXY_OP_PREV_TRACK:
1661 		do_prev_track(&media_player);
1662 		break;
1663 	case MEDIA_PROXY_OP_NEXT_TRACK:
1664 		do_next_track(&media_player);
1665 		/* For next track, the position is kept if the track */
1666 		/* does not change */
1667 		break;
1668 	case MEDIA_PROXY_OP_FIRST_TRACK:
1669 		do_first_track(&media_player, false);
1670 		break;
1671 	case MEDIA_PROXY_OP_LAST_TRACK:
1672 		do_last_track(&media_player);
1673 		break;
1674 	case MEDIA_PROXY_OP_GOTO_TRACK:
1675 		if (command->use_param) {
1676 			do_goto_track(&media_player, command->param);
1677 		} else {
1678 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1679 		}
1680 
1681 		break;
1682 	case MEDIA_PROXY_OP_PREV_GROUP:
1683 		do_full_prev_group(&media_player);
1684 		break;
1685 	case MEDIA_PROXY_OP_NEXT_GROUP:
1686 		do_full_next_group(&media_player);
1687 		break;
1688 	case MEDIA_PROXY_OP_FIRST_GROUP:
1689 		do_full_first_group(&media_player);
1690 		break;
1691 	case MEDIA_PROXY_OP_LAST_GROUP:
1692 		do_full_last_group(&media_player);
1693 		break;
1694 	case MEDIA_PROXY_OP_GOTO_GROUP:
1695 		if (command->use_param) {
1696 			do_full_goto_group(&media_player, command->param);
1697 		} else {
1698 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1699 		}
1700 
1701 		break;
1702 	default:
1703 		LOG_DBG("Invalid command: %d", command->opcode);
1704 		result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1705 		break;
1706 	}
1707 
1708 	return result_code;
1709 }
1710 
seeking_state_command_handler(const struct mpl_cmd * command)1711 static uint8_t seeking_state_command_handler(const struct mpl_cmd *command)
1712 {
1713 	uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1714 
1715 	LOG_DBG("Command opcode: %d", command->opcode);
1716 	if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1717 		if (command->use_param) {
1718 			LOG_DBG("Command parameter: %d", command->param);
1719 		}
1720 	}
1721 
1722 	switch (command->opcode) {
1723 	case MEDIA_PROXY_OP_PLAY:
1724 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1725 		mpl_set_state(MEDIA_PROXY_STATE_PLAYING);
1726 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1727 		break;
1728 	case MEDIA_PROXY_OP_PAUSE:
1729 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1730 		/* TODO: Set track and track position */
1731 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1732 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1733 		break;
1734 	case MEDIA_PROXY_OP_FAST_REWIND:
1735 		/* TODO: Here, and for FAST_FORWARD */
1736 		/* Decide on algorithm for multiple presses - add step (as */
1737 		/* now) or double/half? */
1738 		/* What about FR followed by FF? */
1739 		/* Currently, the seeking speed may also become	 zero */
1740 		/* Lowest value allowed by spec is -64, notify on change only */
1741 		if (media_player.seeking_speed_factor >= -(MEDIA_PROXY_SEEKING_SPEED_FACTOR_MAX
1742 						 - MPL_SEEKING_SPEED_FACTOR_STEP)) {
1743 			media_player.seeking_speed_factor -= MPL_SEEKING_SPEED_FACTOR_STEP;
1744 			media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1745 		}
1746 		break;
1747 	case MEDIA_PROXY_OP_FAST_FORWARD:
1748 		/* Highest value allowed by spec is 64, notify on change only */
1749 		if (media_player.seeking_speed_factor <= (MEDIA_PROXY_SEEKING_SPEED_FACTOR_MAX
1750 						- MPL_SEEKING_SPEED_FACTOR_STEP)) {
1751 			media_player.seeking_speed_factor += MPL_SEEKING_SPEED_FACTOR_STEP;
1752 			media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1753 		}
1754 		break;
1755 	case MEDIA_PROXY_OP_STOP:
1756 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1757 		set_track_position(0);
1758 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1759 		media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1760 		break;
1761 	case MEDIA_PROXY_OP_MOVE_RELATIVE:
1762 		if (command->use_param) {
1763 			set_relative_track_position(command->param);
1764 		} else {
1765 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1766 		}
1767 
1768 		break;
1769 	case MEDIA_PROXY_OP_PREV_SEGMENT:
1770 		/* Switch to previous segment if we are less than 5 seconds */
1771 		/* into the segment, otherwise go to start of segment */
1772 		if (media_player.track_pos - PREV_MARGIN <
1773 		    media_player.group->track->segment->pos) {
1774 			do_prev_segment(&media_player);
1775 		}
1776 		set_track_position(media_player.group->track->segment->pos);
1777 		break;
1778 	case MEDIA_PROXY_OP_NEXT_SEGMENT:
1779 		do_next_segment(&media_player);
1780 		set_track_position(media_player.group->track->segment->pos);
1781 		break;
1782 	case MEDIA_PROXY_OP_FIRST_SEGMENT:
1783 		do_first_segment(&media_player);
1784 		set_track_position(media_player.group->track->segment->pos);
1785 		break;
1786 	case MEDIA_PROXY_OP_LAST_SEGMENT:
1787 		do_last_segment(&media_player);
1788 		set_track_position(media_player.group->track->segment->pos);
1789 		break;
1790 	case MEDIA_PROXY_OP_GOTO_SEGMENT:
1791 		if (command->use_param) {
1792 			if (command->param != 0) {
1793 				do_goto_segment(&media_player, command->param);
1794 			}
1795 			/* If the argument to "goto segment" is zero, */
1796 			/* the segment shall stay the same, and the */
1797 			/* track position shall not change. */
1798 		} else {
1799 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1800 		}
1801 		break;
1802 	case MEDIA_PROXY_OP_PREV_TRACK:
1803 		do_prev_track(&media_player);
1804 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1805 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1806 		break;
1807 	case MEDIA_PROXY_OP_NEXT_TRACK:
1808 		do_next_track(&media_player);
1809 		/* For next track, the position is kept if the track */
1810 		/* does not change */
1811 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1812 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1813 		break;
1814 	case MEDIA_PROXY_OP_FIRST_TRACK:
1815 		do_first_track(&media_player, false);
1816 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1817 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1818 		break;
1819 	case MEDIA_PROXY_OP_LAST_TRACK:
1820 		do_last_track(&media_player);
1821 		media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1822 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1823 		break;
1824 	case MEDIA_PROXY_OP_GOTO_TRACK:
1825 		if (command->use_param) {
1826 			do_goto_track(&media_player, command->param);
1827 			media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1828 			mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1829 		} else {
1830 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1831 		}
1832 		break;
1833 	case MEDIA_PROXY_OP_PREV_GROUP:
1834 		do_full_prev_group(&media_player);
1835 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1836 		break;
1837 	case MEDIA_PROXY_OP_NEXT_GROUP:
1838 		do_full_next_group(&media_player);
1839 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1840 		break;
1841 	case MEDIA_PROXY_OP_FIRST_GROUP:
1842 		do_full_first_group(&media_player);
1843 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1844 		break;
1845 	case MEDIA_PROXY_OP_LAST_GROUP:
1846 		do_full_last_group(&media_player);
1847 		mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1848 		break;
1849 	case MEDIA_PROXY_OP_GOTO_GROUP:
1850 		if (command->use_param) {
1851 			do_full_goto_group(&media_player, command->param);
1852 			mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1853 		} else {
1854 			result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1855 		}
1856 		break;
1857 	default:
1858 		LOG_DBG("Invalid command: %d", command->opcode);
1859 		result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1860 		break;
1861 	}
1862 
1863 	return result_code;
1864 }
1865 
1866 static uint8_t (*command_handlers[MEDIA_PROXY_STATE_LAST])(const struct mpl_cmd *command) = {
1867 	inactive_state_command_handler,
1868 	playing_state_command_handler,
1869 	paused_state_command_handler,
1870 	seeking_state_command_handler,
1871 };
1872 
1873 #ifdef CONFIG_BT_MPL_OBJECTS
1874 /* Find a track by ID
1875  *
1876  * If found, return pointers to the group of the track and the track,
1877  * otherwise, the pointers returned are NULL
1878  *
1879  * Returns true if found, false otherwise
1880  */
find_track_by_id(const struct mpl_mediaplayer * pl,uint64_t id,struct mpl_group ** group,struct mpl_track ** track)1881 static bool find_track_by_id(const struct mpl_mediaplayer *pl, uint64_t id,
1882 			     struct mpl_group **group, struct mpl_track **track)
1883 {
1884 	struct mpl_group *tmp_group = pl->group;
1885 	struct mpl_track *tmp_track;
1886 
1887 	while (tmp_group->prev != NULL) {
1888 		tmp_group = tmp_group->prev;
1889 	}
1890 
1891 	while (tmp_group != NULL) {
1892 		tmp_track = tmp_group->track;
1893 
1894 		while (tmp_track->prev != NULL) {
1895 			tmp_track = tmp_track->prev;
1896 		}
1897 
1898 		while (tmp_track != 0) {
1899 			if (tmp_track->id == id) {
1900 				/* Found the track */
1901 				*group = tmp_group;
1902 				*track = tmp_track;
1903 				return true;
1904 			}
1905 
1906 			tmp_track = tmp_track->next;
1907 		}
1908 
1909 		tmp_group = tmp_group->next;
1910 	}
1911 
1912 	/* Track not found */
1913 	*group = NULL;
1914 	*track = NULL;
1915 	return false;
1916 }
1917 
1918 /* Find a group by ID
1919  *
1920  * If found, return pointer to the group, otherwise, the pointer returned is NULL
1921  *
1922  * Returns true if found, false otherwise
1923  */
find_group_by_id(const struct mpl_mediaplayer * pl,uint64_t id,struct mpl_group ** group)1924 static bool find_group_by_id(const struct mpl_mediaplayer *pl, uint64_t id,
1925 			     struct mpl_group **group)
1926 {
1927 	struct mpl_group *tmp_group = pl->group;
1928 
1929 	while (tmp_group->prev != NULL) {
1930 		tmp_group = tmp_group->prev;
1931 	}
1932 
1933 	while (tmp_group != NULL) {
1934 		if (tmp_group->id == id) {
1935 			/* Found the group */
1936 			*group = tmp_group;
1937 			return true;
1938 		}
1939 
1940 		tmp_group = tmp_group->next;
1941 	}
1942 
1943 	/* Group not found */
1944 	*group = NULL;
1945 	return false;
1946 }
1947 #endif /* CONFIG_BT_MPL_OBJECTS */
1948 
get_player_name(void)1949 static const char *get_player_name(void)
1950 {
1951 	return media_player.name;
1952 }
1953 
1954 #ifdef CONFIG_BT_MPL_OBJECTS
get_icon_id(void)1955 static uint64_t get_icon_id(void)
1956 {
1957 	return media_player.icon_id;
1958 }
1959 #endif /* CONFIG_BT_MPL_OBJECTS */
1960 
get_icon_url(void)1961 static const char *get_icon_url(void)
1962 {
1963 	return media_player.icon_url;
1964 }
1965 
get_track_title(void)1966 static const char *get_track_title(void)
1967 {
1968 	return media_player.group->track->title;
1969 }
1970 
get_track_duration(void)1971 static int32_t get_track_duration(void)
1972 {
1973 	return media_player.group->track->duration;
1974 }
1975 
get_track_position(void)1976 static int32_t get_track_position(void)
1977 {
1978 	return media_player.track_pos;
1979 }
1980 
set_track_position(int32_t position)1981 static void set_track_position(int32_t position)
1982 {
1983 	int32_t old_pos = media_player.track_pos;
1984 	int32_t new_pos;
1985 
1986 	if (position >= 0) {
1987 		if (position > media_player.group->track->duration) {
1988 			/* Do not go beyond end of track */
1989 			new_pos = media_player.group->track->duration;
1990 		} else {
1991 			new_pos = position;
1992 		}
1993 	} else {
1994 		/* Negative position, handle as offset from _end_ of track */
1995 		/* (Note minus sign below) */
1996 		if (position < -media_player.group->track->duration) {
1997 			new_pos = 0;
1998 		} else {
1999 			/* (Remember position is negative) */
2000 			new_pos = media_player.group->track->duration + position;
2001 		}
2002 	}
2003 
2004 	LOG_DBG("Pos. given: %d, resulting pos.: %d (duration is %d)", position, new_pos,
2005 		media_player.group->track->duration);
2006 
2007 	/* Notify when the position changes when not in the playing state, or if the position is set
2008 	 * to 0 which is a special value that typically indicates that the track has stopped or
2009 	 * changed. Since this might occur when media_player.group->track->duration is still 0, we
2010 	 * should always notify this value.
2011 	 */
2012 	if (new_pos != old_pos || new_pos == 0) {
2013 		/* Set new position and notify it */
2014 		media_player.track_pos = new_pos;
2015 
2016 		/* MCS 1.0, section 3.7.1, states:
2017 		 * to avoid an excessive number of notifications, the Track Position should
2018 		 * not be notified when the Media State is set to “Playing” and playback happens
2019 		 * at a constant speed.
2020 		 */
2021 		if (media_player.state != MEDIA_PROXY_STATE_PLAYING) {
2022 			media_proxy_pl_track_position_cb(new_pos);
2023 		}
2024 	}
2025 }
2026 
set_relative_track_position(int32_t rel_pos)2027 static void set_relative_track_position(int32_t rel_pos)
2028 {
2029 	int64_t pos;
2030 
2031 	pos = media_player.track_pos + rel_pos;
2032 	/* Clamp to allowed values */
2033 	pos = CLAMP(pos, 0, media_player.group->track->duration);
2034 
2035 	set_track_position((int32_t)pos);
2036 }
2037 
get_playback_speed(void)2038 static int8_t get_playback_speed(void)
2039 {
2040 	return media_player.playback_speed_param;
2041 }
2042 
set_playback_speed(int8_t speed)2043 static void set_playback_speed(int8_t speed)
2044 {
2045 	/* Set new speed parameter and notify, if different from current */
2046 	if (speed != media_player.playback_speed_param) {
2047 		media_player.playback_speed_param = speed;
2048 		media_proxy_pl_playback_speed_cb(media_player.playback_speed_param);
2049 	}
2050 }
2051 
get_seeking_speed(void)2052 static int8_t get_seeking_speed(void)
2053 {
2054 	return media_player.seeking_speed_factor;
2055 }
2056 
2057 #ifdef CONFIG_BT_MPL_OBJECTS
get_track_segments_id(void)2058 static uint64_t get_track_segments_id(void)
2059 {
2060 	return media_player.group->track->segments_id;
2061 }
2062 
get_current_track_id(void)2063 static uint64_t get_current_track_id(void)
2064 {
2065 	return media_player.group->track->id;
2066 }
2067 
set_current_track_id(uint64_t id)2068 static void set_current_track_id(uint64_t id)
2069 {
2070 	struct mpl_group *group;
2071 	struct mpl_track *track;
2072 
2073 	LOG_DBG_OBJ_ID("Track ID to set: ", id);
2074 
2075 	if (find_track_by_id(&media_player, id, &group, &track)) {
2076 		if (media_player.group != group) {
2077 			media_player.group = group;
2078 			do_group_change_notifications(&media_player);
2079 
2080 			/* Group change implies track change (even if same track in other group) */
2081 			media_player.group->track = track;
2082 			do_track_change_notifications(&media_player);
2083 
2084 		} else if (media_player.group->track != track) {
2085 			media_player.group->track = track;
2086 			do_track_change_notifications(&media_player);
2087 		}
2088 		return;
2089 	}
2090 
2091 	LOG_DBG("Track not found");
2092 
2093 	/* TODO: Should an error be returned here?
2094 	 * That would require a rewrite of the MPL api to add return values to the functions.
2095 	 */
2096 }
2097 
get_next_track_id(void)2098 static uint64_t get_next_track_id(void)
2099 {
2100 	/* If the next track has been set explicitly */
2101 	if (media_player.next_track_set) {
2102 		return media_player.next.track->id;
2103 	}
2104 
2105 	/* Normal playing order */
2106 	if (media_player.group->track->next) {
2107 		return media_player.group->track->next->id;
2108 	}
2109 
2110 	/* Return zero value to indicate that there is no next track */
2111 	return MPL_NO_TRACK_ID;
2112 }
2113 
set_next_track_id(uint64_t id)2114 static void set_next_track_id(uint64_t id)
2115 {
2116 	struct mpl_group *group;
2117 	struct mpl_track *track;
2118 
2119 	LOG_DBG_OBJ_ID("Next Track ID to set: ", id);
2120 
2121 	if (find_track_by_id(&media_player, id, &group, &track)) {
2122 
2123 		media_player.next_track_set = true;
2124 		media_player.next.group = group;
2125 		media_player.next.track = track;
2126 		media_proxy_pl_next_track_id_cb(id);
2127 		return;
2128 	}
2129 
2130 	LOG_DBG("Track not found");
2131 }
2132 
get_parent_group_id(void)2133 static uint64_t get_parent_group_id(void)
2134 {
2135 	return media_player.group->parent->id;
2136 }
2137 
get_current_group_id(void)2138 static uint64_t get_current_group_id(void)
2139 {
2140 	return media_player.group->id;
2141 }
2142 
set_current_group_id(uint64_t id)2143 static void set_current_group_id(uint64_t id)
2144 {
2145 	struct mpl_group *group;
2146 
2147 	LOG_DBG_OBJ_ID("Group ID to set: ", id);
2148 
2149 	if (find_group_by_id(&media_player, id, &group)) {
2150 
2151 		if (media_player.group != group) {
2152 			/* Change to found group */
2153 			media_player.group = group;
2154 			do_group_change_notifications(&media_player);
2155 
2156 			/* And change to first track in group */
2157 			do_first_track(&media_player, false);
2158 		}
2159 		return;
2160 	}
2161 
2162 	LOG_DBG("Group not found");
2163 }
2164 #endif /* CONFIG_BT_MPL_OBJECTS */
2165 
get_playing_order(void)2166 static uint8_t get_playing_order(void)
2167 {
2168 	return media_player.playing_order;
2169 }
2170 
set_playing_order(uint8_t order)2171 static void set_playing_order(uint8_t order)
2172 {
2173 	if (order != media_player.playing_order) {
2174 		if (BIT(order - 1) & media_player.playing_orders_supported) {
2175 			media_player.playing_order = order;
2176 			media_proxy_pl_playing_order_cb(media_player.playing_order);
2177 		}
2178 	}
2179 }
2180 
get_playing_orders_supported(void)2181 static uint16_t get_playing_orders_supported(void)
2182 {
2183 	return media_player.playing_orders_supported;
2184 }
2185 
get_media_state(void)2186 static uint8_t get_media_state(void)
2187 {
2188 	return media_player.state;
2189 }
2190 
send_command(const struct mpl_cmd * command)2191 static void send_command(const struct mpl_cmd *command)
2192 {
2193 	struct mpl_cmd_ntf ntf;
2194 
2195 	if (command->use_param) {
2196 		LOG_DBG("opcode: %d, param: %d", command->opcode, command->param);
2197 	} else {
2198 		LOG_DBG("opcode: %d", command->opcode);
2199 	}
2200 
2201 	if (media_player.state < MEDIA_PROXY_STATE_LAST) {
2202 		ntf.requested_opcode = command->opcode;
2203 		ntf.result_code = command_handlers[media_player.state](command);
2204 
2205 		media_proxy_pl_command_cb(&ntf);
2206 	} else {
2207 		LOG_DBG("INVALID STATE");
2208 	}
2209 }
2210 
get_commands_supported(void)2211 static uint32_t get_commands_supported(void)
2212 {
2213 	return media_player.opcodes_supported;
2214 }
2215 
2216 #ifdef CONFIG_BT_MPL_OBJECTS
2217 
parse_sci(struct bt_data * data,void * user_data)2218 static bool parse_sci(struct bt_data *data, void *user_data)
2219 {
2220 	LOG_DBG("type: %u len %u", data->type, data->data_len);
2221 	LOG_HEXDUMP_DBG(data->data, data->data_len, "param:");
2222 
2223 	if (data->type < MEDIA_PROXY_SEARCH_TYPE_TRACK_NAME ||
2224 	    data->type > MEDIA_PROXY_SEARCH_TYPE_ONLY_GROUPS) {
2225 		LOG_DBG("Invalid search type: %u", data->type);
2226 		return false;
2227 	}
2228 
2229 	return true;
2230 }
2231 
parse_search(const struct mpl_search * search)2232 static void parse_search(const struct mpl_search *search)
2233 {
2234 	bool search_failed = false;
2235 
2236 	if (search->len > SEARCH_LEN_MAX) {
2237 		LOG_WRN("Search too long (%d) - aborting", search->len);
2238 		search_failed = true;
2239 	} else {
2240 		uint8_t search_ltv[SEARCH_LEN_MAX];
2241 		struct net_buf_simple buf;
2242 
2243 		/* Copy so that we can parse it using the net_buf_simple when search is const */
2244 		memcpy(search_ltv, search->search, search->len);
2245 
2246 		net_buf_simple_init_with_data(&buf, search_ltv, search->len);
2247 
2248 		bt_data_parse(&buf, parse_sci, NULL);
2249 
2250 		if (buf.len != 0U) {
2251 			search_failed = true;
2252 		}
2253 	}
2254 
2255 	/* TODO: Add real search functionality. */
2256 	/* For now, just fake it. */
2257 
2258 	if (search_failed) {
2259 		media_player.search_results_id = 0;
2260 		media_proxy_pl_search_cb(MEDIA_PROXY_SEARCH_FAILURE);
2261 	} else {
2262 		/* Use current group as search result for now */
2263 		media_player.search_results_id = media_player.group->id;
2264 		media_proxy_pl_search_cb(MEDIA_PROXY_SEARCH_SUCCESS);
2265 	}
2266 
2267 	media_proxy_pl_search_results_id_cb(media_player.search_results_id);
2268 }
2269 
send_search(const struct mpl_search * search)2270 static void send_search(const struct mpl_search *search)
2271 {
2272 	if (search->len > SEARCH_LEN_MAX) {
2273 		LOG_WRN("Search too long: %d", search->len);
2274 	}
2275 
2276 	LOG_HEXDUMP_DBG(search->search, search->len, "Search");
2277 
2278 	parse_search(search);
2279 }
2280 
get_search_results_id(void)2281 static uint64_t get_search_results_id(void)
2282 {
2283 	return media_player.search_results_id;
2284 }
2285 #endif /* CONFIG_BT_MPL_OBJECTS */
2286 
get_content_ctrl_id(void)2287 static uint8_t get_content_ctrl_id(void)
2288 {
2289 	return media_player.content_ctrl_id;
2290 }
2291 
pos_work_cb(struct k_work * work)2292 static void pos_work_cb(struct k_work *work)
2293 {
2294 	const int32_t pos_diff_cs = TRACK_POS_WORK_DELAY_MS / 10; /* position is in centiseconds*/
2295 
2296 	if (media_player.state == MEDIA_PROXY_STATE_SEEKING) {
2297 		/* When seeking, apply the seeking speed factor */
2298 		set_relative_track_position(pos_diff_cs * media_player.seeking_speed_factor);
2299 	} else if (media_player.state == MEDIA_PROXY_STATE_PLAYING) {
2300 		set_relative_track_position(pos_diff_cs);
2301 	}
2302 
2303 	if (media_player.track_pos == media_player.group->track->duration) {
2304 		/* Go to next track */
2305 		do_next_track(&media_player);
2306 	}
2307 
2308 	(void)k_work_schedule(&media_player.pos_work, TRACK_POS_WORK_DELAY);
2309 }
2310 
media_proxy_pl_init(void)2311 int media_proxy_pl_init(void)
2312 {
2313 	static bool initialized;
2314 	int ret;
2315 
2316 	if (initialized) {
2317 		LOG_DBG("Already initialized");
2318 		return -EALREADY;
2319 	}
2320 
2321 	/* Get a Content Control ID */
2322 	ret = bt_ccid_alloc_value();
2323 	if (ret < 0) {
2324 		LOG_DBG("Could not allocate CCID: %d", ret);
2325 		return ret;
2326 	}
2327 	media_player.content_ctrl_id = (uint8_t)ret;
2328 
2329 	/* Set up the media control service */
2330 	/* TODO: Fix initialization - who initializes what
2331 	 * https://github.com/zephyrproject-rtos/zephyr/issues/42965
2332 	 * Temporarily only initializing if service is present
2333 	 */
2334 #ifdef CONFIG_BT_MCS
2335 #ifdef CONFIG_BT_MPL_OBJECTS
2336 	/* The test here is arguably needed as the objects cannot be accessed before bt_mcs_init is
2337 	 * called, but the set is to avoid the objects being accessed before properly initialized
2338 	 */
2339 	if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
2340 		LOG_ERR("Object busy");
2341 		return -EBUSY;
2342 	}
2343 
2344 	ret = bt_mcs_init(&ots_cbs);
2345 	if (ret < 0) {
2346 		LOG_ERR("Could not init MCS: %d", ret);
2347 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2348 
2349 		return ret;
2350 	}
2351 #else
2352 	ret = bt_mcs_init(NULL);
2353 	if (ret < 0) {
2354 		LOG_ERR("Could not init MCS: %d", ret);
2355 		return ret;
2356 	}
2357 #endif  /* CONFIG_BT_MPL_OBJECTS */
2358 	/* TODO: If anything below fails we should unregister MCS */
2359 #else
2360 	LOG_WRN("MCS not configured");
2361 #endif /* CONFIG_BT_MCS */
2362 
2363 #ifdef CONFIG_BT_MPL_OBJECTS
2364 	/* Initialize the object content buffer */
2365 	net_buf_simple_init(obj.content, 0);
2366 
2367 	/* Icon Object */
2368 	ret = add_icon_object(&media_player);
2369 	if (ret < 0) {
2370 		LOG_ERR("Unable to add icon object, error %d", ret);
2371 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2372 		return ret;
2373 	}
2374 
2375 	/* Add all tracks and groups to OTS */
2376 	ret = add_group_and_track_objects(&media_player);
2377 	if (ret < 0) {
2378 		LOG_ERR("Error adding tracks and groups to OTS, error %d", ret);
2379 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2380 		return ret;
2381 	}
2382 
2383 	/* Initial setup of Track Segments Object */
2384 	/* TODO: Later, this should be done when the tracks are added */
2385 	/* but for no only one of the tracks has segments .*/
2386 	ret = add_current_track_segments_object(&media_player);
2387 	if (ret < 0) {
2388 		LOG_ERR("Error adding Track Segments Object to OTS, error %d", ret);
2389 		atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2390 		return ret;
2391 	}
2392 
2393 	atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2394 #endif /* CONFIG_BT_MPL_OBJECTS */
2395 
2396 	/* Set up the calls structure */
2397 	media_player.calls.get_player_name              = get_player_name;
2398 #ifdef CONFIG_BT_MPL_OBJECTS
2399 	media_player.calls.get_icon_id                  = get_icon_id;
2400 #endif /* CONFIG_BT_MPL_OBJECTS */
2401 	media_player.calls.get_icon_url                 = get_icon_url;
2402 	media_player.calls.get_track_title              = get_track_title;
2403 	media_player.calls.get_track_duration           = get_track_duration;
2404 	media_player.calls.get_track_position           = get_track_position;
2405 	media_player.calls.set_track_position           = set_track_position;
2406 	media_player.calls.get_playback_speed           = get_playback_speed;
2407 	media_player.calls.set_playback_speed           = set_playback_speed;
2408 	media_player.calls.get_seeking_speed            = get_seeking_speed;
2409 #ifdef CONFIG_BT_MPL_OBJECTS
2410 	media_player.calls.get_track_segments_id        = get_track_segments_id;
2411 	media_player.calls.get_current_track_id         = get_current_track_id;
2412 	media_player.calls.set_current_track_id         = set_current_track_id;
2413 	media_player.calls.get_next_track_id            = get_next_track_id;
2414 	media_player.calls.set_next_track_id            = set_next_track_id;
2415 	media_player.calls.get_parent_group_id          = get_parent_group_id;
2416 	media_player.calls.get_current_group_id         = get_current_group_id;
2417 	media_player.calls.set_current_group_id         = set_current_group_id;
2418 #endif /* CONFIG_BT_MPL_OBJECTS */
2419 	media_player.calls.get_playing_order            = get_playing_order;
2420 	media_player.calls.set_playing_order            = set_playing_order;
2421 	media_player.calls.get_playing_orders_supported = get_playing_orders_supported;
2422 	media_player.calls.get_media_state              = get_media_state;
2423 	media_player.calls.send_command                 = send_command;
2424 	media_player.calls.get_commands_supported       = get_commands_supported;
2425 #ifdef CONFIG_BT_MPL_OBJECTS
2426 	media_player.calls.send_search                  = send_search;
2427 	media_player.calls.get_search_results_id        = get_search_results_id;
2428 #endif /* CONFIG_BT_MPL_OBJECTS */
2429 	media_player.calls.get_content_ctrl_id          = get_content_ctrl_id;
2430 
2431 	ret = media_proxy_pl_register(&media_player.calls);
2432 	if (ret < 0) {
2433 		LOG_ERR("Unable to register player");
2434 		return ret;
2435 	}
2436 
2437 	k_work_init_delayable(&media_player.pos_work, pos_work_cb);
2438 
2439 	initialized = true;
2440 	return 0;
2441 }
2442 
2443 #if CONFIG_BT_MPL_LOG_LEVEL_DBG /* Special commands for debugging */
2444 
mpl_debug_dump_state(void)2445 void mpl_debug_dump_state(void)
2446 {
2447 #if CONFIG_BT_MPL_OBJECTS
2448 	char t[BT_OTS_OBJ_ID_STR_LEN];
2449 	struct mpl_group *group;
2450 	struct mpl_track *track;
2451 #endif /* CONFIG_BT_MPL_OBJECTS */
2452 
2453 	LOG_DBG("Mediaplayer name: %s", media_player.name);
2454 
2455 #if CONFIG_BT_MPL_OBJECTS
2456 	(void)bt_ots_obj_id_to_str(media_player.icon_id, t, sizeof(t));
2457 	LOG_DBG("Icon ID: %s", t);
2458 #endif /* CONFIG_BT_MPL_OBJECTS */
2459 
2460 	LOG_DBG("Icon URL: %s", media_player.icon_url);
2461 	LOG_DBG("Track position: %d", media_player.track_pos);
2462 	LOG_DBG("Media state: %d", media_player.state);
2463 	LOG_DBG("Playback speed parameter: %d", media_player.playback_speed_param);
2464 	LOG_DBG("Seeking speed factor: %d", media_player.seeking_speed_factor);
2465 	LOG_DBG("Playing order: %d", media_player.playing_order);
2466 	LOG_DBG("Playing orders supported: 0x%x", media_player.playing_orders_supported);
2467 	LOG_DBG("Opcodes supported: %d", media_player.opcodes_supported);
2468 	LOG_DBG("Content control ID: %d", media_player.content_ctrl_id);
2469 
2470 #if CONFIG_BT_MPL_OBJECTS
2471 	(void)bt_ots_obj_id_to_str(media_player.group->parent->id, t, sizeof(t));
2472 	LOG_DBG("Current group's parent: %s", t);
2473 
2474 	(void)bt_ots_obj_id_to_str(media_player.group->id, t, sizeof(t));
2475 	LOG_DBG("Current group: %s", t);
2476 
2477 	(void)bt_ots_obj_id_to_str(media_player.group->track->id, t, sizeof(t));
2478 	LOG_DBG("Current track: %s", t);
2479 
2480 	if (media_player.next_track_set) {
2481 		(void)bt_ots_obj_id_to_str(media_player.next.track->id, t, sizeof(t));
2482 		LOG_DBG("Next track: %s", t);
2483 	} else if (media_player.group->track->next) {
2484 		(void)bt_ots_obj_id_to_str(media_player.group->track->next->id, t,
2485 					   sizeof(t));
2486 		LOG_DBG("Next track: %s", t);
2487 	} else {
2488 		LOG_DBG("No next track");
2489 	}
2490 
2491 	if (media_player.search_results_id) {
2492 		(void)bt_ots_obj_id_to_str(media_player.search_results_id, t, sizeof(t));
2493 		LOG_DBG("Search results: %s", t);
2494 	} else {
2495 		LOG_DBG("No search results");
2496 	}
2497 
2498 	LOG_DBG("Groups and tracks:");
2499 	group = media_player.group;
2500 
2501 	while (group->prev != NULL) {
2502 		group = group->prev;
2503 	}
2504 
2505 	while (group) {
2506 		(void)bt_ots_obj_id_to_str(group->id, t, sizeof(t));
2507 		LOG_DBG("Group: %s, %s", t, group->title);
2508 
2509 		(void)bt_ots_obj_id_to_str(group->parent->id, t, sizeof(t));
2510 		LOG_DBG("\tParent: %s, %s", t, group->parent->title);
2511 
2512 		track = group->track;
2513 		while (track->prev != NULL) {
2514 			track = track->prev;
2515 		}
2516 
2517 		while (track) {
2518 			(void)bt_ots_obj_id_to_str(track->id, t, sizeof(t));
2519 			LOG_DBG("\tTrack: %s, %s, duration: %d", t, track->title, track->duration);
2520 			track = track->next;
2521 		}
2522 
2523 		group = group->next;
2524 	}
2525 #endif /* CONFIG_BT_MPL_OBJECTS */
2526 }
2527 #endif /* CONFIG_BT_MPL_LOG_LEVEL_DBG */
2528 
2529 #if defined(CONFIG_BT_MPL_LOG_LEVEL_DBG) &&                                                        \
2530 	defined(CONFIG_BT_TESTING) /* Special commands for testing */
2531 
2532 #if CONFIG_BT_MPL_OBJECTS
mpl_test_unset_parent_group(void)2533 void mpl_test_unset_parent_group(void)
2534 {
2535 	LOG_DBG("Setting current group to be it's own parent");
2536 	media_player.group->parent = media_player.group;
2537 }
2538 #endif /* CONFIG_BT_MPL_OBJECTS */
2539 
mpl_test_media_state_set(uint8_t state)2540 void mpl_test_media_state_set(uint8_t state)
2541 {
2542 	mpl_set_state(state);
2543 }
2544 
mpl_test_player_name_changed_cb(void)2545 void mpl_test_player_name_changed_cb(void)
2546 {
2547 	media_proxy_pl_name_cb(media_player.name);
2548 }
2549 
mpl_test_player_icon_url_changed_cb(void)2550 void mpl_test_player_icon_url_changed_cb(void)
2551 {
2552 	media_proxy_pl_icon_url_cb(media_player.icon_url);
2553 }
2554 
mpl_test_track_changed_cb(void)2555 void mpl_test_track_changed_cb(void)
2556 {
2557 	media_proxy_pl_track_changed_cb();
2558 }
2559 
mpl_test_title_changed_cb(void)2560 void mpl_test_title_changed_cb(void)
2561 {
2562 	media_proxy_pl_track_title_cb(media_player.group->track->title);
2563 }
2564 
mpl_test_duration_changed_cb(void)2565 void mpl_test_duration_changed_cb(void)
2566 {
2567 	media_proxy_pl_track_duration_cb(media_player.group->track->duration);
2568 }
2569 
mpl_test_position_changed_cb(void)2570 void mpl_test_position_changed_cb(void)
2571 {
2572 	media_proxy_pl_track_position_cb(media_player.track_pos);
2573 }
2574 
mpl_test_playback_speed_changed_cb(void)2575 void mpl_test_playback_speed_changed_cb(void)
2576 {
2577 	media_proxy_pl_playback_speed_cb(media_player.playback_speed_param);
2578 }
2579 
mpl_test_seeking_speed_changed_cb(void)2580 void mpl_test_seeking_speed_changed_cb(void)
2581 {
2582 	media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
2583 }
2584 
2585 #ifdef CONFIG_BT_MPL_OBJECTS
mpl_test_current_track_id_changed_cb(void)2586 void mpl_test_current_track_id_changed_cb(void)
2587 {
2588 	media_proxy_pl_current_track_id_cb(media_player.group->track->id);
2589 }
2590 
mpl_test_next_track_id_changed_cb(void)2591 void mpl_test_next_track_id_changed_cb(void)
2592 {
2593 	media_proxy_pl_next_track_id_cb(media_player.group->track->next->id);
2594 }
2595 
mpl_test_parent_group_id_changed_cb(void)2596 void mpl_test_parent_group_id_changed_cb(void)
2597 {
2598 	media_proxy_pl_parent_group_id_cb(media_player.group->id);
2599 }
2600 
mpl_test_current_group_id_changed_cb(void)2601 void mpl_test_current_group_id_changed_cb(void)
2602 {
2603 	media_proxy_pl_current_group_id_cb(media_player.group->id);
2604 }
2605 #endif /* CONFIG_BT_MPL_OBJECTS */
2606 
mpl_test_playing_order_changed_cb(void)2607 void mpl_test_playing_order_changed_cb(void)
2608 {
2609 	media_proxy_pl_playing_order_cb(media_player.playing_order);
2610 }
2611 
mpl_test_media_state_changed_cb(void)2612 void mpl_test_media_state_changed_cb(void)
2613 {
2614 	media_proxy_pl_media_state_cb(media_player.playing_order);
2615 }
2616 
mpl_test_opcodes_supported_changed_cb(void)2617 void mpl_test_opcodes_supported_changed_cb(void)
2618 {
2619 	media_proxy_pl_commands_supported_cb(media_player.opcodes_supported);
2620 }
2621 
2622 #ifdef CONFIG_BT_MPL_OBJECTS
mpl_test_search_results_changed_cb(void)2623 void mpl_test_search_results_changed_cb(void)
2624 {
2625 	media_proxy_pl_search_cb(media_player.search_results_id);
2626 }
2627 #endif /* CONFIG_BT_MPL_OBJECTS */
2628 
2629 #endif /* CONFIG_BT_MPL_LOG_LEVEL_DBG && CONFIG_BT_TESTING */
2630