1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #include <zephyr/autoconf.h>
14 #include <zephyr/bluetooth/att.h>
15 #include <zephyr/bluetooth/audio/gmap.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/uuid.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/net_buf.h>
21 #include <zephyr/sys/__assert.h>
22 #include <zephyr/sys/atomic.h>
23 #include <zephyr/sys/check.h>
24
25 #include "audio_internal.h"
26
27 LOG_MODULE_REGISTER(bt_gmap_client, CONFIG_BT_GMAP_LOG_LEVEL);
28
29 static const struct bt_uuid *gmas_uuid = BT_UUID_GMAS;
30 static const struct bt_uuid *gmap_role_uuid = BT_UUID_GMAP_ROLE;
31 static const struct bt_uuid *gmap_ugg_feat_uuid = BT_UUID_GMAP_UGG_FEAT;
32 static const struct bt_uuid *gmap_ugt_feat_uuid = BT_UUID_GMAP_UGT_FEAT;
33 static const struct bt_uuid *gmap_bgs_feat_uuid = BT_UUID_GMAP_BGS_FEAT;
34 static const struct bt_uuid *gmap_bgr_feat_uuid = BT_UUID_GMAP_BGR_FEAT;
35
36 static const struct bt_gmap_cb *gmap_cb;
37
38 enum gmap_client_flag {
39 GMAP_CLIENT_FLAG_BUSY,
40
41 GMAP_CLIENT_FLAG_NUM_FLAGS, /* keep as last */
42 };
43
44 static struct bt_gmap_client {
45 /** Profile connection reference */
46 struct bt_conn *conn;
47
48 /* Remote role and features */
49 enum bt_gmap_role role;
50 struct bt_gmap_feat feat;
51
52 uint16_t svc_start_handle;
53 uint16_t svc_end_handle;
54
55 /* GATT procedure parameters */
56 union {
57 struct bt_gatt_read_params read;
58 struct bt_gatt_discover_params discover;
59 } params;
60
61 ATOMIC_DEFINE(flags, GMAP_CLIENT_FLAG_NUM_FLAGS);
62 } gmap_insts[CONFIG_BT_MAX_CONN];
63
gmap_reset(struct bt_gmap_client * gmap_cli)64 static void gmap_reset(struct bt_gmap_client *gmap_cli)
65 {
66 if (gmap_cli->conn != NULL) {
67 bt_conn_unref(gmap_cli->conn);
68 }
69
70 memset(gmap_cli, 0, sizeof(*gmap_cli));
71 }
72
client_by_conn(struct bt_conn * conn)73 static struct bt_gmap_client *client_by_conn(struct bt_conn *conn)
74 {
75 struct bt_gmap_client *gmap_cli = &gmap_insts[bt_conn_index(conn)];
76
77 if (gmap_cli->conn == conn) {
78 return gmap_cli;
79 }
80
81 return NULL;
82 }
83
disconnected(struct bt_conn * conn,uint8_t reason)84 static void disconnected(struct bt_conn *conn, uint8_t reason)
85 {
86 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
87
88 if (gmap_cli != NULL) {
89 bt_conn_unref(gmap_cli->conn);
90 gmap_cli->conn = NULL;
91 }
92 }
93
94 BT_CONN_CB_DEFINE(conn_callbacks) = {
95 .disconnected = disconnected,
96 };
97
discover_complete(struct bt_gmap_client * gmap_cli)98 static void discover_complete(struct bt_gmap_client *gmap_cli)
99 {
100 LOG_DBG("conn %p", (void *)gmap_cli->conn);
101
102 atomic_clear_bit(gmap_cli->flags, GMAP_CLIENT_FLAG_BUSY);
103
104 if (gmap_cb->discover != NULL) {
105 gmap_cb->discover(gmap_cli->conn, 0, gmap_cli->role, gmap_cli->feat);
106 }
107 }
108
discover_failed(struct bt_gmap_client * gmap_cli,int err)109 static void discover_failed(struct bt_gmap_client *gmap_cli, int err)
110 {
111 struct bt_conn *conn = gmap_cli->conn;
112
113 gmap_reset(gmap_cli);
114
115 LOG_DBG("conn %p err %d", (void *)conn, err);
116
117 gmap_cb->discover(conn, err, 0, (struct bt_gmap_feat){0});
118 }
119
bgr_feat_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)120 static uint8_t bgr_feat_read_cb(struct bt_conn *conn, uint8_t att_err,
121 struct bt_gatt_read_params *params, const void *data, uint16_t len)
122 {
123 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
124 struct net_buf_simple buf;
125 int err = att_err;
126
127 __ASSERT(gmap_cli, "no instance for conn %p", (void *)conn);
128
129 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
130 data, len);
131
132 if (data == NULL || att_err != BT_ATT_ERR_SUCCESS || len != sizeof(uint8_t)) {
133 if (att_err == BT_ATT_ERR_SUCCESS) {
134 att_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
135 }
136
137 discover_failed(gmap_cli, err);
138
139 return BT_GATT_ITER_STOP;
140 }
141
142 net_buf_simple_init_with_data(&buf, (void *)data, len);
143
144 gmap_cli->feat.bgr_feat = net_buf_simple_pull_u8(&buf);
145 LOG_DBG("bgr_feat 0x%02x", gmap_cli->feat.bgr_feat);
146
147 discover_complete(gmap_cli);
148
149 return BT_GATT_ITER_STOP;
150 }
151
gmap_read_bgr_feat(struct bt_gmap_client * gmap_cli,uint16_t handle)152 static int gmap_read_bgr_feat(struct bt_gmap_client *gmap_cli, uint16_t handle)
153 {
154 LOG_DBG("conn %p handle 0x%04x", (void *)gmap_cli->conn, handle);
155
156 memset(&gmap_cli->params.read, 0, sizeof(gmap_cli->params.read));
157
158 gmap_cli->params.read.func = bgr_feat_read_cb;
159 gmap_cli->params.read.handle_count = 1u;
160 gmap_cli->params.read.single.handle = handle;
161 gmap_cli->params.read.single.offset = 0u;
162
163 return bt_gatt_read(gmap_cli->conn, &gmap_cli->params.read);
164 }
165
bgr_feat_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)166 static uint8_t bgr_feat_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
167 struct bt_gatt_discover_params *params)
168 {
169 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
170 const struct bt_gatt_chrc *chrc;
171 int err;
172
173 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
174
175 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
176
177 if (attr == NULL) {
178 discover_failed(gmap_cli, -ENOENT);
179
180 return BT_GATT_ITER_STOP;
181 }
182
183 chrc = attr->user_data;
184
185 /* Read features */
186 err = gmap_read_bgr_feat(gmap_cli, chrc->value_handle);
187 if (err != 0) {
188 discover_failed(gmap_cli, err);
189 }
190
191 return BT_GATT_ITER_STOP;
192 }
193
gmap_discover_bgr_feat(struct bt_gmap_client * gmap_cli)194 static int gmap_discover_bgr_feat(struct bt_gmap_client *gmap_cli)
195 {
196 LOG_DBG("conn %p", (void *)gmap_cli->conn);
197
198 memset(&gmap_cli->params.discover, 0, sizeof(gmap_cli->params.discover));
199
200 gmap_cli->params.discover.func = bgr_feat_discover_func;
201 gmap_cli->params.discover.uuid = gmap_bgr_feat_uuid;
202 gmap_cli->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
203 gmap_cli->params.discover.start_handle = gmap_cli->svc_start_handle;
204 gmap_cli->params.discover.end_handle = gmap_cli->svc_end_handle;
205
206 return bt_gatt_discover(gmap_cli->conn, &gmap_cli->params.discover);
207 }
208
bgs_feat_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)209 static uint8_t bgs_feat_read_cb(struct bt_conn *conn, uint8_t att_err,
210 struct bt_gatt_read_params *params, const void *data, uint16_t len)
211 {
212 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
213 struct net_buf_simple buf;
214 int err = att_err;
215
216 __ASSERT(gmap_cli, "no instance for conn %p", (void *)conn);
217
218 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
219 data, len);
220
221 if (data == NULL || att_err != BT_ATT_ERR_SUCCESS || len != sizeof(uint8_t)) {
222 if (att_err == BT_ATT_ERR_SUCCESS) {
223 att_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
224 }
225
226 discover_failed(gmap_cli, err);
227
228 return BT_GATT_ITER_STOP;
229 }
230
231 net_buf_simple_init_with_data(&buf, (void *)data, len);
232
233 gmap_cli->feat.bgs_feat = net_buf_simple_pull_u8(&buf);
234 LOG_DBG("bgs_feat 0x%02x", gmap_cli->feat.bgs_feat);
235
236 if ((gmap_cli->role & BT_GMAP_ROLE_BGR) != 0) {
237 err = gmap_discover_bgr_feat(gmap_cli);
238 } else {
239 discover_complete(gmap_cli);
240
241 return BT_GATT_ITER_STOP;
242 }
243
244 if (err) {
245 discover_failed(gmap_cli, err);
246 }
247
248 return BT_GATT_ITER_STOP;
249 }
250
gmap_read_bgs_feat(struct bt_gmap_client * gmap_cli,uint16_t handle)251 static int gmap_read_bgs_feat(struct bt_gmap_client *gmap_cli, uint16_t handle)
252 {
253 LOG_DBG("conn %p handle 0x%04x", (void *)gmap_cli->conn, handle);
254
255 memset(&gmap_cli->params.read, 0, sizeof(gmap_cli->params.read));
256
257 gmap_cli->params.read.func = bgs_feat_read_cb;
258 gmap_cli->params.read.handle_count = 1u;
259 gmap_cli->params.read.single.handle = handle;
260 gmap_cli->params.read.single.offset = 0u;
261
262 return bt_gatt_read(gmap_cli->conn, &gmap_cli->params.read);
263 }
264
bgs_feat_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)265 static uint8_t bgs_feat_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
266 struct bt_gatt_discover_params *params)
267 {
268 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
269 const struct bt_gatt_chrc *chrc;
270 int err;
271
272 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
273
274 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
275
276 if (attr == NULL) {
277 discover_failed(gmap_cli, -ENOENT);
278
279 return BT_GATT_ITER_STOP;
280 }
281
282 chrc = attr->user_data;
283
284 /* Read features */
285 err = gmap_read_bgs_feat(gmap_cli, chrc->value_handle);
286 if (err != 0) {
287 discover_failed(gmap_cli, err);
288 }
289
290 return BT_GATT_ITER_STOP;
291 }
292
gmap_discover_bgs_feat(struct bt_gmap_client * gmap_cli)293 static int gmap_discover_bgs_feat(struct bt_gmap_client *gmap_cli)
294 {
295 LOG_DBG("conn %p", (void *)gmap_cli->conn);
296
297 memset(&gmap_cli->params.discover, 0, sizeof(gmap_cli->params.discover));
298
299 gmap_cli->params.discover.func = bgs_feat_discover_func;
300 gmap_cli->params.discover.uuid = gmap_bgs_feat_uuid;
301 gmap_cli->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
302 gmap_cli->params.discover.start_handle = gmap_cli->svc_start_handle;
303 gmap_cli->params.discover.end_handle = gmap_cli->svc_end_handle;
304
305 return bt_gatt_discover(gmap_cli->conn, &gmap_cli->params.discover);
306 }
307
ugt_feat_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)308 static uint8_t ugt_feat_read_cb(struct bt_conn *conn, uint8_t att_err,
309 struct bt_gatt_read_params *params, const void *data, uint16_t len)
310 {
311 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
312 struct net_buf_simple buf;
313 int err = att_err;
314
315 __ASSERT(gmap_cli, "no instance for conn %p", (void *)conn);
316
317 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
318 data, len);
319
320 if (data == NULL || att_err != BT_ATT_ERR_SUCCESS || len != sizeof(uint8_t)) {
321 if (att_err == BT_ATT_ERR_SUCCESS) {
322 att_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
323 }
324
325 discover_failed(gmap_cli, err);
326
327 return BT_GATT_ITER_STOP;
328 }
329
330 net_buf_simple_init_with_data(&buf, (void *)data, len);
331
332 gmap_cli->feat.ugt_feat = net_buf_simple_pull_u8(&buf);
333 LOG_DBG("ugt_feat 0x%02x", gmap_cli->feat.ugt_feat);
334
335 if ((gmap_cli->role & BT_GMAP_ROLE_BGS) != 0) {
336 err = gmap_discover_bgs_feat(gmap_cli);
337 } else if ((gmap_cli->role & BT_GMAP_ROLE_BGR) != 0) {
338 err = gmap_discover_bgr_feat(gmap_cli);
339 } else {
340 discover_complete(gmap_cli);
341
342 return BT_GATT_ITER_STOP;
343 }
344
345 if (err) {
346 discover_failed(gmap_cli, err);
347 }
348
349 return BT_GATT_ITER_STOP;
350 }
351
gmap_read_ugt_feat(struct bt_gmap_client * gmap_cli,uint16_t handle)352 static int gmap_read_ugt_feat(struct bt_gmap_client *gmap_cli, uint16_t handle)
353 {
354 LOG_DBG("conn %p handle 0x%04x", (void *)gmap_cli->conn, handle);
355
356 memset(&gmap_cli->params.read, 0, sizeof(gmap_cli->params.read));
357
358 gmap_cli->params.read.func = ugt_feat_read_cb;
359 gmap_cli->params.read.handle_count = 1u;
360 gmap_cli->params.read.single.handle = handle;
361 gmap_cli->params.read.single.offset = 0u;
362
363 return bt_gatt_read(gmap_cli->conn, &gmap_cli->params.read);
364 }
365
ugt_feat_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)366 static uint8_t ugt_feat_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
367 struct bt_gatt_discover_params *params)
368 {
369 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
370 const struct bt_gatt_chrc *chrc;
371 int err;
372
373 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
374
375 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
376
377 if (attr == NULL) {
378 discover_failed(gmap_cli, -ENOENT);
379
380 return BT_GATT_ITER_STOP;
381 }
382
383 chrc = attr->user_data;
384
385 /* Read features */
386 err = gmap_read_ugt_feat(gmap_cli, chrc->value_handle);
387 if (err != 0) {
388 discover_failed(gmap_cli, err);
389 }
390
391 return BT_GATT_ITER_STOP;
392 }
393
gmap_discover_ugt_feat(struct bt_gmap_client * gmap_cli)394 static int gmap_discover_ugt_feat(struct bt_gmap_client *gmap_cli)
395 {
396 LOG_DBG("conn %p", (void *)gmap_cli->conn);
397
398 memset(&gmap_cli->params.discover, 0, sizeof(gmap_cli->params.discover));
399
400 gmap_cli->params.discover.func = ugt_feat_discover_func;
401 gmap_cli->params.discover.uuid = gmap_ugt_feat_uuid;
402 gmap_cli->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
403 gmap_cli->params.discover.start_handle = gmap_cli->svc_start_handle;
404 gmap_cli->params.discover.end_handle = gmap_cli->svc_end_handle;
405
406 return bt_gatt_discover(gmap_cli->conn, &gmap_cli->params.discover);
407 }
408
ugg_feat_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)409 static uint8_t ugg_feat_read_cb(struct bt_conn *conn, uint8_t att_err,
410 struct bt_gatt_read_params *params, const void *data, uint16_t len)
411 {
412 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
413 struct net_buf_simple buf;
414 int err = att_err;
415
416 __ASSERT(gmap_cli, "no instance for conn %p", (void *)conn);
417
418 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
419 data, len);
420
421 if (data == NULL || att_err != BT_ATT_ERR_SUCCESS || len != sizeof(uint8_t)) {
422 if (att_err == BT_ATT_ERR_SUCCESS) {
423 att_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
424 }
425
426 discover_failed(gmap_cli, err);
427
428 return BT_GATT_ITER_STOP;
429 }
430
431 net_buf_simple_init_with_data(&buf, (void *)data, len);
432
433 gmap_cli->feat.ugg_feat = net_buf_simple_pull_u8(&buf);
434 LOG_DBG("ugg_feat 0x%02x", gmap_cli->feat.ugg_feat);
435
436 if ((gmap_cli->role & BT_GMAP_ROLE_UGT) != 0) {
437 err = gmap_discover_ugt_feat(gmap_cli);
438 } else if ((gmap_cli->role & BT_GMAP_ROLE_BGS) != 0) {
439 err = gmap_discover_bgs_feat(gmap_cli);
440 } else if ((gmap_cli->role & BT_GMAP_ROLE_BGR) != 0) {
441 err = gmap_discover_bgr_feat(gmap_cli);
442 } else {
443 discover_complete(gmap_cli);
444
445 return BT_GATT_ITER_STOP;
446 }
447
448 if (err) {
449 discover_failed(gmap_cli, err);
450 }
451
452 return BT_GATT_ITER_STOP;
453 }
454
gmap_read_ugg_feat(struct bt_gmap_client * gmap_cli,uint16_t handle)455 static int gmap_read_ugg_feat(struct bt_gmap_client *gmap_cli, uint16_t handle)
456 {
457 LOG_DBG("conn %p handle 0x%04x", (void *)gmap_cli->conn, handle);
458
459 memset(&gmap_cli->params.read, 0, sizeof(gmap_cli->params.read));
460
461 gmap_cli->params.read.func = ugg_feat_read_cb;
462 gmap_cli->params.read.handle_count = 1u;
463 gmap_cli->params.read.single.handle = handle;
464 gmap_cli->params.read.single.offset = 0u;
465
466 return bt_gatt_read(gmap_cli->conn, &gmap_cli->params.read);
467 }
468
ugg_feat_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)469 static uint8_t ugg_feat_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
470 struct bt_gatt_discover_params *params)
471 {
472 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
473 const struct bt_gatt_chrc *chrc;
474 int err;
475
476 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
477
478 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
479
480 if (attr == NULL) {
481 discover_failed(gmap_cli, -ENOENT);
482
483 return BT_GATT_ITER_STOP;
484 }
485
486 chrc = attr->user_data;
487
488 /* Read features */
489 err = gmap_read_ugg_feat(gmap_cli, chrc->value_handle);
490 if (err != 0) {
491 discover_failed(gmap_cli, err);
492 }
493
494 return BT_GATT_ITER_STOP;
495 }
496
gmap_discover_ugg_feat(struct bt_gmap_client * gmap_cli)497 static int gmap_discover_ugg_feat(struct bt_gmap_client *gmap_cli)
498 {
499 LOG_DBG("conn %p", (void *)gmap_cli->conn);
500
501 memset(&gmap_cli->params.discover, 0, sizeof(gmap_cli->params.discover));
502
503 gmap_cli->params.discover.func = ugg_feat_discover_func;
504 gmap_cli->params.discover.uuid = gmap_ugg_feat_uuid;
505 gmap_cli->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
506 gmap_cli->params.discover.start_handle = gmap_cli->svc_start_handle;
507 gmap_cli->params.discover.end_handle = gmap_cli->svc_end_handle;
508
509 return bt_gatt_discover(gmap_cli->conn, &gmap_cli->params.discover);
510 }
511
role_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * data,uint16_t len)512 static uint8_t role_read_cb(struct bt_conn *conn, uint8_t att_err,
513 struct bt_gatt_read_params *params, const void *data, uint16_t len)
514 {
515 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
516 struct net_buf_simple buf;
517 int err = att_err;
518
519 __ASSERT(gmap_cli, "no instance for conn %p", (void *)conn);
520
521 LOG_DBG("conn %p att_err 0x%02x params %p data %p len %u", (void *)conn, att_err, params,
522 data, len);
523
524 if (data == NULL || att_err != BT_ATT_ERR_SUCCESS || len != sizeof(uint8_t)) {
525 if (att_err == BT_ATT_ERR_SUCCESS) {
526 att_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
527 }
528
529 discover_failed(gmap_cli, err);
530
531 return BT_GATT_ITER_STOP;
532 }
533
534 net_buf_simple_init_with_data(&buf, (void *)data, len);
535
536 gmap_cli->role = net_buf_simple_pull_u8(&buf);
537 LOG_DBG("role 0x%02x", gmap_cli->role);
538
539 if ((gmap_cli->role & BT_GMAP_ROLE_UGG) != 0) {
540 err = gmap_discover_ugg_feat(gmap_cli);
541 } else if ((gmap_cli->role & BT_GMAP_ROLE_UGT) != 0) {
542 err = gmap_discover_ugt_feat(gmap_cli);
543 } else if ((gmap_cli->role & BT_GMAP_ROLE_BGS) != 0) {
544 err = gmap_discover_bgs_feat(gmap_cli);
545 } else if ((gmap_cli->role & BT_GMAP_ROLE_BGR) != 0) {
546 err = gmap_discover_bgr_feat(gmap_cli);
547 } else {
548 LOG_DBG("Remote device does not support any known roles");
549 err = -ECANCELED;
550 }
551
552 if (err) {
553 discover_failed(gmap_cli, err);
554 }
555
556 return BT_GATT_ITER_STOP;
557 }
558
gmap_read_role(struct bt_gmap_client * gmap_cli,uint16_t handle)559 static int gmap_read_role(struct bt_gmap_client *gmap_cli, uint16_t handle)
560 {
561 LOG_DBG("conn %p handle 0x%04x", (void *)gmap_cli->conn, handle);
562
563 memset(&gmap_cli->params.read, 0, sizeof(gmap_cli->params.read));
564
565 gmap_cli->params.read.func = role_read_cb;
566 gmap_cli->params.read.handle_count = 1u;
567 gmap_cli->params.read.single.handle = handle;
568 gmap_cli->params.read.single.offset = 0u;
569
570 return bt_gatt_read(gmap_cli->conn, &gmap_cli->params.read);
571 }
572
role_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)573 static uint8_t role_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
574 struct bt_gatt_discover_params *params)
575 {
576 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
577 const struct bt_gatt_chrc *chrc;
578 int err;
579
580 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
581
582 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
583
584 if (attr == NULL) {
585 discover_failed(gmap_cli, -ENOENT);
586
587 return BT_GATT_ITER_STOP;
588 }
589
590 chrc = attr->user_data;
591
592 /* Read features */
593 err = gmap_read_role(gmap_cli, chrc->value_handle);
594 if (err != 0) {
595 discover_failed(gmap_cli, err);
596 }
597
598 return BT_GATT_ITER_STOP;
599 }
600
gmap_discover_role(struct bt_gmap_client * gmap_cli)601 static int gmap_discover_role(struct bt_gmap_client *gmap_cli)
602 {
603 LOG_DBG("conn %p", (void *)gmap_cli->conn);
604
605 memset(&gmap_cli->params.discover, 0, sizeof(gmap_cli->params.discover));
606
607 gmap_cli->params.discover.func = role_discover_func;
608 gmap_cli->params.discover.uuid = gmap_role_uuid;
609 gmap_cli->params.discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
610 gmap_cli->params.discover.start_handle = gmap_cli->svc_start_handle;
611 gmap_cli->params.discover.end_handle = gmap_cli->svc_end_handle;
612
613 return bt_gatt_discover(gmap_cli->conn, &gmap_cli->params.discover);
614 }
615
gmas_discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)616 static uint8_t gmas_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
617 struct bt_gatt_discover_params *params)
618 {
619 struct bt_gmap_client *gmap_cli = client_by_conn(conn);
620 const struct bt_gatt_service_val *svc;
621 int err;
622
623 __ASSERT(gmap_cli != NULL, "no instance for conn %p", (void *)conn);
624
625 LOG_DBG("conn %p attr %p params %p", (void *)conn, attr, params);
626
627 if (attr == NULL) {
628 discover_failed(gmap_cli, -ENOENT);
629
630 return BT_GATT_ITER_STOP;
631 }
632
633 svc = (struct bt_gatt_service_val *)attr->user_data;
634 gmap_cli->svc_start_handle = attr->handle;
635 gmap_cli->svc_end_handle = svc->end_handle;
636
637 err = gmap_discover_role(gmap_cli);
638 if (err != 0) {
639 discover_failed(gmap_cli, err);
640 }
641
642 return BT_GATT_ITER_STOP;
643 }
644
bt_gmap_discover(struct bt_conn * conn)645 int bt_gmap_discover(struct bt_conn *conn)
646 {
647 struct bt_gmap_client *gmap_cli;
648 int err;
649
650 CHECKIF(conn == NULL) {
651 LOG_DBG("NULL conn");
652
653 return -EINVAL;
654 }
655
656 gmap_cli = &gmap_insts[bt_conn_index(conn)];
657
658 if (atomic_test_and_set_bit(gmap_cli->flags, GMAP_CLIENT_FLAG_BUSY)) {
659 LOG_DBG("Busy");
660
661 return -EBUSY;
662 }
663
664 gmap_reset(gmap_cli);
665
666 gmap_cli->params.discover.func = gmas_discover_func;
667 gmap_cli->params.discover.uuid = gmas_uuid;
668 gmap_cli->params.discover.type = BT_GATT_DISCOVER_PRIMARY;
669 gmap_cli->params.discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
670 gmap_cli->params.discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
671
672 err = bt_gatt_discover(conn, &gmap_cli->params.discover);
673 if (err != 0) {
674 LOG_DBG("Failed to initiate discovery: %d", err);
675
676 atomic_clear_bit(gmap_cli->flags, GMAP_CLIENT_FLAG_BUSY);
677
678 return -ENOEXEC;
679 }
680
681 gmap_cli->conn = bt_conn_ref(conn);
682
683 return 0;
684 }
685
bt_gmap_cb_register(const struct bt_gmap_cb * cb)686 int bt_gmap_cb_register(const struct bt_gmap_cb *cb)
687 {
688 CHECKIF(cb == NULL) {
689 LOG_DBG("cb is NULL");
690
691 return -EINVAL;
692 }
693
694 if (gmap_cb != NULL) {
695 LOG_DBG("GMAP callbacks already registered");
696
697 return -EALREADY;
698 }
699
700 gmap_cb = cb;
701
702 return 0;
703 }
704