1 /** @file
2 * @brief Bluetooth BR/EDR shell module
3 *
4 * Provide some Bluetooth shell commands that can be useful to applications.
5 */
6
7 /*
8 * Copyright (c) 2018 Intel Corporation
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #include <errno.h>
14 #include <zephyr/types.h>
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <zephyr/sys/byteorder.h>
19 #include <zephyr/kernel.h>
20
21 #include <zephyr/settings/settings.h>
22
23 #include <zephyr/bluetooth/hci.h>
24 #include <zephyr/bluetooth/bluetooth.h>
25 #include <zephyr/bluetooth/conn.h>
26 #include <zephyr/bluetooth/l2cap.h>
27 #include <zephyr/bluetooth/classic/rfcomm.h>
28 #include <zephyr/bluetooth/classic/sdp.h>
29
30 #include <zephyr/shell/shell.h>
31
32 #include "host/shell/bt.h"
33
34 #if defined(CONFIG_BT_CONN)
35 /* Connection context for BR/EDR legacy pairing in sec mode 3 */
36 static struct bt_conn *pairing_conn;
37 #endif /* CONFIG_BT_CONN */
38
39 #define DATA_BREDR_MTU 48
40
41 NET_BUF_POOL_FIXED_DEFINE(data_pool, 1, DATA_BREDR_MTU, 8, NULL);
42
43 #define SDP_CLIENT_USER_BUF_LEN 512
44 NET_BUF_POOL_FIXED_DEFINE(sdp_client_pool, CONFIG_BT_MAX_CONN,
45 SDP_CLIENT_USER_BUF_LEN, 8, NULL);
46
cmd_auth_pincode(const struct shell * sh,size_t argc,char * argv[])47 static int cmd_auth_pincode(const struct shell *sh,
48 size_t argc, char *argv[])
49 {
50 struct bt_conn *conn;
51 uint8_t max = 16U;
52
53 if (default_conn) {
54 conn = default_conn;
55 } else if (pairing_conn) {
56 conn = pairing_conn;
57 } else {
58 conn = NULL;
59 }
60
61 if (!conn) {
62 shell_print(sh, "Not connected");
63 return -ENOEXEC;
64 }
65
66 if (strlen(argv[1]) > max) {
67 shell_print(sh, "PIN code value invalid - enter max %u "
68 "digits", max);
69 return -ENOEXEC;
70 }
71
72 shell_print(sh, "PIN code \"%s\" applied", argv[1]);
73
74 bt_conn_auth_pincode_entry(conn, argv[1]);
75
76 return 0;
77 }
78
cmd_connect(const struct shell * sh,size_t argc,char * argv[])79 static int cmd_connect(const struct shell *sh, size_t argc, char *argv[])
80 {
81 struct bt_conn *conn;
82 bt_addr_t addr;
83 int err;
84
85 err = bt_addr_from_str(argv[1], &addr);
86 if (err) {
87 shell_print(sh, "Invalid peer address (err %d)", err);
88 return -ENOEXEC;
89 }
90
91 conn = bt_conn_create_br(&addr, BT_BR_CONN_PARAM_DEFAULT);
92 if (!conn) {
93 shell_print(sh, "Connection failed");
94 return -ENOEXEC;
95 }
96
97 shell_print(sh, "Connection pending");
98
99 /* unref connection obj in advance as app user */
100 bt_conn_unref(conn);
101
102 return 0;
103 }
104
br_device_found(const bt_addr_t * addr,int8_t rssi,const uint8_t cod[3],const uint8_t eir[240])105 static void br_device_found(const bt_addr_t *addr, int8_t rssi,
106 const uint8_t cod[3], const uint8_t eir[240])
107 {
108 char br_addr[BT_ADDR_STR_LEN];
109 char name[239];
110 int len = 240;
111
112 (void)memset(name, 0, sizeof(name));
113
114 while (len) {
115 if (len < 2) {
116 break;
117 }
118
119 /* Look for early termination */
120 if (!eir[0]) {
121 break;
122 }
123
124 /* Check if field length is correct */
125 if (eir[0] > len - 1) {
126 break;
127 }
128
129 switch (eir[1]) {
130 case BT_DATA_NAME_SHORTENED:
131 case BT_DATA_NAME_COMPLETE:
132 if (eir[0] > sizeof(name) - 1) {
133 memcpy(name, &eir[2], sizeof(name) - 1);
134 } else {
135 memcpy(name, &eir[2], eir[0] - 1);
136 }
137 break;
138 default:
139 break;
140 }
141
142 /* Parse next AD Structure */
143 len -= eir[0] + 1;
144 eir += eir[0] + 1;
145 }
146
147 bt_addr_to_str(addr, br_addr, sizeof(br_addr));
148
149 shell_print(ctx_shell, "[DEVICE]: %s, RSSI %i %s", br_addr, rssi, name);
150 }
151
152 static struct bt_br_discovery_result br_discovery_results[5];
153
br_discovery_complete(const struct bt_br_discovery_result * results,size_t count)154 static void br_discovery_complete(const struct bt_br_discovery_result *results,
155 size_t count)
156 {
157 size_t i;
158
159 shell_print(ctx_shell, "BR/EDR discovery complete");
160
161 for (i = 0; i < count; i++) {
162 br_device_found(&results[i].addr, results[i].rssi,
163 results[i].cod, results[i].eir);
164 }
165 }
166
167 static struct bt_br_discovery_cb discovery_cb = {
168 .recv = NULL,
169 .timeout = br_discovery_complete,
170 };
171
cmd_discovery(const struct shell * sh,size_t argc,char * argv[])172 static int cmd_discovery(const struct shell *sh, size_t argc, char *argv[])
173 {
174 const char *action;
175
176 action = argv[1];
177 if (!strcmp(action, "on")) {
178 static bool reg_cb = true;
179 struct bt_br_discovery_param param;
180
181 param.limited = false;
182 param.length = 8U;
183
184 if (argc > 2) {
185 param.length = atoi(argv[2]);
186 }
187
188 if (argc > 3 && !strcmp(argv[3], "limited")) {
189 param.limited = true;
190 }
191
192 if (reg_cb) {
193 reg_cb = false;
194 bt_br_discovery_cb_register(&discovery_cb);
195 }
196
197 if (bt_br_discovery_start(¶m, br_discovery_results,
198 ARRAY_SIZE(br_discovery_results)) < 0) {
199 shell_print(sh, "Failed to start discovery");
200 return -ENOEXEC;
201 }
202
203 shell_print(sh, "Discovery started");
204 } else if (!strcmp(action, "off")) {
205 if (bt_br_discovery_stop()) {
206 shell_print(sh, "Failed to stop discovery");
207 return -ENOEXEC;
208 }
209
210 shell_print(sh, "Discovery stopped");
211 } else {
212 shell_help(sh);
213 return SHELL_CMD_HELP_PRINTED;
214 }
215
216 return 0;
217 }
218
l2cap_recv(struct bt_l2cap_chan * chan,struct net_buf * buf)219 static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
220 {
221 shell_print(ctx_shell, "Incoming data channel %p len %u", chan,
222 buf->len);
223
224 return 0;
225 }
226
l2cap_connected(struct bt_l2cap_chan * chan)227 static void l2cap_connected(struct bt_l2cap_chan *chan)
228 {
229 shell_print(ctx_shell, "Channel %p connected", chan);
230 }
231
l2cap_disconnected(struct bt_l2cap_chan * chan)232 static void l2cap_disconnected(struct bt_l2cap_chan *chan)
233 {
234 shell_print(ctx_shell, "Channel %p disconnected", chan);
235 }
236
l2cap_alloc_buf(struct bt_l2cap_chan * chan)237 static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
238 {
239 shell_print(ctx_shell, "Channel %p requires buffer", chan);
240
241 return net_buf_alloc(&data_pool, K_FOREVER);
242 }
243
244 static const struct bt_l2cap_chan_ops l2cap_ops = {
245 .alloc_buf = l2cap_alloc_buf,
246 .recv = l2cap_recv,
247 .connected = l2cap_connected,
248 .disconnected = l2cap_disconnected,
249 };
250
251 static struct bt_l2cap_br_chan l2cap_chan = {
252 .chan.ops = &l2cap_ops,
253 /* Set for now min. MTU */
254 .rx.mtu = 48,
255 };
256
l2cap_accept(struct bt_conn * conn,struct bt_l2cap_server * server,struct bt_l2cap_chan ** chan)257 static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server,
258 struct bt_l2cap_chan **chan)
259 {
260 shell_print(ctx_shell, "Incoming BR/EDR conn %p", conn);
261
262 if (l2cap_chan.chan.conn) {
263 shell_error(ctx_shell, "No channels available");
264 return -ENOMEM;
265 }
266
267 *chan = &l2cap_chan.chan;
268
269 return 0;
270 }
271
272 static struct bt_l2cap_server br_server = {
273 .accept = l2cap_accept,
274 };
275
cmd_l2cap_register(const struct shell * sh,size_t argc,char * argv[])276 static int cmd_l2cap_register(const struct shell *sh,
277 size_t argc, char *argv[])
278 {
279 if (br_server.psm) {
280 shell_print(sh, "Already registered");
281 return -ENOEXEC;
282 }
283
284 br_server.psm = strtoul(argv[1], NULL, 16);
285
286 if (bt_l2cap_br_server_register(&br_server) < 0) {
287 shell_error(sh, "Unable to register psm");
288 br_server.psm = 0U;
289 return -ENOEXEC;
290 }
291
292 shell_print(sh, "L2CAP psm %u registered", br_server.psm);
293
294 return 0;
295 }
296
cmd_discoverable(const struct shell * sh,size_t argc,char * argv[])297 static int cmd_discoverable(const struct shell *sh,
298 size_t argc, char *argv[])
299 {
300 int err;
301 const char *action;
302
303 action = argv[1];
304
305 if (!strcmp(action, "on")) {
306 err = bt_br_set_discoverable(true);
307 } else if (!strcmp(action, "off")) {
308 err = bt_br_set_discoverable(false);
309 } else {
310 shell_help(sh);
311 return SHELL_CMD_HELP_PRINTED;
312 }
313
314 if (err) {
315 shell_print(sh, "BR/EDR set/reset discoverable failed "
316 "(err %d)", err);
317 return -ENOEXEC;
318 }
319
320 shell_print(sh, "BR/EDR set/reset discoverable done");
321
322 return 0;
323 }
324
cmd_connectable(const struct shell * sh,size_t argc,char * argv[])325 static int cmd_connectable(const struct shell *sh,
326 size_t argc, char *argv[])
327 {
328 int err;
329 const char *action;
330
331 action = argv[1];
332
333 if (!strcmp(action, "on")) {
334 err = bt_br_set_connectable(true);
335 } else if (!strcmp(action, "off")) {
336 err = bt_br_set_connectable(false);
337 } else {
338 shell_help(sh);
339 return SHELL_CMD_HELP_PRINTED;
340 }
341
342 if (err) {
343 shell_print(sh, "BR/EDR set/rest connectable failed "
344 "(err %d)", err);
345 return -ENOEXEC;
346 }
347
348 shell_print(sh, "BR/EDR set/reset connectable done");
349
350 return 0;
351 }
352
cmd_oob(const struct shell * sh,size_t argc,char * argv[])353 static int cmd_oob(const struct shell *sh, size_t argc, char *argv[])
354 {
355 char addr[BT_ADDR_STR_LEN];
356 struct bt_br_oob oob;
357 int err;
358
359 err = bt_br_oob_get_local(&oob);
360 if (err) {
361 shell_print(sh, "BR/EDR OOB data failed");
362 return -ENOEXEC;
363 }
364
365 bt_addr_to_str(&oob.addr, addr, sizeof(addr));
366
367 shell_print(sh, "BR/EDR OOB data:");
368 shell_print(sh, " addr %s", addr);
369 return 0;
370 }
371
sdp_hfp_ag_user(struct bt_conn * conn,struct bt_sdp_client_result * result,const struct bt_sdp_discover_params * params)372 static uint8_t sdp_hfp_ag_user(struct bt_conn *conn,
373 struct bt_sdp_client_result *result,
374 const struct bt_sdp_discover_params *params)
375 {
376 char addr[BT_ADDR_STR_LEN];
377 uint16_t param, version;
378 uint16_t features;
379 int err;
380
381 conn_addr_str(conn, addr, sizeof(addr));
382
383 if (result && result->resp_buf) {
384 shell_print(ctx_shell, "SDP HFPAG data@%p (len %u) hint %u from"
385 " remote %s", result->resp_buf,
386 result->resp_buf->len, result->next_record_hint,
387 addr);
388
389 /*
390 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
391 * get HFPAG Server Channel Number operating on RFCOMM protocol.
392 */
393 err = bt_sdp_get_proto_param(result->resp_buf,
394 BT_SDP_PROTO_RFCOMM, ¶m);
395 if (err < 0) {
396 shell_error(ctx_shell, "Error getting Server CN, "
397 "err %d", err);
398 goto done;
399 }
400 shell_print(ctx_shell, "HFPAG Server CN param 0x%04x", param);
401
402 err = bt_sdp_get_profile_version(result->resp_buf,
403 BT_SDP_HANDSFREE_SVCLASS,
404 &version);
405 if (err < 0) {
406 shell_error(ctx_shell, "Error getting profile version, "
407 "err %d", err);
408 goto done;
409 }
410 shell_print(ctx_shell, "HFP version param 0x%04x", version);
411
412 /*
413 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
414 * get profile Supported Features mask.
415 */
416 err = bt_sdp_get_features(result->resp_buf, &features);
417 if (err < 0) {
418 shell_error(ctx_shell, "Error getting HFPAG Features, "
419 "err %d", err);
420 goto done;
421 }
422 shell_print(ctx_shell, "HFPAG Supported Features param 0x%04x",
423 features);
424 } else {
425 shell_print(ctx_shell, "No SDP HFPAG data from remote %s",
426 addr);
427 }
428 done:
429 return BT_SDP_DISCOVER_UUID_CONTINUE;
430 }
431
sdp_a2src_user(struct bt_conn * conn,struct bt_sdp_client_result * result,const struct bt_sdp_discover_params * params)432 static uint8_t sdp_a2src_user(struct bt_conn *conn,
433 struct bt_sdp_client_result *result,
434 const struct bt_sdp_discover_params *params)
435 {
436 char addr[BT_ADDR_STR_LEN];
437 uint16_t param, version;
438 uint16_t features;
439 int err;
440
441 conn_addr_str(conn, addr, sizeof(addr));
442
443 if (result && result->resp_buf) {
444 shell_print(ctx_shell, "SDP A2SRC data@%p (len %u) hint %u from"
445 " remote %s", result->resp_buf,
446 result->resp_buf->len, result->next_record_hint,
447 addr);
448
449 /*
450 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
451 * get A2SRC Server PSM Number.
452 */
453 err = bt_sdp_get_proto_param(result->resp_buf,
454 BT_SDP_PROTO_L2CAP, ¶m);
455 if (err < 0) {
456 shell_error(ctx_shell, "A2SRC PSM Number not found, "
457 "err %d", err);
458 goto done;
459 }
460
461 shell_print(ctx_shell, "A2SRC Server PSM Number param 0x%04x",
462 param);
463
464 /*
465 * Focus to get BT_SDP_ATTR_PROFILE_DESC_LIST attribute item to
466 * get profile version number.
467 */
468 err = bt_sdp_get_profile_version(result->resp_buf,
469 BT_SDP_ADVANCED_AUDIO_SVCLASS,
470 &version);
471 if (err < 0) {
472 shell_error(ctx_shell, "A2SRC version not found, "
473 "err %d", err);
474 goto done;
475 }
476 shell_print(ctx_shell, "A2SRC version param 0x%04x", version);
477
478 /*
479 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
480 * get profile supported features mask.
481 */
482 err = bt_sdp_get_features(result->resp_buf, &features);
483 if (err < 0) {
484 shell_error(ctx_shell, "A2SRC Features not found, "
485 "err %d", err);
486 goto done;
487 }
488 shell_print(ctx_shell, "A2SRC Supported Features param 0x%04x",
489 features);
490 } else {
491 shell_print(ctx_shell, "No SDP A2SRC data from remote %s",
492 addr);
493 }
494 done:
495 return BT_SDP_DISCOVER_UUID_CONTINUE;
496 }
497
498 static struct bt_sdp_discover_params discov_hfpag = {
499 .type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR,
500 .uuid = BT_UUID_DECLARE_16(BT_SDP_HANDSFREE_AGW_SVCLASS),
501 .func = sdp_hfp_ag_user,
502 .pool = &sdp_client_pool,
503 };
504
505 static struct bt_sdp_discover_params discov_a2src = {
506 .type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR,
507 .uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SOURCE_SVCLASS),
508 .func = sdp_a2src_user,
509 .pool = &sdp_client_pool,
510 };
511
512 static struct bt_sdp_discover_params discov;
513
cmd_sdp_find_record(const struct shell * sh,size_t argc,char * argv[])514 static int cmd_sdp_find_record(const struct shell *sh,
515 size_t argc, char *argv[])
516 {
517 int err;
518 const char *action;
519
520 if (!default_conn) {
521 shell_print(sh, "Not connected");
522 return -ENOEXEC;
523 }
524
525 action = argv[1];
526
527 if (!strcmp(action, "HFPAG")) {
528 discov = discov_hfpag;
529 } else if (!strcmp(action, "A2SRC")) {
530 discov = discov_a2src;
531 } else {
532 shell_help(sh);
533 return SHELL_CMD_HELP_PRINTED;
534 }
535
536 shell_print(sh, "SDP UUID \'%s\' gets applied", action);
537
538 err = bt_sdp_discover(default_conn, &discov);
539 if (err) {
540 shell_error(sh, "SDP discovery failed: err %d", err);
541 return -ENOEXEC;
542 }
543
544 shell_print(sh, "SDP discovery started");
545
546 return 0;
547 }
548
549 #define HELP_NONE "[none]"
550 #define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"
551
552 SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds,
553 SHELL_CMD_ARG(auth-pincode, NULL, "<pincode>", cmd_auth_pincode, 2, 0),
554 SHELL_CMD_ARG(connect, NULL, "<address>", cmd_connect, 2, 0),
555 SHELL_CMD_ARG(discovery, NULL,
556 "<value: on, off> [length: 1-48] [mode: limited]",
557 cmd_discovery, 2, 2),
558 SHELL_CMD_ARG(iscan, NULL, "<value: on, off>", cmd_discoverable, 2, 0),
559 SHELL_CMD_ARG(l2cap-register, NULL, "<psm>", cmd_l2cap_register, 2, 0),
560 SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0),
561 SHELL_CMD_ARG(pscan, NULL, "<value: on, off>", cmd_connectable, 2, 0),
562 SHELL_CMD_ARG(sdp-find, NULL, "<HFPAG>", cmd_sdp_find_record, 2, 0),
563 SHELL_SUBCMD_SET_END
564 );
565
cmd_br(const struct shell * sh,size_t argc,char ** argv)566 static int cmd_br(const struct shell *sh, size_t argc, char **argv)
567 {
568 if (argc == 1) {
569 shell_help(sh);
570 return SHELL_CMD_HELP_PRINTED;
571 }
572
573 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
574
575 return -ENOEXEC;
576 }
577
578 SHELL_CMD_ARG_REGISTER(br, &br_cmds, "Bluetooth BR/EDR shell commands", cmd_br,
579 1, 1);
580