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