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