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