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