1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/bluetooth.h>
8 #include <zephyr/bluetooth/gatt.h>
9 
10 #include "common.h"
11 
12 CREATE_FLAG(flag_is_connected);
13 CREATE_FLAG(flag_is_encrypted);
14 CREATE_FLAG(flag_discover_complete);
15 CREATE_FLAG(flag_short_subscribed);
16 CREATE_FLAG(flag_long_subscribed);
17 
18 static struct bt_conn *g_conn;
19 static uint16_t chrc_handle;
20 static uint16_t long_chrc_handle;
21 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
22 
connected(struct bt_conn * conn,uint8_t err)23 static void connected(struct bt_conn *conn, uint8_t err)
24 {
25 	char addr[BT_ADDR_LE_STR_LEN];
26 
27 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
28 
29 	if (err != 0) {
30 		FAIL("Failed to connect to %s (%u)\n", addr, err);
31 		return;
32 	}
33 
34 	printk("Connected to %s\n", addr);
35 
36 	SET_FLAG(flag_is_connected);
37 }
38 
disconnected(struct bt_conn * conn,uint8_t reason)39 static void disconnected(struct bt_conn *conn, uint8_t reason)
40 {
41 	char addr[BT_ADDR_LE_STR_LEN];
42 
43 	if (conn != g_conn) {
44 		return;
45 	}
46 
47 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
48 
49 	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
50 
51 	bt_conn_unref(g_conn);
52 
53 	g_conn = NULL;
54 	UNSET_FLAG(flag_is_connected);
55 }
56 
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)57 void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
58 {
59 	if (err) {
60 		FAIL("Encryption failer (%d)\n", err);
61 	} else if (level < BT_SECURITY_L2) {
62 		FAIL("Insufficient sec level (%d)\n", level);
63 	} else {
64 		SET_FLAG(flag_is_encrypted);
65 	}
66 }
67 
68 BT_CONN_CB_DEFINE(conn_callbacks) = {
69 	.connected = connected,
70 	.disconnected = disconnected,
71 	.security_changed = security_changed,
72 };
73 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)74 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad)
75 {
76 	char addr_str[BT_ADDR_LE_STR_LEN];
77 	int err;
78 
79 	if (g_conn != NULL) {
80 		return;
81 	}
82 
83 	/* We're only interested in connectable events */
84 	if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
85 		return;
86 	}
87 
88 	bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
89 	printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
90 
91 	printk("Stopping scan\n");
92 	err = bt_le_scan_stop();
93 	if (err != 0) {
94 		FAIL("Could not stop scan: %d");
95 		return;
96 	}
97 
98 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
99 	if (err != 0) {
100 		FAIL("Could not connect to peer: %d", err);
101 	}
102 }
103 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)104 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
105 			     struct bt_gatt_discover_params *params)
106 {
107 	int err;
108 
109 	if (attr == NULL) {
110 		if (chrc_handle == 0 || long_chrc_handle == 0) {
111 			FAIL("Did not discover chrc (%x) or long_chrc (%x)", chrc_handle,
112 			     long_chrc_handle);
113 		}
114 
115 		(void)memset(params, 0, sizeof(*params));
116 
117 		SET_FLAG(flag_discover_complete);
118 
119 		return BT_GATT_ITER_STOP;
120 	}
121 
122 	printk("[ATTRIBUTE] handle %u\n", attr->handle);
123 
124 	if (params->type == BT_GATT_DISCOVER_PRIMARY &&
125 	    bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
126 		printk("Found test service\n");
127 		params->uuid = NULL;
128 		params->start_handle = attr->handle + 1;
129 		params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
130 
131 		err = bt_gatt_discover(conn, params);
132 		if (err != 0) {
133 			FAIL("Discover failed (err %d)\n", err);
134 		}
135 
136 		return BT_GATT_ITER_STOP;
137 	} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
138 		const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
139 
140 		if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
141 			printk("Found chrc\n");
142 			chrc_handle = chrc->value_handle;
143 		} else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
144 			printk("Found long_chrc\n");
145 			long_chrc_handle = chrc->value_handle;
146 		}
147 	}
148 
149 	return BT_GATT_ITER_CONTINUE;
150 }
151 
gatt_discover(enum bt_att_chan_opt opt)152 static void gatt_discover(enum bt_att_chan_opt opt)
153 {
154 	static struct bt_gatt_discover_params discover_params;
155 	int err;
156 
157 	printk("Discovering services and characteristics\n");
158 
159 	discover_params.uuid = test_svc_uuid;
160 	discover_params.func = discover_func;
161 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
162 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
163 	discover_params.type = BT_GATT_DISCOVER_PRIMARY;
164 	discover_params.chan_opt = opt;
165 
166 	err = bt_gatt_discover(g_conn, &discover_params);
167 	if (err != 0) {
168 		FAIL("Discover failed(err %d)\n", err);
169 	}
170 
171 	WAIT_FOR_FLAG(flag_discover_complete);
172 	printk("Discover complete\n");
173 }
174 
test_short_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)175 static void test_short_subscribed(struct bt_conn *conn, uint8_t err,
176 				  struct bt_gatt_subscribe_params *params)
177 {
178 	if (err) {
179 		FAIL("Subscribe failed (err %d)\n", err);
180 	}
181 
182 	SET_FLAG(flag_short_subscribed);
183 
184 	if (!params) {
185 		printk("params NULL\n");
186 		return;
187 	}
188 
189 	if (params->value_handle == chrc_handle) {
190 		printk("Subscribed to short characteristic\n");
191 	} else {
192 		FAIL("Unknown handle %d\n", params->value_handle);
193 	}
194 }
195 
test_long_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)196 static void test_long_subscribed(struct bt_conn *conn, uint8_t err,
197 				 struct bt_gatt_subscribe_params *params)
198 {
199 	if (err) {
200 		FAIL("Subscribe failed (err %d)\n", err);
201 	}
202 
203 	SET_FLAG(flag_long_subscribed);
204 
205 	if (!params) {
206 		printk("params NULL\n");
207 		return;
208 	}
209 
210 	if (params->value_handle == long_chrc_handle) {
211 		printk("Subscribed to long characteristic\n");
212 	} else {
213 		FAIL("Unknown handle %d\n", params->value_handle);
214 	}
215 }
216 
217 static volatile size_t num_notifications;
test_notify(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)218 uint8_t test_notify(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data,
219 		    uint16_t length)
220 {
221 	printk("Received notification #%u with length %d\n", num_notifications++, length);
222 
223 	return BT_GATT_ITER_CONTINUE;
224 }
225 
226 static struct bt_gatt_discover_params disc_params_short;
227 static struct bt_gatt_subscribe_params sub_params_short = {
228 	.notify = test_notify,
229 	.subscribe = test_short_subscribed,
230 	.ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE,
231 	.disc_params = &disc_params_short,
232 	.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
233 	.value = BT_GATT_CCC_NOTIFY,
234 };
235 static struct bt_gatt_discover_params disc_params_long;
236 static struct bt_gatt_subscribe_params sub_params_long = {
237 	.notify = test_notify,
238 	.subscribe = test_long_subscribed,
239 	.ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE,
240 	.disc_params = &disc_params_long,
241 	.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
242 	.value = BT_GATT_CCC_NOTIFY,
243 };
244 
gatt_subscribe_short(enum bt_att_chan_opt opt)245 static void gatt_subscribe_short(enum bt_att_chan_opt opt)
246 {
247 	int err;
248 
249 	sub_params_short.value_handle = chrc_handle;
250 	sub_params_short.chan_opt = opt;
251 	err = bt_gatt_subscribe(g_conn, &sub_params_short);
252 	if (err < 0) {
253 		FAIL("Failed to subscribe\n");
254 	} else {
255 		printk("Subscribe request sent\n");
256 	}
257 }
258 
gatt_unsubscribe_short(enum bt_att_chan_opt opt)259 static void gatt_unsubscribe_short(enum bt_att_chan_opt opt)
260 {
261 	int err;
262 
263 	sub_params_short.value_handle = chrc_handle;
264 	sub_params_short.chan_opt = opt;
265 	err = bt_gatt_unsubscribe(g_conn, &sub_params_short);
266 	if (err < 0) {
267 		FAIL("Failed to unsubscribe\n");
268 	} else {
269 		printk("Unsubscribe request sent\n");
270 	}
271 }
272 
gatt_subscribe_long(enum bt_att_chan_opt opt)273 static void gatt_subscribe_long(enum bt_att_chan_opt opt)
274 {
275 	int err;
276 
277 	UNSET_FLAG(flag_long_subscribed);
278 	sub_params_long.value_handle = long_chrc_handle;
279 	sub_params_long.chan_opt = opt;
280 	err = bt_gatt_subscribe(g_conn, &sub_params_long);
281 	if (err < 0) {
282 		FAIL("Failed to subscribe\n");
283 	} else {
284 		printk("Subscribe request sent\n");
285 	}
286 }
287 
gatt_unsubscribe_long(enum bt_att_chan_opt opt)288 static void gatt_unsubscribe_long(enum bt_att_chan_opt opt)
289 {
290 	int err;
291 
292 	UNSET_FLAG(flag_long_subscribed);
293 	sub_params_long.value_handle = long_chrc_handle;
294 	sub_params_long.chan_opt = opt;
295 	err = bt_gatt_unsubscribe(g_conn, &sub_params_long);
296 	if (err < 0) {
297 		FAIL("Failed to unsubscribe\n");
298 	} else {
299 		printk("Unsubscribe request sent\n");
300 	}
301 }
302 
setup(void)303 static void setup(void)
304 {
305 	int err;
306 
307 	err = bt_enable(NULL);
308 	if (err != 0) {
309 		FAIL("Bluetooth discover failed (err %d)\n", err);
310 	}
311 
312 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
313 	if (err != 0) {
314 		FAIL("Scanning failed to start (err %d)\n", err);
315 	}
316 
317 	printk("Scanning successfully started\n");
318 
319 	WAIT_FOR_FLAG(flag_is_connected);
320 
321 	err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
322 	if (err) {
323 		FAIL("Starting encryption procedure failed (%d)\n", err);
324 	}
325 
326 	WAIT_FOR_FLAG(flag_is_encrypted);
327 
328 	while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
329 		k_sleep(K_MSEC(10));
330 	}
331 
332 	printk("EATT connected\n");
333 }
334 
test_main_none(void)335 static void test_main_none(void)
336 {
337 	setup();
338 
339 	gatt_discover(BT_ATT_CHAN_OPT_NONE);
340 	gatt_subscribe_short(BT_ATT_CHAN_OPT_NONE);
341 	gatt_subscribe_long(BT_ATT_CHAN_OPT_NONE);
342 	WAIT_FOR_FLAG(flag_short_subscribed);
343 	WAIT_FOR_FLAG(flag_long_subscribed);
344 	printk("Subscribed\n");
345 
346 	while (num_notifications < NOTIFICATION_COUNT) {
347 		k_sleep(K_MSEC(100));
348 	}
349 
350 	gatt_unsubscribe_short(BT_ATT_CHAN_OPT_NONE);
351 	gatt_unsubscribe_long(BT_ATT_CHAN_OPT_NONE);
352 	WAIT_FOR_FLAG(flag_short_subscribed);
353 	WAIT_FOR_FLAG(flag_long_subscribed);
354 
355 	printk("Unsubscribed\n");
356 
357 	PASS("GATT client Passed\n");
358 }
359 
test_main_unenhanced(void)360 static void test_main_unenhanced(void)
361 {
362 	setup();
363 
364 	gatt_discover(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
365 	gatt_subscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
366 	gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
367 	WAIT_FOR_FLAG(flag_short_subscribed);
368 	WAIT_FOR_FLAG(flag_long_subscribed);
369 
370 	printk("Subscribed\n");
371 
372 	while (num_notifications < NOTIFICATION_COUNT) {
373 		k_sleep(K_MSEC(100));
374 	}
375 
376 	gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
377 	gatt_unsubscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
378 	WAIT_FOR_FLAG(flag_short_subscribed);
379 	WAIT_FOR_FLAG(flag_long_subscribed);
380 
381 	printk("Unsubscribed\n");
382 
383 	PASS("GATT client Passed\n");
384 }
385 
test_main_enhanced(void)386 static void test_main_enhanced(void)
387 {
388 	setup();
389 
390 	gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
391 	gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
392 	gatt_subscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
393 	WAIT_FOR_FLAG(flag_short_subscribed);
394 	WAIT_FOR_FLAG(flag_long_subscribed);
395 
396 	printk("Subscribed\n");
397 
398 	while (num_notifications < NOTIFICATION_COUNT) {
399 		k_sleep(K_MSEC(100));
400 	}
401 
402 	gatt_unsubscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
403 	gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
404 	WAIT_FOR_FLAG(flag_short_subscribed);
405 	WAIT_FOR_FLAG(flag_long_subscribed);
406 
407 	printk("Unsubscribed\n");
408 
409 	PASS("GATT client Passed\n");
410 }
411 
test_main_mixed(void)412 static void test_main_mixed(void)
413 {
414 	setup();
415 
416 	gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
417 	gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
418 	gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
419 	WAIT_FOR_FLAG(flag_short_subscribed);
420 	WAIT_FOR_FLAG(flag_long_subscribed);
421 
422 	printk("Subscribed\n");
423 
424 	while (num_notifications < NOTIFICATION_COUNT) {
425 		k_sleep(K_MSEC(100));
426 	}
427 
428 	gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
429 	gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
430 	WAIT_FOR_FLAG(flag_short_subscribed);
431 	WAIT_FOR_FLAG(flag_long_subscribed);
432 
433 	printk("Unsubscribed\n");
434 
435 	PASS("GATT client Passed\n");
436 }
437 
438 static const struct bst_test_instance test_vcs[] = {
439 	{
440 		.test_id = "gatt_client_none",
441 		.test_pre_init_f = test_init,
442 		.test_tick_f = test_tick,
443 		.test_main_f = test_main_none,
444 	},
445 	{
446 		.test_id = "gatt_client_unenhanced",
447 		.test_pre_init_f = test_init,
448 		.test_tick_f = test_tick,
449 		.test_main_f = test_main_unenhanced,
450 	},
451 	{
452 		.test_id = "gatt_client_enhanced",
453 		.test_pre_init_f = test_init,
454 		.test_tick_f = test_tick,
455 		.test_main_f = test_main_enhanced,
456 	},
457 	{
458 		.test_id = "gatt_client_mixed",
459 		.test_pre_init_f = test_init,
460 		.test_tick_f = test_tick,
461 		.test_main_f = test_main_mixed,
462 	},
463 	BSTEST_END_MARKER,
464 };
465 
test_gatt_client_install(struct bst_test_list * tests)466 struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
467 {
468 	return bst_add_tests(tests, test_vcs);
469 }
470