1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/bluetooth/bluetooth.h>
9 #include <zephyr/bluetooth/conn.h>
10 #include <zephyr/bluetooth/att.h>
11 #include <zephyr/bluetooth/l2cap.h>
12 #include <zephyr/logging/log.h>
13 
14 #include "testlib/scan.h"
15 #include "testlib/conn.h"
16 
17 #include "babblekit/flags.h"
18 #include "babblekit/sync.h"
19 #include "babblekit/testcase.h"
20 
21 /* local includes */
22 #include "data.h"
23 
24 LOG_MODULE_REGISTER(central, CONFIG_APP_LOG_LEVEL);
25 
26 static struct bt_l2cap_le_chan le_chan;
27 
sent_cb(struct bt_l2cap_chan * chan)28 static void sent_cb(struct bt_l2cap_chan *chan)
29 {
30 	TEST_FAIL("Tester should not send data");
31 }
32 
recv_cb(struct bt_l2cap_chan * chan,struct net_buf * buf)33 static int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
34 {
35 	LOG_DBG("received %d bytes", buf->len);
36 
37 	return 0;
38 }
39 
l2cap_chan_connected_cb(struct bt_l2cap_chan * chan)40 static void l2cap_chan_connected_cb(struct bt_l2cap_chan *chan)
41 {
42 	LOG_DBG("%p", chan);
43 }
44 
l2cap_chan_disconnected_cb(struct bt_l2cap_chan * chan)45 static void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
46 {
47 	LOG_DBG("%p", chan);
48 }
49 
server_accept_cb(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)50 static int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
51 			    struct bt_l2cap_chan **chan)
52 {
53 	static struct bt_l2cap_chan_ops ops = {
54 		.connected = l2cap_chan_connected_cb,
55 		.disconnected = l2cap_chan_disconnected_cb,
56 		.recv = recv_cb,
57 		.sent = sent_cb,
58 	};
59 
60 	memset(&le_chan, 0, sizeof(le_chan));
61 	le_chan.chan.ops = &ops;
62 	*chan = &le_chan.chan;
63 
64 	return 0;
65 }
66 
l2cap_server_register(bt_security_t sec_level)67 static int l2cap_server_register(bt_security_t sec_level)
68 {
69 	static struct bt_l2cap_server test_l2cap_server = {.accept = server_accept_cb};
70 
71 	test_l2cap_server.psm = 0;
72 	test_l2cap_server.sec_level = sec_level;
73 
74 	int err = bt_l2cap_server_register(&test_l2cap_server);
75 
76 	TEST_ASSERT(err == 0, "Failed to register l2cap server (err %d)", err);
77 
78 	return test_l2cap_server.psm;
79 }
80 
acl_connected(struct bt_conn * conn,uint8_t err)81 static void acl_connected(struct bt_conn *conn, uint8_t err)
82 {
83 	char addr[BT_ADDR_LE_STR_LEN];
84 
85 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
86 
87 	if (err) {
88 		LOG_ERR("Failed to connect to %s (0x%02x)", addr, err);
89 		return;
90 	}
91 
92 	LOG_DBG("Connected to %s", addr);
93 }
94 
acl_disconnected(struct bt_conn * conn,uint8_t reason)95 static void acl_disconnected(struct bt_conn *conn, uint8_t reason)
96 {
97 	char addr[BT_ADDR_LE_STR_LEN];
98 
99 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
100 
101 	LOG_DBG("Disconnected from %s (reason 0x%02x)", addr, reason);
102 }
103 
104 /* Read the comments on `entrypoint_dut()` first. */
entrypoint_central(void)105 void entrypoint_central(void)
106 {
107 	int err;
108 	struct bt_conn *conn = NULL;
109 	bt_addr_le_t dut;
110 	static struct bt_conn_cb central_cb = {
111 		.connected = acl_connected,
112 		.disconnected = acl_disconnected,
113 	};
114 
115 	/* Mark test as in progress. */
116 	TEST_START("central");
117 
118 	/* Initialize Bluetooth */
119 	err = bt_conn_cb_register(&central_cb);
120 	TEST_ASSERT(err == 0, "Can't register callbacks (err %d)", err);
121 
122 	err = bt_enable(NULL);
123 	TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
124 
125 	LOG_DBG("Bluetooth initialized");
126 
127 	int psm = l2cap_server_register(BT_SECURITY_L1);
128 
129 	LOG_DBG("Registered server PSM %x", psm);
130 
131 	/* The device address will not change. Scan only once in order to reduce
132 	 * test time.
133 	 */
134 	err = bt_testlib_scan_find_name(&dut, DUT_NAME);
135 	TEST_ASSERT(!err, "Failed to start scan (err %d)", err);
136 
137 	/* DUT will terminate all devices when it's done. Mark the device as
138 	 * "passed" so bsim doesn't return a nonzero err code when the
139 	 * termination happens.
140 	 */
141 	TEST_PASS("central");
142 
143 	while (true) {
144 		/* Create a connection using that address */
145 		err = bt_testlib_connect(&dut, &conn);
146 		TEST_ASSERT(!err, "Failed to initiate connection (err %d)", err);
147 
148 		LOG_DBG("Connected");
149 
150 		/* Receive in the background */
151 		k_sleep(K_MSEC(1000));
152 
153 		/* Disconnect and destroy connection object */
154 		err = bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
155 		TEST_ASSERT(!err, "Failed to disconnect (err %d)", err);
156 
157 		LOG_DBG("Disconnected");
158 
159 		/* Simulate the central going in and out of range. In the real world, it is unlikely
160 		 * to drop a connection and re-establish it after only a few milliseconds.
161 		 */
162 		k_sleep(K_MSEC(200));
163 	}
164 }
165