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