1 /** @file
2 * @brief Bluetooth Media Control Client/Protocol implementation
3 */
4
5 /*
6 * Copyright (c) 2019 - 2021 Nordic Semiconductor ASA
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/mcc.h>
18 #include <zephyr/bluetooth/att.h>
19 #include <zephyr/bluetooth/audio/mcs.h>
20 #include <zephyr/bluetooth/audio/media_proxy.h>
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/conn.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/services/ots.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/device.h>
27 #include <zephyr/init.h>
28 #include <zephyr/kernel.h>
29 #include <zephyr/logging/log.h>
30 #include <zephyr/net/buf.h>
31 #include <zephyr/sys/__assert.h>
32 #include <zephyr/sys/atomic.h>
33 #include <zephyr/sys/byteorder.h>
34 #include <zephyr/sys/check.h>
35 #include <zephyr/sys/util.h>
36 #include <zephyr/sys/util_macro.h>
37 #include <zephyr/types.h>
38
39 #include "../services/ots/ots_client_internal.h"
40 #include "common/bt_str.h"
41 #include "mcc_internal.h"
42 #include "mcs_internal.h"
43
44 /* TODO: Temporarily copied here from media_proxy_internal.h - clean up */
45 /* Debug output of 48 bit Object ID value */
46 /* (Zephyr does not yet support debug output of more than 32 bit values.) */
47 /* Takes a text and a 64-bit integer as input */
48 #define LOG_DBG_OBJ_ID(text, id64) \
49 do { \
50 if (IS_ENABLED(CONFIG_BT_MCS_LOG_LEVEL)) { \
51 char t[BT_OTS_OBJ_ID_STR_LEN]; \
52 (void)bt_ots_obj_id_to_str(id64, t, sizeof(t)); \
53 LOG_DBG(text "0x%s", t); \
54 } \
55 } while (0)
56
57 LOG_MODULE_REGISTER(bt_mcc, CONFIG_BT_MCC_LOG_LEVEL);
58
59 static struct mcs_instance_t mcs_instance;
60 static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
61
62 static struct bt_mcc_cb *mcc_cb;
63 static bool subscribe_all;
64
65 #ifdef CONFIG_BT_MCC_OTS
66 NET_BUF_SIMPLE_DEFINE_STATIC(otc_obj_buf, CONFIG_BT_MCC_OTC_OBJ_BUF_SIZE);
67 static struct bt_ots_client_cb otc_cb;
68 #endif /* CONFIG_BT_MCC_OTS */
69
70
71
72 #ifdef CONFIG_BT_MCC_OTS
73 void on_obj_selected(struct bt_ots_client *otc_inst,
74 struct bt_conn *conn, int err);
75
76 void on_object_metadata(struct bt_ots_client *otc_inst,
77 struct bt_conn *conn, int err,
78 uint8_t metadata_read);
79
80 int on_icon_content(struct bt_ots_client *otc_inst,
81 struct bt_conn *conn, uint32_t offset,
82 uint32_t len, uint8_t *data_p, bool is_complete);
83 #endif /* CONFIG_BT_MCC_OTS */
84
lookup_inst_by_conn(struct bt_conn * conn)85 struct mcs_instance_t *lookup_inst_by_conn(struct bt_conn *conn)
86 {
87 if (conn == NULL) {
88 return NULL;
89 }
90
91 /* TODO: Expand when supporting more instances */
92 return &mcs_instance;
93 }
94
mcc_player_name_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)95 static void mcc_player_name_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
96 {
97 int cb_err = err;
98 char name[CONFIG_BT_MCC_MEDIA_PLAYER_NAME_MAX];
99
100 LOG_DBG("err: 0x%02x, length: %d, data: %p", err, length, data);
101
102 if (err) {
103 LOG_DBG("err: 0x%02x", err);
104 } else if (!data) {
105 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
106 } else {
107 LOG_HEXDUMP_DBG(data, length, "Player name read");
108
109 if (length >= sizeof(name)) {
110 length = sizeof(name) - 1;
111 }
112
113 (void)memcpy(&name, data, length);
114 name[length] = '\0';
115 LOG_DBG("Player name: %s", name);
116 }
117
118 if (mcc_cb && mcc_cb->read_player_name) {
119 mcc_cb->read_player_name(conn, cb_err, name);
120 }
121 }
122
mcc_read_player_name_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)123 static uint8_t mcc_read_player_name_cb(struct bt_conn *conn, uint8_t err,
124 struct bt_gatt_read_params *params,
125 const void *data, uint16_t length)
126 {
127 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
128
129 mcs_inst->busy = false;
130 mcc_player_name_cb(conn, err, data, length);
131
132 return BT_GATT_ITER_STOP;
133 }
134
135
136 #ifdef CONFIG_BT_MCC_OTS
mcc_read_icon_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)137 static uint8_t mcc_read_icon_obj_id_cb(struct bt_conn *conn, uint8_t err,
138 struct bt_gatt_read_params *params,
139 const void *data, uint16_t length)
140 {
141 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
142 int cb_err = err;
143 uint8_t *pid = (uint8_t *)data;
144 uint64_t id = 0;
145
146 mcs_inst->busy = false;
147 LOG_DBG("err: 0x%02x, length: %d, data: %p", err, length, data);
148 if (err) {
149 LOG_DBG("err: 0x%02x", err);
150 } else if ((!pid) || (length != BT_OTS_OBJ_ID_SIZE)) {
151 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
152 } else {
153 LOG_HEXDUMP_DBG(pid, length, "Icon Object ID");
154 id = sys_get_le48(pid);
155 LOG_DBG_OBJ_ID("Icon Object ID: ", id);
156
157 if (!BT_MCS_VALID_OBJ_ID(id)) {
158 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
159 }
160 }
161
162 if (mcc_cb && mcc_cb->read_icon_obj_id) {
163 mcc_cb->read_icon_obj_id(conn, cb_err, id);
164 }
165
166 return BT_GATT_ITER_STOP;
167 }
168 #endif /* CONFIG_BT_MCC_OTS */
169
170 #if defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL)
mcc_read_icon_url_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)171 static uint8_t mcc_read_icon_url_cb(struct bt_conn *conn, uint8_t err,
172 struct bt_gatt_read_params *params,
173 const void *data, uint16_t length)
174 {
175 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
176 int cb_err = err;
177 char url[CONFIG_BT_MCC_ICON_URL_MAX];
178
179 mcs_inst->busy = false;
180 LOG_DBG("err: 0x%02x, length: %d, data: %p", err, length, data);
181 if (err) {
182 LOG_DBG("err: 0x%02x", err);
183 } else if (!data) {
184 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
185 } else if (length >= sizeof(url)) {
186 cb_err = BT_ATT_ERR_INSUFFICIENT_RESOURCES;
187 } else {
188 LOG_HEXDUMP_DBG(data, length, "Icon URL");
189 (void)memcpy(&url, data, length);
190 url[length] = '\0';
191 LOG_DBG("Icon URL: %s", url);
192 }
193
194 if (mcc_cb && mcc_cb->read_icon_url) {
195 mcc_cb->read_icon_url(conn, cb_err, url);
196 }
197
198 return BT_GATT_ITER_STOP;
199 }
200 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL) */
201
202 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE)
mcc_track_title_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)203 static void mcc_track_title_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
204 {
205 int cb_err = err;
206 char title[CONFIG_BT_MCC_TRACK_TITLE_MAX];
207
208 if (err) {
209 LOG_DBG("err: 0x%02x", err);
210 } else if (!data) {
211 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
212 } else {
213 LOG_HEXDUMP_DBG(data, length, "Track title");
214 if (length >= sizeof(title)) {
215 /* If the description is too long; clip it. */
216 length = sizeof(title) - 1;
217 }
218 (void)memcpy(&title, data, length);
219 title[length] = '\0';
220 LOG_DBG("Track title: %s", title);
221 }
222
223 if (mcc_cb && mcc_cb->read_track_title) {
224 mcc_cb->read_track_title(conn, cb_err, title);
225 }
226 }
227
mcc_read_track_title_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)228 static uint8_t mcc_read_track_title_cb(struct bt_conn *conn, uint8_t err,
229 struct bt_gatt_read_params *params,
230 const void *data, uint16_t length)
231 {
232 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
233
234 mcs_inst->busy = false;
235 mcc_track_title_cb(conn, err, data, length);
236
237 return BT_GATT_ITER_STOP;
238 }
239 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE)*/
240
241 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
mcc_track_duration_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)242 static void mcc_track_duration_cb(struct bt_conn *conn, uint8_t err, const void *data,
243 uint16_t length)
244 {
245 int cb_err = err;
246 int32_t dur = 0;
247
248 if (err) {
249 LOG_DBG("err: 0x%02x", err);
250 } else if ((!data) || (length != sizeof(dur))) {
251 LOG_DBG("length: %d, data: %p", length, data);
252 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
253 } else {
254 dur = sys_get_le32((uint8_t *)data);
255 LOG_DBG("Track duration: %d", dur);
256 LOG_HEXDUMP_DBG(data, sizeof(int32_t), "Track duration");
257 }
258
259 if (mcc_cb && mcc_cb->read_track_duration) {
260 mcc_cb->read_track_duration(conn, cb_err, dur);
261 }
262 }
263
mcc_read_track_duration_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)264 static uint8_t mcc_read_track_duration_cb(struct bt_conn *conn, uint8_t err,
265 struct bt_gatt_read_params *params,
266 const void *data, uint16_t length)
267 {
268 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
269
270 mcs_inst->busy = false;
271 mcc_track_duration_cb(conn, err, data, length);
272
273 return BT_GATT_ITER_STOP;
274 }
275 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
276
277 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
mcc_track_position_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)278 static void mcc_track_position_cb(struct bt_conn *conn, uint8_t err, const void *data,
279 uint16_t length)
280 {
281 int cb_err = err;
282 int32_t pos = 0;
283
284 if (err) {
285 LOG_DBG("err: 0x%02x", err);
286 } else if ((!data) || (length != sizeof(pos))) {
287 LOG_DBG("length: %d, data: %p", length, data);
288 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
289 } else {
290 pos = sys_get_le32((uint8_t *)data);
291 LOG_DBG("Track position: %d", pos);
292 LOG_HEXDUMP_DBG(data, sizeof(pos), "Track position");
293 }
294
295 if (mcc_cb && mcc_cb->read_track_position) {
296 mcc_cb->read_track_position(conn, cb_err, pos);
297 }
298 }
299
mcc_read_track_position_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)300 static uint8_t mcc_read_track_position_cb(struct bt_conn *conn, uint8_t err,
301 struct bt_gatt_read_params *params,
302 const void *data, uint16_t length)
303 {
304 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
305
306 mcs_inst->busy = false;
307 mcc_track_position_cb(conn, err, data, length);
308
309 return BT_GATT_ITER_STOP;
310 }
311 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
312
313 #if defined(CONFIG_BT_MCC_SET_TRACK_POSITION)
mcs_write_track_position_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)314 static void mcs_write_track_position_cb(struct bt_conn *conn, uint8_t err,
315 struct bt_gatt_write_params *params)
316 {
317 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
318 int cb_err = err;
319 int32_t pos = 0;
320
321 mcs_inst->busy = false;
322 if (err) {
323 LOG_DBG("err: 0x%02x", err);
324 } else if (!params->data || params->length != sizeof(pos)) {
325 LOG_DBG("length: %d, data: %p", params->length, params->data);
326 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
327 } else {
328 pos = sys_get_le32((uint8_t *)params->data);
329 LOG_DBG("Track position: %d", pos);
330 LOG_HEXDUMP_DBG(params->data, sizeof(pos), "Track position in callback");
331 }
332
333 if (mcc_cb && mcc_cb->set_track_position) {
334 mcc_cb->set_track_position(conn, cb_err, pos);
335 }
336 }
337 #endif /* defined(CONFIG_BT_MCC_SET_TRACK_POSITION) */
338
339 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
mcc_playback_speed_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)340 static void mcc_playback_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
341 uint16_t length)
342 {
343 int cb_err = err;
344 int8_t speed = 0;
345
346 if (err) {
347 LOG_DBG("err: 0x%02x", err);
348 } else if ((!data) || (length != sizeof(speed))) {
349 LOG_DBG("length: %d, data: %p", length, data);
350 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
351 } else {
352 speed = *(int8_t *)data;
353 LOG_DBG("Playback speed: %d", speed);
354 LOG_HEXDUMP_DBG(data, sizeof(int8_t), "Playback speed");
355 }
356
357 if (mcc_cb && mcc_cb->read_playback_speed) {
358 mcc_cb->read_playback_speed(conn, cb_err, speed);
359 }
360 }
361
mcc_read_playback_speed_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)362 static uint8_t mcc_read_playback_speed_cb(struct bt_conn *conn, uint8_t err,
363 struct bt_gatt_read_params *params,
364 const void *data, uint16_t length)
365 {
366 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
367
368 mcs_inst->busy = false;
369 mcc_playback_speed_cb(conn, err, data, length);
370
371 return BT_GATT_ITER_STOP;
372 }
373 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
374
375 #if defined(CONFIG_BT_MCC_SET_PLAYBACK_SPEED)
mcs_write_playback_speed_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)376 static void mcs_write_playback_speed_cb(struct bt_conn *conn, uint8_t err,
377 struct bt_gatt_write_params *params)
378 {
379 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
380 int cb_err = err;
381 int8_t speed = 0;
382
383 mcs_inst->busy = false;
384 if (err) {
385 LOG_DBG("err: 0x%02x", err);
386 } else if (!params->data || params->length != sizeof(speed)) {
387 LOG_DBG("length: %d, data: %p", params->length, params->data);
388 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
389 } else {
390 speed = *(int8_t *)params->data;
391 LOG_DBG("Playback_speed: %d", speed);
392 }
393
394 if (mcc_cb && mcc_cb->set_playback_speed) {
395 mcc_cb->set_playback_speed(conn, cb_err, speed);
396 }
397 }
398 #endif /* defined (CONFIG_BT_MCC_SET_PLAYBACK_SPEED) */
399
400 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
mcc_seeking_speed_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)401 static void mcc_seeking_speed_cb(struct bt_conn *conn, uint8_t err, const void *data,
402 uint16_t length)
403 {
404 int cb_err = err;
405 int8_t speed = 0;
406
407 if (err) {
408 LOG_DBG("err: 0x%02x", err);
409 } else if ((!data) || (length != sizeof(speed))) {
410 LOG_DBG("length: %d, data: %p", length, data);
411 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
412 } else {
413 speed = *(int8_t *)data;
414 LOG_DBG("Seeking speed: %d", speed);
415 LOG_HEXDUMP_DBG(data, sizeof(int8_t), "Seeking speed");
416 }
417
418 if (mcc_cb && mcc_cb->read_seeking_speed) {
419 mcc_cb->read_seeking_speed(conn, cb_err, speed);
420 }
421 }
422
mcc_read_seeking_speed_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)423 static uint8_t mcc_read_seeking_speed_cb(struct bt_conn *conn, uint8_t err,
424 struct bt_gatt_read_params *params,
425 const void *data, uint16_t length)
426 {
427 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
428
429 mcs_inst->busy = false;
430 mcc_seeking_speed_cb(conn, err, data, length);
431
432 return BT_GATT_ITER_STOP;
433 }
434 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
435
436 #ifdef CONFIG_BT_MCC_OTS
mcc_read_segments_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)437 static uint8_t mcc_read_segments_obj_id_cb(struct bt_conn *conn, uint8_t err,
438 struct bt_gatt_read_params *params,
439 const void *data, uint16_t length)
440 {
441 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
442 int cb_err = err;
443 uint8_t *pid = (uint8_t *)data;
444 uint64_t id = 0;
445
446 mcs_inst->busy = false;
447 if (err) {
448 LOG_DBG("err: 0x%02x", err);
449 } else if ((!pid) || (length != BT_OTS_OBJ_ID_SIZE)) {
450 LOG_DBG("length: %d, data: %p", length, data);
451 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
452 } else {
453 LOG_HEXDUMP_DBG(pid, length, "Segments Object ID");
454 id = sys_get_le48(pid);
455 LOG_DBG_OBJ_ID("Segments Object ID: ", id);
456
457 if (!BT_MCS_VALID_OBJ_ID(id)) {
458 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
459 }
460 }
461
462 if (mcc_cb && mcc_cb->read_segments_obj_id) {
463 mcc_cb->read_segments_obj_id(conn, cb_err, id);
464 }
465
466 return BT_GATT_ITER_STOP;
467 }
468
mcc_current_track_obj_id_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)469 static void mcc_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
470 uint16_t length)
471 {
472 int cb_err = err;
473 uint8_t *pid = (uint8_t *)data;
474 uint64_t id = 0;
475
476 if (err) {
477 LOG_DBG("err: 0x%02x", err);
478 } else if ((!pid) || (length != BT_OTS_OBJ_ID_SIZE)) {
479 LOG_DBG("length: %d, data: %p", length, data);
480 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
481 } else {
482 LOG_HEXDUMP_DBG(pid, length, "Current Track Object ID");
483 id = sys_get_le48(pid);
484 LOG_DBG_OBJ_ID("Current Track Object ID: ", id);
485
486 if (!BT_MCS_VALID_OBJ_ID(id)) {
487 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
488 }
489 }
490
491 if (mcc_cb && mcc_cb->read_current_track_obj_id) {
492 mcc_cb->read_current_track_obj_id(conn, cb_err, id);
493 }
494 }
495
mcc_read_current_track_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)496 static uint8_t mcc_read_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
497 struct bt_gatt_read_params *params,
498 const void *data, uint16_t length)
499 {
500 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
501
502 mcs_inst->busy = false;
503 mcc_current_track_obj_id_cb(conn, err, data, length);
504
505 return BT_GATT_ITER_STOP;
506 }
507
mcs_write_current_track_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)508 static void mcs_write_current_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
509 struct bt_gatt_write_params *params)
510 {
511 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
512 int cb_err = err;
513 uint64_t obj_id = 0;
514
515 mcs_inst->busy = false;
516 if (err) {
517 LOG_DBG("err: 0x%02x", err);
518 } else if (!params->data || params->length != BT_OTS_OBJ_ID_SIZE) {
519 LOG_DBG("length: %d, data: %p", params->length, params->data);
520 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
521 } else {
522 obj_id = sys_get_le48((const uint8_t *)params->data);
523 LOG_DBG_OBJ_ID("Object ID: ", obj_id);
524
525 if (!BT_MCS_VALID_OBJ_ID(obj_id)) {
526 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
527 }
528 }
529
530 if (mcc_cb && mcc_cb->set_current_track_obj_id) {
531 mcc_cb->set_current_track_obj_id(conn, cb_err, obj_id);
532 }
533 }
534
mcc_next_track_obj_id_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)535 static void mcc_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
536 uint16_t length)
537 {
538 int cb_err = err;
539 uint8_t *pid = (uint8_t *)data;
540 uint64_t id = 0;
541
542 if (err) {
543 LOG_DBG("err: 0x%02x", err);
544 } else if (length == 0) {
545 LOG_DBG("Characteristic is empty");
546 } else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
547 LOG_DBG("length: %d, data: %p", length, data);
548 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
549 } else {
550 LOG_HEXDUMP_DBG(pid, length, "Next Track Object ID");
551 id = sys_get_le48(pid);
552 LOG_DBG_OBJ_ID("Next Track Object ID: ", id);
553
554 if (!BT_MCS_VALID_OBJ_ID(id)) {
555 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
556 }
557 }
558
559 if (mcc_cb && mcc_cb->read_next_track_obj_id) {
560 mcc_cb->read_next_track_obj_id(conn, cb_err, id);
561 }
562 }
563
mcc_read_next_track_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)564 static uint8_t mcc_read_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
565 struct bt_gatt_read_params *params,
566 const void *data, uint16_t length)
567 {
568 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
569
570 mcs_inst->busy = false;
571 mcc_next_track_obj_id_cb(conn, err, data, length);
572
573 return BT_GATT_ITER_STOP;
574 }
575
mcs_write_next_track_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)576 static void mcs_write_next_track_obj_id_cb(struct bt_conn *conn, uint8_t err,
577 struct bt_gatt_write_params *params)
578 {
579 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
580 int cb_err = err;
581 uint64_t obj_id = 0;
582
583 mcs_inst->busy = false;
584 if (err) {
585 LOG_DBG("err: 0x%02x", err);
586 } else if (!params->data || params->length != BT_OTS_OBJ_ID_SIZE) {
587 LOG_DBG("length: %d, data: %p", params->length, params->data);
588 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
589 } else {
590 obj_id = sys_get_le48((const uint8_t *)params->data);
591 LOG_DBG_OBJ_ID("Object ID: ", obj_id);
592
593 if (!BT_MCS_VALID_OBJ_ID(obj_id)) {
594 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
595 }
596 }
597
598 if (mcc_cb && mcc_cb->set_next_track_obj_id) {
599 mcc_cb->set_next_track_obj_id(conn, cb_err, obj_id);
600 }
601 }
602
mcc_parent_group_obj_id_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)603 static void mcc_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
604 uint16_t length)
605 {
606 int cb_err = err;
607 uint8_t *pid = (uint8_t *)data;
608 uint64_t id = 0;
609
610 if (err) {
611 LOG_DBG("err: 0x%02x", err);
612 } else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
613 LOG_DBG("length: %d, data: %p", length, data);
614 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
615 } else {
616 LOG_HEXDUMP_DBG(pid, length, "Parent Group Object ID");
617 id = sys_get_le48(pid);
618 LOG_DBG_OBJ_ID("Parent Group Object ID: ", id);
619
620 if (!BT_MCS_VALID_OBJ_ID(id)) {
621 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
622 }
623 }
624
625 if (mcc_cb && mcc_cb->read_parent_group_obj_id) {
626 mcc_cb->read_parent_group_obj_id(conn, cb_err, id);
627 }
628 }
629
mcc_read_parent_group_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)630 static uint8_t mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
631 struct bt_gatt_read_params *params,
632 const void *data, uint16_t length)
633 {
634 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
635
636 mcs_inst->busy = false;
637 mcc_parent_group_obj_id_cb(conn, err, data, length);
638
639 return BT_GATT_ITER_STOP;
640 }
641
mcc_current_group_obj_id_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)642 static void mcc_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err, const void *data,
643 uint16_t length)
644 {
645 int cb_err = err;
646 uint8_t *pid = (uint8_t *)data;
647 uint64_t id = 0;
648
649 if (err) {
650 LOG_DBG("err: 0x%02x", err);
651 } else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
652 LOG_DBG("length: %d, data: %p", length, data);
653 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
654 } else {
655 LOG_HEXDUMP_DBG(pid, length, "Current Group Object ID");
656 id = sys_get_le48(pid);
657 LOG_DBG_OBJ_ID("Current Group Object ID: ", id);
658
659 if (!BT_MCS_VALID_OBJ_ID(id)) {
660 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
661 }
662 }
663
664 if (mcc_cb && mcc_cb->read_current_group_obj_id) {
665 mcc_cb->read_current_group_obj_id(conn, cb_err, id);
666 }
667 }
668
mcc_read_current_group_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)669 static uint8_t mcc_read_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
670 struct bt_gatt_read_params *params,
671 const void *data, uint16_t length)
672 {
673 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
674
675 mcs_inst->busy = false;
676 mcc_current_group_obj_id_cb(conn, err, data, length);
677
678 return BT_GATT_ITER_STOP;
679 }
680
mcs_write_current_group_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)681 static void mcs_write_current_group_obj_id_cb(struct bt_conn *conn, uint8_t err,
682 struct bt_gatt_write_params *params)
683 {
684 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
685 int cb_err = err;
686 uint64_t obj_id = 0;
687
688 mcs_inst->busy = false;
689 if (err) {
690 LOG_DBG("err: 0x%02x", err);
691 } else if (!params->data || params->length != BT_OTS_OBJ_ID_SIZE) {
692 LOG_DBG("length: %d, data: %p", params->length, params->data);
693 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
694 } else {
695 obj_id = sys_get_le48((const uint8_t *)params->data);
696 LOG_DBG_OBJ_ID("Object ID: ", obj_id);
697
698 if (!BT_MCS_VALID_OBJ_ID(obj_id)) {
699 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
700 }
701 }
702
703 if (mcc_cb && mcc_cb->set_current_group_obj_id) {
704 mcc_cb->set_current_group_obj_id(conn, cb_err, obj_id);
705 }
706 }
707 #endif /* CONFIG_BT_MCC_OTS */
708
709 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
mcc_playing_order_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)710 static void mcc_playing_order_cb(struct bt_conn *conn, uint8_t err, const void *data,
711 uint16_t length)
712 {
713 int cb_err = err;
714 uint8_t order = 0;
715
716 if (err) {
717 LOG_DBG("err: 0x%02x", err);
718 } else if ((!data) || (length != sizeof(order))) {
719 LOG_DBG("length: %d, data: %p", length, data);
720 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
721 } else {
722 order = *(uint8_t *)data;
723 LOG_DBG("Playing order: %d", order);
724 LOG_HEXDUMP_DBG(data, sizeof(order), "Playing order");
725 }
726
727 if (mcc_cb && mcc_cb->read_playing_order) {
728 mcc_cb->read_playing_order(conn, cb_err, order);
729 }
730 }
731
mcc_read_playing_order_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)732 static uint8_t mcc_read_playing_order_cb(struct bt_conn *conn, uint8_t err,
733 struct bt_gatt_read_params *params,
734 const void *data, uint16_t length)
735 {
736 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
737
738 mcs_inst->busy = false;
739 mcc_playing_order_cb(conn, err, data, length);
740
741 return BT_GATT_ITER_STOP;
742 }
743 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
744
745 #if defined(CONFIG_BT_MCC_SET_PLAYING_ORDER)
mcs_write_playing_order_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)746 static void mcs_write_playing_order_cb(struct bt_conn *conn, uint8_t err,
747 struct bt_gatt_write_params *params)
748 {
749 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
750 int cb_err = err;
751 uint8_t order = 0;
752
753 mcs_inst->busy = false;
754 if (err) {
755 LOG_DBG("err: 0x%02x", err);
756 } else if (!params->data || params->length != sizeof(order)) {
757 LOG_DBG("length: %d, data: %p", params->length, params->data);
758 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
759 } else {
760 order = *(uint8_t *)params->data;
761 LOG_DBG("Playing order: %d", *(uint8_t *)params->data);
762 }
763
764 if (mcc_cb && mcc_cb->set_playing_order) {
765 mcc_cb->set_playing_order(conn, cb_err, order);
766 }
767 }
768 #endif /* defined(CONFIG_BT_MCC_SET_PLAYING_ORDER) */
769
770 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED)
mcc_read_playing_orders_supported_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)771 static uint8_t mcc_read_playing_orders_supported_cb(struct bt_conn *conn, uint8_t err,
772 struct bt_gatt_read_params *params,
773 const void *data, uint16_t length)
774 {
775 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
776 int cb_err = err;
777 uint16_t orders = 0;
778
779 mcs_inst->busy = false;
780 if (err) {
781 LOG_DBG("err: 0x%02x", err);
782 } else if (!data || length != sizeof(orders)) {
783 LOG_DBG("length: %d, data: %p", length, data);
784 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
785 } else {
786 orders = sys_get_le16((uint8_t *)data);
787 LOG_DBG("Playing orders_supported: %d", orders);
788 LOG_HEXDUMP_DBG(data, sizeof(orders), "Playing orders supported");
789 }
790
791 if (mcc_cb && mcc_cb->read_playing_orders_supported) {
792 mcc_cb->read_playing_orders_supported(conn, cb_err, orders);
793 }
794
795 return BT_GATT_ITER_STOP;
796 }
797 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED) */
798
799 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
mcc_media_state_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)800 static void mcc_media_state_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length)
801 {
802 int cb_err = err;
803 uint8_t state = 0;
804
805 if (err) {
806 LOG_DBG("err: 0x%02x", err);
807 } else if (!data || length != sizeof(state)) {
808 LOG_DBG("length: %d, data: %p", length, data);
809 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
810 } else {
811 state = *(uint8_t *)data;
812 LOG_DBG("Media state: %d", state);
813 LOG_HEXDUMP_DBG(data, sizeof(uint8_t), "Media state");
814 }
815
816 if (mcc_cb && mcc_cb->read_media_state) {
817 mcc_cb->read_media_state(conn, cb_err, state);
818 }
819 }
820
mcc_read_media_state_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)821 static uint8_t mcc_read_media_state_cb(struct bt_conn *conn, uint8_t err,
822 struct bt_gatt_read_params *params,
823 const void *data, uint16_t length)
824 {
825 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
826
827 mcs_inst->busy = false;
828 mcc_media_state_cb(conn, err, data, length);
829
830 return BT_GATT_ITER_STOP;
831 }
832 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
833
834 #if defined(CONFIG_BT_MCC_SET_MEDIA_CONTROL_POINT)
mcs_write_cp_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)835 static void mcs_write_cp_cb(struct bt_conn *conn, uint8_t err,
836 struct bt_gatt_write_params *params)
837 {
838 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
839 int cb_err = err;
840 struct mpl_cmd cmd = {0};
841
842 mcs_inst->busy = false;
843
844 if (err) {
845 LOG_DBG("err: 0x%02x", err);
846 } else if (!params->data ||
847 (params->length != sizeof(cmd.opcode) &&
848 params->length != sizeof(cmd.opcode) + sizeof(cmd.param))) {
849 /* Above: No data pointer, or length not equal to any of the */
850 /* two possible values */
851 LOG_DBG("length: %d, data: %p", params->length, params->data);
852 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
853 } else {
854 (void)memcpy(&cmd.opcode, params->data, sizeof(cmd.opcode));
855 if (params->length == sizeof(cmd.opcode) + sizeof(cmd.param)) {
856 (void)memcpy(&cmd.param,
857 (char *)(params->data) + sizeof(cmd.opcode),
858 sizeof(cmd.param));
859 cmd.use_param = true;
860 LOG_DBG("Command in callback: %d, param: %d", cmd.opcode, cmd.param);
861 }
862 }
863
864 if (mcc_cb && mcc_cb->send_cmd) {
865 mcc_cb->send_cmd(conn, cb_err, &cmd);
866 }
867 }
868 #endif /* defined(CONFIG_BT_MCC_SET_MEDIA_CONTROL_POINT) */
869
870 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
mcc_opcodes_supported_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)871 static void mcc_opcodes_supported_cb(struct bt_conn *conn, uint8_t err, const void *data,
872 uint16_t length)
873 {
874 int cb_err = err;
875 int32_t operations = 0;
876
877 if (err) {
878 LOG_DBG("err: 0x%02x", err);
879 } else if ((!data) || (length != sizeof(operations))) {
880 LOG_DBG("length: %d, data: %p", length, data);
881 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
882
883 } else {
884 operations = sys_get_le32((uint8_t *)data);
885 LOG_DBG("Opcodes supported: %d", operations);
886 LOG_HEXDUMP_DBG(data, sizeof(operations), "Opcodes_supported");
887 }
888
889 if (mcc_cb && mcc_cb->read_opcodes_supported) {
890 mcc_cb->read_opcodes_supported(conn, cb_err, operations);
891 }
892 }
893
mcc_read_opcodes_supported_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)894 static uint8_t mcc_read_opcodes_supported_cb(struct bt_conn *conn, uint8_t err,
895 struct bt_gatt_read_params *params,
896 const void *data, uint16_t length)
897 {
898 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
899
900 mcs_inst->busy = false;
901 mcc_opcodes_supported_cb(conn, err, data, length);
902
903 return BT_GATT_ITER_STOP;
904 }
905 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
906
907 #ifdef CONFIG_BT_MCC_OTS
mcs_write_scp_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)908 static void mcs_write_scp_cb(struct bt_conn *conn, uint8_t err,
909 struct bt_gatt_write_params *params)
910 {
911 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, write_params);
912 int cb_err = err;
913 struct mpl_search search = {0};
914
915 mcs_inst->busy = false;
916
917 if (err) {
918 LOG_DBG("err: 0x%02x", err);
919 } else if (!params->data ||
920 (params->length > SEARCH_LEN_MAX)) {
921 LOG_DBG("length: %d, data: %p", params->length, params->data);
922 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
923 } else {
924 search.len = params->length;
925 (void)memcpy(search.search, params->data, params->length);
926 LOG_DBG("Length of returned value in callback: %d", search.len);
927 }
928
929 if (mcc_cb && mcc_cb->send_search) {
930 mcc_cb->send_search(conn, cb_err, &search);
931 }
932 }
933
mcc_search_results_obj_id_cb(struct bt_conn * conn,uint8_t err,const void * data,uint16_t length)934 static void mcc_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
935 const void *data, uint16_t length)
936 {
937 int cb_err = err;
938 uint8_t *pid = (uint8_t *)data;
939 uint64_t id = 0;
940
941 if (err) {
942 LOG_DBG("err: 0x%02x", err);
943 } else if (length == 0) {
944 /* OK - this characteristic may be zero length */
945 /* cb_err and id already have correct values */
946 LOG_DBG("Zero-length Search Results Object ID");
947 } else if (!pid || (length != BT_OTS_OBJ_ID_SIZE)) {
948 LOG_DBG("length: %d, pid: %p", length, pid);
949 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
950 } else {
951 id = sys_get_le48(pid);
952 LOG_DBG_OBJ_ID("Search Results Object ID: ", id);
953
954 if (!BT_MCS_VALID_OBJ_ID(id)) {
955 cb_err = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
956 }
957 }
958
959 if (mcc_cb && mcc_cb->read_search_results_obj_id) {
960 mcc_cb->read_search_results_obj_id(conn, cb_err, id);
961 }
962 }
963
mcc_read_search_results_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)964 static uint8_t mcc_read_search_results_obj_id_cb(struct bt_conn *conn, uint8_t err,
965 struct bt_gatt_read_params *params,
966 const void *data, uint16_t length)
967 {
968 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
969
970 mcs_inst->busy = false;
971 mcc_search_results_obj_id_cb(conn, err, data, length);
972
973 return BT_GATT_ITER_STOP;
974 }
975 #endif /* CONFIG_BT_MCC_OTS */
976
977 #if defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID)
mcc_read_content_control_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)978 static uint8_t mcc_read_content_control_id_cb(struct bt_conn *conn, uint8_t err,
979 struct bt_gatt_read_params *params,
980 const void *data, uint16_t length)
981 {
982 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params, struct mcs_instance_t, read_params);
983 int cb_err = err;
984 uint8_t ccid = 0;
985
986 mcs_inst->busy = false;
987
988 if (err) {
989 LOG_DBG("err: 0x%02x", err);
990 } else if ((!data) || (length != sizeof(ccid))) {
991 LOG_DBG("length: %d, data: %p", length, data);
992 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
993
994 } else {
995 ccid = *(uint8_t *)data;
996 LOG_DBG("Content control ID: %d", ccid);
997 }
998
999 if (mcc_cb && mcc_cb->read_content_control_id) {
1000 mcc_cb->read_content_control_id(conn, cb_err, ccid);
1001 }
1002
1003 return BT_GATT_ITER_STOP;
1004 }
1005 #endif /* defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID) */
1006
mcs_notify_handler(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)1007 static uint8_t mcs_notify_handler(struct bt_conn *conn,
1008 struct bt_gatt_subscribe_params *params,
1009 const void *data, uint16_t length)
1010 {
1011 uint16_t handle = params->value_handle;
1012 struct mcs_instance_t *mcs_inst;
1013
1014 if (data == NULL) {
1015 LOG_DBG("[UNSUBSCRIBED] 0x%04X", params->value_handle);
1016 params->value_handle = 0U;
1017
1018 return BT_GATT_ITER_CONTINUE;
1019 }
1020
1021 mcs_inst = lookup_inst_by_conn(conn);
1022 if (mcs_inst == NULL) {
1023 LOG_DBG("Could not find MCS instance from conn %p", (void *)conn);
1024
1025 return BT_GATT_ITER_CONTINUE;
1026 }
1027
1028 LOG_DBG("Notification, handle: %d", handle);
1029
1030 if (handle == mcs_inst->player_name_handle) {
1031 LOG_DBG("Player Name notification");
1032 mcc_player_name_cb(conn, 0, data, length);
1033
1034 } else if (handle == mcs_inst->track_changed_handle) {
1035 /* The Track Changed characteristic can only be */
1036 /* notified, so that is handled directly here */
1037 int cb_err = 0;
1038
1039 LOG_DBG("Track Changed notification");
1040 LOG_DBG("data: %p, length: %u", data, length);
1041
1042 if (length != 0) {
1043 LOG_DBG("Non-zero length: %u", length);
1044 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
1045 }
1046
1047 if (mcc_cb && mcc_cb->track_changed_ntf) {
1048 mcc_cb->track_changed_ntf(conn, cb_err);
1049 }
1050
1051 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION)
1052 } else if (handle == mcs_inst->track_title_handle) {
1053 LOG_DBG("Track Title notification");
1054 mcc_track_title_cb(conn, 0, data, length);
1055 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION) */
1056
1057 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
1058 } else if (handle == mcs_inst->track_duration_handle) {
1059 LOG_DBG("Track Duration notification");
1060 mcc_track_duration_cb(conn, 0, data, length);
1061 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
1062
1063 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
1064 } else if (handle == mcs_inst->track_position_handle) {
1065 LOG_DBG("Track Position notification");
1066 mcc_track_position_cb(conn, 0, data, length);
1067 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
1068
1069 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
1070 } else if (handle == mcs_inst->playback_speed_handle) {
1071 LOG_DBG("Playback Speed notification");
1072 mcc_playback_speed_cb(conn, 0, data, length);
1073 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
1074
1075 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
1076 } else if (handle == mcs_inst->seeking_speed_handle) {
1077 LOG_DBG("Seeking Speed notification");
1078 mcc_seeking_speed_cb(conn, 0, data, length);
1079 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
1080
1081 #ifdef CONFIG_BT_MCC_OTS
1082 } else if (handle == mcs_inst->current_track_obj_id_handle) {
1083 LOG_DBG("Current Track notification");
1084 mcc_current_track_obj_id_cb(conn, 0, data, length);
1085
1086 } else if (handle == mcs_inst->next_track_obj_id_handle) {
1087 LOG_DBG("Next Track notification");
1088 mcc_next_track_obj_id_cb(conn, 0, data, length);
1089
1090 } else if (handle == mcs_inst->parent_group_obj_id_handle) {
1091 LOG_DBG("Parent Group notification");
1092 mcc_parent_group_obj_id_cb(conn, 0, data, length);
1093
1094 } else if (handle == mcs_inst->current_group_obj_id_handle) {
1095 LOG_DBG("Current Group notification");
1096 mcc_current_group_obj_id_cb(conn, 0, data, length);
1097 #endif /* CONFIG_BT_MCC_OTS */
1098
1099 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
1100 } else if (handle == mcs_inst->playing_order_handle) {
1101 LOG_DBG("Playing Order notification");
1102 mcc_playing_order_cb(conn, 0, data, length);
1103 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
1104
1105 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
1106 } else if (handle == mcs_inst->media_state_handle) {
1107 LOG_DBG("Media State notification");
1108 mcc_media_state_cb(conn, 0, data, length);
1109 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
1110
1111 } else if (handle == mcs_inst->cp_handle) {
1112 /* The control point is a special case - only */
1113 /* writable and notifiable. Handle directly here. */
1114 struct mpl_cmd_ntf ntf = {0};
1115 int cb_err = 0;
1116
1117 LOG_DBG("Control Point notification");
1118 if (length == sizeof(ntf.requested_opcode) + sizeof(ntf.result_code)) {
1119 ntf.requested_opcode = *(uint8_t *)data;
1120 ntf.result_code = *((uint8_t *)data +
1121 sizeof(ntf.requested_opcode));
1122 LOG_DBG("Command: %d, result: %d",
1123 ntf.requested_opcode, ntf.result_code);
1124 } else {
1125 LOG_DBG("length: %d, data: %p", length, data);
1126 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
1127 }
1128
1129 if (mcc_cb && mcc_cb->cmd_ntf) {
1130 mcc_cb->cmd_ntf(conn, cb_err, &ntf);
1131 }
1132
1133 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
1134 } else if (handle == mcs_inst->opcodes_supported_handle) {
1135 LOG_DBG("Opcodes Supported notification");
1136 mcc_opcodes_supported_cb(conn, 0, data, length);
1137 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
1138
1139 #ifdef CONFIG_BT_MCC_OTS
1140 } else if (handle == mcs_inst->scp_handle) {
1141 /* The search control point is a special case - only */
1142 /* writable and notifiable. Handle directly here. */
1143 int cb_err = 0;
1144 uint8_t result_code = 0;
1145
1146 LOG_DBG("Search Control Point notification");
1147 if (length == sizeof(result_code)) {
1148 result_code = *(uint8_t *)data;
1149 LOG_DBG("Result: %d", result_code);
1150 } else {
1151 LOG_DBG("length: %d, data: %p", length, data);
1152 cb_err = BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
1153 }
1154
1155 if (mcc_cb && mcc_cb->search_ntf) {
1156 mcc_cb->search_ntf(conn, cb_err, result_code);
1157 }
1158
1159 } else if (handle == mcs_inst->search_results_obj_id_handle) {
1160 LOG_DBG("Search Results notification");
1161 mcc_search_results_obj_id_cb(conn, 0, data, length);
1162 #endif /* CONFIG_BT_MCC_OTS */
1163 } else {
1164 LOG_DBG("Unknown handle: %d (0x%04X)", handle, handle);
1165 }
1166
1167 return BT_GATT_ITER_CONTINUE;
1168 }
1169
reset_mcs_inst(struct mcs_instance_t * mcs_inst)1170 static int reset_mcs_inst(struct mcs_instance_t *mcs_inst)
1171 {
1172 if (mcs_inst->conn != NULL) {
1173 struct bt_conn *conn = mcs_inst->conn;
1174 struct bt_conn_info info;
1175 int err;
1176
1177 err = bt_conn_get_info(conn, &info);
1178 if (err != 0) {
1179 return err;
1180 }
1181
1182 if (info.state == BT_CONN_STATE_CONNECTED) {
1183 /* It's okay if these fail with -EINVAL as that means that they are
1184 * not currently subscribed
1185 */
1186 err = bt_gatt_unsubscribe(conn, &mcs_inst->player_name_sub_params);
1187 if (err != 0 && err != -EINVAL) {
1188 LOG_DBG("Failed to unsubscribe to name: %d", err);
1189
1190 return err;
1191 }
1192
1193 err = bt_gatt_unsubscribe(conn, &mcs_inst->track_changed_sub_params);
1194 if (err != 0 && err != -EINVAL) {
1195 LOG_DBG("Failed to unsubscribe to track change: %d", err);
1196
1197 return err;
1198 }
1199
1200 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION)
1201 err = bt_gatt_unsubscribe(conn, &mcs_inst->track_title_sub_params);
1202 if (err != 0 && err != -EINVAL) {
1203 LOG_DBG("Failed to unsubscribe to track title: %d", err);
1204
1205 return err;
1206 }
1207 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION) */
1208
1209 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
1210 err = bt_gatt_unsubscribe(conn, &mcs_inst->track_duration_sub_params);
1211 if (err != 0 && err != -EINVAL) {
1212 LOG_DBG("Failed to unsubscribe to track duration: %d", err);
1213
1214 return err;
1215 }
1216 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
1217
1218 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
1219 err = bt_gatt_unsubscribe(conn, &mcs_inst->track_position_sub_params);
1220 if (err != 0 && err != -EINVAL) {
1221 LOG_DBG("Failed to unsubscribe to track position: %d", err);
1222
1223 return err;
1224 }
1225 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
1226
1227 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
1228 err = bt_gatt_unsubscribe(conn, &mcs_inst->playback_speed_sub_params);
1229 if (err != 0 && err != -EINVAL) {
1230 LOG_DBG("Failed to unsubscribe to playback speed: %d", err);
1231
1232 return err;
1233 }
1234 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
1235
1236 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
1237 err = bt_gatt_unsubscribe(conn, &mcs_inst->seeking_speed_sub_params);
1238 if (err != 0 && err != -EINVAL) {
1239 LOG_DBG("Failed to unsubscribe to seeking speed: %d", err);
1240
1241 return err;
1242 }
1243 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
1244
1245 #ifdef CONFIG_BT_MCC_OTS
1246 err = bt_gatt_unsubscribe(conn, &mcs_inst->current_track_obj_sub_params);
1247 if (err != 0 && err != -EINVAL) {
1248 LOG_DBG("Failed to unsubscribe to current track object: %d", err);
1249
1250 return err;
1251 }
1252
1253 err = bt_gatt_unsubscribe(conn, &mcs_inst->next_track_obj_sub_params);
1254 if (err != 0 && err != -EINVAL) {
1255 LOG_DBG("Failed to unsubscribe to next track object: %d", err);
1256
1257 return err;
1258 }
1259
1260 err = bt_gatt_unsubscribe(conn, &mcs_inst->parent_group_obj_sub_params);
1261 if (err != 0 && err != -EINVAL) {
1262 LOG_DBG("Failed to unsubscribe to parent group object: %d", err);
1263
1264 return err;
1265 }
1266
1267 err = bt_gatt_unsubscribe(conn, &mcs_inst->current_group_obj_sub_params);
1268 if (err != 0 && err != -EINVAL) {
1269 LOG_DBG("Failed to unsubscribe to current group object: %d", err);
1270
1271 return err;
1272 }
1273
1274 #endif /* CONFIG_BT_MCC_OTS */
1275 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
1276 err = bt_gatt_unsubscribe(conn, &mcs_inst->playing_order_sub_params);
1277 if (err != 0 && err != -EINVAL) {
1278 LOG_DBG("Failed to unsubscribe to playing order: %d", err);
1279
1280 return err;
1281 }
1282 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
1283
1284 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
1285 err = bt_gatt_unsubscribe(conn, &mcs_inst->media_state_sub_params);
1286 if (err != 0 && err != -EINVAL) {
1287 LOG_DBG("Failed to unsubscribe to media state: %d", err);
1288
1289 return err;
1290 }
1291 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
1292
1293 err = bt_gatt_unsubscribe(conn, &mcs_inst->cp_sub_params);
1294 if (err != 0 && err != -EINVAL) {
1295 LOG_DBG("Failed to unsubscribe to control point: %d", err);
1296
1297 return err;
1298 }
1299
1300 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
1301 err = bt_gatt_unsubscribe(conn, &mcs_inst->opcodes_supported_sub_params);
1302 if (err != 0 && err != -EINVAL) {
1303 LOG_DBG("Failed to unsubscribe to supported opcodes: %d", err);
1304
1305 return err;
1306 }
1307 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
1308
1309 #ifdef CONFIG_BT_MCC_OTS
1310 err = bt_gatt_unsubscribe(conn, &mcs_inst->scp_sub_params);
1311 if (err != 0 && err != -EINVAL) {
1312 LOG_DBG("Failed to unsubscribe to search control point: %d", err);
1313
1314 return err;
1315 }
1316
1317 err = bt_gatt_unsubscribe(conn, &mcs_inst->search_results_obj_sub_params);
1318 if (err != 0 && err != -EINVAL) {
1319 LOG_DBG("Failed to unsubscribe to search results: %d", err);
1320
1321 return err;
1322 }
1323
1324 err = bt_gatt_unsubscribe(conn, &mcs_inst->otc.oacp_sub_params);
1325 if (err != 0 && err != -EINVAL) {
1326 LOG_DBG("Failed to unsubscribe to oacp: %d", err);
1327
1328 return err;
1329 }
1330
1331 err = bt_gatt_unsubscribe(conn, &mcs_inst->otc.olcp_sub_params);
1332 if (err != 0 && err != -EINVAL) {
1333 LOG_DBG("Failed to unsubscribe to olcp: %d", err);
1334
1335 return err;
1336 }
1337 #endif /* CONFIG_BT_MCC_OTS */
1338 }
1339
1340 bt_conn_unref(conn);
1341 mcs_inst->conn = NULL;
1342 }
1343
1344 (void)memset(mcs_inst, 0, offsetof(struct mcs_instance_t, busy));
1345 #ifdef CONFIG_BT_MCC_OTS
1346 /* Reset OTC instance as well if supported */
1347 (void)memset(&mcs_inst->otc, 0, offsetof(struct bt_ots_client, oacp_sub_params));
1348 #endif /* CONFIG_BT_MCC_OTS */
1349
1350 return 0;
1351 }
1352
disconnected_cb(struct bt_conn * conn,uint8_t reason)1353 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
1354 {
1355 struct mcs_instance_t *mcs_inst;
1356
1357 mcs_inst = lookup_inst_by_conn(conn);
1358 if (mcs_inst != NULL) {
1359 (void)reset_mcs_inst(mcs_inst);
1360 }
1361 }
1362
1363 BT_CONN_CB_DEFINE(conn_callbacks) = {
1364 .disconnected = disconnected_cb,
1365 };
1366
1367 /* Called when discovery is completed - successfully or with error */
discovery_complete(struct bt_conn * conn,int err)1368 static void discovery_complete(struct bt_conn *conn, int err)
1369 {
1370 struct mcs_instance_t *mcs_inst;
1371
1372 LOG_DBG("Discovery completed, err: %d", err);
1373
1374 mcs_inst = lookup_inst_by_conn(conn);
1375 if (mcs_inst != NULL) {
1376 mcs_inst->busy = false;
1377 if (err != 0) {
1378 (void)reset_mcs_inst(mcs_inst);
1379 }
1380 }
1381
1382 if (mcc_cb && mcc_cb->discover_mcs) {
1383 mcc_cb->discover_mcs(conn, err);
1384 }
1385 }
1386
1387 #ifdef CONFIG_BT_MCC_OTS
discover_otc_char_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)1388 static uint8_t discover_otc_char_func(struct bt_conn *conn,
1389 const struct bt_gatt_attr *attr,
1390 struct bt_gatt_discover_params *params)
1391 {
1392 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params,
1393 struct mcs_instance_t,
1394 discover_params);
1395 int err = 0;
1396 struct bt_gatt_chrc *chrc;
1397 struct bt_gatt_subscribe_params *sub_params = NULL;
1398
1399 if (attr) {
1400 /* Found an attribute */
1401 LOG_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle);
1402
1403 if (params->type != BT_GATT_DISCOVER_CHARACTERISTIC) {
1404 /* But it was not a characteristic - continue search */
1405 return BT_GATT_ITER_CONTINUE;
1406 }
1407
1408 /* We have found an attribute, and it is a characteristic */
1409 /* Find out which attribute, and subscribe if we should */
1410 chrc = (struct bt_gatt_chrc *)attr->user_data;
1411 if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_FEATURE)) {
1412 LOG_DBG("OTS Features");
1413 mcs_inst->otc.feature_handle = chrc->value_handle;
1414 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_NAME)) {
1415 LOG_DBG("Object Name");
1416 mcs_inst->otc.obj_name_handle = chrc->value_handle;
1417 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_TYPE)) {
1418 LOG_DBG("Object Type");
1419 mcs_inst->otc.obj_type_handle = chrc->value_handle;
1420 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_SIZE)) {
1421 LOG_DBG("Object Size");
1422 mcs_inst->otc.obj_size_handle = chrc->value_handle;
1423 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_ID)) {
1424 LOG_DBG("Object ID");
1425 mcs_inst->otc.obj_id_handle = chrc->value_handle;
1426 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_PROPERTIES)) {
1427 LOG_DBG("Object properties %d", chrc->value_handle);
1428 mcs_inst->otc.obj_properties_handle = chrc->value_handle;
1429 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_ACTION_CP)) {
1430 LOG_DBG("Object Action Control Point");
1431 mcs_inst->otc.oacp_handle = chrc->value_handle;
1432 sub_params = &mcs_inst->otc.oacp_sub_params;
1433 sub_params->disc_params = &mcs_inst->otc.oacp_sub_disc_params;
1434 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_OTS_LIST_CP)) {
1435 LOG_DBG("Object List Control Point");
1436 mcs_inst->otc.olcp_handle = chrc->value_handle;
1437 sub_params = &mcs_inst->otc.olcp_sub_params;
1438 sub_params->disc_params = &mcs_inst->otc.olcp_sub_disc_params;
1439 }
1440
1441 if (sub_params) {
1442 /* With ccc_handle == 0 it will use auto discovery */
1443 sub_params->ccc_handle = 0;
1444 sub_params->end_handle = mcs_inst->otc.end_handle;
1445 sub_params->value = BT_GATT_CCC_INDICATE;
1446 sub_params->value_handle = chrc->value_handle;
1447 sub_params->notify = bt_ots_client_indicate_handler;
1448 atomic_set_bit(sub_params->flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
1449
1450 err = bt_gatt_subscribe(conn, sub_params);
1451 if (err != 0) {
1452 LOG_DBG("Failed to subscribe (err %d)", err);
1453 discovery_complete(conn, err);
1454
1455 return BT_GATT_ITER_STOP;
1456 }
1457 }
1458
1459 return BT_GATT_ITER_CONTINUE;
1460 }
1461
1462 /* No more attributes found */
1463 mcs_inst->otc.cb = &otc_cb;
1464 bt_ots_client_register(&mcs_inst->otc);
1465
1466 LOG_DBG("Setup complete for included OTS");
1467 (void)memset(params, 0, sizeof(*params));
1468
1469 discovery_complete(conn, err);
1470
1471 return BT_GATT_ITER_STOP;
1472 }
1473 #endif /* CONFIG_BT_MCC_OTS */
1474
1475 #ifdef CONFIG_BT_MCC_OTS
1476 /* This function is called when an included service is found.
1477 * The function will store the start and end handle for the service,
1478 * and continue the search for more instances of included services.
1479 * Lastly, it will start discovery of OTS characteristics.
1480 */
1481
discover_include_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)1482 static uint8_t discover_include_func(struct bt_conn *conn,
1483 const struct bt_gatt_attr *attr,
1484 struct bt_gatt_discover_params *params)
1485 {
1486 struct bt_gatt_include *include;
1487 int err = 0;
1488
1489 if (attr) {
1490 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params,
1491 struct mcs_instance_t,
1492 discover_params);
1493
1494 LOG_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle);
1495
1496 __ASSERT(params->type == BT_GATT_DISCOVER_INCLUDE,
1497 "Wrong type");
1498
1499 /* We have found an included service */
1500 include = (struct bt_gatt_include *)attr->user_data;
1501 LOG_DBG("Include UUID %s", bt_uuid_str(include->uuid));
1502
1503 if (bt_uuid_cmp(include->uuid, BT_UUID_OTS)) {
1504 /* But it is not OTS - continue search */
1505 LOG_WRN("Included service is not OTS");
1506 return BT_GATT_ITER_CONTINUE;
1507 }
1508
1509 /* We have the included OTS service (MCS includes only one) */
1510 LOG_DBG("Discover include complete for GMCS: OTS");
1511 mcs_inst->otc.start_handle = include->start_handle;
1512 mcs_inst->otc.end_handle = include->end_handle;
1513 (void)memset(params, 0, sizeof(*params));
1514
1515 /* Discover characteristics of the included OTS */
1516 mcs_inst->discover_params.start_handle = mcs_inst->otc.start_handle;
1517 mcs_inst->discover_params.end_handle = mcs_inst->otc.end_handle;
1518 mcs_inst->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
1519 mcs_inst->discover_params.func = discover_otc_char_func;
1520
1521 LOG_DBG("Start discovery of OTS characteristics");
1522 err = bt_gatt_discover(conn, &mcs_inst->discover_params);
1523 if (err) {
1524 LOG_DBG("Discovery of OTS chars. failed");
1525 discovery_complete(conn, err);
1526 }
1527 return BT_GATT_ITER_STOP;
1528 }
1529
1530 LOG_DBG("No included OTS found");
1531 /* This is OK, the server may not support OTS. But in that case,
1532 * discovery stops here.
1533 */
1534 discovery_complete(conn, err);
1535 return BT_GATT_ITER_STOP;
1536 }
1537
1538 /* Start discovery of included services */
discover_included(struct mcs_instance_t * mcs_inst,struct bt_conn * conn)1539 static void discover_included(struct mcs_instance_t *mcs_inst, struct bt_conn *conn)
1540 {
1541 int err;
1542
1543 memset(&mcs_inst->discover_params, 0, sizeof(mcs_inst->discover_params));
1544
1545 mcs_inst->discover_params.start_handle = mcs_inst->start_handle;
1546 mcs_inst->discover_params.end_handle = mcs_inst->end_handle;
1547 mcs_inst->discover_params.type = BT_GATT_DISCOVER_INCLUDE;
1548 mcs_inst->discover_params.func = discover_include_func;
1549
1550 LOG_DBG("Start discovery of included services");
1551 err = bt_gatt_discover(conn, &mcs_inst->discover_params);
1552 if (err) {
1553 LOG_DBG("Discovery of included service failed: %d", err);
1554 discovery_complete(conn, err);
1555 }
1556 }
1557 #endif /* CONFIG_BT_MCC_OTS */
1558
1559 static bool subscribe_next_mcs_char(struct mcs_instance_t *mcs_inst,
1560 struct bt_conn *conn);
1561
1562 /* This function will subscribe to GMCS CCCDs.
1563 * After this, the function will start discovery of included services.
1564 */
subscribe_mcs_char_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)1565 static void subscribe_mcs_char_func(struct bt_conn *conn, uint8_t err,
1566 struct bt_gatt_subscribe_params *params)
1567 {
1568 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params->disc_params,
1569 struct mcs_instance_t,
1570 discover_params);
1571 bool subscription_done;
1572
1573 if (err) {
1574 LOG_DBG("Subscription callback error: %u", err);
1575 params->subscribe = NULL;
1576 discovery_complete(conn, err);
1577 return;
1578 }
1579
1580 LOG_DBG("Subscribed: value handle: %d, ccc handle: %d",
1581 params->value_handle, params->ccc_handle);
1582
1583 if (params->value_handle == 0) {
1584 /* Unsubscribing, ignore */
1585 return;
1586 }
1587
1588 /* Subscribe to next characteristic */
1589 subscription_done = subscribe_next_mcs_char(mcs_inst, conn);
1590
1591 if (subscription_done) {
1592 params->subscribe = NULL;
1593 #ifdef CONFIG_BT_MCC_OTS
1594 /* Start discovery of included services to find OTS */
1595 discover_included(mcs_inst, conn);
1596 #else
1597 /* If OTS is not configured, discovery ends here */
1598 discovery_complete(conn, 0);
1599 #endif /* CONFIG_BT_MCC_OTS */
1600 }
1601 }
1602
1603 /* Subscribe to a characteristic - helper function */
do_subscribe(struct mcs_instance_t * mcs_inst,struct bt_conn * conn,uint16_t handle,struct bt_gatt_subscribe_params * sub_params)1604 static int do_subscribe(struct mcs_instance_t *mcs_inst, struct bt_conn *conn,
1605 uint16_t handle,
1606 struct bt_gatt_subscribe_params *sub_params)
1607 {
1608 /* With ccc_handle == 0 it will use auto discovery */
1609 sub_params->ccc_handle = 0;
1610 sub_params->end_handle = mcs_inst->end_handle;
1611 sub_params->value_handle = handle;
1612 sub_params->notify = mcs_notify_handler;
1613 sub_params->subscribe = subscribe_mcs_char_func;
1614 /* disc_params pointer is also used as subscription flag */
1615 sub_params->disc_params = &mcs_inst->discover_params;
1616 atomic_set_bit(sub_params->flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
1617
1618 LOG_DBG("Subscring to handle %d", handle);
1619 return bt_gatt_subscribe(conn, sub_params);
1620 }
1621
1622 /* Subscribe to the next GMCS CCCD.
1623 * @return true if there are no more characteristics to subscribe to
1624 */
subscribe_next_mcs_char(struct mcs_instance_t * mcs_inst,struct bt_conn * conn)1625 static bool subscribe_next_mcs_char(struct mcs_instance_t *mcs_inst,
1626 struct bt_conn *conn)
1627 {
1628 struct bt_gatt_subscribe_params *sub_params = NULL;
1629 uint16_t handle;
1630
1631 /* The characteristics may be in any order on the server, and
1632 * not all of them may exist => need to check all.
1633 * For each of the subscribable characteristics
1634 * - check if we have a handle for it
1635 * - check sub_params.disc_params pointer to see if we have
1636 * already subscribed to it (set in do_subscribe() ).
1637 */
1638
1639 if (mcs_inst->player_name_handle &&
1640 mcs_inst->player_name_sub_params.value &&
1641 mcs_inst->player_name_sub_params.disc_params == NULL) {
1642 sub_params = &mcs_inst->player_name_sub_params;
1643 handle = mcs_inst->player_name_handle;
1644 } else if (mcs_inst->track_changed_handle &&
1645 mcs_inst->track_changed_sub_params.value &&
1646 mcs_inst->track_changed_sub_params.disc_params == NULL) {
1647 sub_params = &mcs_inst->track_changed_sub_params;
1648 handle = mcs_inst->track_changed_handle;
1649 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION)
1650 } else if (mcs_inst->track_title_handle &&
1651 mcs_inst->track_title_sub_params.value &&
1652 mcs_inst->track_title_sub_params.disc_params == NULL) {
1653 sub_params = &mcs_inst->track_title_sub_params;
1654 handle = mcs_inst->track_title_handle;
1655 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION) */
1656 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
1657 } else if (mcs_inst->track_duration_handle &&
1658 mcs_inst->track_duration_sub_params.value &&
1659 mcs_inst->track_duration_sub_params.disc_params == NULL) {
1660 sub_params = &mcs_inst->track_duration_sub_params;
1661 handle = mcs_inst->track_duration_handle;
1662 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
1663 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
1664 } else if (mcs_inst->track_position_handle &&
1665 mcs_inst->track_position_sub_params.value &&
1666 mcs_inst->track_position_sub_params.disc_params == NULL) {
1667 sub_params = &mcs_inst->track_position_sub_params;
1668 handle = mcs_inst->track_position_handle;
1669 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
1670 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
1671 } else if (mcs_inst->playback_speed_handle &&
1672 mcs_inst->playback_speed_sub_params.value &&
1673 mcs_inst->playback_speed_sub_params.disc_params == NULL) {
1674 sub_params = &mcs_inst->playback_speed_sub_params;
1675 handle = mcs_inst->playback_speed_handle;
1676 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
1677 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
1678 } else if (mcs_inst->seeking_speed_handle &&
1679 mcs_inst->seeking_speed_sub_params.value &&
1680 mcs_inst->seeking_speed_sub_params.disc_params == NULL) {
1681 sub_params = &mcs_inst->seeking_speed_sub_params;
1682 handle = mcs_inst->seeking_speed_handle;
1683 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
1684 #ifdef CONFIG_BT_MCC_OTS
1685 } else if (mcs_inst->current_track_obj_id_handle &&
1686 mcs_inst->current_track_obj_sub_params.value &&
1687 mcs_inst->current_track_obj_sub_params.disc_params == NULL) {
1688 sub_params = &mcs_inst->current_track_obj_sub_params;
1689 handle = mcs_inst->current_track_obj_id_handle;
1690 } else if (mcs_inst->next_track_obj_id_handle &&
1691 mcs_inst->next_track_obj_sub_params.value &&
1692 mcs_inst->next_track_obj_sub_params.disc_params == NULL) {
1693 sub_params = &mcs_inst->next_track_obj_sub_params;
1694 handle = mcs_inst->next_track_obj_id_handle;
1695 } else if (mcs_inst->parent_group_obj_id_handle &&
1696 mcs_inst->parent_group_obj_sub_params.value &&
1697 mcs_inst->parent_group_obj_sub_params.disc_params == NULL) {
1698 sub_params = &mcs_inst->parent_group_obj_sub_params;
1699 handle = mcs_inst->parent_group_obj_id_handle;
1700 } else if (mcs_inst->current_group_obj_id_handle &&
1701 mcs_inst->current_group_obj_sub_params.value &&
1702 mcs_inst->current_group_obj_sub_params.disc_params == NULL) {
1703 sub_params = &mcs_inst->current_group_obj_sub_params;
1704 handle = mcs_inst->current_group_obj_id_handle;
1705 #endif /* CONFIG_BT_MCC_OTS */
1706 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
1707 } else if (mcs_inst->playing_order_handle &&
1708 mcs_inst->playing_order_sub_params.value &&
1709 mcs_inst->playing_order_sub_params.disc_params == NULL) {
1710 sub_params = &mcs_inst->playing_order_sub_params;
1711 handle = mcs_inst->playing_order_handle;
1712 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
1713 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
1714 } else if (mcs_inst->media_state_handle &&
1715 mcs_inst->media_state_sub_params.value &&
1716 mcs_inst->media_state_sub_params.disc_params == NULL) {
1717 sub_params = &mcs_inst->media_state_sub_params;
1718 handle = mcs_inst->media_state_handle;
1719 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
1720 } else if (mcs_inst->cp_handle &&
1721 mcs_inst->cp_sub_params.value &&
1722 mcs_inst->cp_sub_params.disc_params == NULL) {
1723 sub_params = &mcs_inst->cp_sub_params;
1724 handle = mcs_inst->cp_handle;
1725 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
1726 } else if (mcs_inst->opcodes_supported_handle &&
1727 mcs_inst->opcodes_supported_sub_params.value &&
1728 mcs_inst->opcodes_supported_sub_params.disc_params == NULL) {
1729 sub_params = &mcs_inst->opcodes_supported_sub_params;
1730 handle = mcs_inst->opcodes_supported_handle;
1731 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
1732 #ifdef CONFIG_BT_MCC_OTS
1733 } else if (mcs_inst->scp_handle &&
1734 mcs_inst->scp_sub_params.value &&
1735 mcs_inst->scp_sub_params.disc_params == NULL) {
1736 sub_params = &mcs_inst->scp_sub_params;
1737 handle = mcs_inst->scp_handle;
1738 } else if (mcs_inst->search_results_obj_id_handle &&
1739 mcs_inst->search_results_obj_sub_params.value &&
1740 mcs_inst->search_results_obj_sub_params.disc_params == NULL) {
1741 sub_params = &mcs_inst->search_results_obj_sub_params;
1742 handle = mcs_inst->search_results_obj_id_handle;
1743 #endif /* CONFIG_BT_MCC_OTS */
1744 }
1745
1746 if (sub_params != NULL) {
1747 const int err = do_subscribe(mcs_inst, conn, handle,
1748 sub_params);
1749
1750 if (err) {
1751 LOG_DBG("Could not subscribe: %d", err);
1752 discovery_complete(conn, err);
1753 }
1754
1755 return false;
1756 }
1757
1758 /* If we have come here, there are no more characteristics to
1759 * subscribe to, and we are done.
1760 */
1761 return true;
1762 }
1763
1764 /* This function is called when characteristics are found.
1765 * The function will store handles to GMCS characteristics.
1766 * After this, the function will start subscription to characteristics
1767 */
discover_mcs_char_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)1768 static uint8_t discover_mcs_char_func(struct bt_conn *conn,
1769 const struct bt_gatt_attr *attr,
1770 struct bt_gatt_discover_params *params)
1771 {
1772 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params,
1773 struct mcs_instance_t,
1774 discover_params);
1775 struct bt_gatt_chrc *chrc;
1776 bool subscription_done = true;
1777
1778 if (attr) {
1779 /* Found an attribute */
1780 LOG_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle);
1781
1782 if (params->type != BT_GATT_DISCOVER_CHARACTERISTIC) {
1783 /* But it was not a characteristic - continue search */
1784 return BT_GATT_ITER_CONTINUE;
1785 }
1786
1787 /* We have found an attribute, and it is a characteristic */
1788 /* Find out which attribute, and subscribe if we should */
1789 chrc = (struct bt_gatt_chrc *)attr->user_data;
1790
1791 if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_PLAYER_NAME)) {
1792 LOG_DBG("Player name, UUID: %s", bt_uuid_str(chrc->uuid));
1793 mcs_inst->player_name_handle = chrc->value_handle;
1794 /* Use discovery params pointer as subscription flag */
1795 mcs_inst->player_name_sub_params.disc_params = NULL;
1796 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1797 mcs_inst->player_name_sub_params.value = BT_GATT_CCC_NOTIFY;
1798 }
1799 #ifdef CONFIG_BT_MCC_OTS
1800 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_ICON_OBJ_ID)) {
1801 LOG_DBG("Icon Object, UUID: %s", bt_uuid_str(chrc->uuid));
1802 mcs_inst->icon_obj_id_handle = chrc->value_handle;
1803 #endif /* CONFIG_BT_MCC_OTS */
1804 #if defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL)
1805 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_ICON_URL)) {
1806 LOG_DBG("Icon URL, UUID: %s", bt_uuid_str(chrc->uuid));
1807 mcs_inst->icon_url_handle = chrc->value_handle;
1808 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL) */
1809 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_TRACK_CHANGED)) {
1810 LOG_DBG("Track Changed, UUID: %s", bt_uuid_str(chrc->uuid));
1811 mcs_inst->track_changed_handle = chrc->value_handle;
1812 mcs_inst->track_changed_sub_params.disc_params = NULL;
1813 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1814 mcs_inst->track_changed_sub_params.value = BT_GATT_CCC_NOTIFY;
1815 }
1816 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE)
1817 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_TRACK_TITLE)) {
1818 LOG_DBG("Track Title, UUID: %s", bt_uuid_str(chrc->uuid));
1819 mcs_inst->track_title_handle = chrc->value_handle;
1820 #if defined(BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION)
1821 mcs_inst->track_title_sub_params.disc_params = NULL;
1822 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1823 mcs_inst->track_title_sub_params.value = BT_GATT_CCC_NOTIFY;
1824 }
1825 #endif /* defined(BT_MCC_READ_TRACK_TITLE_ENABLE_SUBSCRIPTION) */
1826 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE) */
1827 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
1828 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_TRACK_DURATION)) {
1829 LOG_DBG("Track Duration, UUID: %s", bt_uuid_str(chrc->uuid));
1830 mcs_inst->track_duration_handle = chrc->value_handle;
1831 mcs_inst->track_duration_sub_params.disc_params = NULL;
1832 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1833 mcs_inst->track_duration_sub_params.value = BT_GATT_CCC_NOTIFY;
1834 }
1835 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
1836 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION) || defined(CONFIG_BT_MCC_SET_TRACK_POSITION)
1837 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_TRACK_POSITION)) {
1838 LOG_DBG("Track Position, UUID: %s", bt_uuid_str(chrc->uuid));
1839 mcs_inst->track_position_handle = chrc->value_handle;
1840 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
1841 mcs_inst->track_position_sub_params.disc_params = NULL;
1842 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1843 mcs_inst->track_position_sub_params.value = BT_GATT_CCC_NOTIFY;
1844 }
1845 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
1846 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) || defined(CONFIG_BT_MCC_SET_TRACK_POSITION) */
1847 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED) || defined(CONFIG_BT_MCC_SET_PLAYBACK_SPEED)
1848 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_PLAYBACK_SPEED)) {
1849 LOG_DBG("Playback Speed, UUID: %s", bt_uuid_str(chrc->uuid));
1850 mcs_inst->playback_speed_handle = chrc->value_handle;
1851 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
1852 mcs_inst->playback_speed_sub_params.disc_params = NULL;
1853 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1854 mcs_inst->playback_speed_sub_params.value = BT_GATT_CCC_NOTIFY;
1855 }
1856 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
1857 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) ||
1858 * defined (CONFIG_BT_MCC_SET_PLAYBACK_SPEED)
1859 */
1860 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
1861 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_SEEKING_SPEED)) {
1862 LOG_DBG("Seeking Speed, UUID: %s", bt_uuid_str(chrc->uuid));
1863 mcs_inst->seeking_speed_handle = chrc->value_handle;
1864 mcs_inst->seeking_speed_sub_params.disc_params = NULL;
1865 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1866 mcs_inst->seeking_speed_sub_params.value = BT_GATT_CCC_NOTIFY;
1867 }
1868 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
1869 #ifdef CONFIG_BT_MCC_OTS
1870 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID)) {
1871 LOG_DBG("Track Segments Object, UUID: %s", bt_uuid_str(chrc->uuid));
1872 mcs_inst->segments_obj_id_handle = chrc->value_handle;
1873 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_CURRENT_TRACK_OBJ_ID)) {
1874 LOG_DBG("Current Track Object, UUID: %s", bt_uuid_str(chrc->uuid));
1875 mcs_inst->current_track_obj_id_handle = chrc->value_handle;
1876 mcs_inst->current_track_obj_sub_params.disc_params = NULL;
1877 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1878 mcs_inst->current_track_obj_sub_params.value =
1879 BT_GATT_CCC_NOTIFY;
1880 }
1881 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_NEXT_TRACK_OBJ_ID)) {
1882 LOG_DBG("Next Track Object, UUID: %s", bt_uuid_str(chrc->uuid));
1883 mcs_inst->next_track_obj_id_handle = chrc->value_handle;
1884 mcs_inst->next_track_obj_sub_params.disc_params = NULL;
1885 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1886 mcs_inst->next_track_obj_sub_params.value = BT_GATT_CCC_NOTIFY;
1887 }
1888 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_PARENT_GROUP_OBJ_ID)) {
1889 LOG_DBG("Parent Group Object, UUID: %s", bt_uuid_str(chrc->uuid));
1890 mcs_inst->parent_group_obj_id_handle = chrc->value_handle;
1891 mcs_inst->parent_group_obj_sub_params.disc_params = NULL;
1892 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1893 mcs_inst->parent_group_obj_sub_params.value =
1894 BT_GATT_CCC_NOTIFY;
1895 }
1896 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_CURRENT_GROUP_OBJ_ID)) {
1897 LOG_DBG("Group Object, UUID: %s", bt_uuid_str(chrc->uuid));
1898 mcs_inst->current_group_obj_id_handle = chrc->value_handle;
1899 mcs_inst->current_group_obj_sub_params.disc_params = NULL;
1900 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1901 mcs_inst->current_group_obj_sub_params.value =
1902 BT_GATT_CCC_NOTIFY;
1903 }
1904 #endif /* CONFIG_BT_MCC_OTS */
1905 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) || defined(CONFIG_BT_MCC_SET_PLAYING_ORDER)
1906 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_PLAYING_ORDER)) {
1907 LOG_DBG("Playing Order, UUID: %s", bt_uuid_str(chrc->uuid));
1908 mcs_inst->playing_order_handle = chrc->value_handle;
1909 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
1910 mcs_inst->playing_order_sub_params.disc_params = NULL;
1911 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1912 mcs_inst->playing_order_sub_params.value = BT_GATT_CCC_NOTIFY;
1913 }
1914 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
1915 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) || defined(CONFIG_BT_MCC_SET_PLAYING_ORDER) */
1916 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED)
1917 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_PLAYING_ORDERS)) {
1918 LOG_DBG("Playing Orders supported, UUID: %s", bt_uuid_str(chrc->uuid));
1919 mcs_inst->playing_orders_supported_handle = chrc->value_handle;
1920 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED) */
1921 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
1922 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_MEDIA_STATE)) {
1923 LOG_DBG("Media State, UUID: %s", bt_uuid_str(chrc->uuid));
1924 mcs_inst->media_state_handle = chrc->value_handle;
1925 mcs_inst->media_state_sub_params.disc_params = NULL;
1926 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1927 mcs_inst->media_state_sub_params.value = BT_GATT_CCC_NOTIFY;
1928 }
1929 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
1930 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_MEDIA_CONTROL_POINT)) {
1931 LOG_DBG("Media Control Point, UUID: %s", bt_uuid_str(chrc->uuid));
1932 mcs_inst->cp_handle = chrc->value_handle;
1933 mcs_inst->cp_sub_params.disc_params = NULL;
1934 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1935 mcs_inst->cp_sub_params.value = BT_GATT_CCC_NOTIFY;
1936 }
1937 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
1938 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_MEDIA_CONTROL_OPCODES)) {
1939 LOG_DBG("Media control opcodes supported, UUID: %s",
1940 bt_uuid_str(chrc->uuid));
1941 mcs_inst->opcodes_supported_handle = chrc->value_handle;
1942 mcs_inst->opcodes_supported_sub_params.disc_params = NULL;
1943 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1944 mcs_inst->opcodes_supported_sub_params.value =
1945 BT_GATT_CCC_NOTIFY;
1946 }
1947 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
1948 #ifdef CONFIG_BT_MCC_OTS
1949 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_SEARCH_CONTROL_POINT)) {
1950 LOG_DBG("Search control point, UUID: %s", bt_uuid_str(chrc->uuid));
1951 mcs_inst->scp_handle = chrc->value_handle;
1952 mcs_inst->scp_sub_params.disc_params = NULL;
1953 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1954 mcs_inst->scp_sub_params.value = BT_GATT_CCC_NOTIFY;
1955 }
1956 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID)) {
1957 LOG_DBG("Search Results object, UUID: %s", bt_uuid_str(chrc->uuid));
1958 mcs_inst->search_results_obj_id_handle = chrc->value_handle;
1959 mcs_inst->search_results_obj_sub_params.disc_params = NULL;
1960 if (chrc->properties & BT_GATT_CHRC_NOTIFY) {
1961 mcs_inst->search_results_obj_sub_params.value =
1962 BT_GATT_CCC_NOTIFY;
1963 }
1964 #endif /* CONFIG_BT_MCC_OTS */
1965 #if defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID)
1966 } else if (!bt_uuid_cmp(chrc->uuid, BT_UUID_CCID)) {
1967 LOG_DBG("Content Control ID, UUID: %s", bt_uuid_str(chrc->uuid));
1968 mcs_inst->content_control_id_handle = chrc->value_handle;
1969 #endif /* defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID) */
1970 }
1971
1972
1973 /* Continue to search for more attributes */
1974 return BT_GATT_ITER_CONTINUE;
1975 }
1976
1977 /* No more attributes found */
1978 LOG_DBG("GMCS characteristics found");
1979 (void)memset(params, 0, sizeof(*params));
1980
1981 /* Either subscribe to characteristics, or continue to discovery of
1982 *included services.
1983 * Subscription is done after discovery, not in parallel with it,
1984 * to avoid queuing many ATT requests that requires buffers.
1985 */
1986 if (subscribe_all) {
1987 subscription_done = subscribe_next_mcs_char(mcs_inst, conn);
1988 }
1989
1990 if (subscription_done) {
1991 /* Not subscribing, or there was nothing to subscribe to */
1992 #ifdef CONFIG_BT_MCC_OTS
1993 /* Start discovery of included services to find OTS */
1994 discover_included(mcs_inst, conn);
1995 #else
1996 /* If OTS is not configured, discovery ends here */
1997 discovery_complete(conn, 0);
1998 #endif /* CONFIG_BT_MCC_OTS */
1999 }
2000
2001 return BT_GATT_ITER_STOP;
2002 }
2003
2004 /* This function is called when a (primary) GMCS service has been discovered.
2005 * The function will store the start and end handle for the service. It will
2006 * then start discovery of the characteristics of the GMCS service.
2007 */
discover_primary_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)2008 static uint8_t discover_primary_func(struct bt_conn *conn,
2009 const struct bt_gatt_attr *attr,
2010 struct bt_gatt_discover_params *params)
2011 {
2012 struct bt_gatt_service_val *prim_service;
2013
2014 if (attr) {
2015 struct mcs_instance_t *mcs_inst = CONTAINER_OF(params,
2016 struct mcs_instance_t,
2017 discover_params);
2018 int err;
2019 /* Found an attribute */
2020 LOG_DBG("[ATTRIBUTE] handle 0x%04X", attr->handle);
2021
2022 if (params->type != BT_GATT_DISCOVER_PRIMARY) {
2023 /* But it was not a primary service - continue search */
2024 LOG_WRN("Unexpected parameters");
2025 return BT_GATT_ITER_CONTINUE;
2026 }
2027
2028 /* We have found an attribute, and it is a primary service */
2029 /* (Must be GMCS, since that is the one we searched for.) */
2030 LOG_DBG("Primary discovery complete");
2031 LOG_DBG("UUID: %s", bt_uuid_str(attr->uuid));
2032 prim_service = (struct bt_gatt_service_val *)attr->user_data;
2033 LOG_DBG("UUID: %s", bt_uuid_str(prim_service->uuid));
2034
2035 mcs_inst->start_handle = attr->handle + 1;
2036 mcs_inst->end_handle = prim_service->end_handle;
2037
2038 /* Start discovery of characteristics */
2039 mcs_inst->discover_params.uuid = NULL;
2040 mcs_inst->discover_params.start_handle = mcs_inst->start_handle;
2041 mcs_inst->discover_params.end_handle = mcs_inst->end_handle;
2042 mcs_inst->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
2043 mcs_inst->discover_params.func = discover_mcs_char_func;
2044
2045 LOG_DBG("Start discovery of GMCS characteristics");
2046 err = bt_gatt_discover(conn, &mcs_inst->discover_params);
2047 if (err) {
2048 LOG_DBG("Discovery failed: %d", err);
2049 discovery_complete(conn, err);
2050 }
2051 return BT_GATT_ITER_STOP;
2052 }
2053
2054 /* No attribute of the searched for type found */
2055 LOG_DBG("Could not find an GMCS instance on the server");
2056
2057 discovery_complete(conn, -ENODATA);
2058 return BT_GATT_ITER_STOP;
2059 }
2060
2061
bt_mcc_init(struct bt_mcc_cb * cb)2062 int bt_mcc_init(struct bt_mcc_cb *cb)
2063 {
2064 mcc_cb = cb;
2065
2066 #ifdef CONFIG_BT_MCC_OTS
2067 /* Set up the callbacks from OTC */
2068 /* TODO: Have one single content callback. */
2069 /* For now: Use the icon callback for content - it is the first, */
2070 /* and this will anyway be reset later. */
2071 otc_cb.obj_data_read = on_icon_content;
2072 otc_cb.obj_selected = on_obj_selected;
2073 otc_cb.obj_metadata_read = on_object_metadata;
2074
2075 LOG_DBG("Object selected callback: %p", otc_cb.obj_selected);
2076 LOG_DBG("Object content callback: %p", otc_cb.obj_data_read);
2077 LOG_DBG("Object metadata callback: %p", otc_cb.obj_metadata_read);
2078 #endif /* CONFIG_BT_MCC_OTS */
2079
2080 return 0;
2081 }
2082
2083
2084 /* Initiate discovery.
2085 * Discovery is handled by a chain of functions, where each function does its
2086 * part, and then initiates a further discovery, with a new callback function.
2087 *
2088 * The order of discovery is follows:
2089 * 1: Discover GMCS primary service (started here)
2090 * 2: Discover characteristics of GMCS
2091 * 3: Subscribe to characteristics of GMCS
2092 * 4: Discover OTS service included in GMCS
2093 * 5: Discover characteristics of OTS and subscribe to them
2094 */
bt_mcc_discover_mcs(struct bt_conn * conn,bool subscribe)2095 int bt_mcc_discover_mcs(struct bt_conn *conn, bool subscribe)
2096 {
2097 struct mcs_instance_t *mcs_inst;
2098 int err;
2099
2100 CHECKIF(!conn) {
2101 return -EINVAL;
2102 }
2103
2104 mcs_inst = lookup_inst_by_conn(conn);
2105 if (mcs_inst == NULL) {
2106 /* TODO: Need to find existing or new MCS instance here */
2107 return -EINVAL;
2108 }
2109
2110 if (mcs_inst->busy) {
2111 return -EBUSY;
2112 }
2113
2114 subscribe_all = subscribe;
2115 err = reset_mcs_inst(mcs_inst);
2116 if (err != 0) {
2117 LOG_DBG("Failed to reset MCS instance %p: %d", mcs_inst, err);
2118
2119 return err;
2120 }
2121 (void)memcpy(&uuid, BT_UUID_GMCS, sizeof(uuid));
2122
2123 mcs_inst->discover_params.func = discover_primary_func;
2124 mcs_inst->discover_params.uuid = &uuid.uuid;
2125 mcs_inst->discover_params.type = BT_GATT_DISCOVER_PRIMARY;
2126 mcs_inst->discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
2127 mcs_inst->discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
2128
2129 LOG_DBG("start discovery of GMCS primary service");
2130 err = bt_gatt_discover(conn, &mcs_inst->discover_params);
2131 if (err != 0) {
2132 return err;
2133 }
2134
2135 mcs_inst->conn = bt_conn_ref(conn);
2136 mcs_inst->busy = true;
2137
2138 return 0;
2139 }
2140
bt_mcc_read_player_name(struct bt_conn * conn)2141 int bt_mcc_read_player_name(struct bt_conn *conn)
2142 {
2143 struct mcs_instance_t *mcs_inst;
2144 int err;
2145
2146 CHECKIF(conn == NULL) {
2147 LOG_DBG("conn is NULL");
2148
2149 return -EINVAL;
2150 }
2151
2152 mcs_inst = lookup_inst_by_conn(conn);
2153 if (mcs_inst == NULL) {
2154 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2155
2156 return -EINVAL;
2157 } else if (mcs_inst->busy) {
2158
2159 LOG_DBG("mcs_inst busy");
2160 return -EBUSY;
2161 } else if (mcs_inst->player_name_handle == 0) {
2162 LOG_DBG("handle not set");
2163
2164 return -EINVAL;
2165 }
2166
2167 mcs_inst->read_params.func = mcc_read_player_name_cb;
2168 mcs_inst->read_params.handle_count = 1;
2169 mcs_inst->read_params.single.handle = mcs_inst->player_name_handle;
2170 mcs_inst->read_params.single.offset = 0U;
2171
2172 err = bt_gatt_read(conn, &mcs_inst->read_params);
2173 if (!err) {
2174 mcs_inst->busy = true;
2175 }
2176 return err;
2177 }
2178
2179
2180 #ifdef CONFIG_BT_MCC_OTS
bt_mcc_read_icon_obj_id(struct bt_conn * conn)2181 int bt_mcc_read_icon_obj_id(struct bt_conn *conn)
2182 {
2183 struct mcs_instance_t *mcs_inst;
2184 int err;
2185
2186 CHECKIF(conn == NULL) {
2187 LOG_DBG("conn is NULL");
2188
2189 return -EINVAL;
2190 }
2191
2192 mcs_inst = lookup_inst_by_conn(conn);
2193 if (mcs_inst == NULL) {
2194 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2195
2196 return -EINVAL;
2197 } else if (mcs_inst->busy) {
2198
2199 LOG_DBG("mcs_inst busy");
2200 return -EBUSY;
2201 } else if (mcs_inst->icon_obj_id_handle == 0) {
2202 LOG_DBG("handle not set");
2203
2204 return -EINVAL;
2205 }
2206
2207 mcs_inst->read_params.func = mcc_read_icon_obj_id_cb;
2208 mcs_inst->read_params.handle_count = 1;
2209 mcs_inst->read_params.single.handle = mcs_inst->icon_obj_id_handle;
2210 mcs_inst->read_params.single.offset = 0U;
2211
2212 err = bt_gatt_read(conn, &mcs_inst->read_params);
2213 if (!err) {
2214 mcs_inst->busy = true;
2215 }
2216 return err;
2217 }
2218 #endif /* CONFIG_BT_MCC_OTS */
2219
2220 #if defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL)
bt_mcc_read_icon_url(struct bt_conn * conn)2221 int bt_mcc_read_icon_url(struct bt_conn *conn)
2222 {
2223 struct mcs_instance_t *mcs_inst;
2224 int err;
2225
2226 CHECKIF(conn == NULL) {
2227 LOG_DBG("conn is NULL");
2228
2229 return -EINVAL;
2230 }
2231
2232 mcs_inst = lookup_inst_by_conn(conn);
2233 if (mcs_inst == NULL) {
2234 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2235
2236 return -EINVAL;
2237 } else if (mcs_inst->busy) {
2238
2239 LOG_DBG("mcs_inst busy");
2240 return -EBUSY;
2241 } else if (mcs_inst->icon_url_handle == 0) {
2242 LOG_DBG("handle not set");
2243
2244 return -EINVAL;
2245 }
2246
2247 mcs_inst->read_params.func = mcc_read_icon_url_cb;
2248 mcs_inst->read_params.handle_count = 1;
2249 mcs_inst->read_params.single.handle = mcs_inst->icon_url_handle;
2250 mcs_inst->read_params.single.offset = 0U;
2251
2252 err = bt_gatt_read(conn, &mcs_inst->read_params);
2253 if (!err) {
2254 mcs_inst->busy = true;
2255 }
2256 return err;
2257 }
2258 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_PLAYER_ICON_URL) */
2259
2260 #if defined(CONFIG_BT_MCC_READ_TRACK_TITLE)
bt_mcc_read_track_title(struct bt_conn * conn)2261 int bt_mcc_read_track_title(struct bt_conn *conn)
2262 {
2263 struct mcs_instance_t *mcs_inst;
2264 int err;
2265
2266 CHECKIF(conn == NULL) {
2267 LOG_DBG("conn is NULL");
2268
2269 return -EINVAL;
2270 }
2271
2272 mcs_inst = lookup_inst_by_conn(conn);
2273 if (mcs_inst == NULL) {
2274 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2275
2276 return -EINVAL;
2277 } else if (mcs_inst->busy) {
2278
2279 LOG_DBG("mcs_inst busy");
2280 return -EBUSY;
2281 } else if (mcs_inst->track_title_handle == 0) {
2282 LOG_DBG("handle not set");
2283
2284 return -EINVAL;
2285 }
2286
2287 mcs_inst->read_params.func = mcc_read_track_title_cb;
2288 mcs_inst->read_params.handle_count = 1;
2289 mcs_inst->read_params.single.handle = mcs_inst->track_title_handle;
2290 mcs_inst->read_params.single.offset = 0U;
2291
2292 err = bt_gatt_read(conn, &mcs_inst->read_params);
2293 if (!err) {
2294 mcs_inst->busy = true;
2295 }
2296 return err;
2297 }
2298 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_TITLE) */
2299
2300 #if defined(CONFIG_BT_MCC_READ_TRACK_DURATION)
bt_mcc_read_track_duration(struct bt_conn * conn)2301 int bt_mcc_read_track_duration(struct bt_conn *conn)
2302 {
2303 struct mcs_instance_t *mcs_inst;
2304 int err;
2305
2306 CHECKIF(conn == NULL) {
2307 LOG_DBG("conn is NULL");
2308
2309 return -EINVAL;
2310 }
2311
2312 mcs_inst = lookup_inst_by_conn(conn);
2313 if (mcs_inst == NULL) {
2314 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2315
2316 return -EINVAL;
2317 } else if (mcs_inst->busy) {
2318
2319 LOG_DBG("mcs_inst busy");
2320 return -EBUSY;
2321 } else if (mcs_inst->track_duration_handle == 0) {
2322 LOG_DBG("handle not set");
2323
2324 return -EINVAL;
2325 }
2326
2327 mcs_inst->read_params.func = mcc_read_track_duration_cb;
2328 mcs_inst->read_params.handle_count = 1;
2329 mcs_inst->read_params.single.handle = mcs_inst->track_duration_handle;
2330 mcs_inst->read_params.single.offset = 0U;
2331
2332 err = bt_gatt_read(conn, &mcs_inst->read_params);
2333 if (!err) {
2334 mcs_inst->busy = true;
2335 }
2336 return err;
2337 }
2338 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_DURATION) */
2339
2340 #if defined(CONFIG_BT_MCC_READ_TRACK_POSITION)
bt_mcc_read_track_position(struct bt_conn * conn)2341 int bt_mcc_read_track_position(struct bt_conn *conn)
2342 {
2343 struct mcs_instance_t *mcs_inst;
2344 int err;
2345
2346 CHECKIF(conn == NULL) {
2347 LOG_DBG("conn is NULL");
2348
2349 return -EINVAL;
2350 }
2351
2352 mcs_inst = lookup_inst_by_conn(conn);
2353 if (mcs_inst == NULL) {
2354 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2355
2356 return -EINVAL;
2357 } else if (mcs_inst->busy) {
2358
2359 LOG_DBG("mcs_inst busy");
2360 return -EBUSY;
2361 } else if (mcs_inst->track_position_handle == 0) {
2362 LOG_DBG("handle not set");
2363
2364 return -EINVAL;
2365 }
2366
2367 mcs_inst->read_params.func = mcc_read_track_position_cb;
2368 mcs_inst->read_params.handle_count = 1;
2369 mcs_inst->read_params.single.handle = mcs_inst->track_position_handle;
2370 mcs_inst->read_params.single.offset = 0U;
2371
2372 err = bt_gatt_read(conn, &mcs_inst->read_params);
2373 if (!err) {
2374 mcs_inst->busy = true;
2375 }
2376 return err;
2377 }
2378 #endif /* defined(CONFIG_BT_MCC_READ_TRACK_POSITION) */
2379
2380 #if defined(CONFIG_BT_MCC_SET_TRACK_POSITION)
bt_mcc_set_track_position(struct bt_conn * conn,int32_t pos)2381 int bt_mcc_set_track_position(struct bt_conn *conn, int32_t pos)
2382 {
2383 struct mcs_instance_t *mcs_inst;
2384 int err;
2385
2386 CHECKIF(conn == NULL) {
2387 LOG_DBG("conn is NULL");
2388
2389 return -EINVAL;
2390 }
2391
2392 mcs_inst = lookup_inst_by_conn(conn);
2393 if (mcs_inst == NULL) {
2394 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2395
2396 return -EINVAL;
2397 } else if (mcs_inst->busy) {
2398
2399 LOG_DBG("mcs_inst busy");
2400 return -EBUSY;
2401 } else if (mcs_inst->track_position_handle == 0) {
2402 LOG_DBG("handle not set");
2403
2404 return -EINVAL;
2405 }
2406
2407 (void)memcpy(mcs_inst->write_buf, &pos, sizeof(pos));
2408
2409 mcs_inst->write_params.offset = 0;
2410 mcs_inst->write_params.data = mcs_inst->write_buf;
2411 mcs_inst->write_params.length = sizeof(pos);
2412 mcs_inst->write_params.handle = mcs_inst->track_position_handle;
2413 mcs_inst->write_params.func = mcs_write_track_position_cb;
2414
2415 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, sizeof(pos), "Track position sent");
2416
2417 err = bt_gatt_write(conn, &mcs_inst->write_params);
2418 if (!err) {
2419 mcs_inst->busy = true;
2420 }
2421 return err;
2422 }
2423 #endif /* defined(CONFIG_BT_MCC_SET_TRACK_POSITION) */
2424
2425 #if defined(CONFIG_BT_MCC_READ_PLAYBACK_SPEED)
bt_mcc_read_playback_speed(struct bt_conn * conn)2426 int bt_mcc_read_playback_speed(struct bt_conn *conn)
2427 {
2428 struct mcs_instance_t *mcs_inst;
2429 int err;
2430
2431 CHECKIF(conn == NULL) {
2432 LOG_DBG("conn is NULL");
2433
2434 return -EINVAL;
2435 }
2436
2437 mcs_inst = lookup_inst_by_conn(conn);
2438 if (mcs_inst == NULL) {
2439 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2440
2441 return -EINVAL;
2442 } else if (mcs_inst->busy) {
2443
2444 LOG_DBG("mcs_inst busy");
2445 return -EBUSY;
2446 } else if (mcs_inst->playback_speed_handle == 0) {
2447 LOG_DBG("handle not set");
2448
2449 return -EINVAL;
2450 }
2451
2452 mcs_inst->read_params.func = mcc_read_playback_speed_cb;
2453 mcs_inst->read_params.handle_count = 1;
2454 mcs_inst->read_params.single.handle = mcs_inst->playback_speed_handle;
2455 mcs_inst->read_params.single.offset = 0U;
2456
2457 err = bt_gatt_read(conn, &mcs_inst->read_params);
2458 if (!err) {
2459 mcs_inst->busy = true;
2460 }
2461 return err;
2462 }
2463 #endif /* defined (CONFIG_BT_MCC_READ_PLAYBACK_SPEED) */
2464
2465 #if defined(CONFIG_BT_MCC_SET_PLAYBACK_SPEED)
bt_mcc_set_playback_speed(struct bt_conn * conn,int8_t speed)2466 int bt_mcc_set_playback_speed(struct bt_conn *conn, int8_t speed)
2467 {
2468 struct mcs_instance_t *mcs_inst;
2469 int err;
2470
2471 CHECKIF(conn == NULL) {
2472 LOG_DBG("conn is NULL");
2473
2474 return -EINVAL;
2475 }
2476
2477 mcs_inst = lookup_inst_by_conn(conn);
2478 if (mcs_inst == NULL) {
2479 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2480
2481 return -EINVAL;
2482 } else if (mcs_inst->busy) {
2483
2484 LOG_DBG("mcs_inst busy");
2485 return -EBUSY;
2486 } else if (mcs_inst->playback_speed_handle == 0) {
2487 LOG_DBG("handle not set");
2488
2489 return -EINVAL;
2490 }
2491
2492 (void)memcpy(mcs_inst->write_buf, &speed, sizeof(speed));
2493
2494 mcs_inst->write_params.offset = 0;
2495 mcs_inst->write_params.data = mcs_inst->write_buf;
2496 mcs_inst->write_params.length = sizeof(speed);
2497 mcs_inst->write_params.handle = mcs_inst->playback_speed_handle;
2498 mcs_inst->write_params.func = mcs_write_playback_speed_cb;
2499
2500 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, sizeof(speed), "Playback speed");
2501
2502 err = bt_gatt_write(conn, &mcs_inst->write_params);
2503 if (!err) {
2504 mcs_inst->busy = true;
2505 }
2506 return err;
2507 }
2508 #endif /* defined (CONFIG_BT_MCC_SET_PLAYBACK_SPEED) */
2509
2510 #if defined(CONFIG_BT_MCC_READ_SEEKING_SPEED)
bt_mcc_read_seeking_speed(struct bt_conn * conn)2511 int bt_mcc_read_seeking_speed(struct bt_conn *conn)
2512 {
2513 struct mcs_instance_t *mcs_inst;
2514 int err;
2515
2516 CHECKIF(conn == NULL) {
2517 LOG_DBG("conn is NULL");
2518
2519 return -EINVAL;
2520 }
2521
2522 mcs_inst = lookup_inst_by_conn(conn);
2523 if (mcs_inst == NULL) {
2524 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2525
2526 return -EINVAL;
2527 } else if (mcs_inst->busy) {
2528
2529 LOG_DBG("mcs_inst busy");
2530 return -EBUSY;
2531 } else if (mcs_inst->seeking_speed_handle == 0) {
2532 LOG_DBG("handle not set");
2533
2534 return -EINVAL;
2535 }
2536
2537 mcs_inst->read_params.func = mcc_read_seeking_speed_cb;
2538 mcs_inst->read_params.handle_count = 1;
2539 mcs_inst->read_params.single.handle = mcs_inst->seeking_speed_handle;
2540 mcs_inst->read_params.single.offset = 0U;
2541
2542 err = bt_gatt_read(conn, &mcs_inst->read_params);
2543 if (!err) {
2544 mcs_inst->busy = true;
2545 }
2546 return err;
2547 }
2548 #endif /* defined (CONFIG_BT_MCC_READ_SEEKING_SPEED) */
2549
2550 #ifdef CONFIG_BT_MCC_OTS
bt_mcc_read_segments_obj_id(struct bt_conn * conn)2551 int bt_mcc_read_segments_obj_id(struct bt_conn *conn)
2552 {
2553 struct mcs_instance_t *mcs_inst;
2554 int err;
2555
2556 CHECKIF(conn == NULL) {
2557 LOG_DBG("conn is NULL");
2558
2559 return -EINVAL;
2560 }
2561
2562 mcs_inst = lookup_inst_by_conn(conn);
2563 if (mcs_inst == NULL) {
2564 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2565
2566 return -EINVAL;
2567 } else if (mcs_inst->busy) {
2568
2569 LOG_DBG("mcs_inst busy");
2570 return -EBUSY;
2571 } else if (mcs_inst->segments_obj_id_handle == 0) {
2572 LOG_DBG("handle not set");
2573
2574 return -EINVAL;
2575 }
2576
2577 mcs_inst->read_params.func = mcc_read_segments_obj_id_cb;
2578 mcs_inst->read_params.handle_count = 1;
2579 mcs_inst->read_params.single.handle = mcs_inst->segments_obj_id_handle;
2580 mcs_inst->read_params.single.offset = 0U;
2581
2582 err = bt_gatt_read(conn, &mcs_inst->read_params);
2583 if (!err) {
2584 mcs_inst->busy = true;
2585 }
2586 return err;
2587 }
2588
bt_mcc_read_current_track_obj_id(struct bt_conn * conn)2589 int bt_mcc_read_current_track_obj_id(struct bt_conn *conn)
2590 {
2591 struct mcs_instance_t *mcs_inst;
2592 int err;
2593
2594 CHECKIF(conn == NULL) {
2595 LOG_DBG("conn is NULL");
2596
2597 return -EINVAL;
2598 }
2599
2600 mcs_inst = lookup_inst_by_conn(conn);
2601 if (mcs_inst == NULL) {
2602 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2603
2604 return -EINVAL;
2605 } else if (mcs_inst->busy) {
2606
2607 LOG_DBG("mcs_inst busy");
2608 return -EBUSY;
2609 } else if (mcs_inst->current_track_obj_id_handle == 0) {
2610 LOG_DBG("handle not set");
2611
2612 return -EINVAL;
2613 }
2614
2615 mcs_inst->read_params.func = mcc_read_current_track_obj_id_cb;
2616 mcs_inst->read_params.handle_count = 1;
2617 mcs_inst->read_params.single.handle = mcs_inst->current_track_obj_id_handle;
2618 mcs_inst->read_params.single.offset = 0U;
2619
2620 err = bt_gatt_read(conn, &mcs_inst->read_params);
2621 if (!err) {
2622 mcs_inst->busy = true;
2623 }
2624 return err;
2625 }
2626
bt_mcc_set_current_track_obj_id(struct bt_conn * conn,uint64_t obj_id)2627 int bt_mcc_set_current_track_obj_id(struct bt_conn *conn, uint64_t obj_id)
2628 {
2629 struct mcs_instance_t *mcs_inst;
2630 int err;
2631
2632 CHECKIF(conn == NULL) {
2633 LOG_DBG("conn is NULL");
2634
2635 return -EINVAL;
2636 }
2637
2638 CHECKIF(!BT_MCS_VALID_OBJ_ID(obj_id)) {
2639 LOG_DBG("Object ID 0x%016llx invalid", obj_id);
2640 return -EINVAL;
2641 }
2642
2643 mcs_inst = lookup_inst_by_conn(conn);
2644 if (mcs_inst == NULL) {
2645 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2646
2647 return -EINVAL;
2648 } else if (mcs_inst->busy) {
2649
2650 LOG_DBG("mcs_inst busy");
2651 return -EBUSY;
2652 } else if (mcs_inst->current_track_obj_id_handle == 0) {
2653 LOG_DBG("handle not set");
2654
2655 return -EINVAL;
2656 }
2657
2658 sys_put_le48(obj_id, mcs_inst->write_buf);
2659 mcs_inst->write_params.offset = 0;
2660 mcs_inst->write_params.data = mcs_inst->write_buf;
2661 mcs_inst->write_params.length = BT_OTS_OBJ_ID_SIZE;
2662 mcs_inst->write_params.handle = mcs_inst->current_track_obj_id_handle;
2663 mcs_inst->write_params.func = mcs_write_current_track_obj_id_cb;
2664
2665 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, BT_OTS_OBJ_ID_SIZE, "Object Id");
2666
2667 err = bt_gatt_write(conn, &mcs_inst->write_params);
2668 if (!err) {
2669 mcs_inst->busy = true;
2670 }
2671 return err;
2672 }
2673
bt_mcc_read_next_track_obj_id(struct bt_conn * conn)2674 int bt_mcc_read_next_track_obj_id(struct bt_conn *conn)
2675 {
2676 struct mcs_instance_t *mcs_inst;
2677 int err;
2678
2679 CHECKIF(conn == NULL) {
2680 LOG_DBG("conn is NULL");
2681
2682 return -EINVAL;
2683 }
2684
2685 mcs_inst = lookup_inst_by_conn(conn);
2686 if (mcs_inst == NULL) {
2687 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2688
2689 return -EINVAL;
2690 } else if (mcs_inst->busy) {
2691
2692 LOG_DBG("mcs_inst busy");
2693 return -EBUSY;
2694 } else if (mcs_inst->next_track_obj_id_handle == 0) {
2695 LOG_DBG("handle not set");
2696
2697 return -EINVAL;
2698 }
2699
2700 mcs_inst->read_params.func = mcc_read_next_track_obj_id_cb;
2701 mcs_inst->read_params.handle_count = 1;
2702 mcs_inst->read_params.single.handle = mcs_inst->next_track_obj_id_handle;
2703 mcs_inst->read_params.single.offset = 0U;
2704
2705 err = bt_gatt_read(conn, &mcs_inst->read_params);
2706 if (!err) {
2707 mcs_inst->busy = true;
2708 }
2709 return err;
2710 }
2711
bt_mcc_set_next_track_obj_id(struct bt_conn * conn,uint64_t obj_id)2712 int bt_mcc_set_next_track_obj_id(struct bt_conn *conn, uint64_t obj_id)
2713 {
2714 struct mcs_instance_t *mcs_inst;
2715 int err;
2716
2717 CHECKIF(conn == NULL) {
2718 LOG_DBG("conn is NULL");
2719
2720 return -EINVAL;
2721 }
2722
2723 CHECKIF(!BT_MCS_VALID_OBJ_ID(obj_id)) {
2724 LOG_DBG("Object ID 0x%016llx invalid", obj_id);
2725 return -EINVAL;
2726 }
2727
2728 mcs_inst = lookup_inst_by_conn(conn);
2729 if (mcs_inst == NULL) {
2730 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2731
2732 return -EINVAL;
2733 } else if (mcs_inst->busy) {
2734
2735 LOG_DBG("mcs_inst busy");
2736 return -EBUSY;
2737 } else if (mcs_inst->next_track_obj_id_handle == 0) {
2738 LOG_DBG("handle not set");
2739
2740 return -EINVAL;
2741 }
2742
2743 sys_put_le48(obj_id, mcs_inst->write_buf);
2744 mcs_inst->write_params.offset = 0;
2745 mcs_inst->write_params.data = mcs_inst->write_buf;
2746 mcs_inst->write_params.length = BT_OTS_OBJ_ID_SIZE;
2747 mcs_inst->write_params.handle = mcs_inst->next_track_obj_id_handle;
2748 mcs_inst->write_params.func = mcs_write_next_track_obj_id_cb;
2749
2750 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, BT_OTS_OBJ_ID_SIZE, "Object Id");
2751
2752 err = bt_gatt_write(conn, &mcs_inst->write_params);
2753 if (!err) {
2754 mcs_inst->busy = true;
2755 }
2756 return err;
2757 }
2758
bt_mcc_read_parent_group_obj_id(struct bt_conn * conn)2759 int bt_mcc_read_parent_group_obj_id(struct bt_conn *conn)
2760 {
2761 struct mcs_instance_t *mcs_inst;
2762 int err;
2763
2764 CHECKIF(conn == NULL) {
2765 LOG_DBG("conn is NULL");
2766
2767 return -EINVAL;
2768 }
2769
2770 mcs_inst = lookup_inst_by_conn(conn);
2771 if (mcs_inst == NULL) {
2772 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2773
2774 return -EINVAL;
2775 } else if (mcs_inst->busy) {
2776
2777 LOG_DBG("mcs_inst busy");
2778 return -EBUSY;
2779 } else if (mcs_inst->parent_group_obj_id_handle == 0) {
2780 LOG_DBG("handle not set");
2781
2782 return -EINVAL;
2783 }
2784
2785 mcs_inst->read_params.func = mcc_read_parent_group_obj_id_cb;
2786 mcs_inst->read_params.handle_count = 1;
2787 mcs_inst->read_params.single.handle = mcs_inst->parent_group_obj_id_handle;
2788 mcs_inst->read_params.single.offset = 0U;
2789
2790 err = bt_gatt_read(conn, &mcs_inst->read_params);
2791 if (!err) {
2792 mcs_inst->busy = true;
2793 }
2794 return err;
2795 }
2796
bt_mcc_read_current_group_obj_id(struct bt_conn * conn)2797 int bt_mcc_read_current_group_obj_id(struct bt_conn *conn)
2798 {
2799 struct mcs_instance_t *mcs_inst;
2800 int err;
2801
2802 CHECKIF(conn == NULL) {
2803 LOG_DBG("conn is NULL");
2804
2805 return -EINVAL;
2806 }
2807
2808 mcs_inst = lookup_inst_by_conn(conn);
2809 if (mcs_inst == NULL) {
2810 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2811
2812 return -EINVAL;
2813 } else if (mcs_inst->busy) {
2814
2815 LOG_DBG("mcs_inst busy");
2816 return -EBUSY;
2817 } else if (mcs_inst->current_group_obj_id_handle == 0) {
2818 LOG_DBG("handle not set");
2819
2820 return -EINVAL;
2821 }
2822
2823 mcs_inst->read_params.func = mcc_read_current_group_obj_id_cb;
2824 mcs_inst->read_params.handle_count = 1;
2825 mcs_inst->read_params.single.handle = mcs_inst->current_group_obj_id_handle;
2826 mcs_inst->read_params.single.offset = 0U;
2827
2828 err = bt_gatt_read(conn, &mcs_inst->read_params);
2829 if (!err) {
2830 mcs_inst->busy = true;
2831 }
2832 return err;
2833 }
2834
bt_mcc_set_current_group_obj_id(struct bt_conn * conn,uint64_t obj_id)2835 int bt_mcc_set_current_group_obj_id(struct bt_conn *conn, uint64_t obj_id)
2836 {
2837 struct mcs_instance_t *mcs_inst;
2838 int err;
2839
2840 CHECKIF(conn == NULL) {
2841 LOG_DBG("conn is NULL");
2842
2843 return -EINVAL;
2844 }
2845
2846 CHECKIF(!BT_MCS_VALID_OBJ_ID(obj_id)) {
2847 LOG_DBG("Object ID 0x%016llx invalid", obj_id);
2848 return -EINVAL;
2849 }
2850
2851 mcs_inst = lookup_inst_by_conn(conn);
2852 if (mcs_inst == NULL) {
2853 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2854
2855 return -EINVAL;
2856 } else if (mcs_inst->busy) {
2857
2858 LOG_DBG("mcs_inst busy");
2859 return -EBUSY;
2860 } else if (mcs_inst->current_group_obj_id_handle == 0) {
2861 LOG_DBG("handle not set");
2862
2863 return -EINVAL;
2864 }
2865
2866 sys_put_le48(obj_id, mcs_inst->write_buf);
2867 mcs_inst->write_params.offset = 0;
2868 mcs_inst->write_params.data = mcs_inst->write_buf;
2869 mcs_inst->write_params.length = BT_OTS_OBJ_ID_SIZE;
2870 mcs_inst->write_params.handle = mcs_inst->current_group_obj_id_handle;
2871 mcs_inst->write_params.func = mcs_write_current_group_obj_id_cb;
2872
2873 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, BT_OTS_OBJ_ID_SIZE, "Object Id");
2874
2875 err = bt_gatt_write(conn, &mcs_inst->write_params);
2876 if (!err) {
2877 mcs_inst->busy = true;
2878 }
2879 return err;
2880 }
2881 #endif /* CONFIG_BT_MCC_OTS */
2882
2883 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER)
bt_mcc_read_playing_order(struct bt_conn * conn)2884 int bt_mcc_read_playing_order(struct bt_conn *conn)
2885 {
2886 struct mcs_instance_t *mcs_inst;
2887 int err;
2888
2889 CHECKIF(conn == NULL) {
2890 LOG_DBG("conn is NULL");
2891
2892 return -EINVAL;
2893 }
2894
2895 mcs_inst = lookup_inst_by_conn(conn);
2896 if (mcs_inst == NULL) {
2897 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2898
2899 return -EINVAL;
2900 } else if (mcs_inst->busy) {
2901
2902 LOG_DBG("mcs_inst busy");
2903 return -EBUSY;
2904 } else if (mcs_inst->playing_order_handle == 0) {
2905 LOG_DBG("handle not set");
2906
2907 return -EINVAL;
2908 }
2909
2910 mcs_inst->read_params.func = mcc_read_playing_order_cb;
2911 mcs_inst->read_params.handle_count = 1;
2912 mcs_inst->read_params.single.handle = mcs_inst->playing_order_handle;
2913 mcs_inst->read_params.single.offset = 0U;
2914
2915 err = bt_gatt_read(conn, &mcs_inst->read_params);
2916 if (!err) {
2917 mcs_inst->busy = true;
2918 }
2919 return err;
2920 }
2921 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER) */
2922
2923 #if defined(CONFIG_BT_MCC_SET_PLAYING_ORDER)
bt_mcc_set_playing_order(struct bt_conn * conn,uint8_t order)2924 int bt_mcc_set_playing_order(struct bt_conn *conn, uint8_t order)
2925 {
2926 struct mcs_instance_t *mcs_inst;
2927 int err;
2928
2929 CHECKIF(conn == NULL) {
2930 LOG_DBG("conn is NULL");
2931
2932 return -EINVAL;
2933 }
2934
2935 mcs_inst = lookup_inst_by_conn(conn);
2936 if (mcs_inst == NULL) {
2937 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2938
2939 return -EINVAL;
2940 } else if (mcs_inst->busy) {
2941
2942 LOG_DBG("mcs_inst busy");
2943 return -EBUSY;
2944 } else if (mcs_inst->playing_order_handle == 0) {
2945 LOG_DBG("handle not set");
2946
2947 return -EINVAL;
2948 }
2949
2950 CHECKIF(!IN_RANGE(order,
2951 BT_MCS_PLAYING_ORDER_SINGLE_ONCE,
2952 BT_MCS_PLAYING_ORDER_SHUFFLE_REPEAT)) {
2953 LOG_DBG("Invalid playing order 0x%02X", order);
2954
2955 return -EINVAL;
2956 }
2957
2958 (void)memcpy(mcs_inst->write_buf, &order, sizeof(order));
2959
2960 mcs_inst->write_params.offset = 0;
2961 mcs_inst->write_params.data = mcs_inst->write_buf;
2962 mcs_inst->write_params.length = sizeof(order);
2963 mcs_inst->write_params.handle = mcs_inst->playing_order_handle;
2964 mcs_inst->write_params.func = mcs_write_playing_order_cb;
2965
2966 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, sizeof(order), "Playing order");
2967
2968 err = bt_gatt_write(conn, &mcs_inst->write_params);
2969 if (!err) {
2970 mcs_inst->busy = true;
2971 }
2972 return err;
2973 }
2974 #endif /* defined(CONFIG_BT_MCC_SET_PLAYING_ORDER) */
2975
2976 #if defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED)
bt_mcc_read_playing_orders_supported(struct bt_conn * conn)2977 int bt_mcc_read_playing_orders_supported(struct bt_conn *conn)
2978 {
2979 struct mcs_instance_t *mcs_inst;
2980 int err;
2981
2982 CHECKIF(conn == NULL) {
2983 LOG_DBG("conn is NULL");
2984
2985 return -EINVAL;
2986 }
2987
2988 mcs_inst = lookup_inst_by_conn(conn);
2989 if (mcs_inst == NULL) {
2990 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
2991
2992 return -EINVAL;
2993 } else if (mcs_inst->busy) {
2994
2995 LOG_DBG("mcs_inst busy");
2996 return -EBUSY;
2997 } else if (mcs_inst->playing_orders_supported_handle == 0) {
2998 LOG_DBG("handle not set");
2999
3000 return -EINVAL;
3001 }
3002
3003 mcs_inst->read_params.func = mcc_read_playing_orders_supported_cb;
3004 mcs_inst->read_params.handle_count = 1;
3005 mcs_inst->read_params.single.handle = mcs_inst->playing_orders_supported_handle;
3006 mcs_inst->read_params.single.offset = 0U;
3007
3008 err = bt_gatt_read(conn, &mcs_inst->read_params);
3009 if (!err) {
3010 mcs_inst->busy = true;
3011 }
3012 return err;
3013 }
3014 #endif /* defined(CONFIG_BT_MCC_READ_PLAYING_ORDER_SUPPORTED) */
3015
3016 #if defined(CONFIG_BT_MCC_READ_MEDIA_STATE)
bt_mcc_read_media_state(struct bt_conn * conn)3017 int bt_mcc_read_media_state(struct bt_conn *conn)
3018 {
3019 struct mcs_instance_t *mcs_inst;
3020 int err;
3021
3022 CHECKIF(conn == NULL) {
3023 LOG_DBG("conn is NULL");
3024
3025 return -EINVAL;
3026 }
3027
3028 mcs_inst = lookup_inst_by_conn(conn);
3029 if (mcs_inst == NULL) {
3030 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3031
3032 return -EINVAL;
3033 } else if (mcs_inst->busy) {
3034
3035 LOG_DBG("mcs_inst busy");
3036 return -EBUSY;
3037 } else if (mcs_inst->media_state_handle == 0) {
3038 LOG_DBG("handle not set");
3039
3040 return -EINVAL;
3041 }
3042
3043 mcs_inst->read_params.func = mcc_read_media_state_cb;
3044 mcs_inst->read_params.handle_count = 1;
3045 mcs_inst->read_params.single.handle = mcs_inst->media_state_handle;
3046 mcs_inst->read_params.single.offset = 0U;
3047
3048 err = bt_gatt_read(conn, &mcs_inst->read_params);
3049 if (!err) {
3050 mcs_inst->busy = true;
3051 }
3052 return err;
3053 }
3054 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_STATE) */
3055
3056 #if defined(CONFIG_BT_MCC_SET_MEDIA_CONTROL_POINT)
bt_mcc_send_cmd(struct bt_conn * conn,const struct mpl_cmd * cmd)3057 int bt_mcc_send_cmd(struct bt_conn *conn, const struct mpl_cmd *cmd)
3058 {
3059 struct mcs_instance_t *mcs_inst;
3060 size_t length;
3061 int err;
3062
3063 CHECKIF(conn == NULL) {
3064 LOG_DBG("conn is NULL");
3065
3066 return -EINVAL;
3067 }
3068
3069 mcs_inst = lookup_inst_by_conn(conn);
3070 if (mcs_inst == NULL) {
3071 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3072
3073 return -EINVAL;
3074 } else if (mcs_inst->busy) {
3075
3076 LOG_DBG("mcs_inst busy");
3077 return -EBUSY;
3078 } else if (mcs_inst->cp_handle == 0) {
3079 LOG_DBG("handle not set");
3080
3081 return -EINVAL;
3082 }
3083
3084 CHECKIF(cmd == NULL) {
3085 LOG_DBG("cmd is NULL");
3086
3087 return -EINVAL;
3088 }
3089
3090 CHECKIF(!BT_MCS_VALID_OP(cmd->opcode)) {
3091 LOG_DBG("Opcode 0x%02X is invalid", cmd->opcode);
3092
3093 return -EINVAL;
3094 }
3095
3096 length = sizeof(cmd->opcode);
3097 (void)memcpy(mcs_inst->write_buf, &cmd->opcode, length);
3098 if (cmd->use_param) {
3099 length += sizeof(cmd->param);
3100 (void)memcpy(&mcs_inst->write_buf[sizeof(cmd->opcode)], &cmd->param,
3101 sizeof(cmd->param));
3102 }
3103
3104 mcs_inst->write_params.offset = 0;
3105 mcs_inst->write_params.data = mcs_inst->write_buf;
3106 mcs_inst->write_params.length = length;
3107 mcs_inst->write_params.handle = mcs_inst->cp_handle;
3108 mcs_inst->write_params.func = mcs_write_cp_cb;
3109
3110 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, sizeof(*cmd), "Command sent");
3111
3112 err = bt_gatt_write(conn, &mcs_inst->write_params);
3113 if (!err) {
3114 mcs_inst->busy = true;
3115 }
3116 return err;
3117 }
3118 #endif /* defined(CONFIG_BT_MCC_SET_MEDIA_CONTROL_POINT) */
3119
3120 #if defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)
bt_mcc_read_opcodes_supported(struct bt_conn * conn)3121 int bt_mcc_read_opcodes_supported(struct bt_conn *conn)
3122 {
3123 struct mcs_instance_t *mcs_inst;
3124 int err;
3125
3126 CHECKIF(conn == NULL) {
3127 LOG_DBG("conn is NULL");
3128
3129 return -EINVAL;
3130 }
3131
3132 mcs_inst = lookup_inst_by_conn(conn);
3133 if (mcs_inst == NULL) {
3134 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3135
3136 return -EINVAL;
3137 } else if (mcs_inst->busy) {
3138
3139 LOG_DBG("mcs_inst busy");
3140 return -EBUSY;
3141 } else if (mcs_inst->opcodes_supported_handle == 0) {
3142 LOG_DBG("handle not set");
3143
3144 return -EINVAL;
3145 }
3146
3147 mcs_inst->read_params.func = mcc_read_opcodes_supported_cb;
3148 mcs_inst->read_params.handle_count = 1;
3149 mcs_inst->read_params.single.handle = mcs_inst->opcodes_supported_handle;
3150 mcs_inst->read_params.single.offset = 0U;
3151
3152 err = bt_gatt_read(conn, &mcs_inst->read_params);
3153 if (!err) {
3154 mcs_inst->busy = true;
3155 }
3156 return err;
3157 }
3158 #endif /* defined(CONFIG_BT_MCC_READ_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED) */
3159
3160 #ifdef CONFIG_BT_MCC_OTS
bt_mcc_send_search(struct bt_conn * conn,const struct mpl_search * search)3161 int bt_mcc_send_search(struct bt_conn *conn, const struct mpl_search *search)
3162 {
3163 struct mcs_instance_t *mcs_inst;
3164 int err;
3165
3166 CHECKIF(conn == NULL) {
3167 LOG_DBG("conn is NULL");
3168
3169 return -EINVAL;
3170 }
3171
3172 mcs_inst = lookup_inst_by_conn(conn);
3173 if (mcs_inst == NULL) {
3174 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3175
3176 return -EINVAL;
3177 } else if (mcs_inst->busy) {
3178
3179 LOG_DBG("mcs_inst busy");
3180 return -EBUSY;
3181 } else if (mcs_inst->scp_handle == 0) {
3182 LOG_DBG("handle not set");
3183
3184 return -EINVAL;
3185 }
3186
3187 CHECKIF(search == NULL) {
3188 LOG_DBG("search is NULL");
3189
3190 return -EINVAL;
3191 }
3192
3193 CHECKIF(!IN_RANGE(search->len, SEARCH_LEN_MIN, SEARCH_LEN_MAX)) {
3194 LOG_DBG("Invalid search->len: %u", search->len);
3195
3196 return -EINVAL;
3197 }
3198
3199 (void)memcpy(mcs_inst->write_buf, &search->search, search->len);
3200
3201 mcs_inst->write_params.offset = 0;
3202 mcs_inst->write_params.data = mcs_inst->write_buf;
3203 mcs_inst->write_params.length = search->len;
3204 mcs_inst->write_params.handle = mcs_inst->scp_handle;
3205 mcs_inst->write_params.func = mcs_write_scp_cb;
3206
3207 LOG_HEXDUMP_DBG(mcs_inst->write_params.data, search->len, "Search sent");
3208
3209 err = bt_gatt_write(conn, &mcs_inst->write_params);
3210 if (!err) {
3211 mcs_inst->busy = true;
3212 }
3213 return err;
3214 }
3215
bt_mcc_read_search_results_obj_id(struct bt_conn * conn)3216 int bt_mcc_read_search_results_obj_id(struct bt_conn *conn)
3217 {
3218 struct mcs_instance_t *mcs_inst;
3219 int err;
3220
3221 CHECKIF(conn == NULL) {
3222 LOG_DBG("conn is NULL");
3223
3224 return -EINVAL;
3225 }
3226
3227 mcs_inst = lookup_inst_by_conn(conn);
3228 if (mcs_inst == NULL) {
3229 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3230
3231 return -EINVAL;
3232 } else if (mcs_inst->busy) {
3233
3234 LOG_DBG("mcs_inst busy");
3235 return -EBUSY;
3236 } else if (mcs_inst->search_results_obj_id_handle == 0) {
3237 LOG_DBG("handle not set");
3238
3239 return -EINVAL;
3240 }
3241
3242 mcs_inst->read_params.func = mcc_read_search_results_obj_id_cb;
3243 mcs_inst->read_params.handle_count = 1;
3244 mcs_inst->read_params.single.handle = mcs_inst->search_results_obj_id_handle;
3245 mcs_inst->read_params.single.offset = 0U;
3246
3247 err = bt_gatt_read(conn, &mcs_inst->read_params);
3248 if (!err) {
3249 mcs_inst->busy = true;
3250 }
3251 return err;
3252 }
3253 #endif /* CONFIG_BT_MCC_OTS */
3254
3255 #if defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID)
bt_mcc_read_content_control_id(struct bt_conn * conn)3256 int bt_mcc_read_content_control_id(struct bt_conn *conn)
3257 {
3258 struct mcs_instance_t *mcs_inst;
3259 int err;
3260
3261 CHECKIF(conn == NULL) {
3262 LOG_DBG("conn is NULL");
3263
3264 return -EINVAL;
3265 }
3266
3267 mcs_inst = lookup_inst_by_conn(conn);
3268 if (mcs_inst == NULL) {
3269 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3270
3271 return -EINVAL;
3272 } else if (mcs_inst->busy) {
3273
3274 LOG_DBG("mcs_inst busy");
3275 return -EBUSY;
3276 } else if (mcs_inst->content_control_id_handle == 0) {
3277 LOG_DBG("handle not set");
3278
3279 return -EINVAL;
3280 }
3281
3282 mcs_inst->read_params.func = mcc_read_content_control_id_cb;
3283 mcs_inst->read_params.handle_count = 1;
3284 mcs_inst->read_params.single.handle = mcs_inst->content_control_id_handle;
3285 mcs_inst->read_params.single.offset = 0U;
3286
3287 err = bt_gatt_read(conn, &mcs_inst->read_params);
3288 if (!err) {
3289 mcs_inst->busy = true;
3290 }
3291 return err;
3292 }
3293 #endif /* defined(CONFIG_BT_MCC_READ_CONTENT_CONTROL_ID) */
3294
3295 #ifdef CONFIG_BT_MCC_OTS
3296
on_obj_selected(struct bt_ots_client * otc_inst,struct bt_conn * conn,int result)3297 void on_obj_selected(struct bt_ots_client *otc_inst,
3298 struct bt_conn *conn, int result)
3299 {
3300 LOG_DBG("Current object selected");
3301 /* TODO: Read metadata here? */
3302 /* For now: Left to the application */
3303
3304 /* Only one object at a time is selected in OTS */
3305 /* When the selected callback comes, a new object is selected */
3306 /* Reset the object buffer */
3307 net_buf_simple_reset(&otc_obj_buf);
3308
3309 if (mcc_cb && mcc_cb->otc_obj_selected) {
3310 mcc_cb->otc_obj_selected(conn, OLCP_RESULT_TO_ERROR(result));
3311 }
3312 }
3313
3314 /* TODO: Merge the object callback functions into one */
3315 /* Use a notion of the "active" object, as done in mpl.c, for tracking */
on_icon_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3316 int on_icon_content(struct bt_ots_client *otc_inst, struct bt_conn *conn,
3317 uint32_t offset, uint32_t len, uint8_t *data_p,
3318 bool is_complete)
3319 {
3320 int cb_err = 0;
3321
3322 LOG_DBG("Received Media Player Icon content, %i bytes at offset %i",
3323 len, offset);
3324
3325 LOG_HEXDUMP_DBG(data_p, len, "Icon content");
3326
3327 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3328 LOG_WRN("Can not fit whole object");
3329 cb_err = -EMSGSIZE;
3330 }
3331
3332 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3333 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3334
3335 if (is_complete) {
3336 LOG_DBG("Icon object received");
3337
3338 if (mcc_cb && mcc_cb->otc_icon_object) {
3339 mcc_cb->otc_icon_object(conn, cb_err, &otc_obj_buf);
3340 }
3341 /* Reset buf in case the same object is read again without */
3342 /* calling select in between */
3343 net_buf_simple_reset(&otc_obj_buf);
3344 }
3345
3346 return BT_OTS_CONTINUE;
3347 }
3348
3349 #if CONFIG_BT_MCC_LOG_LEVEL_DBG
3350 struct track_seg_t {
3351 uint8_t name_len;
3352 char name[CONFIG_BT_MCC_SEGMENT_NAME_MAX];
3353 int32_t pos;
3354 };
3355
3356 struct track_segs_t {
3357 uint16_t cnt;
3358 struct track_seg_t segs[CONFIG_BT_MCC_TRACK_SEGS_MAX_CNT];
3359 };
3360
decode_track_segments(struct net_buf_simple * buff,struct track_segs_t * track_segs)3361 static void decode_track_segments(struct net_buf_simple *buff,
3362 struct track_segs_t *track_segs)
3363 {
3364 uint16_t i;
3365 struct track_seg_t *seg;
3366 uint8_t *name;
3367 struct net_buf_simple tmp_buf;
3368
3369 /* Copy the buf, to not consume the original in this debug function */
3370 net_buf_simple_clone(buff, &tmp_buf);
3371
3372 while (tmp_buf.len &&
3373 track_segs->cnt < CONFIG_BT_MCC_TRACK_SEGS_MAX_CNT) {
3374
3375 i = track_segs->cnt++;
3376 seg = &track_segs->segs[i];
3377
3378 seg->name_len = net_buf_simple_pull_u8(&tmp_buf);
3379 if (seg->name_len + sizeof(int32_t) > tmp_buf.len) {
3380 LOG_WRN("Segment too long");
3381 return;
3382 }
3383
3384 if (seg->name_len) {
3385
3386 name = net_buf_simple_pull_mem(&tmp_buf, seg->name_len);
3387
3388 if (seg->name_len >= CONFIG_BT_MCC_SEGMENT_NAME_MAX) {
3389 seg->name_len =
3390 CONFIG_BT_MCC_SEGMENT_NAME_MAX - 1;
3391 }
3392 (void)memcpy(seg->name, name, seg->name_len);
3393 }
3394 seg->name[seg->name_len] = '\0';
3395
3396 track_segs->segs[i].pos = (int32_t)net_buf_simple_pull_le32(&tmp_buf);
3397 }
3398 }
3399 #endif /* CONFIG_BT_MCC_LOG_LEVEL_DBG */
3400
on_track_segments_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3401 int on_track_segments_content(struct bt_ots_client *otc_inst,
3402 struct bt_conn *conn, uint32_t offset,
3403 uint32_t len, uint8_t *data_p, bool is_complete)
3404 {
3405 int cb_err = 0;
3406
3407 LOG_DBG("Received Track Segments content, %i bytes at offset %i",
3408 len, offset);
3409
3410 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3411 LOG_WRN("Can not fit whole object");
3412 cb_err = -EMSGSIZE;
3413 }
3414
3415 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3416 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3417
3418 if (is_complete) {
3419 LOG_DBG("Track segment object received");
3420
3421 #if CONFIG_BT_MCC_LOG_LEVEL_DBG
3422 struct track_segs_t track_segments;
3423
3424 track_segments.cnt = 0;
3425 decode_track_segments(&otc_obj_buf, &track_segments);
3426 for (int i = 0; i < track_segments.cnt; i++) {
3427 LOG_DBG("Track segment %i:", i);
3428 LOG_DBG("\t-Name\t:%s",
3429 track_segments.segs[i].name);
3430 LOG_DBG("\t-Position\t:%d", track_segments.segs[i].pos);
3431 }
3432 #endif /* CONFIG_BT_MCC_LOG_LEVEL_DBG */
3433
3434 if (mcc_cb && mcc_cb->otc_track_segments_object) {
3435 mcc_cb->otc_track_segments_object(conn,
3436 cb_err, &otc_obj_buf);
3437 }
3438
3439 net_buf_simple_reset(&otc_obj_buf);
3440 }
3441
3442 return BT_OTS_CONTINUE;
3443 }
3444
on_current_track_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3445 int on_current_track_content(struct bt_ots_client *otc_inst,
3446 struct bt_conn *conn, uint32_t offset,
3447 uint32_t len, uint8_t *data_p, bool is_complete)
3448 {
3449 int cb_err = 0;
3450
3451 LOG_DBG("Received Current Track content, %i bytes at offset %i",
3452 len, offset);
3453
3454 LOG_HEXDUMP_DBG(data_p, len, "Track content");
3455
3456 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3457 LOG_WRN("Can not fit whole object");
3458 cb_err = -EMSGSIZE;
3459 }
3460
3461 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3462 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3463
3464 if (is_complete) {
3465 LOG_DBG("Current Track Object received");
3466
3467 if (mcc_cb && mcc_cb->otc_current_track_object) {
3468 mcc_cb->otc_current_track_object(conn, cb_err, &otc_obj_buf);
3469 }
3470
3471 net_buf_simple_reset(&otc_obj_buf);
3472 }
3473
3474 return BT_OTS_CONTINUE;
3475 }
3476
on_next_track_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3477 int on_next_track_content(struct bt_ots_client *otc_inst,
3478 struct bt_conn *conn, uint32_t offset, uint32_t len,
3479 uint8_t *data_p, bool is_complete)
3480 {
3481 int cb_err = 0;
3482
3483 LOG_DBG("Received Next Track content, %i bytes at offset %i",
3484 len, offset);
3485
3486 LOG_HEXDUMP_DBG(data_p, len, "Track content");
3487
3488 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3489 LOG_WRN("Can not fit whole object");
3490 cb_err = -EMSGSIZE;
3491 }
3492
3493 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3494 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3495
3496 if (is_complete) {
3497 LOG_DBG("Next Track Object received");
3498
3499 if (mcc_cb && mcc_cb->otc_next_track_object) {
3500 mcc_cb->otc_next_track_object(conn, cb_err, &otc_obj_buf);
3501 }
3502
3503 net_buf_simple_reset(&otc_obj_buf);
3504 }
3505
3506 return BT_OTS_CONTINUE;
3507 }
3508
3509
3510 #if CONFIG_BT_MCC_LOG_LEVEL_DBG
3511 struct id_list_elem_t {
3512 uint8_t type;
3513 uint64_t id;
3514 };
3515
3516 struct id_list_t {
3517 struct id_list_elem_t ids[CONFIG_BT_MCC_GROUP_RECORDS_MAX];
3518 uint16_t cnt;
3519 };
3520
decode_group(struct net_buf_simple * buff,struct id_list_t * ids)3521 static void decode_group(struct net_buf_simple *buff,
3522 struct id_list_t *ids)
3523 {
3524 struct net_buf_simple tmp_buf;
3525
3526 /* Copy the buf, to not consume the original in this debug function */
3527 net_buf_simple_clone(buff, &tmp_buf);
3528
3529 while ((tmp_buf.len) && (ids->cnt < CONFIG_BT_MCC_GROUP_RECORDS_MAX)) {
3530 ids->ids[ids->cnt].type = net_buf_simple_pull_u8(&tmp_buf);
3531 ids->ids[ids->cnt++].id = net_buf_simple_pull_le48(&tmp_buf);
3532 }
3533 }
3534 #endif /* CONFIG_BT_MCC_LOG_LEVEL_DBG */
3535
on_parent_group_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3536 int on_parent_group_content(struct bt_ots_client *otc_inst,
3537 struct bt_conn *conn, uint32_t offset,
3538 uint32_t len, uint8_t *data_p, bool is_complete)
3539 {
3540 int cb_err = 0;
3541
3542 LOG_DBG("Received Parent Group content, %i bytes at offset %i",
3543 len, offset);
3544
3545 LOG_HEXDUMP_DBG(data_p, len, "Group content");
3546
3547 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3548 LOG_WRN("Can not fit whole object");
3549 cb_err = -EMSGSIZE;
3550 }
3551
3552 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3553 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3554
3555 if (is_complete) {
3556 LOG_DBG("Parent Group object received");
3557
3558 #if CONFIG_BT_MCC_LOG_LEVEL_DBG
3559 struct id_list_t group = {0};
3560
3561 decode_group(&otc_obj_buf, &group);
3562 for (int i = 0; i < group.cnt; i++) {
3563 char t[BT_OTS_OBJ_ID_STR_LEN];
3564
3565 (void)bt_ots_obj_id_to_str(group.ids[i].id, t,
3566 BT_OTS_OBJ_ID_STR_LEN);
3567 LOG_DBG("Object type: %d, object ID: %s",
3568 group.ids[i].type, t);
3569 }
3570 #endif /* CONFIG_BT_MCC_LOG_LEVEL_DBG */
3571
3572 if (mcc_cb && mcc_cb->otc_parent_group_object) {
3573 mcc_cb->otc_parent_group_object(conn, cb_err, &otc_obj_buf);
3574 }
3575
3576 net_buf_simple_reset(&otc_obj_buf);
3577 }
3578
3579 return BT_OTS_CONTINUE;
3580 }
3581
on_current_group_content(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)3582 int on_current_group_content(struct bt_ots_client *otc_inst,
3583 struct bt_conn *conn, uint32_t offset,
3584 uint32_t len, uint8_t *data_p, bool is_complete)
3585 {
3586 int cb_err = 0;
3587
3588 LOG_DBG("Received Current Group content, %i bytes at offset %i",
3589 len, offset);
3590
3591 LOG_HEXDUMP_DBG(data_p, len, "Group content");
3592
3593 if (len > net_buf_simple_tailroom(&otc_obj_buf)) {
3594 LOG_WRN("Can not fit whole object");
3595 cb_err = -EMSGSIZE;
3596 }
3597
3598 net_buf_simple_add_mem(&otc_obj_buf, data_p,
3599 MIN(net_buf_simple_tailroom(&otc_obj_buf), len));
3600
3601 if (is_complete) {
3602 LOG_DBG("Current Group object received");
3603
3604 #if CONFIG_BT_MCC_LOG_LEVEL_DBG
3605 struct id_list_t group = {0};
3606
3607 decode_group(&otc_obj_buf, &group);
3608 for (int i = 0; i < group.cnt; i++) {
3609 char t[BT_OTS_OBJ_ID_STR_LEN];
3610
3611 (void)bt_ots_obj_id_to_str(group.ids[i].id, t,
3612 BT_OTS_OBJ_ID_STR_LEN);
3613 LOG_DBG("Object type: %d, object ID: %s",
3614 group.ids[i].type, t);
3615 }
3616 #endif /* CONFIG_BT_MCC_LOG_LEVEL_DBG */
3617
3618 if (mcc_cb && mcc_cb->otc_current_group_object) {
3619 mcc_cb->otc_current_group_object(conn, cb_err, &otc_obj_buf);
3620 }
3621
3622 net_buf_simple_reset(&otc_obj_buf);
3623 }
3624
3625 return BT_OTS_CONTINUE;
3626 }
3627
on_object_metadata(struct bt_ots_client * otc_inst,struct bt_conn * conn,int err,uint8_t metadata_read)3628 void on_object_metadata(struct bt_ots_client *otc_inst,
3629 struct bt_conn *conn, int err,
3630 uint8_t metadata_read)
3631 {
3632 LOG_INF("Object's meta data:");
3633 LOG_INF("\tCurrent size\t:%u", otc_inst->cur_object.size.cur);
3634
3635 if (otc_inst->cur_object.size.cur > otc_obj_buf.size) {
3636 LOG_DBG("Object larger than allocated buffer");
3637 }
3638
3639 bt_ots_metadata_display(&otc_inst->cur_object, 1);
3640
3641 if (mcc_cb && mcc_cb->otc_obj_metadata) {
3642 mcc_cb->otc_obj_metadata(conn, err);
3643 }
3644 }
3645
bt_mcc_otc_read_object_metadata(struct bt_conn * conn)3646 int bt_mcc_otc_read_object_metadata(struct bt_conn *conn)
3647 {
3648 struct mcs_instance_t *mcs_inst;
3649 int err;
3650
3651 CHECKIF(conn == NULL) {
3652 LOG_DBG("conn is NULL");
3653
3654 return -EINVAL;
3655 }
3656
3657 mcs_inst = lookup_inst_by_conn(conn);
3658 if (mcs_inst == NULL) {
3659 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3660
3661 return -EINVAL;
3662 } else if (mcs_inst->busy) {
3663
3664 LOG_DBG("mcs_inst busy");
3665 return -EBUSY;
3666 }
3667
3668 err = bt_ots_client_read_object_metadata(&mcs_inst->otc, conn,
3669 BT_OTS_METADATA_REQ_ALL);
3670 if (err) {
3671 LOG_DBG("Error reading the object: %d", err);
3672 }
3673
3674 return err;
3675 }
3676
3677
bt_mcc_otc_read_icon_object(struct bt_conn * conn)3678 int bt_mcc_otc_read_icon_object(struct bt_conn *conn)
3679 {
3680 struct mcs_instance_t *mcs_inst;
3681 int err;
3682
3683 CHECKIF(conn == NULL) {
3684 LOG_DBG("conn is NULL");
3685
3686 return -EINVAL;
3687 }
3688
3689 mcs_inst = lookup_inst_by_conn(conn);
3690 if (mcs_inst == NULL) {
3691 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3692
3693 return -EINVAL;
3694 } else if (mcs_inst->busy) {
3695
3696 LOG_DBG("mcs_inst busy");
3697 return -EBUSY;
3698 }
3699
3700 mcs_inst->otc.cb->obj_data_read = on_icon_content;
3701
3702 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3703 if (err) {
3704 LOG_DBG("Error reading the object: %d", err);
3705 }
3706
3707 return err;
3708 }
3709
bt_mcc_otc_read_track_segments_object(struct bt_conn * conn)3710 int bt_mcc_otc_read_track_segments_object(struct bt_conn *conn)
3711 {
3712 struct mcs_instance_t *mcs_inst;
3713 int err;
3714
3715 CHECKIF(conn == NULL) {
3716 LOG_DBG("conn is NULL");
3717
3718 return -EINVAL;
3719 }
3720
3721 mcs_inst = lookup_inst_by_conn(conn);
3722 if (mcs_inst == NULL) {
3723 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3724
3725 return -EINVAL;
3726 } else if (mcs_inst->busy) {
3727
3728 LOG_DBG("mcs_inst busy");
3729 return -EBUSY;
3730 }
3731
3732 /* TODO: Assumes object is already selected */
3733 mcs_inst->otc.cb->obj_data_read = on_track_segments_content;
3734
3735 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3736 if (err) {
3737 LOG_DBG("Error reading the object: %d", err);
3738 }
3739
3740 return err;
3741 }
3742
bt_mcc_otc_read_current_track_object(struct bt_conn * conn)3743 int bt_mcc_otc_read_current_track_object(struct bt_conn *conn)
3744 {
3745 struct mcs_instance_t *mcs_inst;
3746 int err;
3747
3748 CHECKIF(conn == NULL) {
3749 LOG_DBG("conn is NULL");
3750
3751 return -EINVAL;
3752 }
3753
3754 mcs_inst = lookup_inst_by_conn(conn);
3755 if (mcs_inst == NULL) {
3756 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3757
3758 return -EINVAL;
3759 } else if (mcs_inst->busy) {
3760
3761 LOG_DBG("mcs_inst busy");
3762 return -EBUSY;
3763 }
3764
3765 /* TODO: Assumes object is already selected */
3766 mcs_inst->otc.cb->obj_data_read = on_current_track_content;
3767
3768 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3769 if (err) {
3770 LOG_DBG("Error reading the object: %d", err);
3771 }
3772
3773 return err;
3774 }
3775
bt_mcc_otc_read_next_track_object(struct bt_conn * conn)3776 int bt_mcc_otc_read_next_track_object(struct bt_conn *conn)
3777 {
3778 struct mcs_instance_t *mcs_inst;
3779 int err;
3780
3781 CHECKIF(conn == NULL) {
3782 LOG_DBG("conn is NULL");
3783
3784 return -EINVAL;
3785 }
3786
3787 mcs_inst = lookup_inst_by_conn(conn);
3788 if (mcs_inst == NULL) {
3789 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3790
3791 return -EINVAL;
3792 } else if (mcs_inst->busy) {
3793
3794 LOG_DBG("mcs_inst busy");
3795 return -EBUSY;
3796 }
3797
3798 /* TODO: Assumes object is already selected */
3799 mcs_inst->otc.cb->obj_data_read = on_next_track_content;
3800
3801 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3802 if (err) {
3803 LOG_DBG("Error reading the object: %d", err);
3804 }
3805
3806 return err;
3807 }
3808
bt_mcc_otc_read_parent_group_object(struct bt_conn * conn)3809 int bt_mcc_otc_read_parent_group_object(struct bt_conn *conn)
3810 {
3811 struct mcs_instance_t *mcs_inst;
3812 int err;
3813
3814 CHECKIF(conn == NULL) {
3815 LOG_DBG("conn is NULL");
3816
3817 return -EINVAL;
3818 }
3819
3820 mcs_inst = lookup_inst_by_conn(conn);
3821 if (mcs_inst == NULL) {
3822 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3823
3824 return -EINVAL;
3825 } else if (mcs_inst->busy) {
3826
3827 LOG_DBG("mcs_inst busy");
3828 return -EBUSY;
3829 }
3830
3831 /* TODO: Assumes object is already selected */
3832
3833 /* Reuse callback for current group */
3834 mcs_inst->otc.cb->obj_data_read = on_parent_group_content;
3835
3836 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3837 if (err) {
3838 LOG_DBG("Error reading the object: %d", err);
3839 }
3840
3841 return err;
3842 }
3843
bt_mcc_otc_read_current_group_object(struct bt_conn * conn)3844 int bt_mcc_otc_read_current_group_object(struct bt_conn *conn)
3845 {
3846 struct mcs_instance_t *mcs_inst;
3847 int err;
3848
3849 CHECKIF(conn == NULL) {
3850 LOG_DBG("conn is NULL");
3851
3852 return -EINVAL;
3853 }
3854
3855 mcs_inst = lookup_inst_by_conn(conn);
3856 if (mcs_inst == NULL) {
3857 LOG_DBG("Could not lookup mcs_inst from conn %p", (void *)conn);
3858
3859 return -EINVAL;
3860 } else if (mcs_inst->busy) {
3861
3862 LOG_DBG("mcs_inst busy");
3863 return -EBUSY;
3864 }
3865
3866 /* TODO: Assumes object is already selected */
3867 mcs_inst->otc.cb->obj_data_read = on_current_group_content;
3868
3869 err = bt_ots_client_read_object_data(&mcs_inst->otc, conn);
3870 if (err) {
3871 LOG_DBG("Error reading the object: %d", err);
3872 }
3873
3874 return err;
3875 }
3876
3877 #if defined(CONFIG_BT_MCC_SHELL)
bt_mcc_otc_inst(struct bt_conn * conn)3878 struct bt_ots_client *bt_mcc_otc_inst(struct bt_conn *conn)
3879 {
3880 struct mcs_instance_t *mcs_inst;
3881
3882 mcs_inst = lookup_inst_by_conn(conn);
3883 if (mcs_inst == NULL) {
3884 return NULL;
3885 }
3886
3887 return &mcs_inst->otc;
3888 }
3889 #endif /* defined(CONFIG_BT_MCC_SHELL) */
3890
3891 #endif /* CONFIG_BT_MCC_OTS */
3892