1 /* Copyright (c) 2023 Nordic Semiconductor ASA
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #include <stdint.h>
6
7 #include <zephyr/kernel.h>
8
9 #include <zephyr/bluetooth/addr.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/gatt.h>
12 #include <zephyr/bluetooth/uuid.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14
15 #include <zephyr/logging/log.h>
16
17 #include <zephyr/settings/settings.h>
18
19 #include "common.h"
20 #include "settings.h"
21
22 #include "argparse.h"
23 #include "bs_pc_backchannel.h"
24
25 #define GOOD_CLIENT_CHAN 0
26 #define BAD_CLIENT_CHAN 1
27
28 CREATE_FLAG(connected_flag);
29 CREATE_FLAG(disconnected_flag);
30 CREATE_FLAG(security_updated_flag);
31
32 CREATE_FLAG(ccc_cfg_changed_flag);
33
34 static const struct bt_uuid_128 dummy_service = BT_UUID_INIT_128(DUMMY_SERVICE_TYPE);
35
36 static const struct bt_uuid_128 notify_characteristic_uuid =
37 BT_UUID_INIT_128(DUMMY_SERVICE_NOTIFY_TYPE);
38
39 static struct bt_conn *default_conn;
40
41 static struct bt_conn_cb peripheral_cb;
42
ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)43 static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
44 {
45 ARG_UNUSED(attr);
46
47 bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
48
49 LOG_INF("CCC Update: notification %s", notif_enabled ? "enabled" : "disabled");
50
51 SET_FLAG(ccc_cfg_changed_flag);
52 }
53
54 BT_GATT_SERVICE_DEFINE(dummy_svc, BT_GATT_PRIMARY_SERVICE(&dummy_service),
55 BT_GATT_CHARACTERISTIC(¬ify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
56 BT_GATT_PERM_NONE, NULL, NULL, NULL),
57 BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
58
create_adv(struct bt_le_ext_adv ** adv)59 static void create_adv(struct bt_le_ext_adv **adv)
60 {
61 int err;
62 struct bt_le_adv_param params;
63
64 memset(¶ms, 0, sizeof(struct bt_le_adv_param));
65
66 params.options |= BT_LE_ADV_OPT_CONN;
67
68 params.id = BT_ID_DEFAULT;
69 params.sid = 0;
70 params.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
71 params.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
72
73 err = bt_le_ext_adv_create(¶ms, NULL, adv);
74 if (err) {
75 FAIL("Failed to create advertiser (%d)\n", err);
76 }
77 }
78
start_adv(struct bt_le_ext_adv * adv)79 static void start_adv(struct bt_le_ext_adv *adv)
80 {
81 int err;
82 int32_t timeout = 0;
83 int8_t num_events = 0;
84
85 struct bt_le_ext_adv_start_param start_params;
86
87 start_params.timeout = timeout;
88 start_params.num_events = num_events;
89
90 err = bt_le_ext_adv_start(adv, &start_params);
91 if (err) {
92 FAIL("Failed to start advertiser (err %d)\n", err);
93 }
94
95 LOG_DBG("Advertiser started");
96 }
97
stop_adv(struct bt_le_ext_adv * adv)98 static void stop_adv(struct bt_le_ext_adv *adv)
99 {
100 int err;
101
102 err = bt_le_ext_adv_stop(adv);
103 if (err) {
104 FAIL("Failed to stop advertiser (err %d)\n", err);
105 }
106 }
107
connected(struct bt_conn * conn,uint8_t err)108 static void connected(struct bt_conn *conn, uint8_t err)
109 {
110 char addr_str[BT_ADDR_LE_STR_LEN];
111
112 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
113
114 if (err) {
115 FAIL("Failed to connect to %s (err %d)\n", addr_str, err);
116 }
117
118 LOG_DBG("Connected: %s", addr_str);
119
120 default_conn = bt_conn_ref(conn);
121
122 SET_FLAG(connected_flag);
123 UNSET_FLAG(disconnected_flag);
124 }
125
disconnected(struct bt_conn * conn,uint8_t reason)126 static void disconnected(struct bt_conn *conn, uint8_t reason)
127 {
128 char addr_str[BT_ADDR_LE_STR_LEN];
129
130 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
131
132 LOG_DBG("Disconnected: %s (reason 0x%02x)", addr_str, reason);
133
134 bt_conn_unref(conn);
135 default_conn = NULL;
136
137 SET_FLAG(disconnected_flag);
138 UNSET_FLAG(connected_flag);
139 }
140
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)141 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
142 {
143 char addr_str[BT_ADDR_LE_STR_LEN];
144
145 bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
146
147 if (!err) {
148 LOG_DBG("Security changed: %s level %u", addr_str, level);
149 SET_FLAG(security_updated_flag);
150 } else {
151 LOG_DBG("Security failed: %s level %u err %d", addr_str, level, err);
152 }
153 }
154
is_peer_subscribed(struct bt_conn * conn)155 static bool is_peer_subscribed(struct bt_conn *conn)
156 {
157 struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_DUMMY_SERVICE_NOTIFY);
158 bool is_subscribed = bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY);
159
160 return is_subscribed;
161 }
162
163 /* Test steps */
164
connect_pair_check_subscribtion(struct bt_le_ext_adv * adv)165 static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
166 {
167 start_adv(adv);
168
169 WAIT_FOR_FLAG(connected_flag);
170
171 WAIT_FOR_FLAG(security_updated_flag);
172 UNSET_FLAG(security_updated_flag);
173
174 /* wait for confirmation of subscribtion from good client */
175 backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
176
177 /* check that subscribtion request did not fail */
178 if (!is_peer_subscribed(default_conn)) {
179 FAIL("Good client did not subscribed\n");
180 }
181
182 stop_adv(adv);
183
184 /* confirm to good client that the subscribtion has been well registered */
185 backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
186 }
187
connect_wait_unsubscribtion(struct bt_le_ext_adv * adv)188 static void connect_wait_unsubscribtion(struct bt_le_ext_adv *adv)
189 {
190 UNSET_FLAG(ccc_cfg_changed_flag);
191
192 start_adv(adv);
193
194 WAIT_FOR_FLAG(connected_flag);
195
196 stop_adv(adv);
197
198 /* check that subscribtion is restored for bad client */
199 if (!is_peer_subscribed(default_conn)) {
200 FAIL("Subscribtion has not been restored for bad client\n");
201 }
202
203 /* confirm to bad client that the subscribtion had not been restored */
204 backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
205 /* wait for confirmation that bad client requested unsubscribtion */
206 backchannel_sync_wait(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
207
208 /* check that unsubscribtion request didn't fail */
209 if (!GET_FLAG(ccc_cfg_changed_flag)) {
210 FAIL("Bad client didn't manage to update CCC config\n");
211 }
212
213 /* confirm to bad client that unsubscribtion request has been well registered */
214 backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
215 }
216
connect_restore_sec_check_subscribtion(struct bt_le_ext_adv * adv)217 static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
218 {
219 start_adv(adv);
220
221 WAIT_FOR_FLAG(connected_flag);
222
223 WAIT_FOR_FLAG(security_updated_flag);
224 UNSET_FLAG(security_updated_flag);
225
226 /* wait for good client end of security update */
227 backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
228
229 /* check that subscribtion hasn't been restored */
230 if (is_peer_subscribed(default_conn)) {
231 FAIL("Good client is subscribed\n");
232 }
233
234 /* confirm to good client that the subscribtion has been well restored */
235 backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
236 /* wait for confimation of unsubscribtion from good client */
237 backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
238
239 /* check that unsubscribtion request from good client has been registered */
240 if (is_peer_subscribed(default_conn)) {
241 FAIL("Good client did not unsubscribe\n");
242 }
243 }
244
245 /* Util functions */
246
peripheral_backchannel_init(void)247 void peripheral_backchannel_init(void)
248 {
249 uint device_number = get_device_nbr();
250 uint channel_numbers[2] = {
251 0,
252 0,
253 };
254 uint device_numbers[2] = {
255 GOOD_CLIENT_ID,
256 BAD_CLIENT_ID,
257 };
258 uint num_ch = 2;
259 uint *ch;
260
261 LOG_DBG("Opening back channels for device %d", device_number);
262 ch = bs_open_back_channel(device_number, device_numbers, channel_numbers, num_ch);
263 if (!ch) {
264 FAIL("Unable to open backchannel\n");
265 }
266 }
267
check_ccc_handle(void)268 static void check_ccc_handle(void)
269 {
270 struct bt_gatt_attr *service_notify_attr =
271 bt_gatt_find_by_uuid(NULL, 0, ¬ify_characteristic_uuid.uuid);
272
273 struct bt_gatt_attr attr = {
274 .uuid = BT_UUID_GATT_CHRC,
275 .user_data = &(struct bt_gatt_chrc){
276 .value_handle = bt_gatt_attr_get_handle(service_notify_attr)}};
277
278 struct bt_gatt_attr *ccc_attr = bt_gatt_find_by_uuid(&attr, 0, BT_UUID_GATT_CCC);
279 uint16_t actual_ccc_handle = bt_gatt_attr_get_handle(ccc_attr);
280
281 __ASSERT(actual_ccc_handle == CCC_HANDLE,
282 "Please update the CCC_HANDLE define (actual_ccc_handle=%d)", actual_ccc_handle);
283 }
284
285 /* Main function */
286
run_peripheral(void)287 void run_peripheral(void)
288 {
289 /*
290 * test goal: demonstrate the expected behavior of the GATT server when
291 * a non-bonded peer try to unsubscribe from a previously subscription
292 * done in a bonded context
293 *
294 * test pass if the bad client manage to unsubscribe
295 */
296
297 int err;
298 struct bt_le_ext_adv *adv = NULL;
299
300 peripheral_cb.connected = connected;
301 peripheral_cb.disconnected = disconnected;
302 peripheral_cb.security_changed = security_changed;
303
304 peripheral_backchannel_init();
305
306 err = bt_enable(NULL);
307 if (err) {
308 FAIL("Bluetooth init failed (err %d)\n", err);
309 }
310
311 LOG_DBG("Bluetooth initialized");
312
313 check_ccc_handle();
314
315 bt_conn_cb_register(&peripheral_cb);
316
317 err = settings_load();
318 if (err) {
319 FAIL("Settings load failed (err %d)\n", err);
320 }
321
322 err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
323 if (err) {
324 FAIL("Unpairing failed (err %d)\n", err);
325 }
326
327 create_adv(&adv);
328
329 connect_pair_check_subscribtion(adv);
330 WAIT_FOR_FLAG(disconnected_flag);
331
332 connect_wait_unsubscribtion(adv);
333 WAIT_FOR_FLAG(disconnected_flag);
334
335 connect_restore_sec_check_subscribtion(adv);
336 WAIT_FOR_FLAG(disconnected_flag);
337
338 PASS("Peripheral test passed\n");
339 }
340