1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/hci.h>
13 #include <zephyr/bluetooth/l2cap.h>
14 #include <zephyr/bluetooth/att.h>
15 #include <zephyr/bluetooth/gatt.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/bluetooth/conn.h>
18 
19 #include "babblekit/testcase.h"
20 #include "babblekit/flags.h"
21 
22 #include "common_defs.h"
23 
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF);
26 
27 DEFINE_FLAG_STATIC(is_connected);
28 DEFINE_FLAG_STATIC(is_subscribed);
29 DEFINE_FLAG_STATIC(flag_data_length_updated);
30 
31 static atomic_t nwrites;
32 static atomic_t indications;
33 static atomic_t notifications;
34 
35 /* Defined in conn.c */
36 extern void bt_conn_suspend_tx(bool suspend);
37 
38 static struct bt_conn *dconn;
39 
connected(struct bt_conn * conn,uint8_t conn_err)40 static void connected(struct bt_conn *conn, uint8_t conn_err)
41 {
42 	char addr[BT_ADDR_LE_STR_LEN];
43 
44 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
45 
46 	if (conn_err) {
47 		TEST_FAIL("Failed to connect to %s (%u)", addr, conn_err);
48 		return;
49 	}
50 
51 	LOG_DBG("%s", addr);
52 
53 	dconn = bt_conn_ref(conn);
54 	SET_FLAG(is_connected);
55 }
56 
disconnected(struct bt_conn * conn,uint8_t reason)57 static void disconnected(struct bt_conn *conn, uint8_t reason)
58 {
59 	char addr[BT_ADDR_LE_STR_LEN];
60 
61 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
62 
63 	LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
64 
65 	bt_conn_unref(dconn);
66 	UNSET_FLAG(is_connected);
67 }
68 
data_len_updated(struct bt_conn * conn,struct bt_conn_le_data_len_info * info)69 static void data_len_updated(struct bt_conn *conn,
70 			     struct bt_conn_le_data_len_info *info)
71 {
72 	LOG_DBG("Data length updated: TX %d RX %d",
73 		info->tx_max_len,
74 		info->rx_max_len);
75 	SET_FLAG(flag_data_length_updated);
76 }
77 
do_dlu(void)78 static void do_dlu(void)
79 {
80 	int err;
81 	struct bt_conn_le_data_len_param param;
82 
83 	param.tx_max_len = CONFIG_BT_CTLR_DATA_LENGTH_MAX;
84 	param.tx_max_time = 2500;
85 
86 	err = bt_conn_le_data_len_update(dconn, &param);
87 	TEST_ASSERT(err == 0, "Can't update data length (err %d)", err);
88 
89 	WAIT_FOR_FLAG(flag_data_length_updated);
90 }
91 
92 BT_CONN_CB_DEFINE(conn_callbacks) = {
93 	.connected = connected,
94 	.disconnected = disconnected,
95 	.le_data_len_updated = data_len_updated,
96 };
97 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)98 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
99 			 struct net_buf_simple *ad)
100 {
101 	char str[BT_ADDR_LE_STR_LEN];
102 	struct bt_le_conn_param *param;
103 	struct bt_conn *conn;
104 	int err;
105 
106 	err = bt_le_scan_stop();
107 	if (err) {
108 		TEST_FAIL("Stop LE scan failed (err %d)", err);
109 		return;
110 	}
111 
112 	bt_addr_le_to_str(addr, str, sizeof(str));
113 	LOG_DBG("Connecting to %s", str);
114 
115 	param = BT_LE_CONN_PARAM_DEFAULT;
116 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
117 	if (err) {
118 		TEST_FAIL("Create conn failed (err %d)", err);
119 		return;
120 	}
121 }
122 
connect(void)123 static void connect(void)
124 {
125 	int err;
126 	struct bt_le_scan_param scan_param = {
127 		.type = BT_LE_SCAN_TYPE_ACTIVE,
128 		.options = BT_LE_SCAN_OPT_NONE,
129 		.interval = BT_GAP_SCAN_FAST_INTERVAL,
130 		.window = BT_GAP_SCAN_FAST_WINDOW,
131 	};
132 
133 	UNSET_FLAG(is_connected);
134 
135 	err = bt_le_scan_start(&scan_param, device_found);
136 	TEST_ASSERT(!err, "Scanning failed to start (err %d)", err);
137 
138 	LOG_DBG("Central initiating connection...");
139 	WAIT_FOR_FLAG(is_connected);
140 	LOG_INF("Connected as central");
141 
142 	/* No security support on the tinyhost unfortunately */
143 }
144 
written_to(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)145 static ssize_t written_to(struct bt_conn *conn,
146 			  const struct bt_gatt_attr *attr,
147 			  const void *buf,
148 			  uint16_t len,
149 			  uint16_t offset,
150 			  uint8_t flags)
151 {
152 	LOG_INF("written to: handle 0x%x len %d flags 0x%x",
153 		attr->handle,
154 		len,
155 		flags);
156 
157 	LOG_HEXDUMP_DBG(buf, len, "Write data");
158 
159 	if (atomic_get(&nwrites) == 0) {
160 		/* Suspend on the first write, which is an ATT Request */
161 		LOG_INF("suspending HCI TX thread");
162 		bt_conn_suspend_tx(true);
163 	}
164 
165 	atomic_inc(&nwrites);
166 
167 	return len;
168 }
169 
170 #define test_service_uuid                                                                          \
171 	BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf0debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
172 #define test_characteristic_uuid                                                                   \
173 	BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf2debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
174 
175 BT_GATT_SERVICE_DEFINE(test_gatt_service, BT_GATT_PRIMARY_SERVICE(test_service_uuid),
176 		       BT_GATT_CHARACTERISTIC(test_characteristic_uuid,
177 					      (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
178 					       BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE),
179 					      BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
180 					      NULL, written_to, NULL),
181 		       BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),);
182 
notified(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)183 static uint8_t notified(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
184 			const void *data, uint16_t length)
185 {
186 	static uint8_t notification[] = NOTIFICATION_PAYLOAD;
187 	static uint8_t indication[] = INDICATION_PAYLOAD;
188 	bool is_nfy;
189 
190 	TEST_ASSERT(length >= sizeof(indication), "Unexpected data");
191 	TEST_ASSERT(length <= sizeof(notification), "Unexpected data");
192 
193 	LOG_HEXDUMP_DBG(data, length, "HVx data");
194 
195 	is_nfy = memcmp(data, notification, length) == 0;
196 
197 	LOG_INF("%s from 0x%x", is_nfy ? "notified" : "indicated",
198 		params->value_handle);
199 
200 	if (is_nfy) {
201 		atomic_inc(&notifications);
202 	} else {
203 		atomic_inc(&indications);
204 	}
205 
206 	return BT_GATT_ITER_CONTINUE;
207 }
208 
subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)209 static void subscribed(struct bt_conn *conn,
210 		       uint8_t err,
211 		       struct bt_gatt_subscribe_params *params)
212 {
213 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
214 
215 	TEST_ASSERT(params, "params is NULL");
216 
217 	SET_FLAG(is_subscribed);
218 	/* spoiler: tester doesn't really have attributes */
219 	LOG_INF("Subscribed to Tester attribute");
220 }
221 
subscribe(void)222 void subscribe(void)
223 {
224 	int err;
225 
226 	/* Handle values don't matter, as long as they match on the tester */
227 	static struct bt_gatt_subscribe_params params = {
228 		.notify = notified,
229 		.subscribe = subscribed,
230 		.value = BT_GATT_CCC_NOTIFY | BT_GATT_CCC_INDICATE,
231 		.value_handle = HVX_HANDLE,
232 		.ccc_handle = (HVX_HANDLE + 1),
233 	};
234 
235 	err = bt_gatt_subscribe(dconn, &params);
236 	TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
237 
238 	WAIT_FOR_FLAG(is_subscribed);
239 }
240 
send_write_handle(void)241 static void send_write_handle(void)
242 {
243 	int err;
244 	uint16_t handle;
245 	uint8_t data[sizeof(handle)];
246 	const struct bt_gatt_attr *attr = &test_gatt_service.attrs[2];
247 
248 	/* Inform tester which handle it should write to */
249 	handle = bt_gatt_attr_get_handle(attr);
250 	sys_put_le16(handle, data);
251 
252 	err = bt_gatt_notify(dconn, attr, data, sizeof(data));
253 	TEST_ASSERT(!err, "Failed to transmit handle for write (err %d)", err);
254 }
255 
test_procedure_0(void)256 void test_procedure_0(void)
257 {
258 	LOG_DBG("Test start: ATT sequential protocol");
259 	int err;
260 
261 	err = bt_enable(NULL);
262 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
263 	LOG_DBG("Central: Bluetooth initialized.");
264 
265 	/* Test purpose:
266 	 * Test Spec V.3 P.F 3.3.2 Sequential protocol
267 	 *
268 	 * Verify that a Zephyr host server/client combo can process
269 	 * concurrently: one Request, one Indication, multiple
270 	 * Notifications and multiple Commands.
271 	 *
272 	 * To do this, the application on the DUT will purposefully stall the
273 	 * HCI TX thread, ensuring that the responses are not sent until the
274 	 * tester has finished sending everything.
275 	 *
276 	 * Test procedure:
277 	 *
278 	 * [setup]
279 	 * - connect ACL
280 	 * - update data length (tinyhost doens't have recombination)
281 	 * - dut: subscribe to INDICATE and NOTIFY on tester CHRC
282 	 * - dut: send a handle the tester can write to
283 	 *
284 	 * [proc]
285 	 * - tester: send one ATT write request
286 	 * - tester: send one ATT indication
287 	 * - tester: send two ATT notifications
288 	 * - tester: send two ATT commands
289 	 *
290 	 * - dut: handle the REQuest, build & put the RSP PDU on the HCI TX queue
291 	 * - dut: suspend the HCI TX thread
292 	 * - dut: handle the INDication
293 	 * - dut: handle the notifications
294 	 * - dut: handle the (write) commands
295 	 * - dut: resume the TX thread after a short while
296 	 *
297 	 * [verdict]
298 	 * - all procedures complete successfully, no buffer allocation failures
299 	 *   or timeouts.
300 	 */
301 	connect();
302 	subscribe();
303 
304 	do_dlu();
305 
306 	send_write_handle();
307 
308 	WAIT_FOR_VAL(indications, 1);
309 	WAIT_FOR_VAL(notifications, 2);
310 	/* One REQ, two CMDs */
311 	WAIT_FOR_VAL(nwrites, 3);
312 
313 	/* Send RSP to LL */
314 	bt_conn_suspend_tx(false);
315 
316 	TEST_PASS("DUT done");
317 }
318 
319 static const struct bst_test_instance test_to_add[] = {
320 	{
321 		.test_id = "dut",
322 		.test_main_f = test_procedure_0,
323 	},
324 	BSTEST_END_MARKER,
325 };
326 
install(struct bst_test_list * tests)327 static struct bst_test_list *install(struct bst_test_list *tests)
328 {
329 	return bst_add_tests(tests, test_to_add);
330 };
331 
332 bst_test_install_t test_installers[] = {install, NULL};
333 
main(void)334 int main(void)
335 {
336 	bst_main();
337 
338 	return 0;
339 }
340