1 /*
2 * Copyright (c) 2022 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 chrc_handle;
19 static uint16_t long_chrc_handle;
20 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
21
22 #define NUM_ITERATIONS 10
23
24 #define ARRAY_ITEM(i, _) i
25 static uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
26 static uint8_t long_chrc_data[] = { LISTIFY(LONG_CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
27
connected(struct bt_conn * conn,uint8_t err)28 static void connected(struct bt_conn *conn, uint8_t err)
29 {
30 char addr[BT_ADDR_LE_STR_LEN];
31
32 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
33
34 if (err != 0) {
35 FAIL("Failed to connect to %s (%u)\n", addr, err);
36 return;
37 }
38
39 printk("Connected to %s\n", addr);
40
41 g_conn = conn;
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 (chrc_handle == 0 || long_chrc_handle == 0) {
108 FAIL("Did not discover chrc (%x) or long_chrc (%x)",
109 chrc_handle, long_chrc_handle);
110 }
111
112 (void)memset(params, 0, sizeof(*params));
113
114 SET_FLAG(flag_discover_complete);
115
116 return BT_GATT_ITER_STOP;
117 }
118
119 printk("[ATTRIBUTE] handle %u\n", attr->handle);
120
121 if (params->type == BT_GATT_DISCOVER_PRIMARY &&
122 bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
123 printk("Found test service\n");
124 params->uuid = NULL;
125 params->start_handle = attr->handle + 1;
126 params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
127
128 err = bt_gatt_discover(conn, params);
129 if (err != 0) {
130 FAIL("Discover failed (err %d)\n", err);
131 }
132
133 return BT_GATT_ITER_STOP;
134 } else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
135 struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
136
137 if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
138 printk("Found chrc\n");
139 chrc_handle = chrc->value_handle;
140 } else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
141 printk("Found long_chrc\n");
142 long_chrc_handle = chrc->value_handle;
143 }
144 }
145
146 return BT_GATT_ITER_CONTINUE;
147 }
148
gatt_discover(void)149 static void gatt_discover(void)
150 {
151 static struct bt_gatt_discover_params discover_params;
152 int err;
153
154 printk("Discovering services and characteristics\n");
155
156 discover_params.uuid = test_svc_uuid;
157 discover_params.func = discover_func;
158 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
159 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
160 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
161
162 err = bt_gatt_discover(g_conn, &discover_params);
163 if (err != 0) {
164 FAIL("Discover failed(err %d)\n", err);
165 }
166
167 WAIT_FOR_FLAG(flag_discover_complete);
168 printk("Discover complete\n");
169 }
170
gatt_write_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)171 static void gatt_write_cb(struct bt_conn *conn, uint8_t err,
172 struct bt_gatt_write_params *params)
173 {
174 if (err != BT_ATT_ERR_SUCCESS) {
175 FAIL("Write failed: 0x%02X\n", err);
176 }
177
178 (void)memset(params, 0, sizeof(*params));
179
180 SET_FLAG(flag_write_complete);
181 }
182
gatt_write(uint16_t handle)183 static void gatt_write(uint16_t handle)
184 {
185 static struct bt_gatt_write_params write_params;
186 int err;
187
188 if (handle == chrc_handle) {
189 printk("Writing to chrc\n");
190 write_params.data = chrc_data;
191 write_params.length = sizeof(chrc_data);
192 } else if (handle) {
193 printk("Writing to long_chrc\n");
194 write_params.data = long_chrc_data;
195 write_params.length = sizeof(long_chrc_data);
196 }
197
198 write_params.func = gatt_write_cb;
199 write_params.handle = handle;
200
201 UNSET_FLAG(flag_write_complete);
202
203 err = bt_gatt_write(g_conn, &write_params);
204 if (err != 0) {
205 FAIL("bt_gatt_write failed: %d\n", err);
206 }
207
208 WAIT_FOR_FLAG(flag_write_complete);
209 printk("success\n");
210 }
211
gatt_read_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)212 static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
213 struct bt_gatt_read_params *params,
214 const void *data, uint16_t length)
215 {
216 if (err != BT_ATT_ERR_SUCCESS) {
217 FAIL("Read failed: 0x%02X\n", err);
218 }
219
220 if (params->single.handle == chrc_handle) {
221 if (length != CHRC_SIZE ||
222 memcmp(data, chrc_data, length) != 0) {
223 FAIL("chrc data different than expected", err);
224 }
225 } else if (params->single.handle == chrc_handle) {
226 if (length != LONG_CHRC_SIZE ||
227 memcmp(data, long_chrc_data, length) != 0) {
228 FAIL("long_chrc data different than expected", err);
229 }
230 }
231
232 (void)memset(params, 0, sizeof(*params));
233
234 SET_FLAG(flag_read_complete);
235
236 return 0;
237 }
238
gatt_read(uint16_t handle)239 static void gatt_read(uint16_t handle)
240 {
241 static struct bt_gatt_read_params read_params;
242 int err;
243
244 printk("Reading chrc\n");
245
246 read_params.func = gatt_read_cb;
247 read_params.handle_count = 1;
248 read_params.single.handle = chrc_handle;
249 read_params.single.offset = 0;
250
251 UNSET_FLAG(flag_read_complete);
252
253 err = bt_gatt_read(g_conn, &read_params);
254 if (err != 0) {
255 FAIL("bt_gatt_read failed: %d\n", err);
256 }
257
258 WAIT_FOR_FLAG(flag_read_complete);
259 printk("success\n");
260 }
261
test_main(void)262 static void test_main(void)
263 {
264 int err;
265
266 bt_conn_cb_register(&conn_callbacks);
267
268 for (int i = 0; i < NUM_ITERATIONS; i++) {
269
270 err = bt_enable(NULL);
271 if (err != 0) {
272 FAIL("Bluetooth discover failed (err %d)\n", err);
273 }
274 printk("Bluetooth initialized\n");
275
276 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
277 if (err != 0) {
278 FAIL("Scanning failed to start (err %d)\n", err);
279 }
280
281 printk("Scanning successfully started\n");
282
283 WAIT_FOR_FLAG(flag_is_connected);
284
285 gatt_discover();
286
287 /* Write and read a few times to ensure stateless behavior */
288 for (size_t i = 0; i < 3; i++) {
289 gatt_write(chrc_handle);
290 gatt_read(chrc_handle);
291 gatt_write(long_chrc_handle);
292 gatt_read(long_chrc_handle);
293 }
294
295 err = bt_conn_disconnect(g_conn, 0x13);
296 if (err != 0) {
297 FAIL("Disconnect failed (err %d)\n", err);
298 return;
299 }
300
301 WAIT_FOR_FLAG_UNSET(flag_is_connected);
302
303 err = bt_disable();
304 if (err != 0) {
305 FAIL("Bluetooth disable failed (err %d)\n", err);
306 }
307 printk("Bluetooth successfully disabled\n");
308 }
309
310 PASS("GATT client Passed\n");
311 }
312
313 static const struct bst_test_instance test_vcs[] = {
314 {
315 .test_id = "gatt_client",
316 .test_pre_init_f = test_init,
317 .test_tick_f = test_tick,
318 .test_main_f = test_main
319 },
320 BSTEST_END_MARKER
321 };
322
test_gatt_client_install(struct bst_test_list * tests)323 struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
324 {
325 return bst_add_tests(tests, test_vcs);
326 }
327