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