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