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