1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * EATT notification reliability test:
7 * A central acting as a GATT client scans and connects
8 * to a peripheral acting as a GATT server.
9 * The GATT client will then attempt to connect a number of CONFIG_BT_EATT_MAX bearers
10 * over EATT, send notifications, disconnect all bearers and reconnect EATT_BEARERS_TEST
11 * and send start a transaction with a request, then send a lot of notifications
12 * before the response is received.
13 * The test might be expanded by checking that all the notifications all transmitted
14 * on EATT channels.
15 */
16
17 #include <stddef.h>
18 #include <errno.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/types.h>
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/hci.h>
23 #include <zephyr/bluetooth/conn.h>
24 #include <zephyr/bluetooth/uuid.h>
25 #include <zephyr/bluetooth/gatt.h>
26 #include <zephyr/bluetooth/att.h>
27
28 #include "babblekit/testcase.h"
29 #include "babblekit/flags.h"
30 #include "babblekit/sync.h"
31 #include "common.h"
32
33 DEFINE_FLAG_STATIC(flag_is_connected);
34 DEFINE_FLAG_STATIC(flag_discover_complete);
35 DEFINE_FLAG_STATIC(flag_is_encrypted);
36
37 static struct bt_conn *g_conn;
38 static const struct bt_gatt_attr *local_attr;
39 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
40
41 #define NUM_NOTIF 100
42 #define SAMPLE_DATA 1
43 #define EATT_BEARERS_TEST 1
44
45 volatile int num_eatt_channels;
46
connected(struct bt_conn * conn,uint8_t err)47 static void connected(struct bt_conn *conn, uint8_t err)
48 {
49 char addr[BT_ADDR_LE_STR_LEN];
50
51 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
52
53 if (err != 0) {
54 TEST_FAIL("Failed to connect to %s (%u)", addr, err);
55 return;
56 }
57
58 printk("Connected to %s\n", addr);
59 SET_FLAG(flag_is_connected);
60 }
61
disconnected(struct bt_conn * conn,uint8_t reason)62 static void disconnected(struct bt_conn *conn, uint8_t reason)
63 {
64 char addr[BT_ADDR_LE_STR_LEN];
65
66 if (conn != g_conn) {
67 return;
68 }
69
70 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
71
72 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
73
74 bt_conn_unref(g_conn);
75
76 g_conn = NULL;
77 UNSET_FLAG(flag_is_connected);
78 }
79
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err security_err)80 static void security_changed(struct bt_conn *conn, bt_security_t level,
81 enum bt_security_err security_err)
82 {
83 if (security_err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) {
84 SET_FLAG(flag_is_encrypted);
85 }
86 }
87
88 BT_CONN_CB_DEFINE(conn_callbacks) = {
89 .connected = connected,
90 .disconnected = disconnected,
91 .security_changed = security_changed,
92 };
93
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)94 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
95 struct net_buf_simple *ad)
96 {
97 char addr_str[BT_ADDR_LE_STR_LEN];
98 int err;
99
100 if (g_conn != NULL) {
101 return;
102 }
103
104 /* We're only interested in connectable events */
105 if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
106 return;
107 }
108
109 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
110 printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
111
112 printk("Stopping scan\n");
113 err = bt_le_scan_stop();
114 if (err != 0) {
115 TEST_FAIL("Could not stop scan: %d");
116 return;
117 }
118
119 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
120 BT_LE_CONN_PARAM_DEFAULT, &g_conn);
121 if (err != 0) {
122 TEST_FAIL("Could not connect to peer: %d", err);
123 }
124 }
125
send_notification(void)126 void send_notification(void)
127 {
128 const uint8_t sample_dat = SAMPLE_DATA;
129 int err;
130
131 do {
132 err = bt_gatt_notify(g_conn, local_attr, &sample_dat, sizeof(sample_dat));
133 if (!err) {
134 return;
135 } else if (err != -ENOMEM) {
136 printk("GATT notify failed (err %d)\n", err);
137 return;
138 }
139 k_sleep(K_TICKS(1));
140 } while (err == -ENOMEM);
141 }
142
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)143 static uint8_t discover_func(struct bt_conn *conn,
144 const struct bt_gatt_attr *attr,
145 struct bt_gatt_discover_params *params)
146 {
147 SET_FLAG(flag_discover_complete);
148 printk("Discover complete\n");
149
150 return BT_GATT_ITER_STOP;
151 }
152
gatt_discover(void)153 static void gatt_discover(void)
154 {
155 static struct bt_gatt_discover_params discover_params;
156 int err;
157
158 printk("Discovering services and characteristics\n");
159
160 discover_params.uuid = test_svc_uuid;
161 discover_params.func = discover_func;
162 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
163 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
164 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
165 discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
166
167 err = bt_gatt_discover(g_conn, &discover_params);
168 if (err != 0) {
169 TEST_FAIL("Discover failed(err %d)", err);
170 }
171 }
172
173 BT_GATT_SERVICE_DEFINE(g_svc,
174 BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
175 BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID, BT_GATT_CHRC_NOTIFY,
176 BT_GATT_PERM_READ, NULL, NULL, NULL),
177 BT_GATT_CCC(NULL,
178 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
179
test_main(void)180 static void test_main(void)
181 {
182 int err;
183
184 TEST_ASSERT(bk_sync_init() == 0, "Failed to open backchannel");
185
186 err = bt_enable(NULL);
187 if (err != 0) {
188 TEST_FAIL("Bluetooth enable failed (err %d)", err);
189 }
190
191 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
192 if (err != 0) {
193 TEST_FAIL("Scanning failed to start (err %d)", err);
194 }
195
196 printk("Scanning successfully started\n");
197
198 WAIT_FOR_FLAG(flag_is_connected);
199
200 err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
201 if (err) {
202 TEST_FAIL("Failed to start encryption procedure");
203 }
204
205 WAIT_FOR_FLAG(flag_is_encrypted);
206
207 err = bt_eatt_connect(g_conn, CONFIG_BT_EATT_MAX);
208 if (err) {
209 TEST_FAIL("Sending credit based connection request failed (err %d)", err);
210 }
211
212 /* Wait for the channels to be connected */
213 while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
214 k_sleep(K_TICKS(1));
215 }
216
217 printk("Waiting for sync\n");
218 bk_sync_wait();
219
220 local_attr = &g_svc.attrs[1];
221
222 printk("############# Notification test\n");
223 for (int idx = 0; idx < NUM_NOTIF; idx++) {
224 printk("Notification %d\n", idx);
225 send_notification();
226 }
227
228 printk("############# Disconnect and reconnect\n");
229 for (int idx = 0; idx < CONFIG_BT_EATT_MAX; idx++) {
230 bt_eatt_disconnect_one(g_conn);
231 while (bt_eatt_count(g_conn) != (CONFIG_BT_EATT_MAX - idx)) {
232 k_sleep(K_TICKS(1));
233 }
234 }
235
236 printk("Connecting %d bearers\n", EATT_BEARERS_TEST);
237 err = bt_eatt_connect(g_conn, EATT_BEARERS_TEST);
238 if (err) {
239 TEST_FAIL("Sending credit based connection request failed (err %d)", err);
240 }
241
242 /* Wait for the channels to be connected */
243 while (bt_eatt_count(g_conn) < EATT_BEARERS_TEST) {
244 k_sleep(K_TICKS(1));
245 }
246
247 printk("############# Send notifications during discovery request\n");
248 gatt_discover();
249 while (!IS_FLAG_SET(flag_discover_complete)) {
250 printk("Notifying...\n");
251 send_notification();
252 }
253
254 printk("Sending final sync\n");
255 bk_sync_send();
256
257 TEST_PASS("Client Passed");
258 }
259
260 static const struct bst_test_instance test_vcs[] = {
261 {
262 .test_id = "client",
263 .test_main_f = test_main
264 },
265 BSTEST_END_MARKER
266 };
267
test_client_install(struct bst_test_list * tests)268 struct bst_test_list *test_client_install(struct bst_test_list *tests)
269 {
270 return bst_add_tests(tests, test_vcs);
271 }
272