1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "utils.h"
8 #include "argparse.h"
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/gatt.h>
13 #include <zephyr/sys/__assert.h>
14 
15 #include "babblekit/testcase.h"
16 #include "babblekit/flags.h"
17 
18 /* Custom Service Variables */
19 static const struct bt_uuid_128 test_svc_uuid = BT_UUID_INIT_128(
20 	0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
21 	0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
22 
23 static const struct bt_uuid_128 test_svc_uuid_2 = BT_UUID_INIT_128(
24 	0xf1, 0xdd, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
25 	0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
26 
27 static const struct bt_uuid_128 test_chrc_uuid = BT_UUID_INIT_128(
28 	0xf2, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
29 	0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
30 
31 static uint8_t test_value[] = { 'T', 'e', 's', 't', '\0' };
32 
33 DEFINE_FLAG_STATIC(flag_client_read);
34 
read_test(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)35 static ssize_t read_test(struct bt_conn *conn, const struct bt_gatt_attr *attr,
36 			void *buf, uint16_t len, uint16_t offset)
37 {
38 	const char *value = attr->user_data;
39 
40 	printk("Client has read from test char\n");
41 	SET_FLAG(flag_client_read);
42 
43 	return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
44 				 strlen(value));
45 }
46 
wait_for_client_read(void)47 void wait_for_client_read(void)
48 {
49 	WAIT_FOR_FLAG(flag_client_read);
50 }
51 
write_test(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)52 static ssize_t write_test(struct bt_conn *conn, const struct bt_gatt_attr *attr,
53 			 const void *buf, uint16_t len, uint16_t offset,
54 			 uint8_t flags)
55 {
56 	uint8_t *value = attr->user_data;
57 
58 	printk("Client has written to test char\n");
59 
60 	if (offset + len > sizeof(test_value)) {
61 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
62 	}
63 
64 	memcpy(value + offset, buf, len);
65 
66 	return len;
67 }
68 
69 static struct bt_gatt_attr test_attrs[] = {
70 	/* Vendor Primary Service Declaration */
71 	BT_GATT_PRIMARY_SERVICE(&test_svc_uuid),
72 
73 	BT_GATT_CHARACTERISTIC(&test_chrc_uuid.uuid,
74 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
75 			       BT_GATT_PERM_READ |
76 			       BT_GATT_PERM_WRITE,
77 			       read_test, write_test, test_value),
78 };
79 
80 static struct bt_gatt_attr test_attrs_2[] = {
81 	/* Vendor Primary Service Declaration */
82 	BT_GATT_PRIMARY_SERVICE(&test_svc_uuid_2),
83 
84 	BT_GATT_CHARACTERISTIC(&test_chrc_uuid.uuid,
85 			       BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
86 			       BT_GATT_PERM_READ_ENCRYPT |
87 			       BT_GATT_PERM_WRITE_ENCRYPT,
88 			       read_test, write_test, test_value),
89 };
90 
91 static struct bt_gatt_service test_svc = BT_GATT_SERVICE(test_attrs);
92 static struct bt_gatt_service test_svc_2 = BT_GATT_SERVICE(test_attrs_2);
93 
gatt_register_service_1(void)94 void gatt_register_service_1(void)
95 {
96 	int err = bt_gatt_service_register(&test_svc);
97 
98 	__ASSERT(!err, "Failed to register GATT service (err %d)\n", err);
99 }
100 
gatt_register_service_2(void)101 void gatt_register_service_2(void)
102 {
103 	/* This service is only used to trigger a GATT DB change.
104 	 * No reads or writes will be attempted.
105 	 */
106 	int err = bt_gatt_service_register(&test_svc_2);
107 
108 	__ASSERT(!err, "Failed to register GATT service (err %d)\n", err);
109 }
110 
111 /* We need to discover:
112  * - Dynamic service
113  * - Client Features (to set robust caching)
114  * - Service Changed (to sub to indications)
115  */
116 enum GATT_HANDLES {
117 CLIENT_FEATURES,
118 SERVICE_CHANGED,
119 TEST_CHAR,
120 NUM_HANDLES,
121 };
122 
123 uint16_t gatt_handles[NUM_HANDLES] = {0};
124 
125 DEFINE_FLAG(flag_discovered);
126 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)127 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
128 			     struct bt_gatt_discover_params *params)
129 {
130 	if (attr == NULL) {
131 		for (int i = 0; i < ARRAY_SIZE(gatt_handles); i++) {
132 			printk("handle[%d] = 0x%x\n", i, gatt_handles[i]);
133 
134 			if (gatt_handles[i] == 0) {
135 				TEST_FAIL("Did not discover all characteristics");
136 			}
137 		}
138 
139 		(void)memset(params, 0, sizeof(*params));
140 
141 		SET_FLAG(flag_discovered);
142 
143 		return BT_GATT_ITER_STOP;
144 	}
145 
146 	if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
147 		const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
148 
149 		if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_CLIENT_FEATURES) == 0) {
150 			printk("Found client supported features\n");
151 			gatt_handles[CLIENT_FEATURES] = chrc->value_handle;
152 
153 		} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) {
154 			printk("Found service changed\n");
155 			gatt_handles[SERVICE_CHANGED] = chrc->value_handle;
156 
157 		} else if (bt_uuid_cmp(chrc->uuid, &test_chrc_uuid.uuid) == 0) {
158 			printk("Found test characteristic\n");
159 			gatt_handles[TEST_CHAR] = chrc->value_handle;
160 		}
161 	}
162 
163 	return BT_GATT_ITER_CONTINUE;
164 }
165 
166 DEFINE_FLAG(flag_sc_indicated);
sc_indicated(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)167 static uint8_t sc_indicated(struct bt_conn *conn,
168 			    struct bt_gatt_subscribe_params *params,
169 			    const void *data, uint16_t length)
170 {
171 	if (!data) {
172 		params->value_handle = 0U;
173 		return BT_GATT_ITER_STOP;
174 	}
175 
176 	printk("SC received\n");
177 	SET_FLAG(flag_sc_indicated);
178 
179 	return BT_GATT_ITER_CONTINUE;
180 }
181 
wait_for_sc_indication(void)182 void wait_for_sc_indication(void)
183 {
184 	WAIT_FOR_FLAG(flag_sc_indicated);
185 }
186 
187 DEFINE_FLAG(flag_sc_subscribed);
sc_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)188 static void sc_subscribed(struct bt_conn *conn, uint8_t err,
189 			  struct bt_gatt_subscribe_params *params)
190 {
191 	if (params->value) {
192 		printk("SC subscribed\n");
193 		SET_FLAG(flag_sc_subscribed);
194 	} else {
195 		printk("SC unsubscribed\n");
196 		UNSET_FLAG(flag_sc_subscribed);
197 	}
198 }
199 
200 static struct bt_gatt_discover_params disc_params;
201 static struct bt_gatt_subscribe_params subscribe_params;
gatt_subscribe_to_service_changed(bool subscribe)202 void gatt_subscribe_to_service_changed(bool subscribe)
203 {
204 	int err;
205 
206 	subscribe_params.value_handle = gatt_handles[SERVICE_CHANGED];
207 	subscribe_params.notify = sc_indicated;
208 	subscribe_params.subscribe = sc_subscribed;
209 
210 	if (subscribe) {
211 		subscribe_params.ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE;
212 		subscribe_params.disc_params = &disc_params,
213 		subscribe_params.value = BT_GATT_CCC_INDICATE;
214 		subscribe_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
215 
216 		err = bt_gatt_subscribe(get_conn(), &subscribe_params);
217 		WAIT_FOR_FLAG(flag_sc_subscribed);
218 	} else {
219 		/* Params are already set to the correct values by the previous
220 		 * call of this fn.
221 		 */
222 		err = bt_gatt_unsubscribe(get_conn(), &subscribe_params);
223 		WAIT_FOR_FLAG_UNSET(flag_sc_subscribed);
224 	}
225 
226 	if (err != 0) {
227 		TEST_FAIL("Subscription failed(err %d)", err);
228 	} else {
229 		printk("%subscribed %s SC indications\n",
230 		       subscribe ? "S" : "Uns",
231 		       subscribe ? "to" : "from");
232 	}
233 }
234 
gatt_discover(void)235 void gatt_discover(void)
236 {
237 	static struct bt_gatt_discover_params discover_params;
238 	int err;
239 
240 	printk("Discovering services and characteristics\n");
241 	UNSET_FLAG(flag_discovered);
242 
243 	discover_params.uuid = NULL;
244 	discover_params.func = discover_func;
245 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
246 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
247 	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
248 
249 	err = bt_gatt_discover(get_conn(), &discover_params);
250 	if (err != 0) {
251 		TEST_FAIL("Discover failed(err %d)", err);
252 	}
253 
254 	WAIT_FOR_FLAG(flag_discovered);
255 	printk("Discover complete\n");
256 }
257 
258 DEFINE_FLAG(flag_written);
259 
write_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)260 static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
261 {
262 	if (err != BT_ATT_ERR_SUCCESS) {
263 		TEST_FAIL("Write failed: 0x%02X", err);
264 	}
265 
266 	SET_FLAG(flag_written);
267 }
268 
269 #define CF_BIT_ROBUST_CACHING 0
activate_robust_caching(void)270 void activate_robust_caching(void)
271 {
272 	int err;
273 
274 	static const uint8_t csf = BIT(CF_BIT_ROBUST_CACHING);
275 	static struct bt_gatt_write_params write_params = {
276 		.func = write_cb,
277 		.offset = 0,
278 		.data = &csf,
279 		.length = sizeof(csf),
280 	};
281 
282 	write_params.handle = gatt_handles[CLIENT_FEATURES];
283 
284 	UNSET_FLAG(flag_written);
285 	err = bt_gatt_write(get_conn(), &write_params);
286 
287 	__ASSERT(!err, "Failed to enable robust caching\n");
288 
289 	WAIT_FOR_FLAG(flag_written);
290 	printk("Robust caching enabled\n");
291 }
292 
293 DEFINE_FLAG(flag_read);
294 
_expect_success(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)295 static uint8_t _expect_success(struct bt_conn *conn, uint8_t err,
296 			       struct bt_gatt_read_params *params,
297 			       const void *data, uint16_t length)
298 {
299 	/* printk("GATT read cb: err 0x%02X\n", err); */
300 	__ASSERT(err == 0, "Failed to read: err 0x%x\n", err);
301 
302 	SET_FLAG(flag_read);
303 
304 	return 0;
305 }
306 
_expect_out_of_sync_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)307 static uint8_t _expect_out_of_sync_cb(struct bt_conn *conn, uint8_t err,
308 				     struct bt_gatt_read_params *params,
309 				     const void *data, uint16_t length)
310 {
311 	/* printk("GATT read cb: err 0x%02X\n", err); */
312 	__ASSERT(err == BT_ATT_ERR_DB_OUT_OF_SYNC,
313 		 "Didn't get expected error code: err 0x%x\n", err);
314 
315 	SET_FLAG(flag_read);
316 
317 	return 0;
318 }
319 
read_char(uint16_t handle,bool expect_success)320 static void read_char(uint16_t handle, bool expect_success)
321 {
322 	int err;
323 
324 	struct bt_gatt_read_params read_params = {
325 		.handle_count = 1,
326 		.single = {
327 			.handle = handle,
328 			.offset = 0,
329 		},
330 	};
331 
332 	if (expect_success) {
333 		read_params.func = _expect_success;
334 	} else {
335 		read_params.func = _expect_out_of_sync_cb;
336 	}
337 
338 	UNSET_FLAG(flag_read);
339 
340 	err = bt_gatt_read(get_conn(), &read_params);
341 	__ASSERT(!err, "Failed to read char\n");
342 
343 	WAIT_FOR_FLAG(flag_read);
344 }
345 
read_test_char(bool expect_success)346 void read_test_char(bool expect_success)
347 {
348 	read_char(gatt_handles[TEST_CHAR], expect_success);
349 }
350 
gatt_clear_flags(void)351 void gatt_clear_flags(void)
352 {
353 	UNSET_FLAG(flag_client_read);
354 	UNSET_FLAG(flag_discovered);
355 	UNSET_FLAG(flag_sc_indicated);
356 	UNSET_FLAG(flag_sc_subscribed);
357 	UNSET_FLAG(flag_written);
358 	UNSET_FLAG(flag_read);
359 }
360