1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "common.h"
8 
9 extern enum bst_result_t bst_result;
10 
11 CREATE_FLAG(flag_is_connected);
12 CREATE_FLAG(flag_short_subscribe);
13 CREATE_FLAG(flag_long_subscribe);
14 
15 static struct bt_conn *g_conn;
16 
17 #define ARRAY_ITEM(i, _) i
18 const uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
19 const uint8_t long_chrc_data[] = { LISTIFY(LONG_CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
20 
connected(struct bt_conn * conn,uint8_t err)21 static void connected(struct bt_conn *conn, uint8_t err)
22 {
23 	char addr[BT_ADDR_LE_STR_LEN];
24 
25 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
26 
27 	if (err != 0) {
28 		FAIL("Failed to connect to %s (%u)\n", addr, err);
29 		return;
30 	}
31 
32 	printk("Connected to %s\n", addr);
33 
34 	g_conn = bt_conn_ref(conn);
35 	SET_FLAG(flag_is_connected);
36 }
37 
disconnected(struct bt_conn * conn,uint8_t reason)38 static void disconnected(struct bt_conn *conn, uint8_t reason)
39 {
40 	char addr[BT_ADDR_LE_STR_LEN];
41 
42 	if (conn != g_conn) {
43 		return;
44 	}
45 
46 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
47 
48 	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
49 
50 	bt_conn_unref(g_conn);
51 
52 	g_conn = NULL;
53 	UNSET_FLAG(flag_is_connected);
54 }
55 
56 BT_CONN_CB_DEFINE(conn_callbacks) = {
57 	.connected = connected,
58 	.disconnected = disconnected,
59 };
60 
read_test_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)61 static ssize_t read_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
62 			      uint16_t len, uint16_t offset)
63 {
64 	printk("Read short\n");
65 	return bt_gatt_attr_read(conn, attr, buf, len, offset, (void *)chrc_data,
66 				 sizeof(chrc_data));
67 }
68 
read_long_test_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)69 static ssize_t read_long_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
70 				   uint16_t len, uint16_t offset)
71 {
72 	printk("Read long\n");
73 	return bt_gatt_attr_read(conn, attr, buf, len, offset, (void *)long_chrc_data,
74 				 sizeof(long_chrc_data));
75 }
76 
short_subscribe(const struct bt_gatt_attr * attr,uint16_t value)77 static void short_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
78 {
79 	const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
80 
81 	if (notif_enabled) {
82 		SET_FLAG(flag_short_subscribe);
83 	}
84 
85 	printk("Short notifications %s\n", notif_enabled ? "enabled" : "disabled");
86 }
87 
long_subscribe(const struct bt_gatt_attr * attr,uint16_t value)88 static void long_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
89 {
90 	const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
91 
92 	if (notif_enabled) {
93 		SET_FLAG(flag_long_subscribe);
94 	}
95 
96 	printk("Long notifications %s\n", notif_enabled ? "enabled" : "disabled");
97 }
98 
99 BT_GATT_SERVICE_DEFINE(test_svc, BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
100 		       BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID,
101 					      BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
102 					      BT_GATT_PERM_READ, read_test_chrc, NULL, NULL),
103 		       BT_GATT_CUD("Short test_svc format description", BT_GATT_PERM_READ),
104 		       BT_GATT_CCC(short_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
105 		       BT_GATT_CHARACTERISTIC(TEST_LONG_CHRC_UUID,
106 					      BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
107 					      BT_GATT_PERM_READ, read_long_test_chrc, NULL, NULL),
108 		       BT_GATT_CCC(long_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
109 
110 static volatile size_t num_notifications_sent;
111 
notification_sent(struct bt_conn * conn,void * user_data)112 static void notification_sent(struct bt_conn *conn, void *user_data)
113 {
114 	size_t *length = user_data;
115 
116 	printk("Sent notification #%u with length %d\n", num_notifications_sent++, *length);
117 }
118 
short_notify(enum bt_att_chan_opt opt)119 static void short_notify(enum bt_att_chan_opt opt)
120 {
121 	static size_t length = CHRC_SIZE;
122 	static struct bt_gatt_notify_params params = {
123 		.attr = &attr_test_svc[1],
124 		.data = chrc_data,
125 		.len = CHRC_SIZE,
126 		.func = notification_sent,
127 		.user_data = &length,
128 		.uuid = NULL,
129 	};
130 	int err;
131 
132 	params.chan_opt = opt;
133 
134 	do {
135 		err = bt_gatt_notify_cb(g_conn, &params);
136 
137 		if (err == -ENOMEM) {
138 			k_sleep(K_MSEC(10));
139 		} else if (err) {
140 			FAIL("Short notify failed (err %d)\n", err);
141 		}
142 	} while (err);
143 }
144 
long_notify(enum bt_att_chan_opt opt)145 static void long_notify(enum bt_att_chan_opt opt)
146 {
147 	static size_t length = LONG_CHRC_SIZE;
148 	static struct bt_gatt_notify_params params = {
149 		.attr = &attr_test_svc[5],
150 		.data = long_chrc_data,
151 		.len = LONG_CHRC_SIZE,
152 		.func = notification_sent,
153 		.user_data = &length,
154 		.uuid = NULL,
155 	};
156 	int err;
157 
158 	params.chan_opt = opt;
159 
160 	do {
161 		err = bt_gatt_notify_cb(g_conn, &params);
162 
163 		if (err == -ENOMEM) {
164 			k_sleep(K_MSEC(10));
165 		} else if (err) {
166 			FAIL("Long notify failed (err %d)\n", err);
167 		}
168 	} while (err);
169 }
170 
setup(void)171 static void setup(void)
172 {
173 	int err;
174 	const struct bt_data ad[] = {
175 		BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
176 	};
177 
178 	err = bt_enable(NULL);
179 	if (err != 0) {
180 		FAIL("Bluetooth init failed (err %d)\n", err);
181 		return;
182 	}
183 
184 	printk("Bluetooth initialized\n");
185 
186 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
187 	if (err != 0) {
188 		FAIL("Advertising failed to start (err %d)\n", err);
189 		return;
190 	}
191 
192 	printk("Advertising successfully started\n");
193 
194 	WAIT_FOR_FLAG(flag_is_connected);
195 
196 	while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
197 		k_sleep(K_MSEC(100));
198 	}
199 	printk("EATT connected\n");
200 
201 	WAIT_FOR_FLAG(flag_short_subscribe);
202 	WAIT_FOR_FLAG(flag_long_subscribe);
203 }
204 
test_main_none(void)205 static void test_main_none(void)
206 {
207 	setup();
208 
209 	for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
210 		short_notify(BT_ATT_CHAN_OPT_NONE);
211 		long_notify(BT_ATT_CHAN_OPT_NONE);
212 	}
213 
214 	while (num_notifications_sent < NOTIFICATION_COUNT) {
215 		k_sleep(K_MSEC(100));
216 	}
217 
218 	PASS("GATT server passed\n");
219 }
220 
test_main_enhanced(void)221 static void test_main_enhanced(void)
222 {
223 	setup();
224 
225 	for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
226 		short_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
227 		long_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
228 	}
229 
230 	while (num_notifications_sent < NOTIFICATION_COUNT) {
231 		k_sleep(K_MSEC(100));
232 	}
233 
234 	PASS("GATT server passed\n");
235 }
236 
test_main_unenhanced(void)237 static void test_main_unenhanced(void)
238 {
239 	setup();
240 
241 	for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
242 		short_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
243 		long_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
244 	}
245 
246 	while (num_notifications_sent < NOTIFICATION_COUNT) {
247 		k_sleep(K_MSEC(100));
248 	}
249 
250 	PASS("GATT server passed\n");
251 }
252 
test_main_mixed(void)253 static void test_main_mixed(void)
254 {
255 	setup();
256 
257 	for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
258 		short_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
259 		long_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
260 	}
261 
262 	while (num_notifications_sent < NOTIFICATION_COUNT) {
263 		k_sleep(K_MSEC(100));
264 	}
265 
266 	PASS("GATT server passed\n");
267 }
268 
269 static const struct bst_test_instance test_gatt_server[] = {
270 	{
271 		.test_id = "gatt_server_none",
272 		.test_pre_init_f = test_init,
273 		.test_tick_f = test_tick,
274 		.test_main_f = test_main_none,
275 	},
276 	{
277 		.test_id = "gatt_server_unenhanced",
278 		.test_pre_init_f = test_init,
279 		.test_tick_f = test_tick,
280 		.test_main_f = test_main_unenhanced,
281 	},
282 	{
283 		.test_id = "gatt_server_enhanced",
284 		.test_pre_init_f = test_init,
285 		.test_tick_f = test_tick,
286 		.test_main_f = test_main_enhanced,
287 	},
288 	{
289 		.test_id = "gatt_server_mixed",
290 		.test_pre_init_f = test_init,
291 		.test_tick_f = test_tick,
292 		.test_main_f = test_main_mixed,
293 	},
294 	BSTEST_END_MARKER,
295 };
296 
test_gatt_server_install(struct bst_test_list * tests)297 struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
298 {
299 	return bst_add_tests(tests, test_gatt_server);
300 }
301