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