1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/types.h>
9 #include <stddef.h>
10 #include <errno.h>
11 
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/bluetooth/hci.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/uuid.h>
16 #include <zephyr/bluetooth/gatt.h>
17 
18 #include "babblekit/testcase.h"
19 #include "babblekit/flags.h"
20 #include "common.h"
21 
22 DEFINE_FLAG_STATIC(flag_is_connected);
23 DEFINE_FLAG_STATIC(flag_discover_complete);
24 DEFINE_FLAG_STATIC(flag_write_complete);
25 DEFINE_FLAG_STATIC(flag_read_complete);
26 
27 static struct bt_conn *g_conn;
28 static uint16_t unhandled_chrc_handle;
29 static uint16_t unauthorized_chrc_handle;
30 static uint16_t authorized_chrc_handle;
31 static uint16_t cp_chrc_handle;
32 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
33 
34 #define ARRAY_ITEM(i, _) i
35 static uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
36 
connected(struct bt_conn * conn,uint8_t err)37 static void connected(struct bt_conn *conn, uint8_t err)
38 {
39 	char addr[BT_ADDR_LE_STR_LEN];
40 
41 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
42 
43 	if (err != 0) {
44 		TEST_FAIL("Failed to connect to %s (%u)", addr, err);
45 		return;
46 	}
47 
48 	printk("Connected to %s\n", addr);
49 
50 	__ASSERT_NO_MSG(g_conn == conn);
51 
52 	SET_FLAG(flag_is_connected);
53 }
54 
disconnected(struct bt_conn * conn,uint8_t reason)55 static void disconnected(struct bt_conn *conn, uint8_t reason)
56 {
57 	char addr[BT_ADDR_LE_STR_LEN];
58 
59 	if (conn != g_conn) {
60 		return;
61 	}
62 
63 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
64 
65 	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
66 
67 	bt_conn_unref(g_conn);
68 
69 	g_conn = NULL;
70 	UNSET_FLAG(flag_is_connected);
71 }
72 
73 static struct bt_conn_cb conn_callbacks = {
74 	.connected = connected,
75 	.disconnected = disconnected,
76 };
77 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)78 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
79 		  struct net_buf_simple *ad)
80 {
81 	char addr_str[BT_ADDR_LE_STR_LEN];
82 	int err;
83 
84 	if (g_conn != NULL) {
85 		return;
86 	}
87 
88 	/* We're only interested in connectable events */
89 	if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
90 		return;
91 	}
92 
93 	bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
94 	printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
95 
96 	printk("Stopping scan\n");
97 	err = bt_le_scan_stop();
98 	if (err != 0) {
99 		TEST_FAIL("Could not stop scan: %d");
100 		return;
101 	}
102 
103 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
104 				BT_LE_CONN_PARAM_DEFAULT, &g_conn);
105 	if (err != 0) {
106 		TEST_FAIL("Could not connect to peer: %d", err);
107 	}
108 }
109 
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)110 static uint8_t discover_func(struct bt_conn *conn,
111 		const struct bt_gatt_attr *attr,
112 		struct bt_gatt_discover_params *params)
113 {
114 	int err;
115 
116 	if (attr == NULL) {
117 		if (unhandled_chrc_handle == 0 ||
118 		    unauthorized_chrc_handle == 0 ||
119 		    authorized_chrc_handle == 0) {
120 			TEST_FAIL("Did not discover required characterstics");
121 		}
122 
123 		(void)memset(params, 0, sizeof(*params));
124 
125 		SET_FLAG(flag_discover_complete);
126 
127 		return BT_GATT_ITER_STOP;
128 	}
129 
130 	printk("[ATTRIBUTE] handle %u\n", attr->handle);
131 
132 	if (params->type == BT_GATT_DISCOVER_PRIMARY &&
133 	    bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
134 		printk("Found test service\n");
135 		params->uuid = NULL;
136 		params->start_handle = attr->handle + 1;
137 		params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
138 
139 		err = bt_gatt_discover(conn, params);
140 		if (err != 0) {
141 			TEST_FAIL("Discover failed (err %d)", err);
142 		}
143 
144 		return BT_GATT_ITER_STOP;
145 	} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
146 		struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
147 
148 		if (bt_uuid_cmp(chrc->uuid, TEST_UNHANDLED_CHRC_UUID) == 0) {
149 			printk("Found unhandled chrc\n");
150 			unhandled_chrc_handle = chrc->value_handle;
151 		} else if (bt_uuid_cmp(chrc->uuid, TEST_UNAUTHORIZED_CHRC_UUID) == 0) {
152 			printk("Found unauthorized\n");
153 			unauthorized_chrc_handle = chrc->value_handle;
154 		} else if (bt_uuid_cmp(chrc->uuid, TEST_AUTHORIZED_CHRC_UUID) == 0) {
155 			printk("Found authorized chrc\n");
156 			authorized_chrc_handle = chrc->value_handle;
157 		} else if (bt_uuid_cmp(chrc->uuid, TEST_CP_CHRC_UUID) == 0) {
158 			printk("Found CP chrc\n");
159 			cp_chrc_handle = chrc->value_handle;
160 		}
161 	}
162 
163 	return BT_GATT_ITER_CONTINUE;
164 }
165 
gatt_discover(void)166 static void gatt_discover(void)
167 {
168 	static struct bt_gatt_discover_params discover_params;
169 	int err;
170 
171 	printk("Discovering services and characteristics\n");
172 
173 	discover_params.uuid = test_svc_uuid;
174 	discover_params.func = discover_func;
175 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
176 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
177 	discover_params.type = BT_GATT_DISCOVER_PRIMARY;
178 
179 	err = bt_gatt_discover(g_conn, &discover_params);
180 	if (err != 0) {
181 		TEST_FAIL("Discover failed(err %d)", err);
182 	}
183 
184 	WAIT_FOR_FLAG(flag_discover_complete);
185 	printk("Discover complete\n");
186 }
187 
gatt_write_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)188 static void gatt_write_cb(struct bt_conn *conn, uint8_t err,
189 			  struct bt_gatt_write_params *params)
190 {
191 	if ((err != BT_ATT_ERR_SUCCESS) && (params->handle != unauthorized_chrc_handle)) {
192 		TEST_FAIL("Write failed on authorized characteristics: 0x%02X", err);
193 	}
194 
195 	if ((err != BT_ATT_ERR_AUTHORIZATION) && (params->handle == unauthorized_chrc_handle)) {
196 		TEST_FAIL("Write failed on unauthorized characteristics: 0x%02X", err);
197 	}
198 
199 	(void)memset(params, 0, sizeof(*params));
200 
201 	SET_FLAG(flag_write_complete);
202 }
203 
gatt_write(uint16_t handle)204 static void gatt_write(uint16_t handle)
205 {
206 	static struct bt_gatt_write_params write_params;
207 	int err;
208 
209 	printk("Writing to chrc\n");
210 
211 	write_params.data = chrc_data;
212 	write_params.length = sizeof(chrc_data);
213 	write_params.func = gatt_write_cb;
214 	write_params.handle = handle;
215 
216 	UNSET_FLAG(flag_write_complete);
217 
218 	err = bt_gatt_write(g_conn, &write_params);
219 	if (err != 0) {
220 		TEST_FAIL("bt_gatt_write failed: %d", err);
221 	}
222 
223 	WAIT_FOR_FLAG(flag_write_complete);
224 	printk("success\n");
225 }
226 
gatt_cp_write(void)227 static void gatt_cp_write(void)
228 {
229 	static struct bt_gatt_write_params write_params;
230 	int err;
231 	uint8_t cp_write_data[] = {0x00};
232 
233 	printk("Writing to CP chrc\n");
234 
235 	write_params.data = cp_write_data;
236 	write_params.length = sizeof(cp_write_data);
237 	write_params.func = gatt_write_cb;
238 	write_params.handle = cp_chrc_handle;
239 
240 	UNSET_FLAG(flag_write_complete);
241 
242 	err = bt_gatt_write(g_conn, &write_params);
243 	if (err != 0) {
244 		TEST_FAIL("bt_gatt_write failed: %d", err);
245 	}
246 
247 	WAIT_FOR_FLAG(flag_write_complete);
248 	printk("success\n");
249 }
250 
gatt_read_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)251 static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
252 			    struct bt_gatt_read_params *params,
253 			    const void *data, uint16_t length)
254 {
255 	if ((err != BT_ATT_ERR_SUCCESS) &&
256 	    (params->single.handle != unauthorized_chrc_handle)) {
257 		TEST_FAIL("Read failed on authorized characteristics: 0x%02X", err);
258 
259 		if ((length != CHRC_SIZE) || (memcmp(data, chrc_data, length) != 0)) {
260 			TEST_FAIL("chrc data different than expected: 0x%02X", err);
261 		}
262 	}
263 
264 	if ((err != BT_ATT_ERR_AUTHORIZATION) &&
265 	    (params->single.handle == unauthorized_chrc_handle)) {
266 		TEST_FAIL("Read failed on unauthorized characteristics: 0x%02X", err);
267 	}
268 
269 	(void)memset(params, 0, sizeof(*params));
270 
271 	SET_FLAG(flag_read_complete);
272 
273 	return 0;
274 }
275 
gatt_read(uint16_t handle)276 static void gatt_read(uint16_t handle)
277 {
278 	static struct bt_gatt_read_params read_params;
279 	int err;
280 
281 	printk("Reading chrc\n");
282 
283 	read_params.func = gatt_read_cb;
284 	read_params.handle_count = 1;
285 	read_params.single.handle = handle;
286 	read_params.single.offset = 0;
287 
288 	UNSET_FLAG(flag_read_complete);
289 
290 	err = bt_gatt_read(g_conn, &read_params);
291 	if (err != 0) {
292 		TEST_FAIL("bt_gatt_read failed: %d", err);
293 	}
294 
295 	WAIT_FOR_FLAG(flag_read_complete);
296 	printk("success\n");
297 }
298 
gatt_interact(uint16_t handle)299 static void gatt_interact(uint16_t handle)
300 {
301 	gatt_write(handle);
302 	gatt_read(handle);
303 	gatt_cp_write();
304 }
305 
test_main(void)306 static void test_main(void)
307 {
308 	int err;
309 
310 	bt_conn_cb_register(&conn_callbacks);
311 
312 	err = bt_enable(NULL);
313 	if (err != 0) {
314 		TEST_FAIL("Bluetooth discover failed (err %d)", err);
315 	}
316 
317 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
318 	if (err != 0) {
319 		TEST_FAIL("Scanning failed to start (err %d)", err);
320 	}
321 
322 	printk("Scanning successfully started\n");
323 
324 	WAIT_FOR_FLAG(flag_is_connected);
325 
326 	gatt_discover();
327 
328 	printk("Interacting with the unhandled characteristic\n");
329 	gatt_interact(unhandled_chrc_handle);
330 
331 	printk("Interacting with the unauthorized characteristic\n");
332 	gatt_interact(unauthorized_chrc_handle);
333 
334 	printk("Interacting with the authorized characteristic\n");
335 	gatt_interact(authorized_chrc_handle);
336 
337 	TEST_PASS("GATT client Passed");
338 }
339 
340 static const struct bst_test_instance test_vcs[] = {
341 	{
342 		.test_id = "gatt_client",
343 		.test_main_f = test_main
344 	},
345 	BSTEST_END_MARKER
346 };
347 
test_gatt_client_install(struct bst_test_list * tests)348 struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
349 {
350 	return bst_add_tests(tests, test_vcs);
351 }
352