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