1 /**
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 */
7
8 #include <stdint.h>
9
10 #include <zephyr/kernel.h>
11
12 #include <zephyr/bluetooth/addr.h>
13 #include <zephyr/bluetooth/conn.h>
14 #include <zephyr/bluetooth/gatt.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/settings/settings.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18
19 #include "babblekit/testcase.h"
20 #include "babblekit/flags.h"
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(test_central, LOG_LEVEL_DBG);
24
25 #include "bs_bt_utils.h"
26
27 DEFINE_FLAG_STATIC(flag_discovered);
28 DEFINE_FLAG_STATIC(flag_subscribed);
29 DEFINE_FLAG_STATIC(flag_indicated);
30
31 enum GATT_HANDLES {
32 SC,
33 CCC,
34 NUM_HANDLES,
35 };
36
37 static uint16_t gatt_handles[NUM_HANDLES] = {0};
38
39 static struct bt_gatt_subscribe_params subscribe_params;
40
sc_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)41 static void sc_subscribed(struct bt_conn *conn,
42 uint8_t err,
43 struct bt_gatt_subscribe_params *params)
44 {
45 LOG_DBG("subscribed");
46 SET_FLAG(flag_subscribed);
47 }
48
sc_indicated(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)49 static uint8_t sc_indicated(struct bt_conn *conn,
50 struct bt_gatt_subscribe_params *params,
51 const void *data,
52 uint16_t length)
53 {
54 LOG_DBG("indication received");
55
56 SET_FLAG(flag_indicated);
57
58 return BT_GATT_ITER_CONTINUE;
59 }
60
subscribe(void)61 static void subscribe(void)
62 {
63 int err;
64
65 subscribe_params.ccc_handle = gatt_handles[CCC];
66 subscribe_params.value_handle = gatt_handles[SC];
67 subscribe_params.value = BT_GATT_CCC_INDICATE;
68 subscribe_params.subscribe = sc_subscribed;
69 subscribe_params.notify = sc_indicated;
70
71 err = bt_gatt_subscribe(get_g_conn(), &subscribe_params);
72 TEST_ASSERT(!err, "bt_gatt_subscribe failed (%d)", err);
73
74 WAIT_FOR_FLAG(flag_subscribed);
75 }
76
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)77 static uint8_t discover_func(struct bt_conn *conn,
78 const struct bt_gatt_attr *attr,
79 struct bt_gatt_discover_params *params)
80 {
81 if (attr == NULL) {
82 for (size_t i = 0U; i < ARRAY_SIZE(gatt_handles); i++) {
83 LOG_DBG("handle[%d] = 0x%x", i, gatt_handles[i]);
84 TEST_ASSERT(gatt_handles[i] != 0, "did not find all handles");
85 }
86
87 (void)memset(params, 0, sizeof(*params));
88 SET_FLAG(flag_discovered);
89
90 return BT_GATT_ITER_STOP;
91 }
92
93 if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
94 const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
95 static const struct bt_uuid_16 ccc_uuid = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL);
96
97 if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) {
98 int err;
99
100 LOG_DBG("found sc");
101 gatt_handles[SC] = chrc->value_handle;
102
103 params->uuid = &ccc_uuid.uuid;
104 params->start_handle = attr->handle + 2;
105 params->type = BT_GATT_DISCOVER_DESCRIPTOR;
106
107 err = bt_gatt_discover(conn, params);
108 TEST_ASSERT(!err, "bt_gatt_discover failed (%d)", err);
109
110 return BT_GATT_ITER_STOP;
111 }
112
113 } else if (params->type == BT_GATT_DISCOVER_DESCRIPTOR &&
114 bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC) == 0) {
115 LOG_DBG("found ccc");
116 gatt_handles[CCC] = attr->handle;
117 SET_FLAG(flag_discovered);
118
119 return BT_GATT_ITER_STOP;
120 }
121
122 return BT_GATT_ITER_CONTINUE;
123 }
124
gatt_discover(void)125 static void gatt_discover(void)
126 {
127 int err;
128 static struct bt_gatt_discover_params discover_params;
129
130 discover_params.uuid = NULL;
131 discover_params.func = discover_func;
132 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
133 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
134 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
135
136 err = bt_gatt_discover(get_g_conn(), &discover_params);
137 TEST_ASSERT(!err, "bt_gatt_discover failed (%d)", err);
138
139 WAIT_FOR_FLAG(flag_discovered);
140
141 LOG_DBG("sc handle: %d", gatt_handles[SC]);
142 LOG_DBG("ccc handle: %d", gatt_handles[CCC]);
143 }
144
central(void)145 void central(void)
146 {
147 /*
148 * test goal: check that service changed indication is sent on
149 * reconnection when the server's GATT database has been updated since
150 * last connection
151 *
152 * the central will connect, bond with the peripheral and then
153 * disconnect after doing that, the central will try to connect again,
154 * this time it will not elevate the security
155 *
156 * to pass the test, the central will wait to receive the service
157 * changed indication
158 */
159
160 int err;
161 struct bt_conn_auth_info_cb bt_conn_auth_info_cb = {
162 .pairing_failed = pairing_failed,
163 .pairing_complete = pairing_complete,
164 };
165
166 err = bt_enable(NULL);
167 TEST_ASSERT(!err, "bt_enable failed (%d)", err);
168
169 err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
170 TEST_ASSERT(!err, "bt_conn_auth_info_cb_register failed.");
171
172 err = settings_load();
173 TEST_ASSERT(!err, "settings_load failed (%d)", err);
174
175 scan_connect_to_first_result();
176 wait_connected();
177
178 set_security(BT_SECURITY_L2);
179
180 TAKE_FLAG(flag_pairing_complete);
181 TAKE_FLAG(flag_bonded);
182
183 /* subscribe to the service changed indication */
184 gatt_discover();
185 subscribe();
186
187 disconnect();
188 wait_disconnected();
189 clear_g_conn();
190
191 scan_connect_to_first_result();
192 wait_connected();
193
194 /* wait for service change indication */
195 WAIT_FOR_FLAG(flag_indicated);
196
197 TEST_PASS("PASS");
198 }
199