| /* |
| * Copyright (c) 2023 Codecoup |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stdlib.h> |
| #include <zephyr/types.h> |
| #include <zephyr/bluetooth/gatt.h> |
| #include <zephyr/sys/iterable_sections.h> |
| |
| #include "gatt.h" |
| |
| /* List of fakes used by this unit tester */ |
| #define FFF_FAKES_LIST(FAKE) \ |
| FAKE(mock_bt_gatt_notify_cb) \ |
| FAKE(bt_gatt_attr_read) \ |
| |
| DEFINE_FAKE_VALUE_FUNC(int, mock_bt_gatt_notify_cb, struct bt_conn *, |
| struct bt_gatt_notify_params *); |
| DEFINE_FAKE_VALUE_FUNC(ssize_t, bt_gatt_attr_read, struct bt_conn *, const struct bt_gatt_attr *, |
| void *, uint16_t, uint16_t, const void *, uint16_t); |
| |
| ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, |
| uint16_t len, uint16_t offset) |
| { |
| zassert_unreachable("Unexpected call to '%s()' occurred", __func__); |
| return 0; |
| } |
| |
| ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, |
| uint16_t len, uint16_t offset) |
| { |
| zassert_unreachable("Unexpected call to '%s()' occurred", __func__); |
| return 0; |
| } |
| |
| ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, |
| uint16_t len, uint16_t offset) |
| { |
| zassert_unreachable("Unexpected call to '%s()' occurred", __func__); |
| return 0; |
| } |
| |
| ssize_t 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) |
| { |
| zassert_unreachable("Unexpected call to '%s()' occurred", __func__); |
| return 0; |
| } |
| |
| void mock_bt_gatt_init(void) |
| { |
| FFF_FAKES_LIST(RESET_FAKE); |
| } |
| |
| static void notify_params_deep_copy_destroy(void) |
| { |
| struct bt_gatt_notify_params *copy; |
| |
| for (unsigned int i = 0; i < mock_bt_gatt_notify_cb_fake.call_count; i++) { |
| copy = mock_bt_gatt_notify_cb_fake.arg1_history[i]; |
| |
| /* Free UUID deep copy */ |
| if (copy->uuid) { |
| free((void *)copy->uuid); |
| } |
| |
| free(copy); |
| } |
| } |
| |
| void mock_bt_gatt_cleanup(void) |
| { |
| notify_params_deep_copy_destroy(); |
| } |
| |
| static struct bt_uuid *uuid_deep_copy(const struct bt_uuid *uuid) |
| { |
| struct bt_uuid *copy; |
| |
| switch (uuid->type) { |
| case BT_UUID_TYPE_16: |
| copy = malloc(sizeof(struct bt_uuid_16)); |
| zassert_not_null(copy); |
| memcpy(copy, uuid, sizeof(struct bt_uuid_16)); |
| break; |
| case BT_UUID_TYPE_32: |
| copy = malloc(sizeof(struct bt_uuid_32)); |
| zassert_not_null(copy); |
| memcpy(copy, uuid, sizeof(struct bt_uuid_32)); |
| break; |
| case BT_UUID_TYPE_128: |
| copy = malloc(sizeof(struct bt_uuid_128)); |
| zassert_not_null(copy); |
| memcpy(copy, uuid, sizeof(struct bt_uuid_128)); |
| break; |
| default: |
| zassert_unreachable("Unexpected uuid->type 0x%02x", uuid->type); |
| } |
| |
| return copy; |
| } |
| |
| static struct bt_gatt_notify_params *notify_params_deep_copy(struct bt_gatt_notify_params *params) |
| { |
| struct bt_gatt_notify_params *copy; |
| |
| copy = malloc(sizeof(*params)); |
| zassert_not_null(copy); |
| |
| memcpy(copy, params, sizeof(*params)); |
| |
| if (params->uuid != NULL) { |
| copy->uuid = uuid_deep_copy(params->uuid); |
| } |
| |
| return copy; |
| } |
| |
| int bt_gatt_notify_cb(struct bt_conn *conn, struct bt_gatt_notify_params *params) |
| { |
| zassert_not_null(params, "'%s()' was called with incorrect '%s' value", __func__, "params"); |
| |
| /* Either params->uuid, params->attr, or both has to be provided */ |
| zassert_true(params->uuid != NULL || params->attr != NULL, |
| "'%s()' was called with incorrect '%s' value", __func__, |
| "params->uuid or params->attr"); |
| |
| return mock_bt_gatt_notify_cb(conn, notify_params_deep_copy(params)); |
| } |
| |
| void bt_gatt_notify_cb_reset(void) |
| { |
| notify_params_deep_copy_destroy(); |
| |
| RESET_FAKE(mock_bt_gatt_notify_cb); |
| } |
| |
| #define foreach_attr_type_dyndb(...) |
| #define last_static_handle BT_ATT_LAST_ATTRIBUTE_HANDLE |
| |
| /* Exact copy of subsys/bluetooth/host/gatt.c:gatt_foreach_iter() */ |
| static uint8_t 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) |
| { |
| uint8_t result; |
| |
| /* Stop if over the requested range */ |
| if (handle > end_handle) { |
| return BT_GATT_ITER_STOP; |
| } |
| |
| /* Check if attribute handle is within range */ |
| if (handle < start_handle) { |
| return BT_GATT_ITER_CONTINUE; |
| } |
| |
| /* Match attribute UUID if set */ |
| if (uuid && bt_uuid_cmp(uuid, attr->uuid)) { |
| return BT_GATT_ITER_CONTINUE; |
| } |
| |
| /* Match attribute user_data if set */ |
| if (attr_data && attr_data != attr->user_data) { |
| return BT_GATT_ITER_CONTINUE; |
| } |
| |
| *num_matches -= 1; |
| |
| result = func(attr, handle, user_data); |
| |
| if (!*num_matches) { |
| return BT_GATT_ITER_STOP; |
| } |
| |
| return result; |
| } |
| |
| /* Exact copy of subsys/bluetooth/host/gatt.c:bt_gatt_foreach_attr_type() */ |
| void 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) |
| { |
| size_t i; |
| |
| if (!num_matches) { |
| num_matches = UINT16_MAX; |
| } |
| |
| if (start_handle <= last_static_handle) { |
| uint16_t handle = 1; |
| |
| STRUCT_SECTION_FOREACH(bt_gatt_service_static, static_svc) { |
| /* Skip ahead if start is not within service handles */ |
| if (handle + static_svc->attr_count < start_handle) { |
| handle += static_svc->attr_count; |
| continue; |
| } |
| |
| for (i = 0; i < static_svc->attr_count; i++, handle++) { |
| if (gatt_foreach_iter(&static_svc->attrs[i], |
| handle, start_handle, |
| end_handle, uuid, |
| attr_data, &num_matches, |
| func, user_data) == |
| BT_GATT_ITER_STOP) { |
| return; |
| } |
| } |
| } |
| } |
| |
| /* Iterate over dynamic db */ |
| foreach_attr_type_dyndb(start_handle, end_handle, uuid, attr_data, |
| num_matches, func, user_data); |
| } |
| |
| uint16_t bt_gatt_get_mtu(struct bt_conn *conn) |
| { |
| return 64; |
| } |