1 /* smp_br_bonding.c - Bluetooth classic SMP bonding smoke test */
2 
3 /*
4  * Copyright 2025 NXP
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <errno.h>
10 #include <zephyr/types.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/kernel.h>
16 
17 #include <zephyr/settings/settings.h>
18 
19 #include <zephyr/bluetooth/hci.h>
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/l2cap.h>
23 
24 #include <zephyr/shell/shell.h>
25 
26 #include "host/shell/bt.h"
27 #include "common/bt_shell_private.h"
28 
29 #define DATA_BREDR_MTU 48
30 
31 NET_BUF_POOL_FIXED_DEFINE(data_rx_pool, 1, DATA_BREDR_MTU, 8, NULL);
32 
33 struct l2cap_br_chan {
34 	bool active;
35 	struct bt_l2cap_br_chan chan;
36 };
37 
38 #define APPL_L2CAP_CONNECTION_MAX_COUNT 1
39 static struct l2cap_br_chan l2cap_chans[APPL_L2CAP_CONNECTION_MAX_COUNT];
40 static struct bt_l2cap_server l2cap_servers[APPL_L2CAP_CONNECTION_MAX_COUNT];
41 
l2cap_recv(struct bt_l2cap_chan * chan,struct net_buf * buf)42 static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
43 {
44 	struct l2cap_br_chan *br_chan = CONTAINER_OF(
45 		CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan), struct l2cap_br_chan, chan);
46 
47 	bt_shell_print("Incoming data channel %d len %u", ARRAY_INDEX(l2cap_chans, br_chan),
48 		       buf->len);
49 
50 	if (buf->len) {
51 		bt_shell_hexdump(buf->data, buf->len);
52 	}
53 
54 	return 0;
55 }
56 
l2cap_connected(struct bt_l2cap_chan * chan)57 static void l2cap_connected(struct bt_l2cap_chan *chan)
58 {
59 	struct l2cap_br_chan *br_chan = CONTAINER_OF(
60 		CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan), struct l2cap_br_chan, chan);
61 
62 	bt_shell_print("Channel %d connected", ARRAY_INDEX(l2cap_chans, br_chan));
63 }
64 
l2cap_disconnected(struct bt_l2cap_chan * chan)65 static void l2cap_disconnected(struct bt_l2cap_chan *chan)
66 {
67 	struct l2cap_br_chan *br_chan = CONTAINER_OF(
68 		CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan), struct l2cap_br_chan, chan);
69 
70 	br_chan->active = false;
71 	bt_shell_print("Channel %d disconnected", ARRAY_INDEX(l2cap_chans, br_chan));
72 }
73 
l2cap_alloc_buf(struct bt_l2cap_chan * chan)74 static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
75 {
76 	bt_shell_print("Channel %p requires buffer", chan);
77 
78 	return net_buf_alloc(&data_rx_pool, K_NO_WAIT);
79 }
80 
81 static const struct bt_l2cap_chan_ops l2cap_ops = {
82 	.alloc_buf = l2cap_alloc_buf,
83 	.recv = l2cap_recv,
84 	.connected = l2cap_connected,
85 	.disconnected = l2cap_disconnected,
86 };
87 
l2cap_alloc_chan(void)88 static struct l2cap_br_chan *l2cap_alloc_chan(void)
89 {
90 	ARRAY_FOR_EACH(l2cap_chans, index) {
91 		if (l2cap_chans[index].active == false) {
92 			l2cap_chans[index].active = true;
93 			l2cap_chans[index].chan.chan.ops = &l2cap_ops;
94 			l2cap_chans[index].chan.rx.mtu = DATA_BREDR_MTU;
95 			return &l2cap_chans[index];
96 		}
97 	}
98 	return NULL;
99 }
100 
l2cap_accept(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)101 static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server,
102 			struct bt_l2cap_chan **chan)
103 {
104 	struct l2cap_br_chan *br_chan;
105 
106 	bt_shell_print("Incoming BR/EDR conn %p", conn);
107 
108 	br_chan = l2cap_alloc_chan();
109 	if (br_chan == NULL) {
110 		bt_shell_error("No channels available");
111 		return -ENOMEM;
112 	}
113 
114 	*chan = &br_chan->chan.chan;
115 
116 	return 0;
117 }
118 
l2cap_alloc_server(uint16_t psm)119 static struct bt_l2cap_server *l2cap_alloc_server(uint16_t psm)
120 {
121 	ARRAY_FOR_EACH(l2cap_servers, index) {
122 		if (l2cap_servers[index].psm == 0) {
123 			l2cap_servers[index].psm = psm;
124 			l2cap_servers[index].accept = l2cap_accept;
125 			return &l2cap_servers[index];
126 		}
127 	}
128 	return NULL;
129 }
130 
cmd_l2cap_register(const struct shell * sh,size_t argc,char * argv[])131 static int cmd_l2cap_register(const struct shell *sh, size_t argc, char *argv[])
132 {
133 	uint16_t psm = strtoul(argv[1], NULL, 16);
134 	struct bt_l2cap_server *br_server;
135 
136 	ARRAY_FOR_EACH(l2cap_servers, index) {
137 		if (l2cap_servers[index].psm == psm) {
138 			shell_print(sh, "Already registered");
139 			return -ENOEXEC;
140 		}
141 	}
142 
143 	br_server = l2cap_alloc_server(psm);
144 	if (br_server == NULL) {
145 		shell_error(sh, "No servers available");
146 		return -ENOMEM;
147 	}
148 
149 	if ((argc > 3) && (strcmp(argv[2], "sec") == 0)) {
150 		br_server->sec_level = strtoul(argv[3], NULL, 16);
151 	} else {
152 		br_server->sec_level = BT_SECURITY_L1;
153 	}
154 
155 	if (bt_l2cap_br_server_register(br_server) < 0) {
156 		br_server->psm = 0U;
157 		shell_error(sh, "Unable to register psm");
158 		return -ENOEXEC;
159 	}
160 
161 	shell_print(sh, "L2CAP psm %u registered", br_server->psm);
162 
163 	return 0;
164 }
165 
cmd_l2cap_connect(const struct shell * sh,size_t argc,char * argv[])166 static int cmd_l2cap_connect(const struct shell *sh, size_t argc, char *argv[])
167 {
168 	int err;
169 	struct bt_conn_info info;
170 	struct l2cap_br_chan *br_chan;
171 	uint16_t psm;
172 
173 	if (default_conn == NULL) {
174 		shell_error(sh, "Not connected");
175 		return -ENOEXEC;
176 	}
177 
178 	br_chan = l2cap_alloc_chan();
179 	if (br_chan == NULL) {
180 		shell_error(sh, "No channels available");
181 		br_chan->active = false;
182 		return -ENOMEM;
183 	}
184 
185 	err = bt_conn_get_info(default_conn, &info);
186 	if ((err < 0) || (info.type != BT_CONN_TYPE_BR)) {
187 		shell_error(sh, "Invalid conn type");
188 		br_chan->active = false;
189 		return -ENOEXEC;
190 	}
191 
192 	psm = strtoul(argv[1], NULL, 16);
193 
194 	if ((argc > 3) && (strcmp(argv[2], "sec") == 0)) {
195 		br_chan->chan.required_sec_level = strtoul(argv[3], NULL, 16);
196 	} else {
197 		br_chan->chan.required_sec_level = BT_SECURITY_L1;
198 	}
199 
200 	err = bt_l2cap_chan_connect(default_conn, &br_chan->chan.chan, psm);
201 	if (err < 0) {
202 		br_chan->active = false;
203 		shell_error(sh, "Unable to connect to psm %u (err %d)", psm, err);
204 	} else {
205 		shell_print(sh, "L2CAP connection pending");
206 	}
207 
208 	return err;
209 }
210 
cmd_l2cap_disconnect(const struct shell * sh,size_t argc,char * argv[])211 static int cmd_l2cap_disconnect(const struct shell *sh, size_t argc, char *argv[])
212 {
213 	int err;
214 	uint8_t id;
215 
216 	id = strtoul(argv[1], NULL, 16);
217 	if ((id >= ARRAY_SIZE(l2cap_chans)) || (!l2cap_chans[id].active)) {
218 		shell_print(sh, "channel %d not connected", id);
219 		return -ENOEXEC;
220 	}
221 
222 	err = bt_l2cap_chan_disconnect(&l2cap_chans[id].chan.chan);
223 	if (err) {
224 		shell_error(sh, "Unable to disconnect: %u", -err);
225 		return err;
226 	}
227 
228 	return 0;
229 }
230 
cmd_set_security(const struct shell * sh,size_t argc,char * argv[])231 static int cmd_set_security(const struct shell *sh, size_t argc, char *argv[])
232 {
233 	uint16_t psm = strtoul(argv[1], NULL, 16);
234 	uint8_t sec = strtoul(argv[2], NULL, 16);
235 
236 	if (sec > BT_SECURITY_L4) {
237 		shell_error(sh, "Invalid security level: %d", sec);
238 		return -ENOEXEC;
239 	}
240 
241 	ARRAY_FOR_EACH(l2cap_servers, index) {
242 		if (l2cap_servers[index].psm == psm) {
243 			l2cap_servers[index].sec_level = sec;
244 			shell_print(sh, "L2CAP psm %u security level %u", psm, sec);
245 			return 0;
246 		}
247 	}
248 
249 	shell_error(sh, "L2CAP psm %u not registered", psm);
250 	return -ENOEXEC;
251 }
252 
253 SHELL_STATIC_SUBCMD_SET_CREATE(
254 	l2cap_br_cmds,
255 	SHELL_CMD_ARG(register, NULL, "<psm> [sec] [sec: 0 - 4]", cmd_l2cap_register, 2, 2),
256 	SHELL_CMD_ARG(connect, NULL, "<psm> [sec] [sec: 0 - 4]", cmd_l2cap_connect, 2, 2),
257 	SHELL_CMD_ARG(disconnect, NULL, "<id>", cmd_l2cap_disconnect, 2, 0),
258 	SHELL_CMD_ARG(security, NULL, "<psm> <security level: 0 - 4>", cmd_set_security, 3, 0),
259 	SHELL_SUBCMD_SET_END);
260 
cmd_default_handler(const struct shell * sh,size_t argc,char ** argv)261 static int cmd_default_handler(const struct shell *sh, size_t argc, char **argv)
262 {
263 	if (argc == 1) {
264 		shell_help(sh);
265 		return SHELL_CMD_HELP_PRINTED;
266 	}
267 
268 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
269 
270 	return -EINVAL;
271 }
272 
273 SHELL_CMD_REGISTER(l2cap_br, &l2cap_br_cmds, "Bluetooth classic l2cap shell commands",
274 		   cmd_default_handler);
275