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 <sys/byteorder.h>
19 #include <zephyr.h>
20
21 #include <settings/settings.h>
22
23 #include <bluetooth/hci.h>
24 #include <bluetooth/bluetooth.h>
25 #include <bluetooth/conn.h>
26 #include <bluetooth/l2cap.h>
27 #include <bluetooth/rfcomm.h>
28 #include <bluetooth/sdp.h>
29
30 #include <shell/shell.h>
31
32 #include "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, 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, 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 0;
64 }
65
66 if (strlen(argv[1]) > max) {
67 shell_print(sh, "PIN code value invalid - enter max %u "
68 "digits", max);
69 return 0;
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 } else {
95
96 shell_print(sh, "Connection pending");
97
98 /* unref connection obj in advance as app user */
99 bt_conn_unref(conn);
100 }
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(struct bt_br_discovery_result * results,size_t count)154 static void br_discovery_complete(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
cmd_discovery(const struct shell * sh,size_t argc,char * argv[])167 static int cmd_discovery(const struct shell *sh, size_t argc, char *argv[])
168 {
169 const char *action;
170
171 action = argv[1];
172 if (!strcmp(action, "on")) {
173 struct bt_br_discovery_param param;
174
175 param.limited = false;
176 param.length = 8U;
177
178 if (argc > 2) {
179 param.length = atoi(argv[2]);
180 }
181
182 if (argc > 3 && !strcmp(argv[3], "limited")) {
183 param.limited = true;
184 }
185
186 if (bt_br_discovery_start(¶m, br_discovery_results,
187 ARRAY_SIZE(br_discovery_results),
188 br_discovery_complete) < 0) {
189 shell_print(sh, "Failed to start discovery");
190 return 0;
191 }
192
193 shell_print(sh, "Discovery started");
194 } else if (!strcmp(action, "off")) {
195 if (bt_br_discovery_stop()) {
196 shell_print(sh, "Failed to stop discovery");
197 return 0;
198 }
199
200 shell_print(sh, "Discovery stopped");
201 } else {
202 shell_help(sh);
203 }
204
205 return 0;
206 }
207
l2cap_recv(struct bt_l2cap_chan * chan,struct net_buf * buf)208 static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
209 {
210 shell_print(ctx_shell, "Incoming data channel %p len %u", chan,
211 buf->len);
212
213 return 0;
214 }
215
l2cap_connected(struct bt_l2cap_chan * chan)216 static void l2cap_connected(struct bt_l2cap_chan *chan)
217 {
218 shell_print(ctx_shell, "Channel %p connected", chan);
219 }
220
l2cap_disconnected(struct bt_l2cap_chan * chan)221 static void l2cap_disconnected(struct bt_l2cap_chan *chan)
222 {
223 shell_print(ctx_shell, "Channel %p disconnected", chan);
224 }
225
l2cap_alloc_buf(struct bt_l2cap_chan * chan)226 static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
227 {
228 shell_print(ctx_shell, "Channel %p requires buffer", chan);
229
230 return net_buf_alloc(&data_pool, K_FOREVER);
231 }
232
233 static const struct bt_l2cap_chan_ops l2cap_ops = {
234 .alloc_buf = l2cap_alloc_buf,
235 .recv = l2cap_recv,
236 .connected = l2cap_connected,
237 .disconnected = l2cap_disconnected,
238 };
239
240 static struct bt_l2cap_br_chan l2cap_chan = {
241 .chan.ops = &l2cap_ops,
242 /* Set for now min. MTU */
243 .rx.mtu = 48,
244 };
245
l2cap_accept(struct bt_conn * conn,struct bt_l2cap_chan ** chan)246 static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
247 {
248 shell_print(ctx_shell, "Incoming BR/EDR conn %p", conn);
249
250 if (l2cap_chan.chan.conn) {
251 shell_error(ctx_shell, "No channels available");
252 return -ENOMEM;
253 }
254
255 *chan = &l2cap_chan.chan;
256
257 return 0;
258 }
259
260 static struct bt_l2cap_server br_server = {
261 .accept = l2cap_accept,
262 };
263
cmd_l2cap_register(const struct shell * sh,size_t argc,char * argv[])264 static int cmd_l2cap_register(const struct shell *sh,
265 size_t argc, char *argv[])
266 {
267 if (br_server.psm) {
268 shell_print(sh, "Already registered");
269 return 0;
270 }
271
272 br_server.psm = strtoul(argv[1], NULL, 16);
273
274 if (bt_l2cap_br_server_register(&br_server) < 0) {
275 shell_error(sh, "Unable to register psm");
276 br_server.psm = 0U;
277 return -ENOEXEC;
278 } else {
279 shell_print(sh, "L2CAP psm %u registered", br_server.psm);
280 }
281
282 return 0;
283 }
284
cmd_discoverable(const struct shell * sh,size_t argc,char * argv[])285 static int cmd_discoverable(const struct shell *sh,
286 size_t argc, char *argv[])
287 {
288 int err;
289 const char *action;
290
291 action = argv[1];
292
293 if (!strcmp(action, "on")) {
294 err = bt_br_set_discoverable(true);
295 } else if (!strcmp(action, "off")) {
296 err = bt_br_set_discoverable(false);
297 } else {
298 shell_help(sh);
299 return 0;
300 }
301
302 if (err) {
303 shell_print(sh, "BR/EDR set/reset discoverable failed "
304 "(err %d)", err);
305 return -ENOEXEC;
306 } else {
307 shell_print(sh, "BR/EDR set/reset discoverable done");
308 }
309
310 return 0;
311 }
312
cmd_connectable(const struct shell * sh,size_t argc,char * argv[])313 static int cmd_connectable(const struct shell *sh,
314 size_t argc, char *argv[])
315 {
316 int err;
317 const char *action;
318
319 action = argv[1];
320
321 if (!strcmp(action, "on")) {
322 err = bt_br_set_connectable(true);
323 } else if (!strcmp(action, "off")) {
324 err = bt_br_set_connectable(false);
325 } else {
326 shell_help(sh);
327 return 0;
328 }
329
330 if (err) {
331 shell_print(sh, "BR/EDR set/rest connectable failed "
332 "(err %d)", err);
333 return -ENOEXEC;
334 } else {
335 shell_print(sh, "BR/EDR set/reset connectable done");
336 }
337
338 return 0;
339 }
340
cmd_oob(const struct shell * sh,size_t argc,char * argv[])341 static int cmd_oob(const struct shell *sh, size_t argc, char *argv[])
342 {
343 char addr[BT_ADDR_STR_LEN];
344 struct bt_br_oob oob;
345 int err;
346
347 err = bt_br_oob_get_local(&oob);
348 if (err) {
349 shell_print(sh, "BR/EDR OOB data failed");
350 return -ENOEXEC;
351 }
352
353 bt_addr_to_str(&oob.addr, addr, sizeof(addr));
354
355 shell_print(sh, "BR/EDR OOB data:");
356 shell_print(sh, " addr %s", addr);
357 return 0;
358 }
359
sdp_hfp_ag_user(struct bt_conn * conn,struct bt_sdp_client_result * result)360 static uint8_t sdp_hfp_ag_user(struct bt_conn *conn,
361 struct bt_sdp_client_result *result)
362 {
363 char addr[BT_ADDR_STR_LEN];
364 uint16_t param, version;
365 uint16_t features;
366 int res;
367
368 conn_addr_str(conn, addr, sizeof(addr));
369
370 if (result) {
371 shell_print(ctx_shell, "SDP HFPAG data@%p (len %u) hint %u from"
372 " remote %s", result->resp_buf,
373 result->resp_buf->len, result->next_record_hint,
374 addr);
375
376 /*
377 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
378 * get HFPAG Server Channel Number operating on RFCOMM protocol.
379 */
380 res = bt_sdp_get_proto_param(result->resp_buf,
381 BT_SDP_PROTO_RFCOMM, ¶m);
382 if (res < 0) {
383 shell_error(ctx_shell, "Error getting Server CN, "
384 "err %d", res);
385 goto done;
386 }
387 shell_print(ctx_shell, "HFPAG Server CN param 0x%04x", param);
388
389 res = bt_sdp_get_profile_version(result->resp_buf,
390 BT_SDP_HANDSFREE_SVCLASS,
391 &version);
392 if (res < 0) {
393 shell_error(ctx_shell, "Error getting profile version, "
394 "err %d", res);
395 goto done;
396 }
397 shell_print(ctx_shell, "HFP version param 0x%04x", version);
398
399 /*
400 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
401 * get profile Supported Features mask.
402 */
403 res = bt_sdp_get_features(result->resp_buf, &features);
404 if (res < 0) {
405 shell_error(ctx_shell, "Error getting HFPAG Features, "
406 "err %d", res);
407 goto done;
408 }
409 shell_print(ctx_shell, "HFPAG Supported Features param 0x%04x",
410 features);
411 } else {
412 shell_print(ctx_shell, "No SDP HFPAG data from remote %s",
413 addr);
414 }
415 done:
416 return BT_SDP_DISCOVER_UUID_CONTINUE;
417 }
418
sdp_a2src_user(struct bt_conn * conn,struct bt_sdp_client_result * result)419 static uint8_t sdp_a2src_user(struct bt_conn *conn,
420 struct bt_sdp_client_result *result)
421 {
422 char addr[BT_ADDR_STR_LEN];
423 uint16_t param, version;
424 uint16_t features;
425 int res;
426
427 conn_addr_str(conn, addr, sizeof(addr));
428
429 if (result) {
430 shell_print(ctx_shell, "SDP A2SRC data@%p (len %u) hint %u from"
431 " remote %s", result->resp_buf,
432 result->resp_buf->len, result->next_record_hint,
433 addr);
434
435 /*
436 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
437 * get A2SRC Server PSM Number.
438 */
439 res = bt_sdp_get_proto_param(result->resp_buf,
440 BT_SDP_PROTO_L2CAP, ¶m);
441 if (res < 0) {
442 shell_error(ctx_shell, "A2SRC PSM Number not found, "
443 "err %d", res);
444 goto done;
445 }
446
447 shell_print(ctx_shell, "A2SRC Server PSM Number param 0x%04x",
448 param);
449
450 /*
451 * Focus to get BT_SDP_ATTR_PROFILE_DESC_LIST attribute item to
452 * get profile version number.
453 */
454 res = bt_sdp_get_profile_version(result->resp_buf,
455 BT_SDP_ADVANCED_AUDIO_SVCLASS,
456 &version);
457 if (res < 0) {
458 shell_error(ctx_shell, "A2SRC version not found, "
459 "err %d", res);
460 goto done;
461 }
462 shell_print(ctx_shell, "A2SRC version param 0x%04x", version);
463
464 /*
465 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
466 * get profile supported features mask.
467 */
468 res = bt_sdp_get_features(result->resp_buf, &features);
469 if (res < 0) {
470 shell_error(ctx_shell, "A2SRC Features not found, "
471 "err %d", res);
472 goto done;
473 }
474 shell_print(ctx_shell, "A2SRC Supported Features param 0x%04x",
475 features);
476 } else {
477 shell_print(ctx_shell, "No SDP A2SRC data from remote %s",
478 addr);
479 }
480 done:
481 return BT_SDP_DISCOVER_UUID_CONTINUE;
482 }
483
484 static struct bt_sdp_discover_params discov_hfpag = {
485 .uuid = BT_UUID_DECLARE_16(BT_SDP_HANDSFREE_AGW_SVCLASS),
486 .func = sdp_hfp_ag_user,
487 .pool = &sdp_client_pool,
488 };
489
490 static struct bt_sdp_discover_params discov_a2src = {
491 .uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SOURCE_SVCLASS),
492 .func = sdp_a2src_user,
493 .pool = &sdp_client_pool,
494 };
495
496 static struct bt_sdp_discover_params discov;
497
cmd_sdp_find_record(const struct shell * sh,size_t argc,char * argv[])498 static int cmd_sdp_find_record(const struct shell *sh,
499 size_t argc, char *argv[])
500 {
501 int res;
502 const char *action;
503
504 if (!default_conn) {
505 shell_print(sh, "Not connected");
506 return 0;
507 }
508
509 action = argv[1];
510
511 if (!strcmp(action, "HFPAG")) {
512 discov = discov_hfpag;
513 } else if (!strcmp(action, "A2SRC")) {
514 discov = discov_a2src;
515 } else {
516 shell_help(sh);
517 return 0;
518 }
519
520 shell_print(sh, "SDP UUID \'%s\' gets applied", action);
521
522 res = bt_sdp_discover(default_conn, &discov);
523 if (res) {
524 shell_error(sh, "SDP discovery failed: result %d", res);
525 return -ENOEXEC;
526 } else {
527 shell_print(sh, "SDP discovery started");
528 }
529
530 return 0;
531 }
532
533 #define HELP_NONE "[none]"
534 #define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"
535
536 SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds,
537 SHELL_CMD_ARG(auth-pincode, NULL, "<pincode>", cmd_auth_pincode, 2, 0),
538 SHELL_CMD_ARG(connect, NULL, "<address>", cmd_connect, 2, 0),
539 SHELL_CMD_ARG(discovery, NULL,
540 "<value: on, off> [length: 1-48] [mode: limited]",
541 cmd_discovery, 2, 2),
542 SHELL_CMD_ARG(iscan, NULL, "<value: on, off>", cmd_discoverable, 2, 0),
543 SHELL_CMD_ARG(l2cap-register, NULL, "<psm>", cmd_l2cap_register, 2, 0),
544 SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0),
545 SHELL_CMD_ARG(pscan, NULL, "<value: on, off>", cmd_connectable, 2, 0),
546 SHELL_CMD_ARG(sdp-find, NULL, "<HFPAG>", cmd_sdp_find_record, 2, 0),
547 SHELL_SUBCMD_SET_END
548 );
549
cmd_br(const struct shell * sh,size_t argc,char ** argv)550 static int cmd_br(const struct shell *sh, size_t argc, char **argv)
551 {
552 if (argc == 1) {
553 shell_help(sh);
554 /* shell returns 1 when help is printed */
555 return 1;
556 }
557
558 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
559
560 return -ENOEXEC;
561 }
562
563 SHELL_CMD_ARG_REGISTER(br, &br_cmds, "Bluetooth BR/EDR shell commands", cmd_br,
564 1, 1);
565