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