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