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