1 /** @file
2 * @brief Bluetooth GATT shell functions
3 *
4 */
5
6 /*
7 * Copyright (c) 2017 Intel Corporation
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #include <errno.h>
13 #include <stddef.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include <zephyr/bluetooth/addr.h>
19 #include <zephyr/bluetooth/att.h>
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/kernel.h>
25 #include <zephyr/shell/shell.h>
26 #include <zephyr/shell/shell_string_conv.h>
27 #include <zephyr/sys/__assert.h>
28 #include <zephyr/sys/byteorder.h>
29 #include <zephyr/sys/time_units.h>
30 #include <zephyr/sys_clock.h>
31 #include <zephyr/sys/util.h>
32 #include <sys/types.h>
33
34 #include "common/bt_shell_private.h"
35 #include "host/shell/bt.h"
36
37 #if defined(CONFIG_BT_GATT_CLIENT) || defined(CONFIG_BT_GATT_DYNAMIC_DB)
38 extern uint8_t selected_id;
39
40 static struct write_stats {
41 uint32_t count;
42 uint32_t len;
43 uint32_t total;
44 uint32_t rate;
45 } write_stats;
46
update_write_stats(uint16_t len)47 static void update_write_stats(uint16_t len)
48 {
49 static uint32_t cycle_stamp;
50 uint32_t delta;
51
52 delta = k_cycle_get_32() - cycle_stamp;
53 delta = (uint32_t)k_cyc_to_ns_floor64(delta);
54
55 if (!delta) {
56 delta = 1;
57 }
58
59 write_stats.count++;
60 write_stats.total += len;
61
62 /* if last data rx-ed was greater than 1 second in the past,
63 * reset the metrics.
64 */
65 if (delta > NSEC_PER_SEC) {
66 write_stats.len = 0U;
67 write_stats.rate = 0U;
68 cycle_stamp = k_cycle_get_32();
69 } else {
70 write_stats.len += len;
71 write_stats.rate = ((uint64_t)write_stats.len << 3) *
72 NSEC_PER_SEC / delta;
73 }
74 }
75
76 #if defined(CONFIG_BT_EATT)
77 #define SET_CHAN_OPT_ANY(params) (params).chan_opt = BT_ATT_CHAN_OPT_NONE
78 #else
79 #define SET_CHAN_OPT_ANY(params)
80 #endif /* CONFIG_BT_EATT */
81
print_write_stats(void)82 static void print_write_stats(void)
83 {
84 bt_shell_print("Write #%u: %u bytes (%u bps)",
85 write_stats.count, write_stats.total, write_stats.rate);
86 }
87 #endif /* CONFIG_BT_GATT_CLIENT || CONFIG_BT_GATT_DYNAMIC_DB */
88
89 #if defined(CONFIG_BT_GATT_CLIENT)
reset_write_stats(void)90 static void reset_write_stats(void)
91 {
92 memset(&write_stats, 0, sizeof(write_stats));
93 }
94
95 /* This variable is write-locked when `(exchange_params.func != NULL)`.
96 * Must be zero-initialized when unlocked.
97 */
98 static struct bt_gatt_exchange_params exchange_params;
99
exchange_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_exchange_params * params)100 static void exchange_func(struct bt_conn *conn, uint8_t err,
101 struct bt_gatt_exchange_params *params)
102 {
103 bt_shell_print("Exchange %s", err == 0U ? "successful" : "failed");
104
105 /* Release global `exchange_params`. */
106 __ASSERT_NO_MSG(params == &exchange_params);
107 (void)memset(params, 0, sizeof(*params));
108 }
109
cmd_exchange_mtu(const struct shell * sh,size_t argc,char * argv[])110 static int cmd_exchange_mtu(const struct shell *sh,
111 size_t argc, char *argv[])
112 {
113 int err;
114
115 if (!default_conn) {
116 shell_print(sh, "Not connected");
117 return -ENOEXEC;
118 }
119
120 if (exchange_params.func) {
121 shell_print(sh, "Shell command busy. A previous invocation is in progress.");
122 return -EBUSY;
123 }
124
125 exchange_params.func = exchange_func;
126
127 err = bt_gatt_exchange_mtu(default_conn, &exchange_params);
128 if (err) {
129 /* Release global `exchange_params`. */
130 exchange_params.func = NULL;
131 }
132
133 if (err == -EALREADY) {
134 shell_print(sh, "Already exchanged");
135 } else if (err) {
136 shell_print(sh, "Exchange failed (err %d)", err);
137 } else {
138 shell_print(sh, "Exchange pending");
139 }
140
141 return err;
142 }
143
144 static struct bt_gatt_discover_params discover_params;
145 static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
146
print_chrc_props(uint8_t properties)147 static void print_chrc_props(uint8_t properties)
148 {
149 bt_shell_print("Properties: ");
150
151 if (properties & BT_GATT_CHRC_BROADCAST) {
152 bt_shell_print("[bcast]");
153 }
154
155 if (properties & BT_GATT_CHRC_READ) {
156 bt_shell_print("[read]");
157 }
158
159 if (properties & BT_GATT_CHRC_WRITE) {
160 bt_shell_print("[write]");
161 }
162
163 if (properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) {
164 bt_shell_print("[write w/w rsp]");
165 }
166
167 if (properties & BT_GATT_CHRC_NOTIFY) {
168 bt_shell_print("[notify]");
169 }
170
171 if (properties & BT_GATT_CHRC_INDICATE) {
172 bt_shell_print("[indicate]");
173 }
174
175 if (properties & BT_GATT_CHRC_EXT_PROP) {
176 bt_shell_print("[ext prop]");
177 }
178
179 bt_shell_print("");
180 }
181
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)182 static uint8_t discover_func(struct bt_conn *conn,
183 const struct bt_gatt_attr *attr,
184 struct bt_gatt_discover_params *params)
185 {
186 struct bt_gatt_service_val *gatt_service;
187 struct bt_gatt_chrc *gatt_chrc;
188 struct bt_gatt_include *gatt_include;
189 char str[BT_UUID_STR_LEN];
190
191 if (!attr) {
192 bt_shell_print("Discover complete");
193 (void)memset(params, 0, sizeof(*params));
194 return BT_GATT_ITER_STOP;
195 }
196
197 switch (params->type) {
198 case BT_GATT_DISCOVER_SECONDARY:
199 case BT_GATT_DISCOVER_PRIMARY:
200 gatt_service = attr->user_data;
201 bt_uuid_to_str(gatt_service->uuid, str, sizeof(str));
202 bt_shell_print("Service %s found: start handle %x, end_handle %x",
203 str, attr->handle, gatt_service->end_handle);
204 break;
205 case BT_GATT_DISCOVER_CHARACTERISTIC:
206 gatt_chrc = attr->user_data;
207 bt_uuid_to_str(gatt_chrc->uuid, str, sizeof(str));
208 bt_shell_print("Characteristic %s found: handle %x",
209 str, attr->handle);
210 print_chrc_props(gatt_chrc->properties);
211 break;
212 case BT_GATT_DISCOVER_INCLUDE:
213 gatt_include = attr->user_data;
214 bt_uuid_to_str(gatt_include->uuid, str, sizeof(str));
215 bt_shell_print("Include %s found: handle %x, start %x, end %x", str, attr->handle,
216 gatt_include->start_handle, gatt_include->end_handle);
217 break;
218 default:
219 bt_uuid_to_str(attr->uuid, str, sizeof(str));
220 bt_shell_print("Descriptor %s found: handle %x", str, attr->handle);
221 break;
222 }
223
224 return BT_GATT_ITER_CONTINUE;
225 }
226
cmd_discover(const struct shell * sh,size_t argc,char * argv[])227 static int cmd_discover(const struct shell *sh, size_t argc, char *argv[])
228 {
229 int err;
230
231 if (!default_conn) {
232 shell_error(sh, "Not connected");
233 return -ENOEXEC;
234 }
235
236 if (discover_params.func) {
237 shell_print(sh, "Discover ongoing");
238 return -ENOEXEC;
239 }
240
241 discover_params.func = discover_func;
242 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
243 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
244 SET_CHAN_OPT_ANY(discover_params);
245
246 if (argc > 1) {
247 /* Only set the UUID if the value is valid (non zero) */
248 uuid.val = strtoul(argv[1], NULL, 16);
249 if (uuid.val) {
250 discover_params.uuid = &uuid.uuid;
251 }
252 }
253
254 if (argc > 2) {
255 discover_params.start_handle = strtoul(argv[2], NULL, 16);
256 if (argc > 3) {
257 discover_params.end_handle = strtoul(argv[3], NULL, 16);
258 }
259 }
260
261 if (!strcmp(argv[0], "discover")) {
262 discover_params.type = BT_GATT_DISCOVER_ATTRIBUTE;
263 } else if (!strcmp(argv[0], "discover-secondary")) {
264 discover_params.type = BT_GATT_DISCOVER_SECONDARY;
265 } else if (!strcmp(argv[0], "discover-include")) {
266 discover_params.type = BT_GATT_DISCOVER_INCLUDE;
267 } else if (!strcmp(argv[0], "discover-characteristic")) {
268 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
269 } else if (!strcmp(argv[0], "discover-descriptor")) {
270 discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
271 } else {
272 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
273 }
274
275 err = bt_gatt_discover(default_conn, &discover_params);
276 if (err) {
277 shell_error(sh, "Discover failed (err %d)", err);
278 } else {
279 shell_print(sh, "Discover pending");
280 }
281
282 return err;
283 }
284
285 static struct bt_gatt_read_params read_params;
286
read_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)287 static uint8_t read_func(struct bt_conn *conn, uint8_t err,
288 struct bt_gatt_read_params *params,
289 const void *data, uint16_t length)
290 {
291 bt_shell_print("Read complete: err 0x%02x length %u", err, length);
292
293 if (!data) {
294 (void)memset(params, 0, sizeof(*params));
295 return BT_GATT_ITER_STOP;
296 } else {
297 bt_shell_hexdump(data, length);
298 }
299
300 return BT_GATT_ITER_CONTINUE;
301 }
302
cmd_read(const struct shell * sh,size_t argc,char * argv[])303 static int cmd_read(const struct shell *sh, size_t argc, char *argv[])
304 {
305 int err;
306
307 if (!default_conn) {
308 shell_error(sh, "Not connected");
309 return -ENOEXEC;
310 }
311
312 if (read_params.func) {
313 shell_print(sh, "Read ongoing");
314 return -ENOEXEC;
315 }
316
317 read_params.func = read_func;
318 read_params.handle_count = 1;
319 read_params.single.handle = strtoul(argv[1], NULL, 16);
320 read_params.single.offset = 0U;
321 SET_CHAN_OPT_ANY(read_params);
322
323 if (argc > 2) {
324 read_params.single.offset = strtoul(argv[2], NULL, 16);
325 }
326
327 err = bt_gatt_read(default_conn, &read_params);
328 if (err) {
329 shell_error(sh, "Read failed (err %d)", err);
330 } else {
331 shell_print(sh, "Read pending");
332 }
333
334 return err;
335 }
336
cmd_mread(const struct shell * sh,size_t argc,char * argv[])337 static int cmd_mread(const struct shell *sh, size_t argc, char *argv[])
338 {
339 uint16_t h[8];
340 size_t i;
341 int err;
342
343 if (!default_conn) {
344 shell_error(sh, "Not connected");
345 return -ENOEXEC;
346 }
347
348 if (read_params.func) {
349 shell_print(sh, "Read ongoing");
350 return -ENOEXEC;
351 }
352
353 if ((argc - 1) > ARRAY_SIZE(h)) {
354 shell_print(sh, "Enter max %zu handle items to read", ARRAY_SIZE(h));
355 return -EINVAL;
356 }
357
358 for (i = 0; i < argc - 1; i++) {
359 h[i] = strtoul(argv[i + 1], NULL, 16);
360 }
361
362 read_params.func = read_func;
363 read_params.handle_count = i;
364 read_params.multiple.handles = h;
365 read_params.multiple.variable = true;
366 SET_CHAN_OPT_ANY(read_params);
367
368 err = bt_gatt_read(default_conn, &read_params);
369 if (err) {
370 shell_error(sh, "GATT multiple read request failed (err %d)", err);
371 }
372
373 return err;
374 }
375
cmd_read_uuid(const struct shell * sh,size_t argc,char * argv[])376 static int cmd_read_uuid(const struct shell *sh, size_t argc, char *argv[])
377 {
378 int err;
379
380 if (!default_conn) {
381 shell_error(sh, "Not connected");
382 return -ENOEXEC;
383 }
384
385 if (read_params.func) {
386 shell_print(sh, "Read ongoing");
387 return -ENOEXEC;
388 }
389
390 read_params.func = read_func;
391 read_params.handle_count = 0;
392 read_params.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
393 read_params.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
394 SET_CHAN_OPT_ANY(read_params);
395
396 if (argc > 1) {
397 uuid.val = strtoul(argv[1], NULL, 16);
398 if (uuid.val) {
399 read_params.by_uuid.uuid = &uuid.uuid;
400 }
401 }
402
403 if (argc > 2) {
404 read_params.by_uuid.start_handle = strtoul(argv[2], NULL, 16);
405 if (argc > 3) {
406 read_params.by_uuid.end_handle = strtoul(argv[3], NULL, 16);
407 }
408 }
409
410 err = bt_gatt_read(default_conn, &read_params);
411 if (err) {
412 shell_error(sh, "Read failed (err %d)", err);
413 } else {
414 shell_print(sh, "Read pending");
415 }
416
417 return err;
418 }
419
420 static struct bt_gatt_write_params write_params;
421 static uint8_t gatt_write_buf[BT_ATT_MAX_ATTRIBUTE_LEN];
422
write_func(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)423 static void write_func(struct bt_conn *conn, uint8_t err,
424 struct bt_gatt_write_params *params)
425 {
426 bt_shell_print("Write complete: err 0x%02x", err);
427
428 (void)memset(&write_params, 0, sizeof(write_params));
429 }
430
cmd_write(const struct shell * sh,size_t argc,char * argv[])431 static int cmd_write(const struct shell *sh, size_t argc, char *argv[])
432 {
433 int err;
434 uint16_t handle, offset;
435
436 if (!default_conn) {
437 shell_error(sh, "Not connected");
438 return -ENOEXEC;
439 }
440
441 if (write_params.func) {
442 shell_error(sh, "Write ongoing");
443 return -ENOEXEC;
444 }
445
446 handle = strtoul(argv[1], NULL, 16);
447 offset = strtoul(argv[2], NULL, 16);
448
449 write_params.length = hex2bin(argv[3], strlen(argv[3]),
450 gatt_write_buf, sizeof(gatt_write_buf));
451 if (write_params.length == 0) {
452 shell_error(sh, "No data set");
453 return -ENOEXEC;
454 }
455
456 write_params.data = gatt_write_buf;
457 write_params.handle = handle;
458 write_params.offset = offset;
459 write_params.func = write_func;
460 SET_CHAN_OPT_ANY(write_params);
461
462 err = bt_gatt_write(default_conn, &write_params);
463 if (err) {
464 write_params.func = NULL;
465 shell_error(sh, "Write failed (err %d)", err);
466 } else {
467 shell_print(sh, "Write pending");
468 }
469
470 return err;
471 }
472
write_without_rsp_cb(struct bt_conn * conn,void * user_data)473 static void write_without_rsp_cb(struct bt_conn *conn, void *user_data)
474 {
475 uint16_t len = POINTER_TO_UINT(user_data);
476
477 update_write_stats(len);
478
479 print_write_stats();
480 }
481
cmd_write_without_rsp(const struct shell * sh,size_t argc,char * argv[])482 static int cmd_write_without_rsp(const struct shell *sh,
483 size_t argc, char *argv[])
484 {
485 uint16_t handle;
486 uint16_t repeat;
487 int err;
488 uint16_t len;
489 bool sign;
490 bt_gatt_complete_func_t func = NULL;
491
492 if (!default_conn) {
493 shell_error(sh, "Not connected");
494 return -ENOEXEC;
495 }
496
497 sign = !strcmp(argv[0], "signed-write");
498 if (!sign) {
499 if (!strcmp(argv[0], "write-without-response-cb")) {
500 func = write_without_rsp_cb;
501 reset_write_stats();
502 }
503 }
504
505 handle = strtoul(argv[1], NULL, 16);
506 gatt_write_buf[0] = strtoul(argv[2], NULL, 16);
507 len = 1U;
508
509 if (argc > 3) {
510 int i;
511
512 len = MIN(strtoul(argv[3], NULL, 16), sizeof(gatt_write_buf));
513
514 for (i = 1; i < len; i++) {
515 gatt_write_buf[i] = gatt_write_buf[0];
516 }
517 }
518
519 repeat = 0U;
520 err = 0;
521
522 if (argc > 4) {
523 repeat = strtoul(argv[4], NULL, 16);
524 }
525
526 if (!repeat) {
527 repeat = 1U;
528 }
529
530 while (repeat--) {
531 err = bt_gatt_write_without_response_cb(default_conn, handle,
532 gatt_write_buf, len,
533 sign, func,
534 UINT_TO_POINTER(len));
535 if (err) {
536 break;
537 }
538
539 k_yield();
540 }
541
542 shell_print(sh, "Write Complete (err %d)", err);
543 return err;
544 }
545
546 static struct bt_gatt_subscribe_params subscribe_params;
547
notify_func(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)548 static uint8_t notify_func(struct bt_conn *conn,
549 struct bt_gatt_subscribe_params *params,
550 const void *data, uint16_t length)
551 {
552 if (!data) {
553 bt_shell_print("Unsubscribed");
554 params->value_handle = 0U;
555 return BT_GATT_ITER_STOP;
556 }
557
558 bt_shell_print("Notification: value_handle %u, length %u",
559 params->value_handle, length);
560 bt_shell_hexdump(data, length);
561
562 return BT_GATT_ITER_CONTINUE;
563 }
564
cmd_subscribe(const struct shell * sh,size_t argc,char * argv[])565 static int cmd_subscribe(const struct shell *sh, size_t argc, char *argv[])
566 {
567 int err;
568
569 if (subscribe_params.value_handle) {
570 shell_error(sh, "Cannot subscribe: subscription to %x already exists",
571 subscribe_params.value_handle);
572 return -ENOEXEC;
573 }
574
575 if (!default_conn) {
576 shell_error(sh, "Not connected");
577 return -ENOEXEC;
578 }
579
580 subscribe_params.ccc_handle = strtoul(argv[1], NULL, 16);
581 subscribe_params.value_handle = strtoul(argv[2], NULL, 16);
582 subscribe_params.value = BT_GATT_CCC_NOTIFY;
583 subscribe_params.notify = notify_func;
584 SET_CHAN_OPT_ANY(subscribe_params);
585
586 #if defined(CONFIG_BT_GATT_AUTO_DISCOVER_CCC)
587 if (subscribe_params.ccc_handle == BT_GATT_AUTO_DISCOVER_CCC_HANDLE) {
588 static struct bt_gatt_discover_params disc_params;
589
590 subscribe_params.disc_params = &disc_params;
591 subscribe_params.end_handle = 0xFFFF;
592 }
593 #endif /* CONFIG_BT_GATT_AUTO_DISCOVER_CCC */
594
595 if (argc > 3 && !strcmp(argv[3], "ind")) {
596 subscribe_params.value = BT_GATT_CCC_INDICATE;
597 }
598
599 err = bt_gatt_subscribe(default_conn, &subscribe_params);
600 if (err) {
601 subscribe_params.value_handle = 0U;
602 shell_error(sh, "Subscribe failed (err %d)", err);
603 } else {
604 shell_print(sh, "Subscribed");
605 }
606
607 return err;
608 }
609
cmd_resubscribe(const struct shell * sh,size_t argc,char * argv[])610 static int cmd_resubscribe(const struct shell *sh, size_t argc,
611 char *argv[])
612 {
613 bt_addr_le_t addr;
614 int err;
615
616 if (subscribe_params.value_handle) {
617 shell_error(sh, "Cannot resubscribe: subscription to %x already exists",
618 subscribe_params.value_handle);
619 return -ENOEXEC;
620 }
621
622 err = bt_addr_le_from_str(argv[1], argv[2], &addr);
623 if (err) {
624 shell_error(sh, "Invalid peer address (err %d)", err);
625 return -ENOEXEC;
626 }
627
628 subscribe_params.ccc_handle = strtoul(argv[3], NULL, 16);
629 subscribe_params.value_handle = strtoul(argv[4], NULL, 16);
630 subscribe_params.value = BT_GATT_CCC_NOTIFY;
631 subscribe_params.notify = notify_func;
632 SET_CHAN_OPT_ANY(subscribe_params);
633
634 if (argc > 5 && !strcmp(argv[5], "ind")) {
635 subscribe_params.value = BT_GATT_CCC_INDICATE;
636 }
637
638 err = bt_gatt_resubscribe(selected_id, &addr, &subscribe_params);
639 if (err) {
640 subscribe_params.value_handle = 0U;
641 shell_error(sh, "Resubscribe failed (err %d)", err);
642 } else {
643 shell_print(sh, "Resubscribed");
644 }
645
646 return err;
647 }
648
cmd_unsubscribe(const struct shell * sh,size_t argc,char * argv[])649 static int cmd_unsubscribe(const struct shell *sh,
650 size_t argc, char *argv[])
651 {
652 int err;
653
654 if (!default_conn) {
655 shell_error(sh, "Not connected");
656 return -ENOEXEC;
657 }
658
659 if (!subscribe_params.value_handle) {
660 shell_error(sh, "No subscription found");
661 return -ENOEXEC;
662 }
663
664 err = bt_gatt_unsubscribe(default_conn, &subscribe_params);
665 if (err) {
666 shell_error(sh, "Unsubscribe failed (err %d)", err);
667 } else {
668 shell_print(sh, "Unsubscribe success");
669 }
670
671 return err;
672 }
673 #endif /* CONFIG_BT_GATT_CLIENT */
674
675 static struct db_stats {
676 uint16_t svc_count;
677 uint16_t attr_count;
678 uint16_t chrc_count;
679 uint16_t ccc_count;
680 } stats;
681
print_attr(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)682 static uint8_t print_attr(const struct bt_gatt_attr *attr, uint16_t handle,
683 void *user_data)
684 {
685 const struct shell *sh = user_data;
686 char str[BT_UUID_STR_LEN];
687
688 stats.attr_count++;
689
690 if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) ||
691 !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) {
692 stats.svc_count++;
693 }
694
695 if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) {
696 stats.chrc_count++;
697 }
698
699 if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC) &&
700 attr->write == bt_gatt_attr_write_ccc) {
701 stats.ccc_count++;
702 }
703
704 bt_uuid_to_str(attr->uuid, str, sizeof(str));
705 shell_print(sh, "attr %p handle 0x%04x uuid %s perm 0x%02x",
706 attr, handle, str, attr->perm);
707
708 return BT_GATT_ITER_CONTINUE;
709 }
710
cmd_show_db(const struct shell * sh,size_t argc,char * argv[])711 static int cmd_show_db(const struct shell *sh, size_t argc, char *argv[])
712 {
713 struct bt_uuid_16 uuid16;
714 size_t total_len;
715
716 memset(&stats, 0, sizeof(stats));
717
718 if (argc > 1) {
719 uint16_t num_matches = 0;
720
721 uuid16.uuid.type = BT_UUID_TYPE_16;
722 uuid16.val = strtoul(argv[1], NULL, 16);
723
724 if (argc > 2) {
725 num_matches = strtoul(argv[2], NULL, 10);
726 }
727
728 bt_gatt_foreach_attr_type(0x0001, 0xffff, &uuid16.uuid, NULL,
729 num_matches, print_attr,
730 (void *)sh);
731 return 0;
732 }
733
734 bt_gatt_foreach_attr(0x0001, 0xffff, print_attr, (void *)sh);
735
736 if (!stats.attr_count) {
737 shell_print(sh, "No attribute found");
738 return 0;
739 }
740
741 total_len = stats.svc_count * sizeof(struct bt_gatt_service);
742 total_len += stats.chrc_count * sizeof(struct bt_gatt_chrc);
743 total_len += stats.attr_count * sizeof(struct bt_gatt_attr);
744 total_len += stats.ccc_count * sizeof(struct bt_gatt_ccc_managed_user_data);
745
746 shell_print(sh, "=================================================");
747 shell_print(sh, "Total: %u services %u attributes (%zu bytes)",
748 stats.svc_count, stats.attr_count, total_len);
749
750 return 0;
751 }
752
753 #if defined(CONFIG_BT_GATT_DYNAMIC_DB)
754 /* Custom Service Variables */
755
756 static const struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
757 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));
758
759 static const struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
760 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));
761
762 static const struct bt_uuid_128 vnd_long_uuid1 = BT_UUID_INIT_128(
763 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef3));
764
765 static const struct bt_uuid_128 vnd_long_uuid2 = BT_UUID_INIT_128(
766 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x5678cefaadde));
767
768 static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' };
769
770 static const struct bt_uuid_128 vnd1_uuid = BT_UUID_INIT_128(
771 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x56789abcdef4));
772
773 static const struct bt_uuid_128 vnd1_echo_uuid = BT_UUID_INIT_128(
774 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x12340, 0x56789abcdef5));
775
776 static uint8_t echo_enabled;
777
vnd1_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)778 static void vnd1_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
779 {
780 echo_enabled = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
781 }
782
write_vnd1(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)783 static ssize_t write_vnd1(struct bt_conn *conn, const struct bt_gatt_attr *attr,
784 const void *buf, uint16_t len, uint16_t offset,
785 uint8_t flags)
786 {
787 if (echo_enabled) {
788 bt_shell_print("Echo attr len %u", len);
789 bt_gatt_notify(conn, attr, buf, len);
790 }
791
792 return len;
793 }
794
read_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)795 static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
796 void *buf, uint16_t len, uint16_t offset)
797 {
798 const char *value = attr->user_data;
799
800 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
801 strlen(value));
802 }
803
write_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)804 static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
805 const void *buf, uint16_t len, uint16_t offset,
806 uint8_t flags)
807 {
808 uint8_t *value = attr->user_data;
809
810 if (offset + len > sizeof(vnd_value)) {
811 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
812 }
813
814 memcpy(value + offset, buf, len);
815
816 return len;
817 }
818
819 #define MAX_DATA 30
820 static uint8_t vnd_long_value1[MAX_DATA] = { 'V', 'e', 'n', 'd', 'o', 'r' };
821 static uint8_t vnd_long_value2[MAX_DATA] = { 'S', 't', 'r', 'i', 'n', 'g' };
822
read_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)823 static ssize_t read_long_vnd(struct bt_conn *conn,
824 const struct bt_gatt_attr *attr, void *buf,
825 uint16_t len, uint16_t offset)
826 {
827 uint8_t *value = attr->user_data;
828
829 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
830 sizeof(vnd_long_value1));
831 }
832
write_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)833 static ssize_t write_long_vnd(struct bt_conn *conn,
834 const struct bt_gatt_attr *attr, const void *buf,
835 uint16_t len, uint16_t offset, uint8_t flags)
836 {
837 uint8_t *value = attr->user_data;
838
839 if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
840 return 0;
841 }
842
843 if (offset + len > sizeof(vnd_long_value1)) {
844 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
845 }
846
847 /* Copy to buffer */
848 memcpy(value + offset, buf, len);
849
850 return len;
851 }
852
853 static struct bt_gatt_attr vnd_attrs[] = {
854 /* Vendor Primary Service Declaration */
855 BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
856
857 BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
858 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
859 BT_GATT_PERM_READ_AUTHEN |
860 BT_GATT_PERM_WRITE_AUTHEN,
861 read_vnd, write_vnd, vnd_value),
862
863 BT_GATT_CHARACTERISTIC(&vnd_long_uuid1.uuid, BT_GATT_CHRC_READ |
864 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
865 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
866 BT_GATT_PERM_PREPARE_WRITE,
867 read_long_vnd, write_long_vnd,
868 &vnd_long_value1),
869
870 BT_GATT_CHARACTERISTIC(&vnd_long_uuid2.uuid, BT_GATT_CHRC_READ |
871 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
872 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
873 BT_GATT_PERM_PREPARE_WRITE,
874 read_long_vnd, write_long_vnd,
875 &vnd_long_value2),
876 };
877
878 static struct bt_gatt_service vnd_svc = BT_GATT_SERVICE(vnd_attrs);
879
880 static struct bt_gatt_attr vnd1_attrs[] = {
881 /* Vendor Primary Service Declaration */
882 BT_GATT_PRIMARY_SERVICE(&vnd1_uuid),
883
884 BT_GATT_CHARACTERISTIC(&vnd1_echo_uuid.uuid,
885 BT_GATT_CHRC_WRITE_WITHOUT_RESP |
886 BT_GATT_CHRC_NOTIFY,
887 BT_GATT_PERM_WRITE, NULL, write_vnd1, NULL),
888 BT_GATT_CCC(vnd1_ccc_cfg_changed,
889 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
890 };
891
892 static struct bt_gatt_service vnd1_svc = BT_GATT_SERVICE(vnd1_attrs);
893
cmd_register_test_svc(const struct shell * sh,size_t argc,char * argv[])894 static int cmd_register_test_svc(const struct shell *sh,
895 size_t argc, char *argv[])
896 {
897 char str[BT_UUID_STR_LEN];
898 int err;
899
900 bt_uuid_to_str(&vnd_uuid.uuid, str, sizeof(str));
901 err = bt_gatt_service_register(&vnd_svc);
902 if (!err) {
903 shell_print(sh, "Registered test vendor service %s", str);
904 } else {
905 shell_error(sh, "Failed to register test vendor service %s (%d)", str, err);
906 }
907
908 bt_uuid_to_str(&vnd1_uuid.uuid, str, sizeof(str));
909 err = bt_gatt_service_register(&vnd1_svc);
910 if (!err) {
911 shell_print(sh, "Registered test vendor service %s", str);
912 } else {
913 shell_error(sh, "Failed to register test vendor service %s (%d)", str, err);
914 }
915
916 return 0;
917 }
918
cmd_unregister_test_svc(const struct shell * sh,size_t argc,char * argv[])919 static int cmd_unregister_test_svc(const struct shell *sh,
920 size_t argc, char *argv[])
921 {
922 char str[BT_UUID_STR_LEN];
923 int err;
924
925 bt_uuid_to_str(&vnd_uuid.uuid, str, sizeof(str));
926 err = bt_gatt_service_unregister(&vnd_svc);
927 if (!err) {
928 shell_print(sh, "Unregistered test vendor service %s", str);
929 } else {
930 shell_error(sh, "Failed to unregister test vendor service %s (%d)", str, err);
931 }
932
933 bt_uuid_to_str(&vnd1_uuid.uuid, str, sizeof(str));
934 err = bt_gatt_service_unregister(&vnd1_svc);
935 if (!err) {
936 shell_print(sh, "Unregistered test vendor service %s", str);
937 } else {
938 shell_error(sh, "Failed to unregister test vendor service %s (%d)", str, err);
939 }
940
941 return 0;
942 }
943
found_attr(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)944 static uint8_t found_attr(const struct bt_gatt_attr *attr, uint16_t handle,
945 void *user_data)
946 {
947 const struct bt_gatt_attr **found = user_data;
948
949 *found = attr;
950
951 return BT_GATT_ITER_STOP;
952 }
953
find_attr(uint16_t handle)954 static const struct bt_gatt_attr *find_attr(uint16_t handle)
955 {
956 const struct bt_gatt_attr *attr = NULL;
957
958 bt_gatt_foreach_attr(handle, handle, found_attr, &attr);
959
960 return attr;
961 }
962
cmd_notify(const struct shell * sh,size_t argc,char * argv[])963 static int cmd_notify(const struct shell *sh, size_t argc, char *argv[])
964 {
965 const struct bt_gatt_attr *attr;
966 int err;
967 size_t data_len;
968 unsigned long handle;
969 static char data[BT_ATT_MAX_ATTRIBUTE_LEN];
970
971 const char *arg_handle = argv[1];
972 const char *arg_data = argv[2];
973 size_t arg_data_len = strlen(arg_data);
974
975 err = 0;
976 handle = shell_strtoul(arg_handle, 16, &err);
977 if (err) {
978 shell_error(sh, "Handle '%s': Not a valid hex number.", arg_handle);
979 return -EINVAL;
980 }
981
982 if (!IN_RANGE(handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE)) {
983 shell_error(sh, "Handle 0x%lx: Impossible value.", handle);
984 return -EINVAL;
985 }
986
987 if ((arg_data_len / 2) > BT_ATT_MAX_ATTRIBUTE_LEN) {
988 shell_error(sh, "Data: Size exceeds legal attribute size.");
989 return -EINVAL;
990 }
991
992 data_len = hex2bin(arg_data, arg_data_len, data, sizeof(data));
993 if (data_len == 0 && arg_data_len != 0) {
994 shell_error(sh, "Data: Bad hex.");
995 return -EINVAL;
996 }
997
998 attr = find_attr(handle);
999 if (!attr) {
1000 shell_error(sh, "Handle 0x%lx: Local attribute not found.", handle);
1001 return -EINVAL;
1002 }
1003
1004 err = bt_gatt_notify(NULL, attr, data, data_len);
1005 if (err) {
1006 shell_error(sh, "bt_gatt_notify errno %d (%s)", -err, strerror(-err));
1007 }
1008
1009 return err;
1010 }
1011
1012 #if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
notify_cb(struct bt_conn * conn,void * user_data)1013 static void notify_cb(struct bt_conn *conn, void *user_data)
1014 {
1015 const struct shell *sh = user_data;
1016
1017 shell_print(sh, "Notification sent to conn %p", conn);
1018 }
cmd_notify_mult(const struct shell * sh,size_t argc,char * argv[])1019 static int cmd_notify_mult(const struct shell *sh, size_t argc, char *argv[])
1020 {
1021 const size_t max_cnt = CONFIG_BT_L2CAP_TX_BUF_COUNT;
1022 struct bt_gatt_notify_params params[max_cnt];
1023 const size_t min_cnt = 1U;
1024 unsigned long data;
1025 unsigned long cnt;
1026 uint16_t cnt_u16;
1027 int err = 0;
1028
1029 if (!default_conn) {
1030 shell_error(sh, "Not connected.");
1031
1032 return -ENOEXEC;
1033 }
1034
1035 if (!echo_enabled) {
1036 shell_error(sh, "No clients have enabled notifications for the vnd1_echo CCC.");
1037
1038 return -ENOEXEC;
1039 }
1040
1041 cnt = shell_strtoul(argv[1], 10, &err);
1042 if (err != 0) {
1043 shell_error(sh, "Invalid count parameter: %s", argv[1]);
1044
1045 return -err;
1046 }
1047
1048 if (!IN_RANGE(cnt, min_cnt, max_cnt)) {
1049 shell_error(sh, "Invalid count value %lu (range %zu to %zu)",
1050 cnt, min_cnt, max_cnt);
1051
1052 return -ENOEXEC;
1053 }
1054
1055 cnt_u16 = (uint16_t)cnt;
1056
1057 if (argc > 2) {
1058 data = shell_strtoul(argv[2], 16, &err);
1059 if (err != 0) {
1060 shell_error(sh, "Invalid data parameter: %s", argv[1]);
1061
1062 return -err;
1063 }
1064 }
1065
1066 (void)memset(params, 0, sizeof(params));
1067
1068 for (uint16_t i = 0U; i < cnt_u16; i++) {
1069 params[i].uuid = 0;
1070 params[i].attr = vnd1_attrs;
1071 params[i].data = &data;
1072 params[i].len = sizeof(data);
1073 params[i].func = notify_cb;
1074 params[i].user_data = (void *)sh;
1075 }
1076
1077 err = bt_gatt_notify_multiple(default_conn, cnt_u16, params);
1078 if (err != 0) {
1079 shell_error(sh, "bt_gatt_notify_multiple failed: %d", err);
1080 } else {
1081 shell_print(sh, "Send %u notifications", cnt_u16);
1082 }
1083
1084 return err;
1085 }
1086 #endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
1087
1088 static const struct bt_uuid_128 met_svc_uuid = BT_UUID_INIT_128(
1089 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcde01));
1090 static const struct bt_uuid_128 met_char_uuid = BT_UUID_INIT_128(
1091 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcde02));
1092
1093 static uint8_t met_char_value[BT_ATT_MAX_ATTRIBUTE_LEN] = {
1094 'M', 'e', 't', 'r', 'i', 'c', 's' };
1095
read_met(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)1096 static ssize_t read_met(struct bt_conn *conn, const struct bt_gatt_attr *attr,
1097 void *buf, uint16_t len, uint16_t offset)
1098 {
1099 const char *value = attr->user_data;
1100 uint16_t value_len;
1101
1102 value_len = MIN(strlen(value), BT_ATT_MAX_ATTRIBUTE_LEN);
1103
1104 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
1105 value_len);
1106 }
1107
write_met(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)1108 static ssize_t write_met(struct bt_conn *conn, const struct bt_gatt_attr *attr,
1109 const void *buf, uint16_t len, uint16_t offset,
1110 uint8_t flags)
1111 {
1112 uint8_t *value = attr->user_data;
1113
1114 if (offset + len > sizeof(met_char_value)) {
1115 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
1116 }
1117
1118 memcpy(value + offset, buf, len);
1119
1120 update_write_stats(len);
1121
1122 return len;
1123 }
1124
1125 static struct bt_gatt_attr met_attrs[] = {
1126 BT_GATT_PRIMARY_SERVICE(&met_svc_uuid),
1127
1128 BT_GATT_CHARACTERISTIC(&met_char_uuid.uuid,
1129 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
1130 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
1131 read_met, write_met, met_char_value),
1132 };
1133
1134 static struct bt_gatt_service met_svc = BT_GATT_SERVICE(met_attrs);
1135
cmd_metrics(const struct shell * sh,size_t argc,char * argv[])1136 static int cmd_metrics(const struct shell *sh, size_t argc, char *argv[])
1137 {
1138 int err = 0;
1139
1140 if (argc < 2) {
1141 print_write_stats();
1142 return 0;
1143 }
1144
1145 if (!strcmp(argv[1], "on")) {
1146 shell_print(sh, "Registering GATT metrics test Service.");
1147 err = bt_gatt_service_register(&met_svc);
1148 } else if (!strcmp(argv[1], "off")) {
1149 shell_print(sh, "Unregistering GATT metrics test Service.");
1150 err = bt_gatt_service_unregister(&met_svc);
1151 } else {
1152 shell_error(sh, "Incorrect value: %s", argv[1]);
1153 shell_help(sh);
1154 return -ENOEXEC;
1155 }
1156
1157 if (!err) {
1158 shell_print(sh, "GATT write cmd metrics %s.", argv[1]);
1159 }
1160
1161 return err;
1162 }
1163 #endif /* CONFIG_BT_GATT_DYNAMIC_DB */
1164
get_cb(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)1165 static uint8_t get_cb(const struct bt_gatt_attr *attr, uint16_t handle,
1166 void *user_data)
1167 {
1168 struct shell *sh = user_data;
1169 uint8_t buf[256];
1170 ssize_t ret;
1171 char str[BT_UUID_STR_LEN];
1172
1173 bt_uuid_to_str(attr->uuid, str, sizeof(str));
1174 shell_print(sh, "attr %p uuid %s perm 0x%02x", attr, str,
1175 attr->perm);
1176
1177 if (!attr->read) {
1178 return BT_GATT_ITER_CONTINUE;
1179 }
1180
1181 ret = attr->read(NULL, attr, (void *)buf, sizeof(buf), 0);
1182 if (ret < 0) {
1183 shell_print(sh, "Failed to read: %zd", ret);
1184 return BT_GATT_ITER_STOP;
1185 }
1186
1187 shell_hexdump(sh, buf, ret);
1188
1189 return BT_GATT_ITER_CONTINUE;
1190 }
1191
cmd_get(const struct shell * sh,size_t argc,char * argv[])1192 static int cmd_get(const struct shell *sh, size_t argc, char *argv[])
1193 {
1194 uint16_t start, end;
1195
1196 start = strtoul(argv[1], NULL, 16);
1197 end = start;
1198
1199 if (argc > 2) {
1200 end = strtoul(argv[2], NULL, 16);
1201 }
1202
1203 bt_gatt_foreach_attr(start, end, get_cb, (void *)sh);
1204
1205 return 0;
1206 }
1207
1208 struct set_data {
1209 const struct shell *sh;
1210 size_t argc;
1211 char **argv;
1212 int err;
1213 };
1214
set_cb(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)1215 static uint8_t set_cb(const struct bt_gatt_attr *attr, uint16_t handle,
1216 void *user_data)
1217 {
1218 struct set_data *data = user_data;
1219 uint8_t buf[256];
1220 size_t i;
1221 ssize_t ret;
1222
1223 if (!attr->write) {
1224 shell_error(data->sh, "Write not supported");
1225 data->err = -ENOENT;
1226 return BT_GATT_ITER_CONTINUE;
1227 }
1228
1229 for (i = 0; i < data->argc; i++) {
1230 buf[i] = strtoul(data->argv[i], NULL, 16);
1231 }
1232
1233 ret = attr->write(NULL, attr, (void *)buf, i, 0, 0);
1234 if (ret < 0) {
1235 data->err = ret;
1236 shell_error(data->sh, "Failed to write: %zd", ret);
1237 return BT_GATT_ITER_STOP;
1238 }
1239
1240 return BT_GATT_ITER_CONTINUE;
1241 }
1242
cmd_set(const struct shell * sh,size_t argc,char * argv[])1243 static int cmd_set(const struct shell *sh, size_t argc, char *argv[])
1244 {
1245 uint16_t handle;
1246 struct set_data data;
1247
1248 handle = strtoul(argv[1], NULL, 16);
1249
1250 data.sh = sh;
1251 data.argc = argc - 2;
1252 data.argv = argv + 2;
1253 data.err = 0;
1254
1255 bt_gatt_foreach_attr(handle, handle, set_cb, &data);
1256
1257 if (data.err < 0) {
1258 return -ENOEXEC;
1259 }
1260
1261 bt_gatt_foreach_attr(handle, handle, get_cb, (void *)sh);
1262
1263 return 0;
1264 }
1265
cmd_att_mtu(const struct shell * sh,size_t argc,char * argv[])1266 int cmd_att_mtu(const struct shell *sh, size_t argc, char *argv[])
1267 {
1268 uint16_t mtu;
1269
1270 if (default_conn) {
1271 mtu = bt_gatt_get_mtu(default_conn);
1272 shell_print(sh, "MTU size: %u", mtu);
1273 } else {
1274 shell_print(sh, "No default connection");
1275 }
1276
1277 return 0;
1278 }
1279
1280 #define HELP_NONE "[none]"
1281 #define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"
1282
1283 SHELL_STATIC_SUBCMD_SET_CREATE(gatt_cmds,
1284 #if defined(CONFIG_BT_GATT_CLIENT)
1285 SHELL_CMD_ARG(discover, NULL,
1286 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1287 SHELL_CMD_ARG(discover-characteristic, NULL,
1288 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1289 SHELL_CMD_ARG(discover-descriptor, NULL,
1290 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1291 SHELL_CMD_ARG(discover-include, NULL,
1292 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1293 SHELL_CMD_ARG(discover-primary, NULL,
1294 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1295 SHELL_CMD_ARG(discover-secondary, NULL,
1296 "[UUID] [start handle] [end handle]", cmd_discover, 1, 3),
1297 SHELL_CMD_ARG(exchange-mtu, NULL, HELP_NONE, cmd_exchange_mtu, 1, 0),
1298 SHELL_CMD_ARG(read, NULL, "<handle> [offset]", cmd_read, 2, 1),
1299 SHELL_CMD_ARG(read-uuid, NULL, "<UUID> [start handle] [end handle]",
1300 cmd_read_uuid, 2, 2),
1301 SHELL_CMD_ARG(read-multiple, NULL, "<handle 1> <handle 2> ...",
1302 cmd_mread, 2, -1),
1303 SHELL_CMD_ARG(signed-write, NULL, "<handle> <data> [length] [repeat]",
1304 cmd_write_without_rsp, 3, 2),
1305 SHELL_CMD_ARG(subscribe, NULL, "<CCC handle> <value handle> [ind]",
1306 cmd_subscribe, 3, 1),
1307 SHELL_CMD_ARG(resubscribe, NULL, HELP_ADDR_LE" <CCC handle> "
1308 "<value handle> [ind]", cmd_resubscribe, 5, 1),
1309 SHELL_CMD_ARG(write, NULL, "<handle> <offset> <data>", cmd_write, 4, 0),
1310 SHELL_CMD_ARG(write-without-response, NULL,
1311 "<handle> <data> [length] [repeat]",
1312 cmd_write_without_rsp, 3, 2),
1313 SHELL_CMD_ARG(write-without-response-cb, NULL,
1314 "<handle> <data> [length] [repeat]",
1315 cmd_write_without_rsp, 3, 2),
1316 SHELL_CMD_ARG(unsubscribe, NULL, HELP_NONE, cmd_unsubscribe, 1, 0),
1317 #endif /* CONFIG_BT_GATT_CLIENT */
1318 SHELL_CMD_ARG(get, NULL, "<start handle> [end handle]", cmd_get, 2, 1),
1319 SHELL_CMD_ARG(set, NULL, "<handle> [data...]", cmd_set, 2, 255),
1320 SHELL_CMD_ARG(show-db, NULL, "[uuid] [num_matches]", cmd_show_db, 1, 2),
1321 SHELL_CMD_ARG(att_mtu, NULL, "Output ATT MTU size", cmd_att_mtu, 1, 0),
1322 #if defined(CONFIG_BT_GATT_DYNAMIC_DB)
1323 SHELL_CMD_ARG(metrics, NULL, "[value: on, off]", cmd_metrics, 1, 1),
1324 SHELL_CMD_ARG(register, NULL,
1325 "register pre-predefined test service",
1326 cmd_register_test_svc, 1, 0),
1327 SHELL_CMD_ARG(unregister, NULL,
1328 "unregister pre-predefined test service",
1329 cmd_unregister_test_svc, 1, 0),
1330 SHELL_CMD_ARG(notify, NULL, "<handle> <data>", cmd_notify, 3, 0),
1331 #if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
1332 SHELL_CMD_ARG(notify-mult, NULL, "count [data]", cmd_notify_mult, 2, 1),
1333 #endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
1334 #endif /* CONFIG_BT_GATT_DYNAMIC_DB */
1335 SHELL_SUBCMD_SET_END
1336 );
1337
cmd_gatt(const struct shell * sh,size_t argc,char ** argv)1338 static int cmd_gatt(const struct shell *sh, size_t argc, char **argv)
1339 {
1340 if (argc == 1) {
1341 shell_help(sh);
1342 /* shell returns 1 when help is printed */
1343 return 1;
1344 }
1345
1346 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
1347
1348 return -EINVAL;
1349 }
1350
1351 SHELL_CMD_ARG_REGISTER(gatt, &gatt_cmds, "Bluetooth GATT shell commands",
1352 cmd_gatt, 1, 1);
1353