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