1 /*
2 * Copyright (c) 2023 Codecoup
3 * Copyright (c) 2024 Demant A/S
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/types.h>
14
15 #include <zephyr/autoconf.h>
16 #include <zephyr/bluetooth/att.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/gatt.h>
19 #include <zephyr/bluetooth/uuid.h>
20 #include <zephyr/fff.h>
21 #include <zephyr/sys/__assert.h>
22 #include <zephyr/sys/iterable_sections.h>
23 #include <zephyr/sys/slist.h>
24 #include <zephyr/sys/util.h>
25 #include <zephyr/types.h>
26 #include <zephyr/ztest_test.h>
27 #include <zephyr/ztest_assert.h>
28
29 #include "gatt.h"
30 #include "conn.h"
31 #include "common/bt_str.h"
32
33 #define LOG_LEVEL CONFIG_BT_GATT_LOG_LEVEL
34 #include <zephyr/logging/log.h>
35 LOG_MODULE_REGISTER(bt_gatt);
36
37 /* List of fakes used by this unit tester */
38 #define FFF_FAKES_LIST(FAKE) \
39 FAKE(mock_bt_gatt_notify_cb) \
40 FAKE(mock_bt_gatt_is_subscribed) \
41
42 DEFINE_FAKE_VALUE_FUNC(int, mock_bt_gatt_notify_cb, struct bt_conn *,
43 struct bt_gatt_notify_params *);
44 DEFINE_FAKE_VALUE_FUNC(bool, mock_bt_gatt_is_subscribed, struct bt_conn *,
45 const struct bt_gatt_attr *, uint16_t);
46
47 static uint16_t last_static_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
48 static sys_slist_t db;
49
bt_gatt_attr_read_service(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)50 ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
51 uint16_t len, uint16_t offset)
52 {
53 zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
54 return 0;
55 }
56
bt_gatt_attr_read_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)57 ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
58 uint16_t len, uint16_t offset)
59 {
60 zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
61 return 0;
62 }
63
bt_gatt_attr_read_ccc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)64 ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
65 uint16_t len, uint16_t offset)
66 {
67 zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
68 return 0;
69 }
70
bt_gatt_attr_write_ccc(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)71 ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
72 const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
73 {
74 zassert_unreachable("Unexpected call to '%s()' occurred", __func__);
75 return 0;
76 }
77
mock_bt_gatt_init(void)78 void mock_bt_gatt_init(void)
79 {
80 FFF_FAKES_LIST(RESET_FAKE);
81
82 mock_bt_gatt_is_subscribed_fake.return_val = true;
83 }
84
notify_params_deep_copy_destroy(struct bt_gatt_notify_params * params)85 static void notify_params_deep_copy_destroy(struct bt_gatt_notify_params *params)
86 {
87 struct bt_gatt_notify_params *copy;
88
89 for (unsigned int i = 0; i < mock_bt_gatt_notify_cb_fake.call_count; i++) {
90 copy = mock_bt_gatt_notify_cb_fake.arg1_history[i];
91 if (copy != params) {
92 continue;
93 }
94
95 /* Free UUID deep copy */
96 if (copy->uuid) {
97 free((void *)copy->uuid);
98 }
99
100 free(copy);
101
102 mock_bt_gatt_notify_cb_fake.arg1_history[i] = NULL;
103
104 break;
105 }
106 }
107
notify_params_deep_copy_destroy_all(void)108 static void notify_params_deep_copy_destroy_all(void)
109 {
110 struct bt_gatt_notify_params *copy;
111
112 for (unsigned int i = 0; i < mock_bt_gatt_notify_cb_fake.call_count; i++) {
113 copy = mock_bt_gatt_notify_cb_fake.arg1_history[i];
114 if (copy == NULL) {
115 continue;
116 }
117
118 /* Free UUID deep copy */
119 if (copy->uuid) {
120 free((void *)copy->uuid);
121 }
122
123 free(copy);
124 }
125 }
126
mock_bt_gatt_cleanup(void)127 void mock_bt_gatt_cleanup(void)
128 {
129 notify_params_deep_copy_destroy_all();
130 }
131
uuid_deep_copy(const struct bt_uuid * uuid)132 static struct bt_uuid *uuid_deep_copy(const struct bt_uuid *uuid)
133 {
134 struct bt_uuid *copy;
135
136 switch (uuid->type) {
137 case BT_UUID_TYPE_16:
138 copy = malloc(sizeof(struct bt_uuid_16));
139 zassert_not_null(copy);
140 memcpy(copy, uuid, sizeof(struct bt_uuid_16));
141 break;
142 case BT_UUID_TYPE_32:
143 copy = malloc(sizeof(struct bt_uuid_32));
144 zassert_not_null(copy);
145 memcpy(copy, uuid, sizeof(struct bt_uuid_32));
146 break;
147 case BT_UUID_TYPE_128:
148 copy = malloc(sizeof(struct bt_uuid_128));
149 zassert_not_null(copy);
150 memcpy(copy, uuid, sizeof(struct bt_uuid_128));
151 break;
152 default:
153 zassert_unreachable("Unexpected uuid->type 0x%02x", uuid->type);
154 }
155
156 return copy;
157 }
158
notify_params_deep_copy(struct bt_gatt_notify_params * params)159 static struct bt_gatt_notify_params *notify_params_deep_copy(struct bt_gatt_notify_params *params)
160 {
161 struct bt_gatt_notify_params *copy;
162
163 copy = malloc(sizeof(*params));
164 zassert_not_null(copy);
165
166 memcpy(copy, params, sizeof(*params));
167
168 if (params->uuid != NULL) {
169 copy->uuid = uuid_deep_copy(params->uuid);
170 }
171
172 return copy;
173 }
174
bt_gatt_notify_cb(struct bt_conn * conn,struct bt_gatt_notify_params * params)175 int bt_gatt_notify_cb(struct bt_conn *conn, struct bt_gatt_notify_params *params)
176 {
177 struct bt_gatt_notify_params *copy;
178 int err;
179
180 zassert_not_null(params, "'%s()' was called with incorrect '%s' value", __func__, "params");
181
182 /* Either params->uuid, params->attr, or both has to be provided */
183 zassert_true(params->uuid != NULL || params->attr != NULL,
184 "'%s()' was called with incorrect '%s' value", __func__,
185 "params->uuid or params->attr");
186
187 copy = notify_params_deep_copy(params);
188
189 err = mock_bt_gatt_notify_cb(conn, copy);
190 if (err != 0) {
191 notify_params_deep_copy_destroy(copy);
192 }
193
194 return err;
195 }
196
bt_gatt_notify_cb_reset(void)197 void bt_gatt_notify_cb_reset(void)
198 {
199 notify_params_deep_copy_destroy_all();
200
201 RESET_FAKE(mock_bt_gatt_notify_cb);
202 }
203
204 /* Exact copy of subsys/bluetooth/host/gatt.c:gatt_foreach_iter() */
gatt_foreach_iter(const struct bt_gatt_attr * attr,uint16_t handle,uint16_t start_handle,uint16_t end_handle,const struct bt_uuid * uuid,const void * attr_data,uint16_t * num_matches,bt_gatt_attr_func_t func,void * user_data)205 static uint8_t gatt_foreach_iter(const struct bt_gatt_attr *attr,
206 uint16_t handle, uint16_t start_handle,
207 uint16_t end_handle,
208 const struct bt_uuid *uuid,
209 const void *attr_data, uint16_t *num_matches,
210 bt_gatt_attr_func_t func, void *user_data)
211 {
212 uint8_t result;
213
214 /* Stop if over the requested range */
215 if (handle > end_handle) {
216 return BT_GATT_ITER_STOP;
217 }
218
219 /* Check if attribute handle is within range */
220 if (handle < start_handle) {
221 return BT_GATT_ITER_CONTINUE;
222 }
223
224 /* Match attribute UUID if set */
225 if (uuid && bt_uuid_cmp(uuid, attr->uuid)) {
226 return BT_GATT_ITER_CONTINUE;
227 }
228
229 /* Match attribute user_data if set */
230 if (attr_data && attr_data != attr->user_data) {
231 return BT_GATT_ITER_CONTINUE;
232 }
233
234 *num_matches -= 1;
235
236 result = func(attr, handle, user_data);
237
238 if (!*num_matches) {
239 return BT_GATT_ITER_STOP;
240 }
241
242 return result;
243 }
244
245 /* Exact copy of subsys/bluetooth/host/gatt.c:foreach_attr_type_dyndb() */
foreach_attr_type_dyndb(uint16_t start_handle,uint16_t end_handle,const struct bt_uuid * uuid,const void * attr_data,uint16_t num_matches,bt_gatt_attr_func_t func,void * user_data)246 static void foreach_attr_type_dyndb(uint16_t start_handle, uint16_t end_handle,
247 const struct bt_uuid *uuid, const void *attr_data,
248 uint16_t num_matches, bt_gatt_attr_func_t func, void *user_data)
249 {
250 size_t i;
251 struct bt_gatt_service *svc;
252
253 SYS_SLIST_FOR_EACH_CONTAINER(&db, svc, node) {
254 struct bt_gatt_service *next;
255
256 next = SYS_SLIST_PEEK_NEXT_CONTAINER(svc, node);
257 if (next) {
258 /* Skip ahead if start is not within service handles */
259 if (next->attrs[0].handle <= start_handle) {
260 continue;
261 }
262 }
263
264 for (i = 0; i < svc->attr_count; i++) {
265 struct bt_gatt_attr *attr = &svc->attrs[i];
266
267 if (gatt_foreach_iter(attr, attr->handle, start_handle, end_handle, uuid,
268 attr_data, &num_matches, func,
269 user_data) == BT_GATT_ITER_STOP) {
270 return;
271 }
272 }
273 }
274 }
275
276 /* Exact copy of subsys/bluetooth/host/gatt.c:bt_gatt_foreach_attr_type() */
bt_gatt_foreach_attr_type(uint16_t start_handle,uint16_t end_handle,const struct bt_uuid * uuid,const void * attr_data,uint16_t num_matches,bt_gatt_attr_func_t func,void * user_data)277 void bt_gatt_foreach_attr_type(uint16_t start_handle, uint16_t end_handle,
278 const struct bt_uuid *uuid,
279 const void *attr_data, uint16_t num_matches,
280 bt_gatt_attr_func_t func, void *user_data)
281 {
282 size_t i;
283
284 LOG_DBG("bt_gatt_foreach_attr_type");
285
286 if (!num_matches) {
287 num_matches = UINT16_MAX;
288 }
289
290 if (start_handle <= last_static_handle) {
291 uint16_t handle = 1;
292
293 STRUCT_SECTION_FOREACH(bt_gatt_service_static, static_svc) {
294 /* Skip ahead if start is not within service handles */
295 if (handle + static_svc->attr_count < start_handle) {
296 handle += static_svc->attr_count;
297 continue;
298 }
299
300 for (i = 0; i < static_svc->attr_count; i++, handle++) {
301 if (gatt_foreach_iter(&static_svc->attrs[i],
302 handle, start_handle,
303 end_handle, uuid,
304 attr_data, &num_matches,
305 func, user_data) ==
306 BT_GATT_ITER_STOP) {
307 LOG_DBG("Returning after searching static DB");
308 return;
309 }
310 }
311 }
312 }
313
314 LOG_DBG("foreach_attr_type_dyndb");
315 /* Iterate over dynamic db */
316 foreach_attr_type_dyndb(start_handle, end_handle, uuid, attr_data,
317 num_matches, func, user_data);
318 }
319
bt_gatt_service_init(void)320 static void bt_gatt_service_init(void)
321 {
322 last_static_handle = 0U;
323
324 STRUCT_SECTION_FOREACH(bt_gatt_service_static, svc) {
325 last_static_handle += svc->attr_count;
326 }
327 }
328
329 /* Exact copy of subsys/bluetooth/host/gatt.c:found_attr() */
found_attr(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)330 static uint8_t found_attr(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data)
331 {
332 const struct bt_gatt_attr **found = user_data;
333
334 *found = attr;
335
336 return BT_GATT_ITER_STOP;
337 }
338
339 /* Exact copy of subsys/bluetooth/host/gatt.c:find_attr() */
find_attr(uint16_t handle)340 static const struct bt_gatt_attr *find_attr(uint16_t handle)
341 {
342 const struct bt_gatt_attr *attr = NULL;
343
344 bt_gatt_foreach_attr(handle, handle, found_attr, &attr);
345
346 return attr;
347 }
348
349 /* Exact copy of subsys/bluetooth/host/gatt.c:gatt_insert() */
gatt_insert(struct bt_gatt_service * svc,uint16_t last_handle)350 static void gatt_insert(struct bt_gatt_service *svc, uint16_t last_handle)
351 {
352 struct bt_gatt_service *tmp, *prev = NULL;
353
354 if (last_handle == 0 || svc->attrs[0].handle > last_handle) {
355 sys_slist_append(&db, &svc->node);
356 return;
357 }
358
359 /* DB shall always have its service in ascending order */
360 SYS_SLIST_FOR_EACH_CONTAINER(&db, tmp, node) {
361 if (tmp->attrs[0].handle > svc->attrs[0].handle) {
362 if (prev) {
363 sys_slist_insert(&db, &prev->node, &svc->node);
364 } else {
365 sys_slist_prepend(&db, &svc->node);
366 }
367 return;
368 }
369
370 prev = tmp;
371 }
372 }
373
374 /* Exact copy of subsys/bluetooth/host/gatt.c:gatt_register() */
gatt_register(struct bt_gatt_service * svc)375 static int gatt_register(struct bt_gatt_service *svc)
376 {
377 struct bt_gatt_service *last;
378 uint16_t handle, last_handle;
379 struct bt_gatt_attr *attrs = svc->attrs;
380 uint16_t count = svc->attr_count;
381
382 if (sys_slist_is_empty(&db)) {
383 handle = last_static_handle;
384 last_handle = 0;
385 goto populate;
386 }
387
388 last = SYS_SLIST_PEEK_TAIL_CONTAINER(&db, last, node);
389 handle = last->attrs[last->attr_count - 1].handle;
390 last_handle = handle;
391
392 populate:
393 /* Populate the handles and append them to the list */
394 for (; attrs && count; attrs++, count--) {
395 if (!attrs->handle) {
396 /* Allocate handle if not set already */
397 attrs->handle = ++handle;
398 } else if (attrs->handle > handle) {
399 /* Use existing handle if valid */
400 handle = attrs->handle;
401 } else if (find_attr(attrs->handle)) {
402 /* Service has conflicting handles */
403 LOG_ERR("Mock: Unable to register handle 0x%04x", attrs->handle);
404 return -EINVAL;
405 }
406
407 LOG_DBG("attr %p handle 0x%04x uuid %s perm 0x%02x", attrs, attrs->handle,
408 bt_uuid_str(attrs->uuid), attrs->perm);
409 }
410
411 gatt_insert(svc, last_handle);
412
413 return 0;
414 }
415
gatt_unregister(struct bt_gatt_service * svc)416 static int gatt_unregister(struct bt_gatt_service *svc)
417 {
418 if (!sys_slist_find_and_remove(&db, &svc->node)) {
419 return -ENOENT;
420 }
421
422 return 0;
423 }
424
bt_gatt_service_register(struct bt_gatt_service * svc)425 int bt_gatt_service_register(struct bt_gatt_service *svc)
426 {
427 int err;
428
429 __ASSERT(svc, "invalid parameters\n");
430 __ASSERT(svc->attrs, "invalid parameters\n");
431 __ASSERT(svc->attr_count, "invalid parameters\n");
432
433 /* Init GATT core services */
434 bt_gatt_service_init();
435
436 /* Do no allow to register mandatory services twice */
437 if (!bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GAP) ||
438 !bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GATT)) {
439 return -EALREADY;
440 }
441
442 err = gatt_register(svc);
443 if (err < 0) {
444 return err;
445 }
446
447 return 0;
448 }
449
bt_gatt_service_unregister(struct bt_gatt_service * svc)450 int bt_gatt_service_unregister(struct bt_gatt_service *svc)
451 {
452 int err;
453
454 __ASSERT(svc, "invalid parameters\n");
455
456 err = gatt_unregister(svc);
457 if (err) {
458 return err;
459 }
460
461 return 0;
462 }
463
464 /* Exact copy of subsys/bluetooth/host/gatt.c:bt_gatt_attr_read() */
bt_gatt_attr_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t buf_len,uint16_t offset,const void * value,uint16_t value_len)465 ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
466 void *buf, uint16_t buf_len, uint16_t offset,
467 const void *value, uint16_t value_len)
468 {
469 uint16_t len;
470
471 if (offset > value_len) {
472 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
473 }
474
475 len = MIN(buf_len, value_len - offset);
476
477 LOG_DBG("handle 0x%04x offset %u length %u", attr->handle, offset, len);
478
479 memcpy(buf, (uint8_t *)value + offset, len);
480
481 return len;
482 }
483
bt_gatt_discover(struct bt_conn * conn,struct bt_gatt_discover_params * params)484 int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *params)
485 {
486 zassert_not_null(conn, "'%s()' was called with incorrect '%s' value", __func__, "conn");
487 zassert_not_null(params, "'%s()' was called with incorrect '%s' value", __func__, "params");
488 zassert_not_null(params->func, "'%s()' was called with incorrect '%s' value", __func__,
489 "params->func");
490 zassert_between_inclusive(
491 params->start_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE,
492 "'%s()' was called with incorrect '%s' value", __func__, "params->start_handle");
493 zassert_between_inclusive(
494 params->end_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE,
495 "'%s()' was called with incorrect '%s' value", __func__, "params->end_handle");
496 zassert_true(params->start_handle <= params->end_handle,
497 "'%s()' was called with incorrect '%s' value", __func__, "params->end_handle");
498
499 struct bt_gatt_service_val value;
500 struct bt_uuid_16 uuid;
501 struct bt_gatt_attr attr;
502 uint16_t start_handle;
503 uint16_t end_handle;
504
505 if (conn->info.state != BT_CONN_STATE_CONNECTED) {
506 return -ENOTCONN;
507 }
508
509 switch (params->type) {
510 case BT_GATT_DISCOVER_PRIMARY:
511 case BT_GATT_DISCOVER_SECONDARY:
512 case BT_GATT_DISCOVER_STD_CHAR_DESC:
513 case BT_GATT_DISCOVER_INCLUDE:
514 case BT_GATT_DISCOVER_CHARACTERISTIC:
515 case BT_GATT_DISCOVER_DESCRIPTOR:
516 case BT_GATT_DISCOVER_ATTRIBUTE:
517 break;
518 default:
519 LOG_ERR("Invalid discovery type: %u", params->type);
520 return -EINVAL;
521 }
522
523 uuid.uuid.type = BT_UUID_TYPE_16;
524 uuid.val = params->type;
525 start_handle = params->start_handle;
526 end_handle = params->end_handle;
527 value.end_handle = end_handle;
528 value.uuid = params->uuid;
529
530 attr = (struct bt_gatt_attr){
531 .uuid = &uuid.uuid,
532 .user_data = &value,
533 .handle = start_handle,
534 };
535
536 params->func(conn, &attr, params);
537
538 return 0;
539 }
540
bt_gatt_get_mtu(struct bt_conn * conn)541 uint16_t bt_gatt_get_mtu(struct bt_conn *conn)
542 {
543 return 64;
544 }
545
bt_gatt_is_subscribed(struct bt_conn * conn,const struct bt_gatt_attr * attr,uint16_t ccc_type)546 bool bt_gatt_is_subscribed(struct bt_conn *conn,
547 const struct bt_gatt_attr *attr, uint16_t ccc_type)
548 {
549 return mock_bt_gatt_is_subscribed(conn, attr, ccc_type);
550 }
551
bt_gatt_attr_get_handle(const struct bt_gatt_attr * attr)552 uint16_t bt_gatt_attr_get_handle(const struct bt_gatt_attr *attr)
553 {
554 uint16_t handle = 1;
555
556 if (!attr) {
557 return 0;
558 }
559
560 if (attr->handle) {
561 return attr->handle;
562 }
563
564 STRUCT_SECTION_FOREACH(bt_gatt_service_static, static_svc) {
565 /* Skip ahead if start is not within service attributes array */
566 if ((attr < &static_svc->attrs[0]) ||
567 (attr > &static_svc->attrs[static_svc->attr_count - 1])) {
568 handle += static_svc->attr_count;
569 continue;
570 }
571
572 for (size_t i = 0; i < static_svc->attr_count; i++, handle++) {
573 if (attr == &static_svc->attrs[i]) {
574 return handle;
575 }
576 }
577 }
578
579 return 0;
580 }
581
find_next(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)582 static uint8_t find_next(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data)
583 {
584 struct bt_gatt_attr **next = user_data;
585
586 *next = (struct bt_gatt_attr *)attr;
587
588 return BT_GATT_ITER_STOP;
589 }
590
bt_gatt_find_by_uuid(const struct bt_gatt_attr * attr,uint16_t attr_count,const struct bt_uuid * uuid)591 struct bt_gatt_attr *bt_gatt_find_by_uuid(const struct bt_gatt_attr *attr, uint16_t attr_count,
592 const struct bt_uuid *uuid)
593 {
594 struct bt_gatt_attr *found = NULL;
595 uint16_t start_handle = bt_gatt_attr_get_handle(attr);
596 uint16_t end_handle = start_handle && attr_count
597 ? MIN(start_handle + attr_count, BT_ATT_LAST_ATTRIBUTE_HANDLE)
598 : BT_ATT_LAST_ATTRIBUTE_HANDLE;
599
600 if (attr != NULL && start_handle == 0U) {
601 /* If start_handle is 0 then `attr` is not in our database, and should not be used
602 * as a starting point for the search
603 */
604 LOG_DBG("Could not find handle of attr %p", attr);
605 return NULL;
606 }
607
608 bt_gatt_foreach_attr_type(start_handle, end_handle, uuid, NULL, 1, find_next, &found);
609
610 return found;
611 }
612